]> git.saurik.com Git - apple/mdnsresponder.git/blame_incremental - mDNSCore/mDNS.c
mDNSResponder-108.4.tar.gz
[apple/mdnsresponder.git] / mDNSCore / mDNS.c
... / ...
CommitLineData
1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 *
24 * This code is completely 100% portable C. It does not depend on any external header files
25 * from outside the mDNS project -- all the types it expects to find are defined right here.
26 *
27 * The previous point is very important: This file does not depend on any external
28 * header files. It should complile on *any* platform that has a C compiler, without
29 * making *any* assumptions about availability of so-called "standard" C functions,
30 * routines, or types (which may or may not be present on any given platform).
31
32 * Formatting notes:
33 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
34 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
35 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
36 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
37 * therefore common sense dictates that if they are part of a compound statement then they
38 * should be indented to the same level as everything else in that compound statement.
39 * Indenting curly braces at the same level as the "if" implies that curly braces are
40 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
41 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
42 * understand why variable y is not of type "char*" just proves the point that poor code
43 * layout leads people to unfortunate misunderstandings about how the C language really works.)
44
45 Change History (most recent first):
46
47$Log: mDNS.c,v $
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
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
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
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
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
231<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
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
240<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
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
538<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
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
545<rdar://problem/3651409>: Feature #9516: Need support for NAT-PMP in client
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
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
607
608Revision 1.366 2004/03/20 03:12:57 cheshire
609<rdar://problem/3587619>: UpdateCredits not granted promptly enough
610
611Revision 1.365 2004/03/19 23:51:22 cheshire
612Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
613
614Revision 1.364 2004/03/13 01:57:33 ksekar
615<rdar://problem/3192546>: DynDNS: Dynamic update of service records
616
617Revision 1.363 2004/03/12 21:00:51 cheshire
618Also show port numbers when logging "apparent spoof mDNS Response" messages
619
620Revision 1.362 2004/03/12 08:58:18 cheshire
621Guard against empty TXT records
622
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.
625
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
628
629Revision 1.359 2004/03/02 03:21:56 cheshire
630<rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
631
632Revision 1.358 2004/02/20 08:18:34 cheshire
633<rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
634
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
637
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
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
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
714Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
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.
756Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
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
762Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
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
810
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
845<rdar://problem/3319418> Services always in a state of flux
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
923<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
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
954Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
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
960<rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
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
1094<rdar://problem/3319418> Services always in a state of flux
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
1465<rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
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
1485<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
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:
1497mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
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
1509<rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
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
1551<rdar://problem/3233804> Incorrect goodbye packet after conflict
1552
1553Revision 1.102 2003/04/17 03:06:28 cheshire
1554<rdar://problem/3231321> No need to query again when a service goes away
1555Set UnansweredQueries to 2 when receiving a "goodbye" packet
1556
1557Revision 1.101 2003/04/15 20:58:31 jgraessl
1558<rdar://problem/3229014> Added a hash to lookup records in the cache.
1559
1560Revision 1.100 2003/04/15 18:53:14 cheshire
1561<rdar://problem/3229064> Bug in ScheduleNextTask
1562mDNS.c 1.94 incorrectly combined two "if" statements into one.
1563
1564Revision 1.99 2003/04/15 18:09:13 jgraessl
1565<rdar://problem/3228892>
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
16002. Make HostNameCallback() use DeadvertiseInterface() instead
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
1622<rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
1623Fixed by leaving client in list after conflict, until client explicitly deallocates
1624
1625Revision 1.84 2003/03/05 01:27:30 cheshire
1626<rdar://problem/3185482> Different TTL for multicast versus unicast responses
1627When building unicast responses, record TTLs are capped to 10 seconds
1628
1629Revision 1.83 2003/03/04 23:48:52 cheshire
1630<rdar://problem/3188865> Double probes after wake from sleep
1631Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1632
1633Revision 1.82 2003/03/04 23:38:29 cheshire
1634<rdar://problem/3099194> mDNSResponder needs performance improvements
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
1639<rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
1640
1641Revision 1.80 2003/02/21 02:47:53 cheshire
1642<rdar://problem/3099194> mDNSResponder needs performance improvements
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
1648<rdar://problem/3099194> mDNSResponder needs performance improvements
1649Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1650
1651Revision 1.78 2003/02/20 06:48:32 cheshire
1652<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
1653Reviewed by: Josh Graessley, Bob Bradley
1654
1655Revision 1.77 2003/01/31 03:35:59 cheshire
1656<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
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
1671<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
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
1678<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
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
1683<rdar://problem/3153091> Race condition when network change causes bad stuff
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
1692<rdar://problem/3124348> service name changes are not properly handled
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
1699<rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
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:
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
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
1715<rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
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
1727Added mDNS_RegisterNoSuchService() function for assertion of non-existence
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
1743#include "DNSCommon.h" // Defines general DNS untility routines
1744#include "uDNS.h" // Defines entry points into unicast-specific routines
1745// Disable certain benign warnings with Microsoft compilers
1746#if(defined(_MSC_VER))
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
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)
1758#endif
1759
1760// ***************************************************************************
1761#if COMPILER_LIKES_PRAGMA_MARK
1762#pragma mark -
1763#pragma mark - Program Constants
1764#endif
1765
1766mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
1767mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
1768mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
1769mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
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 } };
1772mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
1773
1774mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
1775mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1;
1776
1777mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
1778
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.
1782#define MulticastDNSPortAsNumber 5353
1783#define LoopbackIPCPortAsNumber 5354
1784
1785mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
1786mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } };
1787mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } };
1788mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
1789mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } };
1790
1791mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
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 } };
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 } } } };
1796
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 } };
1803
1804// Any records bigger than this are considered 'large' records
1805#define SmallRecordLimit 1024
1806
1807#define kMaxUpdateCredits 10
1808#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1809
1810mDNSexport const char *const mDNS_DomainTypeNames[] =
1811 {
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
1817 };
1818
1819#ifdef UNICAST_DISABLED
1820#define uDNS_IsActiveQuery(q, u) mDNSfalse
1821#endif
1822
1823// ***************************************************************************
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;
1841 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1842
1843mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
1844 {
1845 mDNSu32 nwritten = 0;
1846 int c;
1847 if (buflen == 0) return(0);
1848 buflen--; // Pre-reserve one space in the buffer for the terminating null
1849 if (buflen == 0) goto exit;
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 {
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;
1965 switch (ip->type)
1966 {
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;
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;
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;
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;
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 {
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;
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 {
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; }
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 }
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)
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
2090 // Make sure we don't truncate in the middle of a UTF-8 character.
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).
2094 if (i > buflen - nwritten)
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
2126#pragma mark -
2127#pragma mark - General Utility Functions
2128#endif
2129
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)
2135 {
2136 if (ActiveQuestion(q))
2137 if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
2138 m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
2139 }
2140
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
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().
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
2276// ***************************************************************************
2277#if COMPILER_LIKES_PRAGMA_MARK
2278#pragma mark -
2279#pragma mark - Resource Record Utility Functions
2280#endif
2281
2282#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2283
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)) )
2288
2289#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2290 (ResourceRecordIsValidAnswer(RR) && \
2291 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2292
2293#define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2294#define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2295
2296#define InitialAnnounceCount ((mDNSu8)10)
2297
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)
2305
2306#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2307 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2308 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2309
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))
2314
2315#define MaxUnansweredQueries 4
2316
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)
2324 {
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);
2330 return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(r1->name, r2->name));
2331 }
2332
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.
2338mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
2339 {
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);
2345 if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
2346 return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(pktrr->resrec.name, authrr->resrec.name));
2347 }
2348
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)
2352 {
2353 if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
2354 if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
2355 if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name)) return(mDNSfalse);
2356 return(SameRData(r1, r2));
2357 }
2358
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)
2367 {
2368 // If RR signature is different, or data is different, then don't suppress our answer
2369 if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
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);
2381 }
2382
2383mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
2384 {
2385 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2386 {
2387 //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
2388 if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2389 m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
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 }
2396 }
2397
2398mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
2399 {
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)
2409 {
2410 m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique);
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;
2417 }
2418
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);
2440 }
2441
2442#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
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
2450 if (target && SameDomainName(target, &m->MulticastHostname))
2451 debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
2452
2453 if (target && !SameDomainName(target, &m->MulticastHostname))
2454 {
2455 AssignDomainName(target, &m->MulticastHostname);
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.
2466 if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
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));
2468
2469 rr->AnnounceCount = InitialAnnounceCount;
2470 rr->RequireGoodbye = mDNSfalse;
2471 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2472 InitializeLastAPTime(m,rr);
2473 }
2474 }
2475
2476mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
2477 {
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
2489// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
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))
2492
2493mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
2494 {
2495 domainname *target = GetRRDomainNameTarget(&rr->resrec);
2496 AuthRecord *r;
2497 AuthRecord **p = &m->ResourceRecords;
2498 AuthRecord **d = &m->DuplicateRecords;
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); }
2504
2505#ifndef UNICAST_DISABLED
2506 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
2507 rr->uDNS_info.id = zeroID;
2508 else return uDNS_RegisterRecord(m, rr);
2509#endif
2510
2511 while (*p && *p != rr) p=&(*p)->next;
2512 while (*d && *d != rr) d=&(*d)->next;
2513 if (*d || *p)
2514 {
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));
2516 return(mStatus_AlreadyRegistered);
2517 }
2518
2519 if (rr->DependentOn)
2520 {
2521 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2522 rr->resrec.RecordType = kDNSRecordTypeVerified;
2523 else
2524 {
2525 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
2526 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2527 return(mStatus_Invalid);
2528 }
2529 if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
2530 {
2531 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
2532 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
2533 return(mStatus_Invalid);
2534 }
2535 }
2536
2537 // If this resource record is referencing a specific interface, make sure it exists
2538 if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
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
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
2560// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2561// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
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;
2564
2565 // Field Group 2: Transient state for Authoritative Records
2566 rr->Acknowledged = mDNSfalse;
2567 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
2568 rr->AnnounceCount = InitialAnnounceCount;
2569 rr->RequireGoodbye = mDNSfalse;
2570 rr->LocalAnswer = mDNSfalse;
2571 rr->IncludeInProbe = mDNSfalse;
2572 rr->ImmedAnswer = mDNSNULL;
2573 rr->ImmedUnicast = mDNSfalse;
2574 rr->ImmedAdditional = mDNSNULL;
2575 rr->SendRNow = mDNSNULL;
2576 rr->v4Requester = zerov4Addr;
2577 rr->v6Requester = zerov6Addr;
2578 rr->NextResponse = mDNSNULL;
2579 rr->NR_AnswerTo = mDNSNULL;
2580 rr->NR_AdditionalTo = mDNSNULL;
2581 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2582 if (!rr->HostTarget) InitializeLastAPTime(m, rr);
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()
2587 rr->NewRData = mDNSNULL;
2588 rr->newrdlength = 0;
2589 rr->UpdateCallback = mDNSNULL;
2590 rr->UpdateCredits = kMaxUpdateCredits;
2591 rr->NextUpdateCredit = 0;
2592 rr->UpdateBlocked = 0;
2593
2594// rr->resrec.interface = already set in mDNS_SetupResourceRecord
2595// rr->resrec.name->c = MUST be set by client
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
2600
2601 if (rr->HostTarget)
2602 SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2603 else
2604 {
2605 rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
2606 rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
2607 }
2608
2609 if (!ValidateDomainName(rr->resrec.name))
2610 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2611
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.
2615 if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
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))
2619 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2620
2621 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
2622 rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
2623
2624 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
2625 {
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;
2630 for (r = m->ResourceRecords; r; r=r->next)
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 }
2636 if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2637 {
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;
2643 }
2644 }
2645 }
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 {
2652 debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr));
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 }
2661 else
2662 {
2663 debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
2664 if (!m->NewLocalRecords) m->NewLocalRecords = rr;
2665 *p = rr;
2666 }
2667
2668 // For records that are not going to probe, acknowledge them right away
2669 if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
2670 AcknowledgeRecord(m, rr);
2671
2672 return(mStatus_NoError);
2673 }
2674
2675mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
2676 {
2677 m->ProbeFailTime = m->timenow;
2678 m->NumFailedProbes++;
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)
2687 {
2688 m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
2689 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
2690 m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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
2701 }
2702
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.
2710mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
2711 {
2712 AuthRecord *r2;
2713 mDNSu8 RecordType = rr->resrec.RecordType;
2714 AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
2715
2716#ifndef UNICAST_DISABLED
2717 if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)))
2718 return uDNS_DeregisterRecord(m, rr);
2719#endif
2720
2721 while (*p && *p != rr) p=&(*p)->next;
2722
2723 if (*p)
2724 {
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 {
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;
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;
2741 debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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;
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;
2760 }
2761 }
2762 }
2763 else
2764 {
2765 // We didn't find our record on the main list; try the DuplicateRecords list instead.
2766 p = &m->DuplicateRecords;
2767 while (*p && *p != rr) p=&(*p)->next;
2768 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
2769 if (*p) rr->RequireGoodbye = mDNSfalse;
2770 if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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)
2777 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2778 return(mStatus_BadReferenceErr);
2779 }
2780
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
2783 if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
2784 {
2785 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2786 rr->resrec.RecordType = kDNSRecordTypeDeregistering;
2787 rr->resrec.rroriginalttl = 0;
2788 rr->ImmedAnswer = mDNSInterfaceMark;
2789 if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
2790 m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
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
2796 if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
2797 if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
2798 rr->next = mDNSNULL;
2799
2800 if (RecordType == kDNSRecordTypeUnregistered)
2801 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
2802 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2803 else if (RecordType == kDNSRecordTypeDeregistering)
2804 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
2805 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2806 else
2807 {
2808 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2809 rr->resrec.RecordType = kDNSRecordTypeUnregistered;
2810 }
2811
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)",
2814 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2815
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
2819 if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse);
2820
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
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
2831 {
2832 RecordProbeFailure(m, rr);
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 }
2843 }
2844 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2845 }
2846 return(mStatus_NoError);
2847 }
2848
2849// ***************************************************************************
2850#if COMPILER_LIKES_PRAGMA_MARK
2851#pragma mark -
2852#pragma mark -
2853#pragma mark - Packet Sending Functions
2854#endif
2855
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 }
2868 debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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 ...
2889 rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target
2890 SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name))
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
2970mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
2971 {
2972 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
2973 // that it should go ahead and immediately dispose of this registration
2974 rr->resrec.RecordType = kDNSRecordTypeShared;
2975 rr->RequireGoodbye = mDNSfalse;
2976 mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this
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.
2982mDNSlocal void DiscardDeregistrations(mDNS *const m)
2983 {
2984 if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2985 m->CurrentRecord = m->ResourceRecords;
2986
2987 while (m->CurrentRecord)
2988 {
2989 AuthRecord *rr = m->CurrentRecord;
2990 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
2991 CompleteDeregistration(m, rr); // Don't touch rr after this
2992 else
2993 m->CurrentRecord = rr->next;
2994 }
2995 }
2996
2997mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
2998 {
2999 if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
3000 else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
3001 }
3002
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
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.
3019mDNSlocal void SendResponses(mDNS *const m)
3020 {
3021 int pktcount = 0;
3022 AuthRecord *rr, *r2;
3023 mDNSs32 maxExistingAnnounceInterval = 0;
3024 const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
3025
3026 m->NextScheduledResponse = m->timenow + 0x78000000;
3027
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
3044 // ***
3045 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
3046 // ***
3047
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)
3050 {
3051 while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
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
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.
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
3077 if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ...
3078 ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ...
3079 rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ...
3080 rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target
3081 SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) &&
3082 (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID))
3083 r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too
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))
3098 if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
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
3102 {
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;
3107 }
3108 }
3109
3110 // Now set SendRNow state appropriately
3111 for (rr = m->ResourceRecords; rr; rr=rr->next)
3112 {
3113 if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces
3114 {
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))
3121 {
3122 rr->AnnounceCount--;
3123 rr->ThisAPInterval *= 2;
3124 rr->LastAPTime = m->timenow;
3125 if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0;
3126 debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount);
3127 }
3128 }
3129 else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface:
3130 {
3131 rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface
3132 rr->ImmedAdditional = mDNSNULL; // No need to send as additional too
3133 rr->LastMCTime = m->timenow;
3134 rr->LastMCInterface = rr->ImmedAnswer;
3135 }
3136 SetNextAnnounceProbeTime(m, rr);
3137 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
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;
3149 mDNSu8 *responseptr = m->omsg.data;
3150 mDNSu8 *newptr;
3151 InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
3152
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.
3161 for (rr = m->ResourceRecords; rr; rr=rr->next)
3162 if (rr->SendRNow == intf->InterfaceID)
3163 {
3164 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3165 {
3166 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3167 if (!newptr && m->omsg.h.numAnswers) break;
3168 numDereg++;
3169 responseptr = newptr;
3170 }
3171 else if (rr->NewRData && !m->SleepState) // If we have new data for this record
3172 {
3173 RData *OldRData = rr->resrec.rdata;
3174 mDNSu16 oldrdlength = rr->resrec.rdlength;
3175 // See if we should send a courtesy "goodbye" for the old data before we replace it.
3176 if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye)
3177 {
3178 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3179 if (!newptr && m->omsg.h.numAnswers) break;
3180 numDereg++;
3181 responseptr = newptr;
3182 rr->RequireGoodbye = mDNSfalse;
3183 }
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);
3186 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3187 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
3188 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
3189 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
3190 if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; }
3191 SetNewRData(&rr->resrec, OldRData, oldrdlength);
3192 }
3193 else
3194 {
3195 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3196 rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
3197 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl);
3198 rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
3199 if (!newptr && m->omsg.h.numAnswers) break;
3200 rr->RequireGoodbye = (mDNSu8) (!m->SleepState);
3201 if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
3202 responseptr = newptr;
3203 }
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 }
3210
3211 // Second Pass. Add additional records, if there's space.
3212 newptr = responseptr;
3213 for (rr = m->ResourceRecords; rr; rr=rr->next)
3214 if (rr->ImmedAdditional == intf->InterfaceID)
3215 if (ResourceRecordIsValidAnswer(rr))
3216 {
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))
3224 {
3225 const AuthRecord *a;
3226 for (a = m->ResourceRecords; a; a=a->next)
3227 if (a->LastMCTime == m->timenow &&
3228 a->LastMCInterface == intf->InterfaceID &&
3229 SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; }
3230 }
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
3234 {
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 }
3251 }
3252 }
3253
3254 if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals)
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",
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; }
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 }
3276 }
3277
3278 // ***
3279 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3280 // ***
3281
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
3289 if (rr->SendRNow)
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 }
3295
3296 if (rr->ImmedAnswer)
3297 {
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 }
3309 }
3310 }
3311 verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow);
3312 }
3313
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) ( \
3325 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
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
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
3337mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr)
3338 {
3339 rr->NextRequiredQuery = RRExpireTime(rr);
3340
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);
3347 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
3348 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond);
3349 }
3350
3351 if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
3352 m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
3353
3354 if (rr->DelayDelivery)
3355 if (m->NextCacheCheck - rr->DelayDelivery > 0)
3356 m->NextCacheCheck = rr->DelayDelivery;
3357 }
3358
3359#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3360#define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3361#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
3362
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))
3372 {
3373 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
3374 interval += mDNSRandom(interval/3);
3375 rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3;
3376 rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond;
3377 SetNextCacheCheckTime(m, rr);
3378 }
3379 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr));
3380 return(mStatus_NoError);
3381 }
3382
3383#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
3384
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)
3390 {
3391 mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
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 {
3402 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data);
3403 query->h.numQuestions--;
3404 return(mDNSfalse);
3405 }
3406 else
3407 {
3408 mDNSu32 forecast = *answerforecast;
3409 const mDNSu32 slot = HashSlot(&q->qname);
3410 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3411 CacheRecord *rr;
3412 CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update
3413
3414 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
3415 if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface
3416 rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not 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
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)
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 }
3438
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)
3451 {
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
3456 }
3457
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;
3463
3464 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache,
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 }
3476 }
3477
3478mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
3479 {
3480 mDNSu32 slot;
3481 CacheGroup *cg;
3482 CacheRecord *rr;
3483 domainname *target;
3484 FORALL_CACHERECORDS(slot, cg, rr)
3485 if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname))
3486 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
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)
3509 {
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);
3513 }
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;
3531 }
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);
3539 }
3540
3541mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
3542 {
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))
3549 {
3550 // We forecast: qname (n) type (2) class (2)
3551 mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4;
3552 const mDNSu32 slot = HashSlot(&q->qname);
3553 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3554 CacheRecord *rr;
3555 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache,
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
3560 {
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
3564 }
3565 return(mDNStrue);
3566 }
3567
3568 return(mDNSfalse);
3569 }
3570
3571// How Standard Queries are generated:
3572// 1. The Question Section contains the question
3573// 2. The Additional Section contains answers we already know, to suppress duplicate responses
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
3583mDNSlocal void SendQueries(mDNS *const m)
3584 {
3585 int pktcount = 0;
3586 DNSQuestion *q;
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 {
3595 mDNSu32 slot;
3596 CacheGroup *cg;
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
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 }
3612
3613 // Scan our list of questions to see which *unicast* queries need to be sent
3614 for (q = m->Questions; q; q=q->next)
3615 if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow)))
3616 {
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;
3623 if (q->ThisQInterval > MaxQuestionInterval)
3624 q->ThisQInterval = MaxQuestionInterval;
3625 q->LastQTime = m->timenow;
3626 q->LastQTxTime = m->timenow;
3627 q->RecentAnswerPkts = 0;
3628 q->SendQNow = mDNSNULL;
3629 m->ExpectUnicastResponse = m->timenow;
3630 }
3631
3632 // Scan our list of questions to see which *multicast* queries we're definitely going to send
3633 for (q = m->Questions; q; q=q->next)
3634 if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow))
3635 {
3636 q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
3637 if (maxExistingQuestionInterval < q->ThisQInterval)
3638 maxExistingQuestionInterval = q->ThisQInterval;
3639 }
3640
3641 // Scan our list of questions
3642 // (a) to see if there are any more that are worth accelerating, and
3643 // (b) to update the state variables for *all* the questions we're going to send
3644 for (q = m->Questions; q; q=q->next)
3645 {
3646 if (q->SendQNow ||
3647 (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))
3648 {
3649 // If at least halfway to next query time, advance to next interval
3650 // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
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 {
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
3661 }
3662 }
3663
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 }
3671
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);
3675
3676 q->LastQTxTime = m->timenow;
3677 q->RecentAnswerPkts = 0;
3678 if (q->RequestUnicast) q->RequestUnicast--;
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)
3687 {
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)
3693 {
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 }
3712 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
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;
3724 AcknowledgeRecord(m, rr);
3725 }
3726 }
3727 }
3728 m->CurrentRecord = m->DuplicateRecords;
3729 while (m->CurrentRecord)
3730 {
3731 AuthRecord *rr = m->CurrentRecord;
3732 m->CurrentRecord = rr->next;
3733 if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
3734 AcknowledgeRecord(m, rr);
3735 }
3736 }
3737
3738 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
3739 while (intf)
3740 {
3741 AuthRecord *rr;
3742 mDNSu8 *queryptr = m->omsg.data;
3743 InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags);
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 {
3755 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
3756 SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ",
3757 q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
3758 // If we're suppressing this question, or we successfully put it, update its SendQNow state
3759 if (SuppressOnThisInterface(q->DupSuppress, intf) ||
3760 BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
3761 q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
3762 }
3763
3764 // Put probe questions in this packet
3765 for (rr = m->ResourceRecords; rr; rr=rr->next)
3766 if (rr->SendRNow == intf->InterfaceID)
3767 {
3768 mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353;
3769 mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
3770 const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
3771 mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit));
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;
3780 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount);
3781 }
3782 else
3783 {
3784 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3785 m->omsg.h.numQuestions--;
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)
3792 {
3793 CacheRecord *rr = KnownAnswerList;
3794 mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
3795 mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd);
3796 if (newptr)
3797 {
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);
3799 queryptr = newptr;
3800 KnownAnswerList = rr->NextInKAList;
3801 rr->NextInKAList = mDNSNULL;
3802 }
3803 else
3804 {
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.
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;
3810 break;
3811 }
3812 }
3813
3814 for (rr = m->ResourceRecords; rr; rr=rr->next)
3815 if (rr->IncludeInProbe)
3816 {
3817 mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec);
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)?",
3821 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3822 }
3823
3824 if (queryptr > m->omsg.data)
3825 {
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);
3828 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
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);
3834 if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
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.
3839 }
3840 else // Nothing more to send on this interface; go to next
3841 {
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;
3848 }
3849 }
3850
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 }
3862 }
3863
3864// ***************************************************************************
3865#if COMPILER_LIKES_PRAGMA_MARK
3866#pragma mark -
3867#pragma mark - RR List Management & Task Management
3868#endif
3869
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.
3872mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord)
3873 {
3874 verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
3875 q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3876
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
3881 rr->LastUsed = m->timenow;
3882 if (ActiveQuestion(q) && rr->CRActiveQuestion != q)
3883 {
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);
3887 }
3888
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;
3900 q->RecentAnswerPkts = 0;
3901 q->ThisQInterval = MaxQuestionInterval;
3902 q->RequestUnicast = mDNSfalse;
3903 }
3904
3905 if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3906
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
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 {
3920 rr->DelayDelivery = 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
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;
3938 CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
3939 CacheRecord *rr;
3940 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
3941 if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name))
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);
3945 if (delay - start > 0) return(NonZeroTime(delay));
3946 else return(0);
3947 }
3948
3949// CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
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.
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.
3956// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3957mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
3958 {
3959 if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3960 m->CurrentQuestion = m->Questions;
3961 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3962 {
3963 DNSQuestion *q = m->CurrentQuestion;
3964 m->CurrentQuestion = q->next;
3965 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3966 {
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.
3972 if (q->LastAnswerPktNum != m->PktNum)
3973 {
3974 q->LastAnswerPktNum = m->PktNum;
3975 if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
3976 q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
3977 {
3978 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
3979 q->qname.c, DNSTypeName(q->qtype));
3980 q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4);
3981 q->ThisQInterval = InitialQuestionInterval;
3982 SetNextQueryTime(m,q);
3983 }
3984 }
3985 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl);
3986 q->CurrentAnswers++;
3987 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
3988 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
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 }
3998 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3999 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4000 }
4001 }
4002 m->CurrentQuestion = mDNSNULL;
4003 SetNextCacheCheckTime(m, rr);
4004 }
4005
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);
4019 if (m->CurrentQuestion) LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set");
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
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)
4041 {
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))
4049 {
4050 verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr));
4051 if (q->CurrentAnswers == 0)
4052 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype));
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 {
4061 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
4062 ReconfirmAntecedents(m, q);
4063 }
4064 AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
4065 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4066 }
4067 }
4068 m->CurrentQuestion = mDNSNULL;
4069 }
4070
4071mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
4072 {
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;
4079 m->rrcache_totalused--;
4080 }
4081
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);
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));
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
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
4103mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg)
4104 {
4105 CacheRecord **rp = &cg->members;
4106
4107 if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4108 m->lock_rrcache = 1;
4109
4110 while (*rp)
4111 {
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
4117 verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr));
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 }
4123 ReleaseCacheRecord(m, rr);
4124 }
4125 else // else, not expired; see if we need to query
4126 {
4127 if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
4128 event = rr->DelayDelivery;
4129 else
4130 {
4131 if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr);
4132 if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
4133 {
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(),
4142 // which will correctly update m->NextCacheCheck for us
4143 event = m->timenow + 0x3FFFFFFF;
4144 }
4145 }
4146 }
4147 if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0)
4148 m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
4149 rp = &rr->next;
4150 }
4151 }
4152 if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp);
4153 cg->rrcache_tail = rp;
4154 m->lock_rrcache = 0;
4155 }
4156
4157mDNSlocal void AnswerNewQuestion(mDNS *const m)
4158 {
4159 mDNSBool ShouldQueryImmediately = mDNStrue;
4160 CacheRecord *rr;
4161 DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
4162 const mDNSu32 slot = HashSlot(&q->qname);
4163 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
4164
4165 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4166
4167 if (cg) CheckCacheExpiration(m, cg);
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.
4173 // If the client's question callback deletes the question, then m->CurrentQuestion will
4174 // be advanced, and we'll exit out of the loop
4175 m->lock_rrcache = 1;
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
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)
4199 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4200 {
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)",
4206 rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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
4212 if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique))
4213 ShouldQueryImmediately = mDNSfalse;
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
4220 }
4221 else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype))
4222 if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname))
4223 ShouldQueryImmediately = mDNSfalse;
4224
4225 if (ShouldQueryImmediately && m->CurrentQuestion == q)
4226 {
4227 q->ThisQInterval = InitialQuestionInterval;
4228 q->LastQTime = m->timenow - q->ThisQInterval;
4229 m->NextScheduledQuery = m->timenow;
4230 }
4231 m->CurrentQuestion = mDNSNULL;
4232 m->lock_rrcache = 0;
4233 }
4234
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
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)
4241
4242 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4243
4244 if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
4245 m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
4246
4247 if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4248 m->CurrentRecord = m->ResourceRecords;
4249 while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
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
4258 }
4259 }
4260
4261 m->CurrentQuestion = mDNSNULL;
4262 m->CurrentRecord = mDNSNULL;
4263 }
4264
4265mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG)
4266 {
4267 CacheEntity *e = mDNSNULL;
4268
4269 if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
4270 m->lock_rrcache = 1;
4271
4272 // If we have no free records, ask the client layer to give us some more memory
4273 if (!m->rrcache_free && m->MainCallback)
4274 {
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
4287 {
4288 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
4289 m->MainCallback(m, mStatus_GrowCache);
4290 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
4291 }
4292 }
4293
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)
4297 {
4298 #if MDNS_DEBUGMSGS
4299 mDNSu32 oldtotalused = m->rrcache_totalused;
4300 #endif
4301 mDNSu32 slot;
4302 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4303 {
4304 CacheGroup **cp = &m->rrcache_hash[slot];
4305 while (*cp)
4306 {
4307 CacheRecord **rp = &(*cp)->members;
4308 while (*rp)
4309 {
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 }
4320 }
4321 if ((*cp)->rrcache_tail != rp) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp);
4322 (*cp)->rrcache_tail = rp;
4323 if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next;
4324 else ReleaseCacheGroup(m, cp);
4325 }
4326 }
4327 #if MDNS_DEBUGMSGS
4328 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
4329 #endif
4330 }
4331
4332 if (m->rrcache_free) // If there are records in the free list, take one
4333 {
4334 e = m->rrcache_free;
4335 m->rrcache_free = e->next;
4336 if (++m->rrcache_totalused >= m->rrcache_report)
4337 {
4338 debugf("RR Cache now using %ld objects", m->rrcache_totalused);
4339 if (m->rrcache_report < 100) m->rrcache_report += 10;
4340 else m->rrcache_report += 100;
4341 }
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 {
4355 r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage
4356 if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage
4357 {
4358 r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength);
4359 if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength;
4360 else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; }
4361 }
4362 }
4363 return(r);
4364 }
4365
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;
4376 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
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);
4385
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);
4391 }
4392
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);
4404 }
4405
4406mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m)
4407 {
4408 mDNSs32 time;
4409 mDNSPlatformLock(m);
4410 if (m->mDNS_busy)
4411 {
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);
4414 }
4415
4416 if (m->timenow) time = m->timenow;
4417 else time = mDNS_TimeNow_NoLock(m);
4418 mDNSPlatformUnlock(m);
4419 return(time);
4420 }
4421
4422mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
4423 {
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;
4429
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;
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 }
4454 }
4455
4456 // 4. See if we can answer any of our new local questions from the cache
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 }
4462 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4463
4464 for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
4465 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4466
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");
4474
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.
4481 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
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.
4484 // Finally, we send responses, including the previously mentioned records that just completed probing
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 }
4508
4509 m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
4510 }
4511
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
4532#ifndef UNICAST_DISABLED
4533 uDNS_Execute(m);
4534#endif
4535 mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4536 return(m->NextScheduledEvent);
4537 }
4538
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)
4551 {
4552 AuthRecord *rr;
4553
4554 mDNS_Lock(m);
4555
4556 m->SleepState = sleepstate;
4557 LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
4558
4559 if (sleepstate)
4560 {
4561#ifndef UNICAST_DISABLED
4562 uDNS_Sleep(m);
4563#endif
4564 // Mark all the records we need to deregister and send them
4565 for (rr = m->ResourceRecords; rr; rr=rr->next)
4566 if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
4567 rr->ImmedAnswer = mDNSInterfaceMark;
4568 SendResponses(m);
4569 }
4570 else
4571 {
4572 DNSQuestion *q;
4573 mDNSu32 slot;
4574 CacheGroup *cg;
4575 CacheRecord *cr;
4576
4577#ifndef UNICAST_DISABLED
4578 uDNS_Wake(m);
4579#endif
4580 // 1. Retrigger all our questions
4581 for (q = m->Questions; q; q=q->next) // Scan our list of questions
4582 if (ActiveQuestion(q))
4583 {
4584 q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question
4585 q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
4586 q->LastQTime = m->timenow - q->ThisQInterval;
4587 q->RecentAnswerPkts = 0;
4588 ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
4589 m->NextScheduledQuery = m->timenow;
4590 }
4591
4592 // 2. Re-validate our cache records
4593 m->NextCacheCheck = m->timenow;
4594 FORALL_CACHERECORDS(slot, cg, cr)
4595 mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect);
4596
4597 // 3. Retrigger probing and announcing for all our authoritative records
4598 for (rr = m->ResourceRecords; rr; rr=rr->next)
4599 {
4600 if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
4601 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
4602 rr->AnnounceCount = InitialAnnounceCount;
4603 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
4604 InitializeLastAPTime(m, rr);
4605 }
4606 }
4607
4608 mDNS_Unlock(m);
4609 }
4610
4611// ***************************************************************************
4612#if COMPILER_LIKES_PRAGMA_MARK
4613#pragma mark -
4614#pragma mark - Packet Reception Functions
4615#endif
4616
4617#define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4618
4619mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
4620 const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
4621 {
4622 mDNSu8 *responseptr = response->data;
4623 const mDNSu8 *const limit = response->data + sizeof(response->data);
4624 const mDNSu8 *ptr = query->data;
4625 AuthRecord *rr;
4626 mDNSu32 maxttl = 0x70000000;
4627 int i;
4628
4629 // Initialize the response fields so we can answer the questions
4630 InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
4631
4632 // ***
4633 // *** 1. Write out the list of questions we are actually going to answer with this packet
4634 // ***
4635 if (LegacyQuery)
4636 {
4637 maxttl = 10;
4638 for (i=0; i<query->h.numQuestions; i++) // For each question...
4639 {
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 }
4652 }
4653 }
4654
4655 if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
4656 }
4657
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 }
4668
4669 // ***
4670 // *** 3. Write Additionals
4671 // ***
4672 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4673 if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
4674 {
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");
4678 }
4679
4680 return(responseptr);
4681 }
4682
4683// AuthRecord *our is our Resource Record
4684// CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
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
4688mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt)
4689 {
4690 mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
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
4704
4705 LogMsg("CompareRData ERROR: Invalid state");
4706 return(-1);
4707 }
4708
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.
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
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
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
4718mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master)
4719 {
4720 const AuthRecord *r1;
4721 for (r1 = m->ResourceRecords; r1; r1=r1->next)
4722 {
4723 if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4724 {
4725 const AuthRecord *r2 = r1;
4726 while (r2->DependentOn) r2 = r2->DependentOn;
4727 if (r2 == master) return(mDNStrue);
4728 }
4729 }
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);
4740 }
4741
4742// Find the canonical RRSet pointer for this RR received in a packet.
4743// If we find any identical AuthRecord in our authoritative list, then follow its RRSet
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
4746mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr)
4747 {
4748 const AuthRecord *rr;
4749 for (rr = m->ResourceRecords; rr; rr=rr->next)
4750 {
4751 if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
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.
4769mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr)
4770 {
4771 const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
4772
4773 // If not supposed to be unique, not a conflict
4774 if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
4775
4776 // If a dependent record, not a conflict
4777 if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
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,
4790 DNSQuestion *q, AuthRecord *our)
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 {
4798 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
4799 if (!ptr) break;
4800 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
4801 {
4802 FoundUpdate = mDNStrue;
4803 if (PacketRRConflict(m, our, &m->rec.r))
4804 {
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);
4808 if (result > 0)
4809 debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4810 else if (result < 0)
4811 {
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;
4815 }
4816 }
4817 }
4818 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4819 }
4820 if (!FoundUpdate)
4821 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4822exit:
4823 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4824 }
4825
4826mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr)
4827 {
4828 mDNSu32 slot = HashSlot(pktrr->name);
4829 CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
4830 CacheRecord *rr;
4831 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4832 if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
4833 return(rr);
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,
4838 const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast,
4839 DNSMessage *const response)
4840 {
4841 mDNSBool FromLocalSubnet = AddressIsLocalSubnet(m, InterfaceID, srcaddr);
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;
4853 int i;
4854
4855 // ***
4856 // *** 1. Parse Question Section and mark potential answers
4857 // ***
4858 for (i=0; i<query->h.numQuestions; i++) // For each question...
4859 {
4860 mDNSBool QuestionNeedsMulticastResponse;
4861 int NumAnswersForThisQuestion = 0;
4862 DNSQuestion pktq, *q;
4863 ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question...
4864 if (!ptr) goto exit;
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;
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
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");
4885 m->CurrentRecord = m->ResourceRecords;
4886 while (m->CurrentRecord)
4887 {
4888 rr = m->CurrentRecord;
4889 m->CurrentRecord = rr->next;
4890 if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
4891 {
4892 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
4893 ResolveSimultaneousProbe(m, query, end, &pktq, rr);
4894 else if (ResourceRecordIsValidAnswer(rr))
4895 {
4896 NumAnswersForThisQuestion++;
4897 // Notes:
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)
4900 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
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))
4905 {
4906 // We only mark this question for sending if it is at least one second since the last time we multicast it
4907 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
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 }
4913 else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
4914 }
4915 }
4916 }
4917
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
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 {
4927 const mDNSu32 slot = HashSlot(&pktq.qname);
4928 CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
4929 CacheRecord *rr;
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
4933 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
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)
4956 if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
4957 if (!q->InterfaceID || q->InterfaceID == InterfaceID)
4958 if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
4959 if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
4960 { *dqp = q; dqp = &q->NextInDQList; }
4961 }
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...
4969 AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list
4970
4971 // ***
4972 // *** 3. Add additional records
4973 // ***
4974 AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
4975
4976 // ***
4977 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
4978 // ***
4979 for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section...
4980 {
4981 // Get the record...
4982 AuthRecord *rr;
4983 CacheRecord *ourcacherr;
4984 ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
4985 if (!ptr) goto exit;
4986
4987 // See if this Known-Answer suppresses any of our currently planned answers
4988 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4989 if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
4990 { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
4991
4992 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
4993 for (rr=m->ResourceRecords; rr; rr=rr->next)
4994 {
4995 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
4996 if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr))
4997 {
4998 if (srcaddr->type == mDNSAddrType_IPv4)
4999 {
5000 if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr;
5001 }
5002 else if (srcaddr->type == mDNSAddrType_IPv6)
5003 {
5004 if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
5005 }
5006 if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
5007 {
5008 rr->ImmedAnswer = mDNSNULL;
5009 rr->ImmedUnicast = mDNSfalse;
5010#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5011 LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr));
5012#endif
5013 }
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).
5019 ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
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;
5033 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
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;
5045 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
5046 { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
5047 else dqp = &q->NextInDQList;
5048 }
5049 }
5050 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
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 {
5065 if (rr->NR_AnswerTo)
5066 {
5067 mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response
5068 mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response)
5069
5070 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
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 }
5079
5080 // If the client insists on a multicast response, then we'd better send one
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;
5084
5085 if (SendMulticastResponse || SendUnicastResponse)
5086 {
5087#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5088 rr->ImmedAnswerMarkTime = m->timenow;
5089#endif
5090 m->NextScheduledResponse = m->timenow;
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)
5093 rr->ImmedAnswer = mDNSInterfaceMark;
5094 else
5095 {
5096 rr->ImmedAnswer = InterfaceID; // Record interface to send it on
5097 if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue;
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 }
5108 }
5109 }
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
5116 }
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 }
5126 }
5127
5128 // ***
5129 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5130 // ***
5131 if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50))
5132 {
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
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,
5148 // because if that happens we'll fail the Bonjour Conformance Test.
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;
5152 if (m->SuppressSending == 0) m->SuppressSending = 1;
5153#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5154 if (oldss && delayresponse)
5155 LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow);
5156#endif
5157 }
5158
5159 // ***
5160 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5161 // ***
5162 if (SendLegacyResponse)
5163 responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
5164
5165exit:
5166 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5167
5168 // ***
5169 // *** 9. Finally, clear our link chains ready for use next time
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
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",
5196 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
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",
5207 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
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",
5228 rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
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
5253 return(responseptr);
5254 }
5255
5256mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
5257 const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5258 const mDNSInterfaceID InterfaceID)
5259 {
5260 mDNSu8 *responseend = mDNSNULL;
5261 mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr);
5262
5263 if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))
5264 {
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)",
5266 srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
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");
5271 return;
5272 }
5273
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",
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,
5282 (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg);
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",
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",
5290 srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type);
5291 mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL);
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,
5299 const DNSMessage *const response, const mDNSu8 *end,
5300 const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5301 const mDNSInterfaceID InterfaceID)
5302 {
5303 int i;
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;
5312 CacheRecord **cfp = &CacheFlushRecords;
5313
5314 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
5315 // to guard against spoof responses, then the only credible protection against that is cryptographic
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
5319 (void)srcaddr; // Currently used only for display in debugging message
5320 (void)srcport;
5321 (void)dstport;
5322
5323 verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5324 srcaddr, dstaddr, InterfaceID,
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,",
5328 response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
5329
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))
5332 {
5333 if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))
5334 return;
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 }
5340
5341 for (i = 0; i < totalrecords && ptr && ptr < end; i++)
5342 {
5343 const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd);
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
5346
5347 // 1. Check that this packet resource record does not conflict with any of ours
5348 if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
5349 m->CurrentRecord = m->ResourceRecords;
5350 while (m->CurrentRecord)
5351 {
5352 AuthRecord *rr = m->CurrentRecord;
5353 m->CurrentRecord = rr->next;
5354 if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match...
5355 {
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))
5358 {
5359 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
5360 if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
5361 {
5362 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
5363 if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; }
5364 }
5365 else
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 }
5370 }
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))
5373 {
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));
5376
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;
5380
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)
5386 {
5387 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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 {
5397 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
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",
5407 rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5408 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
5409 }
5410 else
5411 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5412 rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5413 }
5414 }
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; }
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 {
5428 const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
5429 CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
5430 CacheRecord *rr;
5431 // 2a. Check if this packet resource record is already in our cache
5432 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5433 {
5434 // If we found this exact resource record, refresh its TTL
5435 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
5436 {
5437 if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5438 verbosedebugf("Found record size %5d interface %p already in cache: %s",
5439 m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
5440 rr->TimeRcvd = m->timenow;
5441
5442 if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
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)
5446 { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
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++;
5453 rr->resrec.RecordType = m->rec.r.resrec.RecordType;
5454 }
5455 }
5456
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)
5468 {
5469 rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl;
5470 rr->UnansweredQueries = 0;
5471 rr->MPUnansweredQ = 0;
5472 rr->MPUnansweredKA = 0;
5473 rr->MPExpectingKA = mDNSfalse;
5474 SetNextCacheCheckTime(m, rr);
5475 break;
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;
5486 SetNextCacheCheckTime(m, rr);
5487 break;
5488 }
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)
5494 if (!rr && m->rec.r.resrec.rroriginalttl > 0)
5495 {
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);
5500 else
5501 {
5502 RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer
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
5506 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
5507 { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
5508 // If this is an oversized record with external storage allocated, copy rdata to external storage
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);
5511 if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5512 mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength);
5513 rr->next = mDNSNULL; // Clear 'next' pointer
5514 *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list
5515 cg->rrcache_tail = &(rr->next); // Advance tail pointer
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
5519 rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot);
5520 CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us
5521 }
5522 }
5523 }
5524 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5525 }
5526
5527exit:
5528 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5529
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
5532 while (CacheFlushRecords != (CacheRecord*)1)
5533 {
5534 CacheRecord *r1 = CacheFlushRecords, *r2;
5535 const mDNSu32 slot = HashSlot(r1->resrec.name);
5536 CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
5537 CacheFlushRecords = CacheFlushRecords->NextInCFList;
5538 r1->NextInCFList = mDNSNULL;
5539 for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
5540 if (SameResourceRecordSignature(&r1->resrec, &r2->resrec))
5541 if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond)
5542 {
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;
5568 SetNextCacheCheckTime(m, r2);
5569 }
5570 if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to
5571 {
5572 // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
5573 r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
5574 if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
5575 }
5576 }
5577 }
5578
5579mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
5580 const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
5581 const mDNSInterfaceID InterfaceID)
5582 {
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;
5588 const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
5589
5590#ifndef UNICAST_DISABLED
5591 if (srcport.NotAnInteger == NATPMPPort.NotAnInteger)
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);
5601 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
5602 ptr = (mDNSu8 *)&msg->h.numQuestions;
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]);
5607
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; }
5613
5614 mDNS_Lock(m);
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
5621 if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
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);
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// ***************************************************************************
5636#if COMPILER_LIKES_PRAGMA_MARK
5637#pragma mark -
5638#pragma mark -
5639#pragma mark - Searcher Functions
5640#endif
5641
5642#define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5643
5644mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
5645 {
5646 DNSQuestion *q;
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.
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,
5653 SameQTarget(q, question) && // and same unicast/multicast target settings
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
5658 return(q);
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
5664mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question)
5665 {
5666 DNSQuestion *q;
5667 for (q = m->Questions; q; q=q->next) // Scan our list of questions
5668 if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate
5669 {
5670 q->ThisQInterval = question->ThisQInterval;
5671 q->RequestUnicast = question->RequestUnicast;
5672 q->LastQTime = question->LastQTime;
5673 q->RecentAnswerPkts = 0;
5674 q->DuplicateOf = FindDuplicateQuestion(m, q);
5675 q->LastQTxTime = question->LastQTxTime;
5676 SetNextQueryTime(m,q);
5677 }
5678 }
5679
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
5683mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
5684 {
5685 if (question->Target.type && !ValidQuestionTarget(question))
5686 {
5687 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5688 question->Target.type, mDNSVal16(question->TargetPort));
5689 question->Target.type = mDNSAddrType_None;
5690 }
5691
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'
5700 // then we do a multicast query on that interface, even for unicast domains.
5701 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5702 question->uDNS_info.id = zeroID;
5703 else return uDNS_StartQuery(m, question);
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
5710 if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated
5711 return(mStatus_NoCache);
5712 else
5713 {
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;
5717 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5718 while (*q && *q != question) q=&(*q)->next;
5719
5720 if (*q)
5721 {
5722 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5723 question->qname.c, DNSTypeName(question->qtype));
5724 return(mStatus_AlreadyRegistered);
5725 }
5726
5727 // If this question is referencing a specific interface, make sure it exists
5728 if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly)
5729 {
5730 NetworkInterfaceInfo *intf;
5731 for (intf = m->HostInterfaces; intf; intf = intf->next)
5732 if (intf->InterfaceID == question->InterfaceID) break;
5733 if (!intf)
5734 {
5735 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID);
5736 return(mStatus_BadInterfaceErr);
5737 }
5738 }
5739
5740 if (!ValidateDomainName(&question->qname))
5741 {
5742 LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5743 return(mStatus_Invalid);
5744 }
5745
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.
5750 if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
5751
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;
5765 for (i=0; i<DupSuppressInfoSize; i++)
5766 question->DupSuppress[i].InterfaceID = mDNSNULL;
5767 // question->InterfaceID must be already set by caller
5768 question->SendQNow = mDNSNULL;
5769 question->SendOnAll = mDNSfalse;
5770 question->LastQTxTime = m->timenow;
5771
5772 if (!question->DuplicateOf)
5773 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
5774 question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question);
5775 else
5776 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5777 question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf);
5778
5779 *q = question;
5780 if (question->InterfaceID == mDNSInterface_LocalOnly)
5781 {
5782 if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
5783 }
5784 else
5785 {
5786 if (!m->NewQuestions) m->NewQuestions = question;
5787 SetNextQueryTime(m,question);
5788 }
5789
5790 return(mStatus_NoError);
5791 }
5792 }
5793
5794mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
5795 {
5796 const mDNSu32 slot = HashSlot(&question->qname);
5797 CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
5798 CacheRecord *rr;
5799 DNSQuestion **q = &m->Questions;
5800
5801 if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question);
5802
5803 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5804 while (*q && *q != question) q=&(*q)->next;
5805 if (*q) *q = (*q)->next;
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 }
5813
5814 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5815 UpdateQuestionDuplicates(m, question);
5816 // But don't trash ThisQInterval until afterwards.
5817 question->ThisQInterval = -1;
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.
5821 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
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;
5829 verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q);
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,
5836 // bump its pointer forward one question.
5837 if (m->CurrentQuestion == question)
5838 {
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;
5842 }
5843
5844 if (m->NewQuestions == question)
5845 {
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;
5849 }
5850
5851 if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
5852
5853 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5854 question->next = mDNSNULL;
5855 return(mStatus_NoError);
5856 }
5857
5858mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
5859 {
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);
5881 mDNS_Unlock(m);
5882 return(status);
5883 }
5884
5885mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
5886 {
5887 mStatus status = mStatus_BadReferenceErr;
5888 CacheRecord *cr;
5889 mDNS_Lock(m);
5890 cr = FindIdenticalRecordInCache(m, rr);
5891 if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
5892 mDNS_Unlock(m);
5893 return(status);
5894 }
5895
5896mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
5897 const domainname *const srv, const domainname *const domain,
5898 const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context)
5899 {
5900 question->InterfaceID = InterfaceID;
5901 question->Target = zeroAddr;
5902 question->qtype = kDNSType_PTR;
5903 question->qclass = kDNSClass_IN;
5904 question->LongLived = mDNSfalse;
5905 question->ExpectUnique = mDNSfalse;
5906 question->ForceMCast = ForceMCast;
5907 question->QuestionCallback = Callback;
5908 question->QuestionContext = Context;
5909 if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
5910
5911#ifndef UNICAST_DISABLED
5912 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5913 {
5914 question->LongLived = mDNSfalse;
5915 question->uDNS_info.id = zeroID;
5916 return(mDNS_StartQuery(m, question));
5917 }
5918 else
5919 {
5920 mStatus status;
5921 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5922 mDNS_Lock(m);
5923 question->LongLived = mDNStrue;
5924 status = uDNS_StartQuery(m, question);
5925 mDNS_Unlock(m);
5926 return(status);
5927 }
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);
5939 }
5940
5941mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5942 {
5943 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5944 mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger);
5945 if (!AddRecord) return;
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;
5954 query->qAv4.InterfaceID = answer->InterfaceID;
5955 AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
5956 query->qAv6.InterfaceID = answer->InterfaceID;
5957 AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
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);
5961 }
5962 // If this is not our first answer, only re-issue the address query if the target host name has changed
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 {
5966 mDNS_StopQuery(m, &query->qAv4);
5967 if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6);
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;
5981 AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
5982 query->qAv6.InterfaceID = answer->InterfaceID;
5983 AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
5984 }
5985 debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c);
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);
5989 }
5990 else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
5991 {
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,
5995 mDNSVal16(answer->rdata->u.srv.port));
5996 query->ServiceInfoQueryCallback(m, query);
5997 }
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.
6000 }
6001
6002mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6003 {
6004 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
6005 if (!AddRecord) return;
6006 if (answer->rrtype != kDNSType_TXT) return;
6007 if (answer->rdlength > sizeof(query->info->TXTinfo)) return;
6008
6009 query->GotTXT = mDNStrue;
6010 query->info->TXTlen = answer->rdlength;
6011 query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero
6012 mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength);
6013
6014 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
6015
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 }
6025 }
6026
6027mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6028 {
6029 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
6030 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
6031 if (!AddRecord) return;
6032
6033 if (answer->rrtype == kDNSType_A)
6034 {
6035 query->info->ip.type = mDNSAddrType_IPv4;
6036 query->info->ip.ip.v4 = answer->rdata->u.ipv4;
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 {
6045 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype));
6046 return;
6047 }
6048
6049 query->GotADD = mDNStrue;
6050 query->info->InterfaceID = answer->InterfaceID;
6051
6052 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
6053
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)
6057 {
6058 if (++query->Answers >= 100)
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 }
6065 query->ServiceInfoQueryCallback(m, query);
6066 }
6067 }
6068
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
6071// Each time the Callback is invoked, the remainder of the fields will have been filled in
6072// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
6073mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
6074 ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context)
6075 {
6076 mStatus status;
6077 mDNS_Lock(m);
6078
6079 query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6080 query->qSRV.InterfaceID = info->InterfaceID;
6081 query->qSRV.Target = zeroAddr;
6082 AssignDomainName(&query->qSRV.qname, &info->name);
6083 query->qSRV.qtype = kDNSType_SRV;
6084 query->qSRV.qclass = kDNSClass_IN;
6085 query->qSRV.LongLived = mDNSfalse;
6086 query->qSRV.ExpectUnique = mDNStrue;
6087 query->qSRV.ForceMCast = mDNSfalse;
6088 query->qSRV.QuestionCallback = FoundServiceInfoSRV;
6089 query->qSRV.QuestionContext = query;
6090
6091 query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6092 query->qTXT.InterfaceID = info->InterfaceID;
6093 query->qTXT.Target = zeroAddr;
6094 AssignDomainName(&query->qTXT.qname, &info->name);
6095 query->qTXT.qtype = kDNSType_TXT;
6096 query->qTXT.qclass = kDNSClass_IN;
6097 query->qTXT.LongLived = mDNSfalse;
6098 query->qTXT.ExpectUnique = mDNStrue;
6099 query->qTXT.ForceMCast = mDNSfalse;
6100 query->qTXT.QuestionCallback = FoundServiceInfoTXT;
6101 query->qTXT.QuestionContext = query;
6102
6103 query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6104 query->qAv4.InterfaceID = info->InterfaceID;
6105 query->qAv4.Target = zeroAddr;
6106 query->qAv4.qname.c[0] = 0;
6107 query->qAv4.qtype = kDNSType_A;
6108 query->qAv4.qclass = kDNSClass_IN;
6109 query->qAv4.LongLived = mDNSfalse;
6110 query->qAv4.ExpectUnique = mDNStrue;
6111 query->qAv4.ForceMCast = mDNSfalse;
6112 query->qAv4.QuestionCallback = FoundServiceInfo;
6113 query->qAv4.QuestionContext = query;
6114
6115 query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6116 query->qAv6.InterfaceID = info->InterfaceID;
6117 query->qAv6.Target = zeroAddr;
6118 query->qAv6.qname.c[0] = 0;
6119 query->qAv6.qtype = kDNSType_AAAA;
6120 query->qAv6.qclass = kDNSClass_IN;
6121 query->qAv6.LongLived = mDNSfalse;
6122 query->qAv6.ExpectUnique = mDNStrue;
6123 query->qAv6.ForceMCast = mDNSfalse;
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;
6135
6136// info->name = Must already be set up by client
6137// info->interface = Must already be set up by client
6138 info->ip = zeroAddr;
6139 info->port = zeroIPPort;
6140 info->TXTlen = 0;
6141
6142 // We use mDNS_StartQuery_internal here because we're already holding the lock
6143 status = mDNS_StartQuery_internal(m, &query->qSRV);
6144 if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT);
6145 if (status != mStatus_NoError) mDNS_StopResolveService(m, query);
6146
6147 mDNS_Unlock(m);
6148 return(status);
6149 }
6150
6151mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q)
6152 {
6153 mDNS_Lock(m);
6154 // We use mDNS_StopQuery_internal here because we're already holding the lock
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);
6159 mDNS_Unlock(m);
6160 }
6161
6162mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
6163 const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
6164 {
6165 question->InterfaceID = InterfaceID;
6166 question->Target = zeroAddr;
6167 question->qtype = kDNSType_PTR;
6168 question->qclass = kDNSClass_IN;
6169 question->LongLived = mDNSfalse;
6170 question->ExpectUnique = mDNSfalse;
6171 question->ForceMCast = mDNSfalse;
6172 question->QuestionCallback = Callback;
6173 question->QuestionContext = Context;
6174 if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
6175 if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
6176 if (!dom) dom = &localdomain;
6177 if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr);
6178 return(mDNS_StartQuery(m, question));
6179 }
6180
6181// ***************************************************************************
6182#if COMPILER_LIKES_PRAGMA_MARK
6183#pragma mark -
6184#pragma mark - Responder Functions
6185#endif
6186
6187mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
6188 {
6189 mStatus status;
6190 mDNS_Lock(m);
6191 status = mDNS_Register_internal(m, rr);
6192 mDNS_Unlock(m);
6193 return(status);
6194 }
6195
6196mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
6197 const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback)
6198 {
6199#ifndef UNICAST_DISABLED
6200 mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name));
6201#else
6202 mDNSBool unicast = mDNSfalse;
6203#endif
6204
6205 if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata))
6206 { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); }
6207
6208 mDNS_Lock(m);
6209
6210 // If TTL is unspecified, leave TTL unchanged
6211 if (newttl == 0) newttl = rr->resrec.rroriginalttl;
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
6215 if (!unicast && rr->NewRData)
6216 {
6217 RData *n = rr->NewRData;
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 }
6222
6223 rr->NewRData = newrdata;
6224 rr->newrdlength = newrdlength;
6225 rr->UpdateCallback = Callback;
6226
6227 if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); }
6228
6229 if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
6230 CompleteRDataUpdate(m, rr);
6231 else
6232 {
6233 domainlabel name;
6234 domainname type, domain;
6235 DeconstructServiceName(rr->resrec.name, &name, &type, &domain);
6236 rr->AnnounceCount = InitialAnnounceCount;
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--;
6247 if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval);
6248 if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1);
6249 if (rr->UpdateCredits <= 5)
6250 {
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);
6253 rr->ThisAPInterval *= 4;
6254 rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval;
6255 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name->c, delay, delay > 1 ? "s" : "");
6256 }
6257 rr->resrec.rroriginalttl = newttl;
6258 }
6259
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.
6267mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr)
6268 {
6269 mStatus status;
6270 mDNS_Lock(m);
6271 status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
6272 mDNS_Unlock(m);
6273 return(status);
6274 }
6275
6276mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
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
6286mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
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
6291
6292 // Send dynamic update for non-linklocal IPv4 Addresses
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
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
6304 AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname);
6305 if (set->ip.type == mDNSAddrType_IPv4)
6306 {
6307 set->RR_A.resrec.rrtype = kDNSType_A;
6308 set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4;
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
6329 MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer);
6330 set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
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
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;
6341 AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname);
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
6355mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6356 {
6357 NetworkInterfaceInfo *intf;
6358
6359 // If we still have address records referring to this one, update them
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.
6367 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
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);
6374 }
6375
6376mDNSexport void mDNS_SetFQDN(mDNS *const m)
6377 {
6378 domainname newmname;
6379 NetworkInterfaceInfo *intf;
6380 AuthRecord *rr;
6381 newmname.c[0] = 0;
6382
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; }
6386
6387 mDNS_Lock(m);
6388 AssignDomainName(&m->MulticastHostname, &newmname);
6389
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);
6393
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);
6397
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);
6404 }
6405
6406mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6407 {
6408 (void)rr; // Unused parameter
6409
6410 #if MDNS_DEBUGMSGS
6411 {
6412 char *msg = "Unknown result";
6413 if (result == mStatus_NoError) msg = "Name registered";
6414 else if (result == mStatus_NameConflict) msg = "Name conflict";
6415 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6416 }
6417 #endif
6418
6419 if (result == mStatus_NoError)
6420 {
6421 // Notify the client that the host name is successfully registered
6422 if (m->MainCallback)
6423 {
6424 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
6425 m->MainCallback(m, result);
6426 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6427 }
6428 }
6429 else if (result == mStatus_NameConflict)
6430 {
6431 domainlabel oldlabel = m->hostlabel;
6432
6433 // 1. First give the client callback a chance to pick a new name
6434 if (m->MainCallback)
6435 {
6436 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
6437 m->MainCallback(m, mStatus_NameConflict);
6438 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6439 }
6440
6441 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6442 if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
6443 IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
6444
6445 // 3. Generate the FQDNs from the hostlabel,
6446 // and make sure all SRV records, etc., are updated to reference our new hostname
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)");
6455 }
6456 else
6457 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c);
6458 }
6459
6460mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
6461 {
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 {
6468 if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue;
6469 if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue;
6470 }
6471 }
6472
6473mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSs32 delay)
6474 {
6475 mDNSBool FirstOfType = mDNStrue;
6476 NetworkInterfaceInfo **p = &m->HostInterfaces;
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
6484 mDNS_Lock(m);
6485
6486 // Assume this interface will be active
6487 set->InterfaceActive = mDNStrue;
6488 set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx);
6489 set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx);
6490
6491 while (*p)
6492 {
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 }
6499
6500 // This InterfaceID is already in the list, so mark this interface inactive for now
6501 if ((*p)->InterfaceID == set->InterfaceID)
6502 {
6503 set->InterfaceActive = mDNSfalse;
6504 if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse;
6505 if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue;
6506 if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue;
6507 }
6508
6509 p=&(*p)->next;
6510 }
6511
6512 set->next = mDNSNULL;
6513 *p = set;
6514
6515 if (set->Advertise)
6516 AdvertiseInterface(m, set);
6517
6518 debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip,
6519 set->InterfaceActive ?
6520 "not represented in list; marking active and retriggering queries" :
6521 "already represented in list; marking inactive for now");
6522
6523 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
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.
6527 if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
6528 {
6529 DNSQuestion *q;
6530 AuthRecord *rr;
6531 mDNSs32 initial = InitialQuestionInterval;
6532
6533 // Use a small amount of randomness:
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.
6536 if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
6537
6538 if (delay)
6539 {
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
6543 if (!m->SuppressProbes ||
6544 m->SuppressProbes - (m->timenow + delay) < 0)
6545 m->SuppressProbes = (m->timenow + delay);
6546 }
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
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;
6553 q->RecentAnswerPkts = 0;
6554 SetNextQueryTime(m,q);
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;
6563 rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
6564 rr->AnnounceCount = delay ? (mDNSu8)1 : InitialAnnounceCount;
6565 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
6566 InitializeLastAPTime(m, rr);
6567 }
6568 }
6569
6570 mDNS_Unlock(m);
6571 return(mStatus_NoError);
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.
6577mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
6578 {
6579 NetworkInterfaceInfo **p = &m->HostInterfaces;
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);
6588
6589 // Find this record in our list
6590 while (*p && *p != set) p=&(*p)->next;
6591 if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; }
6592
6593 // Unlink this record from our list
6594 *p = (*p)->next;
6595 set->next = mDNSNULL;
6596
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 {
6613 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6614 set->InterfaceID);
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 {
6627 mDNSu32 slot;
6628 CacheGroup *cg;
6629 CacheRecord *rr;
6630 DNSQuestion *q;
6631 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6632 set->InterfaceID);
6633
6634 // 1. Deactivate any questions specific to this interface
6635 for (q = m->Questions; q; q=q->next)
6636 if (q->InterfaceID == set->InterfaceID)
6637 q->ThisQInterval = 0;
6638
6639 // 2. Flush any cache records received on this interface
6640 revalidate = mDNSfalse; // Don't revalidate if we're flushing the records
6641 FORALL_CACHERECORDS(slot, cg, rr)
6642 if (rr->resrec.InterfaceID == set->InterfaceID)
6643 PurgeCacheResourceRecord(m, rr);
6644 }
6645 }
6646
6647 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6648 if (set->Advertise) DeadvertiseInterface(m, set);
6649
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;
6657 CacheGroup *cg;
6658 CacheRecord *rr;
6659 m->NextCacheCheck = m->timenow;
6660 FORALL_CACHERECORDS(slot, cg, rr)
6661 if (rr->resrec.InterfaceID == set->InterfaceID)
6662 mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect);
6663 }
6664
6665 mDNS_Unlock(m);
6666 }
6667
6668mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6669 {
6670 ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6671 (void)m; // Unused parameter
6672
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";
6679 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6680 }
6681 #endif
6682
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
6686 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6687 if (result == mStatus_NameConflict)
6688 {
6689 sr->Conflict = mDNStrue; // Record that this service set had a conflict
6690 mDNS_DeregisterService(m, sr); // Unlink the records from our list
6691 return;
6692 }
6693
6694 if (result == mStatus_MemFree)
6695 {
6696 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
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 }
6706
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);
6711 }
6712
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
6720// Note:
6721// Name is first label of domain name (any dots in the name are actual dots, not label separators)
6722// Type is service type (e.g. "_ipp._tcp.")
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.)
6726// If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6727// then the default host name (m->MulticastHostname) is automatically used
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,
6731 AuthRecord *SubTypes, mDNSu32 NumSubTypes,
6732 const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
6733 {
6734 mStatus err;
6735 mDNSu32 i;
6736
6737 sr->ServiceCallback = Callback;
6738 sr->ServiceContext = Context;
6739 sr->Extras = mDNSNULL;
6740 sr->NumSubTypes = NumSubTypes;
6741 sr->SubTypes = SubTypes;
6742 sr->Conflict = mDNSfalse;
6743 if (host && host->c[0]) sr->Host = *host;
6744 else sr->Host.c[0] = 0;
6745
6746 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6747 if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
6748
6749 // Initialize the AuthRecord objects to sane values
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);
6754
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
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
6763 if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL)
6764 return(mStatus_BadParamErr);
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);
6767 AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name);
6768
6769 // 1. Set up the ADV record rdata to advertise our service type
6770 AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name);
6771
6772 // 2. Set up the PTR record rdata to point to our service name
6773 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6774 AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name);
6775 sr->RR_PTR.Additional1 = &sr->RR_SRV;
6776 sr->RR_PTR.Additional2 = &sr->RR_TXT;
6777
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 {
6783 domainname st;
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())
6786 AppendDomainName(&st, type);
6787 mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
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);
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;
6798
6799 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6800 if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host);
6801 else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; }
6802
6803 // 4. Set up the TXT record rdata,
6804 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6805 if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0;
6806 else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c)
6807 {
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);
6811 }
6812 sr->RR_TXT.DependentOn = &sr->RR_SRV;
6813
6814#ifndef UNICAST_DISABLED
6815 // If the client has specified an explicit InterfaceID,
6816 // then we do a multicast registration on that interface, even for unicast domains.
6817 if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6818 {
6819 mStatus status;
6820 mDNS_Lock(m);
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.
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
6831 mDNS_Lock(m);
6832 err = mDNS_Register_internal(m, &sr->RR_SRV);
6833 if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
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.
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);
6842
6843 mDNS_Unlock(m);
6844
6845 if (err) mDNS_DeregisterService(m, sr);
6846 return(err);
6847 }
6848
6849mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
6850 ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
6851 {
6852 ExtraResourceRecord **e;
6853 mStatus status;
6854
6855 extra->next = mDNSNULL;
6856 mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
6857 AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name);
6858
6859#ifndef UNICAST_DISABLED
6860 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6861 {
6862 mDNS_Lock(m);
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.
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;
6872 }
6873#endif
6874
6875 mDNS_Lock(m);
6876 e = &sr->Extras;
6877 while (*e) e = &(*e)->next;
6878
6879 if (ttl == 0) ttl = kStandardTTL;
6880
6881 extra->r.DependentOn = &sr->RR_SRV;
6882
6883 debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c);
6884
6885 status = mDNS_Register_internal(m, &extra->r);
6886 if (status == mStatus_NoError) *e = extra;
6887 mDNS_Unlock(m);
6888 return(status);
6889 }
6890
6891mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context)
6892 {
6893 ExtraResourceRecord **e;
6894 mStatus status;
6895
6896 mDNS_Lock(m);
6897 e = &sr->Extras;
6898 while (*e && *e != extra) e = &(*e)->next;
6899 if (!*e)
6900 {
6901 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c);
6902 status = mStatus_BadReferenceErr;
6903 }
6904 else
6905 {
6906 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c);
6907 extra->r.RecordCallback = MemFreeCallback;
6908 extra->r.RecordContext = Context;
6909 *e = (*e)->next;
6910#ifndef UNICAST_DISABLED
6911 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6912 status = uDNS_DeregisterRecord(m, &extra->r);
6913 else
6914#endif
6915 status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal);
6916 }
6917 mDNS_Unlock(m);
6918 return(status);
6919 }
6920
6921mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
6922 {
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;
6926 domainname type, domain;
6927 domainname *host = mDNSNULL;
6928 ExtraResourceRecord *extras = sr->Extras;
6929 mStatus err;
6930
6931 DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain);
6932 if (!newname)
6933 {
6934 name2 = name1;
6935 IncrementLabelSuffix(&name2, mDNStrue);
6936 newname = &name2;
6937 }
6938 LogMsg("Service \"%##s\" renamed to \"%#s\"", sr->RR_SRV.resrec.name->c, newname->c);
6939 if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
6940
6941 err = mDNS_RegisterService(m, sr, newname, &type, &domain,
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);
6945
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
6949 while (!err && extras)
6950 {
6951 ExtraResourceRecord *e = extras;
6952 extras = extras->next;
6953 err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
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.
6962mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
6963 {
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));
6966
6967#ifndef UNICAST_DISABLED
6968 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6969 {
6970 mStatus status;
6971 mDNS_Lock(m);
6972 status = uDNS_DeregisterService(m, sr);
6973 mDNS_Unlock(m);
6974 return(status);
6975 }
6976#endif
6977 if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
6978 {
6979 debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c);
6980 return(mStatus_BadReferenceErr);
6981 }
6982 else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
6983 {
6984 debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
6985 return(mStatus_NoError);
6986 }
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 }
7009
7010 for (i=0; i<sr->NumSubTypes; i++)
7011 mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal);
7012
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 {
7035 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
7036 if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
7037 rr->resrec.rdata->u.srv.priority = 0;
7038 rr->resrec.rdata->u.srv.weight = 0;
7039 rr->resrec.rdata->u.srv.port = zeroIPPort;
7040 if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host);
7041 else rr->HostTarget = mDNStrue;
7042 return(mDNS_Register(m, rr));
7043 }
7044
7045mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
7046 mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
7047 {
7048 mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
7049 if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
7050 if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr);
7051 return(mDNS_Register(m, rr));
7052 }
7053
7054// ***************************************************************************
7055#if COMPILER_LIKES_PRAGMA_MARK
7056#pragma mark -
7057#pragma mark -
7058#pragma mark - Startup and Shutdown
7059#endif
7060
7061mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7062 {
7063 if (storage && numrecords)
7064 {
7065 mDNSu32 i;
7066 debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity));
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
7074mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7075 {
7076 mDNS_Lock(m);
7077 mDNS_GrowCache_internal(m, storage, numrecords);
7078 mDNS_Unlock(m);
7079 }
7080
7081mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
7082 CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
7083 mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
7084 {
7085 mDNSu32 slot;
7086 mDNSs32 timenow;
7087 mStatus result;
7088
7089 if (!rrcachestorage) rrcachesize = 0;
7090
7091 m->p = p;
7092 m->KnownBugs = 0;
7093 m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
7094 m->AdvertiseLocalAddresses = AdvertiseLocalAddresses;
7095 m->mDNSPlatformStatus = mStatus_Waiting;
7096 m->UnicastPort4 = zeroIPPort;
7097 m->UnicastPort6 = zeroIPPort;
7098 m->MainCallback = Callback;
7099 m->MainContext = Context;
7100 m->rec.r.resrec.RecordType = 0;
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
7111 result = mDNSPlatformTimeInit();
7112 if (result != mStatus_NoError) return(result);
7113 m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF);
7114 timenow = mDNS_TimeNow_NoLock(m);
7115
7116 m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
7117 m->timenow_last = timenow;
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;
7126 m->PktNum = 0;
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
7143 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL;
7144
7145 mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
7146
7147 // Fields below only required for mDNS Responder...
7148 m->hostlabel.c[0] = 0;
7149 m->nicelabel.c[0] = 0;
7150 m->MulticastHostname.c[0] = 0;
7151 m->HIHardware.c[0] = 0;
7152 m->HISoftware.c[0] = 0;
7153 m->ResourceRecords = mDNSNULL;
7154 m->DuplicateRecords = mDNSNULL;
7155 m->NewLocalRecords = mDNSNULL;
7156 m->CurrentRecord = mDNSNULL;
7157 m->HostInterfaces = mDNSNULL;
7158 m->ProbeFailTime = 0;
7159 m->NumFailedProbes = 0;
7160 m->SuppressProbes = 0;
7161
7162#ifndef UNICAST_DISABLED
7163 uDNS_Init(m);
7164 m->SuppressStdPort53Queries = 0;
7165#endif
7166 result = mDNSPlatformInit(m);
7167
7168 return(result);
7169 }
7170
7171mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
7172 {
7173 m->mDNSPlatformStatus = result;
7174 if (m->MainCallback)
7175 {
7176 mDNS_Lock(m);
7177 m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
7178 m->MainCallback(m, mStatus_NoError);
7179 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
7180 mDNS_Unlock(m);
7181 }
7182 }
7183
7184mDNSexport void mDNS_Close(mDNS *const m)
7185 {
7186 mDNSu32 rrcache_active = 0;
7187 mDNSu32 rrcache_totalused = 0;
7188 mDNSu32 slot;
7189 NetworkInterfaceInfo *intf;
7190 mDNS_Lock(m);
7191
7192 m->mDNS_shutdown = mDNStrue;
7193
7194#ifndef UNICAST_DISABLED
7195 uDNS_Close(m);
7196#endif
7197 rrcache_totalused = m->rrcache_totalused;
7198 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
7199 {
7200 while(m->rrcache_hash[slot])
7201 {
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]);
7212 }
7213 }
7214 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active);
7215 if (rrcache_active != m->rrcache_active)
7216 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active);
7217
7218 for (intf = m->HostInterfaces; intf; intf = intf->next)
7219 if (intf->Advertise)
7220 DeadvertiseInterface(m, intf);
7221
7222 // Make sure there are nothing but deregistering records remaining in the list
7223 if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
7224 m->CurrentRecord = m->ResourceRecords;
7225 while (m->CurrentRecord)
7226 {
7227 AuthRecord *rr = m->CurrentRecord;
7228 if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
7229 {
7230 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c);
7231 mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
7232 }
7233 else
7234 m->CurrentRecord = rr->next;
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
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));
7244
7245 mDNS_Unlock(m);
7246 debugf("mDNS_Close: mDNSPlatformClose");
7247 mDNSPlatformClose(m);
7248 debugf("mDNS_Close: done");
7249 }