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