]> git.saurik.com Git - apple/mdnsresponder.git/blame - mDNSCore/mDNS.c
mDNSResponder-108.2.tar.gz
[apple/mdnsresponder.git] / mDNSCore / mDNS.c
CommitLineData
7f0064bd
A
1/* -*- Mode: C; tab-width: 4 -*-
2 *
c9d2d929 3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
c9b9ae52 4 *
c9d2d929 5 * @APPLE_LICENSE_HEADER_START@
c9b9ae52 6 *
c9d2d929
A
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.
c9b9ae52 13 *
c9d2d929
A
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
c9b9ae52 20 * limitations under the License.
c9d2d929
A
21 *
22 * @APPLE_LICENSE_HEADER_END@
c9b9ae52
A
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
6528fe3e
A
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.)
c9b9ae52
A
44
45 Change History (most recent first):
46
47$Log: mDNS.c,v $
28f7d060
A
48Revision 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
51Revision 1.525 2005/09/24 00:47:17 cheshire
52Fix comment typos
53
54Revision 1.524 2005/09/16 21:06:49 cheshire
55Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
56
57Revision 1.523 2005/03/21 00:33:51 shersche
58<rdar://problem/4021486> Fix build warnings on Win32 platform
59
7cb34e5c
A
60Revision 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
63Revision 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
66Revision 1.520 2005/02/16 01:14:11 cheshire
67Convert RR Cache LogOperation() calls to debugf()
68
69Revision 1.519 2005/02/15 01:57:20 cheshire
70When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
71
72Revision 1.518 2005/02/10 22:35:17 cheshire
73<rdar://problem/3727944> Update name
74
75Revision 1.517 2005/02/03 00:21:21 cheshire
76Update comments about BIND named and zero-length TXT records
77
283ee3ff
A
78Revision 1.516 2005/01/28 06:06:32 cheshire
79Update comment
80
81Revision 1.515 2005/01/27 00:21:49 cheshire
82<rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
83
84Revision 1.514 2005/01/21 01:33:45 cheshire
85<rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
86
87Revision 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
90Revision 1.512 2005/01/20 00:37:45 cheshire
91<rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
92Take care not to recycle records while they are on the CacheFlushRecords list
93
94Revision 1.511 2005/01/19 22:48:53 cheshire
95<rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
96
97Revision 1.510 2005/01/19 03:12:45 cheshire
98Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
99
100Revision 1.509 2005/01/19 03:08:49 cheshire
101<rdar://problem/3961051> CPU Spin in mDNSResponder
102Log messages to help catch and report CPU spins
103
104Revision 1.508 2005/01/18 18:56:32 cheshire
105<rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
106
107Revision 1.507 2005/01/18 01:12:07 cheshire
108<rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
109
110Revision 1.506 2005/01/17 23:28:53 cheshire
111Fix compile error
112
113Revision 1.505 2005/01/11 02:02:56 shersche
114Move variable declaration to the beginning of statement block
115
116Revision 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
119Revision 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
122Revision 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
125Revision 1.501 2004/12/19 23:50:18 cheshire
126<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
127Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
128
129Revision 1.500 2004/12/18 03:13:46 cheshire
130<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
131
132Revision 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
136Revision 1.498 2004/12/17 05:25:46 cheshire
137<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
138
139Revision 1.497 2004/12/17 03:20:58 cheshire
140<rdar://problem/3925168> Don't send unicast replies we know will be ignored
141
142Revision 1.496 2004/12/16 22:18:26 cheshire
143Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
144
145Revision 1.495 2004/12/16 21:27:37 ksekar
146Fixed build failures when compiled with verbose debugging messages
147
148Revision 1.494 2004/12/16 20:46:56 cheshire
149Fix compiler warnings
150
151Revision 1.493 2004/12/16 20:13:00 cheshire
152<rdar://problem/3324626> Cache memory management improvements
153
154Revision 1.492 2004/12/16 08:03:24 shersche
155Fix compilation error when UNICAST_DISABLED is set
156
7f0064bd
A
157Revision 1.491 2004/12/11 01:52:11 cheshire
158<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
159
160Revision 1.490 2004/12/10 20:06:25 cheshire
161<rdar://problem/3915074> Reduce egregious stack space usage
162Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
163
164Revision 1.489 2004/12/10 20:03:43 cheshire
165<rdar://problem/3915074> Reduce egregious stack space usage
166Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
167
168Revision 1.488 2004/12/10 19:50:41 cheshire
169<rdar://problem/3915074> Reduce egregious stack space usage
170Reduced SendResponses() stack frame from 9K to 176 bytes
171
172Revision 1.487 2004/12/10 19:39:13 cheshire
173<rdar://problem/3915074> Reduce egregious stack space usage
174Reduced SendQueries() stack frame from 18K to 112 bytes
175
176Revision 1.486 2004/12/10 14:16:17 cheshire
177<rdar://problem/3889788> Relax update rate limiting
178We now allow an average rate of ten updates per minute.
179Updates in excess of that are rate limited, but more gently than before.
180
181Revision 1.485 2004/12/10 02:09:24 cheshire
182<rdar://problem/3898376> Modify default TTLs
183
184Revision 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
187Revision 1.483 2004/12/07 23:00:14 ksekar
188<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
189Call RecordProbeFailure even if there is no record callback
190
191Revision 1.482 2004/12/07 22:49:06 cheshire
7cb34e5c 192<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
7f0064bd
A
193
194Revision 1.481 2004/12/07 21:26:04 ksekar
195<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
196
197Revision 1.480 2004/12/07 20:42:33 cheshire
198Add explicit context parameter to mDNS_RemoveRecordFromService()
199
200Revision 1.479 2004/12/07 17:50:49 ksekar
7cb34e5c 201<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
7f0064bd
A
202
203Revision 1.478 2004/12/06 21:15:22 ksekar
204<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
205
206Revision 1.477 2004/12/04 02:12:45 cheshire
207<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
208
209Revision 1.476 2004/11/29 23:34:31 cheshire
210On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
211is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
212only nudges the time value to 1 if the interval calculation happens to result in the value zero.
213
214Revision 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
216Additional 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
218by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
219
220Revision 1.474 2004/11/29 17:18:12 cheshire
221Remove "Unknown DNS packet type" message for update responses
222
223Revision 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
226Revision 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
229Revision 1.471 2004/11/25 01:10:13 cheshire
230Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
231
232Revision 1.470 2004/11/24 21:54:44 cheshire
233<rdar://problem/3894475> mDNSCore not receiving unicast responses properly
234
235Revision 1.469 2004/11/24 04:50:39 cheshire
236Minor tidying
237
238Revision 1.468 2004/11/24 01:47:07 cheshire
239<rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
240
241Revision 1.467 2004/11/24 01:41:28 cheshire
242Rename CompleteProbing() to AcknowledgeRecord()
243
244Revision 1.466 2004/11/23 21:08:07 ksekar
245Don't use ID to demux multicast/unicast now that unicast uses random IDs
246
247Revision 1.465 2004/11/15 20:09:21 ksekar
248<rdar://problem/3719050> Wide Area support for Add/Remove record
249
250Revision 1.464 2004/11/03 01:44:36 cheshire
251Update debugging messages
252
253Revision 1.463 2004/10/29 02:38:48 cheshire
254Fix Windows compile errors
255
256Revision 1.462 2004/10/28 19:21:07 cheshire
257Guard against registering interface with zero InterfaceID
258
259Revision 1.461 2004/10/28 19:02:16 cheshire
260Remove \n from LogMsg() call
261
262Revision 1.460 2004/10/28 03:24:40 cheshire
263Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
264
265Revision 1.459 2004/10/26 22:34:37 cheshire
266<rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
267
268Revision 1.458 2004/10/26 20:45:28 cheshire
269Show mask in "invalid mask" message
270
271Revision 1.457 2004/10/26 06:28:36 cheshire
272Now that we don't check IP TTL any more, remove associated log message
273
274Revision 1.456 2004/10/26 06:21:42 cheshire
275Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
276
277Revision 1.455 2004/10/26 06:11:40 cheshire
278Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
279
280Revision 1.454 2004/10/23 01:16:00 cheshire
281<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
282
283Revision 1.453 2004/10/22 20:52:06 ksekar
284<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
285
286Revision 1.452 2004/10/20 01:50:40 cheshire
287<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
288Implemented ForceMCast mode for AuthRecords as well as for Questions
289
290Revision 1.451 2004/10/19 21:33:15 cheshire
291<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
292Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
293doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
294
295Revision 1.450 2004/10/19 17:42:59 ksekar
296Fixed compiler warnings for non-debug builds.
297
298Revision 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
301Revision 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
304Revision 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
307Revision 1.446 2004/10/14 00:43:34 cheshire
308<rdar://problem/3815984> Services continue to announce SRV and HINFO
309
310Revision 1.445 2004/10/12 21:07:09 cheshire
311Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
312
313Revision 1.444 2004/10/11 17:54:16 ksekar
314Changed hashtable pointer output from debugf to verbosedebugf.
315
316Revision 1.443 2004/10/10 07:05:45 cheshire
317For consistency, use symbol "localdomain" instead of literal string
318
319Revision 1.442 2004/10/08 20:25:10 cheshire
320Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
321
322Revision 1.441 2004/10/08 03:25:01 ksekar
323<rdar://problem/3831716> domain enumeration should use LLQs
324
325Revision 1.440 2004/10/06 01:44:19 cheshire
326<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
327
328Revision 1.439 2004/10/03 23:14:11 cheshire
329Add "mDNSEthAddr" type and "zeroEthAddr" constant
330
331Revision 1.438 2004/09/29 23:07:04 cheshire
332Patch from Pavel Repin to fix compile error on Windows
333
334Revision 1.437 2004/09/28 02:23:50 cheshire
335<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
336Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
337For records with the cache flush bit set, defer the decision until the end of the packet
338
339Revision 1.436 2004/09/28 01:27:04 cheshire
340Update incorrect log message
341
342Revision 1.435 2004/09/25 02:41:39 cheshire
343<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
344
345Revision 1.434 2004/09/25 02:32:06 cheshire
346Update comments
347
348Revision 1.433 2004/09/25 02:24:27 cheshire
349Removed unused rr->UseCount
350
351Revision 1.432 2004/09/24 21:35:17 cheshire
352<rdar://problem/3561220> Browses are no longer piggybacking on other browses
353TargetPort and TargetQID are allowed to be undefined if no question->Target is set
354
355Revision 1.431 2004/09/24 21:33:12 cheshire
356Adjust comment
357
358Revision 1.430 2004/09/24 02:15:49 cheshire
359<rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
360
361Revision 1.429 2004/09/24 00:20:21 cheshire
362<rdar://problem/3483349> Any rrtype is a conflict for unique records
363
364Revision 1.428 2004/09/24 00:12:25 cheshire
365Get rid of unused RRUniqueOrKnownUnique(RR)
366
367Revision 1.427 2004/09/23 20:44:11 cheshire
368<rdar://problem/3813148> Reduce timeout before expiring records on failure
369
370Revision 1.426 2004/09/23 20:21:07 cheshire
371<rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
372Associate a unique sequence number with each received packet, and only increment the count of recent answer
373packets if the packet sequence number for this answer record is not one we've already seen and counted.
374
375Revision 1.425 2004/09/23 20:14:38 cheshire
376Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
377
378Revision 1.424 2004/09/23 00:58:36 cheshire
379<rdar://problem/3781269> Rate limiting interferes with updating TXT records
380
381Revision 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
384Revision 1.422 2004/09/22 02:34:46 cheshire
385Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
386
387Revision 1.421 2004/09/21 23:29:49 cheshire
388<rdar://problem/3680045> DNSServiceResolve should delay sending packets
389
390Revision 1.420 2004/09/21 23:01:42 cheshire
391Update debugf messages
392
393Revision 1.419 2004/09/21 19:51:14 cheshire
394Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
395
396Revision 1.418 2004/09/21 18:40:17 cheshire
397<rdar://problem/3376752> Adjust default record TTLs
398
399Revision 1.417 2004/09/21 17:32:16 cheshire
400<rdar://problem/3809484> Rate limiting imposed too soon
401
402Revision 1.416 2004/09/20 23:52:01 cheshire
403CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
404
405Revision 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
408Revision 1.414 2004/09/18 01:06:48 cheshire
409Add comments
410
411Revision 1.413 2004/09/17 01:08:48 cheshire
412Renamed 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
417Revision 1.412 2004/09/17 00:46:33 cheshire
418mDNS_TimeNow should take const mDNS parameter
419
420Revision 1.411 2004/09/17 00:31:51 cheshire
421For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
422
423Revision 1.410 2004/09/17 00:19:10 cheshire
424For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
425
426Revision 1.409 2004/09/16 21:59:15 cheshire
427For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
428
429Revision 1.408 2004/09/16 21:36:36 cheshire
430<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
431Changes to add necessary locking calls around unicast DNS operations
432
433Revision 1.407 2004/09/16 02:29:39 cheshire
434Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
435uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
436
437Revision 1.406 2004/09/16 01:58:14 cheshire
438Fix compiler warnings
439
440Revision 1.405 2004/09/16 00:24:48 cheshire
441<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
442
443Revision 1.404 2004/09/15 21:44:11 cheshire
444<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
445Show time value in log to help diagnose errors
446
447Revision 1.403 2004/09/15 00:46:32 ksekar
448Changed debugf to verbosedebugf in CheckCacheExpiration
449
450Revision 1.402 2004/09/14 23:59:55 cheshire
451<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
452
453Revision 1.401 2004/09/14 23:27:46 cheshire
454Fix compile errors
455
456Revision 1.400 2004/09/02 03:48:47 cheshire
457<rdar://problem/3709039> Disable targeted unicast query support by default
4581. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
4592. New field AllowRemoteQuery in AuthRecord structure
4603. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
4614. mDNS.c only answers remote queries if AllowRemoteQuery is set
462
463Revision 1.399 2004/09/02 01:39:40 cheshire
464For better readability, follow consistent convention that QR bit comes first, followed by OP bits
465
466Revision 1.398 2004/09/01 03:59:29 ksekar
467<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
468
469Revision 1.397 2004/08/25 22:04:25 rpantos
470Fix the standard Windows compile error.
471
472Revision 1.396 2004/08/25 00:37:27 ksekar
473<rdar://problem/3774635>: Cleanup DynDNS hostname registration code
474
475Revision 1.395 2004/08/18 17:21:18 ksekar
476Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
477
478Revision 1.394 2004/08/14 03:22:41 cheshire
479<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
480Add GetUserSpecifiedDDNSName() routine
481Convert ServiceRegDomain to domainname instead of C string
482Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
483
484Revision 1.393 2004/08/13 23:42:52 cheshire
485Removed unused "zeroDomainNamePtr"
486
487Revision 1.392 2004/08/13 23:37:02 cheshire
488Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
489"uDNS_info.UnicastHostname" for clarity
490
491Revision 1.391 2004/08/13 23:25:00 cheshire
492Now that we do both uDNS and mDNS, global replace "m->hostname" with
493"m->MulticastHostname" for clarity
494
495Revision 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
498Revision 1.389 2004/08/10 23:19:14 ksekar
7cb34e5c 499<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
7f0064bd
A
500Moved routines/constants to allow extern access for garbage collection daemon
501
502Revision 1.388 2004/07/30 17:40:06 ksekar
503<rdar://problem/3739115>: TXT Record updates not available for wide-area services
504
505Revision 1.387 2004/07/26 22:49:30 ksekar
506<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
507
508Revision 1.386 2004/07/13 21:24:24 rpantos
509Fix for <rdar://problem/3701120>.
510
511Revision 1.385 2004/06/18 19:09:59 cheshire
512<rdar://problem/3588761> Current method of doing subtypes causes name collisions
513
514Revision 1.384 2004/06/15 04:31:23 cheshire
515Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
516
517Revision 1.383 2004/06/11 00:04:59 cheshire
518<rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
519
8e92c31c
A
520Revision 1.382 2004/06/08 04:59:40 cheshire
521Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
522
523Revision 1.381 2004/06/05 00:57:30 cheshire
524Remove incorrect LogMsg()
525
526Revision 1.380 2004/06/05 00:04:26 cheshire
527<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
528
529Revision 1.379 2004/05/28 23:42:36 ksekar
530<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
531
532Revision 1.378 2004/05/25 17:25:25 cheshire
533Remove extraneous blank lines and white space
534
535Revision 1.377 2004/05/18 23:51:25 cheshire
536Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
537
538Revision 1.376 2004/05/05 18:30:44 ksekar
539Restored surpressed Cache Tail debug messages.
540
541Revision 1.375 2004/04/26 21:36:25 cheshire
542Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
543is indicated as being available on that interface
544
545Revision 1.374 2004/04/21 02:53:26 cheshire
546Typo in debugf statement
547
548Revision 1.373 2004/04/21 02:49:11 cheshire
549To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
550
551Revision 1.372 2004/04/21 02:38:51 cheshire
552Add debugging checks
553
554Revision 1.371 2004/04/14 23:09:28 ksekar
555Support for TSIG signed dynamic updates.
556
557Revision 1.370 2004/04/09 17:40:26 cheshire
558Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
559
560Revision 1.369 2004/04/09 16:34:00 cheshire
561Debugging code for later; currently unused
562
563Revision 1.368 2004/04/02 19:19:48 cheshire
564Add code to do optional logging of multi-packet KA list time intervals
565
566Revision 1.367 2004/03/20 03:16:10 cheshire
567Minor refinement to "Excessive update rate" message
b7388343 568
8e92c31c
A
569Revision 1.366 2004/03/20 03:12:57 cheshire
570<rdar://problem/3587619>: UpdateCredits not granted promptly enough
b7388343 571
8e92c31c
A
572Revision 1.365 2004/03/19 23:51:22 cheshire
573Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
b7388343 574
8e92c31c
A
575Revision 1.364 2004/03/13 01:57:33 ksekar
576<rdar://problem/3192546>: DynDNS: Dynamic update of service records
b7388343 577
8e92c31c
A
578Revision 1.363 2004/03/12 21:00:51 cheshire
579Also show port numbers when logging "apparent spoof mDNS Response" messages
b7388343 580
8e92c31c
A
581Revision 1.362 2004/03/12 08:58:18 cheshire
582Guard against empty TXT records
73792575 583
8e92c31c
A
584Revision 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.
73792575 586
8e92c31c
A
587Revision 1.360 2004/03/08 02:52:41 cheshire
588Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
73792575 589
8e92c31c 590Revision 1.359 2004/03/02 03:21:56 cheshire
8d1ca615
A
591<rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
592
8e92c31c
A
593Revision 1.358 2004/02/20 08:18:34 cheshire
594<rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
8d1ca615 595
8e92c31c
A
596Revision 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
54811a41 598
8e92c31c
A
599Revision 1.356 2004/02/06 23:04:19 ksekar
600Basic Dynamic Update support via mDNS_Register (dissabled via
601UNICAST_REGISTRATION #define)
602
603Revision 1.355 2004/02/05 09:32:33 cheshire
604Fix from Bob Bradley: When using the "%.*s" string form,
605guard against truncating in the middle of a multi-byte UTF-8 character.
606
607Revision 1.354 2004/02/05 09:30:22 cheshire
608Update comments
609
610Revision 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
613Revision 1.352 2004/01/28 02:30:07 ksekar
614Added default Search Domains to unicast browsing, controlled via
615Networking sharing prefs pane. Stopped sending unicast messages on
616every interface. Fixed unicast resolving via mach-port API.
617
618Revision 1.351 2004/01/27 20:15:22 cheshire
619<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
620
621Revision 1.350 2004/01/24 23:38:16 cheshire
622Use mDNSVal16() instead of shifting and ORing operations
623
624Revision 1.349 2004/01/23 23:23:14 ksekar
625Added TCP support for truncated unicast messages.
626
627Revision 1.348 2004/01/22 03:54:11 cheshire
628Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
629which means "do this query via multicast, even if it's apparently a unicast domain"
630
631Revision 1.347 2004/01/22 03:50:49 cheshire
632If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
633
634Revision 1.346 2004/01/22 03:48:41 cheshire
635Make sure uDNS client doesn't accidentally use query ID zero
636
637Revision 1.345 2004/01/22 03:43:08 cheshire
638Export constants like mDNSInterface_LocalOnly so that the client layers can use them
639
640Revision 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
643Revision 1.343 2003/12/23 00:07:47 cheshire
644Make port number in debug message be five-character field, left justified
645
646Revision 1.342 2003/12/20 01:34:28 cheshire
8abd1236
A
647<rdar://problem/3515876>: Error putting additional records into packets
648Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
649after every record, not once at the end.
650
8e92c31c
A
651Revision 1.341 2003/12/18 22:56:12 cheshire
652<rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
653
654Revision 1.340 2003/12/16 02:31:37 cheshire
655Minor update to comments
656
657Revision 1.339 2003/12/13 05:50:33 bradley
658Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
659layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
660fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
661
662Revision 1.338 2003/12/13 03:05:27 ksekar
663<rdar://problem/3192548>: DynDNS: Unicast query of service records
664
665Revision 1.337 2003/12/01 21:46:05 cheshire
666mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
667
668Revision 1.336 2003/12/01 21:26:19 cheshire
669Guard against zero-length sbuffer in mDNS_vsnprintf()
670
671Revision 1.335 2003/12/01 20:27:48 cheshire
672Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
673
674Revision 1.334 2003/11/20 22:59:53 cheshire
7f0064bd 675Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
8e92c31c
A
676Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
677
678Revision 1.333 2003/11/20 20:49:53 cheshire
679Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
680
681Revision 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
683Now that we only include answers in the known answer list if they are less than
684halfway to expiry, the check to also see if we have another query scheduled
685before the record expires is no longer necessary (and in fact, not correct).
686
687Revision 1.331 2003/11/19 22:31:48 cheshire
688When automatically adding A records to SRVs, add them as additionals, not answers
689
690Revision 1.330 2003/11/19 22:28:50 cheshire
691Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
692to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
693
694Revision 1.329 2003/11/19 22:19:24 cheshire
695Show log message when ignoring packets with bad TTL.
696This is to help diagnose problems on Linux versions that may not report the TTL reliably.
697
698Revision 1.328 2003/11/19 22:06:38 cheshire
699Show log messages when a service or hostname is renamed
700
701Revision 1.327 2003/11/19 22:03:44 cheshire
702Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
703
704Revision 1.326 2003/11/17 22:27:02 cheshire
705Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
706on platforms that have native clock rates below fifty ticks per second.
707
708Revision 1.325 2003/11/17 20:41:44 cheshire
709Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
710
711Revision 1.324 2003/11/17 20:36:32 cheshire
712Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
713DeadvertiseInterface() -- they're internal private routines, not API routines.
714
715Revision 1.323 2003/11/14 20:59:08 cheshire
716Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
7f0064bd 717Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
8e92c31c
A
718
719Revision 1.322 2003/11/14 19:47:52 cheshire
720Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
721
722Revision 1.321 2003/11/14 19:18:34 cheshire
7f0064bd 723Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
8e92c31c
A
724
725Revision 1.320 2003/11/13 06:45:04 cheshire
726Fix compiler warning on certain compilers
727
728Revision 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
731Revision 1.318 2003/11/13 00:33:26 cheshire
732Change macro "RRIsAddressType" to "RRTypeIsAddressType"
733
734Revision 1.317 2003/11/13 00:10:49 cheshire
735<rdar://problem/3436412>: Verify that rr data is different before updating.
736
737Revision 1.316 2003/11/08 23:37:54 cheshire
738Give explicit zero initializers to blank static structure, required by certain compilers.
739(Thanks to ramaprasad.kr@hp.com for reporting this.)
740
741Revision 1.315 2003/11/07 03:32:56 cheshire
742<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
743This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
744purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
745to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
746can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
747
748Revision 1.314 2003/11/07 03:19:49 cheshire
749Minor variable renaming for clarity
750
751Revision 1.313 2003/11/07 03:14:49 cheshire
752Previous checkin proved to be overly simplistic; reversing
753
754Revision 1.312 2003/11/03 23:45:15 cheshire
755<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
756Build 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
759Revision 1.311 2003/10/09 18:00:11 cheshire
760Another compiler warning fix.
761
762Revision 1.310 2003/10/07 20:27:05 cheshire
763Patch from Bob Bradley, to fix warning and compile error on Windows
764
765Revision 1.309 2003/09/26 01:06:36 cheshire
766<rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
767Made new routine HaveSentEntireRRSet() to check if flag should be set
768
769Revision 1.308 2003/09/23 01:05:01 cheshire
770Minor changes to comments and debugf() message
716635cc 771
c9b9ae52
A
772Revision 1.307 2003/09/09 20:13:30 cheshire
773<rdar://problem/3411105> Don't send a Goodbye record if we never announced it
774Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
775rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
776
777Revision 1.306 2003/09/09 03:00:03 cheshire
778<rdar://problem/3413099> Services take a long time to disappear when switching networks.
779Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
780
781Revision 1.305 2003/09/09 02:49:31 cheshire
782<rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
783
784Revision 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
787Revision 1.303 2003/09/05 19:55:02 cheshire
788<rdar://problem/3409533> Include address records when announcing SRV records
789
790Revision 1.302 2003/09/05 00:01:36 cheshire
791<rdar://problem/3407549> Don't accelerate queries that have large KA lists
792
793Revision 1.301 2003/09/04 22:51:13 cheshire
794<rdar://problem/3398213> Group probes and goodbyes better
795
796Revision 1.300 2003/09/03 02:40:37 cheshire
797<rdar://problem/3404842> mDNSResponder complains about '_'s
798Underscores are not supposed to be legal in standard DNS names, but IANA appears
799to have allowed them in previous service name registrations, so we should too.
800
801Revision 1.299 2003/09/03 02:33:09 cheshire
802<rdar://problem/3404795> CacheRecordRmv ERROR
803Don't update m->NewQuestions until *after* CheckCacheExpiration();
804
805Revision 1.298 2003/09/03 01:47:01 cheshire
7f0064bd 806<rdar://problem/3319418> Services always in a state of flux
c9b9ae52
A
807Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
808
809Revision 1.297 2003/08/29 19:44:15 cheshire
810<rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
8111. 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
8132. For these queries, go straight to QM, skipping QU
814
815Revision 1.296 2003/08/29 19:08:21 cheshire
816<rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
817Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
818
819Revision 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
822Revision 1.294 2003/08/27 02:30:22 cheshire
823<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
824One more change: "query->GotTXT" is now a straightforward bi-state boolean again
825
826Revision 1.293 2003/08/27 02:25:31 cheshire
827<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
828
829Revision 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
832Revision 1.291 2003/08/21 18:57:44 cheshire
833<rdar://problem/3387140> Synchronized queries on the network
834
835Revision 1.290 2003/08/21 02:25:23 cheshire
836Minor changes to comments and debugf() messages
837
838Revision 1.289 2003/08/21 02:21:50 cheshire
839<rdar://problem/3386473> Efficiency: Reduce repeated queries
840
841Revision 1.288 2003/08/20 23:39:30 cheshire
842<rdar://problem/3344098> Review syslog messages, and remove as appropriate
843
844Revision 1.287 2003/08/20 20:47:18 cheshire
845Fix compiler warning
846
847Revision 1.286 2003/08/20 02:18:51 cheshire
848<rdar://problem/3344098> Cleanup: Review syslog messages
849
850Revision 1.285 2003/08/20 01:59:06 cheshire
851<rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
852Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
853
854Revision 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
856More minor refinements
857
858Revision 1.283 2003/08/19 22:16:27 cheshire
859Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
860
861Revision 1.282 2003/08/19 06:48:25 cheshire
862<rdar://problem/3376552> Guard against excessive record updates
863Each record starts with 10 UpdateCredits.
864Every update consumes one UpdateCredit.
865UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
866As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
867When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
868
869Revision 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
8711. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6.
8722. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
8733. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
874
875Revision 1.280 2003/08/19 02:33:36 cheshire
876Update comments
877
878Revision 1.279 2003/08/19 02:31:11 cheshire
879<rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
880Final expiration queries now only mark the question for sending on the particular interface
881pertaining to the record that's expiring.
882
883Revision 1.278 2003/08/18 22:53:37 cheshire
7f0064bd 884<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
c9b9ae52
A
885
886Revision 1.277 2003/08/18 19:05:44 cheshire
887<rdar://problem/3382423> UpdateRecord not working right
888Added "newrdlength" field to hold new length of updated rdata
889
890Revision 1.276 2003/08/16 03:39:00 cheshire
891<rdar://problem/3338440> InterfaceID -1 indicates "local only"
892
893Revision 1.275 2003/08/16 02:51:27 cheshire
894<rdar://problem/3366590> mDNSResponder takes too much RPRVT
895Don't try to compute namehash etc, until *after* validating the name
896
897Revision 1.274 2003/08/16 01:12:40 cheshire
898<rdar://problem/3366590> mDNSResponder takes too much RPRVT
899Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
900simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
901and in the process of copying it, the C compiler may run off the end of the rdata object into
902unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
903call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
904
905Revision 1.273 2003/08/15 20:16:02 cheshire
906<rdar://problem/3366590> mDNSResponder takes too much RPRVT
907We want to avoid touching the rdata pages, so we don't page them in.
9081. 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.
9102. 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
913Revision 1.272 2003/08/14 19:29:04 cheshire
914<rdar://problem/3378473> Include cache records in SIGINFO output
7f0064bd 915Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
c9b9ae52
A
916
917Revision 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
920Revision 1.270 2003/08/13 17:07:28 ksekar
8e92c31c 921<rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
c9b9ae52
A
922Added check to result of mDNS_Register() before linking extra record into list.
923
924Revision 1.269 2003/08/12 19:56:23 cheshire
925Update to APSL 2.0
926
927Revision 1.268 2003/08/12 15:01:10 cheshire
928Add comments
929
930Revision 1.267 2003/08/12 14:59:27 cheshire
931<rdar://problem/3374490> Rate-limiting blocks some legitimate responses
932When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
933whether to suppress the response, also check LastMCInterface to see if it matches.
934
935Revision 1.266 2003/08/12 12:47:16 cheshire
936In mDNSCoreMachineSleep debugf message, display value of m->timenow
937
938Revision 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
941Revision 1.264 2003/08/09 00:55:02 cheshire
942<rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
943Don't scan the whole cache after every packet.
944
945Revision 1.263 2003/08/09 00:35:29 cheshire
946Moved AnswerNewQuestion() later in the file, in preparation for next checkin
947
948Revision 1.262 2003/08/08 19:50:33 cheshire
949<rdar://problem/3370332> Remove "Cache size now xxx" messages
950
951Revision 1.261 2003/08/08 19:18:45 cheshire
952<rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
953
954Revision 1.260 2003/08/08 18:55:48 cheshire
955<rdar://problem/3370365> Guard against time going backwards
956
957Revision 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
960Revision 1.258 2003/08/08 16:22:05 cheshire
961<rdar://problem/3335473> Need to check validity of TXT (and other) records
962Remove unneeded LogMsg
963
964Revision 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
967Revision 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
970Revision 1.255 2003/08/06 23:22:50 cheshire
971Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
972
973Revision 1.254 2003/08/06 21:33:39 cheshire
974Fix compiler warnings on PocketPC 2003 (Windows CE)
975
976Revision 1.253 2003/08/06 20:43:57 cheshire
977<rdar://problem/3335473> Need to check validity of TXT (and other) records
978Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
979
980Revision 1.252 2003/08/06 20:35:47 cheshire
981Enhance debugging routine GetRRDisplayString() so it can also be used to display
982other RDataBody objects, not just the one currently attached the given ResourceRecord
983
984Revision 1.251 2003/08/06 19:07:34 cheshire
985<rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
986Was checking LastAPTime instead of LastMCTime
987
988Revision 1.250 2003/08/06 19:01:55 cheshire
989Update comments
990
991Revision 1.249 2003/08/06 00:13:28 cheshire
992Tidy up debugf messages
993
994Revision 1.248 2003/08/05 22:20:15 cheshire
995<rdar://problem/3330324> Need to check IP TTL on responses
996
997Revision 1.247 2003/08/05 00:56:39 cheshire
998<rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
999
1000Revision 1.246 2003/08/04 19:20:49 cheshire
1001Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
1002
1003Revision 1.245 2003/08/02 01:56:29 cheshire
1004For debugging: log message if we ever get more than one question in a truncated packet
1005
1006Revision 1.244 2003/08/01 23:55:32 cheshire
1007Fix for compiler warnings on Windows, submitted by Bob Bradley
1008
1009Revision 1.243 2003/07/25 02:26:09 cheshire
1010Typo: FIxed missing semicolon
1011
1012Revision 1.242 2003/07/25 01:18:41 cheshire
1013Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1014
1015Revision 1.241 2003/07/23 21:03:42 cheshire
1016Only show "Found record..." debugf message in verbose mode
1017
1018Revision 1.240 2003/07/23 21:01:11 cheshire
1019<rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
1020After sending a packet, suppress further sending for the next 100ms.
1021
1022Revision 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
1025Revision 1.238 2003/07/22 00:10:20 cheshire
1026<rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1027
1028Revision 1.237 2003/07/19 03:23:13 cheshire
1029<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1030
1031Revision 1.236 2003/07/19 03:04:55 cheshire
1032Fix warnings; some debugf message improvements
1033
1034Revision 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
1036ScheduleNextTask is quite an expensive operation.
1037We don't need to do all that work after receiving a no-op packet that didn't change our state.
1038
1039Revision 1.234 2003/07/18 23:52:11 cheshire
1040To improve consistency of field naming, global search-and-replace:
1041NextProbeTime -> NextScheduledProbe
1042NextResponseTime -> NextScheduledResponse
1043
1044Revision 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
1047Revision 1.232 2003/07/18 00:11:38 cheshire
1048Add 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
1051Revision 1.231 2003/07/18 00:06:37 cheshire
1052To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
1053
1054Revision 1.230 2003/07/17 18:16:54 cheshire
7f0064bd 1055<rdar://problem/3319418> Services always in a state of flux
c9b9ae52
A
1056In preparation for working on this, made some debugf messages a little more selective
1057
1058Revision 1.229 2003/07/17 17:35:04 cheshire
1059<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1060
1061Revision 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
1064Revision 1.227 2003/07/16 05:01:36 cheshire
1065Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
1066<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1067
1068Revision 1.226 2003/07/16 04:51:44 cheshire
1069Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1070
1071Revision 1.225 2003/07/16 04:46:41 cheshire
1072Minor wording cleanup: The correct DNS term is "response", not "reply"
1073
1074Revision 1.224 2003/07/16 04:39:02 cheshire
1075Textual cleanup (no change to functionality):
1076Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
1077
1078Revision 1.223 2003/07/16 00:09:22 cheshire
1079Textual cleanup (no change to functionality):
1080Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
1081replace with macro "TicksTTL(rr)"
1082Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
1083replaced with macro "RRExpireTime(rr)"
1084
1085Revision 1.222 2003/07/15 23:40:46 cheshire
1086Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1087
1088Revision 1.221 2003/07/15 22:17:56 cheshire
1089<rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1090
1091Revision 1.220 2003/07/15 02:12:51 cheshire
1092Slight tidy-up of debugf messages and comments
1093
1094Revision 1.219 2003/07/15 01:55:12 cheshire
1095<rdar://problem/3315777> Need to implement service registration with subtypes
1096
1097Revision 1.218 2003/07/14 16:26:06 cheshire
1098<rdar://problem/3324795> Duplicate query suppression not working right
1099Refinement: Don't record DS information for a question in the first quarter second
1100right after we send it -- in the case where a question happens to be accelerated by
1101the maximum allowed amount, we don't want it to then be suppressed because the previous
1102time *we* sent that question falls (just) within the valid duplicate suppression window.
1103
1104Revision 1.217 2003/07/13 04:43:53 cheshire
1105<rdar://problem/3325169> Services on multiple interfaces not always resolving
1106Minor refinement: No need to make address query broader than the original SRV query that provoked it
1107
1108Revision 1.216 2003/07/13 03:13:17 cheshire
1109<rdar://problem/3325169> Services on multiple interfaces not always resolving
1110If we get an identical SRV on a second interface, convert address queries to non-specific
1111
1112Revision 1.215 2003/07/13 02:28:00 cheshire
1113<rdar://problem/3325166> SendResponses didn't all its responses
1114Delete all references to RRInterfaceActive -- it's now superfluous
1115
1116Revision 1.214 2003/07/13 01:47:53 cheshire
1117Fix one error and one warning in the Windows build
1118
1119Revision 1.213 2003/07/12 04:25:48 cheshire
1120Fix minor signed/unsigned warnings
1121
1122Revision 1.212 2003/07/12 01:59:11 cheshire
1123Minor changes to debugf messages
1124
1125Revision 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
1128Revision 1.210 2003/07/12 01:43:28 cheshire
1129<rdar://problem/3324795> Duplicate query suppression not working right
1130The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
1131The code was incorrectly using the last query time plus one-half the query interval.
1132This was only correct in the case where query acceleration was not in effect.
1133
1134Revision 1.209 2003/07/12 01:27:50 cheshire
1135<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1136Fix missing "-1" in RemoveLabelSuffix()
1137
1138Revision 1.208 2003/07/11 01:32:38 cheshire
1139Syntactic cleanup (no change to funcationality): Now that we only have one host name,
1140rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
1141
1142Revision 1.207 2003/07/11 01:28:00 cheshire
1143<rdar://problem/3161289> No more local.arpa
1144
1145Revision 1.206 2003/07/11 00:45:02 cheshire
1146<rdar://problem/3321909> Client should get callback confirming successful host name registration
1147
1148Revision 1.205 2003/07/11 00:40:18 cheshire
1149Tidy up debug message in HostNameCallback()
1150
1151Revision 1.204 2003/07/11 00:20:32 cheshire
1152<rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1153
1154Revision 1.203 2003/07/10 23:53:41 cheshire
1155<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1156
1157Revision 1.202 2003/07/04 02:23:20 cheshire
1158<rdar://problem/3311955> Responder too aggressive at flushing stale data
1159Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
1160
1161Revision 1.201 2003/07/04 01:09:41 cheshire
1162<rdar://problem/3315775> Need to implement subtype queries
1163Modified ConstructServiceName() to allow three-part service types
1164
1165Revision 1.200 2003/07/03 23:55:26 cheshire
1166Minor change to wording of syslog warning messages
1167
1168Revision 1.199 2003/07/03 23:51:13 cheshire
1169<rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
1170Added more detailed debugging information
1171
1172Revision 1.198 2003/07/03 22:19:30 cheshire
1173<rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
1174Make exception to allow _tivo_servemedia._tcp.
1175
1176Revision 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
1178Minor refinements:
1179When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
1180Allow cache to grow to 512 records before considering it a potential denial-of-service attack
1181
1182Revision 1.196 2003/07/02 21:19:45 cheshire
1183<rdar://problem/3313413> Update copyright notices, etc., in source code comments
1184
1185Revision 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
1187Minor refinement: m->rrcache_active was not being decremented when
1188an active record was deleted because its TTL expired
1189
1190Revision 1.194 2003/07/02 18:47:40 cheshire
1191Minor wording change to log messages
1192
1193Revision 1.193 2003/07/02 02:44:13 cheshire
1194Fix warning in non-debug build
1195
1196Revision 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
1199Revision 1.191 2003/07/02 02:30:51 cheshire
1200HashSlot() returns an array index. It can't be negative; hence it should not be signed.
1201
1202Revision 1.190 2003/06/27 00:03:05 vlubet
1203<rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1204
1205Revision 1.189 2003/06/11 19:24:03 cheshire
1206<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1207Slight refinement to previous checkin
1208
1209Revision 1.188 2003/06/10 20:33:28 cheshire
1210<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1211
1212Revision 1.187 2003/06/10 04:30:44 cheshire
1213<rdar://problem/3286234> Need to re-probe/re-announce on configuration change
1214Only interface-specific records were re-probing and re-announcing, not non-specific records.
1215
1216Revision 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
1218Some additional refinements:
1219Don't try to do this for unicast-response queries
1220better tracking of Qs and KAs in multi-packet KA lists
1221
1222Revision 1.185 2003/06/10 03:52:49 cheshire
1223Update comments and debug messages
1224
1225Revision 1.184 2003/06/10 02:26:39 cheshire
1226<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1227Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
1228
1229Revision 1.183 2003/06/09 18:53:13 cheshire
1230Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1231
1232Revision 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
1234Only issue a correction if the TTL in the proxy packet is less than half the correct value.
1235
1236Revision 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
1239Revision 1.180 2003/06/07 06:31:07 cheshire
1240Create little four-line helper function "FindIdenticalRecordInCache()"
1241
1242Revision 1.179 2003/06/07 06:28:13 cheshire
1243For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1244
1245Revision 1.178 2003/06/07 06:25:12 cheshire
1246Update some comments
1247
1248Revision 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
1251Revision 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
1253Minor change: Increment/decrement logic for q->CurrentAnswers should be in
1254CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
1255
1256Revision 1.175 2003/06/07 04:11:52 cheshire
1257Minor changes to comments and debug messages
1258
1259Revision 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
1262Revision 1.173 2003/06/07 01:22:13 cheshire
1263<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1264
1265Revision 1.172 2003/06/07 00:59:42 cheshire
1266<rdar://problem/3283454> Need some randomness to spread queries on the network
1267
1268Revision 1.171 2003/06/06 21:41:10 cheshire
1269For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
1270
1271Revision 1.170 2003/06/06 21:38:55 cheshire
1272Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
1273already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
1274
1275Revision 1.169 2003/06/06 21:35:55 cheshire
1276Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
1277(the target is a domain name, but not necessarily a host name)
1278
1279Revision 1.168 2003/06/06 21:33:31 cheshire
1280Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
1281
1282Revision 1.167 2003/06/06 21:30:42 cheshire
1283<rdar://problem/3282962> Don't delay queries for shared record types
1284
1285Revision 1.166 2003/06/06 17:20:14 cheshire
1286For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
1287(Global search-and-replace; no functional change to code execution.)
1288
1289Revision 1.165 2003/06/04 02:53:21 cheshire
1290Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1291
1292Revision 1.164 2003/06/04 01:25:33 cheshire
1293<rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
1294Display time interval between first and subsequent queries
1295
1296Revision 1.163 2003/06/03 19:58:14 cheshire
1297<rdar://problem/3277665> mDNS_DeregisterService() fixes:
1298When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
1299Guard against a couple of possible mDNS_DeregisterService() race conditions.
1300
1301Revision 1.162 2003/06/03 19:30:39 cheshire
1302Minor addition refinements for
1303<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1304
1305Revision 1.161 2003/06/03 18:29:03 cheshire
1306Minor changes to comments and debugf() messages
1307
1308Revision 1.160 2003/06/03 05:02:16 cheshire
1309<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1310
1311Revision 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
1314Revision 1.158 2003/06/02 22:57:09 cheshire
1315Minor clarifying changes to comments and log messages;
1316IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
1317
1318Revision 1.157 2003/05/31 00:09:49 cheshire
1319<rdar://problem/3274862> Add ability to discover what services are on a network
1320
1321Revision 1.156 2003/05/30 23:56:49 cheshire
1322<rdar://problem/3274847> Crash after error in mDNS_RegisterService()
1323Need to set "sr->Extras = mDNSNULL" before returning
1324
1325Revision 1.155 2003/05/30 23:48:00 cheshire
1326<rdar://problem/3274832> Announcements not properly grouped
1327Due to inconsistent setting of rr->LastAPTime at different places in the
1328code, announcements were not properly grouped into a single packet.
1329Fixed by creating a single routine called InitializeLastAPTime().
1330
1331Revision 1.154 2003/05/30 23:38:14 cheshire
1332<rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
1333Wrote buffer[32] where it should have said buffer[64]
1334
1335Revision 1.153 2003/05/30 19:10:56 cheshire
1336<rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1337
1338Revision 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
1341Revision 1.151 2003/05/29 06:35:42 cheshire
1342<rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1343
1344Revision 1.150 2003/05/29 06:25:45 cheshire
1345<rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1346
1347Revision 1.149 2003/05/29 06:18:39 cheshire
1348<rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1349
1350Revision 1.148 2003/05/29 06:11:34 cheshire
1351<rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
1352
1353Revision 1.147 2003/05/29 06:01:18 cheshire
1354Change some debugf() calls to LogMsg() calls to help with debugging
1355
1356Revision 1.146 2003/05/28 21:00:44 cheshire
1357Re-enable "immediate answer burst" debugf message
1358
1359Revision 1.145 2003/05/28 20:57:44 cheshire
1360<rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
1361known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
1362version of mDNSResponder, so for now we should suppress this warning message.
1363
1364Revision 1.144 2003/05/28 18:05:12 cheshire
1365<rdar://problem/3009899> mDNSResponder allows invalid service registrations
1366Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
1367
1368Revision 1.143 2003/05/28 04:31:29 cheshire
1369<rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1370
1371Revision 1.142 2003/05/28 03:13:07 cheshire
1372<rdar://problem/3009899> mDNSResponder allows invalid service registrations
1373Require that the transport protocol be _udp or _tcp
1374
1375Revision 1.141 2003/05/28 02:19:12 cheshire
1376<rdar://problem/3270634> Misleading messages generated by iChat
1377Better fix: Only generate the log message for queries where the TC bit is set.
1378
1379Revision 1.140 2003/05/28 01:55:24 cheshire
1380Minor change to log messages
1381
1382Revision 1.139 2003/05/28 01:52:51 cheshire
1383<rdar://problem/3270634> Misleading messages generated by iChat
1384
1385Revision 1.138 2003/05/27 22:35:00 cheshire
1386<rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1387
1388Revision 1.137 2003/05/27 20:04:33 cheshire
1389<rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1390
1391Revision 1.136 2003/05/27 18:50:07 cheshire
1392<rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
1393
1394Revision 1.135 2003/05/26 04:57:28 cheshire
1395<rdar://problem/3268953> Delay queries when there are already answers in the cache
1396
1397Revision 1.134 2003/05/26 04:54:54 cheshire
1398<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1399Accidentally deleted '%' case from the switch statement
1400
1401Revision 1.133 2003/05/26 03:21:27 cheshire
1402Tidy up address structure naming:
1403mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
1404mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
1405mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
1406
1407Revision 1.132 2003/05/26 03:01:26 cheshire
1408<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1409
1410Revision 1.131 2003/05/26 00:42:05 cheshire
1411<rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1412
1413Revision 1.130 2003/05/24 16:39:48 cheshire
1414<rdar://problem/3268631> SendResponses also needs to handle multihoming better
1415
1416Revision 1.129 2003/05/23 02:15:37 cheshire
1417Fixed misleading use of the term "duplicate suppression" where it should have
1418said "known answer suppression". (Duplicate answer suppression is something
1419different, and duplicate question suppression is yet another thing, so the use
1420of the completely vague term "duplicate suppression" was particularly bad.)
1421
1422Revision 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
1425Revision 1.127 2003/05/23 01:02:15 ksekar
8e92c31c 1426<rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
c9b9ae52
A
1427
1428Revision 1.126 2003/05/22 02:29:22 cheshire
1429<rdar://problem/2984918> SendQueries needs to handle multihoming better
1430Complete rewrite of SendQueries. Works much better now :-)
1431
1432Revision 1.125 2003/05/22 01:50:45 cheshire
1433Fix warnings, and improve log messages
1434
1435Revision 1.124 2003/05/22 01:41:50 cheshire
1436DiscardDeregistrations doesn't need InterfaceID parameter
1437
1438Revision 1.123 2003/05/22 01:38:55 cheshire
1439Change bracketing of #pragma mark
1440
1441Revision 1.122 2003/05/21 19:59:04 cheshire
1442<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1443Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
1444
1445Revision 1.121 2003/05/21 17:54:07 ksekar
8e92c31c 1446<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
c9b9ae52
A
1447New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
1448
1449Revision 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
1452Revision 1.119 2003/05/16 01:34:10 cheshire
1453Fix some warnings
1454
1455Revision 1.118 2003/05/14 18:48:40 cheshire
1456<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1457More minor refinements:
7f0064bd 1458mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
c9b9ae52
A
1459mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
1460
1461Revision 1.117 2003/05/14 07:08:36 cheshire
1462<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1463Previously, when there was any network configuration change, mDNSResponder
1464would tear down the entire list of active interfaces and start again.
1465That was very disruptive, and caused the entire cache to be flushed,
1466and caused lots of extra network traffic. Now it only removes interfaces
1467that have really gone, and only adds new ones that weren't there before.
1468
1469Revision 1.116 2003/05/14 06:51:56 cheshire
7f0064bd 1470<rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
c9b9ae52
A
1471
1472Revision 1.115 2003/05/14 06:44:31 cheshire
1473Improve debugging message
1474
1475Revision 1.114 2003/05/07 01:47:03 cheshire
1476<rdar://problem/3250330> Also protect against NULL domainlabels
1477
1478Revision 1.113 2003/05/07 00:28:18 cheshire
1479<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1480
1481Revision 1.112 2003/05/06 00:00:46 cheshire
1482<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1483
1484Revision 1.111 2003/05/05 23:42:08 cheshire
1485<rdar://problem/3245631> Resolves never succeed
1486Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
1487instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
1488
1489Revision 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
1492Revision 1.109 2003/04/26 02:41:56 cheshire
1493<rdar://problem/3241281> Change timenow from a local variable to a structure member
1494
1495Revision 1.108 2003/04/25 01:45:56 cheshire
1496<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1497
1498Revision 1.107 2003/04/25 00:41:31 cheshire
1499<rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1500
1501Revision 1.106 2003/04/22 03:14:45 cheshire
1502<rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1503
1504Revision 1.105 2003/04/22 01:07:43 cheshire
1505<rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
1506If TTL parameter is zero, leave record TTL unchanged
1507
1508Revision 1.104 2003/04/21 19:15:52 cheshire
1509Fix some compiler warnings
1510
1511Revision 1.103 2003/04/19 02:26:35 cheshire
8e92c31c 1512<rdar://problem/3233804> Incorrect goodbye packet after conflict
c9b9ae52
A
1513
1514Revision 1.102 2003/04/17 03:06:28 cheshire
8e92c31c 1515<rdar://problem/3231321> No need to query again when a service goes away
c9b9ae52
A
1516Set UnansweredQueries to 2 when receiving a "goodbye" packet
1517
1518Revision 1.101 2003/04/15 20:58:31 jgraessl
8e92c31c 1519<rdar://problem/3229014> Added a hash to lookup records in the cache.
c9b9ae52
A
1520
1521Revision 1.100 2003/04/15 18:53:14 cheshire
8e92c31c 1522<rdar://problem/3229064> Bug in ScheduleNextTask
c9b9ae52
A
1523mDNS.c 1.94 incorrectly combined two "if" statements into one.
1524
1525Revision 1.99 2003/04/15 18:09:13 jgraessl
8e92c31c 1526<rdar://problem/3228892>
c9b9ae52
A
1527Reviewed by: Stuart Cheshire
1528Added code to keep track of when the next cache item will expire so we can
1529call TidyRRCache only when necessary.
1530
1531Revision 1.98 2003/04/03 03:43:55 cheshire
1532<rdar://problem/3216837> Off-by-one error in probe rate limiting
1533
1534Revision 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
1536Additional fix pointed out by Josh:
1537Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
1538
1539Revision 1.96 2003/04/01 23:58:55 cheshire
1540Minor comment changes
1541
1542Revision 1.95 2003/04/01 23:46:05 cheshire
1543<rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
1544mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
1545to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
1546window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
1547FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
1548
1549Revision 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
1551Solution: Major cleanup of packet timing and conflict handling rules
1552
1553Revision 1.93 2003/03/28 01:54:36 cheshire
1554Minor tidyup of IPv6 (AAAA) code
1555
1556Revision 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
1558Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
1559Fixes:
15601. Make mDNS_DeregisterInterface() safe to call from a callback
8e92c31c 15612. Make HostNameCallback() use DeadvertiseInterface() instead
c9b9ae52
A
1562 (it never really needed to deregister the interface at all)
1563
1564Revision 1.91 2003/03/15 04:40:36 cheshire
1565Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1566
1567Revision 1.90 2003/03/14 20:26:37 cheshire
1568Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1569
1570Revision 1.89 2003/03/12 19:57:50 cheshire
1571Fixed typo in debug message
1572
1573Revision 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
1576Revision 1.87 2003/03/11 01:27:20 cheshire
1577Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1578
1579Revision 1.86 2003/03/06 20:44:33 cheshire
1580Comment tidyup
1581
1582Revision 1.85 2003/03/05 03:38:35 cheshire
8e92c31c 1583<rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
c9b9ae52
A
1584Fixed by leaving client in list after conflict, until client explicitly deallocates
1585
1586Revision 1.84 2003/03/05 01:27:30 cheshire
8e92c31c 1587<rdar://problem/3185482> Different TTL for multicast versus unicast responses
c9b9ae52
A
1588When building unicast responses, record TTLs are capped to 10 seconds
1589
1590Revision 1.83 2003/03/04 23:48:52 cheshire
8e92c31c 1591<rdar://problem/3188865> Double probes after wake from sleep
c9b9ae52
A
1592Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1593
1594Revision 1.82 2003/03/04 23:38:29 cheshire
8e92c31c 1595<rdar://problem/3099194> mDNSResponder needs performance improvements
c9b9ae52
A
1596Only set rr->CRActiveQuestion to point to the
1597currently active representative of a question set
1598
1599Revision 1.81 2003/02/21 03:35:34 cheshire
8e92c31c 1600<rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
c9b9ae52
A
1601
1602Revision 1.80 2003/02/21 02:47:53 cheshire
8e92c31c 1603<rdar://problem/3099194> mDNSResponder needs performance improvements
c9b9ae52
A
1604Several places in the code were calling CacheRRActive(), which searched the entire
1605question list every time, to see if this cache resource record answers any question.
1606Instead, we now have a field "CRActiveQuestion" in the resource record structure
1607
1608Revision 1.79 2003/02/21 01:54:07 cheshire
8e92c31c 1609<rdar://problem/3099194> mDNSResponder needs performance improvements
c9b9ae52
A
1610Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1611
1612Revision 1.78 2003/02/20 06:48:32 cheshire
8e92c31c 1613<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
c9b9ae52
A
1614Reviewed by: Josh Graessley, Bob Bradley
1615
1616Revision 1.77 2003/01/31 03:35:59 cheshire
8e92c31c 1617<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
c9b9ae52
A
1618When there were *two* active questions in the list, they were incorrectly
1619finding *each other* and *both* being marked as duplicates of another question
1620
1621Revision 1.76 2003/01/29 02:46:37 cheshire
1622Fix for IPv6:
1623A physical interface is identified solely by its InterfaceID (not by IP and type).
1624On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
1625In cases where the requested outbound protocol (v4 or v6) is not supported on
1626that InterfaceID, the platform support layer should simply discard that packet.
1627
1628Revision 1.75 2003/01/29 01:47:40 cheshire
1629Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1630
1631Revision 1.74 2003/01/28 05:26:25 cheshire
8e92c31c 1632<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
c9b9ae52
A
1633Add 'Active' flag for interfaces
1634
1635Revision 1.73 2003/01/28 03:45:12 cheshire
1636Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1637
1638Revision 1.72 2003/01/28 01:49:48 cheshire
8e92c31c 1639<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
c9b9ae52
A
1640FindDuplicateQuestion() was incorrectly finding the question itself in the list,
1641and incorrectly marking it as a duplicate (of itself), so that it became inactive.
1642
1643Revision 1.71 2003/01/28 01:41:44 cheshire
8e92c31c 1644<rdar://problem/3153091> Race condition when network change causes bad stuff
c9b9ae52
A
1645When an interface goes away, interface-specific questions on that interface become orphaned.
1646Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
1647Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
1648
1649Revision 1.70 2003/01/23 19:00:20 cheshire
1650Protect against infinite loops in mDNS_Execute
1651
1652Revision 1.69 2003/01/21 22:56:32 jgraessl
8e92c31c 1653<rdar://problem/3124348> service name changes are not properly handled
c9b9ae52
A
1654Submitted by: Stuart Cheshire
1655Reviewed by: Joshua Graessley
1656Applying changes for 3124348 to main branch. 3124348 changes went in to a
1657branch for SU.
1658
1659Revision 1.68 2003/01/17 04:09:27 cheshire
8e92c31c 1660<rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
c9b9ae52
A
1661
1662Revision 1.67 2003/01/17 03:56:45 cheshire
1663Default 24-hour TTL is far too long. Changing to two hours.
1664
1665Revision 1.66 2003/01/13 23:49:41 jgraessl
1666Merged changes for the following fixes in to top of tree:
8e92c31c
A
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
c9b9ae52
A
1670
1671Revision 1.65 2002/12/23 22:13:28 jgraessl
1672Reviewed by: Stuart Cheshire
1673Initial IPv6 support for mDNSResponder.
1674
1675Revision 1.64 2002/11/26 20:49:06 cheshire
8e92c31c 1676<rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
c9b9ae52
A
1677
1678Revision 1.63 2002/09/21 20:44:49 zarzycki
1679Added APSL info
1680
1681Revision 1.62 2002/09/20 03:25:37 cheshire
1682Fix some compiler warnings
1683
1684Revision 1.61 2002/09/20 01:05:24 cheshire
1685Don't kill the Extras list in mDNS_DeregisterService()
1686
1687Revision 1.60 2002/09/19 23:47:35 cheshire
7f0064bd 1688Added mDNS_RegisterNoSuchService() function for assertion of non-existence
c9b9ae52
A
1689of a particular named service
1690
1691Revision 1.59 2002/09/19 21:25:34 cheshire
1692mDNS_snprintf() doesn't need to be in a separate file
1693
1694Revision 1.58 2002/09/19 04:20:43 cheshire
1695Remove high-ascii characters that confuse some systems
1696
1697Revision 1.57 2002/09/17 01:07:08 cheshire
1698Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1699
1700Revision 1.56 2002/09/16 19:44:17 cheshire
1701Merge in license terms from Quinn's copy, in preparation for Darwin release
1702*/
1703
8e92c31c
A
1704#include "DNSCommon.h" // Defines general DNS untility routines
1705#include "uDNS.h" // Defines entry points into unicast-specific routines
c9b9ae52 1706// Disable certain benign warnings with Microsoft compilers
6528fe3e 1707#if(defined(_MSC_VER))
c9b9ae52
A
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
c9b9ae52
A
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)
6528fe3e
A
1719#endif
1720
6528fe3e 1721// ***************************************************************************
c9b9ae52 1722#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
1723#pragma mark -
1724#pragma mark - Program Constants
1725#endif
1726
c9b9ae52 1727mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
7f0064bd 1728mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
c9b9ae52 1729mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
7f0064bd 1730mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
c9b9ae52
A
1731mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
1732mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
8e92c31c
A
1733mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
1734
1735mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
283ee3ff 1736mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1;
c9b9ae52 1737
8e92c31c 1738mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
6528fe3e 1739
c9d2d929 1740#define UnicastDNSPortAsNumber 53
6528fe3e
A
1741#define MulticastDNSPortAsNumber 5353
1742mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
1743mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
c9b9ae52 1744mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
c9d2d929
A
1745mDNSexport const mDNSv4Addr AllDNSLinkGroupv4 = { { 224, 0, 0, 251 } };
1746mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } };
c9b9ae52
A
1747mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
1748mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
6528fe3e 1749
c9d2d929
A
1750mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
1751mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
1752mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
1753mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
1754mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
1755mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
6528fe3e 1756
c9b9ae52
A
1757// Any records bigger than this are considered 'large' records
1758#define SmallRecordLimit 1024
1759
c9b9ae52 1760#define kMaxUpdateCredits 10
7f0064bd 1761#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
c9b9ae52 1762
283ee3ff 1763mDNSexport const char *const mDNS_DomainTypeNames[] =
6528fe3e 1764 {
283ee3ff
A
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
6528fe3e
A
1770 };
1771
7f0064bd
A
1772#ifdef UNICAST_DISABLED
1773#define uDNS_IsActiveQuery(q, u) mDNSfalse
1774#endif
1775
6528fe3e 1776// ***************************************************************************
c9b9ae52
A
1777#if COMPILER_LIKES_PRAGMA_MARK
1778#pragma mark -
1779#pragma mark - Specialized mDNS version of vsnprintf
1780#endif
1781
1782static 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;
8e92c31c 1794 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
c9b9ae52
A
1795
1796mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
1797 {
1798 mDNSu32 nwritten = 0;
1799 int c;
8e92c31c 1800 if (buflen == 0) return(0);
28f7d060 1801 buflen--; // Pre-reserve one space in the buffer for the terminating null
8e92c31c 1802 if (buflen == 0) goto exit;
c9b9ae52
A
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 {
c9b9ae52
A
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;
c9b9ae52
A
1918 switch (ip->type)
1919 {
8e92c31c
A
1920 case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
1921 case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
c9b9ae52
A
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;
8e92c31c
A
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;
c9d2d929
A
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;
c9b9ae52
A
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 {
8e92c31c
A
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;
c9b9ae52
A
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 {
c9d2d929
A
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; }
c9b9ae52
A
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 }
8e92c31c
A
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)
c9b9ae52
A
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
8e92c31c 2043 // Make sure we don't truncate in the middle of a UTF-8 character.
c9d2d929
A
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).
8e92c31c 2047 if (i > buflen - nwritten)
c9b9ae52
A
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
2065mDNSexport 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
6528fe3e
A
2079#pragma mark -
2080#pragma mark - General Utility Functions
2081#endif
2082
c9b9ae52
A
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
2087mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
6528fe3e 2088 {
c9b9ae52
A
2089 if (ActiveQuestion(q))
2090 if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
2091 m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
6528fe3e
A
2092 }
2093
283ee3ff
A
2094mDNSlocal 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
2103mDNSlocal 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
2108mDNSlocal 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
2138mDNSexport 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()
2186mDNSlocal 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
c9d2d929
A
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().
283ee3ff
A
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()
2200mDNSlocal 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
6528fe3e 2229// ***************************************************************************
c9b9ae52 2230#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e 2231#pragma mark -
8e92c31c 2232#pragma mark - Resource Record Utility Functions
6528fe3e
A
2233#endif
2234
8e92c31c 2235#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
6528fe3e 2236
8e92c31c
A
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)) )
6528fe3e 2241
8e92c31c
A
2242#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2243 (ResourceRecordIsValidAnswer(RR) && \
2244 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
6528fe3e 2245
8e92c31c
A
2246#define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2247#define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
6528fe3e 2248
8e92c31c 2249#define InitialAnnounceCount ((mDNSu8)10)
c9b9ae52 2250
8e92c31c
A
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)
6528fe3e 2258
8e92c31c
A
2259#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2260 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2261 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
6528fe3e 2262
8e92c31c
A
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))
6528fe3e 2267
8e92c31c 2268#define MaxUnansweredQueries 4
c9b9ae52 2269
8e92c31c
A
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
2276mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
c9b9ae52 2277 {
8e92c31c
A
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);
c9d2d929 2283 return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(r1->name, r2->name));
c9b9ae52
A
2284 }
2285
7f0064bd
A
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.
8e92c31c 2291mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
c9b9ae52 2292 {
8e92c31c
A
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);
7f0064bd 2298 if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
c9d2d929 2299 return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(pktrr->resrec.name, authrr->resrec.name));
c9b9ae52 2300 }
6528fe3e 2301
8e92c31c
A
2302// IdenticalResourceRecord returns true if two resources records have
2303// the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
2304mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
c9b9ae52 2305 {
8e92c31c
A
2306 if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
2307 if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
c9d2d929 2308 if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name)) return(mDNSfalse);
8e92c31c 2309 return(SameRData(r1, r2));
6528fe3e
A
2310 }
2311
8e92c31c
A
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.
2319mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
c9b9ae52 2320 {
8e92c31c 2321 // If RR signature is different, or data is different, then don't suppress our answer
283ee3ff 2322 if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
8e92c31c
A
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);
c9b9ae52 2334 }
6528fe3e 2335
8e92c31c 2336mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
6528fe3e 2337 {
8e92c31c 2338 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
6528fe3e 2339 {
c9d2d929 2340 //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
8e92c31c
A
2341 if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2342 m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
c9b9ae52
A
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 }
6528fe3e
A
2349 }
2350
c9b9ae52 2351mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
6528fe3e 2352 {
c9b9ae52
A
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)
6528fe3e 2362 {
7f0064bd 2363 m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique);
c9b9ae52
A
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;
6528fe3e 2370 }
6528fe3e 2371
c9b9ae52
A
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);
6528fe3e
A
2393 }
2394
8e92c31c 2395#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
c9b9ae52
A
2396
2397mDNSlocal 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
7f0064bd 2403 if (target && SameDomainName(target, &m->MulticastHostname))
283ee3ff 2404 debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
c9b9ae52 2405
7f0064bd 2406 if (target && !SameDomainName(target, &m->MulticastHostname))
c9b9ae52 2407 {
283ee3ff 2408 AssignDomainName(target, &m->MulticastHostname);
c9b9ae52
A
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.
7f0064bd 2419 if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
c9d2d929 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));
c9b9ae52 2421
7f0064bd
A
2422 rr->AnnounceCount = InitialAnnounceCount;
2423 rr->RequireGoodbye = mDNSfalse;
c9b9ae52
A
2424 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2425 InitializeLastAPTime(m,rr);
2426 }
2427 }
2428
7f0064bd 2429mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
c9b9ae52 2430 {
c9b9ae52
A
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
c9b9ae52 2442// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
c9d2d929
A
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))
c9b9ae52
A
2445
2446mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
6528fe3e 2447 {
c9b9ae52
A
2448 domainname *target = GetRRDomainNameTarget(&rr->resrec);
2449 AuthRecord *r;
2450 AuthRecord **p = &m->ResourceRecords;
2451 AuthRecord **d = &m->DuplicateRecords;
7f0064bd
A
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); }
c9b9ae52 2457
7f0064bd 2458#ifndef UNICAST_DISABLED
283ee3ff 2459 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
8e92c31c
A
2460 rr->uDNS_info.id = zeroID;
2461 else return uDNS_RegisterRecord(m, rr);
7f0064bd
A
2462#endif
2463
6528fe3e 2464 while (*p && *p != rr) p=&(*p)->next;
c9b9ae52 2465 while (*d && *d != rr) d=&(*d)->next;
283ee3ff 2466 if (*d || *p)
6528fe3e 2467 {
c9d2d929 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));
6528fe3e
A
2469 return(mStatus_AlreadyRegistered);
2470 }
2471
2472 if (rr->DependentOn)
2473 {
c9b9ae52
A
2474 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2475 rr->resrec.RecordType = kDNSRecordTypeVerified;
6528fe3e
A
2476 else
2477 {
c9b9ae52 2478 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
283ee3ff 2479 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
6528fe3e
A
2480 return(mStatus_Invalid);
2481 }
c9b9ae52 2482 if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
6528fe3e 2483 {
c9b9ae52 2484 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
283ee3ff 2485 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
6528fe3e
A
2486 return(mStatus_Invalid);
2487 }
2488 }
2489
c9b9ae52 2490 // If this resource record is referencing a specific interface, make sure it exists
8e92c31c 2491 if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
c9b9ae52
A
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
6528fe3e
A
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
c9b9ae52 2513// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
7f0064bd 2514// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
8e92c31c
A
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;
6528fe3e
A
2517
2518 // Field Group 2: Transient state for Authoritative Records
2519 rr->Acknowledged = mDNSfalse;
c9b9ae52
A
2520 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
2521 rr->AnnounceCount = InitialAnnounceCount;
7f0064bd 2522 rr->RequireGoodbye = mDNSfalse;
283ee3ff 2523 rr->LocalAnswer = mDNSfalse;
6528fe3e 2524 rr->IncludeInProbe = mDNSfalse;
c9b9ae52 2525 rr->ImmedAnswer = mDNSNULL;
7f0064bd 2526 rr->ImmedUnicast = mDNSfalse;
c9b9ae52
A
2527 rr->ImmedAdditional = mDNSNULL;
2528 rr->SendRNow = mDNSNULL;
7f0064bd 2529 rr->v4Requester = zerov4Addr;
c9b9ae52 2530 rr->v6Requester = zerov6Addr;
6528fe3e
A
2531 rr->NextResponse = mDNSNULL;
2532 rr->NR_AnswerTo = mDNSNULL;
2533 rr->NR_AdditionalTo = mDNSNULL;
c9b9ae52 2534 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
8e92c31c 2535 if (!rr->HostTarget) InitializeLastAPTime(m, rr);
c9b9ae52
A
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()
6528fe3e 2540 rr->NewRData = mDNSNULL;
c9b9ae52 2541 rr->newrdlength = 0;
6528fe3e 2542 rr->UpdateCallback = mDNSNULL;
c9b9ae52
A
2543 rr->UpdateCredits = kMaxUpdateCredits;
2544 rr->NextUpdateCredit = 0;
2545 rr->UpdateBlocked = 0;
6528fe3e 2546
c9b9ae52 2547// rr->resrec.interface = already set in mDNS_SetupResourceRecord
283ee3ff 2548// rr->resrec.name->c = MUST be set by client
c9b9ae52
A
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
6528fe3e
A
2553
2554 if (rr->HostTarget)
8e92c31c 2555 SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
6528fe3e
A
2556 else
2557 {
c9b9ae52
A
2558 rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
2559 rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
6528fe3e 2560 }
6528fe3e 2561
283ee3ff 2562 if (!ValidateDomainName(rr->resrec.name))
7f0064bd
A
2563 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2564
7cb34e5c
A
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.
7f0064bd 2568 if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
c9b9ae52
A
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))
7f0064bd 2572 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
c9b9ae52 2573
283ee3ff 2574 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
7cb34e5c 2575 rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
c9b9ae52 2576
8e92c31c 2577 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
c9b9ae52 2578 {
c9b9ae52
A
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;
283ee3ff 2583 for (r = m->ResourceRecords; r; r=r->next)
c9b9ae52
A
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 }
283ee3ff 2589 if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
c9b9ae52 2590 {
283ee3ff
A
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;
c9b9ae52
A
2596 }
2597 }
2598 }
283ee3ff
A
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 {
7cb34e5c 2605 debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr));
283ee3ff
A
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 }
c9b9ae52
A
2614 else
2615 {
7cb34e5c 2616 debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
283ee3ff
A
2617 if (!m->NewLocalRecords) m->NewLocalRecords = rr;
2618 *p = rr;
c9b9ae52 2619 }
7f0064bd
A
2620
2621 // For records that are not going to probe, acknowledge them right away
283ee3ff
A
2622 if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
2623 AcknowledgeRecord(m, rr);
7f0064bd 2624
6528fe3e
A
2625 return(mStatus_NoError);
2626 }
2627
c9b9ae52
A
2628mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
2629 {
2630 m->ProbeFailTime = m->timenow;
2631 m->NumFailedProbes++;
7f0064bd
A
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)
8e92c31c 2640 {
7f0064bd
A
2641 m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
2642 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
283ee3ff 2643 m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
8e92c31c
A
2644 }
2645 }
2646
2647mDNSlocal 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
c9b9ae52
A
2654 }
2655
6528fe3e
A
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
2659typedef 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.
c9b9ae52 2663mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
6528fe3e 2664 {
283ee3ff 2665 AuthRecord *r2;
c9b9ae52
A
2666 mDNSu8 RecordType = rr->resrec.RecordType;
2667 AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
8e92c31c 2668
7f0064bd 2669#ifndef UNICAST_DISABLED
283ee3ff 2670 if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)))
8e92c31c 2671 return uDNS_DeregisterRecord(m, rr);
7f0064bd 2672#endif
8e92c31c 2673
c9b9ae52
A
2674 while (*p && *p != rr) p=&(*p)->next;
2675
2676 if (*p)
6528fe3e 2677 {
c9b9ae52
A
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 {
283ee3ff
A
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;
c9b9ae52
A
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;
c9d2d929 2694 debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52
A
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;
7f0064bd
A
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;
c9b9ae52
A
2713 }
2714 }
6528fe3e
A
2715 }
2716 else
2717 {
c9b9ae52
A
2718 // We didn't find our record on the main list; try the DuplicateRecords list instead.
2719 p = &m->DuplicateRecords;
6528fe3e 2720 while (*p && *p != rr) p=&(*p)->next;
c9b9ae52 2721 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
7f0064bd 2722 if (*p) rr->RequireGoodbye = mDNSfalse;
c9d2d929 2723 if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52
A
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)
c9d2d929 2730 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52
A
2731 return(mStatus_BadReferenceErr);
2732 }
6528fe3e 2733
c9b9ae52
A
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
7f0064bd 2736 if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
c9b9ae52 2737 {
c9d2d929 2738 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52
A
2739 rr->resrec.RecordType = kDNSRecordTypeDeregistering;
2740 rr->resrec.rroriginalttl = 0;
2741 rr->ImmedAnswer = mDNSInterfaceMark;
283ee3ff
A
2742 if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
2743 m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
8e92c31c
A
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
283ee3ff
A
2749 if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
2750 if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
8e92c31c 2751 rr->next = mDNSNULL;
6528fe3e 2752
8e92c31c
A
2753 if (RecordType == kDNSRecordTypeUnregistered)
2754 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
283ee3ff 2755 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
8e92c31c
A
2756 else if (RecordType == kDNSRecordTypeDeregistering)
2757 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
283ee3ff 2758 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
8e92c31c
A
2759 else
2760 {
c9d2d929 2761 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
8e92c31c
A
2762 rr->resrec.RecordType = kDNSRecordTypeUnregistered;
2763 }
6528fe3e 2764
8e92c31c
A
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)",
283ee3ff 2767 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
6528fe3e 2768
8e92c31c
A
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
283ee3ff
A
2772 if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse);
2773
8e92c31c
A
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
283ee3ff
A
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
8e92c31c
A
2784 {
2785 RecordProbeFailure(m, rr);
283ee3ff
A
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 }
8e92c31c
A
2796 }
2797 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2798 }
2799 return(mStatus_NoError);
6528fe3e
A
2800 }
2801
2802// ***************************************************************************
c9b9ae52 2803#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
2804#pragma mark -
2805#pragma mark -
2806#pragma mark - Packet Sending Functions
2807#endif
2808
7f0064bd
A
2809mDNSlocal 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 }
283ee3ff 2821 debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
7f0064bd
A
2822 }
2823
2824mDNSlocal 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 ...
7cb34e5c 2842 rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target
283ee3ff 2843 SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name))
7f0064bd
A
2844 AddRecordToResponseList(nrpp, rr2, rr);
2845 }
2846 }
2847
2848mDNSlocal 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
c9b9ae52 2923mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
6528fe3e 2924 {
7f0064bd 2925 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
c9b9ae52 2926 // that it should go ahead and immediately dispose of this registration
7f0064bd
A
2927 rr->resrec.RecordType = kDNSRecordTypeShared;
2928 rr->RequireGoodbye = mDNSfalse;
2929 mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this
6528fe3e
A
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.
c9b9ae52 2935mDNSlocal void DiscardDeregistrations(mDNS *const m)
6528fe3e 2936 {
c9b9ae52 2937 if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
6528fe3e
A
2938 m->CurrentRecord = m->ResourceRecords;
2939
2940 while (m->CurrentRecord)
2941 {
c9b9ae52 2942 AuthRecord *rr = m->CurrentRecord;
c9b9ae52 2943 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
7f0064bd 2944 CompleteDeregistration(m, rr); // Don't touch rr after this
283ee3ff
A
2945 else
2946 m->CurrentRecord = rr->next;
6528fe3e
A
2947 }
2948 }
2949
8e92c31c
A
2950mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
2951 {
2952 if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
7f0064bd 2953 else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
716635cc
A
2954 }
2955
c9b9ae52
A
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
6528fe3e
A
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.
c9b9ae52 2972mDNSlocal void SendResponses(mDNS *const m)
6528fe3e 2973 {
c9b9ae52
A
2974 int pktcount = 0;
2975 AuthRecord *rr, *r2;
2976 mDNSs32 maxExistingAnnounceInterval = 0;
2977 const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
6528fe3e 2978
c9b9ae52
A
2979 m->NextScheduledResponse = m->timenow + 0x78000000;
2980
7f0064bd
A
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
c9b9ae52
A
2997 // ***
2998 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
2999 // ***
4e28aad3 3000
c9b9ae52
A
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)
6528fe3e 3003 {
8e92c31c 3004 while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
c9b9ae52
A
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
8e92c31c
A
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.
c9b9ae52
A
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
8e92c31c 3030 if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ...
c9b9ae52
A
3031 ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ...
3032 rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ...
7cb34e5c 3033 rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target
283ee3ff 3034 SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) &&
c9b9ae52 3035 (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID))
8e92c31c 3036 r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too
c9b9ae52
A
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))
c9d2d929 3051 if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
c9b9ae52
A
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
6528fe3e 3055 {
c9b9ae52
A
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;
6528fe3e
A
3060 }
3061 }
c9b9ae52
A
3062
3063 // Now set SendRNow state appropriately
3064 for (rr = m->ResourceRecords; rr; rr=rr->next)
6528fe3e 3065 {
c9b9ae52 3066 if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces
6528fe3e 3067 {
c9b9ae52
A
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))
6528fe3e 3074 {
c9b9ae52
A
3075 rr->AnnounceCount--;
3076 rr->ThisAPInterval *= 2;
3077 rr->LastAPTime = m->timenow;
3078 if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0;
283ee3ff 3079 debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount);
6528fe3e
A
3080 }
3081 }
c9b9ae52 3082 else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface:
6528fe3e 3083 {
8e92c31c 3084 rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface
c9b9ae52
A
3085 rr->ImmedAdditional = mDNSNULL; // No need to send as additional too
3086 rr->LastMCTime = m->timenow;
3087 rr->LastMCInterface = rr->ImmedAnswer;
6528fe3e 3088 }
c9b9ae52 3089 SetNextAnnounceProbeTime(m, rr);
7f0064bd 3090 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
c9b9ae52
A
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;
7f0064bd 3102 mDNSu8 *responseptr = m->omsg.data;
c9b9ae52 3103 mDNSu8 *newptr;
7f0064bd 3104 InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
6528fe3e 3105
c9b9ae52
A
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.
377735b0 3114 for (rr = m->ResourceRecords; rr; rr=rr->next)
c9b9ae52
A
3115 if (rr->SendRNow == intf->InterfaceID)
3116 {
3117 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3118 {
7f0064bd
A
3119 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3120 if (!newptr && m->omsg.h.numAnswers) break;
c9b9ae52
A
3121 numDereg++;
3122 responseptr = newptr;
3123 }
c9d2d929 3124 else if (rr->NewRData) // If we have new data for this record
377735b0 3125 {
c9b9ae52
A
3126 RData *OldRData = rr->resrec.rdata;
3127 mDNSu16 oldrdlength = rr->resrec.rdlength;
8e92c31c 3128 // See if we should send a courtesy "goodbye" for the old data before we replace it.
7f0064bd 3129 if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye)
377735b0 3130 {
7f0064bd
A
3131 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3132 if (!newptr && m->omsg.h.numAnswers) break;
c9b9ae52 3133 numDereg++;
377735b0
A
3134 responseptr = newptr;
3135 }
c9b9ae52
A
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);
7f0064bd 3138 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
716635cc 3139 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
7f0064bd 3140 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
716635cc 3141 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
c9d2d929 3142 if (newptr) responseptr = newptr;
c9b9ae52
A
3143 SetNewRData(&rr->resrec, OldRData, oldrdlength);
3144 }
3145 else
3146 {
7f0064bd 3147 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
716635cc 3148 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
7f0064bd 3149 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl);
c9b9ae52 3150 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
7f0064bd 3151 if (!newptr && m->omsg.h.numAnswers) break;
28f7d060 3152 rr->RequireGoodbye = (mDNSu8) (!m->SleepState);
c9b9ae52
A
3153 if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
3154 responseptr = newptr;
377735b0 3155 }
c9b9ae52
A
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 }
377735b0 3162
c9b9ae52
A
3163 // Second Pass. Add additional records, if there's space.
3164 newptr = responseptr;
6528fe3e 3165 for (rr = m->ResourceRecords; rr; rr=rr->next)
c9b9ae52 3166 if (rr->ImmedAdditional == intf->InterfaceID)
7f0064bd 3167 if (ResourceRecordIsValidAnswer(rr))
6528fe3e 3168 {
7f0064bd
A
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))
6528fe3e 3176 {
c9b9ae52
A
3177 const AuthRecord *a;
3178 for (a = m->ResourceRecords; a; a=a->next)
7f0064bd
A
3179 if (a->LastMCTime == m->timenow &&
3180 a->LastMCInterface == intf->InterfaceID &&
3181 SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; }
6528fe3e 3182 }
7f0064bd
A
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
8e92c31c 3186 {
7f0064bd
A
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 }
8e92c31c 3203 }
6528fe3e 3204 }
c9b9ae52 3205
7f0064bd 3206 if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals)
c9b9ae52
A
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",
7f0064bd
A
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; }
c9b9ae52
A
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 }
6528fe3e
A
3228 }
3229
c9b9ae52
A
3230 // ***
3231 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3232 // ***
6528fe3e 3233
c9b9ae52
A
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
8e92c31c 3241 if (rr->SendRNow)
283ee3ff
A
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 }
c9b9ae52 3247
7f0064bd 3248 if (rr->ImmedAnswer)
c9b9ae52 3249 {
7f0064bd
A
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 }
c9b9ae52
A
3261 }
3262 }
7f0064bd 3263 verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow);
6528fe3e
A
3264 }
3265
c9b9ae52
A
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) ( \
7f0064bd 3277 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
c9b9ae52
A
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
c9d2d929 3284// rr->DelayDelivery
c9b9ae52
A
3285// rr->resrec.rroriginalttl
3286// rr->UnansweredQueries
3287// rr->CRActiveQuestion
3288mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr)
6528fe3e 3289 {
c9b9ae52 3290 rr->NextRequiredQuery = RRExpireTime(rr);
6528fe3e 3291
c9b9ae52
A
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);
c9d2d929
A
3298 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
3299 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond);
c9b9ae52
A
3300 }
3301
3302 if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
3303 m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
7f0064bd
A
3304
3305 if (rr->DelayDelivery)
3306 if (m->NextCacheCheck - rr->DelayDelivery > 0)
3307 m->NextCacheCheck = rr->DelayDelivery;
c9b9ae52 3308 }
6528fe3e 3309
c9d2d929
A
3310#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3311#define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3312#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
6528fe3e 3313
c9b9ae52
A
3314mDNSlocal 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))
6528fe3e 3323 {
c9b9ae52 3324 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
c9d2d929 3325 interval += mDNSRandom(interval/3);
c9b9ae52 3326 rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3;
c9d2d929 3327 rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond;
c9b9ae52 3328 SetNextCacheCheckTime(m, rr);
6528fe3e 3329 }
c9d2d929 3330 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr));
c9b9ae52 3331 return(mStatus_NoError);
6528fe3e
A
3332 }
3333
c9b9ae52 3334#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
6528fe3e 3335
c9b9ae52
A
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.
3339mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
3340 CacheRecord ***kalistptrptr, mDNSu32 *answerforecast)
6528fe3e 3341 {
283ee3ff 3342 mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
c9b9ae52
A
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 {
c9d2d929 3353 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data);
c9b9ae52
A
3354 query->h.numQuestions--;
3355 return(mDNSfalse);
3356 }
3357 else
3358 {
3359 mDNSu32 forecast = *answerforecast;
283ee3ff
A
3360 const mDNSu32 slot = HashSlot(&q->qname);
3361 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
c9b9ae52
A
3362 CacheRecord *rr;
3363 CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update
3364
283ee3ff 3365 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
c9b9ae52
A
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
c9d2d929 3370 rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0) // and it is less than half-way to expiry
c9b9ae52
A
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 }
6528fe3e 3388
c9b9ae52
A
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)
6528fe3e 3401 {
c9b9ae52
A
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
6528fe3e 3406 }
6528fe3e 3407
c9b9ae52
A
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;
6528fe3e 3413
283ee3ff 3414 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache,
c9b9ae52
A
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 }
6528fe3e
A
3426 }
3427
c9b9ae52 3428mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
6528fe3e 3429 {
c9b9ae52 3430 mDNSu32 slot;
283ee3ff 3431 CacheGroup *cg;
c9b9ae52
A
3432 CacheRecord *rr;
3433 domainname *target;
283ee3ff 3434 FORALL_CACHERECORDS(slot, cg, rr)
7cb34e5c 3435 if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname))
283ee3ff 3436 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
c9b9ae52
A
3437 }
3438
3439// Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
3440mDNSlocal 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
3446mDNSlocal 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
3452mDNSlocal 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)
6528fe3e 3459 {
c9b9ae52
A
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);
6528fe3e 3463 }
c9b9ae52
A
3464 return(mDNSfalse);
3465 }
3466
3467mDNSlocal 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;
6528fe3e 3481 }
c9b9ae52
A
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);
6528fe3e
A
3489 }
3490
c9b9ae52 3491mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
6528fe3e 3492 {
c9b9ae52
A
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))
6528fe3e 3499 {
c9b9ae52 3500 // We forecast: qname (n) type (2) class (2)
73792575 3501 mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4;
283ee3ff
A
3502 const mDNSu32 slot = HashSlot(&q->qname);
3503 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
c9b9ae52 3504 CacheRecord *rr;
283ee3ff 3505 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
c9b9ae52
A
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
6528fe3e 3510 {
c9b9ae52
A
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
6528fe3e 3514 }
c9b9ae52 3515 return(mDNStrue);
6528fe3e 3516 }
c9b9ae52
A
3517
3518 return(mDNSfalse);
6528fe3e
A
3519 }
3520
3521// How Standard Queries are generated:
3522// 1. The Question Section contains the question
c9b9ae52 3523// 2. The Additional Section contains answers we already know, to suppress duplicate responses
6528fe3e
A
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
c9b9ae52 3533mDNSlocal void SendQueries(mDNS *const m)
6528fe3e 3534 {
c9b9ae52 3535 int pktcount = 0;
6528fe3e 3536 DNSQuestion *q;
c9b9ae52
A
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 {
c9d2d929
A
3545 mDNSu32 slot;
3546 CacheGroup *cg;
c9b9ae52
A
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
283ee3ff
A
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 }
c9b9ae52 3562
283ee3ff 3563 // Scan our list of questions to see which *unicast* queries need to be sent
8e92c31c
A
3564 for (q = m->Questions; q; q=q->next)
3565 if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow)))
3566 {
7f0064bd
A
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;
283ee3ff
A
3573 if (q->ThisQInterval > MaxQuestionInterval)
3574 q->ThisQInterval = MaxQuestionInterval;
7f0064bd
A
3575 q->LastQTime = m->timenow;
3576 q->LastQTxTime = m->timenow;
3577 q->RecentAnswerPkts = 0;
3578 q->SendQNow = mDNSNULL;
8e92c31c
A
3579 m->ExpectUnicastResponse = m->timenow;
3580 }
3581
283ee3ff 3582 // Scan our list of questions to see which *multicast* queries we're definitely going to send
c9b9ae52 3583 for (q = m->Questions; q; q=q->next)
8e92c31c 3584 if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow))
c9b9ae52
A
3585 {
3586 q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
3587 if (maxExistingQuestionInterval < q->ThisQInterval)
3588 maxExistingQuestionInterval = q->ThisQInterval;
3589 }
6528fe3e 3590
c9b9ae52
A
3591 // Scan our list of questions
3592 // (a) to see if there are any more that are worth accelerating, and
283ee3ff 3593 // (b) to update the state variables for *all* the questions we're going to send
c9b9ae52
A
3594 for (q = m->Questions; q; q=q->next)
3595 {
8e92c31c
A
3596 if (q->SendQNow ||
3597 (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))
c9b9ae52
A
3598 {
3599 // If at least halfway to next query time, advance to next interval
c9d2d929 3600 // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
c9b9ae52
A
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 {
c9d2d929
A
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
c9b9ae52
A
3611 }
3612 }
6528fe3e 3613
c9b9ae52
A
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 }
6528fe3e 3621
c9b9ae52
A
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);
6528fe3e 3625
7f0064bd
A
3626 q->LastQTxTime = m->timenow;
3627 q->RecentAnswerPkts = 0;
283ee3ff 3628 if (q->RequestUnicast) q->RequestUnicast--;
c9b9ae52
A
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)
6528fe3e 3637 {
c9b9ae52
A
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)
6528fe3e 3643 {
c9b9ae52
A
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 }
c9d2d929 3662 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
c9b9ae52
A
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;
7f0064bd 3674 AcknowledgeRecord(m, rr);
c9b9ae52
A
3675 }
3676 }
6528fe3e 3677 }
c9b9ae52
A
3678 m->CurrentRecord = m->DuplicateRecords;
3679 while (m->CurrentRecord)
6528fe3e 3680 {
c9b9ae52
A
3681 AuthRecord *rr = m->CurrentRecord;
3682 m->CurrentRecord = rr->next;
3683 if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
7f0064bd 3684 AcknowledgeRecord(m, rr);
6528fe3e
A
3685 }
3686 }
6528fe3e 3687
c9d2d929 3688 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
c9b9ae52 3689 while (intf)
6528fe3e 3690 {
c9b9ae52 3691 AuthRecord *rr;
7f0064bd
A
3692 mDNSu8 *queryptr = m->omsg.data;
3693 InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags);
c9b9ae52
A
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 {
7f0064bd 3705 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
c9b9ae52 3706 SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ",
7f0064bd 3707 q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
c9b9ae52
A
3708 // If we're suppressing this question, or we successfully put it, update its SendQNow state
3709 if (SuppressOnThisInterface(q->DupSuppress, intf) ||
7f0064bd 3710 BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
c9b9ae52
A
3711 q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
3712 }
6528fe3e 3713
c9b9ae52
A
3714 // Put probe questions in this packet
3715 for (rr = m->ResourceRecords; rr; rr=rr->next)
3716 if (rr->SendRNow == intf->InterfaceID)
3717 {
7f0064bd 3718 mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353;
c9b9ae52 3719 mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
7f0064bd 3720 const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
283ee3ff 3721 mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit));
c9b9ae52
A
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;
c9d2d929 3730 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount);
c9b9ae52
A
3731 }
3732 else
3733 {
c9d2d929 3734 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
7f0064bd 3735 m->omsg.h.numQuestions--;
c9b9ae52
A
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)
6528fe3e 3742 {
c9b9ae52
A
3743 CacheRecord *rr = KnownAnswerList;
3744 mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
7f0064bd 3745 mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd);
6528fe3e 3746 if (newptr)
c9b9ae52 3747 {
c9d2d929 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);
6528fe3e 3749 queryptr = newptr;
c9b9ae52
A
3750 KnownAnswerList = rr->NextInKAList;
3751 rr->NextInKAList = mDNSNULL;
3752 }
6528fe3e
A
3753 else
3754 {
c9b9ae52
A
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.
7f0064bd
A
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;
6528fe3e
A
3760 break;
3761 }
3762 }
6528fe3e 3763
c9b9ae52
A
3764 for (rr = m->ResourceRecords; rr; rr=rr->next)
3765 if (rr->IncludeInProbe)
3766 {
7f0064bd 3767 mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec);
c9b9ae52
A
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)?",
283ee3ff 3771 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52
A
3772 }
3773
7f0064bd 3774 if (queryptr > m->omsg.data)
6528fe3e 3775 {
7f0064bd
A
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);
c9b9ae52 3778 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
7f0064bd
A
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);
7cb34e5c 3784 if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
c9b9ae52
A
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.
6528fe3e 3789 }
c9b9ae52 3790 else // Nothing more to send on this interface; go to next
6528fe3e 3791 {
c9b9ae52
A
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;
6528fe3e 3798 }
c9b9ae52 3799 }
8e92c31c 3800
c9d2d929
A
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 }
6528fe3e
A
3812 }
3813
3814// ***************************************************************************
c9b9ae52 3815#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
3816#pragma mark -
3817#pragma mark - RR List Management & Task Management
3818#endif
3819
6528fe3e
A
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.
c9b9ae52 3822mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord)
6528fe3e 3823 {
c9b9ae52 3824 verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
283ee3ff 3825 q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
6528fe3e 3826
7f0064bd
A
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
c9b9ae52 3831 rr->LastUsed = m->timenow;
c9b9ae52 3832 if (ActiveQuestion(q) && rr->CRActiveQuestion != q)
6528fe3e 3833 {
c9b9ae52
A
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);
6528fe3e 3837 }
6528fe3e 3838
283ee3ff
A
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;
7cb34e5c 3850 q->RecentAnswerPkts = 0;
283ee3ff
A
3851 q->ThisQInterval = MaxQuestionInterval;
3852 q->RequestUnicast = mDNSfalse;
3853 }
3854
7f0064bd
A
3855 if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3856
c9b9ae52
A
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
7f0064bd
A
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
3868mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
3869 {
c9d2d929 3870 rr->DelayDelivery = 0;
7f0064bd
A
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
3883mDNSlocal 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;
283ee3ff 3888 CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
7f0064bd 3889 CacheRecord *rr;
283ee3ff
A
3890 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
3891 if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name))
7f0064bd
A
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);
c9d2d929 3895 if (delay - start > 0) return(delay ? delay : 1); // Make sure we return non-zero if we want to delay
7f0064bd 3896 else return(0);
6528fe3e
A
3897 }
3898
c9b9ae52 3899// CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
6528fe3e
A
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.
c9b9ae52
A
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.
6528fe3e 3906// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
c9b9ae52 3907mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
6528fe3e 3908 {
c9b9ae52
A
3909 if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3910 m->CurrentQuestion = m->Questions;
6528fe3e
A
3911 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3912 {
3913 DNSQuestion *q = m->CurrentQuestion;
3914 m->CurrentQuestion = q->next;
c9b9ae52
A
3915 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3916 {
c9d2d929
A
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.
7f0064bd 3922 if (q->LastAnswerPktNum != m->PktNum)
c9b9ae52 3923 {
7f0064bd
A
3924 q->LastAnswerPktNum = m->PktNum;
3925 if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
283ee3ff 3926 q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
7f0064bd
A
3927 {
3928 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
3929 q->qname.c, DNSTypeName(q->qtype));
283ee3ff
A
3930 q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4);
3931 q->ThisQInterval = InitialQuestionInterval;
7f0064bd
A
3932 SetNextQueryTime(m,q);
3933 }
c9b9ae52 3934 }
c9d2d929 3935 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl);
c9b9ae52
A
3936 q->CurrentAnswers++;
3937 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
3938 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
7f0064bd
A
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 }
c9b9ae52
A
3948 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3949 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3950 }
6528fe3e
A
3951 }
3952 m->CurrentQuestion = mDNSNULL;
3953 }
3954
283ee3ff
A
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.
3965mDNSlocal 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);
c9d2d929 3968 if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
283ee3ff
A
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
c9b9ae52
A
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.
3989mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
6528fe3e 3990 {
c9b9ae52
A
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))
4e28aad3 3998 {
c9d2d929 3999 verbosedebugf("CacheRecordRmv %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52 4000 if (q->CurrentAnswers == 0)
c9d2d929 4001 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype));
c9b9ae52
A
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 {
c9d2d929 4010 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
c9b9ae52
A
4011 ReconfirmAntecedents(m, q);
4012 }
4013 AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
4014 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4e28aad3 4015 }
c9b9ae52 4016 }
6528fe3e 4017 m->CurrentQuestion = mDNSNULL;
6528fe3e
A
4018 }
4019
283ee3ff 4020mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
6528fe3e 4021 {
283ee3ff
A
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;
c9b9ae52 4028 m->rrcache_totalused--;
6528fe3e
A
4029 }
4030
283ee3ff
A
4031mDNSlocal 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);
c9d2d929
A
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));
283ee3ff
A
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
4043mDNSlocal 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
c9d2d929
A
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
283ee3ff 4052mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg)
6528fe3e 4053 {
283ee3ff 4054 CacheRecord **rp = &cg->members;
c9b9ae52
A
4055
4056 if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
6528fe3e 4057 m->lock_rrcache = 1;
c9b9ae52
A
4058
4059 while (*rp)
6528fe3e 4060 {
c9b9ae52
A
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
7f0064bd 4066 verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr));
c9b9ae52
A
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 }
283ee3ff 4072 ReleaseCacheRecord(m, rr);
c9b9ae52
A
4073 }
4074 else // else, not expired; see if we need to query
6528fe3e 4075 {
7f0064bd
A
4076 if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
4077 event = rr->DelayDelivery;
4078 else
c9b9ae52 4079 {
7f0064bd
A
4080 if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr);
4081 if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
c9b9ae52 4082 {
7f0064bd
A
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(),
c9d2d929 4091 // which will correctly update m->NextCacheCheck for us
7f0064bd
A
4092 event = m->timenow + 0x3FFFFFFF;
4093 }
c9b9ae52
A
4094 }
4095 }
4096 if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0)
4097 m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
4098 rp = &rr->next;
6528fe3e
A
4099 }
4100 }
283ee3ff
A
4101 if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp);
4102 cg->rrcache_tail = rp;
6528fe3e 4103 m->lock_rrcache = 0;
6528fe3e
A
4104 }
4105
c9b9ae52 4106mDNSlocal void AnswerNewQuestion(mDNS *const m)
6528fe3e 4107 {
c9b9ae52
A
4108 mDNSBool ShouldQueryImmediately = mDNStrue;
4109 CacheRecord *rr;
4110 DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
7f0064bd 4111 const mDNSu32 slot = HashSlot(&q->qname);
283ee3ff 4112 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
c9b9ae52
A
4113
4114 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
6528fe3e 4115
283ee3ff 4116 if (cg) CheckCacheExpiration(m, cg);
c9b9ae52
A
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.
8e92c31c 4122 // If the client's question callback deletes the question, then m->CurrentQuestion will
c9b9ae52 4123 // be advanced, and we'll exit out of the loop
6528fe3e 4124 m->lock_rrcache = 1;
c9b9ae52
A
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
283ee3ff
A
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)
c9b9ae52 4148 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
6528fe3e 4149 {
c9b9ae52
A
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)",
283ee3ff 4155 rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
c9b9ae52
A
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
7f0064bd
A
4161 if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique))
4162 ShouldQueryImmediately = mDNSfalse;
c9b9ae52
A
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
6528fe3e 4169 }
716635cc 4170 else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype))
283ee3ff 4171 if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname))
716635cc 4172 ShouldQueryImmediately = mDNSfalse;
c9b9ae52
A
4173
4174 if (ShouldQueryImmediately && m->CurrentQuestion == q)
6528fe3e 4175 {
283ee3ff
A
4176 q->ThisQInterval = InitialQuestionInterval;
4177 q->LastQTime = m->timenow - q->ThisQInterval;
c9b9ae52
A
4178 m->NextScheduledQuery = m->timenow;
4179 }
4180 m->CurrentQuestion = mDNSNULL;
4181 m->lock_rrcache = 0;
4182 }
6528fe3e 4183
c9d2d929
A
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
c9b9ae52
A
4186mDNSlocal 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)
6528fe3e 4190
c9b9ae52
A
4191 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4192
7f0064bd 4193 if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
c9b9ae52 4194 m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
6528fe3e 4195
7f0064bd 4196 if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
283ee3ff
A
4197 m->CurrentRecord = m->ResourceRecords;
4198 while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
c9b9ae52
A
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
6528fe3e 4207 }
c9b9ae52
A
4208 }
4209
4210 m->CurrentQuestion = mDNSNULL;
7f0064bd 4211 m->CurrentRecord = mDNSNULL;
c9b9ae52 4212 }
6528fe3e 4213
283ee3ff 4214mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG)
6528fe3e 4215 {
283ee3ff 4216 CacheEntity *e = mDNSNULL;
6528fe3e 4217
c9b9ae52
A
4218 if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
4219 m->lock_rrcache = 1;
6528fe3e 4220
c9b9ae52
A
4221 // If we have no free records, ask the client layer to give us some more memory
4222 if (!m->rrcache_free && m->MainCallback)
6528fe3e 4223 {
c9b9ae52
A
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
8e92c31c
A
4236 {
4237 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
c9b9ae52 4238 m->MainCallback(m, mStatus_GrowCache);
8e92c31c
A
4239 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
4240 }
6528fe3e
A
4241 }
4242
c9b9ae52
A
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)
6528fe3e 4246 {
c9b9ae52
A
4247 #if MDNS_DEBUGMSGS
4248 mDNSu32 oldtotalused = m->rrcache_totalused;
4249 #endif
4250 mDNSu32 slot;
c9b9ae52 4251 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
6528fe3e 4252 {
283ee3ff
A
4253 CacheGroup **cp = &m->rrcache_hash[slot];
4254 while (*cp)
6528fe3e 4255 {
283ee3ff
A
4256 CacheRecord **rp = &(*cp)->members;
4257 while (*rp)
6528fe3e 4258 {
283ee3ff
A
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 }
6528fe3e 4269 }
c9d2d929 4270 if ((*cp)->rrcache_tail != rp) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp);
283ee3ff
A
4271 (*cp)->rrcache_tail = rp;
4272 if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next;
4273 else ReleaseCacheGroup(m, cp);
6528fe3e
A
4274 }
4275 }
c9b9ae52
A
4276 #if MDNS_DEBUGMSGS
4277 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
4278 #endif
4279 }
c9b9ae52 4280
283ee3ff 4281 if (m->rrcache_free) // If there are records in the free list, take one
c9b9ae52 4282 {
283ee3ff
A
4283 e = m->rrcache_free;
4284 m->rrcache_free = e->next;
c9b9ae52
A
4285 if (++m->rrcache_totalused >= m->rrcache_report)
4286 {
7cb34e5c 4287 debugf("RR Cache now using %ld objects", m->rrcache_totalused);
c9b9ae52
A
4288 if (m->rrcache_report < 100) m->rrcache_report += 10;
4289 else m->rrcache_report += 100;
4290 }
283ee3ff
A
4291 mDNSPlatformMemZero(e, sizeof(*e));
4292 }
4293
4294 m->lock_rrcache = 0;
4295
4296 return(e);
4297 }
4298
4299mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength)
4300 {
4301 CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg);
4302 if (r)
4303 {
7f0064bd 4304 r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage
7f0064bd 4305 if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage
c9b9ae52
A
4306 {
4307 r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength);
4308 if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength;
283ee3ff 4309 else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; }
c9b9ae52 4310 }
6528fe3e 4311 }
283ee3ff
A
4312 return(r);
4313 }
6528fe3e 4314
283ee3ff
A
4315mDNSlocal 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;
c9d2d929 4325 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
283ee3ff
A
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);
c9b9ae52 4334
283ee3ff
A
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);
c9b9ae52 4340 }
6528fe3e 4341
c9b9ae52
A
4342mDNSlocal 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);
6528fe3e
A
4353 }
4354
7f0064bd 4355mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m)
6528fe3e 4356 {
7f0064bd 4357 mDNSs32 time;
6528fe3e 4358 mDNSPlatformLock(m);
7f0064bd 4359 if (m->mDNS_busy)
c9b9ae52 4360 {
7f0064bd
A
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);
c9b9ae52 4363 }
c9b9ae52 4364
7f0064bd 4365 if (m->timenow) time = m->timenow;
28f7d060 4366 else time = mDNS_TimeNow_NoLock(m);
6528fe3e 4367 mDNSPlatformUnlock(m);
7f0064bd 4368 return(time);
6528fe3e
A
4369 }
4370
c9b9ae52 4371mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
6528fe3e 4372 {
c9b9ae52
A
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;
6528fe3e 4378
c9b9ae52
A
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;
283ee3ff
A
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 }
c9b9ae52 4403 }
6528fe3e 4404
c9b9ae52 4405 // 4. See if we can answer any of our new local questions from the cache
7f0064bd
A
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 }
283ee3ff 4411 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
c9b9ae52 4412
c9b9ae52 4413 for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
283ee3ff 4414 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4e28aad3 4415
283ee3ff
A
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");
6528fe3e 4423
c9b9ae52
A
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.
c9d2d929 4430 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
c9b9ae52
A
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.
c9d2d929 4433 // Finally, we send responses, including the previously mentioned records that just completed probing
c9b9ae52
A
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 }
6528fe3e 4457
c9d2d929 4458 m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
c9b9ae52 4459 }
6528fe3e 4460
c9b9ae52
A
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
7f0064bd 4481#ifndef UNICAST_DISABLED
8e92c31c 4482 uDNS_Execute(m);
7f0064bd 4483#endif
c9b9ae52
A
4484 mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4485 return(m->NextScheduledEvent);
6528fe3e
A
4486 }
4487
c9b9ae52
A
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.
4499mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
6528fe3e 4500 {
c9b9ae52
A
4501 AuthRecord *rr;
4502
4503 mDNS_Lock(m);
6528fe3e
A
4504
4505 m->SleepState = sleepstate;
283ee3ff 4506 LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
6528fe3e
A
4507
4508 if (sleepstate)
4509 {
7f0064bd
A
4510#ifndef UNICAST_DISABLED
4511 uDNS_Sleep(m);
4512#endif
c9b9ae52 4513 // Mark all the records we need to deregister and send them
6528fe3e 4514 for (rr = m->ResourceRecords; rr; rr=rr->next)
7f0064bd 4515 if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
c9b9ae52
A
4516 rr->ImmedAnswer = mDNSInterfaceMark;
4517 SendResponses(m);
6528fe3e
A
4518 }
4519 else
4520 {
4521 DNSQuestion *q;
c9b9ae52 4522 mDNSu32 slot;
283ee3ff 4523 CacheGroup *cg;
c9b9ae52
A
4524 CacheRecord *cr;
4525
7f0064bd
A
4526#ifndef UNICAST_DISABLED
4527 uDNS_Wake(m);
4528#endif
8e92c31c 4529 // 1. Retrigger all our questions
c9b9ae52
A
4530 for (q = m->Questions; q; q=q->next) // Scan our list of questions
4531 if (ActiveQuestion(q))
4532 {
7f0064bd 4533 q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question
283ee3ff 4534 q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
7f0064bd
A
4535 q->LastQTime = m->timenow - q->ThisQInterval;
4536 q->RecentAnswerPkts = 0;
c9b9ae52
A
4537 ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
4538 m->NextScheduledQuery = m->timenow;
4539 }
6528fe3e 4540
c9b9ae52
A
4541 // 2. Re-validate our cache records
4542 m->NextCacheCheck = m->timenow;
283ee3ff 4543 FORALL_CACHERECORDS(slot, cg, cr)
c9d2d929 4544 mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect);
c9b9ae52
A
4545
4546 // 3. Retrigger probing and announcing for all our authoritative records
6528fe3e
A
4547 for (rr = m->ResourceRecords; rr; rr=rr->next)
4548 {
c9b9ae52 4549 if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
7f0064bd
A
4550 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
4551 rr->AnnounceCount = InitialAnnounceCount;
4552 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
c9b9ae52 4553 InitializeLastAPTime(m, rr);
6528fe3e 4554 }
6528fe3e
A
4555 }
4556
4557 mDNS_Unlock(m);
4558 }
4559
4560// ***************************************************************************
c9b9ae52 4561#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
4562#pragma mark -
4563#pragma mark - Packet Reception Functions
4564#endif
4565
6528fe3e
A
4566#define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4567
4568mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
c9b9ae52 4569 const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
6528fe3e 4570 {
c9b9ae52
A
4571 mDNSu8 *responseptr = response->data;
4572 const mDNSu8 *const limit = response->data + sizeof(response->data);
6528fe3e 4573 const mDNSu8 *ptr = query->data;
c9b9ae52
A
4574 AuthRecord *rr;
4575 mDNSu32 maxttl = 0x70000000;
6528fe3e
A
4576 int i;
4577
4578 // Initialize the response fields so we can answer the questions
c9b9ae52 4579 InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
6528fe3e
A
4580
4581 // ***
4582 // *** 1. Write out the list of questions we are actually going to answer with this packet
4583 // ***
c9b9ae52 4584 if (LegacyQuery)
6528fe3e 4585 {
c9b9ae52
A
4586 maxttl = 10;
4587 for (i=0; i<query->h.numQuestions; i++) // For each question...
6528fe3e 4588 {
c9b9ae52
A
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 }
6528fe3e
A
4601 }
4602 }
c9b9ae52
A
4603
4604 if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
6528fe3e
A
4605 }
4606
c9b9ae52
A
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 }
6528fe3e
A
4617
4618 // ***
c9b9ae52 4619 // *** 3. Write Additionals
6528fe3e
A
4620 // ***
4621 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
c9b9ae52 4622 if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
6528fe3e 4623 {
c9b9ae52
A
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");
6528fe3e 4627 }
c9b9ae52 4628
6528fe3e
A
4629 return(responseptr);
4630 }
4631
c9b9ae52
A
4632// AuthRecord *our is our Resource Record
4633// CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
6528fe3e
A
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
c9b9ae52 4637mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt)
6528fe3e 4638 {
6528fe3e 4639 mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
c9b9ae52
A
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
6528fe3e 4653
c9d2d929 4654 debugf("CompareRData: How did we get here?");
6528fe3e
A
4655 return(-1);
4656 }
4657
c9b9ae52
A
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.
6528fe3e
A
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
c9b9ae52
A
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
6528fe3e
A
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
c9b9ae52 4667mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master)
6528fe3e 4668 {
c9b9ae52
A
4669 const AuthRecord *r1;
4670 for (r1 = m->ResourceRecords; r1; r1=r1->next)
6528fe3e 4671 {
c9b9ae52 4672 if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
6528fe3e 4673 {
c9b9ae52
A
4674 const AuthRecord *r2 = r1;
4675 while (r2->DependentOn) r2 = r2->DependentOn;
4676 if (r2 == master) return(mDNStrue);
6528fe3e
A
4677 }
4678 }
c9b9ae52
A
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);
6528fe3e
A
4689 }
4690
4691// Find the canonical RRSet pointer for this RR received in a packet.
c9b9ae52 4692// If we find any identical AuthRecord in our authoritative list, then follow its RRSet
6528fe3e
A
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
c9b9ae52 4695mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr)
6528fe3e 4696 {
c9b9ae52 4697 const AuthRecord *rr;
6528fe3e
A
4698 for (rr = m->ResourceRecords; rr; rr=rr->next)
4699 {
c9b9ae52 4700 if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
6528fe3e
A
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.
c9b9ae52 4718mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr)
6528fe3e 4719 {
c9b9ae52 4720 const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
6528fe3e
A
4721
4722 // If not supposed to be unique, not a conflict
c9b9ae52 4723 if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
6528fe3e
A
4724
4725 // If a dependent record, not a conflict
c9b9ae52 4726 if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
6528fe3e
A
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.
4738mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
c9b9ae52 4739 DNSQuestion *q, AuthRecord *our)
6528fe3e
A
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 {
7f0064bd 4747 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
6528fe3e 4748 if (!ptr) break;
7f0064bd 4749 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
6528fe3e
A
4750 {
4751 FoundUpdate = mDNStrue;
7f0064bd 4752 if (PacketRRConflict(m, our, &m->rec.r))
6528fe3e 4753 {
7f0064bd
A
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);
c9d2d929 4757 switch (result)
6528fe3e 4758 {
c9d2d929
A
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;
6528fe3e
A
4765 }
4766 }
4767 }
7f0064bd 4768 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
6528fe3e
A
4769 }
4770 if (!FoundUpdate)
283ee3ff 4771 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
7f0064bd
A
4772exit:
4773 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
c9b9ae52
A
4774 }
4775
4776mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr)
4777 {
283ee3ff
A
4778 mDNSu32 slot = HashSlot(pktrr->name);
4779 CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
c9b9ae52 4780 CacheRecord *rr;
283ee3ff 4781 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
c9b9ae52
A
4782 if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
4783 return(rr);
6528fe3e
A
4784 }
4785
4786// ProcessQuery examines a received query to see if we have any answers to give
4787mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
c9d2d929
A
4788 const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast,
4789 DNSMessage *const response)
6528fe3e 4790 {
283ee3ff 4791 mDNSBool FromLocalSubnet = AddressIsLocalSubnet(m, InterfaceID, srcaddr);
7f0064bd
A
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;
6528fe3e
A
4803 int i;
4804
6528fe3e
A
4805 // ***
4806 // *** 1. Parse Question Section and mark potential answers
4807 // ***
4808 for (i=0; i<query->h.numQuestions; i++) // For each question...
4809 {
c9b9ae52 4810 mDNSBool QuestionNeedsMulticastResponse;
6528fe3e 4811 int NumAnswersForThisQuestion = 0;
c9b9ae52
A
4812 DNSQuestion pktq, *q;
4813 ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question...
6528fe3e 4814 if (!ptr) goto exit;
c9b9ae52
A
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;
6528fe3e
A
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
c9b9ae52
A
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");
6528fe3e
A
4835 m->CurrentRecord = m->ResourceRecords;
4836 while (m->CurrentRecord)
4837 {
4838 rr = m->CurrentRecord;
4839 m->CurrentRecord = rr->next;
7f0064bd 4840 if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
6528fe3e 4841 {
c9b9ae52
A
4842 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
4843 ResolveSimultaneousProbe(m, query, end, &pktq, rr);
6528fe3e
A
4844 else if (ResourceRecordIsValidAnswer(rr))
4845 {
4846 NumAnswersForThisQuestion++;
c9b9ae52 4847 // Notes:
283ee3ff
A
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)
7f0064bd 4850 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
283ee3ff
A
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))
c9b9ae52
A
4855 {
4856 // We only mark this question for sending if it is at least one second since the last time we multicast it
283ee3ff 4857 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
c9b9ae52
A
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 }
7f0064bd 4863 else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
6528fe3e
A
4864 }
4865 }
4866 }
c9b9ae52 4867
7f0064bd
A
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
c9b9ae52
A
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 {
283ee3ff
A
4877 const mDNSu32 slot = HashSlot(&pktq.qname);
4878 CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
c9b9ae52 4879 CacheRecord *rr;
c9b9ae52
A
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
283ee3ff 4883 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
c9b9ae52
A
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)
8e92c31c 4906 if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
c9b9ae52
A
4907 if (!q->InterfaceID || q->InterfaceID == InterfaceID)
4908 if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
c9d2d929 4909 if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
c9b9ae52
A
4910 { *dqp = q; dqp = &q->NextInDQList; }
4911 }
6528fe3e
A
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...
c9b9ae52 4919 AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list
6528fe3e
A
4920
4921 // ***
4922 // *** 3. Add additional records
4923 // ***
7f0064bd 4924 AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
6528fe3e
A
4925
4926 // ***
c9b9ae52 4927 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
6528fe3e
A
4928 // ***
4929 for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section...
4930 {
4931 // Get the record...
c9b9ae52
A
4932 AuthRecord *rr;
4933 CacheRecord *ourcacherr;
7f0064bd 4934 ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
6528fe3e
A
4935 if (!ptr) goto exit;
4936
c9b9ae52 4937 // See if this Known-Answer suppresses any of our currently planned answers
6528fe3e 4938 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
7f0064bd 4939 if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
6528fe3e
A
4940 { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
4941
c9b9ae52 4942 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
6528fe3e
A
4943 for (rr=m->ResourceRecords; rr; rr=rr->next)
4944 {
c9b9ae52 4945 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
7f0064bd 4946 if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr))
c9b9ae52
A
4947 {
4948 if (srcaddr->type == mDNSAddrType_IPv4)
6528fe3e 4949 {
7f0064bd 4950 if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr;
6528fe3e 4951 }
c9b9ae52
A
4952 else if (srcaddr->type == mDNSAddrType_IPv6)
4953 {
4954 if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
4955 }
8e92c31c
A
4956 if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
4957 {
7f0064bd
A
4958 rr->ImmedAnswer = mDNSNULL;
4959 rr->ImmedUnicast = mDNSfalse;
8e92c31c 4960#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
7f0064bd 4961 LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr));
8e92c31c
A
4962#endif
4963 }
c9b9ae52
A
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).
7f0064bd 4969 ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
c9b9ae52
A
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;
7f0064bd 4983 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
c9b9ae52
A
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;
7f0064bd 4995 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
c9b9ae52
A
4996 { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
4997 else dqp = &q->NextInDQList;
4998 }
6528fe3e 4999 }
7f0064bd 5000 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
6528fe3e
A
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 {
c9b9ae52 5015 if (rr->NR_AnswerTo)
6528fe3e 5016 {
7f0064bd
A
5017 mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response
5018 mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response)
c9b9ae52
A
5019
5020 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
283ee3ff
A
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 }
c9b9ae52
A
5029
5030 // If the client insists on a multicast response, then we'd better send one
7f0064bd
A
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;
c9b9ae52 5034
7f0064bd 5035 if (SendMulticastResponse || SendUnicastResponse)
6528fe3e 5036 {
8e92c31c
A
5037#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5038 rr->ImmedAnswerMarkTime = m->timenow;
5039#endif
5040 m->NextScheduledResponse = m->timenow;
c9b9ae52
A
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)
c9b9ae52 5043 rr->ImmedAnswer = mDNSInterfaceMark;
6528fe3e
A
5044 else
5045 {
c9b9ae52 5046 rr->ImmedAnswer = InterfaceID; // Record interface to send it on
7f0064bd 5047 if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue;
c9b9ae52
A
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 }
6528fe3e
A
5058 }
5059 }
7f0064bd
A
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
6528fe3e 5066 }
c9b9ae52
A
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 }
6528fe3e
A
5076 }
5077
5078 // ***
5079 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5080 // ***
8d1ca615
A
5081 if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50))
5082 {
8e92c31c
A
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
8d1ca615
A
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,
7cb34e5c 5098 // because if that happens we'll fail the Bonjour Conformance Test.
8d1ca615
A
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;
6528fe3e 5102 if (m->SuppressSending == 0) m->SuppressSending = 1;
8e92c31c
A
5103#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5104 if (oldss && delayresponse)
5105 LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow);
5106#endif
6528fe3e
A
5107 }
5108
5109 // ***
8e92c31c 5110 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
6528fe3e 5111 // ***
7f0064bd 5112 if (SendLegacyResponse)
c9b9ae52 5113 responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
6528fe3e
A
5114
5115exit:
7f0064bd
A
5116 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5117
6528fe3e 5118 // ***
c9b9ae52 5119 // *** 9. Finally, clear our link chains ready for use next time
6528fe3e
A
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
c9b9ae52
A
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",
7f0064bd 5146 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
c9b9ae52
A
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",
7f0064bd 5157 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
c9b9ae52
A
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",
7f0064bd 5178 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
c9b9ae52
A
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
6528fe3e
A
5203 return(responseptr);
5204 }
5205
5206mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
c9b9ae52
A
5207 const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5208 const mDNSInterfaceID InterfaceID)
6528fe3e 5209 {
7f0064bd
A
5210 mDNSu8 *responseend = mDNSNULL;
5211 mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr);
8e92c31c 5212
7f0064bd 5213 if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))
6528fe3e 5214 {
c9d2d929 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)",
8e92c31c 5216 srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
b7388343
A
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");
8e92c31c
A
5221 return;
5222 }
b7388343 5223
c9d2d929 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",
8e92c31c
A
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,
7f0064bd 5232 (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg);
8e92c31c
A
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",
7f0064bd
A
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",
8e92c31c 5240 srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type);
7f0064bd 5241 mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL);
6528fe3e
A
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.
5248mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
8e92c31c
A
5249 const DNSMessage *const response, const mDNSu8 *end,
5250 const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
7f0064bd 5251 const mDNSInterfaceID InterfaceID)
6528fe3e
A
5252 {
5253 int i;
c9d2d929
A
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
c9b9ae52 5256 CacheRecord **cfp = &CacheFlushRecords;
7f0064bd 5257
6528fe3e 5258 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
c9b9ae52 5259 // to guard against spoof responses, then the only credible protection against that is cryptographic
6528fe3e
A
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
c9b9ae52 5263 (void)srcaddr; // Currently used only for display in debugging message
7f0064bd
A
5264 (void)srcport;
5265 (void)dstport;
c9b9ae52 5266
c9d2d929 5267 verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
7f0064bd 5268 srcaddr, dstaddr, InterfaceID,
c9b9ae52
A
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,",
6528fe3e
A
5272 response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
5273
7f0064bd
A
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))
283ee3ff 5276 {
7f0064bd
A
5277 if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))
5278 return;
283ee3ff
A
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 }
6528fe3e
A
5284
5285 for (i = 0; i < totalrecords && ptr && ptr < end; i++)
5286 {
c9b9ae52 5287 const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd);
7f0064bd
A
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
6528fe3e
A
5290
5291 // 1. Check that this packet resource record does not conflict with any of ours
c9b9ae52 5292 if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
6528fe3e
A
5293 m->CurrentRecord = m->ResourceRecords;
5294 while (m->CurrentRecord)
5295 {
c9b9ae52 5296 AuthRecord *rr = m->CurrentRecord;
6528fe3e 5297 m->CurrentRecord = rr->next;
7f0064bd 5298 if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match...
c9b9ae52 5299 {
7f0064bd
A
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))
6528fe3e
A
5302 {
5303 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
7f0064bd 5304 if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
c9b9ae52
A
5305 {
5306 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
7f0064bd 5307 if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; }
c9b9ae52 5308 }
6528fe3e 5309 else
c9b9ae52
A
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 }
6528fe3e 5314 }
7f0064bd
A
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))
6528fe3e 5317 {
7cb34e5c
A
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));
c9b9ae52 5320
7f0064bd
A
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;
c9b9ae52 5324
7f0064bd
A
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)
6528fe3e 5330 {
283ee3ff 5331 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
7f0064bd
A
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 {
283ee3ff 5341 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
7f0064bd
A
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",
283ee3ff 5351 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
7f0064bd 5352 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
6528fe3e 5353 }
7f0064bd
A
5354 else
5355 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
283ee3ff 5356 rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
6528fe3e
A
5357 }
5358 }
7f0064bd
A
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; }
6528fe3e
A
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 {
283ee3ff
A
5372 const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
5373 CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
c9b9ae52 5374 CacheRecord *rr;
6528fe3e 5375 // 2a. Check if this packet resource record is already in our cache
283ee3ff 5376 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
6528fe3e
A
5377 {
5378 // If we found this exact resource record, refresh its TTL
7f0064bd 5379 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
6528fe3e 5380 {
7f0064bd 5381 if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
c9b9ae52 5382 verbosedebugf("Found record size %5d interface %p already in cache: %s",
7f0064bd 5383 m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
c9b9ae52
A
5384 rr->TimeRcvd = m->timenow;
5385
7f0064bd 5386 if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
c9b9ae52
A
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)
283ee3ff 5390 { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
c9b9ae52
A
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++;
7f0064bd 5397 rr->resrec.RecordType = m->rec.r.resrec.RecordType;
c9b9ae52
A
5398 }
5399 }
5400
7cb34e5c
A
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)
c9b9ae52 5412 {
7f0064bd 5413 rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl;
c9b9ae52
A
5414 rr->UnansweredQueries = 0;
5415 rr->MPUnansweredQ = 0;
5416 rr->MPUnansweredKA = 0;
5417 rr->MPExpectingKA = mDNSfalse;
7cb34e5c
A
5418 SetNextCacheCheckTime(m, rr);
5419 break;
c9b9ae52
A
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;
7cb34e5c
A
5430 SetNextCacheCheckTime(m, rr);
5431 break;
c9b9ae52 5432 }
6528fe3e
A
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)
7f0064bd 5438 if (!rr && m->rec.r.resrec.rroriginalttl > 0)
6528fe3e 5439 {
283ee3ff
A
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);
6528fe3e
A
5444 else
5445 {
c9b9ae52 5446 RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer
283ee3ff
A
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
c9b9ae52 5450 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
283ee3ff 5451 { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
c9b9ae52 5452 // If this is an oversized record with external storage allocated, copy rdata to external storage
283ee3ff
A
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);
7f0064bd
A
5455 if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5456 mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength);
716635cc 5457 rr->next = mDNSNULL; // Clear 'next' pointer
283ee3ff
A
5458 *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list
5459 cg->rrcache_tail = &(rr->next); // Advance tail pointer
7f0064bd
A
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
283ee3ff 5463 rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot);
c9d2d929
A
5464 CacheRecordAdd(m, rr);
5465 // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us
5466 SetNextCacheCheckTime(m, rr);
6528fe3e
A
5467 }
5468 }
5469 }
7f0064bd 5470 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
6528fe3e
A
5471 }
5472
7f0064bd
A
5473exit:
5474 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5475
c9b9ae52
A
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
283ee3ff 5478 while (CacheFlushRecords != (CacheRecord*)1)
6528fe3e 5479 {
c9b9ae52 5480 CacheRecord *r1 = CacheFlushRecords, *r2;
283ee3ff
A
5481 const mDNSu32 slot = HashSlot(r1->resrec.name);
5482 CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
c9b9ae52
A
5483 CacheFlushRecords = CacheFlushRecords->NextInCFList;
5484 r1->NextInCFList = mDNSNULL;
283ee3ff
A
5485 for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
5486 if (SameResourceRecordSignature(&r1->resrec, &r2->resrec))
6528fe3e 5487 {
c9d2d929
A
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)
283ee3ff 5491 r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
c9d2d929 5492 else
283ee3ff 5493 {
c9d2d929 5494 verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name->c, DNSTypeName(r2->resrec.rrtype));
283ee3ff
A
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;
c9d2d929 5509 SetNextCacheCheckTime(m, r2);
283ee3ff 5510 }
6528fe3e 5511 }
7f0064bd
A
5512 if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to
5513 {
283ee3ff 5514 r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
7f0064bd
A
5515 if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
5516 }
6528fe3e
A
5517 }
5518 }
5519
7f0064bd 5520mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
c9b9ae52 5521 const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
7f0064bd 5522 const mDNSInterfaceID InterfaceID)
6528fe3e 5523 {
7f0064bd
A
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;
283ee3ff 5529 const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
7f0064bd
A
5530
5531#ifndef UNICAST_DISABLED
c9d2d929
A
5532 mDNSIPPort NATPort = mDNSOpaque16fromIntVal(NATMAP_PORT);
5533
5534 if (srcport.NotAnInteger == NATPort.NotAnInteger)
7f0064bd
A
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);
6528fe3e 5544 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
7f0064bd 5545 ptr = (mDNSu8 *)&msg->h.numQuestions;
6528fe3e
A
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]);
8e92c31c 5550
c9b9ae52
A
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; }
8e92c31c 5556
6528fe3e 5557 mDNS_Lock(m);
7f0064bd
A
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
c9b9ae52 5564 if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
7f0064bd
A
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);
6528fe3e
A
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// ***************************************************************************
c9b9ae52 5579#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
5580#pragma mark -
5581#pragma mark -
5582#pragma mark - Searcher Functions
5583#endif
5584
8e92c31c
A
5585#define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5586
6528fe3e
A
5587mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
5588 {
5589 DNSQuestion *q;
4e28aad3
A
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.
c9b9ae52
A
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,
8e92c31c 5596 SameQTarget(q, question) && // and same unicast/multicast target settings
c9b9ae52
A
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
4e28aad3 5601 return(q);
6528fe3e
A
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
c9b9ae52 5607mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question)
6528fe3e
A
5608 {
5609 DNSQuestion *q;
c9b9ae52 5610 for (q = m->Questions; q; q=q->next) // Scan our list of questions
6528fe3e
A
5611 if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate
5612 {
7f0064bd 5613 q->ThisQInterval = question->ThisQInterval;
283ee3ff 5614 q->RequestUnicast = question->RequestUnicast;
7f0064bd
A
5615 q->LastQTime = question->LastQTime;
5616 q->RecentAnswerPkts = 0;
5617 q->DuplicateOf = FindDuplicateQuestion(m, q);
5618 q->LastQTxTime = question->LastQTxTime;
c9b9ae52 5619 SetNextQueryTime(m,q);
6528fe3e
A
5620 }
5621 }
5622
8e92c31c
A
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
c9b9ae52 5626mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
6528fe3e 5627 {
8e92c31c
A
5628 if (question->Target.type && !ValidQuestionTarget(question))
5629 {
7f0064bd 5630 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
8e92c31c
A
5631 question->Target.type, mDNSVal16(question->TargetPort));
5632 question->Target.type = mDNSAddrType_None;
5633 }
5634
7f0064bd
A
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'
8e92c31c 5643 // then we do a multicast query on that interface, even for unicast domains.
7f0064bd 5644 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
8e92c31c
A
5645 question->uDNS_info.id = zeroID;
5646 else return uDNS_StartQuery(m, question);
7f0064bd
A
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
6528fe3e
A
5653 if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated
5654 return(mStatus_NoCache);
5655 else
5656 {
c9b9ae52
A
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;
8e92c31c 5660 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
6528fe3e
A
5661 while (*q && *q != question) q=&(*q)->next;
5662
5663 if (*q)
5664 {
c9b9ae52
A
5665 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5666 question->qname.c, DNSTypeName(question->qtype));
6528fe3e
A
5667 return(mStatus_AlreadyRegistered);
5668 }
5669
c9d2d929 5670 // If this question is referencing a specific interface, make sure it exists
8e92c31c 5671 if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly)
4e28aad3 5672 {
c9b9ae52
A
5673 NetworkInterfaceInfo *intf;
5674 for (intf = m->HostInterfaces; intf; intf = intf->next)
5675 if (intf->InterfaceID == question->InterfaceID) break;
5676 if (!intf)
c9d2d929
A
5677 {
5678 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID);
5679 return(mStatus_BadInterfaceErr);
5680 }
4e28aad3
A
5681 }
5682
c9b9ae52
A
5683 if (!ValidateDomainName(&question->qname))
5684 {
28f7d060 5685 LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
c9b9ae52
A
5686 return(mStatus_Invalid);
5687 }
5688
7f0064bd
A
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.
c9b9ae52
A
5693 if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
5694
c9d2d929
A
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;
c9b9ae52
A
5708 for (i=0; i<DupSuppressInfoSize; i++)
5709 question->DupSuppress[i].InterfaceID = mDNSNULL;
5710 // question->InterfaceID must be already set by caller
c9d2d929
A
5711 question->SendQNow = mDNSNULL;
5712 question->SendOnAll = mDNSfalse;
5713 question->LastQTxTime = m->timenow;
c9b9ae52
A
5714
5715 if (!question->DuplicateOf)
28f7d060 5716 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
c9d2d929 5717 question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question);
c9b9ae52 5718 else
28f7d060 5719 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
c9d2d929 5720 question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf);
c9b9ae52 5721
6528fe3e 5722 *q = question;
8e92c31c 5723 if (question->InterfaceID == mDNSInterface_LocalOnly)
c9b9ae52
A
5724 {
5725 if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
5726 }
5727 else
5728 {
5729 if (!m->NewQuestions) m->NewQuestions = question;
5730 SetNextQueryTime(m,question);
5731 }
6528fe3e
A
5732
5733 return(mStatus_NoError);
5734 }
5735 }
5736
c9b9ae52 5737mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
6528fe3e 5738 {
283ee3ff
A
5739 const mDNSu32 slot = HashSlot(&question->qname);
5740 CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
c9b9ae52
A
5741 CacheRecord *rr;
5742 DNSQuestion **q = &m->Questions;
8e92c31c 5743
7f0064bd
A
5744 if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question);
5745
8e92c31c 5746 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
6528fe3e
A
5747 while (*q && *q != question) q=&(*q)->next;
5748 if (*q) *q = (*q)->next;
c9b9ae52
A
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 }
6528fe3e 5756
c9b9ae52 5757 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
6528fe3e 5758 UpdateQuestionDuplicates(m, question);
c9b9ae52 5759 // But don't trash ThisQInterval until afterwards.
4e28aad3 5760 question->ThisQInterval = -1;
c9b9ae52
A
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.
283ee3ff 5764 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
c9b9ae52
A
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;
c9d2d929 5772 verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q);
c9b9ae52
A
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,
6528fe3e
A
5779 // bump its pointer forward one question.
5780 if (m->CurrentQuestion == question)
5781 {
c9b9ae52
A
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;
6528fe3e
A
5785 }
5786
c9b9ae52 5787 if (m->NewQuestions == question)
6528fe3e 5788 {
c9b9ae52
A
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;
6528fe3e 5792 }
c9b9ae52
A
5793
5794 if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
5795
4e28aad3
A
5796 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5797 question->next = mDNSNULL;
c9b9ae52 5798 return(mStatus_NoError);
6528fe3e
A
5799 }
5800
5801mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
5802 {
c9b9ae52
A
5803 mStatus status;
5804 mDNS_Lock(m);
5805 status = mDNS_StartQuery_internal(m, question);
5806 mDNS_Unlock(m);
5807 return(status);
5808 }
5809
5810mDNSexport 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
5819mDNSexport 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);
6528fe3e
A
5824 mDNS_Unlock(m);
5825 return(status);
5826 }
5827
c9b9ae52 5828mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
6528fe3e 5829 {
c9b9ae52
A
5830 mStatus status = mStatus_BadReferenceErr;
5831 CacheRecord *cr;
6528fe3e 5832 mDNS_Lock(m);
c9b9ae52
A
5833 cr = FindIdenticalRecordInCache(m, rr);
5834 if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
6528fe3e 5835 mDNS_Unlock(m);
c9b9ae52 5836 return(status);
6528fe3e
A
5837 }
5838
5839mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
5840 const domainname *const srv, const domainname *const domain,
7f0064bd 5841 const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context)
c9b9ae52 5842 {
8e92c31c
A
5843 question->InterfaceID = InterfaceID;
5844 question->Target = zeroAddr;
5845 question->qtype = kDNSType_PTR;
5846 question->qclass = kDNSClass_IN;
7f0064bd
A
5847 question->LongLived = mDNSfalse;
5848 question->ExpectUnique = mDNSfalse;
5849 question->ForceMCast = ForceMCast;
8e92c31c
A
5850 question->QuestionCallback = Callback;
5851 question->QuestionContext = Context;
c9b9ae52 5852 if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
8e92c31c 5853
7f0064bd
A
5854#ifndef UNICAST_DISABLED
5855 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
8e92c31c
A
5856 {
5857 question->LongLived = mDNSfalse;
5858 question->uDNS_info.id = zeroID;
5859 return(mDNS_StartQuery(m, question));
5860 }
5861 else
5862 {
7f0064bd
A
5863 mStatus status;
5864 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5865 mDNS_Lock(m);
8e92c31c 5866 question->LongLived = mDNStrue;
7f0064bd
A
5867 status = uDNS_StartQuery(m, question);
5868 mDNS_Unlock(m);
5869 return(status);
8e92c31c 5870 }
7f0064bd
A
5871#else
5872 return(mDNS_StartQuery(m, question));
5873#endif // UNICAST_DISABLED
5874 }
5875
5876mDNSlocal 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);
6528fe3e
A
5882 }
5883
c9b9ae52 5884mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6528fe3e 5885 {
c9b9ae52
A
5886 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5887 mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger);
5888 if (!AddRecord) return;
6528fe3e
A
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;
c9b9ae52 5897 query->qAv4.InterfaceID = answer->InterfaceID;
283ee3ff 5898 AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
c9b9ae52 5899 query->qAv6.InterfaceID = answer->InterfaceID;
283ee3ff 5900 AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
7f0064bd
A
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);
6528fe3e
A
5904 }
5905 // If this is not our first answer, only re-issue the address query if the target host name has changed
c9b9ae52
A
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 {
7f0064bd
A
5909 mDNS_StopQuery(m, &query->qAv4);
5910 if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6);
c9b9ae52
A
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;
283ee3ff 5924 AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
c9b9ae52 5925 query->qAv6.InterfaceID = answer->InterfaceID;
283ee3ff 5926 AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
c9b9ae52
A
5927 }
5928 debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c);
7f0064bd
A
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);
c9b9ae52
A
5932 }
5933 else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
6528fe3e 5934 {
c9b9ae52
A
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,
8e92c31c 5938 mDNSVal16(answer->rdata->u.srv.port));
c9b9ae52 5939 query->ServiceInfoQueryCallback(m, query);
6528fe3e 5940 }
c9b9ae52
A
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.
6528fe3e
A
5943 }
5944
c9b9ae52 5945mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6528fe3e 5946 {
c9b9ae52
A
5947 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5948 if (!AddRecord) return;
6528fe3e 5949 if (answer->rrtype != kDNSType_TXT) return;
c9b9ae52 5950 if (answer->rdlength > sizeof(query->info->TXTinfo)) return;
6528fe3e 5951
c9b9ae52
A
5952 query->GotTXT = mDNStrue;
5953 query->info->TXTlen = answer->rdlength;
8e92c31c 5954 query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero
c9b9ae52 5955 mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength);
6528fe3e 5956
c9b9ae52 5957 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
6528fe3e 5958
c9b9ae52
A
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 }
6528fe3e
A
5968 }
5969
c9b9ae52 5970mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6528fe3e 5971 {
c9b9ae52 5972 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
7f0064bd 5973 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
c9b9ae52
A
5974 if (!AddRecord) return;
5975
5976 if (answer->rrtype == kDNSType_A)
5977 {
5978 query->info->ip.type = mDNSAddrType_IPv4;
7f0064bd 5979 query->info->ip.ip.v4 = answer->rdata->u.ipv4;
c9b9ae52
A
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 {
283ee3ff 5988 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype));
c9b9ae52
A
5989 return;
5990 }
5991
6528fe3e 5992 query->GotADD = mDNStrue;
c9b9ae52 5993 query->info->InterfaceID = answer->InterfaceID;
6528fe3e 5994
7f0064bd 5995 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
6528fe3e 5996
c9b9ae52
A
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)
6528fe3e 6000 {
c9b9ae52 6001 if (++query->Answers >= 100)
c9d2d929
A
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 }
c9b9ae52 6008 query->ServiceInfoQueryCallback(m, query);
6528fe3e 6009 }
6528fe3e
A
6010 }
6011
c9b9ae52
A
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
6528fe3e 6014// Each time the Callback is invoked, the remainder of the fields will have been filled in
c9b9ae52 6015// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
6528fe3e 6016mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
c9b9ae52 6017 ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context)
6528fe3e
A
6018 {
6019 mStatus status;
c9b9ae52
A
6020 mDNS_Lock(m);
6021
8e92c31c 6022 query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
c9b9ae52 6023 query->qSRV.InterfaceID = info->InterfaceID;
8e92c31c 6024 query->qSRV.Target = zeroAddr;
283ee3ff 6025 AssignDomainName(&query->qSRV.qname, &info->name);
c9b9ae52
A
6026 query->qSRV.qtype = kDNSType_SRV;
6027 query->qSRV.qclass = kDNSClass_IN;
7f0064bd
A
6028 query->qSRV.LongLived = mDNSfalse;
6029 query->qSRV.ExpectUnique = mDNStrue;
6030 query->qSRV.ForceMCast = mDNSfalse;
c9b9ae52
A
6031 query->qSRV.QuestionCallback = FoundServiceInfoSRV;
6032 query->qSRV.QuestionContext = query;
6033
8e92c31c 6034 query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
c9b9ae52 6035 query->qTXT.InterfaceID = info->InterfaceID;
8e92c31c 6036 query->qTXT.Target = zeroAddr;
283ee3ff 6037 AssignDomainName(&query->qTXT.qname, &info->name);
c9b9ae52
A
6038 query->qTXT.qtype = kDNSType_TXT;
6039 query->qTXT.qclass = kDNSClass_IN;
7f0064bd
A
6040 query->qTXT.LongLived = mDNSfalse;
6041 query->qTXT.ExpectUnique = mDNStrue;
6042 query->qTXT.ForceMCast = mDNSfalse;
c9b9ae52
A
6043 query->qTXT.QuestionCallback = FoundServiceInfoTXT;
6044 query->qTXT.QuestionContext = query;
6045
8e92c31c 6046 query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
c9b9ae52 6047 query->qAv4.InterfaceID = info->InterfaceID;
8e92c31c 6048 query->qAv4.Target = zeroAddr;
c9b9ae52
A
6049 query->qAv4.qname.c[0] = 0;
6050 query->qAv4.qtype = kDNSType_A;
6051 query->qAv4.qclass = kDNSClass_IN;
7f0064bd
A
6052 query->qAv4.LongLived = mDNSfalse;
6053 query->qAv4.ExpectUnique = mDNStrue;
6054 query->qAv4.ForceMCast = mDNSfalse;
c9b9ae52
A
6055 query->qAv4.QuestionCallback = FoundServiceInfo;
6056 query->qAv4.QuestionContext = query;
6057
8e92c31c 6058 query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
c9b9ae52 6059 query->qAv6.InterfaceID = info->InterfaceID;
8e92c31c 6060 query->qAv6.Target = zeroAddr;
c9b9ae52
A
6061 query->qAv6.qname.c[0] = 0;
6062 query->qAv6.qtype = kDNSType_AAAA;
6063 query->qAv6.qclass = kDNSClass_IN;
7f0064bd
A
6064 query->qAv6.LongLived = mDNSfalse;
6065 query->qAv6.ExpectUnique = mDNStrue;
6066 query->qAv6.ForceMCast = mDNSfalse;
c9b9ae52
A
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;
6528fe3e
A
6078
6079// info->name = Must already be set up by client
6080// info->interface = Must already be set up by client
c9b9ae52 6081 info->ip = zeroAddr;
6528fe3e
A
6082 info->port = zeroIPPort;
6083 info->TXTlen = 0;
6084
7f0064bd 6085 // We use mDNS_StartQuery_internal here because we're already holding the lock
c9b9ae52
A
6086 status = mDNS_StartQuery_internal(m, &query->qSRV);
6087 if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT);
6528fe3e
A
6088 if (status != mStatus_NoError) mDNS_StopResolveService(m, query);
6089
6090 mDNS_Unlock(m);
6091 return(status);
6092 }
6093
283ee3ff 6094mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q)
6528fe3e
A
6095 {
6096 mDNS_Lock(m);
7f0064bd 6097 // We use mDNS_StopQuery_internal here because we're already holding the lock
283ee3ff
A
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);
6528fe3e
A
6102 mDNS_Unlock(m);
6103 }
6104
8e92c31c 6105mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
c9b9ae52 6106 const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
6528fe3e 6107 {
c9b9ae52 6108 question->InterfaceID = InterfaceID;
8e92c31c 6109 question->Target = zeroAddr;
c9b9ae52
A
6110 question->qtype = kDNSType_PTR;
6111 question->qclass = kDNSClass_IN;
7f0064bd
A
6112 question->LongLived = mDNSfalse;
6113 question->ExpectUnique = mDNSfalse;
6114 question->ForceMCast = mDNSfalse;
c9b9ae52
A
6115 question->QuestionCallback = Callback;
6116 question->QuestionContext = Context;
283ee3ff 6117 if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
8e92c31c 6118 if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
7f0064bd 6119 if (!dom) dom = &localdomain;
8e92c31c
A
6120 if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr);
6121 return(mDNS_StartQuery(m, question));
6528fe3e
A
6122 }
6123
6124// ***************************************************************************
c9b9ae52 6125#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
6126#pragma mark -
6127#pragma mark - Responder Functions
6128#endif
6129
c9b9ae52 6130mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
6528fe3e 6131 {
c9b9ae52
A
6132 mStatus status;
6133 mDNS_Lock(m);
6134 status = mDNS_Register_internal(m, rr);
6528fe3e
A
6135 mDNS_Unlock(m);
6136 return(status);
6137 }
6138
c9b9ae52 6139mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
7f0064bd 6140 const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback)
6528fe3e 6141 {
7f0064bd 6142#ifndef UNICAST_DISABLED
283ee3ff 6143 mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name));
7f0064bd
A
6144#else
6145 mDNSBool unicast = mDNSfalse;
6146#endif
8e92c31c 6147
c9b9ae52 6148 if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata))
c9d2d929 6149 { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); }
7f0064bd 6150
73792575
A
6151 mDNS_Lock(m);
6152
c9b9ae52
A
6153 // If TTL is unspecified, leave TTL unchanged
6154 if (newttl == 0) newttl = rr->resrec.rroriginalttl;
6528fe3e
A
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
7f0064bd 6158 if (!unicast && rr->NewRData)
6528fe3e
A
6159 {
6160 RData *n = rr->NewRData;
c9b9ae52
A
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 }
8e92c31c 6165
c9b9ae52
A
6166 rr->NewRData = newrdata;
6167 rr->newrdlength = newrdlength;
6168 rr->UpdateCallback = Callback;
7f0064bd
A
6169
6170 if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); }
6171
c9d2d929 6172 if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
8e92c31c
A
6173 CompleteRDataUpdate(m, rr);
6174 else
c9b9ae52 6175 {
8e92c31c
A
6176 domainlabel name;
6177 domainname type, domain;
283ee3ff 6178 DeconstructServiceName(rr->resrec.name, &name, &type, &domain);
7f0064bd 6179 rr->AnnounceCount = InitialAnnounceCount;
8e92c31c
A
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--;
7f0064bd 6190 if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval);
8e92c31c
A
6191 if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1);
6192 if (rr->UpdateCredits <= 5)
6193 {
283ee3ff
A
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);
8e92c31c 6196 rr->ThisAPInterval *= 4;
7f0064bd 6197 rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval;
c9d2d929 6198 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name->c, delay, delay > 1 ? "s" : "");
8e92c31c
A
6199 }
6200 rr->resrec.rroriginalttl = newttl;
c9b9ae52 6201 }
8e92c31c 6202
6528fe3e
A
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.
c9b9ae52 6210mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr)
6528fe3e 6211 {
c9b9ae52
A
6212 mStatus status;
6213 mDNS_Lock(m);
6214 status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
6528fe3e 6215 mDNS_Unlock(m);
c9b9ae52
A
6216 return(status);
6217 }
6218
8e92c31c 6219mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
c9b9ae52
A
6220
6221mDNSlocal 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
8e92c31c 6229mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
c9b9ae52
A
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
8e92c31c
A
6234
6235 // Send dynamic update for non-linklocal IPv4 Addresses
7f0064bd
A
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
c9b9ae52
A
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
283ee3ff 6247 AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname);
c9b9ae52
A
6248 if (set->ip.type == mDNSAddrType_IPv4)
6249 {
6250 set->RR_A.resrec.rrtype = kDNSType_A;
7f0064bd 6251 set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4;
c9b9ae52
A
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
283ee3ff 6272 MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer);
c9b9ae52 6273 set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
7f0064bd 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
c9b9ae52
A
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;
283ee3ff 6284 AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname);
c9b9ae52
A
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
8e92c31c 6298mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
c9b9ae52
A
6299 {
6300 NetworkInterfaceInfo *intf;
8e92c31c
A
6301
6302 // If we still have address records referring to this one, update them
c9b9ae52
A
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.
8e92c31c 6310 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
c9b9ae52
A
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);
6528fe3e
A
6317 }
6318
7f0064bd 6319mDNSexport void mDNS_SetFQDN(mDNS *const m)
6528fe3e 6320 {
7f0064bd
A
6321 domainname newmname;
6322 NetworkInterfaceInfo *intf;
6323 AuthRecord *rr;
6324 newmname.c[0] = 0;
c9b9ae52 6325
7f0064bd
A
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; }
c9b9ae52 6329
7f0064bd 6330 mDNS_Lock(m);
283ee3ff 6331 AssignDomainName(&m->MulticastHostname, &newmname);
c9b9ae52 6332
7f0064bd
A
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);
6528fe3e 6336
7f0064bd
A
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);
6528fe3e 6340
7f0064bd
A
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);
8e92c31c 6347 }
c9b9ae52 6348
8e92c31c
A
6349mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6350 {
6351 (void)rr; // Unused parameter
6352
c9b9ae52 6353 #if MDNS_DEBUGMSGS
6528fe3e 6354 {
c9b9ae52
A
6355 char *msg = "Unknown result";
6356 if (result == mStatus_NoError) msg = "Name registered";
6357 else if (result == mStatus_NameConflict) msg = "Name conflict";
283ee3ff 6358 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6528fe3e 6359 }
c9b9ae52 6360 #endif
6528fe3e 6361
c9b9ae52
A
6362 if (result == mStatus_NoError)
6363 {
6364 // Notify the client that the host name is successfully registered
6365 if (m->MainCallback)
8e92c31c
A
6366 {
6367 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
c9b9ae52 6368 m->MainCallback(m, result);
8e92c31c
A
6369 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6370 }
c9b9ae52
A
6371 }
6372 else if (result == mStatus_NameConflict)
6528fe3e 6373 {
6528fe3e 6374 domainlabel oldlabel = m->hostlabel;
7f0064bd 6375
c9b9ae52
A
6376 // 1. First give the client callback a chance to pick a new name
6377 if (m->MainCallback)
8e92c31c
A
6378 {
6379 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
c9b9ae52 6380 m->MainCallback(m, mStatus_NameConflict);
8e92c31c
A
6381 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6382 }
c9b9ae52
A
6383
6384 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6528fe3e
A
6385 if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
6386 IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
8e92c31c 6387
c9b9ae52
A
6388 // 3. Generate the FQDNs from the hostlabel,
6389 // and make sure all SRV records, etc., are updated to reference our new hostname
7f0064bd
A
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)");
6528fe3e 6398 }
7f0064bd 6399 else
283ee3ff 6400 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c);
6528fe3e
A
6401 }
6402
c9b9ae52 6403mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
6528fe3e 6404 {
c9b9ae52
A
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 {
8e92c31c
A
6411 if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue;
6412 if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue;
c9b9ae52 6413 }
6528fe3e
A
6414 }
6415
c9d2d929 6416mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSs32 delay)
6528fe3e 6417 {
c9b9ae52 6418 mDNSBool FirstOfType = mDNStrue;
6528fe3e 6419 NetworkInterfaceInfo **p = &m->HostInterfaces;
7f0064bd
A
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
c9b9ae52 6427 mDNS_Lock(m);
6528fe3e 6428
c9d2d929 6429 // Assume this interface will be active
c9b9ae52 6430 set->InterfaceActive = mDNStrue;
8e92c31c
A
6431 set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx);
6432 set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx);
6528fe3e 6433
c9b9ae52 6434 while (*p)
6528fe3e 6435 {
c9b9ae52
A
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 }
6528fe3e 6442
c9d2d929 6443 // This InterfaceID is already in the list, so mark this interface inactive for now
c9b9ae52 6444 if ((*p)->InterfaceID == set->InterfaceID)
4e28aad3 6445 {
c9b9ae52
A
6446 set->InterfaceActive = mDNSfalse;
6447 if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse;
8e92c31c
A
6448 if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue;
6449 if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue;
4e28aad3 6450 }
c9b9ae52
A
6451
6452 p=&(*p)->next;
6453 }
4e28aad3 6454
6528fe3e
A
6455 set->next = mDNSNULL;
6456 *p = set;
c9b9ae52 6457
283ee3ff
A
6458 if (set->Advertise)
6459 AdvertiseInterface(m, set);
6460
c9d2d929 6461 debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip,
c9b9ae52
A
6462 set->InterfaceActive ?
6463 "not represented in list; marking active and retriggering queries" :
6464 "already represented in list; marking inactive for now");
8e92c31c 6465
c9d2d929 6466 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
c9b9ae52
A
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.
283ee3ff 6470 if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
6528fe3e 6471 {
716635cc
A
6472 DNSQuestion *q;
6473 AuthRecord *rr;
c9d2d929 6474 mDNSs32 initial = InitialQuestionInterval;
283ee3ff 6475
c9b9ae52 6476 // Use a small amount of randomness:
c9d2d929
A
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.
c9b9ae52 6479 if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
283ee3ff 6480
c9d2d929 6481 if (delay)
283ee3ff 6482 {
c9d2d929
A
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
283ee3ff
A
6486 if (!m->SuppressProbes ||
6487 m->SuppressProbes - (m->timenow + delay) < 0)
6488 m->SuppressProbes = (m->timenow + delay);
6489 }
c9b9ae52
A
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
c9d2d929
A
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;
7f0064bd 6496 q->RecentAnswerPkts = 0;
283ee3ff 6497 SetNextQueryTime(m,q);
c9b9ae52
A
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;
7f0064bd 6506 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
c9d2d929 6507 rr->AnnounceCount = delay ? (mDNSu8)1 : InitialAnnounceCount;
7f0064bd 6508 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
c9b9ae52
A
6509 InitializeLastAPTime(m, rr);
6510 }
6528fe3e
A
6511 }
6512
c9b9ae52
A
6513 mDNS_Unlock(m);
6514 return(mStatus_NoError);
6528fe3e
A
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.
c9d2d929 6520mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
6528fe3e
A
6521 {
6522 NetworkInterfaceInfo **p = &m->HostInterfaces;
c9b9ae52
A
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);
6528fe3e
A
6531
6532 // Find this record in our list
6533 while (*p && *p != set) p=&(*p)->next;
c9b9ae52 6534 if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; }
6528fe3e
A
6535
6536 // Unlink this record from our list
6537 *p = (*p)->next;
6538 set->next = mDNSNULL;
6539
c9b9ae52
A
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 {
c9d2d929
A
6556 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6557 set->InterfaceID);
c9b9ae52
A
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 {
283ee3ff
A
6570 mDNSu32 slot;
6571 CacheGroup *cg;
c9b9ae52
A
6572 CacheRecord *rr;
6573 DNSQuestion *q;
c9d2d929
A
6574 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6575 set->InterfaceID);
619ee211 6576
c9d2d929 6577 // 1. Deactivate any questions specific to this interface
c9b9ae52 6578 for (q = m->Questions; q; q=q->next)
c9d2d929
A
6579 if (q->InterfaceID == set->InterfaceID)
6580 q->ThisQInterval = 0;
c9b9ae52
A
6581
6582 // 2. Flush any cache records received on this interface
6583 revalidate = mDNSfalse; // Don't revalidate if we're flushing the records
283ee3ff
A
6584 FORALL_CACHERECORDS(slot, cg, rr)
6585 if (rr->resrec.InterfaceID == set->InterfaceID)
c9d2d929 6586 PurgeCacheResourceRecord(m, rr);
c9b9ae52
A
6587 }
6588 }
6528fe3e 6589
c9b9ae52 6590 // If we were advertising on this interface, deregister those address and reverse-lookup records now
7f0064bd
A
6591 if (set->Advertise) DeadvertiseInterface(m, set);
6592
c9b9ae52
A
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;
283ee3ff 6600 CacheGroup *cg;
c9b9ae52
A
6601 CacheRecord *rr;
6602 m->NextCacheCheck = m->timenow;
283ee3ff
A
6603 FORALL_CACHERECORDS(slot, cg, rr)
6604 if (rr->resrec.InterfaceID == set->InterfaceID)
c9d2d929 6605 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect);
c9b9ae52 6606 }
6528fe3e
A
6607
6608 mDNS_Unlock(m);
6609 }
6610
c9b9ae52 6611mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6528fe3e 6612 {
c9b9ae52
A
6613 ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6614 (void)m; // Unused parameter
6528fe3e 6615
c9b9ae52
A
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";
283ee3ff 6622 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6528fe3e 6623 }
c9b9ae52 6624 #endif
6528fe3e 6625
7f0064bd
A
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
6528fe3e 6629 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
c9b9ae52
A
6630 if (result == mStatus_NameConflict)
6631 {
7f0064bd
A
6632 sr->Conflict = mDNStrue; // Record that this service set had a conflict
6633 mDNS_DeregisterService(m, sr); // Unlink the records from our list
c9b9ae52
A
6634 return;
6635 }
6528fe3e 6636
c9b9ae52
A
6637 if (result == mStatus_MemFree)
6638 {
283ee3ff 6639 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
c9b9ae52
A
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 }
6528fe3e 6649
c9b9ae52
A
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);
6528fe3e
A
6654 }
6655
7f0064bd
A
6656mDNSlocal 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
6528fe3e
A
6663// Note:
6664// Name is first label of domain name (any dots in the name are actual dots, not label separators)
8e92c31c 6665// Type is service type (e.g. "_ipp._tcp.")
6528fe3e
A
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.)
c9b9ae52 6669// If the host parameter is mDNSNULL or the root domain (ASCII NUL),
7f0064bd 6670// then the default host name (m->MulticastHostname) is automatically used
6528fe3e
A
6671mDNSexport 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,
c9b9ae52
A
6674 AuthRecord *SubTypes, mDNSu32 NumSubTypes,
6675 const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
6528fe3e 6676 {
c9b9ae52
A
6677 mStatus err;
6678 mDNSu32 i;
6528fe3e 6679
c9b9ae52
A
6680 sr->ServiceCallback = Callback;
6681 sr->ServiceContext = Context;
6682 sr->Extras = mDNSNULL;
6683 sr->NumSubTypes = NumSubTypes;
6684 sr->SubTypes = SubTypes;
6685 sr->Conflict = mDNSfalse;
6528fe3e
A
6686 if (host && host->c[0]) sr->Host = *host;
6687 else sr->Host.c[0] = 0;
6688
7f0064bd 6689 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
c9d2d929 6690 if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
7f0064bd 6691
c9b9ae52 6692 // Initialize the AuthRecord objects to sane values
7f0064bd
A
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);
8e92c31c 6697
6528fe3e
A
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
c9b9ae52
A
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
283ee3ff 6706 if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL)
c9b9ae52 6707 return(mStatus_BadParamErr);
283ee3ff
A
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);
c9b9ae52 6710 AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name);
6528fe3e 6711
c9b9ae52 6712 // 1. Set up the ADV record rdata to advertise our service type
283ee3ff 6713 AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name);
c9b9ae52
A
6714
6715 // 2. Set up the PTR record rdata to point to our service name
6528fe3e 6716 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
283ee3ff 6717 AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name);
6528fe3e
A
6718 sr->RR_PTR.Additional1 = &sr->RR_SRV;
6719 sr->RR_PTR.Additional2 = &sr->RR_TXT;
6720
c9b9ae52
A
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 {
7f0064bd 6726 domainname st;
283ee3ff
A
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())
7f0064bd
A
6729 AppendDomainName(&st, type);
6730 mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
283ee3ff
A
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);
c9b9ae52
A
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;
6528fe3e
A
6741
6742 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
283ee3ff 6743 if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host);
8e92c31c 6744 else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; }
6528fe3e 6745
c9b9ae52 6746 // 4. Set up the TXT record rdata,
6528fe3e 6747 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
c9b9ae52
A
6748 if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0;
6749 else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c)
6528fe3e 6750 {
c9b9ae52
A
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);
6528fe3e
A
6754 }
6755 sr->RR_TXT.DependentOn = &sr->RR_SRV;
6756
7f0064bd 6757#ifndef UNICAST_DISABLED
8e92c31c 6758 // If the client has specified an explicit InterfaceID,
28f7d060 6759 // then we do a multicast registration on that interface, even for unicast domains.
283ee3ff 6760 if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
7f0064bd
A
6761 {
6762 mStatus status;
6763 mDNS_Lock(m);
7cb34e5c
A
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.
7f0064bd
A
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
c9b9ae52
A
6774 mDNS_Lock(m);
6775 err = mDNS_Register_internal(m, &sr->RR_SRV);
6776 if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
6528fe3e
A
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.
c9b9ae52
A
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);
6528fe3e 6785
8e92c31c
A
6786 mDNS_Unlock(m);
6787
c9b9ae52
A
6788 if (err) mDNS_DeregisterService(m, sr);
6789 return(err);
6528fe3e
A
6790 }
6791
c9b9ae52
A
6792mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
6793 ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
6528fe3e 6794 {
8e92c31c
A
6795 ExtraResourceRecord **e;
6796 mStatus status;
6797
7f0064bd 6798 extra->next = mDNSNULL;
c9d2d929 6799 mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
7f0064bd
A
6800 AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name);
6801
6802#ifndef UNICAST_DISABLED
283ee3ff 6803 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
8e92c31c 6804 {
7f0064bd 6805 mDNS_Lock(m);
7cb34e5c
A
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.
7f0064bd
A
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;
8e92c31c 6815 }
7f0064bd
A
6816#endif
6817
8e92c31c
A
6818 mDNS_Lock(m);
6819 e = &sr->Extras;
6528fe3e
A
6820 while (*e) e = &(*e)->next;
6821
7f0064bd 6822 if (ttl == 0) ttl = kStandardTTL;
6528fe3e 6823
6528fe3e
A
6824 extra->r.DependentOn = &sr->RR_SRV;
6825
283ee3ff 6826 debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c);
6528fe3e 6827
8e92c31c
A
6828 status = mDNS_Register_internal(m, &extra->r);
6829 if (status == mStatus_NoError) *e = extra;
6830 mDNS_Unlock(m);
6831 return(status);
6528fe3e
A
6832 }
6833
c9d2d929 6834mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context)
6528fe3e 6835 {
8e92c31c
A
6836 ExtraResourceRecord **e;
6837 mStatus status;
6838
8e92c31c
A
6839 mDNS_Lock(m);
6840 e = &sr->Extras;
6528fe3e
A
6841 while (*e && *e != extra) e = &(*e)->next;
6842 if (!*e)
6843 {
283ee3ff 6844 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c);
8e92c31c 6845 status = mStatus_BadReferenceErr;
6528fe3e 6846 }
8e92c31c
A
6847 else
6848 {
283ee3ff 6849 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c);
7f0064bd
A
6850 extra->r.RecordCallback = MemFreeCallback;
6851 extra->r.RecordContext = Context;
8e92c31c 6852 *e = (*e)->next;
7f0064bd 6853#ifndef UNICAST_DISABLED
283ee3ff 6854 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
7f0064bd
A
6855 status = uDNS_DeregisterRecord(m, &extra->r);
6856 else
6857#endif
8e92c31c
A
6858 status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal);
6859 }
6860 mDNS_Unlock(m);
6861 return(status);
6528fe3e
A
6862 }
6863
377735b0 6864mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
6528fe3e 6865 {
8e92c31c
A
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;
6528fe3e
A
6869 domainname type, domain;
6870 domainname *host = mDNSNULL;
6871 ExtraResourceRecord *extras = sr->Extras;
6872 mStatus err;
6873
283ee3ff 6874 DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain);
377735b0
A
6875 if (!newname)
6876 {
8e92c31c
A
6877 name2 = name1;
6878 IncrementLabelSuffix(&name2, mDNStrue);
6879 newname = &name2;
377735b0 6880 }
c9d2d929 6881 LogMsg("Service \"%##s\" renamed to \"%#s\"", sr->RR_SRV.resrec.name->c, newname->c);
6528fe3e
A
6882 if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
6883
377735b0 6884 err = mDNS_RegisterService(m, sr, newname, &type, &domain,
c9b9ae52
A
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);
6528fe3e 6888
c9b9ae52
A
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
6528fe3e
A
6892 while (!err && extras)
6893 {
6894 ExtraResourceRecord *e = extras;
6895 extras = extras->next;
c9b9ae52 6896 err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
6528fe3e
A
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.
c9b9ae52 6905mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
6528fe3e 6906 {
7f0064bd
A
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));
8e92c31c 6909
7f0064bd 6910#ifndef UNICAST_DISABLED
283ee3ff 6911 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
7f0064bd
A
6912 {
6913 mStatus status;
6914 mDNS_Lock(m);
6915 status = uDNS_DeregisterService(m, sr);
6916 mDNS_Unlock(m);
6917 return(status);
6918 }
6919#endif
c9b9ae52
A
6920 if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
6921 {
283ee3ff 6922 debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c);
c9b9ae52
A
6923 return(mStatus_BadReferenceErr);
6924 }
6925 else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
6528fe3e 6926 {
283ee3ff 6927 debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
c9b9ae52 6928 return(mStatus_NoError);
6528fe3e 6929 }
c9b9ae52
A
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 }
6528fe3e 6952
c9b9ae52
A
6953 for (i=0; i<sr->NumSubTypes; i++)
6954 mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal);
6528fe3e 6955
c9b9ae52
A
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.
6973mDNSexport 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 {
7f0064bd 6978 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
283ee3ff 6979 if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
c9b9ae52
A
6980 rr->resrec.rdata->u.srv.priority = 0;
6981 rr->resrec.rdata->u.srv.weight = 0;
6982 rr->resrec.rdata->u.srv.port = zeroIPPort;
283ee3ff 6983 if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host);
c9b9ae52
A
6984 else rr->HostTarget = mDNStrue;
6985 return(mDNS_Register(m, rr));
6528fe3e
A
6986 }
6987
c9b9ae52
A
6988mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
6989 mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
6528fe3e 6990 {
7f0064bd 6991 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
283ee3ff 6992 if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
c9b9ae52 6993 if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr);
6528fe3e
A
6994 return(mDNS_Register(m, rr));
6995 }
6996
6997// ***************************************************************************
c9b9ae52 6998#if COMPILER_LIKES_PRAGMA_MARK
6528fe3e
A
6999#pragma mark -
7000#pragma mark -
7001#pragma mark - Startup and Shutdown
7002#endif
7003
283ee3ff 7004mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
c9b9ae52
A
7005 {
7006 if (storage && numrecords)
7007 {
7008 mDNSu32 i;
7cb34e5c 7009 debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity));
c9b9ae52
A
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
283ee3ff 7017mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
8e92c31c
A
7018 {
7019 mDNS_Lock(m);
7020 mDNS_GrowCache_internal(m, storage, numrecords);
7021 mDNS_Unlock(m);
7022 }
7023
6528fe3e 7024mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
283ee3ff 7025 CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
c9b9ae52 7026 mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
6528fe3e 7027 {
716635cc 7028 mDNSu32 slot;
28f7d060 7029 mDNSs32 timenow;
7f0064bd 7030 mStatus result;
6528fe3e
A
7031
7032 if (!rrcachestorage) rrcachesize = 0;
7033
c9b9ae52
A
7034 m->p = p;
7035 m->KnownBugs = 0;
c9d2d929 7036 m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
c9b9ae52
A
7037 m->AdvertiseLocalAddresses = AdvertiseLocalAddresses;
7038 m->mDNSPlatformStatus = mStatus_Waiting;
7f0064bd
A
7039 m->UnicastPort4 = zeroIPPort;
7040 m->UnicastPort6 = zeroIPPort;
c9b9ae52
A
7041 m->MainCallback = Callback;
7042 m->MainContext = Context;
7f0064bd 7043 m->rec.r.resrec.RecordType = 0;
c9b9ae52
A
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
7f0064bd
A
7054 result = mDNSPlatformTimeInit();
7055 if (result != mStatus_NoError) return(result);
28f7d060
A
7056 m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF);
7057 timenow = mDNS_TimeNow_NoLock(m);
7f0064bd 7058
c9b9ae52
A
7059 m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
7060 m->timenow_last = timenow;
c9b9ae52
A
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;
7f0064bd 7069 m->PktNum = 0;
c9b9ae52
A
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
283ee3ff 7086 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL;
c9b9ae52 7087
8e92c31c 7088 mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
c9b9ae52
A
7089
7090 // Fields below only required for mDNS Responder...
7091 m->hostlabel.c[0] = 0;
7092 m->nicelabel.c[0] = 0;
7f0064bd 7093 m->MulticastHostname.c[0] = 0;
c9b9ae52
A
7094 m->HIHardware.c[0] = 0;
7095 m->HISoftware.c[0] = 0;
7096 m->ResourceRecords = mDNSNULL;
7097 m->DuplicateRecords = mDNSNULL;
283ee3ff 7098 m->NewLocalRecords = mDNSNULL;
c9b9ae52
A
7099 m->CurrentRecord = mDNSNULL;
7100 m->HostInterfaces = mDNSNULL;
7101 m->ProbeFailTime = 0;
7102 m->NumFailedProbes = 0;
7103 m->SuppressProbes = 0;
6528fe3e 7104
7f0064bd 7105#ifndef UNICAST_DISABLED
8e92c31c 7106 uDNS_Init(m);
28f7d060 7107 m->SuppressStdPort53Queries = 0;
7f0064bd 7108#endif
6528fe3e 7109 result = mDNSPlatformInit(m);
c9b9ae52 7110
6528fe3e
A
7111 return(result);
7112 }
7113
c9b9ae52 7114mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
6528fe3e
A
7115 {
7116 m->mDNSPlatformStatus = result;
c9b9ae52 7117 if (m->MainCallback)
8e92c31c
A
7118 {
7119 mDNS_Lock(m);
7120 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
c9b9ae52 7121 m->MainCallback(m, mStatus_NoError);
8e92c31c
A
7122 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
7123 mDNS_Unlock(m);
7124 }
6528fe3e
A
7125 }
7126
c9b9ae52 7127mDNSexport void mDNS_Close(mDNS *const m)
6528fe3e 7128 {
c9b9ae52
A
7129 mDNSu32 rrcache_active = 0;
7130 mDNSu32 rrcache_totalused = 0;
7131 mDNSu32 slot;
7132 NetworkInterfaceInfo *intf;
7133 mDNS_Lock(m);
8e92c31c 7134
c9b9ae52 7135 m->mDNS_shutdown = mDNStrue;
6528fe3e 7136
7f0064bd
A
7137#ifndef UNICAST_DISABLED
7138 uDNS_Close(m);
7139#endif
c9b9ae52
A
7140 rrcache_totalused = m->rrcache_totalused;
7141 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
716635cc 7142 {
283ee3ff 7143 while(m->rrcache_hash[slot])
c9b9ae52 7144 {
283ee3ff
A
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]);
c9b9ae52 7155 }
716635cc 7156 }
7f0064bd 7157 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active);
c9b9ae52
A
7158 if (rrcache_active != m->rrcache_active)
7159 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active);
7160
c9b9ae52
A
7161 for (intf = m->HostInterfaces; intf; intf = intf->next)
7162 if (intf->Advertise)
8e92c31c 7163 DeadvertiseInterface(m, intf);
6528fe3e
A
7164
7165 // Make sure there are nothing but deregistering records remaining in the list
c9b9ae52 7166 if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
6528fe3e
A
7167 m->CurrentRecord = m->ResourceRecords;
7168 while (m->CurrentRecord)
7169 {
c9b9ae52 7170 AuthRecord *rr = m->CurrentRecord;
c9b9ae52 7171 if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
6528fe3e 7172 {
283ee3ff 7173 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c);
c9b9ae52 7174 mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
6528fe3e 7175 }
283ee3ff
A
7176 else
7177 m->CurrentRecord = rr->next;
6528fe3e
A
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
283ee3ff
A
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));
6528fe3e
A
7187
7188 mDNS_Unlock(m);
7189 debugf("mDNS_Close: mDNSPlatformClose");
7190 mDNSPlatformClose(m);
7191 debugf("mDNS_Close: done");
7192 }