]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/mDNS.c
mDNSResponder-108.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.526 2005/10/20 00:10:33 cheshire
49 <rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
50
51 Revision 1.525 2005/09/24 00:47:17 cheshire
52 Fix comment typos
53
54 Revision 1.524 2005/09/16 21:06:49 cheshire
55 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
56
57 Revision 1.523 2005/03/21 00:33:51 shersche
58 <rdar://problem/4021486> Fix build warnings on Win32 platform
59
60 Revision 1.522 2005/03/04 21:48:12 cheshire
61 <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity
62
63 Revision 1.521 2005/02/25 04:21:00 cheshire
64 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
65
66 Revision 1.520 2005/02/16 01:14:11 cheshire
67 Convert RR Cache LogOperation() calls to debugf()
68
69 Revision 1.519 2005/02/15 01:57:20 cheshire
70 When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
71
72 Revision 1.518 2005/02/10 22:35:17 cheshire
73 <rdar://problem/3727944> Update name
74
75 Revision 1.517 2005/02/03 00:21:21 cheshire
76 Update comments about BIND named and zero-length TXT records
77
78 Revision 1.516 2005/01/28 06:06:32 cheshire
79 Update comment
80
81 Revision 1.515 2005/01/27 00:21:49 cheshire
82 <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
83
84 Revision 1.514 2005/01/21 01:33:45 cheshire
85 <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
86
87 Revision 1.513 2005/01/21 00:07:54 cheshire
88 <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict
89
90 Revision 1.512 2005/01/20 00:37:45 cheshire
91 <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
92 Take care not to recycle records while they are on the CacheFlushRecords list
93
94 Revision 1.511 2005/01/19 22:48:53 cheshire
95 <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
96
97 Revision 1.510 2005/01/19 03:12:45 cheshire
98 Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
99
100 Revision 1.509 2005/01/19 03:08:49 cheshire
101 <rdar://problem/3961051> CPU Spin in mDNSResponder
102 Log messages to help catch and report CPU spins
103
104 Revision 1.508 2005/01/18 18:56:32 cheshire
105 <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
106
107 Revision 1.507 2005/01/18 01:12:07 cheshire
108 <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
109
110 Revision 1.506 2005/01/17 23:28:53 cheshire
111 Fix compile error
112
113 Revision 1.505 2005/01/11 02:02:56 shersche
114 Move variable declaration to the beginning of statement block
115
116 Revision 1.504 2004/12/20 20:24:35 cheshire
117 <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer
118
119 Revision 1.503 2004/12/20 18:41:47 cheshire
120 <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space
121
122 Revision 1.502 2004/12/20 18:04:08 cheshire
123 <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache
124
125 Revision 1.501 2004/12/19 23:50:18 cheshire
126 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
127 Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
128
129 Revision 1.500 2004/12/18 03:13:46 cheshire
130 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
131
132 Revision 1.499 2004/12/17 23:37:45 cheshire
133 <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
134 (and other repetitive configuration changes)
135
136 Revision 1.498 2004/12/17 05:25:46 cheshire
137 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
138
139 Revision 1.497 2004/12/17 03:20:58 cheshire
140 <rdar://problem/3925168> Don't send unicast replies we know will be ignored
141
142 Revision 1.496 2004/12/16 22:18:26 cheshire
143 Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
144
145 Revision 1.495 2004/12/16 21:27:37 ksekar
146 Fixed build failures when compiled with verbose debugging messages
147
148 Revision 1.494 2004/12/16 20:46:56 cheshire
149 Fix compiler warnings
150
151 Revision 1.493 2004/12/16 20:13:00 cheshire
152 <rdar://problem/3324626> Cache memory management improvements
153
154 Revision 1.492 2004/12/16 08:03:24 shersche
155 Fix compilation error when UNICAST_DISABLED is set
156
157 Revision 1.491 2004/12/11 01:52:11 cheshire
158 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
159
160 Revision 1.490 2004/12/10 20:06:25 cheshire
161 <rdar://problem/3915074> Reduce egregious stack space usage
162 Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
163
164 Revision 1.489 2004/12/10 20:03:43 cheshire
165 <rdar://problem/3915074> Reduce egregious stack space usage
166 Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
167
168 Revision 1.488 2004/12/10 19:50:41 cheshire
169 <rdar://problem/3915074> Reduce egregious stack space usage
170 Reduced SendResponses() stack frame from 9K to 176 bytes
171
172 Revision 1.487 2004/12/10 19:39:13 cheshire
173 <rdar://problem/3915074> Reduce egregious stack space usage
174 Reduced SendQueries() stack frame from 18K to 112 bytes
175
176 Revision 1.486 2004/12/10 14:16:17 cheshire
177 <rdar://problem/3889788> Relax update rate limiting
178 We now allow an average rate of ten updates per minute.
179 Updates in excess of that are rate limited, but more gently than before.
180
181 Revision 1.485 2004/12/10 02:09:24 cheshire
182 <rdar://problem/3898376> Modify default TTLs
183
184 Revision 1.484 2004/12/09 03:15:40 ksekar
185 <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
186
187 Revision 1.483 2004/12/07 23:00:14 ksekar
188 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
189 Call RecordProbeFailure even if there is no record callback
190
191 Revision 1.482 2004/12/07 22:49:06 cheshire
192 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
193
194 Revision 1.481 2004/12/07 21:26:04 ksekar
195 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
196
197 Revision 1.480 2004/12/07 20:42:33 cheshire
198 Add explicit context parameter to mDNS_RemoveRecordFromService()
199
200 Revision 1.479 2004/12/07 17:50:49 ksekar
201 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
202
203 Revision 1.478 2004/12/06 21:15:22 ksekar
204 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
205
206 Revision 1.477 2004/12/04 02:12:45 cheshire
207 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
208
209 Revision 1.476 2004/11/29 23:34:31 cheshire
210 On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
211 is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
212 only nudges the time value to 1 if the interval calculation happens to result in the value zero.
213
214 Revision 1.475 2004/11/29 23:13:31 cheshire
215 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
216 Additional check: Make sure we don't unnecessarily send packets containing only additionals.
217 (This could occur with multi-packet KA lists, if the answer and additionals were marked
218 by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
219
220 Revision 1.474 2004/11/29 17:18:12 cheshire
221 Remove "Unknown DNS packet type" message for update responses
222
223 Revision 1.473 2004/11/25 01:57:52 cheshire
224 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
225
226 Revision 1.472 2004/11/25 01:28:09 cheshire
227 <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
228
229 Revision 1.471 2004/11/25 01:10:13 cheshire
230 Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
231
232 Revision 1.470 2004/11/24 21:54:44 cheshire
233 <rdar://problem/3894475> mDNSCore not receiving unicast responses properly
234
235 Revision 1.469 2004/11/24 04:50:39 cheshire
236 Minor tidying
237
238 Revision 1.468 2004/11/24 01:47:07 cheshire
239 <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
240
241 Revision 1.467 2004/11/24 01:41:28 cheshire
242 Rename CompleteProbing() to AcknowledgeRecord()
243
244 Revision 1.466 2004/11/23 21:08:07 ksekar
245 Don't use ID to demux multicast/unicast now that unicast uses random IDs
246
247 Revision 1.465 2004/11/15 20:09:21 ksekar
248 <rdar://problem/3719050> Wide Area support for Add/Remove record
249
250 Revision 1.464 2004/11/03 01:44:36 cheshire
251 Update debugging messages
252
253 Revision 1.463 2004/10/29 02:38:48 cheshire
254 Fix Windows compile errors
255
256 Revision 1.462 2004/10/28 19:21:07 cheshire
257 Guard against registering interface with zero InterfaceID
258
259 Revision 1.461 2004/10/28 19:02:16 cheshire
260 Remove \n from LogMsg() call
261
262 Revision 1.460 2004/10/28 03:24:40 cheshire
263 Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
264
265 Revision 1.459 2004/10/26 22:34:37 cheshire
266 <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
267
268 Revision 1.458 2004/10/26 20:45:28 cheshire
269 Show mask in "invalid mask" message
270
271 Revision 1.457 2004/10/26 06:28:36 cheshire
272 Now that we don't check IP TTL any more, remove associated log message
273
274 Revision 1.456 2004/10/26 06:21:42 cheshire
275 Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
276
277 Revision 1.455 2004/10/26 06:11:40 cheshire
278 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
279
280 Revision 1.454 2004/10/23 01:16:00 cheshire
281 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
282
283 Revision 1.453 2004/10/22 20:52:06 ksekar
284 <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
285
286 Revision 1.452 2004/10/20 01:50:40 cheshire
287 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
288 Implemented ForceMCast mode for AuthRecords as well as for Questions
289
290 Revision 1.451 2004/10/19 21:33:15 cheshire
291 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
292 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
293 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
294
295 Revision 1.450 2004/10/19 17:42:59 ksekar
296 Fixed compiler warnings for non-debug builds.
297
298 Revision 1.449 2004/10/18 22:57:07 cheshire
299 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
300
301 Revision 1.448 2004/10/16 00:16:59 cheshire
302 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
303
304 Revision 1.447 2004/10/15 00:51:21 cheshire
305 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
306
307 Revision 1.446 2004/10/14 00:43:34 cheshire
308 <rdar://problem/3815984> Services continue to announce SRV and HINFO
309
310 Revision 1.445 2004/10/12 21:07:09 cheshire
311 Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
312
313 Revision 1.444 2004/10/11 17:54:16 ksekar
314 Changed hashtable pointer output from debugf to verbosedebugf.
315
316 Revision 1.443 2004/10/10 07:05:45 cheshire
317 For consistency, use symbol "localdomain" instead of literal string
318
319 Revision 1.442 2004/10/08 20:25:10 cheshire
320 Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
321
322 Revision 1.441 2004/10/08 03:25:01 ksekar
323 <rdar://problem/3831716> domain enumeration should use LLQs
324
325 Revision 1.440 2004/10/06 01:44:19 cheshire
326 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
327
328 Revision 1.439 2004/10/03 23:14:11 cheshire
329 Add "mDNSEthAddr" type and "zeroEthAddr" constant
330
331 Revision 1.438 2004/09/29 23:07:04 cheshire
332 Patch from Pavel Repin to fix compile error on Windows
333
334 Revision 1.437 2004/09/28 02:23:50 cheshire
335 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
336 Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
337 For records with the cache flush bit set, defer the decision until the end of the packet
338
339 Revision 1.436 2004/09/28 01:27:04 cheshire
340 Update incorrect log message
341
342 Revision 1.435 2004/09/25 02:41:39 cheshire
343 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
344
345 Revision 1.434 2004/09/25 02:32:06 cheshire
346 Update comments
347
348 Revision 1.433 2004/09/25 02:24:27 cheshire
349 Removed unused rr->UseCount
350
351 Revision 1.432 2004/09/24 21:35:17 cheshire
352 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
353 TargetPort and TargetQID are allowed to be undefined if no question->Target is set
354
355 Revision 1.431 2004/09/24 21:33:12 cheshire
356 Adjust comment
357
358 Revision 1.430 2004/09/24 02:15:49 cheshire
359 <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
360
361 Revision 1.429 2004/09/24 00:20:21 cheshire
362 <rdar://problem/3483349> Any rrtype is a conflict for unique records
363
364 Revision 1.428 2004/09/24 00:12:25 cheshire
365 Get rid of unused RRUniqueOrKnownUnique(RR)
366
367 Revision 1.427 2004/09/23 20:44:11 cheshire
368 <rdar://problem/3813148> Reduce timeout before expiring records on failure
369
370 Revision 1.426 2004/09/23 20:21:07 cheshire
371 <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
372 Associate a unique sequence number with each received packet, and only increment the count of recent answer
373 packets if the packet sequence number for this answer record is not one we've already seen and counted.
374
375 Revision 1.425 2004/09/23 20:14:38 cheshire
376 Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
377
378 Revision 1.424 2004/09/23 00:58:36 cheshire
379 <rdar://problem/3781269> Rate limiting interferes with updating TXT records
380
381 Revision 1.423 2004/09/23 00:50:53 cheshire
382 <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
383
384 Revision 1.422 2004/09/22 02:34:46 cheshire
385 Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
386
387 Revision 1.421 2004/09/21 23:29:49 cheshire
388 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
389
390 Revision 1.420 2004/09/21 23:01:42 cheshire
391 Update debugf messages
392
393 Revision 1.419 2004/09/21 19:51:14 cheshire
394 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
395
396 Revision 1.418 2004/09/21 18:40:17 cheshire
397 <rdar://problem/3376752> Adjust default record TTLs
398
399 Revision 1.417 2004/09/21 17:32:16 cheshire
400 <rdar://problem/3809484> Rate limiting imposed too soon
401
402 Revision 1.416 2004/09/20 23:52:01 cheshire
403 CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
404
405 Revision 1.415 2004/09/18 01:14:09 cheshire
406 <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces
407
408 Revision 1.414 2004/09/18 01:06:48 cheshire
409 Add comments
410
411 Revision 1.413 2004/09/17 01:08:48 cheshire
412 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
413 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
414 declared in that file are ONLY appropriate to single-address-space embedded applications.
415 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
416
417 Revision 1.412 2004/09/17 00:46:33 cheshire
418 mDNS_TimeNow should take const mDNS parameter
419
420 Revision 1.411 2004/09/17 00:31:51 cheshire
421 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
422
423 Revision 1.410 2004/09/17 00:19:10 cheshire
424 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
425
426 Revision 1.409 2004/09/16 21:59:15 cheshire
427 For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
428
429 Revision 1.408 2004/09/16 21:36:36 cheshire
430 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
431 Changes to add necessary locking calls around unicast DNS operations
432
433 Revision 1.407 2004/09/16 02:29:39 cheshire
434 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
435 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
436
437 Revision 1.406 2004/09/16 01:58:14 cheshire
438 Fix compiler warnings
439
440 Revision 1.405 2004/09/16 00:24:48 cheshire
441 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
442
443 Revision 1.404 2004/09/15 21:44:11 cheshire
444 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
445 Show time value in log to help diagnose errors
446
447 Revision 1.403 2004/09/15 00:46:32 ksekar
448 Changed debugf to verbosedebugf in CheckCacheExpiration
449
450 Revision 1.402 2004/09/14 23:59:55 cheshire
451 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
452
453 Revision 1.401 2004/09/14 23:27:46 cheshire
454 Fix compile errors
455
456 Revision 1.400 2004/09/02 03:48:47 cheshire
457 <rdar://problem/3709039> Disable targeted unicast query support by default
458 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
459 2. New field AllowRemoteQuery in AuthRecord structure
460 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
461 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
462
463 Revision 1.399 2004/09/02 01:39:40 cheshire
464 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
465
466 Revision 1.398 2004/09/01 03:59:29 ksekar
467 <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
468
469 Revision 1.397 2004/08/25 22:04:25 rpantos
470 Fix the standard Windows compile error.
471
472 Revision 1.396 2004/08/25 00:37:27 ksekar
473 <rdar://problem/3774635>: Cleanup DynDNS hostname registration code
474
475 Revision 1.395 2004/08/18 17:21:18 ksekar
476 Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
477
478 Revision 1.394 2004/08/14 03:22:41 cheshire
479 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
480 Add GetUserSpecifiedDDNSName() routine
481 Convert ServiceRegDomain to domainname instead of C string
482 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
483
484 Revision 1.393 2004/08/13 23:42:52 cheshire
485 Removed unused "zeroDomainNamePtr"
486
487 Revision 1.392 2004/08/13 23:37:02 cheshire
488 Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
489 "uDNS_info.UnicastHostname" for clarity
490
491 Revision 1.391 2004/08/13 23:25:00 cheshire
492 Now that we do both uDNS and mDNS, global replace "m->hostname" with
493 "m->MulticastHostname" for clarity
494
495 Revision 1.390 2004/08/11 02:17:01 cheshire
496 <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record
497
498 Revision 1.389 2004/08/10 23:19:14 ksekar
499 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
500 Moved routines/constants to allow extern access for garbage collection daemon
501
502 Revision 1.388 2004/07/30 17:40:06 ksekar
503 <rdar://problem/3739115>: TXT Record updates not available for wide-area services
504
505 Revision 1.387 2004/07/26 22:49:30 ksekar
506 <rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
507
508 Revision 1.386 2004/07/13 21:24:24 rpantos
509 Fix for <rdar://problem/3701120>.
510
511 Revision 1.385 2004/06/18 19:09:59 cheshire
512 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
513
514 Revision 1.384 2004/06/15 04:31:23 cheshire
515 Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
516
517 Revision 1.383 2004/06/11 00:04:59 cheshire
518 <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
519
520 Revision 1.382 2004/06/08 04:59:40 cheshire
521 Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
522
523 Revision 1.381 2004/06/05 00:57:30 cheshire
524 Remove incorrect LogMsg()
525
526 Revision 1.380 2004/06/05 00:04:26 cheshire
527 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
528
529 Revision 1.379 2004/05/28 23:42:36 ksekar
530 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
531
532 Revision 1.378 2004/05/25 17:25:25 cheshire
533 Remove extraneous blank lines and white space
534
535 Revision 1.377 2004/05/18 23:51:25 cheshire
536 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
537
538 Revision 1.376 2004/05/05 18:30:44 ksekar
539 Restored surpressed Cache Tail debug messages.
540
541 Revision 1.375 2004/04/26 21:36:25 cheshire
542 Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
543 is indicated as being available on that interface
544
545 Revision 1.374 2004/04/21 02:53:26 cheshire
546 Typo in debugf statement
547
548 Revision 1.373 2004/04/21 02:49:11 cheshire
549 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
550
551 Revision 1.372 2004/04/21 02:38:51 cheshire
552 Add debugging checks
553
554 Revision 1.371 2004/04/14 23:09:28 ksekar
555 Support for TSIG signed dynamic updates.
556
557 Revision 1.370 2004/04/09 17:40:26 cheshire
558 Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
559
560 Revision 1.369 2004/04/09 16:34:00 cheshire
561 Debugging code for later; currently unused
562
563 Revision 1.368 2004/04/02 19:19:48 cheshire
564 Add code to do optional logging of multi-packet KA list time intervals
565
566 Revision 1.367 2004/03/20 03:16:10 cheshire
567 Minor refinement to "Excessive update rate" message
568
569 Revision 1.366 2004/03/20 03:12:57 cheshire
570 <rdar://problem/3587619>: UpdateCredits not granted promptly enough
571
572 Revision 1.365 2004/03/19 23:51:22 cheshire
573 Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
574
575 Revision 1.364 2004/03/13 01:57:33 ksekar
576 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
577
578 Revision 1.363 2004/03/12 21:00:51 cheshire
579 Also show port numbers when logging "apparent spoof mDNS Response" messages
580
581 Revision 1.362 2004/03/12 08:58:18 cheshire
582 Guard against empty TXT records
583
584 Revision 1.361 2004/03/09 03:00:46 cheshire
585 <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good.
586
587 Revision 1.360 2004/03/08 02:52:41 cheshire
588 Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
589
590 Revision 1.359 2004/03/02 03:21:56 cheshire
591 <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
592
593 Revision 1.358 2004/02/20 08:18:34 cheshire
594 <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
595
596 Revision 1.357 2004/02/18 01:47:41 cheshire
597 <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms
598
599 Revision 1.356 2004/02/06 23:04:19 ksekar
600 Basic Dynamic Update support via mDNS_Register (dissabled via
601 UNICAST_REGISTRATION #define)
602
603 Revision 1.355 2004/02/05 09:32:33 cheshire
604 Fix from Bob Bradley: When using the "%.*s" string form,
605 guard against truncating in the middle of a multi-byte UTF-8 character.
606
607 Revision 1.354 2004/02/05 09:30:22 cheshire
608 Update comments
609
610 Revision 1.353 2004/01/28 03:41:00 cheshire
611 <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
612
613 Revision 1.352 2004/01/28 02:30:07 ksekar
614 Added default Search Domains to unicast browsing, controlled via
615 Networking sharing prefs pane. Stopped sending unicast messages on
616 every interface. Fixed unicast resolving via mach-port API.
617
618 Revision 1.351 2004/01/27 20:15:22 cheshire
619 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
620
621 Revision 1.350 2004/01/24 23:38:16 cheshire
622 Use mDNSVal16() instead of shifting and ORing operations
623
624 Revision 1.349 2004/01/23 23:23:14 ksekar
625 Added TCP support for truncated unicast messages.
626
627 Revision 1.348 2004/01/22 03:54:11 cheshire
628 Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
629 which means "do this query via multicast, even if it's apparently a unicast domain"
630
631 Revision 1.347 2004/01/22 03:50:49 cheshire
632 If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
633
634 Revision 1.346 2004/01/22 03:48:41 cheshire
635 Make sure uDNS client doesn't accidentally use query ID zero
636
637 Revision 1.345 2004/01/22 03:43:08 cheshire
638 Export constants like mDNSInterface_LocalOnly so that the client layers can use them
639
640 Revision 1.344 2004/01/21 21:53:18 cheshire
641 <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
642
643 Revision 1.343 2003/12/23 00:07:47 cheshire
644 Make port number in debug message be five-character field, left justified
645
646 Revision 1.342 2003/12/20 01:34:28 cheshire
647 <rdar://problem/3515876>: Error putting additional records into packets
648 Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
649 after every record, not once at the end.
650
651 Revision 1.341 2003/12/18 22:56:12 cheshire
652 <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
653
654 Revision 1.340 2003/12/16 02:31:37 cheshire
655 Minor update to comments
656
657 Revision 1.339 2003/12/13 05:50:33 bradley
658 Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
659 layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
660 fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
661
662 Revision 1.338 2003/12/13 03:05:27 ksekar
663 <rdar://problem/3192548>: DynDNS: Unicast query of service records
664
665 Revision 1.337 2003/12/01 21:46:05 cheshire
666 mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
667
668 Revision 1.336 2003/12/01 21:26:19 cheshire
669 Guard against zero-length sbuffer in mDNS_vsnprintf()
670
671 Revision 1.335 2003/12/01 20:27:48 cheshire
672 Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
673
674 Revision 1.334 2003/11/20 22:59:53 cheshire
675 Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
676 Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
677
678 Revision 1.333 2003/11/20 20:49:53 cheshire
679 Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
680
681 Revision 1.332 2003/11/20 05:47:37 cheshire
682 <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query
683 Now that we only include answers in the known answer list if they are less than
684 halfway to expiry, the check to also see if we have another query scheduled
685 before the record expires is no longer necessary (and in fact, not correct).
686
687 Revision 1.331 2003/11/19 22:31:48 cheshire
688 When automatically adding A records to SRVs, add them as additionals, not answers
689
690 Revision 1.330 2003/11/19 22:28:50 cheshire
691 Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
692 to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
693
694 Revision 1.329 2003/11/19 22:19:24 cheshire
695 Show log message when ignoring packets with bad TTL.
696 This is to help diagnose problems on Linux versions that may not report the TTL reliably.
697
698 Revision 1.328 2003/11/19 22:06:38 cheshire
699 Show log messages when a service or hostname is renamed
700
701 Revision 1.327 2003/11/19 22:03:44 cheshire
702 Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
703
704 Revision 1.326 2003/11/17 22:27:02 cheshire
705 Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
706 on platforms that have native clock rates below fifty ticks per second.
707
708 Revision 1.325 2003/11/17 20:41:44 cheshire
709 Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
710
711 Revision 1.324 2003/11/17 20:36:32 cheshire
712 Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
713 DeadvertiseInterface() -- they're internal private routines, not API routines.
714
715 Revision 1.323 2003/11/14 20:59:08 cheshire
716 Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
717 Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
718
719 Revision 1.322 2003/11/14 19:47:52 cheshire
720 Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
721
722 Revision 1.321 2003/11/14 19:18:34 cheshire
723 Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
724
725 Revision 1.320 2003/11/13 06:45:04 cheshire
726 Fix compiler warning on certain compilers
727
728 Revision 1.319 2003/11/13 00:47:40 cheshire
729 <rdar://problem/3437556> We should delay AAAA record query if A record already in cache.
730
731 Revision 1.318 2003/11/13 00:33:26 cheshire
732 Change macro "RRIsAddressType" to "RRTypeIsAddressType"
733
734 Revision 1.317 2003/11/13 00:10:49 cheshire
735 <rdar://problem/3436412>: Verify that rr data is different before updating.
736
737 Revision 1.316 2003/11/08 23:37:54 cheshire
738 Give explicit zero initializers to blank static structure, required by certain compilers.
739 (Thanks to ramaprasad.kr@hp.com for reporting this.)
740
741 Revision 1.315 2003/11/07 03:32:56 cheshire
742 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
743 This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
744 purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
745 to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
746 can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
747
748 Revision 1.314 2003/11/07 03:19:49 cheshire
749 Minor variable renaming for clarity
750
751 Revision 1.313 2003/11/07 03:14:49 cheshire
752 Previous checkin proved to be overly simplistic; reversing
753
754 Revision 1.312 2003/11/03 23:45:15 cheshire
755 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
756 Build cache lists in FIFO order, not customary C LIFO order
757 (Append new elements to tail of cache list, instead of prepending at the head.)
758
759 Revision 1.311 2003/10/09 18:00:11 cheshire
760 Another compiler warning fix.
761
762 Revision 1.310 2003/10/07 20:27:05 cheshire
763 Patch from Bob Bradley, to fix warning and compile error on Windows
764
765 Revision 1.309 2003/09/26 01:06:36 cheshire
766 <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
767 Made new routine HaveSentEntireRRSet() to check if flag should be set
768
769 Revision 1.308 2003/09/23 01:05:01 cheshire
770 Minor changes to comments and debugf() message
771
772 Revision 1.307 2003/09/09 20:13:30 cheshire
773 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
774 Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
775 rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
776
777 Revision 1.306 2003/09/09 03:00:03 cheshire
778 <rdar://problem/3413099> Services take a long time to disappear when switching networks.
779 Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
780
781 Revision 1.305 2003/09/09 02:49:31 cheshire
782 <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
783
784 Revision 1.304 2003/09/09 02:41:19 cheshire
785 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
786
787 Revision 1.303 2003/09/05 19:55:02 cheshire
788 <rdar://problem/3409533> Include address records when announcing SRV records
789
790 Revision 1.302 2003/09/05 00:01:36 cheshire
791 <rdar://problem/3407549> Don't accelerate queries that have large KA lists
792
793 Revision 1.301 2003/09/04 22:51:13 cheshire
794 <rdar://problem/3398213> Group probes and goodbyes better
795
796 Revision 1.300 2003/09/03 02:40:37 cheshire
797 <rdar://problem/3404842> mDNSResponder complains about '_'s
798 Underscores are not supposed to be legal in standard DNS names, but IANA appears
799 to have allowed them in previous service name registrations, so we should too.
800
801 Revision 1.299 2003/09/03 02:33:09 cheshire
802 <rdar://problem/3404795> CacheRecordRmv ERROR
803 Don't update m->NewQuestions until *after* CheckCacheExpiration();
804
805 Revision 1.298 2003/09/03 01:47:01 cheshire
806 <rdar://problem/3319418> Services always in a state of flux
807 Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
808
809 Revision 1.297 2003/08/29 19:44:15 cheshire
810 <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
811 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
812 that already have at least one unique answer in the cache
813 2. For these queries, go straight to QM, skipping QU
814
815 Revision 1.296 2003/08/29 19:08:21 cheshire
816 <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
817 Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
818
819 Revision 1.295 2003/08/28 01:10:59 cheshire
820 <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
821
822 Revision 1.294 2003/08/27 02:30:22 cheshire
823 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
824 One more change: "query->GotTXT" is now a straightforward bi-state boolean again
825
826 Revision 1.293 2003/08/27 02:25:31 cheshire
827 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
828
829 Revision 1.292 2003/08/21 19:27:36 cheshire
830 <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
831
832 Revision 1.291 2003/08/21 18:57:44 cheshire
833 <rdar://problem/3387140> Synchronized queries on the network
834
835 Revision 1.290 2003/08/21 02:25:23 cheshire
836 Minor changes to comments and debugf() messages
837
838 Revision 1.289 2003/08/21 02:21:50 cheshire
839 <rdar://problem/3386473> Efficiency: Reduce repeated queries
840
841 Revision 1.288 2003/08/20 23:39:30 cheshire
842 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
843
844 Revision 1.287 2003/08/20 20:47:18 cheshire
845 Fix compiler warning
846
847 Revision 1.286 2003/08/20 02:18:51 cheshire
848 <rdar://problem/3344098> Cleanup: Review syslog messages
849
850 Revision 1.285 2003/08/20 01:59:06 cheshire
851 <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
852 Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
853
854 Revision 1.284 2003/08/19 22:20:00 cheshire
855 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
856 More minor refinements
857
858 Revision 1.283 2003/08/19 22:16:27 cheshire
859 Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
860
861 Revision 1.282 2003/08/19 06:48:25 cheshire
862 <rdar://problem/3376552> Guard against excessive record updates
863 Each record starts with 10 UpdateCredits.
864 Every update consumes one UpdateCredit.
865 UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
866 As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
867 When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
868
869 Revision 1.281 2003/08/19 04:49:28 cheshire
870 <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
871 1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6.
872 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
873 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
874
875 Revision 1.280 2003/08/19 02:33:36 cheshire
876 Update comments
877
878 Revision 1.279 2003/08/19 02:31:11 cheshire
879 <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
880 Final expiration queries now only mark the question for sending on the particular interface
881 pertaining to the record that's expiring.
882
883 Revision 1.278 2003/08/18 22:53:37 cheshire
884 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
885
886 Revision 1.277 2003/08/18 19:05:44 cheshire
887 <rdar://problem/3382423> UpdateRecord not working right
888 Added "newrdlength" field to hold new length of updated rdata
889
890 Revision 1.276 2003/08/16 03:39:00 cheshire
891 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
892
893 Revision 1.275 2003/08/16 02:51:27 cheshire
894 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
895 Don't try to compute namehash etc, until *after* validating the name
896
897 Revision 1.274 2003/08/16 01:12:40 cheshire
898 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
899 Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
900 simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
901 and in the process of copying it, the C compiler may run off the end of the rdata object into
902 unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
903 call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
904
905 Revision 1.273 2003/08/15 20:16:02 cheshire
906 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
907 We want to avoid touching the rdata pages, so we don't page them in.
908 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
909 Moved this from the RData to the ResourceRecord object.
910 2. To avoid unnecessarily touching the rdata just to compare it,
911 compute a hash of the rdata and store the hash in the ResourceRecord object.
912
913 Revision 1.272 2003/08/14 19:29:04 cheshire
914 <rdar://problem/3378473> Include cache records in SIGINFO output
915 Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
916
917 Revision 1.271 2003/08/14 02:17:05 cheshire
918 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
919
920 Revision 1.270 2003/08/13 17:07:28 ksekar
921 <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
922 Added check to result of mDNS_Register() before linking extra record into list.
923
924 Revision 1.269 2003/08/12 19:56:23 cheshire
925 Update to APSL 2.0
926
927 Revision 1.268 2003/08/12 15:01:10 cheshire
928 Add comments
929
930 Revision 1.267 2003/08/12 14:59:27 cheshire
931 <rdar://problem/3374490> Rate-limiting blocks some legitimate responses
932 When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
933 whether to suppress the response, also check LastMCInterface to see if it matches.
934
935 Revision 1.266 2003/08/12 12:47:16 cheshire
936 In mDNSCoreMachineSleep debugf message, display value of m->timenow
937
938 Revision 1.265 2003/08/11 20:04:28 cheshire
939 <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
940
941 Revision 1.264 2003/08/09 00:55:02 cheshire
942 <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
943 Don't scan the whole cache after every packet.
944
945 Revision 1.263 2003/08/09 00:35:29 cheshire
946 Moved AnswerNewQuestion() later in the file, in preparation for next checkin
947
948 Revision 1.262 2003/08/08 19:50:33 cheshire
949 <rdar://problem/3370332> Remove "Cache size now xxx" messages
950
951 Revision 1.261 2003/08/08 19:18:45 cheshire
952 <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
953
954 Revision 1.260 2003/08/08 18:55:48 cheshire
955 <rdar://problem/3370365> Guard against time going backwards
956
957 Revision 1.259 2003/08/08 18:36:04 cheshire
958 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
959
960 Revision 1.258 2003/08/08 16:22:05 cheshire
961 <rdar://problem/3335473> Need to check validity of TXT (and other) records
962 Remove unneeded LogMsg
963
964 Revision 1.257 2003/08/07 01:41:08 cheshire
965 <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
966
967 Revision 1.256 2003/08/06 23:25:51 cheshire
968 <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
969
970 Revision 1.255 2003/08/06 23:22:50 cheshire
971 Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
972
973 Revision 1.254 2003/08/06 21:33:39 cheshire
974 Fix compiler warnings on PocketPC 2003 (Windows CE)
975
976 Revision 1.253 2003/08/06 20:43:57 cheshire
977 <rdar://problem/3335473> Need to check validity of TXT (and other) records
978 Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
979
980 Revision 1.252 2003/08/06 20:35:47 cheshire
981 Enhance debugging routine GetRRDisplayString() so it can also be used to display
982 other RDataBody objects, not just the one currently attached the given ResourceRecord
983
984 Revision 1.251 2003/08/06 19:07:34 cheshire
985 <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
986 Was checking LastAPTime instead of LastMCTime
987
988 Revision 1.250 2003/08/06 19:01:55 cheshire
989 Update comments
990
991 Revision 1.249 2003/08/06 00:13:28 cheshire
992 Tidy up debugf messages
993
994 Revision 1.248 2003/08/05 22:20:15 cheshire
995 <rdar://problem/3330324> Need to check IP TTL on responses
996
997 Revision 1.247 2003/08/05 00:56:39 cheshire
998 <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
999
1000 Revision 1.246 2003/08/04 19:20:49 cheshire
1001 Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
1002
1003 Revision 1.245 2003/08/02 01:56:29 cheshire
1004 For debugging: log message if we ever get more than one question in a truncated packet
1005
1006 Revision 1.244 2003/08/01 23:55:32 cheshire
1007 Fix for compiler warnings on Windows, submitted by Bob Bradley
1008
1009 Revision 1.243 2003/07/25 02:26:09 cheshire
1010 Typo: FIxed missing semicolon
1011
1012 Revision 1.242 2003/07/25 01:18:41 cheshire
1013 Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1014
1015 Revision 1.241 2003/07/23 21:03:42 cheshire
1016 Only show "Found record..." debugf message in verbose mode
1017
1018 Revision 1.240 2003/07/23 21:01:11 cheshire
1019 <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
1020 After sending a packet, suppress further sending for the next 100ms.
1021
1022 Revision 1.239 2003/07/22 01:30:05 cheshire
1023 <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
1024
1025 Revision 1.238 2003/07/22 00:10:20 cheshire
1026 <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1027
1028 Revision 1.237 2003/07/19 03:23:13 cheshire
1029 <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1030
1031 Revision 1.236 2003/07/19 03:04:55 cheshire
1032 Fix warnings; some debugf message improvements
1033
1034 Revision 1.235 2003/07/19 00:03:32 cheshire
1035 <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
1036 ScheduleNextTask is quite an expensive operation.
1037 We don't need to do all that work after receiving a no-op packet that didn't change our state.
1038
1039 Revision 1.234 2003/07/18 23:52:11 cheshire
1040 To improve consistency of field naming, global search-and-replace:
1041 NextProbeTime -> NextScheduledProbe
1042 NextResponseTime -> NextScheduledResponse
1043
1044 Revision 1.233 2003/07/18 00:29:59 cheshire
1045 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
1046
1047 Revision 1.232 2003/07/18 00:11:38 cheshire
1048 Add extra case to switch statements to handle HINFO data for Get, Put and Display
1049 (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
1050
1051 Revision 1.231 2003/07/18 00:06:37 cheshire
1052 To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
1053
1054 Revision 1.230 2003/07/17 18:16:54 cheshire
1055 <rdar://problem/3319418> Services always in a state of flux
1056 In preparation for working on this, made some debugf messages a little more selective
1057
1058 Revision 1.229 2003/07/17 17:35:04 cheshire
1059 <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1060
1061 Revision 1.228 2003/07/16 20:50:27 cheshire
1062 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1063
1064 Revision 1.227 2003/07/16 05:01:36 cheshire
1065 Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
1066 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1067
1068 Revision 1.226 2003/07/16 04:51:44 cheshire
1069 Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1070
1071 Revision 1.225 2003/07/16 04:46:41 cheshire
1072 Minor wording cleanup: The correct DNS term is "response", not "reply"
1073
1074 Revision 1.224 2003/07/16 04:39:02 cheshire
1075 Textual cleanup (no change to functionality):
1076 Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
1077
1078 Revision 1.223 2003/07/16 00:09:22 cheshire
1079 Textual cleanup (no change to functionality):
1080 Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
1081 replace with macro "TicksTTL(rr)"
1082 Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
1083 replaced with macro "RRExpireTime(rr)"
1084
1085 Revision 1.222 2003/07/15 23:40:46 cheshire
1086 Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1087
1088 Revision 1.221 2003/07/15 22:17:56 cheshire
1089 <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1090
1091 Revision 1.220 2003/07/15 02:12:51 cheshire
1092 Slight tidy-up of debugf messages and comments
1093
1094 Revision 1.219 2003/07/15 01:55:12 cheshire
1095 <rdar://problem/3315777> Need to implement service registration with subtypes
1096
1097 Revision 1.218 2003/07/14 16:26:06 cheshire
1098 <rdar://problem/3324795> Duplicate query suppression not working right
1099 Refinement: Don't record DS information for a question in the first quarter second
1100 right after we send it -- in the case where a question happens to be accelerated by
1101 the maximum allowed amount, we don't want it to then be suppressed because the previous
1102 time *we* sent that question falls (just) within the valid duplicate suppression window.
1103
1104 Revision 1.217 2003/07/13 04:43:53 cheshire
1105 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1106 Minor refinement: No need to make address query broader than the original SRV query that provoked it
1107
1108 Revision 1.216 2003/07/13 03:13:17 cheshire
1109 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1110 If we get an identical SRV on a second interface, convert address queries to non-specific
1111
1112 Revision 1.215 2003/07/13 02:28:00 cheshire
1113 <rdar://problem/3325166> SendResponses didn't all its responses
1114 Delete all references to RRInterfaceActive -- it's now superfluous
1115
1116 Revision 1.214 2003/07/13 01:47:53 cheshire
1117 Fix one error and one warning in the Windows build
1118
1119 Revision 1.213 2003/07/12 04:25:48 cheshire
1120 Fix minor signed/unsigned warnings
1121
1122 Revision 1.212 2003/07/12 01:59:11 cheshire
1123 Minor changes to debugf messages
1124
1125 Revision 1.211 2003/07/12 01:47:01 cheshire
1126 <rdar://problem/3324495> After name conflict, appended number should be higher than previous number
1127
1128 Revision 1.210 2003/07/12 01:43:28 cheshire
1129 <rdar://problem/3324795> Duplicate query suppression not working right
1130 The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
1131 The code was incorrectly using the last query time plus one-half the query interval.
1132 This was only correct in the case where query acceleration was not in effect.
1133
1134 Revision 1.209 2003/07/12 01:27:50 cheshire
1135 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1136 Fix missing "-1" in RemoveLabelSuffix()
1137
1138 Revision 1.208 2003/07/11 01:32:38 cheshire
1139 Syntactic cleanup (no change to funcationality): Now that we only have one host name,
1140 rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
1141
1142 Revision 1.207 2003/07/11 01:28:00 cheshire
1143 <rdar://problem/3161289> No more local.arpa
1144
1145 Revision 1.206 2003/07/11 00:45:02 cheshire
1146 <rdar://problem/3321909> Client should get callback confirming successful host name registration
1147
1148 Revision 1.205 2003/07/11 00:40:18 cheshire
1149 Tidy up debug message in HostNameCallback()
1150
1151 Revision 1.204 2003/07/11 00:20:32 cheshire
1152 <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1153
1154 Revision 1.203 2003/07/10 23:53:41 cheshire
1155 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1156
1157 Revision 1.202 2003/07/04 02:23:20 cheshire
1158 <rdar://problem/3311955> Responder too aggressive at flushing stale data
1159 Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
1160
1161 Revision 1.201 2003/07/04 01:09:41 cheshire
1162 <rdar://problem/3315775> Need to implement subtype queries
1163 Modified ConstructServiceName() to allow three-part service types
1164
1165 Revision 1.200 2003/07/03 23:55:26 cheshire
1166 Minor change to wording of syslog warning messages
1167
1168 Revision 1.199 2003/07/03 23:51:13 cheshire
1169 <rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
1170 Added more detailed debugging information
1171
1172 Revision 1.198 2003/07/03 22:19:30 cheshire
1173 <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
1174 Make exception to allow _tivo_servemedia._tcp.
1175
1176 Revision 1.197 2003/07/02 22:33:05 cheshire
1177 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1178 Minor refinements:
1179 When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
1180 Allow cache to grow to 512 records before considering it a potential denial-of-service attack
1181
1182 Revision 1.196 2003/07/02 21:19:45 cheshire
1183 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
1184
1185 Revision 1.195 2003/07/02 19:56:58 cheshire
1186 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1187 Minor refinement: m->rrcache_active was not being decremented when
1188 an active record was deleted because its TTL expired
1189
1190 Revision 1.194 2003/07/02 18:47:40 cheshire
1191 Minor wording change to log messages
1192
1193 Revision 1.193 2003/07/02 02:44:13 cheshire
1194 Fix warning in non-debug build
1195
1196 Revision 1.192 2003/07/02 02:41:23 cheshire
1197 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1198
1199 Revision 1.191 2003/07/02 02:30:51 cheshire
1200 HashSlot() returns an array index. It can't be negative; hence it should not be signed.
1201
1202 Revision 1.190 2003/06/27 00:03:05 vlubet
1203 <rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1204
1205 Revision 1.189 2003/06/11 19:24:03 cheshire
1206 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1207 Slight refinement to previous checkin
1208
1209 Revision 1.188 2003/06/10 20:33:28 cheshire
1210 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1211
1212 Revision 1.187 2003/06/10 04:30:44 cheshire
1213 <rdar://problem/3286234> Need to re-probe/re-announce on configuration change
1214 Only interface-specific records were re-probing and re-announcing, not non-specific records.
1215
1216 Revision 1.186 2003/06/10 04:24:39 cheshire
1217 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1218 Some additional refinements:
1219 Don't try to do this for unicast-response queries
1220 better tracking of Qs and KAs in multi-packet KA lists
1221
1222 Revision 1.185 2003/06/10 03:52:49 cheshire
1223 Update comments and debug messages
1224
1225 Revision 1.184 2003/06/10 02:26:39 cheshire
1226 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1227 Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
1228
1229 Revision 1.183 2003/06/09 18:53:13 cheshire
1230 Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1231
1232 Revision 1.182 2003/06/09 18:38:42 cheshire
1233 <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
1234 Only issue a correction if the TTL in the proxy packet is less than half the correct value.
1235
1236 Revision 1.181 2003/06/07 06:45:05 cheshire
1237 <rdar://problem/3283666> No need for multiple machines to all be sending the same queries
1238
1239 Revision 1.180 2003/06/07 06:31:07 cheshire
1240 Create little four-line helper function "FindIdenticalRecordInCache()"
1241
1242 Revision 1.179 2003/06/07 06:28:13 cheshire
1243 For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1244
1245 Revision 1.178 2003/06/07 06:25:12 cheshire
1246 Update some comments
1247
1248 Revision 1.177 2003/06/07 04:50:53 cheshire
1249 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1250
1251 Revision 1.176 2003/06/07 04:33:26 cheshire
1252 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1253 Minor change: Increment/decrement logic for q->CurrentAnswers should be in
1254 CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
1255
1256 Revision 1.175 2003/06/07 04:11:52 cheshire
1257 Minor changes to comments and debug messages
1258
1259 Revision 1.174 2003/06/07 01:46:38 cheshire
1260 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1261
1262 Revision 1.173 2003/06/07 01:22:13 cheshire
1263 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1264
1265 Revision 1.172 2003/06/07 00:59:42 cheshire
1266 <rdar://problem/3283454> Need some randomness to spread queries on the network
1267
1268 Revision 1.171 2003/06/06 21:41:10 cheshire
1269 For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
1270
1271 Revision 1.170 2003/06/06 21:38:55 cheshire
1272 Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
1273 already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
1274
1275 Revision 1.169 2003/06/06 21:35:55 cheshire
1276 Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
1277 (the target is a domain name, but not necessarily a host name)
1278
1279 Revision 1.168 2003/06/06 21:33:31 cheshire
1280 Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
1281
1282 Revision 1.167 2003/06/06 21:30:42 cheshire
1283 <rdar://problem/3282962> Don't delay queries for shared record types
1284
1285 Revision 1.166 2003/06/06 17:20:14 cheshire
1286 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
1287 (Global search-and-replace; no functional change to code execution.)
1288
1289 Revision 1.165 2003/06/04 02:53:21 cheshire
1290 Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1291
1292 Revision 1.164 2003/06/04 01:25:33 cheshire
1293 <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
1294 Display time interval between first and subsequent queries
1295
1296 Revision 1.163 2003/06/03 19:58:14 cheshire
1297 <rdar://problem/3277665> mDNS_DeregisterService() fixes:
1298 When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
1299 Guard against a couple of possible mDNS_DeregisterService() race conditions.
1300
1301 Revision 1.162 2003/06/03 19:30:39 cheshire
1302 Minor addition refinements for
1303 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1304
1305 Revision 1.161 2003/06/03 18:29:03 cheshire
1306 Minor changes to comments and debugf() messages
1307
1308 Revision 1.160 2003/06/03 05:02:16 cheshire
1309 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1310
1311 Revision 1.159 2003/06/03 03:31:57 cheshire
1312 <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
1313
1314 Revision 1.158 2003/06/02 22:57:09 cheshire
1315 Minor clarifying changes to comments and log messages;
1316 IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
1317
1318 Revision 1.157 2003/05/31 00:09:49 cheshire
1319 <rdar://problem/3274862> Add ability to discover what services are on a network
1320
1321 Revision 1.156 2003/05/30 23:56:49 cheshire
1322 <rdar://problem/3274847> Crash after error in mDNS_RegisterService()
1323 Need to set "sr->Extras = mDNSNULL" before returning
1324
1325 Revision 1.155 2003/05/30 23:48:00 cheshire
1326 <rdar://problem/3274832> Announcements not properly grouped
1327 Due to inconsistent setting of rr->LastAPTime at different places in the
1328 code, announcements were not properly grouped into a single packet.
1329 Fixed by creating a single routine called InitializeLastAPTime().
1330
1331 Revision 1.154 2003/05/30 23:38:14 cheshire
1332 <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
1333 Wrote buffer[32] where it should have said buffer[64]
1334
1335 Revision 1.153 2003/05/30 19:10:56 cheshire
1336 <rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1337
1338 Revision 1.152 2003/05/29 22:39:16 cheshire
1339 <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
1340
1341 Revision 1.151 2003/05/29 06:35:42 cheshire
1342 <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1343
1344 Revision 1.150 2003/05/29 06:25:45 cheshire
1345 <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1346
1347 Revision 1.149 2003/05/29 06:18:39 cheshire
1348 <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1349
1350 Revision 1.148 2003/05/29 06:11:34 cheshire
1351 <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
1352
1353 Revision 1.147 2003/05/29 06:01:18 cheshire
1354 Change some debugf() calls to LogMsg() calls to help with debugging
1355
1356 Revision 1.146 2003/05/28 21:00:44 cheshire
1357 Re-enable "immediate answer burst" debugf message
1358
1359 Revision 1.145 2003/05/28 20:57:44 cheshire
1360 <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
1361 known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
1362 version of mDNSResponder, so for now we should suppress this warning message.
1363
1364 Revision 1.144 2003/05/28 18:05:12 cheshire
1365 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1366 Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
1367
1368 Revision 1.143 2003/05/28 04:31:29 cheshire
1369 <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1370
1371 Revision 1.142 2003/05/28 03:13:07 cheshire
1372 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1373 Require that the transport protocol be _udp or _tcp
1374
1375 Revision 1.141 2003/05/28 02:19:12 cheshire
1376 <rdar://problem/3270634> Misleading messages generated by iChat
1377 Better fix: Only generate the log message for queries where the TC bit is set.
1378
1379 Revision 1.140 2003/05/28 01:55:24 cheshire
1380 Minor change to log messages
1381
1382 Revision 1.139 2003/05/28 01:52:51 cheshire
1383 <rdar://problem/3270634> Misleading messages generated by iChat
1384
1385 Revision 1.138 2003/05/27 22:35:00 cheshire
1386 <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1387
1388 Revision 1.137 2003/05/27 20:04:33 cheshire
1389 <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1390
1391 Revision 1.136 2003/05/27 18:50:07 cheshire
1392 <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
1393
1394 Revision 1.135 2003/05/26 04:57:28 cheshire
1395 <rdar://problem/3268953> Delay queries when there are already answers in the cache
1396
1397 Revision 1.134 2003/05/26 04:54:54 cheshire
1398 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1399 Accidentally deleted '%' case from the switch statement
1400
1401 Revision 1.133 2003/05/26 03:21:27 cheshire
1402 Tidy up address structure naming:
1403 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
1404 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
1405 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
1406
1407 Revision 1.132 2003/05/26 03:01:26 cheshire
1408 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1409
1410 Revision 1.131 2003/05/26 00:42:05 cheshire
1411 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1412
1413 Revision 1.130 2003/05/24 16:39:48 cheshire
1414 <rdar://problem/3268631> SendResponses also needs to handle multihoming better
1415
1416 Revision 1.129 2003/05/23 02:15:37 cheshire
1417 Fixed misleading use of the term "duplicate suppression" where it should have
1418 said "known answer suppression". (Duplicate answer suppression is something
1419 different, and duplicate question suppression is yet another thing, so the use
1420 of the completely vague term "duplicate suppression" was particularly bad.)
1421
1422 Revision 1.128 2003/05/23 01:55:13 cheshire
1423 <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
1424
1425 Revision 1.127 2003/05/23 01:02:15 ksekar
1426 <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
1427
1428 Revision 1.126 2003/05/22 02:29:22 cheshire
1429 <rdar://problem/2984918> SendQueries needs to handle multihoming better
1430 Complete rewrite of SendQueries. Works much better now :-)
1431
1432 Revision 1.125 2003/05/22 01:50:45 cheshire
1433 Fix warnings, and improve log messages
1434
1435 Revision 1.124 2003/05/22 01:41:50 cheshire
1436 DiscardDeregistrations doesn't need InterfaceID parameter
1437
1438 Revision 1.123 2003/05/22 01:38:55 cheshire
1439 Change bracketing of #pragma mark
1440
1441 Revision 1.122 2003/05/21 19:59:04 cheshire
1442 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1443 Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
1444
1445 Revision 1.121 2003/05/21 17:54:07 ksekar
1446 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1447 New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
1448
1449 Revision 1.120 2003/05/19 22:14:14 ksekar
1450 <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
1451
1452 Revision 1.119 2003/05/16 01:34:10 cheshire
1453 Fix some warnings
1454
1455 Revision 1.118 2003/05/14 18:48:40 cheshire
1456 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1457 More minor refinements:
1458 mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
1459 mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
1460
1461 Revision 1.117 2003/05/14 07:08:36 cheshire
1462 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1463 Previously, when there was any network configuration change, mDNSResponder
1464 would tear down the entire list of active interfaces and start again.
1465 That was very disruptive, and caused the entire cache to be flushed,
1466 and caused lots of extra network traffic. Now it only removes interfaces
1467 that have really gone, and only adds new ones that weren't there before.
1468
1469 Revision 1.116 2003/05/14 06:51:56 cheshire
1470 <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
1471
1472 Revision 1.115 2003/05/14 06:44:31 cheshire
1473 Improve debugging message
1474
1475 Revision 1.114 2003/05/07 01:47:03 cheshire
1476 <rdar://problem/3250330> Also protect against NULL domainlabels
1477
1478 Revision 1.113 2003/05/07 00:28:18 cheshire
1479 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1480
1481 Revision 1.112 2003/05/06 00:00:46 cheshire
1482 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1483
1484 Revision 1.111 2003/05/05 23:42:08 cheshire
1485 <rdar://problem/3245631> Resolves never succeed
1486 Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
1487 instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
1488
1489 Revision 1.110 2003/04/30 21:09:59 cheshire
1490 <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
1491
1492 Revision 1.109 2003/04/26 02:41:56 cheshire
1493 <rdar://problem/3241281> Change timenow from a local variable to a structure member
1494
1495 Revision 1.108 2003/04/25 01:45:56 cheshire
1496 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1497
1498 Revision 1.107 2003/04/25 00:41:31 cheshire
1499 <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1500
1501 Revision 1.106 2003/04/22 03:14:45 cheshire
1502 <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1503
1504 Revision 1.105 2003/04/22 01:07:43 cheshire
1505 <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
1506 If TTL parameter is zero, leave record TTL unchanged
1507
1508 Revision 1.104 2003/04/21 19:15:52 cheshire
1509 Fix some compiler warnings
1510
1511 Revision 1.103 2003/04/19 02:26:35 cheshire
1512 <rdar://problem/3233804> Incorrect goodbye packet after conflict
1513
1514 Revision 1.102 2003/04/17 03:06:28 cheshire
1515 <rdar://problem/3231321> No need to query again when a service goes away
1516 Set UnansweredQueries to 2 when receiving a "goodbye" packet
1517
1518 Revision 1.101 2003/04/15 20:58:31 jgraessl
1519 <rdar://problem/3229014> Added a hash to lookup records in the cache.
1520
1521 Revision 1.100 2003/04/15 18:53:14 cheshire
1522 <rdar://problem/3229064> Bug in ScheduleNextTask
1523 mDNS.c 1.94 incorrectly combined two "if" statements into one.
1524
1525 Revision 1.99 2003/04/15 18:09:13 jgraessl
1526 <rdar://problem/3228892>
1527 Reviewed by: Stuart Cheshire
1528 Added code to keep track of when the next cache item will expire so we can
1529 call TidyRRCache only when necessary.
1530
1531 Revision 1.98 2003/04/03 03:43:55 cheshire
1532 <rdar://problem/3216837> Off-by-one error in probe rate limiting
1533
1534 Revision 1.97 2003/04/02 01:48:17 cheshire
1535 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1536 Additional fix pointed out by Josh:
1537 Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
1538
1539 Revision 1.96 2003/04/01 23:58:55 cheshire
1540 Minor comment changes
1541
1542 Revision 1.95 2003/04/01 23:46:05 cheshire
1543 <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
1544 mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
1545 to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
1546 window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
1547 FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
1548
1549 Revision 1.94 2003/03/29 01:55:19 cheshire
1550 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1551 Solution: Major cleanup of packet timing and conflict handling rules
1552
1553 Revision 1.93 2003/03/28 01:54:36 cheshire
1554 Minor tidyup of IPv6 (AAAA) code
1555
1556 Revision 1.92 2003/03/27 03:30:55 cheshire
1557 <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
1558 Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
1559 Fixes:
1560 1. Make mDNS_DeregisterInterface() safe to call from a callback
1561 2. Make HostNameCallback() use DeadvertiseInterface() instead
1562 (it never really needed to deregister the interface at all)
1563
1564 Revision 1.91 2003/03/15 04:40:36 cheshire
1565 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1566
1567 Revision 1.90 2003/03/14 20:26:37 cheshire
1568 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1569
1570 Revision 1.89 2003/03/12 19:57:50 cheshire
1571 Fixed typo in debug message
1572
1573 Revision 1.88 2003/03/12 00:17:44 cheshire
1574 <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
1575
1576 Revision 1.87 2003/03/11 01:27:20 cheshire
1577 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1578
1579 Revision 1.86 2003/03/06 20:44:33 cheshire
1580 Comment tidyup
1581
1582 Revision 1.85 2003/03/05 03:38:35 cheshire
1583 <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
1584 Fixed by leaving client in list after conflict, until client explicitly deallocates
1585
1586 Revision 1.84 2003/03/05 01:27:30 cheshire
1587 <rdar://problem/3185482> Different TTL for multicast versus unicast responses
1588 When building unicast responses, record TTLs are capped to 10 seconds
1589
1590 Revision 1.83 2003/03/04 23:48:52 cheshire
1591 <rdar://problem/3188865> Double probes after wake from sleep
1592 Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1593
1594 Revision 1.82 2003/03/04 23:38:29 cheshire
1595 <rdar://problem/3099194> mDNSResponder needs performance improvements
1596 Only set rr->CRActiveQuestion to point to the
1597 currently active representative of a question set
1598
1599 Revision 1.81 2003/02/21 03:35:34 cheshire
1600 <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
1601
1602 Revision 1.80 2003/02/21 02:47:53 cheshire
1603 <rdar://problem/3099194> mDNSResponder needs performance improvements
1604 Several places in the code were calling CacheRRActive(), which searched the entire
1605 question list every time, to see if this cache resource record answers any question.
1606 Instead, we now have a field "CRActiveQuestion" in the resource record structure
1607
1608 Revision 1.79 2003/02/21 01:54:07 cheshire
1609 <rdar://problem/3099194> mDNSResponder needs performance improvements
1610 Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1611
1612 Revision 1.78 2003/02/20 06:48:32 cheshire
1613 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
1614 Reviewed by: Josh Graessley, Bob Bradley
1615
1616 Revision 1.77 2003/01/31 03:35:59 cheshire
1617 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1618 When there were *two* active questions in the list, they were incorrectly
1619 finding *each other* and *both* being marked as duplicates of another question
1620
1621 Revision 1.76 2003/01/29 02:46:37 cheshire
1622 Fix for IPv6:
1623 A physical interface is identified solely by its InterfaceID (not by IP and type).
1624 On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
1625 In cases where the requested outbound protocol (v4 or v6) is not supported on
1626 that InterfaceID, the platform support layer should simply discard that packet.
1627
1628 Revision 1.75 2003/01/29 01:47:40 cheshire
1629 Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1630
1631 Revision 1.74 2003/01/28 05:26:25 cheshire
1632 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1633 Add 'Active' flag for interfaces
1634
1635 Revision 1.73 2003/01/28 03:45:12 cheshire
1636 Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1637
1638 Revision 1.72 2003/01/28 01:49:48 cheshire
1639 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1640 FindDuplicateQuestion() was incorrectly finding the question itself in the list,
1641 and incorrectly marking it as a duplicate (of itself), so that it became inactive.
1642
1643 Revision 1.71 2003/01/28 01:41:44 cheshire
1644 <rdar://problem/3153091> Race condition when network change causes bad stuff
1645 When an interface goes away, interface-specific questions on that interface become orphaned.
1646 Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
1647 Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
1648
1649 Revision 1.70 2003/01/23 19:00:20 cheshire
1650 Protect against infinite loops in mDNS_Execute
1651
1652 Revision 1.69 2003/01/21 22:56:32 jgraessl
1653 <rdar://problem/3124348> service name changes are not properly handled
1654 Submitted by: Stuart Cheshire
1655 Reviewed by: Joshua Graessley
1656 Applying changes for 3124348 to main branch. 3124348 changes went in to a
1657 branch for SU.
1658
1659 Revision 1.68 2003/01/17 04:09:27 cheshire
1660 <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
1661
1662 Revision 1.67 2003/01/17 03:56:45 cheshire
1663 Default 24-hour TTL is far too long. Changing to two hours.
1664
1665 Revision 1.66 2003/01/13 23:49:41 jgraessl
1666 Merged changes for the following fixes in to top of tree:
1667 <rdar://problem/3086540> computer name changes not handled properly
1668 <rdar://problem/3124348> service name changes are not properly handled
1669 <rdar://problem/3124352> announcements sent in pairs, failing chattiness test
1670
1671 Revision 1.65 2002/12/23 22:13:28 jgraessl
1672 Reviewed by: Stuart Cheshire
1673 Initial IPv6 support for mDNSResponder.
1674
1675 Revision 1.64 2002/11/26 20:49:06 cheshire
1676 <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
1677
1678 Revision 1.63 2002/09/21 20:44:49 zarzycki
1679 Added APSL info
1680
1681 Revision 1.62 2002/09/20 03:25:37 cheshire
1682 Fix some compiler warnings
1683
1684 Revision 1.61 2002/09/20 01:05:24 cheshire
1685 Don't kill the Extras list in mDNS_DeregisterService()
1686
1687 Revision 1.60 2002/09/19 23:47:35 cheshire
1688 Added mDNS_RegisterNoSuchService() function for assertion of non-existence
1689 of a particular named service
1690
1691 Revision 1.59 2002/09/19 21:25:34 cheshire
1692 mDNS_snprintf() doesn't need to be in a separate file
1693
1694 Revision 1.58 2002/09/19 04:20:43 cheshire
1695 Remove high-ascii characters that confuse some systems
1696
1697 Revision 1.57 2002/09/17 01:07:08 cheshire
1698 Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1699
1700 Revision 1.56 2002/09/16 19:44:17 cheshire
1701 Merge in license terms from Quinn's copy, in preparation for Darwin release
1702 */
1703
1704 #include "DNSCommon.h" // Defines general DNS untility routines
1705 #include "uDNS.h" // Defines entry points into unicast-specific routines
1706 // Disable certain benign warnings with Microsoft compilers
1707 #if(defined(_MSC_VER))
1708 // Disable "conditional expression is constant" warning for debug macros.
1709 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
1710 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
1711 #pragma warning(disable:4127)
1712
1713 // Disable "assignment within conditional expression".
1714 // Other compilers understand the convention that if you place the assignment expression within an extra pair
1715 // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
1716 // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
1717 // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
1718 #pragma warning(disable:4706)
1719 #endif
1720
1721 // ***************************************************************************
1722 #if COMPILER_LIKES_PRAGMA_MARK
1723 #pragma mark -
1724 #pragma mark - Program Constants
1725 #endif
1726
1727 mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
1728 mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
1729 mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
1730 mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
1731 mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
1732 mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
1733 mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
1734
1735 mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
1736 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1;
1737
1738 mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
1739
1740 #define UnicastDNSPortAsNumber 53
1741 #define MulticastDNSPortAsNumber 5353
1742 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
1743 mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
1744 mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
1745 mDNSexport const mDNSv4Addr AllDNSLinkGroupv4 = { { 224, 0, 0, 251 } };
1746 mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } };
1747 mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
1748 mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
1749
1750 mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
1751 mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
1752 mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
1753 mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
1754 mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
1755 mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
1756
1757 // Any records bigger than this are considered 'large' records
1758 #define SmallRecordLimit 1024
1759
1760 #define kMaxUpdateCredits 10
1761 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1762
1763 mDNSexport const char *const mDNS_DomainTypeNames[] =
1764 {
1765 "b._dns-sd._udp.", // Browse
1766 "db._dns-sd._udp.", // Default Browse
1767 "lb._dns-sd._udp.", // Legacy Browse
1768 "r._dns-sd._udp.", // Registration
1769 "dr._dns-sd._udp." // Default Registration
1770 };
1771
1772 #ifdef UNICAST_DISABLED
1773 #define uDNS_IsActiveQuery(q, u) mDNSfalse
1774 #endif
1775
1776 // ***************************************************************************
1777 #if COMPILER_LIKES_PRAGMA_MARK
1778 #pragma mark -
1779 #pragma mark - Specialized mDNS version of vsnprintf
1780 #endif
1781
1782 static const struct mDNSprintf_format
1783 {
1784 unsigned leftJustify : 1;
1785 unsigned forceSign : 1;
1786 unsigned zeroPad : 1;
1787 unsigned havePrecision : 1;
1788 unsigned hSize : 1;
1789 unsigned lSize : 1;
1790 char altForm;
1791 char sign; // +, - or space
1792 unsigned int fieldWidth;
1793 unsigned int precision;
1794 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1795
1796 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
1797 {
1798 mDNSu32 nwritten = 0;
1799 int c;
1800 if (buflen == 0) return(0);
1801 buflen--; // Pre-reserve one space in the buffer for the terminating null
1802 if (buflen == 0) goto exit;
1803
1804 for (c = *fmt; c != 0; c = *++fmt)
1805 {
1806 if (c != '%')
1807 {
1808 *sbuffer++ = (char)c;
1809 if (++nwritten >= buflen) goto exit;
1810 }
1811 else
1812 {
1813 unsigned int i=0, j;
1814 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
1815 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
1816 // The size needs to be enough for a 256-byte domain name plus some error text.
1817 #define mDNS_VACB_Size 300
1818 char mDNS_VACB[mDNS_VACB_Size];
1819 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
1820 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
1821 char *s = mDNS_VACB_Lim, *digits;
1822 struct mDNSprintf_format F = mDNSprintf_format_default;
1823
1824 while (1) // decode flags
1825 {
1826 c = *++fmt;
1827 if (c == '-') F.leftJustify = 1;
1828 else if (c == '+') F.forceSign = 1;
1829 else if (c == ' ') F.sign = ' ';
1830 else if (c == '#') F.altForm++;
1831 else if (c == '0') F.zeroPad = 1;
1832 else break;
1833 }
1834
1835 if (c == '*') // decode field width
1836 {
1837 int f = va_arg(arg, int);
1838 if (f < 0) { f = -f; F.leftJustify = 1; }
1839 F.fieldWidth = (unsigned int)f;
1840 c = *++fmt;
1841 }
1842 else
1843 {
1844 for (; c >= '0' && c <= '9'; c = *++fmt)
1845 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
1846 }
1847
1848 if (c == '.') // decode precision
1849 {
1850 if ((c = *++fmt) == '*')
1851 { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
1852 else for (; c >= '0' && c <= '9'; c = *++fmt)
1853 F.precision = (10 * F.precision) + (c - '0');
1854 F.havePrecision = 1;
1855 }
1856
1857 if (F.leftJustify) F.zeroPad = 0;
1858
1859 conv:
1860 switch (c) // perform appropriate conversion
1861 {
1862 unsigned long n;
1863 case 'h' : F.hSize = 1; c = *++fmt; goto conv;
1864 case 'l' : // fall through
1865 case 'L' : F.lSize = 1; c = *++fmt; goto conv;
1866 case 'd' :
1867 case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long);
1868 else n = (unsigned long)va_arg(arg, int);
1869 if (F.hSize) n = (short) n;
1870 if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
1871 else if (F.forceSign) F.sign = '+';
1872 goto decimal;
1873 case 'u' : if (F.lSize) n = va_arg(arg, unsigned long);
1874 else n = va_arg(arg, unsigned int);
1875 if (F.hSize) n = (unsigned short) n;
1876 F.sign = 0;
1877 goto decimal;
1878 decimal: if (!F.havePrecision)
1879 {
1880 if (F.zeroPad)
1881 {
1882 F.precision = F.fieldWidth;
1883 if (F.sign) --F.precision;
1884 }
1885 if (F.precision < 1) F.precision = 1;
1886 }
1887 if (F.precision > mDNS_VACB_Size - 1)
1888 F.precision = mDNS_VACB_Size - 1;
1889 for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
1890 for (; i < F.precision; i++) *--s = '0';
1891 if (F.sign) { *--s = F.sign; i++; }
1892 break;
1893
1894 case 'o' : if (F.lSize) n = va_arg(arg, unsigned long);
1895 else n = va_arg(arg, unsigned int);
1896 if (F.hSize) n = (unsigned short) n;
1897 if (!F.havePrecision)
1898 {
1899 if (F.zeroPad) F.precision = F.fieldWidth;
1900 if (F.precision < 1) F.precision = 1;
1901 }
1902 if (F.precision > mDNS_VACB_Size - 1)
1903 F.precision = mDNS_VACB_Size - 1;
1904 for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
1905 if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
1906 for (; i < F.precision; i++) *--s = '0';
1907 break;
1908
1909 case 'a' : {
1910 unsigned char *a = va_arg(arg, unsigned char *);
1911 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
1912 else
1913 {
1914 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
1915 if (F.altForm)
1916 {
1917 mDNSAddr *ip = (mDNSAddr*)a;
1918 switch (ip->type)
1919 {
1920 case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
1921 case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
1922 default: F.precision = 0; break;
1923 }
1924 }
1925 switch (F.precision)
1926 {
1927 case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
1928 a[0], a[1], a[2], a[3]); break;
1929 case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
1930 a[0], a[1], a[2], a[3], a[4], a[5]); break;
1931 case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
1932 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
1933 a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
1934 a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
1935 default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
1936 "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
1937 }
1938 }
1939 }
1940 break;
1941
1942 case 'p' : F.havePrecision = F.lSize = 1;
1943 F.precision = 8;
1944 case 'X' : digits = "0123456789ABCDEF";
1945 goto hexadecimal;
1946 case 'x' : digits = "0123456789abcdef";
1947 hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
1948 else n = va_arg(arg, unsigned int);
1949 if (F.hSize) n = (unsigned short) n;
1950 if (!F.havePrecision)
1951 {
1952 if (F.zeroPad)
1953 {
1954 F.precision = F.fieldWidth;
1955 if (F.altForm) F.precision -= 2;
1956 }
1957 if (F.precision < 1) F.precision = 1;
1958 }
1959 if (F.precision > mDNS_VACB_Size - 1)
1960 F.precision = mDNS_VACB_Size - 1;
1961 for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
1962 for (; i < F.precision; i++) *--s = '0';
1963 if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
1964 break;
1965
1966 case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
1967
1968 case 's' : s = va_arg(arg, char *);
1969 if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
1970 else switch (F.altForm)
1971 {
1972 case 0: i=0;
1973 if (!F.havePrecision) // C string
1974 while(s[i]) i++;
1975 else
1976 {
1977 while ((i < F.precision) && s[i]) i++;
1978 // Make sure we don't truncate in the middle of a UTF-8 character
1979 // If last character we got was any kind of UTF-8 multi-byte character,
1980 // then see if we have to back up.
1981 // This is not as easy as the similar checks below, because
1982 // here we can't assume it's safe to examine the *next* byte, so we
1983 // have to confine ourselves to working only backwards in the string.
1984 j = i; // Record where we got to
1985 // Now, back up until we find first non-continuation-char
1986 while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
1987 // Now s[i-1] is the first non-continuation-char
1988 // and (j-i) is the number of continuation-chars we found
1989 if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char
1990 {
1991 i--; // Tentatively eliminate this start-char as well
1992 // Now (j-i) is the number of characters we're considering eliminating.
1993 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
1994 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
1995 // (with sign extension) then the result has to be 0xFE.
1996 // If this is right, then we reinstate the tentatively eliminated bytes.
1997 if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
1998 }
1999 }
2000 break;
2001 case 1: i = (unsigned char) *s++; break; // Pascal string
2002 case 2: { // DNS label-sequence name
2003 unsigned char *a = (unsigned char *)s;
2004 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
2005 if (*a == 0) *s++ = '.'; // Special case for root DNS name
2006 while (*a)
2007 {
2008 if (*a > 63) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
2009 if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
2010 s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a);
2011 a += 1 + *a;
2012 }
2013 i = (mDNSu32)(s - mDNS_VACB);
2014 s = mDNS_VACB; // Reset s back to the start of the buffer
2015 break;
2016 }
2017 }
2018 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
2019 if (F.havePrecision && i > F.precision)
2020 { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
2021 break;
2022
2023 case 'n' : s = va_arg(arg, char *);
2024 if (F.hSize) * (short *) s = (short)nwritten;
2025 else if (F.lSize) * (long *) s = (long)nwritten;
2026 else * (int *) s = (int)nwritten;
2027 continue;
2028
2029 default: s = mDNS_VACB;
2030 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
2031
2032 case '%' : *sbuffer++ = (char)c;
2033 if (++nwritten >= buflen) goto exit;
2034 break;
2035 }
2036
2037 if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
2038 do {
2039 *sbuffer++ = ' ';
2040 if (++nwritten >= buflen) goto exit;
2041 } while (i < --F.fieldWidth);
2042
2043 // Make sure we don't truncate in the middle of a UTF-8 character.
2044 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the allowed output. If s[i] is a
2045 // UTF-8 continuation character, then we've cut a unicode character in half, so back up 'i' until s[i] is no longer a UTF-8 continuation
2046 // character. (if the input was proprly formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
2047 if (i > buflen - nwritten)
2048 { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
2049 for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
2050 nwritten += i;
2051 if (nwritten >= buflen) goto exit;
2052
2053 for (; i < F.fieldWidth; i++) // Pad on the right
2054 {
2055 *sbuffer++ = ' ';
2056 if (++nwritten >= buflen) goto exit;
2057 }
2058 }
2059 }
2060 exit:
2061 *sbuffer++ = 0;
2062 return(nwritten);
2063 }
2064
2065 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
2066 {
2067 mDNSu32 length;
2068
2069 va_list ptr;
2070 va_start(ptr,fmt);
2071 length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
2072 va_end(ptr);
2073
2074 return(length);
2075 }
2076
2077 // ***************************************************************************
2078 #if COMPILER_LIKES_PRAGMA_MARK
2079 #pragma mark -
2080 #pragma mark - General Utility Functions
2081 #endif
2082
2083 #define InitialQuestionInterval (mDNSPlatformOneSecond/2)
2084 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
2085 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
2086
2087 mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
2088 {
2089 if (ActiveQuestion(q))
2090 if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
2091 m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
2092 }
2093
2094 mDNSlocal CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
2095 {
2096 CacheGroup *cg;
2097 for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
2098 if (cg->namehash == namehash && SameDomainName(cg->name, name))
2099 break;
2100 return(cg);
2101 }
2102
2103 mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
2104 {
2105 return(CacheGroupForName(m, slot, rr->namehash, rr->name));
2106 }
2107
2108 mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
2109 {
2110 NetworkInterfaceInfo *intf;
2111
2112 if (addr->type == mDNSAddrType_IPv4)
2113 {
2114 if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue);
2115 for (intf = m->HostInterfaces; intf; intf = intf->next)
2116 if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
2117 if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
2118 return(mDNStrue);
2119 }
2120
2121 if (addr->type == mDNSAddrType_IPv6)
2122 {
2123 if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue);
2124 for (intf = m->HostInterfaces; intf; intf = intf->next)
2125 if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
2126 if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
2127 (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
2128 (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
2129 (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
2130 return(mDNStrue);
2131 }
2132
2133 return(mDNSfalse);
2134 }
2135
2136 // Set up a AuthRecord with sensible default values.
2137 // These defaults may be overwritten with new values before mDNS_Register is called
2138 mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
2139 mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
2140 {
2141 mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
2142 // Don't try to store a TTL bigger than we can represent in platform time units
2143 if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
2144 ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
2145 else if (ttl == 0) // And Zero TTL is illegal
2146 ttl = DefaultTTLforRRType(rrtype);
2147
2148 // Field Group 1: The actual information pertaining to this resource record
2149 rr->resrec.RecordType = RecordType;
2150 rr->resrec.InterfaceID = InterfaceID;
2151 rr->resrec.name = &rr->namestorage;
2152 rr->resrec.rrtype = rrtype;
2153 rr->resrec.rrclass = kDNSClass_IN;
2154 rr->resrec.rroriginalttl = ttl;
2155 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
2156 // rr->resrec.rdestimate = set in mDNS_Register_internal
2157 // rr->resrec.rdata = MUST be set by client
2158
2159 if (RDataStorage)
2160 rr->resrec.rdata = RDataStorage;
2161 else
2162 {
2163 rr->resrec.rdata = &rr->rdatastorage;
2164 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
2165 }
2166
2167 // Field Group 2: Persistent metadata for Authoritative Records
2168 rr->Additional1 = mDNSNULL;
2169 rr->Additional2 = mDNSNULL;
2170 rr->DependentOn = mDNSNULL;
2171 rr->RRSet = mDNSNULL;
2172 rr->RecordCallback = Callback;
2173 rr->RecordContext = Context;
2174
2175 rr->HostTarget = mDNSfalse;
2176 rr->AllowRemoteQuery = mDNSfalse;
2177 rr->ForceMCast = mDNSfalse;
2178
2179 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
2180
2181 rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
2182 }
2183
2184 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
2185 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
2186 mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord)
2187 {
2188 // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
2189 if (AddRecord) rr->LocalAnswer = mDNStrue;
2190 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
2191 if (q->QuestionCallback)
2192 q->QuestionCallback(m, q, &rr->resrec, AddRecord);
2193 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2194 }
2195
2196 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers to each,
2197 // stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
2198 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
2199 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
2200 mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord)
2201 {
2202 if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2203
2204 m->CurrentQuestion = m->LocalOnlyQuestions;
2205 while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
2206 {
2207 DNSQuestion *q = m->CurrentQuestion;
2208 m->CurrentQuestion = q->next;
2209 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
2210 AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
2211 }
2212
2213 // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
2214 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
2215 {
2216 m->CurrentQuestion = m->Questions;
2217 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
2218 {
2219 DNSQuestion *q = m->CurrentQuestion;
2220 m->CurrentQuestion = q->next;
2221 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
2222 AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
2223 }
2224 }
2225
2226 m->CurrentQuestion = mDNSNULL;
2227 }
2228
2229 // ***************************************************************************
2230 #if COMPILER_LIKES_PRAGMA_MARK
2231 #pragma mark -
2232 #pragma mark - Resource Record Utility Functions
2233 #endif
2234
2235 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2236
2237 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
2238 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2239 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2240 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
2241
2242 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2243 (ResourceRecordIsValidAnswer(RR) && \
2244 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2245
2246 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2247 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2248
2249 #define InitialAnnounceCount ((mDNSu8)10)
2250
2251 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
2252 // This means that because the announce interval is doubled after sending the first packet, the first
2253 // observed on-the-wire inter-packet interval between announcements is actually one second.
2254 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
2255 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
2256 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
2257 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
2258
2259 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2260 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2261 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2262
2263 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
2264 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
2265 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
2266 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
2267
2268 #define MaxUnansweredQueries 4
2269
2270 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
2271 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
2272 // TTL and rdata may differ.
2273 // This is used for cache flush management:
2274 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
2275 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
2276 mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
2277 {
2278 if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
2279 if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
2280 if (r1->InterfaceID &&
2281 r2->InterfaceID &&
2282 r1->InterfaceID != r2->InterfaceID) return(mDNSfalse);
2283 return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(r1->name, r2->name));
2284 }
2285
2286 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
2287 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
2288 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
2289 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
2290 // so a response of any type should match, even if it is not actually the type the client plans to use.
2291 mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
2292 {
2293 if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); }
2294 if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); }
2295 if (pktrr->resrec.InterfaceID &&
2296 authrr->resrec.InterfaceID &&
2297 pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse);
2298 if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
2299 return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(pktrr->resrec.name, authrr->resrec.name));
2300 }
2301
2302 // IdenticalResourceRecord returns true if two resources records have
2303 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
2304 mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
2305 {
2306 if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
2307 if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
2308 if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name)) return(mDNSfalse);
2309 return(SameRData(r1, r2));
2310 }
2311
2312 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
2313 // This is the information that the requester believes to be correct.
2314 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
2315 // This is the information that we believe to be correct.
2316 // We've already determined that we plan to give this answer on this interface
2317 // (either the record is non-specific, or it is specific to this interface)
2318 // so now we just need to check the name, type, class, rdata and TTL.
2319 mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
2320 {
2321 // If RR signature is different, or data is different, then don't suppress our answer
2322 if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
2323
2324 // If the requester's indicated TTL is less than half the real TTL,
2325 // we need to give our answer before the requester's copy expires.
2326 // If the requester's indicated TTL is at least half the real TTL,
2327 // then we can suppress our answer this time.
2328 // If the requester's indicated TTL is greater than the TTL we believe,
2329 // then that's okay, and we don't need to do anything about it.
2330 // (If two responders on the network are offering the same information,
2331 // that's okay, and if they are offering the information with different TTLs,
2332 // the one offering the lower TTL should defer to the one offering the higher TTL.)
2333 return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2);
2334 }
2335
2336 mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
2337 {
2338 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2339 {
2340 //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
2341 if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2342 m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
2343 }
2344 else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr))
2345 {
2346 if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2347 m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
2348 }
2349 }
2350
2351 mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
2352 {
2353 // To allow us to aggregate probes when a group of services are registered together,
2354 // the first probe is delayed 1/4 second. This means the common-case behaviour is:
2355 // 1/4 second wait; probe
2356 // 1/4 second wait; probe
2357 // 1/4 second wait; probe
2358 // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
2359
2360 // If we have no probe suppression time set, or it is in the past, set it now
2361 if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0)
2362 {
2363 m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique);
2364 // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
2365 if (m->SuppressProbes - m->NextScheduledProbe >= 0)
2366 m->SuppressProbes = m->NextScheduledProbe;
2367 // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
2368 if (m->SuppressProbes - m->NextScheduledQuery >= 0)
2369 m->SuppressProbes = m->NextScheduledQuery;
2370 }
2371
2372 // We announce to flush stale data from other caches. It is a reasonable assumption that any
2373 // old stale copies will probably have the same TTL we're using, so announcing longer than
2374 // this serves no purpose -- any stale copies of that record will have expired by then anyway.
2375 rr->AnnounceUntil = m->timenow + TicksTTL(rr);
2376 rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
2377 // Set LastMCTime to now, to inhibit multicast responses
2378 // (no need to send additional multicast responses when we're announcing anyway)
2379 rr->LastMCTime = m->timenow;
2380 rr->LastMCInterface = mDNSInterfaceMark;
2381
2382 // If this is a record type that's not going to probe, then delay its first announcement so that
2383 // it will go out synchronized with the first announcement for the other records that *are* probing.
2384 // This is a minor performance tweak that helps keep groups of related records synchronized together.
2385 // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
2386 // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
2387 // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
2388 // because they will meet the criterion of being at least half-way to their scheduled announcement time.
2389 if (rr->resrec.RecordType != kDNSRecordTypeUnique)
2390 rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
2391
2392 SetNextAnnounceProbeTime(m, rr);
2393 }
2394
2395 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
2396
2397 mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
2398 {
2399 domainname *target = GetRRDomainNameTarget(&rr->resrec);
2400
2401 if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype);
2402
2403 if (target && SameDomainName(target, &m->MulticastHostname))
2404 debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
2405
2406 if (target && !SameDomainName(target, &m->MulticastHostname))
2407 {
2408 AssignDomainName(target, &m->MulticastHostname);
2409 SetNewRData(&rr->resrec, mDNSNULL, 0);
2410
2411 // If we're in the middle of probing this record, we need to start again,
2412 // because changing its rdata may change the outcome of the tie-breaker.
2413 // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
2414 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
2415
2416 // If we've announced this record, we really should send a goodbye packet for the old rdata before
2417 // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
2418 // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
2419 if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
2420 debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2421
2422 rr->AnnounceCount = InitialAnnounceCount;
2423 rr->RequireGoodbye = mDNSfalse;
2424 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2425 InitializeLastAPTime(m,rr);
2426 }
2427 }
2428
2429 mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
2430 {
2431 if (!rr->Acknowledged && rr->RecordCallback)
2432 {
2433 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2434 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2435 rr->Acknowledged = mDNStrue;
2436 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
2437 rr->RecordCallback(m, rr, mStatus_NoError);
2438 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2439 }
2440 }
2441
2442 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
2443 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
2444 #define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
2445
2446 mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
2447 {
2448 domainname *target = GetRRDomainNameTarget(&rr->resrec);
2449 AuthRecord *r;
2450 AuthRecord **p = &m->ResourceRecords;
2451 AuthRecord **d = &m->DuplicateRecords;
2452
2453 mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
2454
2455 if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
2456 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
2457
2458 #ifndef UNICAST_DISABLED
2459 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
2460 rr->uDNS_info.id = zeroID;
2461 else return uDNS_RegisterRecord(m, rr);
2462 #endif
2463
2464 while (*p && *p != rr) p=&(*p)->next;
2465 while (*d && *d != rr) d=&(*d)->next;
2466 if (*d || *p)
2467 {
2468 LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2469 return(mStatus_AlreadyRegistered);
2470 }
2471
2472 if (rr->DependentOn)
2473 {
2474 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2475 rr->resrec.RecordType = kDNSRecordTypeVerified;
2476 else
2477 {
2478 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
2479 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2480 return(mStatus_Invalid);
2481 }
2482 if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
2483 {
2484 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
2485 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
2486 return(mStatus_Invalid);
2487 }
2488 }
2489
2490 // If this resource record is referencing a specific interface, make sure it exists
2491 if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
2492 {
2493 NetworkInterfaceInfo *intf;
2494 for (intf = m->HostInterfaces; intf; intf = intf->next)
2495 if (intf->InterfaceID == rr->resrec.InterfaceID) break;
2496 if (!intf)
2497 {
2498 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
2499 return(mStatus_BadReferenceErr);
2500 }
2501 }
2502
2503 rr->next = mDNSNULL;
2504
2505 // Field Group 1: Persistent metadata for Authoritative Records
2506 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2507 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2508 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2509 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2510 // rr->Callback = already set in mDNS_SetupResourceRecord
2511 // rr->Context = already set in mDNS_SetupResourceRecord
2512 // rr->RecordType = already set in mDNS_SetupResourceRecord
2513 // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2514 // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2515 // Make sure target is not uninitialized data, or we may crash writing debugging log messages
2516 if (rr->HostTarget && target) target->c[0] = 0;
2517
2518 // Field Group 2: Transient state for Authoritative Records
2519 rr->Acknowledged = mDNSfalse;
2520 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
2521 rr->AnnounceCount = InitialAnnounceCount;
2522 rr->RequireGoodbye = mDNSfalse;
2523 rr->LocalAnswer = mDNSfalse;
2524 rr->IncludeInProbe = mDNSfalse;
2525 rr->ImmedAnswer = mDNSNULL;
2526 rr->ImmedUnicast = mDNSfalse;
2527 rr->ImmedAdditional = mDNSNULL;
2528 rr->SendRNow = mDNSNULL;
2529 rr->v4Requester = zerov4Addr;
2530 rr->v6Requester = zerov6Addr;
2531 rr->NextResponse = mDNSNULL;
2532 rr->NR_AnswerTo = mDNSNULL;
2533 rr->NR_AdditionalTo = mDNSNULL;
2534 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2535 if (!rr->HostTarget) InitializeLastAPTime(m, rr);
2536 // rr->AnnounceUntil = Set for us in InitializeLastAPTime()
2537 // rr->LastAPTime = Set for us in InitializeLastAPTime()
2538 // rr->LastMCTime = Set for us in InitializeLastAPTime()
2539 // rr->LastMCInterface = Set for us in InitializeLastAPTime()
2540 rr->NewRData = mDNSNULL;
2541 rr->newrdlength = 0;
2542 rr->UpdateCallback = mDNSNULL;
2543 rr->UpdateCredits = kMaxUpdateCredits;
2544 rr->NextUpdateCredit = 0;
2545 rr->UpdateBlocked = 0;
2546
2547 // rr->resrec.interface = already set in mDNS_SetupResourceRecord
2548 // rr->resrec.name->c = MUST be set by client
2549 // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
2550 // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
2551 // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
2552 // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
2553
2554 if (rr->HostTarget)
2555 SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2556 else
2557 {
2558 rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
2559 rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
2560 }
2561
2562 if (!ValidateDomainName(rr->resrec.name))
2563 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2564
2565 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2566 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2567 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2568 if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
2569
2570 // Don't do this until *after* we've set rr->resrec.rdlength
2571 if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata))
2572 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2573
2574 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
2575 rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
2576
2577 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
2578 {
2579 // If this is supposed to be unique, make sure we don't have any name conflicts
2580 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2581 {
2582 const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
2583 for (r = m->ResourceRecords; r; r=r->next)
2584 {
2585 const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
2586 if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec))
2587 break;
2588 }
2589 if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2590 {
2591 debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2592 rr->resrec.RecordType = kDNSRecordTypeDeregistering;
2593 rr->resrec.rroriginalttl = 0;
2594 rr->ImmedAnswer = mDNSInterfaceMark;
2595 m->NextScheduledResponse = m->timenow;
2596 }
2597 }
2598 }
2599
2600 // Now that we've finished building our new record, make sure it's not identical to one we already have
2601 for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break;
2602
2603 if (r)
2604 {
2605 debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr));
2606 *d = rr;
2607 // If the previous copy of this record is already verified unique,
2608 // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
2609 // Setting ProbeCount to zero will cause SendQueries() to advance this record to
2610 // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
2611 if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified)
2612 rr->ProbeCount = 0;
2613 }
2614 else
2615 {
2616 debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
2617 if (!m->NewLocalRecords) m->NewLocalRecords = rr;
2618 *p = rr;
2619 }
2620
2621 // For records that are not going to probe, acknowledge them right away
2622 if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
2623 AcknowledgeRecord(m, rr);
2624
2625 return(mStatus_NoError);
2626 }
2627
2628 mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
2629 {
2630 m->ProbeFailTime = m->timenow;
2631 m->NumFailedProbes++;
2632 // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
2633 // If a bunch of hosts have all been configured with the same name, then they'll all
2634 // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
2635 // up to name-10. After that they'll start adding random increments in the range 1-100,
2636 // so they're more likely to branch out in the available namespace and settle on a set of
2637 // unique names quickly. If after five more tries the host is still conflicting, then we
2638 // may have a serious problem, so we start rate-limiting so we don't melt down the network.
2639 if (m->NumFailedProbes >= 15)
2640 {
2641 m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
2642 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
2643 m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2644 }
2645 }
2646
2647 mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr)
2648 {
2649 RData *OldRData = rr->resrec.rdata;
2650 SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata
2651 rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
2652 if (rr->UpdateCallback)
2653 rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
2654 }
2655
2656 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
2657 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
2658 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
2659 typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type;
2660
2661 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
2662 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2663 mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
2664 {
2665 AuthRecord *r2;
2666 mDNSu8 RecordType = rr->resrec.RecordType;
2667 AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
2668
2669 #ifndef UNICAST_DISABLED
2670 if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)))
2671 return uDNS_DeregisterRecord(m, rr);
2672 #endif
2673
2674 while (*p && *p != rr) p=&(*p)->next;
2675
2676 if (*p)
2677 {
2678 // We found our record on the main list. See if there are any duplicates that need special handling.
2679 if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment
2680 {
2681 // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
2682 // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
2683 for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF;
2684 }
2685 else
2686 {
2687 // Before we delete the record (and potentially send a goodbye packet)
2688 // first see if we have a record on the duplicate list ready to take over from it.
2689 AuthRecord **d = &m->DuplicateRecords;
2690 while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next;
2691 if (*d)
2692 {
2693 AuthRecord *dup = *d;
2694 debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2695 *d = dup->next; // Cut replacement record from DuplicateRecords list
2696 dup->next = rr->next; // And then...
2697 rr->next = dup; // ... splice it in right after the record we're about to delete
2698 dup->resrec.RecordType = rr->resrec.RecordType;
2699 dup->ProbeCount = rr->ProbeCount;
2700 dup->AnnounceCount = rr->AnnounceCount;
2701 dup->RequireGoodbye = rr->RequireGoodbye;
2702 dup->ImmedAnswer = rr->ImmedAnswer;
2703 dup->ImmedUnicast = rr->ImmedUnicast;
2704 dup->ImmedAdditional = rr->ImmedAdditional;
2705 dup->v4Requester = rr->v4Requester;
2706 dup->v6Requester = rr->v6Requester;
2707 dup->ThisAPInterval = rr->ThisAPInterval;
2708 dup->AnnounceUntil = rr->AnnounceUntil;
2709 dup->LastAPTime = rr->LastAPTime;
2710 dup->LastMCTime = rr->LastMCTime;
2711 dup->LastMCInterface = rr->LastMCInterface;
2712 rr->RequireGoodbye = mDNSfalse;
2713 }
2714 }
2715 }
2716 else
2717 {
2718 // We didn't find our record on the main list; try the DuplicateRecords list instead.
2719 p = &m->DuplicateRecords;
2720 while (*p && *p != rr) p=&(*p)->next;
2721 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
2722 if (*p) rr->RequireGoodbye = mDNSfalse;
2723 if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2724 }
2725
2726 if (!*p)
2727 {
2728 // No need to log an error message if we already know this is a potentially repeated deregistration
2729 if (drt != mDNS_Dereg_repeat)
2730 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2731 return(mStatus_BadReferenceErr);
2732 }
2733
2734 // If this is a shared record and we've announced it at least once,
2735 // we need to retract that announcement before we delete the record
2736 if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
2737 {
2738 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2739 rr->resrec.RecordType = kDNSRecordTypeDeregistering;
2740 rr->resrec.rroriginalttl = 0;
2741 rr->ImmedAnswer = mDNSInterfaceMark;
2742 if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
2743 m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
2744 }
2745 else
2746 {
2747 *p = rr->next; // Cut this record from the list
2748 // If someone is about to look at this, bump the pointer forward
2749 if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
2750 if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
2751 rr->next = mDNSNULL;
2752
2753 if (RecordType == kDNSRecordTypeUnregistered)
2754 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
2755 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2756 else if (RecordType == kDNSRecordTypeDeregistering)
2757 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
2758 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2759 else
2760 {
2761 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2762 rr->resrec.RecordType = kDNSRecordTypeUnregistered;
2763 }
2764
2765 if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared)
2766 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
2767 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2768
2769 // If we have an update queued up which never executed, give the client a chance to free that memory
2770 if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client
2771
2772 if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse);
2773
2774 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2775 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2776 // In this case the likely client action to the mStatus_MemFree message is to free the memory,
2777 // so any attempt to touch rr after this is likely to lead to a crash.
2778 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
2779 if (drt != mDNS_Dereg_conflict)
2780 {
2781 if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this
2782 }
2783 else
2784 {
2785 RecordProbeFailure(m, rr);
2786 if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this
2787 // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
2788 // Note that with all the client callbacks going on, by the time we get here all the
2789 // records we marked may have been explicitly deregistered by the client anyway.
2790 r2 = m->DuplicateRecords;
2791 while (r2)
2792 {
2793 if (r2->ProbeCount != 0xFF) r2 = r2->next;
2794 else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; }
2795 }
2796 }
2797 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2798 }
2799 return(mStatus_NoError);
2800 }
2801
2802 // ***************************************************************************
2803 #if COMPILER_LIKES_PRAGMA_MARK
2804 #pragma mark -
2805 #pragma mark -
2806 #pragma mark - Packet Sending Functions
2807 #endif
2808
2809 mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add)
2810 {
2811 if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse)
2812 {
2813 **nrpp = rr;
2814 // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
2815 // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
2816 // The referenced record will definitely be acceptable (by recursive application of this rule)
2817 if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo;
2818 rr->NR_AdditionalTo = add;
2819 *nrpp = &rr->NextResponse;
2820 }
2821 debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2822 }
2823
2824 mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID)
2825 {
2826 AuthRecord *rr, *rr2;
2827 for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put
2828 {
2829 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2830 // later in the "for" loop, and we will follow further "additional" links then.)
2831 if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID))
2832 AddRecordToResponseList(nrpp, rr->Additional1, rr);
2833
2834 if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID))
2835 AddRecordToResponseList(nrpp, rr->Additional2, rr);
2836
2837 // For SRV records, automatically add the Address record(s) for the target host
2838 if (rr->resrec.rrtype == kDNSType_SRV)
2839 for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records
2840 if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ...
2841 ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ...
2842 rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target
2843 SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name))
2844 AddRecordToResponseList(nrpp, rr2, rr);
2845 }
2846 }
2847
2848 mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
2849 {
2850 AuthRecord *rr;
2851 AuthRecord *ResponseRecords = mDNSNULL;
2852 AuthRecord **nrp = &ResponseRecords;
2853
2854 // Make a list of all our records that need to be unicast to this destination
2855 for (rr = m->ResourceRecords; rr; rr=rr->next)
2856 {
2857 // If we find we can no longer unicast this answer, clear ImmedUnicast
2858 if (rr->ImmedAnswer == mDNSInterfaceMark ||
2859 mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) ||
2860 mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) )
2861 rr->ImmedUnicast = mDNSfalse;
2862
2863 if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID)
2864 if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) ||
2865 (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6)))
2866 {
2867 rr->ImmedAnswer = mDNSNULL; // Clear the state fields
2868 rr->ImmedUnicast = mDNSfalse;
2869 rr->v4Requester = zerov4Addr;
2870 rr->v6Requester = zerov6Addr;
2871 if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo
2872 { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; }
2873 }
2874 }
2875
2876 AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
2877
2878 while (ResponseRecords)
2879 {
2880 mDNSu8 *responseptr = m->omsg.data;
2881 mDNSu8 *newptr;
2882 InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
2883
2884 // Put answers in the packet
2885 while (ResponseRecords && ResponseRecords->NR_AnswerTo)
2886 {
2887 rr = ResponseRecords;
2888 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2889 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
2890 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
2891 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
2892 if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now
2893 if (newptr) responseptr = newptr;
2894 ResponseRecords = rr->NextResponse;
2895 rr->NextResponse = mDNSNULL;
2896 rr->NR_AnswerTo = mDNSNULL;
2897 rr->NR_AdditionalTo = mDNSNULL;
2898 rr->RequireGoodbye = mDNStrue;
2899 }
2900
2901 // Add additionals, if there's space
2902 while (ResponseRecords && !ResponseRecords->NR_AnswerTo)
2903 {
2904 rr = ResponseRecords;
2905 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2906 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
2907 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec);
2908 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
2909
2910 if (newptr) responseptr = newptr;
2911 if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue;
2912 else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark;
2913 ResponseRecords = rr->NextResponse;
2914 rr->NextResponse = mDNSNULL;
2915 rr->NR_AnswerTo = mDNSNULL;
2916 rr->NR_AdditionalTo = mDNSNULL;
2917 }
2918
2919 if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL);
2920 }
2921 }
2922
2923 mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
2924 {
2925 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
2926 // that it should go ahead and immediately dispose of this registration
2927 rr->resrec.RecordType = kDNSRecordTypeShared;
2928 rr->RequireGoodbye = mDNSfalse;
2929 mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this
2930 }
2931
2932 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
2933 // the record list and/or question list.
2934 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2935 mDNSlocal void DiscardDeregistrations(mDNS *const m)
2936 {
2937 if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2938 m->CurrentRecord = m->ResourceRecords;
2939
2940 while (m->CurrentRecord)
2941 {
2942 AuthRecord *rr = m->CurrentRecord;
2943 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
2944 CompleteDeregistration(m, rr); // Don't touch rr after this
2945 else
2946 m->CurrentRecord = rr->next;
2947 }
2948 }
2949
2950 mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
2951 {
2952 if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
2953 else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
2954 }
2955
2956 // Note about acceleration of announcements to facilitate automatic coalescing of
2957 // multiple independent threads of announcements into a single synchronized thread:
2958 // The announcements in the packet may be at different stages of maturity;
2959 // One-second interval, two-second interval, four-second interval, and so on.
2960 // After we've put in all the announcements that are due, we then consider
2961 // whether there are other nearly-due announcements that are worth accelerating.
2962 // To be eligible for acceleration, a record MUST NOT be older (further along
2963 // its timeline) than the most mature record we've already put in the packet.
2964 // In other words, younger records can have their timelines accelerated to catch up
2965 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
2966 // Older records cannot have their timelines accelerated; this would just widen
2967 // the gap between them and their younger bretheren and get them even more out of sync.
2968
2969 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
2970 // the record list and/or question list.
2971 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2972 mDNSlocal void SendResponses(mDNS *const m)
2973 {
2974 int pktcount = 0;
2975 AuthRecord *rr, *r2;
2976 mDNSs32 maxExistingAnnounceInterval = 0;
2977 const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
2978
2979 m->NextScheduledResponse = m->timenow + 0x78000000;
2980
2981 for (rr = m->ResourceRecords; rr; rr=rr->next)
2982 if (rr->ImmedUnicast)
2983 {
2984 mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} };
2985 mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} };
2986 v4.ip.v4 = rr->v4Requester;
2987 v6.ip.v6 = rr->v6Requester;
2988 if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer);
2989 if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer);
2990 if (rr->ImmedUnicast)
2991 {
2992 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr));
2993 rr->ImmedUnicast = mDNSfalse;
2994 }
2995 }
2996
2997 // ***
2998 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
2999 // ***
3000
3001 // Run through our list of records, and decide which ones we're going to announce on all interfaces
3002 for (rr = m->ResourceRecords; rr; rr=rr->next)
3003 {
3004 while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
3005 if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr))
3006 {
3007 rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces
3008 if (maxExistingAnnounceInterval < rr->ThisAPInterval)
3009 maxExistingAnnounceInterval = rr->ThisAPInterval;
3010 if (rr->UpdateBlocked) rr->UpdateBlocked = 0;
3011 }
3012 }
3013
3014 // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
3015 // Eligible records that are more than half-way to their announcement time are accelerated
3016 for (rr = m->ResourceRecords; rr; rr=rr->next)
3017 if ((rr->resrec.InterfaceID && rr->ImmedAnswer) ||
3018 (rr->ThisAPInterval <= maxExistingAnnounceInterval &&
3019 TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) &&
3020 ResourceRecordIsValidAnswer(rr)))
3021 rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces
3022
3023 // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
3024 // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
3025 // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
3026 // then all that means is that it won't get sent -- which would not be the end of the world.
3027 for (rr = m->ResourceRecords; rr; rr=rr->next)
3028 if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV)
3029 for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records
3030 if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ...
3031 ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ...
3032 rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ...
3033 rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target
3034 SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) &&
3035 (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID))
3036 r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too
3037
3038 // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
3039 // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
3040 // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
3041 // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
3042 // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface
3043 // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
3044 for (rr = m->ResourceRecords; rr; rr=rr->next)
3045 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3046 {
3047 if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked
3048 {
3049 for (r2 = m->ResourceRecords; r2; r2=r2->next)
3050 if (ResourceRecordIsValidAnswer(r2))
3051 if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
3052 r2->ImmedAnswer = rr->ImmedAnswer;
3053 }
3054 else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked
3055 {
3056 for (r2 = m->ResourceRecords; r2; r2=r2->next)
3057 if (ResourceRecordIsValidAnswer(r2))
3058 if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
3059 r2->ImmedAdditional = rr->ImmedAdditional;
3060 }
3061 }
3062
3063 // Now set SendRNow state appropriately
3064 for (rr = m->ResourceRecords; rr; rr=rr->next)
3065 {
3066 if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces
3067 {
3068 rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
3069 rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer
3070 rr->LastMCTime = m->timenow;
3071 rr->LastMCInterface = rr->ImmedAnswer;
3072 // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
3073 if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2))
3074 {
3075 rr->AnnounceCount--;
3076 rr->ThisAPInterval *= 2;
3077 rr->LastAPTime = m->timenow;
3078 if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0;
3079 debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount);
3080 }
3081 }
3082 else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface:
3083 {
3084 rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface
3085 rr->ImmedAdditional = mDNSNULL; // No need to send as additional too
3086 rr->LastMCTime = m->timenow;
3087 rr->LastMCInterface = rr->ImmedAnswer;
3088 }
3089 SetNextAnnounceProbeTime(m, rr);
3090 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
3091 }
3092
3093 // ***
3094 // *** 2. Loop through interface list, sending records as appropriate
3095 // ***
3096
3097 while (intf)
3098 {
3099 int numDereg = 0;
3100 int numAnnounce = 0;
3101 int numAnswer = 0;
3102 mDNSu8 *responseptr = m->omsg.data;
3103 mDNSu8 *newptr;
3104 InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
3105
3106 // First Pass. Look for:
3107 // 1. Deregistering records that need to send their goodbye packet
3108 // 2. Updated records that need to retract their old data
3109 // 3. Answers and announcements we need to send
3110 // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
3111 // send this packet and then try again.
3112 // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
3113 // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
3114 for (rr = m->ResourceRecords; rr; rr=rr->next)
3115 if (rr->SendRNow == intf->InterfaceID)
3116 {
3117 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3118 {
3119 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3120 if (!newptr && m->omsg.h.numAnswers) break;
3121 numDereg++;
3122 responseptr = newptr;
3123 }
3124 else if (rr->NewRData) // If we have new data for this record
3125 {
3126 RData *OldRData = rr->resrec.rdata;
3127 mDNSu16 oldrdlength = rr->resrec.rdlength;
3128 // See if we should send a courtesy "goodbye" for the old data before we replace it.
3129 if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye)
3130 {
3131 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3132 if (!newptr && m->omsg.h.numAnswers) break;
3133 numDereg++;
3134 responseptr = newptr;
3135 }
3136 // Now try to see if we can fit the update in the same packet (not fatal if we can't)
3137 SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
3138 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3139 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
3140 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
3141 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
3142 if (newptr) responseptr = newptr;
3143 SetNewRData(&rr->resrec, OldRData, oldrdlength);
3144 }
3145 else
3146 {
3147 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3148 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
3149 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl);
3150 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
3151 if (!newptr && m->omsg.h.numAnswers) break;
3152 rr->RequireGoodbye = (mDNSu8) (!m->SleepState);
3153 if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
3154 responseptr = newptr;
3155 }
3156 // If sending on all interfaces, go to next interface; else we're finished now
3157 if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any)
3158 rr->SendRNow = GetNextActiveInterfaceID(intf);
3159 else
3160 rr->SendRNow = mDNSNULL;
3161 }
3162
3163 // Second Pass. Add additional records, if there's space.
3164 newptr = responseptr;
3165 for (rr = m->ResourceRecords; rr; rr=rr->next)
3166 if (rr->ImmedAdditional == intf->InterfaceID)
3167 if (ResourceRecordIsValidAnswer(rr))
3168 {
3169 // If we have at least one answer already in the packet, then plan to add additionals too
3170 mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0);
3171
3172 // If we're not planning to send any additionals, but this record is a unique one, then
3173 // make sure we haven't already sent any other members of its RRSet -- if we have, then they
3174 // will have had the cache flush bit set, so now we need to finish the job and send the rest.
3175 if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask))
3176 {
3177 const AuthRecord *a;
3178 for (a = m->ResourceRecords; a; a=a->next)
3179 if (a->LastMCTime == m->timenow &&
3180 a->LastMCInterface == intf->InterfaceID &&
3181 SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; }
3182 }
3183 if (!SendAdditional) // If we don't want to send this after all,
3184 rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field
3185 else if (newptr) // Else, try to add it if we can
3186 {
3187 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3188 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
3189 newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec);
3190 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
3191 if (newptr)
3192 {
3193 responseptr = newptr;
3194 rr->ImmedAdditional = mDNSNULL;
3195 rr->RequireGoodbye = mDNStrue;
3196 // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
3197 // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
3198 // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
3199 // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
3200 rr->LastMCTime = m->timenow;
3201 rr->LastMCInterface = intf->InterfaceID;
3202 }
3203 }
3204 }
3205
3206 if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals)
3207 {
3208 debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
3209 numDereg, numDereg == 1 ? "" : "s",
3210 numAnnounce, numAnnounce == 1 ? "" : "s",
3211 numAnswer, numAnswer == 1 ? "" : "s",
3212 m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID);
3213 if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL);
3214 if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL);
3215 if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
3216 if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; }
3217 // There might be more things to send on this interface, so go around one more time and try again.
3218 }
3219 else // Nothing more to send on this interface; go to next
3220 {
3221 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
3222 #if MDNS_DEBUGMSGS && 0
3223 const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
3224 debugf(msg, intf, next);
3225 #endif
3226 intf = next;
3227 }
3228 }
3229
3230 // ***
3231 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3232 // ***
3233
3234 if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
3235 m->CurrentRecord = m->ResourceRecords;
3236 while (m->CurrentRecord)
3237 {
3238 rr = m->CurrentRecord;
3239 m->CurrentRecord = rr->next;
3240
3241 if (rr->SendRNow)
3242 {
3243 if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
3244 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr));
3245 rr->SendRNow = mDNSNULL;
3246 }
3247
3248 if (rr->ImmedAnswer)
3249 {
3250 if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client
3251
3252 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3253 CompleteDeregistration(m, rr); // Don't touch rr after this
3254 else
3255 {
3256 rr->ImmedAnswer = mDNSNULL;
3257 rr->ImmedUnicast = mDNSfalse;
3258 rr->v4Requester = zerov4Addr;
3259 rr->v6Requester = zerov6Addr;
3260 }
3261 }
3262 }
3263 verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow);
3264 }
3265
3266 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
3267 // so we want to be lazy about how frequently we do it.
3268 // 1. If a cache record is currently referenced by *no* active questions,
3269 // then we don't mind expiring it up to a minute late (who will know?)
3270 // 2. Else, if a cache record is due for some of its final expiration queries,
3271 // we'll allow them to be late by up to 2% of the TTL
3272 // 3. Else, if a cache record has completed all its final expiration queries without success,
3273 // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
3274 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
3275 // so allow at most 1/10 second lateness
3276 #define CacheCheckGracePeriod(RR) ( \
3277 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
3278 ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
3279 ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
3280 ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
3281
3282 // Note: MUST call SetNextCacheCheckTime any time we change:
3283 // rr->TimeRcvd
3284 // rr->DelayDelivery
3285 // rr->resrec.rroriginalttl
3286 // rr->UnansweredQueries
3287 // rr->CRActiveQuestion
3288 mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr)
3289 {
3290 rr->NextRequiredQuery = RRExpireTime(rr);
3291
3292 // If we have an active question, then see if we want to schedule a refresher query for this record.
3293 // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
3294 if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
3295 {
3296 rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries);
3297 rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50);
3298 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
3299 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond);
3300 }
3301
3302 if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
3303 m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
3304
3305 if (rr->DelayDelivery)
3306 if (m->NextCacheCheck - rr->DelayDelivery > 0)
3307 m->NextCacheCheck = rr->DelayDelivery;
3308 }
3309
3310 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3311 #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3312 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
3313
3314 mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
3315 {
3316 if (interval < kMinimumReconfirmTime)
3317 interval = kMinimumReconfirmTime;
3318 if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
3319 interval = 0x10000000;
3320
3321 // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
3322 if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3))
3323 {
3324 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
3325 interval += mDNSRandom(interval/3);
3326 rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3;
3327 rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond;
3328 SetNextCacheCheckTime(m, rr);
3329 }
3330 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr));
3331 return(mStatus_NoError);
3332 }
3333
3334 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
3335
3336 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
3337 // It also appends to the list of known answer records that need to be included,
3338 // and updates the forcast for the size of the known answer section.
3339 mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
3340 CacheRecord ***kalistptrptr, mDNSu32 *answerforecast)
3341 {
3342 mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
3343 mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
3344 const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData;
3345 mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit));
3346 if (!newptr)
3347 {
3348 debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c);
3349 return(mDNSfalse);
3350 }
3351 else if (newptr + *answerforecast >= limit)
3352 {
3353 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data);
3354 query->h.numQuestions--;
3355 return(mDNSfalse);
3356 }
3357 else
3358 {
3359 mDNSu32 forecast = *answerforecast;
3360 const mDNSu32 slot = HashSlot(&q->qname);
3361 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3362 CacheRecord *rr;
3363 CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update
3364
3365 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
3366 if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface
3367 rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list
3368 rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet
3369 ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question
3370 rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0) // and it is less than half-way to expiry
3371 {
3372 *ka = rr; // Link this record into our known answer chain
3373 ka = &rr->NextInKAList;
3374 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3375 forecast += 12 + rr->resrec.rdestimate;
3376 // If we're trying to put more than one question in this packet, and it doesn't fit
3377 // then undo that last question and try again next time
3378 if (query->h.numQuestions > 1 && newptr + forecast >= limit)
3379 {
3380 debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
3381 q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data);
3382 query->h.numQuestions--;
3383 ka = *kalistptrptr; // Go back to where we started and retract these answer records
3384 while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
3385 return(mDNSfalse); // Return false, so we'll try again in the next packet
3386 }
3387 }
3388
3389 // Traffic reduction:
3390 // If we already have at least one unique answer in the cache,
3391 // OR we have so many shared answers that the KA list is too big to fit in one packet
3392 // The we suppress queries number 3 and 5:
3393 // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies)
3394 // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally)
3395 // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress)
3396 // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally)
3397 // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress)
3398 // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
3399 if (q->UniqueAnswers || newptr + forecast >= limit)
3400 if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32)
3401 {
3402 query->h.numQuestions--;
3403 ka = *kalistptrptr; // Go back to where we started and retract these answer records
3404 while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
3405 return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question
3406 }
3407
3408 // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
3409 *queryptr = newptr; // Update the packet pointer
3410 *answerforecast = forecast; // Update the forecast
3411 *kalistptrptr = ka; // Update the known answer list pointer
3412 if (ucast) m->ExpectUnicastResponse = m->timenow;
3413
3414 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache,
3415 if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface
3416 rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list
3417 ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question
3418 {
3419 rr->UnansweredQueries++; // indicate that we're expecting a response
3420 rr->LastUnansweredTime = m->timenow;
3421 SetNextCacheCheckTime(m, rr);
3422 }
3423
3424 return(mDNStrue);
3425 }
3426 }
3427
3428 mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
3429 {
3430 mDNSu32 slot;
3431 CacheGroup *cg;
3432 CacheRecord *rr;
3433 domainname *target;
3434 FORALL_CACHERECORDS(slot, cg, rr)
3435 if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname))
3436 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
3437 }
3438
3439 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
3440 mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time)
3441 {
3442 int i;
3443 for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
3444 }
3445
3446 mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID)
3447 {
3448 int i;
3449 for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
3450 }
3451
3452 mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf)
3453 {
3454 int i;
3455 mDNSBool v4 = !intf->IPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
3456 mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
3457 for (i=0; i<DupSuppressInfoSize; i++)
3458 if (ds[i].InterfaceID == intf->InterfaceID)
3459 {
3460 if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue;
3461 else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue;
3462 if (v4 && v6) return(mDNStrue);
3463 }
3464 return(mDNSfalse);
3465 }
3466
3467 mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type)
3468 {
3469 int i, j;
3470
3471 // See if we have this one in our list somewhere already
3472 for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break;
3473
3474 // If not, find a slot we can re-use
3475 if (i >= DupSuppressInfoSize)
3476 {
3477 i = 0;
3478 for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++)
3479 if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0)
3480 i = j;
3481 }
3482
3483 // Record the info about this query we saw
3484 ds[i].Time = Time;
3485 ds[i].InterfaceID = InterfaceID;
3486 ds[i].Type = Type;
3487
3488 return(i);
3489 }
3490
3491 mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
3492 {
3493 // If more than 90% of the way to the query time, we should unconditionally accelerate it
3494 if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10))
3495 return(mDNStrue);
3496
3497 // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
3498 if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2))
3499 {
3500 // We forecast: qname (n) type (2) class (2)
3501 mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4;
3502 const mDNSu32 slot = HashSlot(&q->qname);
3503 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3504 CacheRecord *rr;
3505 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
3506 if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet
3507 ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question
3508 rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry
3509 rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery
3510 {
3511 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3512 forecast += 12 + rr->resrec.rdestimate;
3513 if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate
3514 }
3515 return(mDNStrue);
3516 }
3517
3518 return(mDNSfalse);
3519 }
3520
3521 // How Standard Queries are generated:
3522 // 1. The Question Section contains the question
3523 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
3524
3525 // How Probe Queries are generated:
3526 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
3527 // if some other host is already using *any* records with this name, we want to know about it.
3528 // 2. The Authority Section contains the proposed values we intend to use for one or more
3529 // of our records with that name (analogous to the Update section of DNS Update packets)
3530 // because if some other host is probing at the same time, we each want to know what the other is
3531 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
3532
3533 mDNSlocal void SendQueries(mDNS *const m)
3534 {
3535 int pktcount = 0;
3536 DNSQuestion *q;
3537 // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
3538 mDNSs32 maxExistingQuestionInterval = 0;
3539 const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
3540 CacheRecord *KnownAnswerList = mDNSNULL;
3541
3542 // 1. If time for a query, work out what we need to do
3543 if (m->timenow - m->NextScheduledQuery >= 0)
3544 {
3545 mDNSu32 slot;
3546 CacheGroup *cg;
3547 CacheRecord *rr;
3548 m->NextScheduledQuery = m->timenow + 0x78000000;
3549
3550 // We're expecting to send a query anyway, so see if any expiring cache records are close enough
3551 // to their NextRequiredQuery to be worth batching them together with this one
3552 FORALL_CACHERECORDS(slot, cg, rr)
3553 if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
3554 if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0)
3555 {
3556 q = rr->CRActiveQuestion;
3557 ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID);
3558 if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If unicast query, mark it
3559 else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID;
3560 else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
3561 }
3562
3563 // Scan our list of questions to see which *unicast* queries need to be sent
3564 for (q = m->Questions; q; q=q->next)
3565 if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow)))
3566 {
3567 mDNSu8 *qptr = m->omsg.data;
3568 const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data);
3569 InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags);
3570 qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass);
3571 mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL);
3572 q->ThisQInterval *= 2;
3573 if (q->ThisQInterval > MaxQuestionInterval)
3574 q->ThisQInterval = MaxQuestionInterval;
3575 q->LastQTime = m->timenow;
3576 q->LastQTxTime = m->timenow;
3577 q->RecentAnswerPkts = 0;
3578 q->SendQNow = mDNSNULL;
3579 m->ExpectUnicastResponse = m->timenow;
3580 }
3581
3582 // Scan our list of questions to see which *multicast* queries we're definitely going to send
3583 for (q = m->Questions; q; q=q->next)
3584 if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow))
3585 {
3586 q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
3587 if (maxExistingQuestionInterval < q->ThisQInterval)
3588 maxExistingQuestionInterval = q->ThisQInterval;
3589 }
3590
3591 // Scan our list of questions
3592 // (a) to see if there are any more that are worth accelerating, and
3593 // (b) to update the state variables for *all* the questions we're going to send
3594 for (q = m->Questions; q; q=q->next)
3595 {
3596 if (q->SendQNow ||
3597 (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))
3598 {
3599 // If at least halfway to next query time, advance to next interval
3600 // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
3601 if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0)
3602 {
3603 q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
3604 q->ThisQInterval *= 2;
3605 if (q->ThisQInterval > MaxQuestionInterval)
3606 q->ThisQInterval = MaxQuestionInterval;
3607 else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8)
3608 {
3609 debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
3610 ReconfirmAntecedents(m, q); // If sending third query, and no answers yet, time to begin doubting the source
3611 }
3612 }
3613
3614 // Mark for sending. (If no active interfaces, then don't even try.)
3615 q->SendOnAll = (q->SendQNow == mDNSInterfaceMark);
3616 if (q->SendOnAll)
3617 {
3618 q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID;
3619 q->LastQTime = m->timenow;
3620 }
3621
3622 // If we recorded a duplicate suppression for this question less than half an interval ago,
3623 // then we consider it recent enough that we don't need to do an identical query ourselves.
3624 ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2);
3625
3626 q->LastQTxTime = m->timenow;
3627 q->RecentAnswerPkts = 0;
3628 if (q->RequestUnicast) q->RequestUnicast--;
3629 }
3630 // For all questions (not just the ones we're sending) check what the next scheduled event will be
3631 SetNextQueryTime(m,q);
3632 }
3633 }
3634
3635 // 2. Scan our authoritative RR list to see what probes we might need to send
3636 if (m->timenow - m->NextScheduledProbe >= 0)
3637 {
3638 m->NextScheduledProbe = m->timenow + 0x78000000;
3639
3640 if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
3641 m->CurrentRecord = m->ResourceRecords;
3642 while (m->CurrentRecord)
3643 {
3644 AuthRecord *rr = m->CurrentRecord;
3645 m->CurrentRecord = rr->next;
3646 if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing...
3647 {
3648 // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
3649 if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
3650 {
3651 SetNextAnnounceProbeTime(m, rr);
3652 }
3653 // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
3654 else if (rr->ProbeCount)
3655 {
3656 // Mark for sending. (If no active interfaces, then don't even try.)
3657 rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
3658 rr->LastAPTime = m->timenow;
3659 rr->ProbeCount--;
3660 SetNextAnnounceProbeTime(m, rr);
3661 }
3662 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
3663 else
3664 {
3665 AuthRecord *r2;
3666 rr->resrec.RecordType = kDNSRecordTypeVerified;
3667 rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique;
3668 rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique;
3669 SetNextAnnounceProbeTime(m, rr);
3670 // If we have any records on our duplicate list that match this one, they have now also completed probing
3671 for (r2 = m->DuplicateRecords; r2; r2=r2->next)
3672 if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr))
3673 r2->ProbeCount = 0;
3674 AcknowledgeRecord(m, rr);
3675 }
3676 }
3677 }
3678 m->CurrentRecord = m->DuplicateRecords;
3679 while (m->CurrentRecord)
3680 {
3681 AuthRecord *rr = m->CurrentRecord;
3682 m->CurrentRecord = rr->next;
3683 if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
3684 AcknowledgeRecord(m, rr);
3685 }
3686 }
3687
3688 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
3689 while (intf)
3690 {
3691 AuthRecord *rr;
3692 mDNSu8 *queryptr = m->omsg.data;
3693 InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags);
3694 if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
3695 if (!KnownAnswerList)
3696 {
3697 // Start a new known-answer list
3698 CacheRecord **kalistptr = &KnownAnswerList;
3699 mDNSu32 answerforecast = 0;
3700
3701 // Put query questions in this packet
3702 for (q = m->Questions; q; q=q->next)
3703 if (q->SendQNow == intf->InterfaceID)
3704 {
3705 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
3706 SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ",
3707 q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
3708 // If we're suppressing this question, or we successfully put it, update its SendQNow state
3709 if (SuppressOnThisInterface(q->DupSuppress, intf) ||
3710 BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
3711 q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
3712 }
3713
3714 // Put probe questions in this packet
3715 for (rr = m->ResourceRecords; rr; rr=rr->next)
3716 if (rr->SendRNow == intf->InterfaceID)
3717 {
3718 mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353;
3719 mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
3720 const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
3721 mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit));
3722 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3723 mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate;
3724 if (newptr && newptr + forecast < limit)
3725 {
3726 queryptr = newptr;
3727 answerforecast = forecast;
3728 rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf);
3729 rr->IncludeInProbe = mDNStrue;
3730 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount);
3731 }
3732 else
3733 {
3734 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3735 m->omsg.h.numQuestions--;
3736 }
3737 }
3738 }
3739
3740 // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
3741 while (KnownAnswerList)
3742 {
3743 CacheRecord *rr = KnownAnswerList;
3744 mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
3745 mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd);
3746 if (newptr)
3747 {
3748 verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data);
3749 queryptr = newptr;
3750 KnownAnswerList = rr->NextInKAList;
3751 rr->NextInKAList = mDNSNULL;
3752 }
3753 else
3754 {
3755 // If we ran out of space and we have more than one question in the packet, that's an error --
3756 // we shouldn't have put more than one question if there was a risk of us running out of space.
3757 if (m->omsg.h.numQuestions > 1)
3758 LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers);
3759 m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
3760 break;
3761 }
3762 }
3763
3764 for (rr = m->ResourceRecords; rr; rr=rr->next)
3765 if (rr->IncludeInProbe)
3766 {
3767 mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec);
3768 rr->IncludeInProbe = mDNSfalse;
3769 if (newptr) queryptr = newptr;
3770 else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
3771 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3772 }
3773
3774 if (queryptr > m->omsg.data)
3775 {
3776 if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1)
3777 LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions);
3778 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
3779 m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s",
3780 m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s",
3781 m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID);
3782 if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL);
3783 if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL);
3784 if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
3785 if (++pktcount >= 1000)
3786 { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; }
3787 // There might be more records left in the known answer list, or more questions to send
3788 // on this interface, so go around one more time and try again.
3789 }
3790 else // Nothing more to send on this interface; go to next
3791 {
3792 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
3793 #if MDNS_DEBUGMSGS && 0
3794 const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
3795 debugf(msg, intf, next);
3796 #endif
3797 intf = next;
3798 }
3799 }
3800
3801 // Final sanity check for debugging purposes
3802 {
3803 AuthRecord *rr;
3804 for (rr = m->ResourceRecords; rr; rr=rr->next)
3805 if (rr->SendRNow)
3806 {
3807 if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
3808 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, rr));
3809 rr->SendRNow = mDNSNULL;
3810 }
3811 }
3812 }
3813
3814 // ***************************************************************************
3815 #if COMPILER_LIKES_PRAGMA_MARK
3816 #pragma mark -
3817 #pragma mark - RR List Management & Task Management
3818 #endif
3819
3820 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
3821 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3822 mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord)
3823 {
3824 verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
3825 q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3826
3827 // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue)
3828 // may be called twice, once when the record is received, and again when it's time to notify local clients.
3829 // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
3830
3831 rr->LastUsed = m->timenow;
3832 if (ActiveQuestion(q) && rr->CRActiveQuestion != q)
3833 {
3834 if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count
3835 rr->CRActiveQuestion = q; // We know q is non-null
3836 SetNextCacheCheckTime(m, rr);
3837 }
3838
3839 // If this is:
3840 // (a) a no-cache add, where we've already done at least one 'QM' query, or
3841 // (b) a normal add, where we have at least one unique-type answer,
3842 // then there's no need to keep polling the network.
3843 // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
3844 if ((AddRecord == 2 && !q->RequestUnicast) ||
3845 (AddRecord == 1 && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))))
3846 if (ActiveQuestion(q))
3847 {
3848 q->LastQTime = m->timenow;
3849 q->LastQTxTime = m->timenow;
3850 q->RecentAnswerPkts = 0;
3851 q->ThisQInterval = MaxQuestionInterval;
3852 q->RequestUnicast = mDNSfalse;
3853 }
3854
3855 if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3856
3857 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
3858 if (q->QuestionCallback)
3859 q->QuestionCallback(m, q, &rr->resrec, AddRecord);
3860 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
3861 // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function
3862 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
3863 // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv()
3864 // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions
3865 // being deleted out from under them.
3866 }
3867
3868 mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
3869 {
3870 rr->DelayDelivery = 0;
3871 if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set");
3872 m->CurrentQuestion = m->Questions;
3873 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3874 {
3875 DNSQuestion *q = m->CurrentQuestion;
3876 m->CurrentQuestion = q->next;
3877 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3878 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3879 }
3880 m->CurrentQuestion = mDNSNULL;
3881 }
3882
3883 mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot)
3884 {
3885 const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second
3886 const mDNSs32 start = m->timenow - 0x10000000;
3887 mDNSs32 delay = start;
3888 CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
3889 CacheRecord *rr;
3890 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
3891 if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name))
3892 if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second
3893 if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted
3894 delay = RRExpireTime(rr);
3895 if (delay - start > 0) return(delay ? delay : 1); // Make sure we return non-zero if we want to delay
3896 else return(0);
3897 }
3898
3899 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3900 // If new questions are created as a result of invoking client callbacks, they will be added to
3901 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3902 // rr is a new CacheRecord just received into our cache
3903 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3904 // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback,
3905 // which may change the record list and/or question list.
3906 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3907 mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
3908 {
3909 if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3910 m->CurrentQuestion = m->Questions;
3911 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3912 {
3913 DNSQuestion *q = m->CurrentQuestion;
3914 m->CurrentQuestion = q->next;
3915 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3916 {
3917 // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last
3918 // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start.
3919 // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less,
3920 // there's not much benefit accelerating because we will anyway send another query within a few seconds.
3921 // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines.
3922 if (q->LastAnswerPktNum != m->PktNum)
3923 {
3924 q->LastAnswerPktNum = m->PktNum;
3925 if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
3926 q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
3927 {
3928 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
3929 q->qname.c, DNSTypeName(q->qtype));
3930 q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4);
3931 q->ThisQInterval = InitialQuestionInterval;
3932 SetNextQueryTime(m,q);
3933 }
3934 }
3935 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl);
3936 q->CurrentAnswers++;
3937 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
3938 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
3939 if (q->CurrentAnswers > 4000)
3940 {
3941 static int msgcount = 0;
3942 if (msgcount++ < 10)
3943 LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
3944 q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers);
3945 rr->resrec.rroriginalttl = 1;
3946 rr->UnansweredQueries = MaxUnansweredQueries;
3947 }
3948 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3949 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3950 }
3951 }
3952 m->CurrentQuestion = mDNSNULL;
3953 }
3954
3955 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3956 // If new questions are created as a result of invoking client callbacks, they will be added to
3957 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3958 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
3959 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
3960 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
3961 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
3962 // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback,
3963 // which may change the record list and/or question list.
3964 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3965 mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr)
3966 {
3967 LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c);
3968 if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3969 m->CurrentQuestion = m->Questions;
3970 while (m->CurrentQuestion)
3971 {
3972 DNSQuestion *q = m->CurrentQuestion;
3973 m->CurrentQuestion = q->next;
3974 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3975 AnswerQuestionWithResourceRecord(m, q, rr, 2); // Value '2' indicates "don't expect 'remove' events for this"
3976 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3977 }
3978 m->CurrentQuestion = mDNSNULL;
3979 }
3980
3981 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
3982 // If new questions are created as a result of invoking client callbacks, they will be added to
3983 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3984 // rr is an existing cache CacheRecord that just expired and is being deleted
3985 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3986 // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback,
3987 // which may change the record list and/or question list.
3988 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3989 mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
3990 {
3991 if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set");
3992 m->CurrentQuestion = m->Questions;
3993 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3994 {
3995 DNSQuestion *q = m->CurrentQuestion;
3996 m->CurrentQuestion = q->next;
3997 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3998 {
3999 verbosedebugf("CacheRecordRmv %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
4000 if (q->CurrentAnswers == 0)
4001 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype));
4002 else
4003 {
4004 q->CurrentAnswers--;
4005 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
4006 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
4007 }
4008 if (q->CurrentAnswers == 0)
4009 {
4010 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
4011 ReconfirmAntecedents(m, q);
4012 }
4013 AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
4014 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4015 }
4016 }
4017 m->CurrentQuestion = mDNSNULL;
4018 }
4019
4020 mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
4021 {
4022 #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
4023 unsigned int i;
4024 for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
4025 #endif
4026 e->next = m->rrcache_free;
4027 m->rrcache_free = e;
4028 m->rrcache_totalused--;
4029 }
4030
4031 mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp)
4032 {
4033 CacheEntity *e = (CacheEntity *)(*cp);
4034 //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
4035 if ((*cp)->rrcache_tail != &(*cp)->members) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
4036 //if ((*cp)->name != (domainname*)((*cp)->namestorage)) LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
4037 if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
4038 (*cp)->name = mDNSNULL;
4039 *cp = (*cp)->next; // Cut record from list
4040 ReleaseCacheEntity(m, e);
4041 }
4042
4043 mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
4044 {
4045 if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) mDNSPlatformMemFree(r->resrec.rdata);
4046 r->resrec.rdata = mDNSNULL;
4047 ReleaseCacheEntity(m, (CacheEntity *)r);
4048 }
4049
4050 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls
4051 // The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records
4052 mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg)
4053 {
4054 CacheRecord **rp = &cg->members;
4055
4056 if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4057 m->lock_rrcache = 1;
4058
4059 while (*rp)
4060 {
4061 CacheRecord *const rr = *rp;
4062 mDNSs32 event = RRExpireTime(rr);
4063 if (m->timenow - event >= 0) // If expired, delete it
4064 {
4065 *rp = rr->next; // Cut it from the list
4066 verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr));
4067 if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away
4068 {
4069 CacheRecordRmv(m, rr);
4070 m->rrcache_active--;
4071 }
4072 ReleaseCacheRecord(m, rr);
4073 }
4074 else // else, not expired; see if we need to query
4075 {
4076 if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
4077 event = rr->DelayDelivery;
4078 else
4079 {
4080 if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr);
4081 if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
4082 {
4083 if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query
4084 event = rr->NextRequiredQuery; // then just record when we want the next query
4085 else // else trigger our question to go out now
4086 {
4087 // Set NextScheduledQuery to timenow so that SendQueries() will run.
4088 // SendQueries() will see that we have records close to expiration, and send FEQs for them.
4089 m->NextScheduledQuery = m->timenow;
4090 // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
4091 // which will correctly update m->NextCacheCheck for us
4092 event = m->timenow + 0x3FFFFFFF;
4093 }
4094 }
4095 }
4096 if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0)
4097 m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
4098 rp = &rr->next;
4099 }
4100 }
4101 if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp);
4102 cg->rrcache_tail = rp;
4103 m->lock_rrcache = 0;
4104 }
4105
4106 mDNSlocal void AnswerNewQuestion(mDNS *const m)
4107 {
4108 mDNSBool ShouldQueryImmediately = mDNStrue;
4109 CacheRecord *rr;
4110 DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
4111 const mDNSu32 slot = HashSlot(&q->qname);
4112 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
4113
4114 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4115
4116 if (cg) CheckCacheExpiration(m, cg);
4117 m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
4118
4119 if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
4120 // This should be safe, because calling the client's question callback may cause the
4121 // question list to be modified, but should not ever cause the rrcache list to be modified.
4122 // If the client's question callback deletes the question, then m->CurrentQuestion will
4123 // be advanced, and we'll exit out of the loop
4124 m->lock_rrcache = 1;
4125 if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
4126 m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
4127
4128 if (q->InterfaceID == mDNSInterface_Any) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
4129 {
4130 if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4131 m->CurrentRecord = m->ResourceRecords;
4132 while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
4133 {
4134 AuthRecord *rr = m->CurrentRecord;
4135 m->CurrentRecord = rr->next;
4136 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
4137 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4138 {
4139 AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
4140 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4141 if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
4142 }
4143 }
4144 m->CurrentRecord = mDNSNULL;
4145 }
4146
4147 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4148 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4149 {
4150 // SecsSinceRcvd is whole number of elapsed seconds, rounded down
4151 mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
4152 if (rr->resrec.rroriginalttl <= SecsSinceRcvd)
4153 {
4154 LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)",
4155 rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
4156 continue; // Go to next one in loop
4157 }
4158
4159 // If this record set is marked unique, then that means we can reasonably assume we have the whole set
4160 // -- we don't need to rush out on the network and query immediately to see if there are more answers out there
4161 if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique))
4162 ShouldQueryImmediately = mDNSfalse;
4163 q->CurrentAnswers++;
4164 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
4165 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
4166 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
4167 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4168 if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
4169 }
4170 else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype))
4171 if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname))
4172 ShouldQueryImmediately = mDNSfalse;
4173
4174 if (ShouldQueryImmediately && m->CurrentQuestion == q)
4175 {
4176 q->ThisQInterval = InitialQuestionInterval;
4177 q->LastQTime = m->timenow - q->ThisQInterval;
4178 m->NextScheduledQuery = m->timenow;
4179 }
4180 m->CurrentQuestion = mDNSNULL;
4181 m->lock_rrcache = 0;
4182 }
4183
4184 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any appropriate answers,
4185 // stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
4186 mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
4187 {
4188 DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer
4189 m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions to the next (if any)
4190
4191 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4192
4193 if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
4194 m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
4195
4196 if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4197 m->CurrentRecord = m->ResourceRecords;
4198 while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
4199 {
4200 AuthRecord *rr = m->CurrentRecord;
4201 m->CurrentRecord = rr->next;
4202 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4203 {
4204 AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
4205 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4206 if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
4207 }
4208 }
4209
4210 m->CurrentQuestion = mDNSNULL;
4211 m->CurrentRecord = mDNSNULL;
4212 }
4213
4214 mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG)
4215 {
4216 CacheEntity *e = mDNSNULL;
4217
4218 if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
4219 m->lock_rrcache = 1;
4220
4221 // If we have no free records, ask the client layer to give us some more memory
4222 if (!m->rrcache_free && m->MainCallback)
4223 {
4224 if (m->rrcache_totalused != m->rrcache_size)
4225 LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
4226 m->rrcache_totalused, m->rrcache_size);
4227
4228 // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
4229 // number of bogus records so that we keep growing our cache until the machine runs out of memory.
4230 // To guard against this, if we're actively using less than 1/32 of our cache, then we
4231 // purge all the unused records and recycle them, instead of allocating more memory.
4232 if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active)
4233 debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
4234 m->rrcache_size, m->rrcache_active);
4235 else
4236 {
4237 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
4238 m->MainCallback(m, mStatus_GrowCache);
4239 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
4240 }
4241 }
4242
4243 // If we still have no free records, recycle all the records we can.
4244 // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
4245 if (!m->rrcache_free)
4246 {
4247 #if MDNS_DEBUGMSGS
4248 mDNSu32 oldtotalused = m->rrcache_totalused;
4249 #endif
4250 mDNSu32 slot;
4251 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4252 {
4253 CacheGroup **cp = &m->rrcache_hash[slot];
4254 while (*cp)
4255 {
4256 CacheRecord **rp = &(*cp)->members;
4257 while (*rp)
4258 {
4259 // Records that answer still-active questions are not candidates for recycling
4260 // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
4261 if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList)
4262 rp=&(*rp)->next;
4263 else
4264 {
4265 CacheRecord *rr = *rp;
4266 *rp = (*rp)->next; // Cut record from list
4267 ReleaseCacheRecord(m, rr);
4268 }
4269 }
4270 if ((*cp)->rrcache_tail != rp) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp);
4271 (*cp)->rrcache_tail = rp;
4272 if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next;
4273 else ReleaseCacheGroup(m, cp);
4274 }
4275 }
4276 #if MDNS_DEBUGMSGS
4277 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
4278 #endif
4279 }
4280
4281 if (m->rrcache_free) // If there are records in the free list, take one
4282 {
4283 e = m->rrcache_free;
4284 m->rrcache_free = e->next;
4285 if (++m->rrcache_totalused >= m->rrcache_report)
4286 {
4287 debugf("RR Cache now using %ld objects", m->rrcache_totalused);
4288 if (m->rrcache_report < 100) m->rrcache_report += 10;
4289 else m->rrcache_report += 100;
4290 }
4291 mDNSPlatformMemZero(e, sizeof(*e));
4292 }
4293
4294 m->lock_rrcache = 0;
4295
4296 return(e);
4297 }
4298
4299 mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength)
4300 {
4301 CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg);
4302 if (r)
4303 {
4304 r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage
4305 if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage
4306 {
4307 r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength);
4308 if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength;
4309 else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; }
4310 }
4311 }
4312 return(r);
4313 }
4314
4315 mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
4316 {
4317 mDNSu16 namelen = DomainNameLength(rr->name);
4318 CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL);
4319 if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
4320 cg->next = m->rrcache_hash[slot];
4321 cg->namehash = rr->namehash;
4322 cg->members = mDNSNULL;
4323 cg->rrcache_tail = &cg->members;
4324 cg->name = (domainname*)cg->namestorage;
4325 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
4326 if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen);
4327 if (!cg->name)
4328 {
4329 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c);
4330 ReleaseCacheEntity(m, (CacheEntity*)cg);
4331 return(mDNSNULL);
4332 }
4333 AssignDomainName(cg->name, rr->name);
4334
4335 if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c);
4336 m->rrcache_hash[slot] = cg;
4337 if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c);
4338
4339 return(cg);
4340 }
4341
4342 mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
4343 {
4344 // Make sure we mark this record as thoroughly expired -- we don't ever want to give
4345 // a positive answer using an expired record (e.g. from an interface that has gone away).
4346 // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
4347 // summary deletion without giving the proper callback to any questions that are monitoring it.
4348 // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
4349 rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60;
4350 rr->UnansweredQueries = MaxUnansweredQueries;
4351 rr->resrec.rroriginalttl = 0;
4352 SetNextCacheCheckTime(m, rr);
4353 }
4354
4355 mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m)
4356 {
4357 mDNSs32 time;
4358 mDNSPlatformLock(m);
4359 if (m->mDNS_busy)
4360 {
4361 LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
4362 if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
4363 }
4364
4365 if (m->timenow) time = m->timenow;
4366 else time = mDNS_TimeNow_NoLock(m);
4367 mDNSPlatformUnlock(m);
4368 return(time);
4369 }
4370
4371 mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
4372 {
4373 mDNS_Lock(m); // Must grab lock before trying to read m->timenow
4374
4375 if (m->timenow - m->NextScheduledEvent >= 0)
4376 {
4377 int i;
4378
4379 verbosedebugf("mDNS_Execute");
4380 if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
4381
4382 // 1. If we're past the probe suppression time, we can clear it
4383 if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0;
4384
4385 // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
4386 if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0;
4387
4388 // 3. Purge our cache of stale old records
4389 if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
4390 {
4391 mDNSu32 slot;
4392 m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
4393 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4394 {
4395 CacheGroup **cp = &m->rrcache_hash[slot];
4396 while (*cp)
4397 {
4398 CheckCacheExpiration(m, *cp);
4399 if ((*cp)->members) cp=&(*cp)->next;
4400 else ReleaseCacheGroup(m, cp);
4401 }
4402 }
4403 }
4404
4405 // 4. See if we can answer any of our new local questions from the cache
4406 for (i=0; m->NewQuestions && i<1000; i++)
4407 {
4408 if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break;
4409 AnswerNewQuestion(m);
4410 }
4411 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4412
4413 for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
4414 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4415
4416 for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++)
4417 {
4418 AuthRecord *rr = m->NewLocalRecords;
4419 m->NewLocalRecords = m->NewLocalRecords->next;
4420 AnswerLocalQuestions(m, rr, mDNStrue);
4421 }
4422 if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
4423
4424 // 5. See what packets we need to send
4425 if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m);
4426 else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)
4427 {
4428 // If the platform code is ready, and we're not suppressing packet generation right now
4429 // then send our responses, probes, and questions.
4430 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
4431 // We send queries next, because there might be final-stage probes that complete their probing here, causing
4432 // them to advance to announcing state, and we want those to be included in any announcements we send out.
4433 // Finally, we send responses, including the previously mentioned records that just completed probing
4434 m->SuppressSending = 0;
4435
4436 // 6. Send Query packets. This may cause some probing records to advance to announcing state
4437 if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m);
4438 if (m->timenow - m->NextScheduledQuery >= 0)
4439 {
4440 LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
4441 m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond;
4442 }
4443 if (m->timenow - m->NextScheduledProbe >= 0)
4444 {
4445 LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
4446 m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond;
4447 }
4448
4449 // 7. Send Response packets, including probing records just advanced to announcing state
4450 if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m);
4451 if (m->timenow - m->NextScheduledResponse >= 0)
4452 {
4453 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
4454 m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond;
4455 }
4456 }
4457
4458 m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
4459 }
4460
4461 // Note about multi-threaded systems:
4462 // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
4463 // performing mDNS API operations that change our next scheduled event time.
4464 //
4465 // On multi-threaded systems (like the current Windows implementation) that have a single main thread
4466 // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
4467 // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
4468 // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
4469 // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
4470 // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
4471 // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
4472 // without blocking. This avoids the race condition between the signal from the other thread arriving
4473 // just *before* or just *after* the main thread enters the blocking primitive.
4474 //
4475 // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
4476 // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
4477 // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
4478 // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
4479 // by the time it gets to the timer callback function).
4480
4481 #ifndef UNICAST_DISABLED
4482 uDNS_Execute(m);
4483 #endif
4484 mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4485 return(m->NextScheduledEvent);
4486 }
4487
4488 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
4489 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
4490 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
4491 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
4492 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
4493 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
4494 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
4495 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
4496 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
4497 // traffic, so it should only be called when there is legitimate reason to believe the machine
4498 // may have become attached to a new network.
4499 mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
4500 {
4501 AuthRecord *rr;
4502
4503 mDNS_Lock(m);
4504
4505 m->SleepState = sleepstate;
4506 LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
4507
4508 if (sleepstate)
4509 {
4510 #ifndef UNICAST_DISABLED
4511 uDNS_Sleep(m);
4512 #endif
4513 // Mark all the records we need to deregister and send them
4514 for (rr = m->ResourceRecords; rr; rr=rr->next)
4515 if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
4516 rr->ImmedAnswer = mDNSInterfaceMark;
4517 SendResponses(m);
4518 }
4519 else
4520 {
4521 DNSQuestion *q;
4522 mDNSu32 slot;
4523 CacheGroup *cg;
4524 CacheRecord *cr;
4525
4526 #ifndef UNICAST_DISABLED
4527 uDNS_Wake(m);
4528 #endif
4529 // 1. Retrigger all our questions
4530 for (q = m->Questions; q; q=q->next) // Scan our list of questions
4531 if (ActiveQuestion(q))
4532 {
4533 q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question
4534 q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
4535 q->LastQTime = m->timenow - q->ThisQInterval;
4536 q->RecentAnswerPkts = 0;
4537 ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
4538 m->NextScheduledQuery = m->timenow;
4539 }
4540
4541 // 2. Re-validate our cache records
4542 m->NextCacheCheck = m->timenow;
4543 FORALL_CACHERECORDS(slot, cg, cr)
4544 mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect);
4545
4546 // 3. Retrigger probing and announcing for all our authoritative records
4547 for (rr = m->ResourceRecords; rr; rr=rr->next)
4548 {
4549 if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
4550 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
4551 rr->AnnounceCount = InitialAnnounceCount;
4552 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
4553 InitializeLastAPTime(m, rr);
4554 }
4555 }
4556
4557 mDNS_Unlock(m);
4558 }
4559
4560 // ***************************************************************************
4561 #if COMPILER_LIKES_PRAGMA_MARK
4562 #pragma mark -
4563 #pragma mark - Packet Reception Functions
4564 #endif
4565
4566 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4567
4568 mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
4569 const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
4570 {
4571 mDNSu8 *responseptr = response->data;
4572 const mDNSu8 *const limit = response->data + sizeof(response->data);
4573 const mDNSu8 *ptr = query->data;
4574 AuthRecord *rr;
4575 mDNSu32 maxttl = 0x70000000;
4576 int i;
4577
4578 // Initialize the response fields so we can answer the questions
4579 InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
4580
4581 // ***
4582 // *** 1. Write out the list of questions we are actually going to answer with this packet
4583 // ***
4584 if (LegacyQuery)
4585 {
4586 maxttl = 10;
4587 for (i=0; i<query->h.numQuestions; i++) // For each question...
4588 {
4589 DNSQuestion q;
4590 ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question...
4591 if (!ptr) return(mDNSNULL);
4592
4593 for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers
4594 {
4595 if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question
4596 { // then put the question in the question section
4597 responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass);
4598 if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); }
4599 break; // break out of the ResponseRecords loop, and go on to the next question
4600 }
4601 }
4602 }
4603
4604 if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
4605 }
4606
4607 // ***
4608 // *** 2. Write Answers
4609 // ***
4610 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4611 if (rr->NR_AnswerTo)
4612 {
4613 mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl);
4614 if (p) responseptr = p;
4615 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; }
4616 }
4617
4618 // ***
4619 // *** 3. Write Additionals
4620 // ***
4621 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4622 if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
4623 {
4624 mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl);
4625 if (p) responseptr = p;
4626 else debugf("GenerateUnicastResponse: No more space for additionals");
4627 }
4628
4629 return(responseptr);
4630 }
4631
4632 // AuthRecord *our is our Resource Record
4633 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
4634 // Returns 0 if there is no conflict
4635 // Returns +1 if there was a conflict and we won
4636 // Returns -1 if there was a conflict and we lost and have to rename
4637 mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt)
4638 {
4639 mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
4640 mDNSu8 pktdata[256], *pktptr = pktdata, *pktend;
4641 if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
4642 if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
4643
4644 ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec);
4645 pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec);
4646 while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; }
4647 if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict
4648
4649 if (ourptr >= ourend) return(-1); // Our data ran out first; We lost
4650 if (pktptr >= pktend) return(+1); // Packet data ran out first; We won
4651 if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost
4652 if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won
4653
4654 debugf("CompareRData: How did we get here?");
4655 return(-1);
4656 }
4657
4658 // See if we have an authoritative record that's identical to this packet record,
4659 // whose canonical DependentOn record is the specified master record.
4660 // The DependentOn pointer is typically used for the TXT record of service registrations
4661 // It indicates that there is no inherent conflict detection for the TXT record
4662 // -- it depends on the SRV record to resolve name conflicts
4663 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
4664 // pointer chain (if any) to make sure we reach the canonical DependentOn record
4665 // If the record has no DependentOn, then just return that record's pointer
4666 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4667 mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master)
4668 {
4669 const AuthRecord *r1;
4670 for (r1 = m->ResourceRecords; r1; r1=r1->next)
4671 {
4672 if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4673 {
4674 const AuthRecord *r2 = r1;
4675 while (r2->DependentOn) r2 = r2->DependentOn;
4676 if (r2 == master) return(mDNStrue);
4677 }
4678 }
4679 for (r1 = m->DuplicateRecords; r1; r1=r1->next)
4680 {
4681 if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4682 {
4683 const AuthRecord *r2 = r1;
4684 while (r2->DependentOn) r2 = r2->DependentOn;
4685 if (r2 == master) return(mDNStrue);
4686 }
4687 }
4688 return(mDNSfalse);
4689 }
4690
4691 // Find the canonical RRSet pointer for this RR received in a packet.
4692 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
4693 // pointers (if any) to make sure we return the canonical member of this name/type/class
4694 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4695 mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr)
4696 {
4697 const AuthRecord *rr;
4698 for (rr = m->ResourceRecords; rr; rr=rr->next)
4699 {
4700 if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
4701 {
4702 while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
4703 return(rr);
4704 }
4705 }
4706 return(mDNSNULL);
4707 }
4708
4709 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
4710 // as one of our records (our) but different rdata.
4711 // 1. If our record is not a type that's supposed to be unique, we don't care.
4712 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
4713 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
4714 // points to our record, ignore this conflict (e.g. the packet record matches one of our
4715 // TXT records, and that record is marked as dependent on 'our', its SRV record).
4716 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
4717 // are members of the same RRSet, then this is not a conflict.
4718 mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr)
4719 {
4720 const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
4721
4722 // If not supposed to be unique, not a conflict
4723 if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
4724
4725 // If a dependent record, not a conflict
4726 if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
4727
4728 // If the pktrr matches a member of ourset, not a conflict
4729 if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse);
4730
4731 // Okay, this is a conflict
4732 return(mDNStrue);
4733 }
4734
4735 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
4736 // the record list and/or question list.
4737 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4738 mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
4739 DNSQuestion *q, AuthRecord *our)
4740 {
4741 int i;
4742 const mDNSu8 *ptr = LocateAuthorities(query, end);
4743 mDNSBool FoundUpdate = mDNSfalse;
4744
4745 for (i = 0; i < query->h.numAuthorities; i++)
4746 {
4747 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
4748 if (!ptr) break;
4749 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
4750 {
4751 FoundUpdate = mDNStrue;
4752 if (PacketRRConflict(m, our, &m->rec.r))
4753 {
4754 int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass;
4755 if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype;
4756 if (!result) result = CompareRData(our, &m->rec.r);
4757 switch (result)
4758 {
4759 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4760 break;
4761 case 0: break;
4762 case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4763 mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict);
4764 goto exit;
4765 }
4766 }
4767 }
4768 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4769 }
4770 if (!FoundUpdate)
4771 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4772 exit:
4773 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4774 }
4775
4776 mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr)
4777 {
4778 mDNSu32 slot = HashSlot(pktrr->name);
4779 CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
4780 CacheRecord *rr;
4781 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4782 if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
4783 return(rr);
4784 }
4785
4786 // ProcessQuery examines a received query to see if we have any answers to give
4787 mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
4788 const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast,
4789 DNSMessage *const response)
4790 {
4791 mDNSBool FromLocalSubnet = AddressIsLocalSubnet(m, InterfaceID, srcaddr);
4792 AuthRecord *ResponseRecords = mDNSNULL;
4793 AuthRecord **nrp = &ResponseRecords;
4794 CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated
4795 CacheRecord **eap = &ExpectedAnswers;
4796 DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet
4797 DNSQuestion **dqp = &DupQuestions;
4798 mDNSs32 delayresponse = 0;
4799 mDNSBool SendLegacyResponse = mDNSfalse;
4800 const mDNSu8 *ptr = query->data;
4801 mDNSu8 *responseptr = mDNSNULL;
4802 AuthRecord *rr;
4803 int i;
4804
4805 // ***
4806 // *** 1. Parse Question Section and mark potential answers
4807 // ***
4808 for (i=0; i<query->h.numQuestions; i++) // For each question...
4809 {
4810 mDNSBool QuestionNeedsMulticastResponse;
4811 int NumAnswersForThisQuestion = 0;
4812 DNSQuestion pktq, *q;
4813 ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question...
4814 if (!ptr) goto exit;
4815
4816 // The only queries that *need* a multicast response are:
4817 // * Queries sent via multicast
4818 // * from port 5353
4819 // * that don't have the kDNSQClass_UnicastResponse bit set
4820 // These queries need multicast responses because other clients will:
4821 // * suppress their own identical questions when they see these questions, and
4822 // * expire their cache records if they don't see the expected responses
4823 // For other queries, we may still choose to send the occasional multicast response anyway,
4824 // to keep our neighbours caches warm, and for ongoing conflict detection.
4825 QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse);
4826 // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
4827 pktq.qclass &= ~kDNSQClass_UnicastResponse;
4828
4829 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
4830 // can result in user callbacks which may change the record list and/or question list.
4831 // Also note: we just mark potential answer records here, without trying to build the
4832 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
4833 // from that list while we're in the middle of trying to build it.
4834 if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set");
4835 m->CurrentRecord = m->ResourceRecords;
4836 while (m->CurrentRecord)
4837 {
4838 rr = m->CurrentRecord;
4839 m->CurrentRecord = rr->next;
4840 if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
4841 {
4842 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
4843 ResolveSimultaneousProbe(m, query, end, &pktq, rr);
4844 else if (ResourceRecordIsValidAnswer(rr))
4845 {
4846 NumAnswersForThisQuestion++;
4847 // Notes:
4848 // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
4849 // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
4850 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
4851 // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
4852 // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
4853 // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
4854 if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery))
4855 {
4856 // We only mark this question for sending if it is at least one second since the last time we multicast it
4857 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
4858 // This is to guard against the case where someone blasts us with queries as fast as they can.
4859 if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
4860 (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
4861 rr->NR_AnswerTo = (mDNSu8*)~0;
4862 }
4863 else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
4864 }
4865 }
4866 }
4867
4868 // If we couldn't answer this question, someone else might be able to,
4869 // so use random delay on response to reduce collisions
4870 if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms
4871
4872 // We only do the following accelerated cache expiration processing and duplicate question suppression processing
4873 // for multicast queries with multicast responses.
4874 // For any query generating a unicast response we don't do this because we can't assume we will see the response
4875 if (QuestionNeedsMulticastResponse)
4876 {
4877 const mDNSu32 slot = HashSlot(&pktq.qname);
4878 CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
4879 CacheRecord *rr;
4880
4881 // Make a list indicating which of our own cache records we expect to see updated as a result of this query
4882 // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
4883 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4884 if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit)
4885 if (!rr->NextInKAList && eap != &rr->NextInKAList)
4886 {
4887 *eap = rr;
4888 eap = &rr->NextInKAList;
4889 if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond)
4890 {
4891 // Although MPUnansweredQ is only really used for multi-packet query processing,
4892 // we increment it for both single-packet and multi-packet queries, so that it stays in sync
4893 // with the MPUnansweredKA value, which by necessity is incremented for both query types.
4894 rr->MPUnansweredQ++;
4895 rr->MPLastUnansweredQT = m->timenow;
4896 rr->MPExpectingKA = mDNStrue;
4897 }
4898 }
4899
4900 // Check if this question is the same as any of mine.
4901 // We only do this for non-truncated queries. Right now it would be too complicated to try
4902 // to keep track of duplicate suppression state between multiple packets, especially when we
4903 // can't guarantee to receive all of the Known Answer packets that go with a particular query.
4904 if (!(query->h.flags.b[0] & kDNSFlag0_TC))
4905 for (q = m->Questions; q; q=q->next)
4906 if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
4907 if (!q->InterfaceID || q->InterfaceID == InterfaceID)
4908 if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
4909 if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
4910 { *dqp = q; dqp = &q->NextInDQList; }
4911 }
4912 }
4913
4914 // ***
4915 // *** 2. Now we can safely build the list of marked answers
4916 // ***
4917 for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers
4918 if (rr->NR_AnswerTo) // If we marked the record...
4919 AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list
4920
4921 // ***
4922 // *** 3. Add additional records
4923 // ***
4924 AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
4925
4926 // ***
4927 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
4928 // ***
4929 for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section...
4930 {
4931 // Get the record...
4932 AuthRecord *rr;
4933 CacheRecord *ourcacherr;
4934 ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
4935 if (!ptr) goto exit;
4936
4937 // See if this Known-Answer suppresses any of our currently planned answers
4938 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4939 if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
4940 { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
4941
4942 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
4943 for (rr=m->ResourceRecords; rr; rr=rr->next)
4944 {
4945 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
4946 if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr))
4947 {
4948 if (srcaddr->type == mDNSAddrType_IPv4)
4949 {
4950 if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr;
4951 }
4952 else if (srcaddr->type == mDNSAddrType_IPv6)
4953 {
4954 if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
4955 }
4956 if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
4957 {
4958 rr->ImmedAnswer = mDNSNULL;
4959 rr->ImmedUnicast = mDNSfalse;
4960 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4961 LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr));
4962 #endif
4963 }
4964 }
4965 }
4966
4967 // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
4968 // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list).
4969 ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
4970 if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond)
4971 {
4972 ourcacherr->MPUnansweredKA++;
4973 ourcacherr->MPExpectingKA = mDNSfalse;
4974 }
4975
4976 // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
4977 // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
4978 // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
4979 eap = &ExpectedAnswers;
4980 while (*eap)
4981 {
4982 CacheRecord *rr = *eap;
4983 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
4984 { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; }
4985 else eap = &rr->NextInKAList;
4986 }
4987
4988 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
4989 if (!ourcacherr)
4990 {
4991 dqp = &DupQuestions;
4992 while (*dqp)
4993 {
4994 DNSQuestion *q = *dqp;
4995 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
4996 { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
4997 else dqp = &q->NextInDQList;
4998 }
4999 }
5000 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5001 }
5002
5003 // ***
5004 // *** 5. Cancel any additionals that were added because of now-deleted records
5005 // ***
5006 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
5007 if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo))
5008 { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
5009
5010 // ***
5011 // *** 6. Mark the send flags on the records we plan to send
5012 // ***
5013 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
5014 {
5015 if (rr->NR_AnswerTo)
5016 {
5017 mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response
5018 mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response)
5019
5020 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
5021 if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0)
5022 {
5023 SendMulticastResponse = mDNStrue;
5024 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
5025 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
5026 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
5027 if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0;
5028 }
5029
5030 // If the client insists on a multicast response, then we'd better send one
5031 if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue;
5032 else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue;
5033 else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue;
5034
5035 if (SendMulticastResponse || SendUnicastResponse)
5036 {
5037 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5038 rr->ImmedAnswerMarkTime = m->timenow;
5039 #endif
5040 m->NextScheduledResponse = m->timenow;
5041 // If we're already planning to send this on another interface, just send it on all interfaces
5042 if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID)
5043 rr->ImmedAnswer = mDNSInterfaceMark;
5044 else
5045 {
5046 rr->ImmedAnswer = InterfaceID; // Record interface to send it on
5047 if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue;
5048 if (srcaddr->type == mDNSAddrType_IPv4)
5049 {
5050 if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4;
5051 else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr;
5052 }
5053 else if (srcaddr->type == mDNSAddrType_IPv6)
5054 {
5055 if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6;
5056 else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr;
5057 }
5058 }
5059 }
5060 // If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
5061 // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
5062 // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
5063 // else, for a simple unique record reply, we can reply immediately; no need for delay
5064 if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms
5065 else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms
5066 }
5067 else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0)
5068 {
5069 // Since additional records are an optimization anyway, we only ever send them on one interface at a time
5070 // If two clients on different interfaces do queries that invoke the same optional additional answer,
5071 // then the earlier client is out of luck
5072 rr->ImmedAdditional = InterfaceID;
5073 // No need to set m->NextScheduledResponse here
5074 // We'll send these additional records when we send them, or not, as the case may be
5075 }
5076 }
5077
5078 // ***
5079 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5080 // ***
5081 if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50))
5082 {
5083 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5084 mDNSs32 oldss = m->SuppressSending;
5085 if (oldss && delayresponse)
5086 LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50);
5087 #endif
5088 // Pick a random delay:
5089 // We start with the base delay chosen above (typically either 1 second or 20 seconds),
5090 // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
5091 // This is an integer value, with resolution determined by the platform clock rate.
5092 // We then divide that by 50 to get the delay value in ticks. We defer the division until last
5093 // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
5094 // The +49 before dividing is to ensure we round up, not down, to ensure that even
5095 // on platforms where the native clock rate is less than fifty ticks per second,
5096 // we still guarantee that the final calculated delay is at least one platform tick.
5097 // We want to make sure we don't ever allow the delay to be zero ticks,
5098 // because if that happens we'll fail the Bonjour Conformance Test.
5099 // Our final computed delay is 20-120ms for normal delayed replies,
5100 // or 400-500ms in the case of multi-packet known-answer lists.
5101 m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50;
5102 if (m->SuppressSending == 0) m->SuppressSending = 1;
5103 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5104 if (oldss && delayresponse)
5105 LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow);
5106 #endif
5107 }
5108
5109 // ***
5110 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5111 // ***
5112 if (SendLegacyResponse)
5113 responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
5114
5115 exit:
5116 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5117
5118 // ***
5119 // *** 9. Finally, clear our link chains ready for use next time
5120 // ***
5121 while (ResponseRecords)
5122 {
5123 rr = ResponseRecords;
5124 ResponseRecords = rr->NextResponse;
5125 rr->NextResponse = mDNSNULL;
5126 rr->NR_AnswerTo = mDNSNULL;
5127 rr->NR_AdditionalTo = mDNSNULL;
5128 }
5129
5130 while (ExpectedAnswers)
5131 {
5132 CacheRecord *rr;
5133 rr = ExpectedAnswers;
5134 ExpectedAnswers = rr->NextInKAList;
5135 rr->NextInKAList = mDNSNULL;
5136
5137 // For non-truncated queries, we can definitively say that we should expect
5138 // to be seeing a response for any records still left in the ExpectedAnswers list
5139 if (!(query->h.flags.b[0] & kDNSFlag0_TC))
5140 if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond)
5141 {
5142 rr->UnansweredQueries++;
5143 rr->LastUnansweredTime = m->timenow;
5144 if (rr->UnansweredQueries > 1)
5145 debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
5146 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
5147 SetNextCacheCheckTime(m, rr);
5148 }
5149
5150 // If we've seen multiple unanswered queries for this record,
5151 // then mark it to expire in five seconds if we don't get a response by then.
5152 if (rr->UnansweredQueries >= MaxUnansweredQueries)
5153 {
5154 // Only show debugging message if this record was not about to expire anyway
5155 if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond)
5156 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5157 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
5158 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
5159 }
5160 // Make a guess, based on the multi-packet query / known answer counts, whether we think we
5161 // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
5162 // possible packet loss of up to 20% of the additional KA packets.)
5163 else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8)
5164 {
5165 // We want to do this conservatively.
5166 // If there are so many machines on the network that they have to use multi-packet known-answer lists,
5167 // then we don't want them to all hit the network simultaneously with their final expiration queries.
5168 // By setting the record to expire in four minutes, we achieve two things:
5169 // (a) the 90-95% final expiration queries will be less bunched together
5170 // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
5171 mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4;
5172 if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond)
5173 remain = 240 * (mDNSu32)mDNSPlatformOneSecond;
5174
5175 // Only show debugging message if this record was not about to expire anyway
5176 if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond)
5177 debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5178 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
5179
5180 if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond)
5181 rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query
5182 rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics
5183 rr->MPUnansweredKA = 0;
5184 rr->MPExpectingKA = mDNSfalse;
5185
5186 if (remain < kDefaultReconfirmTimeForNoAnswer)
5187 remain = kDefaultReconfirmTimeForNoAnswer;
5188 mDNS_Reconfirm_internal(m, rr, remain);
5189 }
5190 }
5191
5192 while (DupQuestions)
5193 {
5194 int i;
5195 DNSQuestion *q = DupQuestions;
5196 DupQuestions = q->NextInDQList;
5197 q->NextInDQList = mDNSNULL;
5198 i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type);
5199 debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID,
5200 srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i);
5201 }
5202
5203 return(responseptr);
5204 }
5205
5206 mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
5207 const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5208 const mDNSInterfaceID InterfaceID)
5209 {
5210 mDNSu8 *responseend = mDNSNULL;
5211 mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr);
5212
5213 if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))
5214 {
5215 LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)",
5216 srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
5217 msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,",
5218 msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,",
5219 msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,",
5220 msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
5221 return;
5222 }
5223
5224 verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5225 srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
5226 msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,",
5227 msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,",
5228 msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,",
5229 msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
5230
5231 responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID,
5232 (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg);
5233
5234 if (responseend) // If responseend is non-null, that means we built a unicast response packet
5235 {
5236 debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
5237 m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s",
5238 m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s",
5239 m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s",
5240 srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type);
5241 mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL);
5242 }
5243 }
5244
5245 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
5246 // the record list and/or question list.
5247 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
5248 mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
5249 const DNSMessage *const response, const mDNSu8 *end,
5250 const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5251 const mDNSInterfaceID InterfaceID)
5252 {
5253 int i;
5254 const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet
5255 CacheRecord *CacheFlushRecords = (CacheRecord*)1; // "(CacheRecord*)1" is special (non-zero) end-of-list marker
5256 CacheRecord **cfp = &CacheFlushRecords;
5257
5258 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
5259 // to guard against spoof responses, then the only credible protection against that is cryptographic
5260 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
5261 int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals;
5262
5263 (void)srcaddr; // Currently used only for display in debugging message
5264 (void)srcport;
5265 (void)dstport;
5266
5267 verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5268 srcaddr, dstaddr, InterfaceID,
5269 response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,",
5270 response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,",
5271 response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
5272 response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
5273
5274 // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
5275 if (!mDNSAddrIsDNSMulticast(dstaddr))
5276 {
5277 if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))
5278 return;
5279 // For now we don't put standard wide-area unicast responses in our main cache
5280 // (Later we should fix this and cache all known results in a unified manner.)
5281 if (response->h.id.NotAnInteger != 0 || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger)
5282 return;
5283 }
5284
5285 for (i = 0; i < totalrecords && ptr && ptr < end; i++)
5286 {
5287 const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd);
5288 ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec);
5289 if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting
5290
5291 // 1. Check that this packet resource record does not conflict with any of ours
5292 if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
5293 m->CurrentRecord = m->ResourceRecords;
5294 while (m->CurrentRecord)
5295 {
5296 AuthRecord *rr = m->CurrentRecord;
5297 m->CurrentRecord = rr->next;
5298 if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match...
5299 {
5300 // ... check to see if type and rdata are identical
5301 if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec))
5302 {
5303 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
5304 if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
5305 {
5306 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
5307 if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; }
5308 }
5309 else
5310 {
5311 if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; }
5312 else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
5313 }
5314 }
5315 // else, the packet RR has different type or different rdata -- check to see if this is a conflict
5316 else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r))
5317 {
5318 debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr));
5319 debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
5320
5321 // If this record is marked DependentOn another record for conflict detection purposes,
5322 // then *that* record has to be bumped back to probing state to resolve the conflict
5323 while (rr->DependentOn) rr = rr->DependentOn;
5324
5325 // If we've just whacked this record's ProbeCount, don't need to do it again
5326 if (rr->ProbeCount <= DefaultProbeCountForTypeUnique)
5327 {
5328 // If we'd previously verified this record, put it back to probing state and try again
5329 if (rr->resrec.RecordType == kDNSRecordTypeVerified)
5330 {
5331 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5332 rr->resrec.RecordType = kDNSRecordTypeUnique;
5333 rr->ProbeCount = DefaultProbeCountForTypeUnique + 1;
5334 rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique);
5335 InitializeLastAPTime(m, rr);
5336 RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate
5337 }
5338 // If we're probing for this record, we just failed
5339 else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
5340 {
5341 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5342 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
5343 }
5344 // We assumed this record must be unique, but we were wrong.
5345 // (e.g. There are two mDNSResponders on the same machine giving
5346 // different answers for the reverse mapping record.)
5347 // This is simply a misconfiguration, and we don't try to recover from it.
5348 else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
5349 {
5350 debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
5351 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5352 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
5353 }
5354 else
5355 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5356 rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5357 }
5358 }
5359 // Else, matching signature, different type or rdata, but not a considered a conflict.
5360 // If the packet record has the cache-flush bit set, then we check to see if we
5361 // have any record(s) of the same type that we should re-assert to rescue them
5362 // (see note about "multi-homing and bridged networks" at the end of this function).
5363 else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype)
5364 if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2)
5365 { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
5366 }
5367 }
5368
5369 // 2. See if we want to add this packet resource record to our cache
5370 if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in
5371 {
5372 const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
5373 CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
5374 CacheRecord *rr;
5375 // 2a. Check if this packet resource record is already in our cache
5376 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5377 {
5378 // If we found this exact resource record, refresh its TTL
5379 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
5380 {
5381 if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5382 verbosedebugf("Found record size %5d interface %p already in cache: %s",
5383 m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
5384 rr->TimeRcvd = m->timenow;
5385
5386 if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
5387 {
5388 // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
5389 if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList)
5390 { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
5391
5392 // If this packet record is marked unique, and our previous cached copy was not, then fix it
5393 if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
5394 {
5395 DNSQuestion *q;
5396 for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++;
5397 rr->resrec.RecordType = m->rec.r.resrec.RecordType;
5398 }
5399 }
5400
5401 if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength))
5402 {
5403 // If the rdata of the packet record differs in name capitalization from the record in our cache
5404 // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
5405 // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
5406 rr->resrec.rroriginalttl = 0;
5407 rr->UnansweredQueries = MaxUnansweredQueries;
5408 SetNextCacheCheckTime(m, rr);
5409 // DO NOT break out here -- we want to continue as if we never found it
5410 }
5411 else if (m->rec.r.resrec.rroriginalttl > 0)
5412 {
5413 rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl;
5414 rr->UnansweredQueries = 0;
5415 rr->MPUnansweredQ = 0;
5416 rr->MPUnansweredKA = 0;
5417 rr->MPExpectingKA = mDNSfalse;
5418 SetNextCacheCheckTime(m, rr);
5419 break;
5420 }
5421 else
5422 {
5423 // If the packet TTL is zero, that means we're deleting this record.
5424 // To give other hosts on the network a chance to protest, we push the deletion
5425 // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
5426 // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
5427 // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
5428 rr->resrec.rroriginalttl = 1;
5429 rr->UnansweredQueries = MaxUnansweredQueries;
5430 SetNextCacheCheckTime(m, rr);
5431 break;
5432 }
5433 }
5434 }
5435
5436 // If packet resource record not in our cache, add it now
5437 // (unless it is just a deletion of a record we never had, in which case we don't care)
5438 if (!rr && m->rec.r.resrec.rroriginalttl > 0)
5439 {
5440 // If we don't have a CacheGroup for this name, make one now
5441 if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec);
5442 if (cg) rr = GetCacheRecord(m, cg, m->rec.r.resrec.rdlength); // Make a cache record, being careful not to recycle cg
5443 if (!rr) NoCacheAnswer(m, &m->rec.r);
5444 else
5445 {
5446 RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer
5447 *rr = m->rec.r; // Block copy the CacheRecord object
5448 rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment
5449 rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header
5450 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
5451 { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
5452 // If this is an oversized record with external storage allocated, copy rdata to external storage
5453 if (rr->resrec.rdata != (RData*)&rr->rdatastorage && !(m->rec.r.resrec.rdlength > InlineCacheRDSize))
5454 LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c);
5455 if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5456 mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength);
5457 rr->next = mDNSNULL; // Clear 'next' pointer
5458 *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list
5459 cg->rrcache_tail = &(rr->next); // Advance tail pointer
5460 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have
5461 rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event
5462 else
5463 rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot);
5464 CacheRecordAdd(m, rr);
5465 // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us
5466 SetNextCacheCheckTime(m, rr);
5467 }
5468 }
5469 }
5470 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5471 }
5472
5473 exit:
5474 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5475
5476 // If we've just received one or more records with their cache flush bits set,
5477 // then scan that cache slot to see if there are any old stale records we need to flush
5478 while (CacheFlushRecords != (CacheRecord*)1)
5479 {
5480 CacheRecord *r1 = CacheFlushRecords, *r2;
5481 const mDNSu32 slot = HashSlot(r1->resrec.name);
5482 CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
5483 CacheFlushRecords = CacheFlushRecords->NextInCFList;
5484 r1->NextInCFList = mDNSNULL;
5485 for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
5486 if (SameResourceRecordSignature(&r1->resrec, &r2->resrec))
5487 {
5488 // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
5489 // else, if record is old, mark it to be flushed
5490 if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond)
5491 r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
5492 else
5493 {
5494 verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name->c, DNSTypeName(r2->resrec.rrtype));
5495 // We set stale records to expire in one second.
5496 // This gives the owner a chance to rescue it if necessary.
5497 // This is important in the case of multi-homing and bridged networks:
5498 // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
5499 // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
5500 // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
5501 // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
5502 // By delaying the deletion by one second, we give X a change to notice that this bridging has
5503 // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
5504 // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
5505 // final expiration queries for this record.
5506 r2->resrec.rroriginalttl = 1;
5507 r2->TimeRcvd = m->timenow;
5508 r2->UnansweredQueries = MaxUnansweredQueries;
5509 SetNextCacheCheckTime(m, r2);
5510 }
5511 }
5512 if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to
5513 {
5514 r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
5515 if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
5516 }
5517 }
5518 }
5519
5520 mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
5521 const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
5522 const mDNSInterfaceID InterfaceID)
5523 {
5524 DNSMessage *msg = (DNSMessage *)pkt;
5525 const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
5526 const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
5527 mDNSu8 QR_OP;
5528 mDNSu8 *ptr = mDNSNULL;
5529 const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
5530
5531 #ifndef UNICAST_DISABLED
5532 mDNSIPPort NATPort = mDNSOpaque16fromIntVal(NATMAP_PORT);
5533
5534 if (srcport.NotAnInteger == NATPort.NotAnInteger)
5535 {
5536 mDNS_Lock(m);
5537 uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
5538 mDNS_Unlock(m);
5539 return;
5540 }
5541 #endif
5542 if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; }
5543 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
5544 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
5545 ptr = (mDNSu8 *)&msg->h.numQuestions;
5546 msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
5547 msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
5548 msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
5549 msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
5550
5551 if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
5552
5553 // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
5554 // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
5555 if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; }
5556
5557 mDNS_Lock(m);
5558 m->PktNum++;
5559 #ifndef UNICAST_DISABLED
5560 if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR))
5561 uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
5562 // Note: mDNSCore also needs to get access to received unicast responses
5563 #endif
5564 if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
5565 else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
5566 else if (QR_OP != UpdateR)
5567 LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
5568 msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID);
5569
5570 // Packet reception often causes a change to the task list:
5571 // 1. Inbound queries can cause us to need to send responses
5572 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
5573 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
5574 // 4. Response packets that answer questions may cause our client to issue new questions
5575 mDNS_Unlock(m);
5576 }
5577
5578 // ***************************************************************************
5579 #if COMPILER_LIKES_PRAGMA_MARK
5580 #pragma mark -
5581 #pragma mark -
5582 #pragma mark - Searcher Functions
5583 #endif
5584
5585 #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5586
5587 mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
5588 {
5589 DNSQuestion *q;
5590 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
5591 // This prevents circular references, where two questions are each marked as a duplicate of the other.
5592 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
5593 // further in the list.
5594 for (q = m->Questions; q && q != question; q=q->next) // Scan our list of questions
5595 if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID,
5596 SameQTarget(q, question) && // and same unicast/multicast target settings
5597 q->qtype == question->qtype && // type,
5598 q->qclass == question->qclass && // class,
5599 q->qnamehash == question->qnamehash &&
5600 SameDomainName(&q->qname, &question->qname)) // and name
5601 return(q);
5602 return(mDNSNULL);
5603 }
5604
5605 // This is called after a question is deleted, in case other identical questions were being
5606 // suppressed as duplicates
5607 mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question)
5608 {
5609 DNSQuestion *q;
5610 for (q = m->Questions; q; q=q->next) // Scan our list of questions
5611 if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate
5612 {
5613 q->ThisQInterval = question->ThisQInterval;
5614 q->RequestUnicast = question->RequestUnicast;
5615 q->LastQTime = question->LastQTime;
5616 q->RecentAnswerPkts = 0;
5617 q->DuplicateOf = FindDuplicateQuestion(m, q);
5618 q->LastQTxTime = question->LastQTxTime;
5619 SetNextQueryTime(m,q);
5620 }
5621 }
5622
5623 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
5624 ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger))
5625
5626 mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
5627 {
5628 if (question->Target.type && !ValidQuestionTarget(question))
5629 {
5630 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5631 question->Target.type, mDNSVal16(question->TargetPort));
5632 question->Target.type = mDNSAddrType_None;
5633 }
5634
5635 if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID
5636 {
5637 question->TargetPort = zeroIPPort;
5638 question->TargetQID = zeroID;
5639 }
5640
5641 #ifndef UNICAST_DISABLED
5642 // If the client has specified 'kDNSServiceFlagsForceMulticast'
5643 // then we do a multicast query on that interface, even for unicast domains.
5644 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5645 question->uDNS_info.id = zeroID;
5646 else return uDNS_StartQuery(m, question);
5647 #else
5648 question->uDNS_info.id = zeroID;
5649 #endif // UNICAST_DISABLED
5650
5651 //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5652
5653 if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated
5654 return(mStatus_NoCache);
5655 else
5656 {
5657 int i;
5658 // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5659 DNSQuestion **q = &m->Questions;
5660 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5661 while (*q && *q != question) q=&(*q)->next;
5662
5663 if (*q)
5664 {
5665 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5666 question->qname.c, DNSTypeName(question->qtype));
5667 return(mStatus_AlreadyRegistered);
5668 }
5669
5670 // If this question is referencing a specific interface, make sure it exists
5671 if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly)
5672 {
5673 NetworkInterfaceInfo *intf;
5674 for (intf = m->HostInterfaces; intf; intf = intf->next)
5675 if (intf->InterfaceID == question->InterfaceID) break;
5676 if (!intf)
5677 {
5678 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID);
5679 return(mStatus_BadInterfaceErr);
5680 }
5681 }
5682
5683 if (!ValidateDomainName(&question->qname))
5684 {
5685 LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5686 return(mStatus_Invalid);
5687 }
5688
5689 // Note: In the case where we already have the answer to this question in our cache, that may be all the client
5690 // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5691 // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds
5692 // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately.
5693 if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
5694
5695 question->next = mDNSNULL;
5696 question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion()
5697 question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname));
5698 question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question
5699 question->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
5700 question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization
5701 question->LastAnswerPktNum = m->PktNum;
5702 question->RecentAnswerPkts = 0;
5703 question->CurrentAnswers = 0;
5704 question->LargeAnswers = 0;
5705 question->UniqueAnswers = 0;
5706 question->DuplicateOf = FindDuplicateQuestion(m, question);
5707 question->NextInDQList = mDNSNULL;
5708 for (i=0; i<DupSuppressInfoSize; i++)
5709 question->DupSuppress[i].InterfaceID = mDNSNULL;
5710 // question->InterfaceID must be already set by caller
5711 question->SendQNow = mDNSNULL;
5712 question->SendOnAll = mDNSfalse;
5713 question->LastQTxTime = m->timenow;
5714
5715 if (!question->DuplicateOf)
5716 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
5717 question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question);
5718 else
5719 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5720 question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf);
5721
5722 *q = question;
5723 if (question->InterfaceID == mDNSInterface_LocalOnly)
5724 {
5725 if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
5726 }
5727 else
5728 {
5729 if (!m->NewQuestions) m->NewQuestions = question;
5730 SetNextQueryTime(m,question);
5731 }
5732
5733 return(mStatus_NoError);
5734 }
5735 }
5736
5737 mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
5738 {
5739 const mDNSu32 slot = HashSlot(&question->qname);
5740 CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
5741 CacheRecord *rr;
5742 DNSQuestion **q = &m->Questions;
5743
5744 if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question);
5745
5746 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5747 while (*q && *q != question) q=&(*q)->next;
5748 if (*q) *q = (*q)->next;
5749 else
5750 {
5751 if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active
5752 LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5753 question->qname.c, DNSTypeName(question->qtype));
5754 return(mStatus_BadReferenceErr);
5755 }
5756
5757 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5758 UpdateQuestionDuplicates(m, question);
5759 // But don't trash ThisQInterval until afterwards.
5760 question->ThisQInterval = -1;
5761
5762 // If there are any cache records referencing this as their active question, then see if any other
5763 // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5764 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5765 {
5766 if (rr->CRActiveQuestion == question)
5767 {
5768 DNSQuestion *q;
5769 for (q = m->Questions; q; q=q->next) // Scan our list of questions
5770 if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
5771 break;
5772 verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q);
5773 rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null
5774 if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count
5775 }
5776 }
5777
5778 // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at,
5779 // bump its pointer forward one question.
5780 if (m->CurrentQuestion == question)
5781 {
5782 debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5783 question->qname.c, DNSTypeName(question->qtype));
5784 m->CurrentQuestion = question->next;
5785 }
5786
5787 if (m->NewQuestions == question)
5788 {
5789 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5790 question->qname.c, DNSTypeName(question->qtype));
5791 m->NewQuestions = question->next;
5792 }
5793
5794 if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
5795
5796 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5797 question->next = mDNSNULL;
5798 return(mStatus_NoError);
5799 }
5800
5801 mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
5802 {
5803 mStatus status;
5804 mDNS_Lock(m);
5805 status = mDNS_StartQuery_internal(m, question);
5806 mDNS_Unlock(m);
5807 return(status);
5808 }
5809
5810 mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
5811 {
5812 mStatus status;
5813 mDNS_Lock(m);
5814 status = mDNS_StopQuery_internal(m, question);
5815 mDNS_Unlock(m);
5816 return(status);
5817 }
5818
5819 mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr)
5820 {
5821 mStatus status;
5822 mDNS_Lock(m);
5823 status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
5824 mDNS_Unlock(m);
5825 return(status);
5826 }
5827
5828 mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
5829 {
5830 mStatus status = mStatus_BadReferenceErr;
5831 CacheRecord *cr;
5832 mDNS_Lock(m);
5833 cr = FindIdenticalRecordInCache(m, rr);
5834 if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
5835 mDNS_Unlock(m);
5836 return(status);
5837 }
5838
5839 mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
5840 const domainname *const srv, const domainname *const domain,
5841 const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context)
5842 {
5843 question->InterfaceID = InterfaceID;
5844 question->Target = zeroAddr;
5845 question->qtype = kDNSType_PTR;
5846 question->qclass = kDNSClass_IN;
5847 question->LongLived = mDNSfalse;
5848 question->ExpectUnique = mDNSfalse;
5849 question->ForceMCast = ForceMCast;
5850 question->QuestionCallback = Callback;
5851 question->QuestionContext = Context;
5852 if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
5853
5854 #ifndef UNICAST_DISABLED
5855 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5856 {
5857 question->LongLived = mDNSfalse;
5858 question->uDNS_info.id = zeroID;
5859 return(mDNS_StartQuery(m, question));
5860 }
5861 else
5862 {
5863 mStatus status;
5864 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5865 mDNS_Lock(m);
5866 question->LongLived = mDNStrue;
5867 status = uDNS_StartQuery(m, question);
5868 mDNS_Unlock(m);
5869 return(status);
5870 }
5871 #else
5872 return(mDNS_StartQuery(m, question));
5873 #endif // UNICAST_DISABLED
5874 }
5875
5876 mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m)
5877 {
5878 NetworkInterfaceInfo *intf;
5879 for (intf = m->HostInterfaces; intf; intf = intf->next)
5880 if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue);
5881 return(mDNSfalse);
5882 }
5883
5884 mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5885 {
5886 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5887 mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger);
5888 if (!AddRecord) return;
5889 if (answer->rrtype != kDNSType_SRV) return;
5890
5891 query->info->port = answer->rdata->u.srv.port;
5892
5893 // If this is our first answer, then set the GotSRV flag and start the address query
5894 if (!query->GotSRV)
5895 {
5896 query->GotSRV = mDNStrue;
5897 query->qAv4.InterfaceID = answer->InterfaceID;
5898 AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
5899 query->qAv6.InterfaceID = answer->InterfaceID;
5900 AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
5901 mDNS_StartQuery(m, &query->qAv4);
5902 // Only do the AAAA query if this machine actually has IPv6 active
5903 if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6);
5904 }
5905 // If this is not our first answer, only re-issue the address query if the target host name has changed
5906 else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) ||
5907 !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target))
5908 {
5909 mDNS_StopQuery(m, &query->qAv4);
5910 if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6);
5911 if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged)
5912 {
5913 // If we get here, it means:
5914 // 1. This is not our first SRV answer
5915 // 2. The interface ID is different, but the target host and port are the same
5916 // This implies that we're seeing the exact same SRV record on more than one interface, so we should
5917 // make our address queries at least as broad as the original SRV query so that we catch all the answers.
5918 query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface
5919 query->qAv6.InterfaceID = query->qSRV.InterfaceID;
5920 }
5921 else
5922 {
5923 query->qAv4.InterfaceID = answer->InterfaceID;
5924 AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
5925 query->qAv6.InterfaceID = answer->InterfaceID;
5926 AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
5927 }
5928 debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c);
5929 mDNS_StartQuery(m, &query->qAv4);
5930 // Only do the AAAA query if this machine actually has IPv6 active
5931 if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6);
5932 }
5933 else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
5934 {
5935 if (++query->Answers >= 100)
5936 debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
5937 query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c,
5938 mDNSVal16(answer->rdata->u.srv.port));
5939 query->ServiceInfoQueryCallback(m, query);
5940 }
5941 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5942 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5943 }
5944
5945 mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5946 {
5947 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5948 if (!AddRecord) return;
5949 if (answer->rrtype != kDNSType_TXT) return;
5950 if (answer->rdlength > sizeof(query->info->TXTinfo)) return;
5951
5952 query->GotTXT = mDNStrue;
5953 query->info->TXTlen = answer->rdlength;
5954 query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero
5955 mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength);
5956
5957 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
5958
5959 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5960 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5961 if (query->ServiceInfoQueryCallback && query->GotADD)
5962 {
5963 if (++query->Answers >= 100)
5964 debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
5965 query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c);
5966 query->ServiceInfoQueryCallback(m, query);
5967 }
5968 }
5969
5970 mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5971 {
5972 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5973 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
5974 if (!AddRecord) return;
5975
5976 if (answer->rrtype == kDNSType_A)
5977 {
5978 query->info->ip.type = mDNSAddrType_IPv4;
5979 query->info->ip.ip.v4 = answer->rdata->u.ipv4;
5980 }
5981 else if (answer->rrtype == kDNSType_AAAA)
5982 {
5983 query->info->ip.type = mDNSAddrType_IPv6;
5984 query->info->ip.ip.v6 = answer->rdata->u.ipv6;
5985 }
5986 else
5987 {
5988 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype));
5989 return;
5990 }
5991
5992 query->GotADD = mDNStrue;
5993 query->info->InterfaceID = answer->InterfaceID;
5994
5995 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
5996
5997 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5998 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5999 if (query->ServiceInfoQueryCallback && query->GotTXT)
6000 {
6001 if (++query->Answers >= 100)
6002 {
6003 if (answer->rrtype == kDNSType_A)
6004 debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv4);
6005 else
6006 debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6);
6007 }
6008 query->ServiceInfoQueryCallback(m, query);
6009 }
6010 }
6011
6012 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
6013 // If the query is not interface-specific, then InterfaceID may be zero
6014 // Each time the Callback is invoked, the remainder of the fields will have been filled in
6015 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
6016 mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
6017 ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context)
6018 {
6019 mStatus status;
6020 mDNS_Lock(m);
6021
6022 query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6023 query->qSRV.InterfaceID = info->InterfaceID;
6024 query->qSRV.Target = zeroAddr;
6025 AssignDomainName(&query->qSRV.qname, &info->name);
6026 query->qSRV.qtype = kDNSType_SRV;
6027 query->qSRV.qclass = kDNSClass_IN;
6028 query->qSRV.LongLived = mDNSfalse;
6029 query->qSRV.ExpectUnique = mDNStrue;
6030 query->qSRV.ForceMCast = mDNSfalse;
6031 query->qSRV.QuestionCallback = FoundServiceInfoSRV;
6032 query->qSRV.QuestionContext = query;
6033
6034 query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6035 query->qTXT.InterfaceID = info->InterfaceID;
6036 query->qTXT.Target = zeroAddr;
6037 AssignDomainName(&query->qTXT.qname, &info->name);
6038 query->qTXT.qtype = kDNSType_TXT;
6039 query->qTXT.qclass = kDNSClass_IN;
6040 query->qTXT.LongLived = mDNSfalse;
6041 query->qTXT.ExpectUnique = mDNStrue;
6042 query->qTXT.ForceMCast = mDNSfalse;
6043 query->qTXT.QuestionCallback = FoundServiceInfoTXT;
6044 query->qTXT.QuestionContext = query;
6045
6046 query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6047 query->qAv4.InterfaceID = info->InterfaceID;
6048 query->qAv4.Target = zeroAddr;
6049 query->qAv4.qname.c[0] = 0;
6050 query->qAv4.qtype = kDNSType_A;
6051 query->qAv4.qclass = kDNSClass_IN;
6052 query->qAv4.LongLived = mDNSfalse;
6053 query->qAv4.ExpectUnique = mDNStrue;
6054 query->qAv4.ForceMCast = mDNSfalse;
6055 query->qAv4.QuestionCallback = FoundServiceInfo;
6056 query->qAv4.QuestionContext = query;
6057
6058 query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6059 query->qAv6.InterfaceID = info->InterfaceID;
6060 query->qAv6.Target = zeroAddr;
6061 query->qAv6.qname.c[0] = 0;
6062 query->qAv6.qtype = kDNSType_AAAA;
6063 query->qAv6.qclass = kDNSClass_IN;
6064 query->qAv6.LongLived = mDNSfalse;
6065 query->qAv6.ExpectUnique = mDNStrue;
6066 query->qAv6.ForceMCast = mDNSfalse;
6067 query->qAv6.QuestionCallback = FoundServiceInfo;
6068 query->qAv6.QuestionContext = query;
6069
6070 query->GotSRV = mDNSfalse;
6071 query->GotTXT = mDNSfalse;
6072 query->GotADD = mDNSfalse;
6073 query->Answers = 0;
6074
6075 query->info = info;
6076 query->ServiceInfoQueryCallback = Callback;
6077 query->ServiceInfoQueryContext = Context;
6078
6079 // info->name = Must already be set up by client
6080 // info->interface = Must already be set up by client
6081 info->ip = zeroAddr;
6082 info->port = zeroIPPort;
6083 info->TXTlen = 0;
6084
6085 // We use mDNS_StartQuery_internal here because we're already holding the lock
6086 status = mDNS_StartQuery_internal(m, &query->qSRV);
6087 if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT);
6088 if (status != mStatus_NoError) mDNS_StopResolveService(m, query);
6089
6090 mDNS_Unlock(m);
6091 return(status);
6092 }
6093
6094 mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q)
6095 {
6096 mDNS_Lock(m);
6097 // We use mDNS_StopQuery_internal here because we're already holding the lock
6098 if (q->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qSRV);
6099 if (q->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qTXT);
6100 if (q->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv4);
6101 if (q->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv6);
6102 mDNS_Unlock(m);
6103 }
6104
6105 mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
6106 const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
6107 {
6108 question->InterfaceID = InterfaceID;
6109 question->Target = zeroAddr;
6110 question->qtype = kDNSType_PTR;
6111 question->qclass = kDNSClass_IN;
6112 question->LongLived = mDNSfalse;
6113 question->ExpectUnique = mDNSfalse;
6114 question->ForceMCast = mDNSfalse;
6115 question->QuestionCallback = Callback;
6116 question->QuestionContext = Context;
6117 if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
6118 if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
6119 if (!dom) dom = &localdomain;
6120 if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr);
6121 return(mDNS_StartQuery(m, question));
6122 }
6123
6124 // ***************************************************************************
6125 #if COMPILER_LIKES_PRAGMA_MARK
6126 #pragma mark -
6127 #pragma mark - Responder Functions
6128 #endif
6129
6130 mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
6131 {
6132 mStatus status;
6133 mDNS_Lock(m);
6134 status = mDNS_Register_internal(m, rr);
6135 mDNS_Unlock(m);
6136 return(status);
6137 }
6138
6139 mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
6140 const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback)
6141 {
6142 #ifndef UNICAST_DISABLED
6143 mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name));
6144 #else
6145 mDNSBool unicast = mDNSfalse;
6146 #endif
6147
6148 if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata))
6149 { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); }
6150
6151 mDNS_Lock(m);
6152
6153 // If TTL is unspecified, leave TTL unchanged
6154 if (newttl == 0) newttl = rr->resrec.rroriginalttl;
6155
6156 // If we already have an update queued up which has not gone through yet,
6157 // give the client a chance to free that memory
6158 if (!unicast && rr->NewRData)
6159 {
6160 RData *n = rr->NewRData;
6161 rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
6162 if (rr->UpdateCallback)
6163 rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary
6164 }
6165
6166 rr->NewRData = newrdata;
6167 rr->newrdlength = newrdlength;
6168 rr->UpdateCallback = Callback;
6169
6170 if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); }
6171
6172 if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
6173 CompleteRDataUpdate(m, rr);
6174 else
6175 {
6176 domainlabel name;
6177 domainname type, domain;
6178 DeconstructServiceName(rr->resrec.name, &name, &type, &domain);
6179 rr->AnnounceCount = InitialAnnounceCount;
6180 // iChat often does suprious record updates where no data has changed. For the _presence service type, using
6181 // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
6182 // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
6183 // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
6184 // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
6185 if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1;
6186 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
6187 InitializeLastAPTime(m, rr);
6188 while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
6189 if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--;
6190 if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval);
6191 if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1);
6192 if (rr->UpdateCredits <= 5)
6193 {
6194 mDNSu32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
6195 if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond);
6196 rr->ThisAPInterval *= 4;
6197 rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval;
6198 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name->c, delay, delay > 1 ? "s" : "");
6199 }
6200 rr->resrec.rroriginalttl = newttl;
6201 }
6202
6203 mDNS_Unlock(m);
6204 return(mStatus_NoError);
6205 }
6206
6207 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
6208 // the record list and/or question list.
6209 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6210 mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr)
6211 {
6212 mStatus status;
6213 mDNS_Lock(m);
6214 status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
6215 mDNS_Unlock(m);
6216 return(status);
6217 }
6218
6219 mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
6220
6221 mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
6222 {
6223 NetworkInterfaceInfo *intf;
6224 for (intf = m->HostInterfaces; intf; intf = intf->next)
6225 if (intf->Advertise) break;
6226 return(intf);
6227 }
6228
6229 mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6230 {
6231 char buffer[256];
6232 NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
6233 if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
6234
6235 // Send dynamic update for non-linklocal IPv4 Addresses
6236 mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set);
6237 mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
6238 mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
6239
6240 #if ANSWER_REMOTE_HOSTNAME_QUERIES
6241 set->RR_A .AllowRemoteQuery = mDNStrue;
6242 set->RR_PTR .AllowRemoteQuery = mDNStrue;
6243 set->RR_HINFO.AllowRemoteQuery = mDNStrue;
6244 #endif
6245 // 1. Set up Address record to map from host name ("foo.local.") to IP address
6246 // 2. Set up reverse-lookup PTR record to map from our address back to our host name
6247 AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname);
6248 if (set->ip.type == mDNSAddrType_IPv4)
6249 {
6250 set->RR_A.resrec.rrtype = kDNSType_A;
6251 set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4;
6252 // Note: This is reverse order compared to a normal dotted-decimal IP address
6253 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.",
6254 set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]);
6255 }
6256 else if (set->ip.type == mDNSAddrType_IPv6)
6257 {
6258 int i;
6259 set->RR_A.resrec.rrtype = kDNSType_AAAA;
6260 set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6;
6261 for (i = 0; i < 16; i++)
6262 {
6263 static const char hexValues[] = "0123456789ABCDEF";
6264 buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F];
6265 buffer[i * 4 + 1] = '.';
6266 buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4];
6267 buffer[i * 4 + 3] = '.';
6268 }
6269 mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
6270 }
6271
6272 MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer);
6273 set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
6274 set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
6275
6276 set->RR_A.RRSet = &primary->RR_A; // May refer to self
6277
6278 mDNS_Register_internal(m, &set->RR_A);
6279 mDNS_Register_internal(m, &set->RR_PTR);
6280
6281 if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254)
6282 {
6283 mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data;
6284 AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname);
6285 set->RR_HINFO.DependentOn = &set->RR_A;
6286 mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]);
6287 p += 1 + (int)p[0];
6288 mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]);
6289 mDNS_Register_internal(m, &set->RR_HINFO);
6290 }
6291 else
6292 {
6293 debugf("Not creating HINFO record: platform support layer provided no information");
6294 set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered;
6295 }
6296 }
6297
6298 mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6299 {
6300 NetworkInterfaceInfo *intf;
6301
6302 // If we still have address records referring to this one, update them
6303 NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
6304 AuthRecord *A = primary ? &primary->RR_A : mDNSNULL;
6305 for (intf = m->HostInterfaces; intf; intf = intf->next)
6306 if (intf->RR_A.RRSet == &set->RR_A)
6307 intf->RR_A.RRSet = A;
6308
6309 // Unregister these records.
6310 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
6311 // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
6312 // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
6313 // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
6314 if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal);
6315 if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal);
6316 if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal);
6317 }
6318
6319 mDNSexport void mDNS_SetFQDN(mDNS *const m)
6320 {
6321 domainname newmname;
6322 NetworkInterfaceInfo *intf;
6323 AuthRecord *rr;
6324 newmname.c[0] = 0;
6325
6326 if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6327 if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6328 if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
6329
6330 mDNS_Lock(m);
6331 AssignDomainName(&m->MulticastHostname, &newmname);
6332
6333 // 1. Stop advertising our address records on all interfaces
6334 for (intf = m->HostInterfaces; intf; intf = intf->next)
6335 if (intf->Advertise) DeadvertiseInterface(m, intf);
6336
6337 // 2. Start advertising our address records using the new name
6338 for (intf = m->HostInterfaces; intf; intf = intf->next)
6339 if (intf->Advertise) AdvertiseInterface(m, intf);
6340
6341 // 3. Make sure that any SRV records (and the like) that reference our
6342 // host name in their rdata get updated to reference this new host name
6343 for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr);
6344 for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr);
6345
6346 mDNS_Unlock(m);
6347 }
6348
6349 mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6350 {
6351 (void)rr; // Unused parameter
6352
6353 #if MDNS_DEBUGMSGS
6354 {
6355 char *msg = "Unknown result";
6356 if (result == mStatus_NoError) msg = "Name registered";
6357 else if (result == mStatus_NameConflict) msg = "Name conflict";
6358 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6359 }
6360 #endif
6361
6362 if (result == mStatus_NoError)
6363 {
6364 // Notify the client that the host name is successfully registered
6365 if (m->MainCallback)
6366 {
6367 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
6368 m->MainCallback(m, result);
6369 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6370 }
6371 }
6372 else if (result == mStatus_NameConflict)
6373 {
6374 domainlabel oldlabel = m->hostlabel;
6375
6376 // 1. First give the client callback a chance to pick a new name
6377 if (m->MainCallback)
6378 {
6379 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
6380 m->MainCallback(m, mStatus_NameConflict);
6381 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6382 }
6383
6384 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6385 if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
6386 IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
6387
6388 // 3. Generate the FQDNs from the hostlabel,
6389 // and make sure all SRV records, etc., are updated to reference our new hostname
6390 mDNS_SetFQDN(m);
6391 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c);
6392 }
6393 else if (result == mStatus_MemFree)
6394 {
6395 // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
6396 // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
6397 debugf("mDNS_HostNameCallback: MemFree (ignored)");
6398 }
6399 else
6400 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c);
6401 }
6402
6403 mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
6404 {
6405 NetworkInterfaceInfo *intf;
6406 active->IPv4Available = mDNSfalse;
6407 active->IPv6Available = mDNSfalse;
6408 for (intf = m->HostInterfaces; intf; intf = intf->next)
6409 if (intf->InterfaceID == active->InterfaceID)
6410 {
6411 if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue;
6412 if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue;
6413 }
6414 }
6415
6416 mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSs32 delay)
6417 {
6418 mDNSBool FirstOfType = mDNStrue;
6419 NetworkInterfaceInfo **p = &m->HostInterfaces;
6420
6421 if (!set->InterfaceID)
6422 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
6423
6424 if (!mDNSAddressIsValidNonZero(&set->mask))
6425 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
6426
6427 mDNS_Lock(m);
6428
6429 // Assume this interface will be active
6430 set->InterfaceActive = mDNStrue;
6431 set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx);
6432 set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx);
6433
6434 while (*p)
6435 {
6436 if (*p == set)
6437 {
6438 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6439 mDNS_Unlock(m);
6440 return(mStatus_AlreadyRegistered);
6441 }
6442
6443 // This InterfaceID is already in the list, so mark this interface inactive for now
6444 if ((*p)->InterfaceID == set->InterfaceID)
6445 {
6446 set->InterfaceActive = mDNSfalse;
6447 if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse;
6448 if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue;
6449 if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue;
6450 }
6451
6452 p=&(*p)->next;
6453 }
6454
6455 set->next = mDNSNULL;
6456 *p = set;
6457
6458 if (set->Advertise)
6459 AdvertiseInterface(m, set);
6460
6461 debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip,
6462 set->InterfaceActive ?
6463 "not represented in list; marking active and retriggering queries" :
6464 "already represented in list; marking inactive for now");
6465
6466 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6467 // giving the false impression that there's an active representative of this interface when there really isn't.
6468 // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
6469 // even if we believe that we previously had an active representative of this interface.
6470 if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
6471 {
6472 DNSQuestion *q;
6473 AuthRecord *rr;
6474 mDNSs32 initial = InitialQuestionInterval;
6475
6476 // Use a small amount of randomness:
6477 // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at
6478 // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment.
6479 if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
6480
6481 if (delay)
6482 {
6483 LogMsg("Repeated transitions for interface %s (%#a); delaying packets by %d seconds",
6484 set->ifname, &set->ip, delay/mDNSPlatformOneSecond);
6485 initial = InitialQuestionInterval * 8; // Delay between first and second queries is eight seconds
6486 if (!m->SuppressProbes ||
6487 m->SuppressProbes - (m->timenow + delay) < 0)
6488 m->SuppressProbes = (m->timenow + delay);
6489 }
6490 for (q = m->Questions; q; q=q->next) // Scan our list of questions
6491 if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface,
6492 { // then reactivate this question
6493 q->ThisQInterval = initial; // MUST be > zero for an active question
6494 q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
6495 q->LastQTime = m->timenow - q->ThisQInterval + delay;
6496 q->RecentAnswerPkts = 0;
6497 SetNextQueryTime(m,q);
6498 }
6499
6500 // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
6501 // we now need them to re-probe if necessary, and then re-announce.
6502 for (rr = m->ResourceRecords; rr; rr=rr->next)
6503 if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
6504 {
6505 if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
6506 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
6507 rr->AnnounceCount = delay ? (mDNSu8)1 : InitialAnnounceCount;
6508 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
6509 InitializeLastAPTime(m, rr);
6510 }
6511 }
6512
6513 mDNS_Unlock(m);
6514 return(mStatus_NoError);
6515 }
6516
6517 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6518 // the record list and/or question list.
6519 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6520 mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
6521 {
6522 NetworkInterfaceInfo **p = &m->HostInterfaces;
6523
6524 mDNSBool revalidate = mDNSfalse;
6525 // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6526 // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6527 // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6528 if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue;
6529
6530 mDNS_Lock(m);
6531
6532 // Find this record in our list
6533 while (*p && *p != set) p=&(*p)->next;
6534 if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; }
6535
6536 // Unlink this record from our list
6537 *p = (*p)->next;
6538 set->next = mDNSNULL;
6539
6540 if (!set->InterfaceActive)
6541 {
6542 // If this interface not the active member of its set, update the v4/v6Available flags for the active member
6543 NetworkInterfaceInfo *intf;
6544 for (intf = m->HostInterfaces; intf; intf = intf->next)
6545 if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID)
6546 UpdateInterfaceProtocols(m, intf);
6547 }
6548 else
6549 {
6550 NetworkInterfaceInfo *intf;
6551 for (intf = m->HostInterfaces; intf; intf = intf->next)
6552 if (intf->InterfaceID == set->InterfaceID)
6553 break;
6554 if (intf)
6555 {
6556 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6557 set->InterfaceID);
6558 intf->InterfaceActive = mDNStrue;
6559 UpdateInterfaceProtocols(m, intf);
6560
6561 // See if another representative *of the same type* exists. If not, we mave have gone from
6562 // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6563 for (intf = m->HostInterfaces; intf; intf = intf->next)
6564 if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type)
6565 break;
6566 if (!intf) revalidate = mDNStrue;
6567 }
6568 else
6569 {
6570 mDNSu32 slot;
6571 CacheGroup *cg;
6572 CacheRecord *rr;
6573 DNSQuestion *q;
6574 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6575 set->InterfaceID);
6576
6577 // 1. Deactivate any questions specific to this interface
6578 for (q = m->Questions; q; q=q->next)
6579 if (q->InterfaceID == set->InterfaceID)
6580 q->ThisQInterval = 0;
6581
6582 // 2. Flush any cache records received on this interface
6583 revalidate = mDNSfalse; // Don't revalidate if we're flushing the records
6584 FORALL_CACHERECORDS(slot, cg, rr)
6585 if (rr->resrec.InterfaceID == set->InterfaceID)
6586 PurgeCacheResourceRecord(m, rr);
6587 }
6588 }
6589
6590 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6591 if (set->Advertise) DeadvertiseInterface(m, set);
6592
6593 // If we have any cache records received on this interface that went away, then re-verify them.
6594 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6595 // giving the false impression that there's an active representative of this interface when there really isn't.
6596 // Don't need to do this when shutting down, because *all* interfaces are about to go away
6597 if (revalidate && !m->mDNS_shutdown)
6598 {
6599 mDNSu32 slot;
6600 CacheGroup *cg;
6601 CacheRecord *rr;
6602 m->NextCacheCheck = m->timenow;
6603 FORALL_CACHERECORDS(slot, cg, rr)
6604 if (rr->resrec.InterfaceID == set->InterfaceID)
6605 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect);
6606 }
6607
6608 mDNS_Unlock(m);
6609 }
6610
6611 mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6612 {
6613 ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6614 (void)m; // Unused parameter
6615
6616 #if MDNS_DEBUGMSGS
6617 {
6618 char *msg = "Unknown result";
6619 if (result == mStatus_NoError) msg = "Name Registered";
6620 else if (result == mStatus_NameConflict) msg = "Name Conflict";
6621 else if (result == mStatus_MemFree) msg = "Memory Free";
6622 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6623 }
6624 #endif
6625
6626 // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6627 if (result == mStatus_NoError && rr != &sr->RR_SRV) return;
6628
6629 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6630 if (result == mStatus_NameConflict)
6631 {
6632 sr->Conflict = mDNStrue; // Record that this service set had a conflict
6633 mDNS_DeregisterService(m, sr); // Unlink the records from our list
6634 return;
6635 }
6636
6637 if (result == mStatus_MemFree)
6638 {
6639 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6640 // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6641 mDNSu32 i;
6642 if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return;
6643 for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return;
6644
6645 // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6646 // then we can now report the NameConflict to the client
6647 if (sr->Conflict) result = mStatus_NameConflict;
6648 }
6649
6650 // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6651 // function is allowed to do anything, including deregistering this service and freeing its memory.
6652 if (sr->ServiceCallback)
6653 sr->ServiceCallback(m, sr, result);
6654 }
6655
6656 mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6657 {
6658 ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6659 if (sr->ServiceCallback)
6660 sr->ServiceCallback(m, sr, result);
6661 }
6662
6663 // Note:
6664 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6665 // Type is service type (e.g. "_ipp._tcp.")
6666 // Domain is fully qualified domain name (i.e. ending with a null label)
6667 // We always register a TXT, even if it is empty (so that clients are not
6668 // left waiting forever looking for a nonexistent record.)
6669 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6670 // then the default host name (m->MulticastHostname) is automatically used
6671 mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
6672 const domainlabel *const name, const domainname *const type, const domainname *const domain,
6673 const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
6674 AuthRecord *SubTypes, mDNSu32 NumSubTypes,
6675 const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
6676 {
6677 mStatus err;
6678 mDNSu32 i;
6679
6680 sr->ServiceCallback = Callback;
6681 sr->ServiceContext = Context;
6682 sr->Extras = mDNSNULL;
6683 sr->NumSubTypes = NumSubTypes;
6684 sr->SubTypes = SubTypes;
6685 sr->Conflict = mDNSfalse;
6686 if (host && host->c[0]) sr->Host = *host;
6687 else sr->Host.c[0] = 0;
6688
6689 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6690 if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
6691
6692 // Initialize the AuthRecord objects to sane values
6693 mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr);
6694 mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
6695 mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr);
6696 mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr);
6697
6698 // If the client is registering an oversized TXT record,
6699 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6700 if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen)
6701 sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen;
6702
6703 // Set up the record names
6704 // For now we only create an advisory record for the main type, not for subtypes
6705 // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6706 if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL)
6707 return(mStatus_BadParamErr);
6708 if (ConstructServiceName(sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
6709 if (ConstructServiceName(sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
6710 AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name);
6711
6712 // 1. Set up the ADV record rdata to advertise our service type
6713 AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name);
6714
6715 // 2. Set up the PTR record rdata to point to our service name
6716 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6717 AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name);
6718 sr->RR_PTR.Additional1 = &sr->RR_SRV;
6719 sr->RR_PTR.Additional2 = &sr->RR_TXT;
6720
6721 // 2a. Set up any subtype PTRs to point to our service name
6722 // If the client is using subtypes, it is the client's responsibility to have
6723 // already set the first label of the record name to the subtype being registered
6724 for (i=0; i<NumSubTypes; i++)
6725 {
6726 domainname st;
6727 AssignDomainName(&st, sr->SubTypes[i].resrec.name);
6728 st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6729 AppendDomainName(&st, type);
6730 mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
6731 if (ConstructServiceName(sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr);
6732 AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name);
6733 sr->SubTypes[i].Additional1 = &sr->RR_SRV;
6734 sr->SubTypes[i].Additional2 = &sr->RR_TXT;
6735 }
6736
6737 // 3. Set up the SRV record rdata.
6738 sr->RR_SRV.resrec.rdata->u.srv.priority = 0;
6739 sr->RR_SRV.resrec.rdata->u.srv.weight = 0;
6740 sr->RR_SRV.resrec.rdata->u.srv.port = port;
6741
6742 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6743 if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host);
6744 else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; }
6745
6746 // 4. Set up the TXT record rdata,
6747 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6748 if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0;
6749 else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c)
6750 {
6751 sr->RR_TXT.resrec.rdlength = txtlen;
6752 if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr);
6753 mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen);
6754 }
6755 sr->RR_TXT.DependentOn = &sr->RR_SRV;
6756
6757 #ifndef UNICAST_DISABLED
6758 // If the client has specified an explicit InterfaceID,
6759 // then we do a multicast registration on that interface, even for unicast domains.
6760 if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6761 {
6762 mStatus status;
6763 mDNS_Lock(m);
6764 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6765 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6766 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6767 // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6768 if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; }
6769 status = uDNS_RegisterService(m, sr);
6770 mDNS_Unlock(m);
6771 return(status);
6772 }
6773 #endif
6774 mDNS_Lock(m);
6775 err = mDNS_Register_internal(m, &sr->RR_SRV);
6776 if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
6777 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6778 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6779 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6780 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6781 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6782 if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV);
6783 for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]);
6784 if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR);
6785
6786 mDNS_Unlock(m);
6787
6788 if (err) mDNS_DeregisterService(m, sr);
6789 return(err);
6790 }
6791
6792 mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
6793 ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
6794 {
6795 ExtraResourceRecord **e;
6796 mStatus status;
6797
6798 extra->next = mDNSNULL;
6799 mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
6800 AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name);
6801
6802 #ifndef UNICAST_DISABLED
6803 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6804 {
6805 mDNS_Lock(m);
6806 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6807 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6808 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6809 // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck)
6810 if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0)
6811 { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; }
6812 status = uDNS_AddRecordToService(m, sr, extra);
6813 mDNS_Unlock(m);
6814 return status;
6815 }
6816 #endif
6817
6818 mDNS_Lock(m);
6819 e = &sr->Extras;
6820 while (*e) e = &(*e)->next;
6821
6822 if (ttl == 0) ttl = kStandardTTL;
6823
6824 extra->r.DependentOn = &sr->RR_SRV;
6825
6826 debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c);
6827
6828 status = mDNS_Register_internal(m, &extra->r);
6829 if (status == mStatus_NoError) *e = extra;
6830 mDNS_Unlock(m);
6831 return(status);
6832 }
6833
6834 mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context)
6835 {
6836 ExtraResourceRecord **e;
6837 mStatus status;
6838
6839 mDNS_Lock(m);
6840 e = &sr->Extras;
6841 while (*e && *e != extra) e = &(*e)->next;
6842 if (!*e)
6843 {
6844 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c);
6845 status = mStatus_BadReferenceErr;
6846 }
6847 else
6848 {
6849 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c);
6850 extra->r.RecordCallback = MemFreeCallback;
6851 extra->r.RecordContext = Context;
6852 *e = (*e)->next;
6853 #ifndef UNICAST_DISABLED
6854 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6855 status = uDNS_DeregisterRecord(m, &extra->r);
6856 else
6857 #endif
6858 status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal);
6859 }
6860 mDNS_Unlock(m);
6861 return(status);
6862 }
6863
6864 mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
6865 {
6866 // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
6867 // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
6868 domainlabel name1, name2;
6869 domainname type, domain;
6870 domainname *host = mDNSNULL;
6871 ExtraResourceRecord *extras = sr->Extras;
6872 mStatus err;
6873
6874 DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain);
6875 if (!newname)
6876 {
6877 name2 = name1;
6878 IncrementLabelSuffix(&name2, mDNStrue);
6879 newname = &name2;
6880 }
6881 LogMsg("Service \"%##s\" renamed to \"%#s\"", sr->RR_SRV.resrec.name->c, newname->c);
6882 if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
6883
6884 err = mDNS_RegisterService(m, sr, newname, &type, &domain,
6885 host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
6886 sr->SubTypes, sr->NumSubTypes,
6887 sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext);
6888
6889 // mDNS_RegisterService() just reset sr->Extras to NULL.
6890 // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
6891 // through the old list of extra records, and re-add them to our freshly created service registration
6892 while (!err && extras)
6893 {
6894 ExtraResourceRecord *e = extras;
6895 extras = extras->next;
6896 err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
6897 }
6898
6899 return(err);
6900 }
6901
6902 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
6903 // which may change the record list and/or question list.
6904 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6905 mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
6906 {
6907 // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
6908 if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV));
6909
6910 #ifndef UNICAST_DISABLED
6911 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6912 {
6913 mStatus status;
6914 mDNS_Lock(m);
6915 status = uDNS_DeregisterService(m, sr);
6916 mDNS_Unlock(m);
6917 return(status);
6918 }
6919 #endif
6920 if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
6921 {
6922 debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c);
6923 return(mStatus_BadReferenceErr);
6924 }
6925 else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
6926 {
6927 debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
6928 return(mStatus_NoError);
6929 }
6930 else
6931 {
6932 mDNSu32 i;
6933 mStatus status;
6934 ExtraResourceRecord *e;
6935 mDNS_Lock(m);
6936 e = sr->Extras;
6937
6938 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
6939 // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
6940 mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat);
6941 mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat);
6942
6943 mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal);
6944
6945 // We deregister all of the extra records, but we leave the sr->Extras list intact
6946 // in case the client wants to do a RenameAndReregister and reinstate the registration
6947 while (e)
6948 {
6949 mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat);
6950 e = e->next;
6951 }
6952
6953 for (i=0; i<sr->NumSubTypes; i++)
6954 mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal);
6955
6956 // Be sure to deregister the PTR last!
6957 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
6958 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
6959 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
6960 // we've deregistered all our records and done any other necessary cleanup before that happens.
6961 status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal);
6962 mDNS_Unlock(m);
6963 return(status);
6964 }
6965 }
6966
6967 // Create a registration that asserts that no such service exists with this name.
6968 // This can be useful where there is a given function is available through several protocols.
6969 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
6970 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
6971 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
6972 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
6973 mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
6974 const domainlabel *const name, const domainname *const type, const domainname *const domain,
6975 const domainname *const host,
6976 const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context)
6977 {
6978 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
6979 if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
6980 rr->resrec.rdata->u.srv.priority = 0;
6981 rr->resrec.rdata->u.srv.weight = 0;
6982 rr->resrec.rdata->u.srv.port = zeroIPPort;
6983 if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host);
6984 else rr->HostTarget = mDNStrue;
6985 return(mDNS_Register(m, rr));
6986 }
6987
6988 mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
6989 mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
6990 {
6991 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
6992 if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
6993 if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr);
6994 return(mDNS_Register(m, rr));
6995 }
6996
6997 // ***************************************************************************
6998 #if COMPILER_LIKES_PRAGMA_MARK
6999 #pragma mark -
7000 #pragma mark -
7001 #pragma mark - Startup and Shutdown
7002 #endif
7003
7004 mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7005 {
7006 if (storage && numrecords)
7007 {
7008 mDNSu32 i;
7009 debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity));
7010 for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1];
7011 storage[numrecords-1].next = m->rrcache_free;
7012 m->rrcache_free = storage;
7013 m->rrcache_size += numrecords;
7014 }
7015 }
7016
7017 mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7018 {
7019 mDNS_Lock(m);
7020 mDNS_GrowCache_internal(m, storage, numrecords);
7021 mDNS_Unlock(m);
7022 }
7023
7024 mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
7025 CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
7026 mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
7027 {
7028 mDNSu32 slot;
7029 mDNSs32 timenow;
7030 mStatus result;
7031
7032 if (!rrcachestorage) rrcachesize = 0;
7033
7034 m->p = p;
7035 m->KnownBugs = 0;
7036 m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
7037 m->AdvertiseLocalAddresses = AdvertiseLocalAddresses;
7038 m->mDNSPlatformStatus = mStatus_Waiting;
7039 m->UnicastPort4 = zeroIPPort;
7040 m->UnicastPort6 = zeroIPPort;
7041 m->MainCallback = Callback;
7042 m->MainContext = Context;
7043 m->rec.r.resrec.RecordType = 0;
7044
7045 // For debugging: To catch and report locking failures
7046 m->mDNS_busy = 0;
7047 m->mDNS_reentrancy = 0;
7048 m->mDNS_shutdown = mDNSfalse;
7049 m->lock_rrcache = 0;
7050 m->lock_Questions = 0;
7051 m->lock_Records = 0;
7052
7053 // Task Scheduling variables
7054 result = mDNSPlatformTimeInit();
7055 if (result != mStatus_NoError) return(result);
7056 m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF);
7057 timenow = mDNS_TimeNow_NoLock(m);
7058
7059 m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
7060 m->timenow_last = timenow;
7061 m->NextScheduledEvent = timenow;
7062 m->SuppressSending = timenow;
7063 m->NextCacheCheck = timenow + 0x78000000;
7064 m->NextScheduledQuery = timenow + 0x78000000;
7065 m->NextScheduledProbe = timenow + 0x78000000;
7066 m->NextScheduledResponse = timenow + 0x78000000;
7067 m->ExpectUnicastResponse = timenow + 0x78000000;
7068 m->RandomQueryDelay = 0;
7069 m->PktNum = 0;
7070 m->SendDeregistrations = mDNSfalse;
7071 m->SendImmediateAnswers = mDNSfalse;
7072 m->SleepState = mDNSfalse;
7073
7074 // These fields only required for mDNS Searcher...
7075 m->Questions = mDNSNULL;
7076 m->NewQuestions = mDNSNULL;
7077 m->CurrentQuestion = mDNSNULL;
7078 m->LocalOnlyQuestions = mDNSNULL;
7079 m->NewLocalOnlyQuestions = mDNSNULL;
7080 m->rrcache_size = 0;
7081 m->rrcache_totalused = 0;
7082 m->rrcache_active = 0;
7083 m->rrcache_report = 10;
7084 m->rrcache_free = mDNSNULL;
7085
7086 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL;
7087
7088 mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
7089
7090 // Fields below only required for mDNS Responder...
7091 m->hostlabel.c[0] = 0;
7092 m->nicelabel.c[0] = 0;
7093 m->MulticastHostname.c[0] = 0;
7094 m->HIHardware.c[0] = 0;
7095 m->HISoftware.c[0] = 0;
7096 m->ResourceRecords = mDNSNULL;
7097 m->DuplicateRecords = mDNSNULL;
7098 m->NewLocalRecords = mDNSNULL;
7099 m->CurrentRecord = mDNSNULL;
7100 m->HostInterfaces = mDNSNULL;
7101 m->ProbeFailTime = 0;
7102 m->NumFailedProbes = 0;
7103 m->SuppressProbes = 0;
7104
7105 #ifndef UNICAST_DISABLED
7106 uDNS_Init(m);
7107 m->SuppressStdPort53Queries = 0;
7108 #endif
7109 result = mDNSPlatformInit(m);
7110
7111 return(result);
7112 }
7113
7114 mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
7115 {
7116 m->mDNSPlatformStatus = result;
7117 if (m->MainCallback)
7118 {
7119 mDNS_Lock(m);
7120 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
7121 m->MainCallback(m, mStatus_NoError);
7122 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
7123 mDNS_Unlock(m);
7124 }
7125 }
7126
7127 mDNSexport void mDNS_Close(mDNS *const m)
7128 {
7129 mDNSu32 rrcache_active = 0;
7130 mDNSu32 rrcache_totalused = 0;
7131 mDNSu32 slot;
7132 NetworkInterfaceInfo *intf;
7133 mDNS_Lock(m);
7134
7135 m->mDNS_shutdown = mDNStrue;
7136
7137 #ifndef UNICAST_DISABLED
7138 uDNS_Close(m);
7139 #endif
7140 rrcache_totalused = m->rrcache_totalused;
7141 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
7142 {
7143 while(m->rrcache_hash[slot])
7144 {
7145 CacheGroup *cg = m->rrcache_hash[slot];
7146 while (cg->members)
7147 {
7148 CacheRecord *rr = cg->members;
7149 cg->members = cg->members->next;
7150 if (rr->CRActiveQuestion) rrcache_active++;
7151 ReleaseCacheRecord(m, rr);
7152 }
7153 cg->rrcache_tail = &cg->members;
7154 ReleaseCacheGroup(m, &m->rrcache_hash[slot]);
7155 }
7156 }
7157 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active);
7158 if (rrcache_active != m->rrcache_active)
7159 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active);
7160
7161 for (intf = m->HostInterfaces; intf; intf = intf->next)
7162 if (intf->Advertise)
7163 DeadvertiseInterface(m, intf);
7164
7165 // Make sure there are nothing but deregistering records remaining in the list
7166 if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
7167 m->CurrentRecord = m->ResourceRecords;
7168 while (m->CurrentRecord)
7169 {
7170 AuthRecord *rr = m->CurrentRecord;
7171 if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
7172 {
7173 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c);
7174 mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
7175 }
7176 else
7177 m->CurrentRecord = rr->next;
7178 }
7179
7180 if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
7181 else debugf("mDNS_Close: No deregistering records remain");
7182
7183 // If any deregistering records remain, send their deregistration announcements before we exit
7184 if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m);
7185 else if (m->ResourceRecords) SendResponses(m);
7186 if (m->ResourceRecords) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m, m->ResourceRecords));
7187
7188 mDNS_Unlock(m);
7189 debugf("mDNS_Close: mDNSPlatformClose");
7190 mDNSPlatformClose(m);
7191 debugf("mDNS_Close: done");
7192 }