xref: /titanic_44/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c (revision 4b22b9337f359bfd063322244f5336cc7c6ffcfa)
1*4b22b933Srs200217 /* -*- Mode: C; tab-width: 4 -*-
2*4b22b933Srs200217  *
3*4b22b933Srs200217  * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
4*4b22b933Srs200217  *
5*4b22b933Srs200217  * Licensed under the Apache License, Version 2.0 (the "License");
6*4b22b933Srs200217  * you may not use this file except in compliance with the License.
7*4b22b933Srs200217  * You may obtain a copy of the License at
8*4b22b933Srs200217  *
9*4b22b933Srs200217  *     http://www.apache.org/licenses/LICENSE-2.0
10*4b22b933Srs200217  *
11*4b22b933Srs200217  * Unless required by applicable law or agreed to in writing, software
12*4b22b933Srs200217  * distributed under the License is distributed on an "AS IS" BASIS,
13*4b22b933Srs200217  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*4b22b933Srs200217  * See the License for the specific language governing permissions and
15*4b22b933Srs200217  * limitations under the License.
16*4b22b933Srs200217  *
17*4b22b933Srs200217  * This code is completely 100% portable C. It does not depend on any external header files
18*4b22b933Srs200217  * from outside the mDNS project -- all the types it expects to find are defined right here.
19*4b22b933Srs200217  *
20*4b22b933Srs200217  * The previous point is very important: This file does not depend on any external
21*4b22b933Srs200217  * header files. It should complile on *any* platform that has a C compiler, without
22*4b22b933Srs200217  * making *any* assumptions about availability of so-called "standard" C functions,
23*4b22b933Srs200217  * routines, or types (which may or may not be present on any given platform).
24*4b22b933Srs200217 
25*4b22b933Srs200217  * Formatting notes:
26*4b22b933Srs200217  * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27*4b22b933Srs200217  * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28*4b22b933Srs200217  * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29*4b22b933Srs200217  * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30*4b22b933Srs200217  * therefore common sense dictates that if they are part of a compound statement then they
31*4b22b933Srs200217  * should be indented to the same level as everything else in that compound statement.
32*4b22b933Srs200217  * Indenting curly braces at the same level as the "if" implies that curly braces are
33*4b22b933Srs200217  * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34*4b22b933Srs200217  * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35*4b22b933Srs200217  * understand why variable y is not of type "char*" just proves the point that poor code
36*4b22b933Srs200217  * layout leads people to unfortunate misunderstandings about how the C language really works.)
37*4b22b933Srs200217 
38*4b22b933Srs200217     Change History (most recent first):
39*4b22b933Srs200217 
40*4b22b933Srs200217 $Log: mDNS.c,v $
41*4b22b933Srs200217 Revision 1.537.2.1  2006/08/29 06:24:22  cheshire
42*4b22b933Srs200217 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
43*4b22b933Srs200217 
44*4b22b933Srs200217 Revision 1.537  2006/03/19 02:00:07  cheshire
45*4b22b933Srs200217 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
46*4b22b933Srs200217 
47*4b22b933Srs200217 Revision 1.536  2006/03/08 23:29:53  cheshire
48*4b22b933Srs200217 <rdar://problem/4468716> Improve "Service Renamed" log message
49*4b22b933Srs200217 
50*4b22b933Srs200217 Revision 1.535  2006/03/02 20:41:17  cheshire
51*4b22b933Srs200217 <rdar://problem/4111464> After record update, old record sometimes remains in cache
52*4b22b933Srs200217 Minor code tidying and comments to reduce the risk of similar programming errors in future
53*4b22b933Srs200217 
54*4b22b933Srs200217 Revision 1.534  2006/03/02 03:25:46  cheshire
55*4b22b933Srs200217 <rdar://problem/4111464> After record update, old record sometimes remains in cache
56*4b22b933Srs200217 Code to harmonize RRSet TTLs was inadvertently rescuing expiring records
57*4b22b933Srs200217 
58*4b22b933Srs200217 Revision 1.533  2006/02/26 00:54:41  cheshire
59*4b22b933Srs200217 Fixes to avoid code generation warning/error on FreeBSD 7
60*4b22b933Srs200217 
61*4b22b933Srs200217 Revision 1.532  2005/12/02 20:24:36  cheshire
62*4b22b933Srs200217 <rdar://problem/4363209> Adjust cutoff time for KA list by one second
63*4b22b933Srs200217 
64*4b22b933Srs200217 Revision 1.531  2005/12/02 19:05:42  cheshire
65*4b22b933Srs200217 Tidy up constants
66*4b22b933Srs200217 
67*4b22b933Srs200217 Revision 1.530  2005/11/07 01:49:48  cheshire
68*4b22b933Srs200217 For consistency, use NonZeroTime() function instead of ?: expression
69*4b22b933Srs200217 
70*4b22b933Srs200217 Revision 1.529  2005/10/25 23:42:24  cheshire
71*4b22b933Srs200217 <rdar://problem/4316057> Error in ResolveSimultaneousProbe() when type or class don't match
72*4b22b933Srs200217 Changed switch statement to an "if"
73*4b22b933Srs200217 
74*4b22b933Srs200217 Revision 1.528  2005/10/25 23:34:22  cheshire
75*4b22b933Srs200217 <rdar://problem/4316048> RequireGoodbye state not set/respected sometimes when machine going to sleep
76*4b22b933Srs200217 
77*4b22b933Srs200217 Revision 1.527  2005/10/25 22:43:59  cheshire
78*4b22b933Srs200217 Add clarifying comments
79*4b22b933Srs200217 
80*4b22b933Srs200217 Revision 1.526  2005/10/20 00:10:33  cheshire
81*4b22b933Srs200217 <rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
82*4b22b933Srs200217 
83*4b22b933Srs200217 Revision 1.525  2005/09/24 00:47:17  cheshire
84*4b22b933Srs200217 Fix comment typos
85*4b22b933Srs200217 
86*4b22b933Srs200217 Revision 1.524  2005/09/16 21:06:49  cheshire
87*4b22b933Srs200217 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
88*4b22b933Srs200217 
89*4b22b933Srs200217 Revision 1.523  2005/03/21 00:33:51  shersche
90*4b22b933Srs200217 <rdar://problem/4021486> Fix build warnings on Win32 platform
91*4b22b933Srs200217 
92*4b22b933Srs200217 Revision 1.522  2005/03/04 21:48:12  cheshire
93*4b22b933Srs200217 <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity
94*4b22b933Srs200217 
95*4b22b933Srs200217 Revision 1.521  2005/02/25 04:21:00  cheshire
96*4b22b933Srs200217 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
97*4b22b933Srs200217 
98*4b22b933Srs200217 Revision 1.520  2005/02/16 01:14:11  cheshire
99*4b22b933Srs200217 Convert RR Cache LogOperation() calls to debugf()
100*4b22b933Srs200217 
101*4b22b933Srs200217 Revision 1.519  2005/02/15 01:57:20  cheshire
102*4b22b933Srs200217 When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
103*4b22b933Srs200217 
104*4b22b933Srs200217 Revision 1.518  2005/02/10 22:35:17  cheshire
105*4b22b933Srs200217 <rdar://problem/3727944> Update name
106*4b22b933Srs200217 
107*4b22b933Srs200217 Revision 1.517  2005/02/03 00:21:21  cheshire
108*4b22b933Srs200217 Update comments about BIND named and zero-length TXT records
109*4b22b933Srs200217 
110*4b22b933Srs200217 Revision 1.516  2005/01/28 06:06:32  cheshire
111*4b22b933Srs200217 Update comment
112*4b22b933Srs200217 
113*4b22b933Srs200217 Revision 1.515  2005/01/27 00:21:49  cheshire
114*4b22b933Srs200217 <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
115*4b22b933Srs200217 
116*4b22b933Srs200217 Revision 1.514  2005/01/21 01:33:45  cheshire
117*4b22b933Srs200217 <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
118*4b22b933Srs200217 
119*4b22b933Srs200217 Revision 1.513  2005/01/21 00:07:54  cheshire
120*4b22b933Srs200217 <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict
121*4b22b933Srs200217 
122*4b22b933Srs200217 Revision 1.512  2005/01/20 00:37:45  cheshire
123*4b22b933Srs200217 <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
124*4b22b933Srs200217 Take care not to recycle records while they are on the CacheFlushRecords list
125*4b22b933Srs200217 
126*4b22b933Srs200217 Revision 1.511  2005/01/19 22:48:53  cheshire
127*4b22b933Srs200217 <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
128*4b22b933Srs200217 
129*4b22b933Srs200217 Revision 1.510  2005/01/19 03:12:45  cheshire
130*4b22b933Srs200217 Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
131*4b22b933Srs200217 
132*4b22b933Srs200217 Revision 1.509  2005/01/19 03:08:49  cheshire
133*4b22b933Srs200217 <rdar://problem/3961051> CPU Spin in mDNSResponder
134*4b22b933Srs200217 Log messages to help catch and report CPU spins
135*4b22b933Srs200217 
136*4b22b933Srs200217 Revision 1.508  2005/01/18 18:56:32  cheshire
137*4b22b933Srs200217 <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
138*4b22b933Srs200217 
139*4b22b933Srs200217 Revision 1.507  2005/01/18 01:12:07  cheshire
140*4b22b933Srs200217 <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
141*4b22b933Srs200217 
142*4b22b933Srs200217 Revision 1.506  2005/01/17 23:28:53  cheshire
143*4b22b933Srs200217 Fix compile error
144*4b22b933Srs200217 
145*4b22b933Srs200217 Revision 1.505  2005/01/11 02:02:56  shersche
146*4b22b933Srs200217 Move variable declaration to the beginning of statement block
147*4b22b933Srs200217 
148*4b22b933Srs200217 Revision 1.504  2004/12/20 20:24:35  cheshire
149*4b22b933Srs200217 <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer
150*4b22b933Srs200217 
151*4b22b933Srs200217 Revision 1.503  2004/12/20 18:41:47  cheshire
152*4b22b933Srs200217 <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space
153*4b22b933Srs200217 
154*4b22b933Srs200217 Revision 1.502  2004/12/20 18:04:08  cheshire
155*4b22b933Srs200217 <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache
156*4b22b933Srs200217 
157*4b22b933Srs200217 Revision 1.501  2004/12/19 23:50:18  cheshire
158*4b22b933Srs200217 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
159*4b22b933Srs200217 Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
160*4b22b933Srs200217 
161*4b22b933Srs200217 Revision 1.500  2004/12/18 03:13:46  cheshire
162*4b22b933Srs200217 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
163*4b22b933Srs200217 
164*4b22b933Srs200217 Revision 1.499  2004/12/17 23:37:45  cheshire
165*4b22b933Srs200217 <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
166*4b22b933Srs200217 (and other repetitive configuration changes)
167*4b22b933Srs200217 
168*4b22b933Srs200217 Revision 1.498  2004/12/17 05:25:46  cheshire
169*4b22b933Srs200217 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
170*4b22b933Srs200217 
171*4b22b933Srs200217 Revision 1.497  2004/12/17 03:20:58  cheshire
172*4b22b933Srs200217 <rdar://problem/3925168> Don't send unicast replies we know will be ignored
173*4b22b933Srs200217 
174*4b22b933Srs200217 Revision 1.496  2004/12/16 22:18:26  cheshire
175*4b22b933Srs200217 Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
176*4b22b933Srs200217 
177*4b22b933Srs200217 Revision 1.495  2004/12/16 21:27:37  ksekar
178*4b22b933Srs200217 Fixed build failures when compiled with verbose debugging messages
179*4b22b933Srs200217 
180*4b22b933Srs200217 Revision 1.494  2004/12/16 20:46:56  cheshire
181*4b22b933Srs200217 Fix compiler warnings
182*4b22b933Srs200217 
183*4b22b933Srs200217 Revision 1.493  2004/12/16 20:13:00  cheshire
184*4b22b933Srs200217 <rdar://problem/3324626> Cache memory management improvements
185*4b22b933Srs200217 
186*4b22b933Srs200217 Revision 1.492  2004/12/16 08:03:24  shersche
187*4b22b933Srs200217 Fix compilation error when UNICAST_DISABLED is set
188*4b22b933Srs200217 
189*4b22b933Srs200217 Revision 1.491  2004/12/11 01:52:11  cheshire
190*4b22b933Srs200217 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
191*4b22b933Srs200217 
192*4b22b933Srs200217 Revision 1.490  2004/12/10 20:06:25  cheshire
193*4b22b933Srs200217 <rdar://problem/3915074> Reduce egregious stack space usage
194*4b22b933Srs200217 Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
195*4b22b933Srs200217 
196*4b22b933Srs200217 Revision 1.489  2004/12/10 20:03:43  cheshire
197*4b22b933Srs200217 <rdar://problem/3915074> Reduce egregious stack space usage
198*4b22b933Srs200217 Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
199*4b22b933Srs200217 
200*4b22b933Srs200217 Revision 1.488  2004/12/10 19:50:41  cheshire
201*4b22b933Srs200217 <rdar://problem/3915074> Reduce egregious stack space usage
202*4b22b933Srs200217 Reduced SendResponses() stack frame from 9K to 176 bytes
203*4b22b933Srs200217 
204*4b22b933Srs200217 Revision 1.487  2004/12/10 19:39:13  cheshire
205*4b22b933Srs200217 <rdar://problem/3915074> Reduce egregious stack space usage
206*4b22b933Srs200217 Reduced SendQueries() stack frame from 18K to 112 bytes
207*4b22b933Srs200217 
208*4b22b933Srs200217 Revision 1.486  2004/12/10 14:16:17  cheshire
209*4b22b933Srs200217 <rdar://problem/3889788> Relax update rate limiting
210*4b22b933Srs200217 We now allow an average rate of ten updates per minute.
211*4b22b933Srs200217 Updates in excess of that are rate limited, but more gently than before.
212*4b22b933Srs200217 
213*4b22b933Srs200217 Revision 1.485  2004/12/10 02:09:24  cheshire
214*4b22b933Srs200217 <rdar://problem/3898376> Modify default TTLs
215*4b22b933Srs200217 
216*4b22b933Srs200217 Revision 1.484  2004/12/09 03:15:40  ksekar
217*4b22b933Srs200217 <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
218*4b22b933Srs200217 
219*4b22b933Srs200217 Revision 1.483  2004/12/07 23:00:14  ksekar
220*4b22b933Srs200217 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
221*4b22b933Srs200217 Call RecordProbeFailure even if there is no record callback
222*4b22b933Srs200217 
223*4b22b933Srs200217 Revision 1.482  2004/12/07 22:49:06  cheshire
224*4b22b933Srs200217 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
225*4b22b933Srs200217 
226*4b22b933Srs200217 Revision 1.481  2004/12/07 21:26:04  ksekar
227*4b22b933Srs200217 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
228*4b22b933Srs200217 
229*4b22b933Srs200217 Revision 1.480  2004/12/07 20:42:33  cheshire
230*4b22b933Srs200217 Add explicit context parameter to mDNS_RemoveRecordFromService()
231*4b22b933Srs200217 
232*4b22b933Srs200217 Revision 1.479  2004/12/07 17:50:49  ksekar
233*4b22b933Srs200217 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
234*4b22b933Srs200217 
235*4b22b933Srs200217 Revision 1.478  2004/12/06 21:15:22  ksekar
236*4b22b933Srs200217 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
237*4b22b933Srs200217 
238*4b22b933Srs200217 Revision 1.477  2004/12/04 02:12:45  cheshire
239*4b22b933Srs200217 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
240*4b22b933Srs200217 
241*4b22b933Srs200217 Revision 1.476  2004/11/29 23:34:31  cheshire
242*4b22b933Srs200217 On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
243*4b22b933Srs200217 is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
244*4b22b933Srs200217 only nudges the time value to 1 if the interval calculation happens to result in the value zero.
245*4b22b933Srs200217 
246*4b22b933Srs200217 Revision 1.475  2004/11/29 23:13:31  cheshire
247*4b22b933Srs200217 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
248*4b22b933Srs200217 Additional check: Make sure we don't unnecessarily send packets containing only additionals.
249*4b22b933Srs200217 (This could occur with multi-packet KA lists, if the answer and additionals were marked
250*4b22b933Srs200217 by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
251*4b22b933Srs200217 
252*4b22b933Srs200217 Revision 1.474  2004/11/29 17:18:12  cheshire
253*4b22b933Srs200217 Remove "Unknown DNS packet type" message for update responses
254*4b22b933Srs200217 
255*4b22b933Srs200217 Revision 1.473  2004/11/25 01:57:52  cheshire
256*4b22b933Srs200217 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
257*4b22b933Srs200217 
258*4b22b933Srs200217 Revision 1.472  2004/11/25 01:28:09  cheshire
259*4b22b933Srs200217 <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
260*4b22b933Srs200217 
261*4b22b933Srs200217 Revision 1.471  2004/11/25 01:10:13  cheshire
262*4b22b933Srs200217 Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
263*4b22b933Srs200217 
264*4b22b933Srs200217 Revision 1.470  2004/11/24 21:54:44  cheshire
265*4b22b933Srs200217 <rdar://problem/3894475> mDNSCore not receiving unicast responses properly
266*4b22b933Srs200217 
267*4b22b933Srs200217 Revision 1.469  2004/11/24 04:50:39  cheshire
268*4b22b933Srs200217 Minor tidying
269*4b22b933Srs200217 
270*4b22b933Srs200217 Revision 1.468  2004/11/24 01:47:07  cheshire
271*4b22b933Srs200217 <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
272*4b22b933Srs200217 
273*4b22b933Srs200217 Revision 1.467  2004/11/24 01:41:28  cheshire
274*4b22b933Srs200217 Rename CompleteProbing() to AcknowledgeRecord()
275*4b22b933Srs200217 
276*4b22b933Srs200217 Revision 1.466  2004/11/23 21:08:07  ksekar
277*4b22b933Srs200217 Don't use ID to demux multicast/unicast now that unicast uses random IDs
278*4b22b933Srs200217 
279*4b22b933Srs200217 Revision 1.465  2004/11/15 20:09:21  ksekar
280*4b22b933Srs200217 <rdar://problem/3719050> Wide Area support for Add/Remove record
281*4b22b933Srs200217 
282*4b22b933Srs200217 Revision 1.464  2004/11/03 01:44:36  cheshire
283*4b22b933Srs200217 Update debugging messages
284*4b22b933Srs200217 
285*4b22b933Srs200217 Revision 1.463  2004/10/29 02:38:48  cheshire
286*4b22b933Srs200217 Fix Windows compile errors
287*4b22b933Srs200217 
288*4b22b933Srs200217 Revision 1.462  2004/10/28 19:21:07  cheshire
289*4b22b933Srs200217 Guard against registering interface with zero InterfaceID
290*4b22b933Srs200217 
291*4b22b933Srs200217 Revision 1.461  2004/10/28 19:02:16  cheshire
292*4b22b933Srs200217 Remove \n from LogMsg() call
293*4b22b933Srs200217 
294*4b22b933Srs200217 Revision 1.460  2004/10/28 03:24:40  cheshire
295*4b22b933Srs200217 Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
296*4b22b933Srs200217 
297*4b22b933Srs200217 Revision 1.459  2004/10/26 22:34:37  cheshire
298*4b22b933Srs200217 <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
299*4b22b933Srs200217 
300*4b22b933Srs200217 Revision 1.458  2004/10/26 20:45:28  cheshire
301*4b22b933Srs200217 Show mask in "invalid mask" message
302*4b22b933Srs200217 
303*4b22b933Srs200217 Revision 1.457  2004/10/26 06:28:36  cheshire
304*4b22b933Srs200217 Now that we don't check IP TTL any more, remove associated log message
305*4b22b933Srs200217 
306*4b22b933Srs200217 Revision 1.456  2004/10/26 06:21:42  cheshire
307*4b22b933Srs200217 Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
308*4b22b933Srs200217 
309*4b22b933Srs200217 Revision 1.455  2004/10/26 06:11:40  cheshire
310*4b22b933Srs200217 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
311*4b22b933Srs200217 
312*4b22b933Srs200217 Revision 1.454  2004/10/23 01:16:00  cheshire
313*4b22b933Srs200217 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
314*4b22b933Srs200217 
315*4b22b933Srs200217 Revision 1.453  2004/10/22 20:52:06  ksekar
316*4b22b933Srs200217 <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
317*4b22b933Srs200217 
318*4b22b933Srs200217 Revision 1.452  2004/10/20 01:50:40  cheshire
319*4b22b933Srs200217 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
320*4b22b933Srs200217 Implemented ForceMCast mode for AuthRecords as well as for Questions
321*4b22b933Srs200217 
322*4b22b933Srs200217 Revision 1.451  2004/10/19 21:33:15  cheshire
323*4b22b933Srs200217 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
324*4b22b933Srs200217 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
325*4b22b933Srs200217 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
326*4b22b933Srs200217 
327*4b22b933Srs200217 Revision 1.450  2004/10/19 17:42:59  ksekar
328*4b22b933Srs200217 Fixed compiler warnings for non-debug builds.
329*4b22b933Srs200217 
330*4b22b933Srs200217 Revision 1.449  2004/10/18 22:57:07  cheshire
331*4b22b933Srs200217 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
332*4b22b933Srs200217 
333*4b22b933Srs200217 Revision 1.448  2004/10/16 00:16:59  cheshire
334*4b22b933Srs200217 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
335*4b22b933Srs200217 
336*4b22b933Srs200217 Revision 1.447  2004/10/15 00:51:21  cheshire
337*4b22b933Srs200217 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
338*4b22b933Srs200217 
339*4b22b933Srs200217 Revision 1.446  2004/10/14 00:43:34  cheshire
340*4b22b933Srs200217 <rdar://problem/3815984> Services continue to announce SRV and HINFO
341*4b22b933Srs200217 
342*4b22b933Srs200217 Revision 1.445  2004/10/12 21:07:09  cheshire
343*4b22b933Srs200217 Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
344*4b22b933Srs200217 
345*4b22b933Srs200217 Revision 1.444  2004/10/11 17:54:16  ksekar
346*4b22b933Srs200217 Changed hashtable pointer output from debugf to verbosedebugf.
347*4b22b933Srs200217 
348*4b22b933Srs200217 Revision 1.443  2004/10/10 07:05:45  cheshire
349*4b22b933Srs200217 For consistency, use symbol "localdomain" instead of literal string
350*4b22b933Srs200217 
351*4b22b933Srs200217 Revision 1.442  2004/10/08 20:25:10  cheshire
352*4b22b933Srs200217 Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
353*4b22b933Srs200217 
354*4b22b933Srs200217 Revision 1.441  2004/10/08 03:25:01  ksekar
355*4b22b933Srs200217 <rdar://problem/3831716> domain enumeration should use LLQs
356*4b22b933Srs200217 
357*4b22b933Srs200217 Revision 1.440  2004/10/06 01:44:19  cheshire
358*4b22b933Srs200217 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
359*4b22b933Srs200217 
360*4b22b933Srs200217 Revision 1.439  2004/10/03 23:14:11  cheshire
361*4b22b933Srs200217 Add "mDNSEthAddr" type and "zeroEthAddr" constant
362*4b22b933Srs200217 
363*4b22b933Srs200217 Revision 1.438  2004/09/29 23:07:04  cheshire
364*4b22b933Srs200217 Patch from Pavel Repin to fix compile error on Windows
365*4b22b933Srs200217 
366*4b22b933Srs200217 Revision 1.437  2004/09/28 02:23:50  cheshire
367*4b22b933Srs200217 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
368*4b22b933Srs200217 Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
369*4b22b933Srs200217 For records with the cache flush bit set, defer the decision until the end of the packet
370*4b22b933Srs200217 
371*4b22b933Srs200217 Revision 1.436  2004/09/28 01:27:04  cheshire
372*4b22b933Srs200217 Update incorrect log message
373*4b22b933Srs200217 
374*4b22b933Srs200217 Revision 1.435  2004/09/25 02:41:39  cheshire
375*4b22b933Srs200217 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
376*4b22b933Srs200217 
377*4b22b933Srs200217 Revision 1.434  2004/09/25 02:32:06  cheshire
378*4b22b933Srs200217 Update comments
379*4b22b933Srs200217 
380*4b22b933Srs200217 Revision 1.433  2004/09/25 02:24:27  cheshire
381*4b22b933Srs200217 Removed unused rr->UseCount
382*4b22b933Srs200217 
383*4b22b933Srs200217 Revision 1.432  2004/09/24 21:35:17  cheshire
384*4b22b933Srs200217 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
385*4b22b933Srs200217 TargetPort and TargetQID are allowed to be undefined if no question->Target is set
386*4b22b933Srs200217 
387*4b22b933Srs200217 Revision 1.431  2004/09/24 21:33:12  cheshire
388*4b22b933Srs200217 Adjust comment
389*4b22b933Srs200217 
390*4b22b933Srs200217 Revision 1.430  2004/09/24 02:15:49  cheshire
391*4b22b933Srs200217 <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
392*4b22b933Srs200217 
393*4b22b933Srs200217 Revision 1.429  2004/09/24 00:20:21  cheshire
394*4b22b933Srs200217 <rdar://problem/3483349> Any rrtype is a conflict for unique records
395*4b22b933Srs200217 
396*4b22b933Srs200217 Revision 1.428  2004/09/24 00:12:25  cheshire
397*4b22b933Srs200217 Get rid of unused RRUniqueOrKnownUnique(RR)
398*4b22b933Srs200217 
399*4b22b933Srs200217 Revision 1.427  2004/09/23 20:44:11  cheshire
400*4b22b933Srs200217 <rdar://problem/3813148> Reduce timeout before expiring records on failure
401*4b22b933Srs200217 
402*4b22b933Srs200217 Revision 1.426  2004/09/23 20:21:07  cheshire
403*4b22b933Srs200217 <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
404*4b22b933Srs200217 Associate a unique sequence number with each received packet, and only increment the count of recent answer
405*4b22b933Srs200217 packets if the packet sequence number for this answer record is not one we've already seen and counted.
406*4b22b933Srs200217 
407*4b22b933Srs200217 Revision 1.425  2004/09/23 20:14:38  cheshire
408*4b22b933Srs200217 Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
409*4b22b933Srs200217 
410*4b22b933Srs200217 Revision 1.424  2004/09/23 00:58:36  cheshire
411*4b22b933Srs200217 <rdar://problem/3781269> Rate limiting interferes with updating TXT records
412*4b22b933Srs200217 
413*4b22b933Srs200217 Revision 1.423  2004/09/23 00:50:53  cheshire
414*4b22b933Srs200217 <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
415*4b22b933Srs200217 
416*4b22b933Srs200217 Revision 1.422  2004/09/22 02:34:46  cheshire
417*4b22b933Srs200217 Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
418*4b22b933Srs200217 
419*4b22b933Srs200217 Revision 1.421  2004/09/21 23:29:49  cheshire
420*4b22b933Srs200217 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
421*4b22b933Srs200217 
422*4b22b933Srs200217 Revision 1.420  2004/09/21 23:01:42  cheshire
423*4b22b933Srs200217 Update debugf messages
424*4b22b933Srs200217 
425*4b22b933Srs200217 Revision 1.419  2004/09/21 19:51:14  cheshire
426*4b22b933Srs200217 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
427*4b22b933Srs200217 
428*4b22b933Srs200217 Revision 1.418  2004/09/21 18:40:17  cheshire
429*4b22b933Srs200217 <rdar://problem/3376752> Adjust default record TTLs
430*4b22b933Srs200217 
431*4b22b933Srs200217 Revision 1.417  2004/09/21 17:32:16  cheshire
432*4b22b933Srs200217 <rdar://problem/3809484> Rate limiting imposed too soon
433*4b22b933Srs200217 
434*4b22b933Srs200217 Revision 1.416  2004/09/20 23:52:01  cheshire
435*4b22b933Srs200217 CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
436*4b22b933Srs200217 
437*4b22b933Srs200217 Revision 1.415  2004/09/18 01:14:09  cheshire
438*4b22b933Srs200217 <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces
439*4b22b933Srs200217 
440*4b22b933Srs200217 Revision 1.414  2004/09/18 01:06:48  cheshire
441*4b22b933Srs200217 Add comments
442*4b22b933Srs200217 
443*4b22b933Srs200217 Revision 1.413  2004/09/17 01:08:48  cheshire
444*4b22b933Srs200217 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
445*4b22b933Srs200217   The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
446*4b22b933Srs200217   declared in that file are ONLY appropriate to single-address-space embedded applications.
447*4b22b933Srs200217   For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
448*4b22b933Srs200217 
449*4b22b933Srs200217 Revision 1.412  2004/09/17 00:46:33  cheshire
450*4b22b933Srs200217 mDNS_TimeNow should take const mDNS parameter
451*4b22b933Srs200217 
452*4b22b933Srs200217 Revision 1.411  2004/09/17 00:31:51  cheshire
453*4b22b933Srs200217 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
454*4b22b933Srs200217 
455*4b22b933Srs200217 Revision 1.410  2004/09/17 00:19:10  cheshire
456*4b22b933Srs200217 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
457*4b22b933Srs200217 
458*4b22b933Srs200217 Revision 1.409  2004/09/16 21:59:15  cheshire
459*4b22b933Srs200217 For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
460*4b22b933Srs200217 
461*4b22b933Srs200217 Revision 1.408  2004/09/16 21:36:36  cheshire
462*4b22b933Srs200217 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
463*4b22b933Srs200217 Changes to add necessary locking calls around unicast DNS operations
464*4b22b933Srs200217 
465*4b22b933Srs200217 Revision 1.407  2004/09/16 02:29:39  cheshire
466*4b22b933Srs200217 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
467*4b22b933Srs200217 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
468*4b22b933Srs200217 
469*4b22b933Srs200217 Revision 1.406  2004/09/16 01:58:14  cheshire
470*4b22b933Srs200217 Fix compiler warnings
471*4b22b933Srs200217 
472*4b22b933Srs200217 Revision 1.405  2004/09/16 00:24:48  cheshire
473*4b22b933Srs200217 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
474*4b22b933Srs200217 
475*4b22b933Srs200217 Revision 1.404  2004/09/15 21:44:11  cheshire
476*4b22b933Srs200217 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
477*4b22b933Srs200217 Show time value in log to help diagnose errors
478*4b22b933Srs200217 
479*4b22b933Srs200217 Revision 1.403  2004/09/15 00:46:32  ksekar
480*4b22b933Srs200217 Changed debugf to verbosedebugf in CheckCacheExpiration
481*4b22b933Srs200217 
482*4b22b933Srs200217 Revision 1.402  2004/09/14 23:59:55  cheshire
483*4b22b933Srs200217 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
484*4b22b933Srs200217 
485*4b22b933Srs200217 Revision 1.401  2004/09/14 23:27:46  cheshire
486*4b22b933Srs200217 Fix compile errors
487*4b22b933Srs200217 
488*4b22b933Srs200217 Revision 1.400  2004/09/02 03:48:47  cheshire
489*4b22b933Srs200217 <rdar://problem/3709039> Disable targeted unicast query support by default
490*4b22b933Srs200217 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
491*4b22b933Srs200217 2. New field AllowRemoteQuery in AuthRecord structure
492*4b22b933Srs200217 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
493*4b22b933Srs200217 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
494*4b22b933Srs200217 
495*4b22b933Srs200217 Revision 1.399  2004/09/02 01:39:40  cheshire
496*4b22b933Srs200217 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
497*4b22b933Srs200217 
498*4b22b933Srs200217 Revision 1.398  2004/09/01 03:59:29  ksekar
499*4b22b933Srs200217 <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
500*4b22b933Srs200217 
501*4b22b933Srs200217 Revision 1.397  2004/08/25 22:04:25  rpantos
502*4b22b933Srs200217 Fix the standard Windows compile error.
503*4b22b933Srs200217 
504*4b22b933Srs200217 Revision 1.396  2004/08/25 00:37:27  ksekar
505*4b22b933Srs200217 <rdar://problem/3774635>: Cleanup DynDNS hostname registration code
506*4b22b933Srs200217 
507*4b22b933Srs200217 Revision 1.395  2004/08/18 17:21:18  ksekar
508*4b22b933Srs200217 Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
509*4b22b933Srs200217 
510*4b22b933Srs200217 Revision 1.394  2004/08/14 03:22:41  cheshire
511*4b22b933Srs200217 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
512*4b22b933Srs200217 Add GetUserSpecifiedDDNSName() routine
513*4b22b933Srs200217 Convert ServiceRegDomain to domainname instead of C string
514*4b22b933Srs200217 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
515*4b22b933Srs200217 
516*4b22b933Srs200217 Revision 1.393  2004/08/13 23:42:52  cheshire
517*4b22b933Srs200217 Removed unused "zeroDomainNamePtr"
518*4b22b933Srs200217 
519*4b22b933Srs200217 Revision 1.392  2004/08/13 23:37:02  cheshire
520*4b22b933Srs200217 Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
521*4b22b933Srs200217 "uDNS_info.UnicastHostname" for clarity
522*4b22b933Srs200217 
523*4b22b933Srs200217 Revision 1.391  2004/08/13 23:25:00  cheshire
524*4b22b933Srs200217 Now that we do both uDNS and mDNS, global replace "m->hostname" with
525*4b22b933Srs200217 "m->MulticastHostname" for clarity
526*4b22b933Srs200217 
527*4b22b933Srs200217 Revision 1.390  2004/08/11 02:17:01  cheshire
528*4b22b933Srs200217 <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record
529*4b22b933Srs200217 
530*4b22b933Srs200217 Revision 1.389  2004/08/10 23:19:14  ksekar
531*4b22b933Srs200217 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
532*4b22b933Srs200217 Moved routines/constants to allow extern access for garbage collection daemon
533*4b22b933Srs200217 
534*4b22b933Srs200217 Revision 1.388  2004/07/30 17:40:06  ksekar
535*4b22b933Srs200217 <rdar://problem/3739115>: TXT Record updates not available for wide-area services
536*4b22b933Srs200217 
537*4b22b933Srs200217 Revision 1.387  2004/07/26 22:49:30  ksekar
538*4b22b933Srs200217 <rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
539*4b22b933Srs200217 
540*4b22b933Srs200217 Revision 1.386  2004/07/13 21:24:24  rpantos
541*4b22b933Srs200217 Fix for <rdar://problem/3701120>.
542*4b22b933Srs200217 
543*4b22b933Srs200217 Revision 1.385  2004/06/18 19:09:59  cheshire
544*4b22b933Srs200217 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
545*4b22b933Srs200217 
546*4b22b933Srs200217 Revision 1.384  2004/06/15 04:31:23  cheshire
547*4b22b933Srs200217 Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
548*4b22b933Srs200217 
549*4b22b933Srs200217 Revision 1.383  2004/06/11 00:04:59  cheshire
550*4b22b933Srs200217 <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
551*4b22b933Srs200217 
552*4b22b933Srs200217 Revision 1.382  2004/06/08 04:59:40  cheshire
553*4b22b933Srs200217 Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
554*4b22b933Srs200217 
555*4b22b933Srs200217 Revision 1.381  2004/06/05 00:57:30  cheshire
556*4b22b933Srs200217 Remove incorrect LogMsg()
557*4b22b933Srs200217 
558*4b22b933Srs200217 Revision 1.380  2004/06/05 00:04:26  cheshire
559*4b22b933Srs200217 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
560*4b22b933Srs200217 
561*4b22b933Srs200217 Revision 1.379  2004/05/28 23:42:36  ksekar
562*4b22b933Srs200217 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
563*4b22b933Srs200217 
564*4b22b933Srs200217 Revision 1.378  2004/05/25 17:25:25  cheshire
565*4b22b933Srs200217 Remove extraneous blank lines and white space
566*4b22b933Srs200217 
567*4b22b933Srs200217 Revision 1.377  2004/05/18 23:51:25  cheshire
568*4b22b933Srs200217 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
569*4b22b933Srs200217 
570*4b22b933Srs200217 Revision 1.376  2004/05/05 18:30:44  ksekar
571*4b22b933Srs200217 Restored surpressed Cache Tail debug messages.
572*4b22b933Srs200217 
573*4b22b933Srs200217 Revision 1.375  2004/04/26 21:36:25  cheshire
574*4b22b933Srs200217 Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
575*4b22b933Srs200217 is indicated as being available on that interface
576*4b22b933Srs200217 
577*4b22b933Srs200217 Revision 1.374  2004/04/21 02:53:26  cheshire
578*4b22b933Srs200217 Typo in debugf statement
579*4b22b933Srs200217 
580*4b22b933Srs200217 Revision 1.373  2004/04/21 02:49:11  cheshire
581*4b22b933Srs200217 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
582*4b22b933Srs200217 
583*4b22b933Srs200217 Revision 1.372  2004/04/21 02:38:51  cheshire
584*4b22b933Srs200217 Add debugging checks
585*4b22b933Srs200217 
586*4b22b933Srs200217 Revision 1.371  2004/04/14 23:09:28  ksekar
587*4b22b933Srs200217 Support for TSIG signed dynamic updates.
588*4b22b933Srs200217 
589*4b22b933Srs200217 Revision 1.370  2004/04/09 17:40:26  cheshire
590*4b22b933Srs200217 Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
591*4b22b933Srs200217 
592*4b22b933Srs200217 Revision 1.369  2004/04/09 16:34:00  cheshire
593*4b22b933Srs200217 Debugging code for later; currently unused
594*4b22b933Srs200217 
595*4b22b933Srs200217 Revision 1.368  2004/04/02 19:19:48  cheshire
596*4b22b933Srs200217 Add code to do optional logging of multi-packet KA list time intervals
597*4b22b933Srs200217 
598*4b22b933Srs200217 Revision 1.367  2004/03/20 03:16:10  cheshire
599*4b22b933Srs200217 Minor refinement to "Excessive update rate" message
600*4b22b933Srs200217 
601*4b22b933Srs200217 Revision 1.366  2004/03/20 03:12:57  cheshire
602*4b22b933Srs200217 <rdar://problem/3587619>: UpdateCredits not granted promptly enough
603*4b22b933Srs200217 
604*4b22b933Srs200217 Revision 1.365  2004/03/19 23:51:22  cheshire
605*4b22b933Srs200217 Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
606*4b22b933Srs200217 
607*4b22b933Srs200217 Revision 1.364  2004/03/13 01:57:33  ksekar
608*4b22b933Srs200217 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
609*4b22b933Srs200217 
610*4b22b933Srs200217 Revision 1.363  2004/03/12 21:00:51  cheshire
611*4b22b933Srs200217 Also show port numbers when logging "apparent spoof mDNS Response" messages
612*4b22b933Srs200217 
613*4b22b933Srs200217 Revision 1.362  2004/03/12 08:58:18  cheshire
614*4b22b933Srs200217 Guard against empty TXT records
615*4b22b933Srs200217 
616*4b22b933Srs200217 Revision 1.361  2004/03/09 03:00:46  cheshire
617*4b22b933Srs200217 <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good.
618*4b22b933Srs200217 
619*4b22b933Srs200217 Revision 1.360  2004/03/08 02:52:41  cheshire
620*4b22b933Srs200217 Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
621*4b22b933Srs200217 
622*4b22b933Srs200217 Revision 1.359  2004/03/02 03:21:56  cheshire
623*4b22b933Srs200217 <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
624*4b22b933Srs200217 
625*4b22b933Srs200217 Revision 1.358  2004/02/20 08:18:34  cheshire
626*4b22b933Srs200217 <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
627*4b22b933Srs200217 
628*4b22b933Srs200217 Revision 1.357  2004/02/18 01:47:41  cheshire
629*4b22b933Srs200217 <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms
630*4b22b933Srs200217 
631*4b22b933Srs200217 Revision 1.356  2004/02/06 23:04:19  ksekar
632*4b22b933Srs200217 Basic Dynamic Update support via mDNS_Register (dissabled via
633*4b22b933Srs200217 UNICAST_REGISTRATION #define)
634*4b22b933Srs200217 
635*4b22b933Srs200217 Revision 1.355  2004/02/05 09:32:33  cheshire
636*4b22b933Srs200217 Fix from Bob Bradley: When using the "%.*s" string form,
637*4b22b933Srs200217 guard against truncating in the middle of a multi-byte UTF-8 character.
638*4b22b933Srs200217 
639*4b22b933Srs200217 Revision 1.354  2004/02/05 09:30:22  cheshire
640*4b22b933Srs200217 Update comments
641*4b22b933Srs200217 
642*4b22b933Srs200217 Revision 1.353  2004/01/28 03:41:00  cheshire
643*4b22b933Srs200217 <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
644*4b22b933Srs200217 
645*4b22b933Srs200217 Revision 1.352  2004/01/28 02:30:07  ksekar
646*4b22b933Srs200217 Added default Search Domains to unicast browsing, controlled via
647*4b22b933Srs200217 Networking sharing prefs pane.  Stopped sending unicast messages on
648*4b22b933Srs200217 every interface.  Fixed unicast resolving via mach-port API.
649*4b22b933Srs200217 
650*4b22b933Srs200217 Revision 1.351  2004/01/27 20:15:22  cheshire
651*4b22b933Srs200217 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
652*4b22b933Srs200217 
653*4b22b933Srs200217 Revision 1.350  2004/01/24 23:38:16  cheshire
654*4b22b933Srs200217 Use mDNSVal16() instead of shifting and ORing operations
655*4b22b933Srs200217 
656*4b22b933Srs200217 Revision 1.349  2004/01/23 23:23:14  ksekar
657*4b22b933Srs200217 Added TCP support for truncated unicast messages.
658*4b22b933Srs200217 
659*4b22b933Srs200217 Revision 1.348  2004/01/22 03:54:11  cheshire
660*4b22b933Srs200217 Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
661*4b22b933Srs200217 which means "do this query via multicast, even if it's apparently a unicast domain"
662*4b22b933Srs200217 
663*4b22b933Srs200217 Revision 1.347  2004/01/22 03:50:49  cheshire
664*4b22b933Srs200217 If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
665*4b22b933Srs200217 
666*4b22b933Srs200217 Revision 1.346  2004/01/22 03:48:41  cheshire
667*4b22b933Srs200217 Make sure uDNS client doesn't accidentally use query ID zero
668*4b22b933Srs200217 
669*4b22b933Srs200217 Revision 1.345  2004/01/22 03:43:08  cheshire
670*4b22b933Srs200217 Export constants like mDNSInterface_LocalOnly so that the client layers can use them
671*4b22b933Srs200217 
672*4b22b933Srs200217 Revision 1.344  2004/01/21 21:53:18  cheshire
673*4b22b933Srs200217 <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
674*4b22b933Srs200217 
675*4b22b933Srs200217 Revision 1.343  2003/12/23 00:07:47  cheshire
676*4b22b933Srs200217 Make port number in debug message be five-character field, left justified
677*4b22b933Srs200217 
678*4b22b933Srs200217 Revision 1.342  2003/12/20 01:34:28  cheshire
679*4b22b933Srs200217 <rdar://problem/3515876>: Error putting additional records into packets
680*4b22b933Srs200217 Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
681*4b22b933Srs200217 after every record, not once at the end.
682*4b22b933Srs200217 
683*4b22b933Srs200217 Revision 1.341  2003/12/18 22:56:12  cheshire
684*4b22b933Srs200217 <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
685*4b22b933Srs200217 
686*4b22b933Srs200217 Revision 1.340  2003/12/16 02:31:37  cheshire
687*4b22b933Srs200217 Minor update to comments
688*4b22b933Srs200217 
689*4b22b933Srs200217 Revision 1.339  2003/12/13 05:50:33  bradley
690*4b22b933Srs200217 Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
691*4b22b933Srs200217 layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
692*4b22b933Srs200217 fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
693*4b22b933Srs200217 
694*4b22b933Srs200217 Revision 1.338  2003/12/13 03:05:27  ksekar
695*4b22b933Srs200217 <rdar://problem/3192548>: DynDNS: Unicast query of service records
696*4b22b933Srs200217 
697*4b22b933Srs200217 Revision 1.337  2003/12/01 21:46:05  cheshire
698*4b22b933Srs200217 mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
699*4b22b933Srs200217 
700*4b22b933Srs200217 Revision 1.336  2003/12/01 21:26:19  cheshire
701*4b22b933Srs200217 Guard against zero-length sbuffer in mDNS_vsnprintf()
702*4b22b933Srs200217 
703*4b22b933Srs200217 Revision 1.335  2003/12/01 20:27:48  cheshire
704*4b22b933Srs200217 Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
705*4b22b933Srs200217 
706*4b22b933Srs200217 Revision 1.334  2003/11/20 22:59:53  cheshire
707*4b22b933Srs200217 Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
708*4b22b933Srs200217 Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
709*4b22b933Srs200217 
710*4b22b933Srs200217 Revision 1.333  2003/11/20 20:49:53  cheshire
711*4b22b933Srs200217 Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
712*4b22b933Srs200217 
713*4b22b933Srs200217 Revision 1.332  2003/11/20 05:47:37  cheshire
714*4b22b933Srs200217 <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query
715*4b22b933Srs200217 Now that we only include answers in the known answer list if they are less than
716*4b22b933Srs200217 halfway to expiry, the check to also see if we have another query scheduled
717*4b22b933Srs200217 before the record expires is no longer necessary (and in fact, not correct).
718*4b22b933Srs200217 
719*4b22b933Srs200217 Revision 1.331  2003/11/19 22:31:48  cheshire
720*4b22b933Srs200217 When automatically adding A records to SRVs, add them as additionals, not answers
721*4b22b933Srs200217 
722*4b22b933Srs200217 Revision 1.330  2003/11/19 22:28:50  cheshire
723*4b22b933Srs200217 Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
724*4b22b933Srs200217 to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
725*4b22b933Srs200217 
726*4b22b933Srs200217 Revision 1.329  2003/11/19 22:19:24  cheshire
727*4b22b933Srs200217 Show log message when ignoring packets with bad TTL.
728*4b22b933Srs200217 This is to help diagnose problems on Linux versions that may not report the TTL reliably.
729*4b22b933Srs200217 
730*4b22b933Srs200217 Revision 1.328  2003/11/19 22:06:38  cheshire
731*4b22b933Srs200217 Show log messages when a service or hostname is renamed
732*4b22b933Srs200217 
733*4b22b933Srs200217 Revision 1.327  2003/11/19 22:03:44  cheshire
734*4b22b933Srs200217 Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
735*4b22b933Srs200217 
736*4b22b933Srs200217 Revision 1.326  2003/11/17 22:27:02  cheshire
737*4b22b933Srs200217 Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
738*4b22b933Srs200217 on platforms that have native clock rates below fifty ticks per second.
739*4b22b933Srs200217 
740*4b22b933Srs200217 Revision 1.325  2003/11/17 20:41:44  cheshire
741*4b22b933Srs200217 Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
742*4b22b933Srs200217 
743*4b22b933Srs200217 Revision 1.324  2003/11/17 20:36:32  cheshire
744*4b22b933Srs200217 Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
745*4b22b933Srs200217 DeadvertiseInterface() -- they're internal private routines, not API routines.
746*4b22b933Srs200217 
747*4b22b933Srs200217 Revision 1.323  2003/11/14 20:59:08  cheshire
748*4b22b933Srs200217 Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
749*4b22b933Srs200217 Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
750*4b22b933Srs200217 
751*4b22b933Srs200217 Revision 1.322  2003/11/14 19:47:52  cheshire
752*4b22b933Srs200217 Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
753*4b22b933Srs200217 
754*4b22b933Srs200217 Revision 1.321  2003/11/14 19:18:34  cheshire
755*4b22b933Srs200217 Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
756*4b22b933Srs200217 
757*4b22b933Srs200217 Revision 1.320  2003/11/13 06:45:04  cheshire
758*4b22b933Srs200217 Fix compiler warning on certain compilers
759*4b22b933Srs200217 
760*4b22b933Srs200217 Revision 1.319  2003/11/13 00:47:40  cheshire
761*4b22b933Srs200217 <rdar://problem/3437556> We should delay AAAA record query if A record already in cache.
762*4b22b933Srs200217 
763*4b22b933Srs200217 Revision 1.318  2003/11/13 00:33:26  cheshire
764*4b22b933Srs200217 Change macro "RRIsAddressType" to "RRTypeIsAddressType"
765*4b22b933Srs200217 
766*4b22b933Srs200217 Revision 1.317  2003/11/13 00:10:49  cheshire
767*4b22b933Srs200217 <rdar://problem/3436412>: Verify that rr data is different before updating.
768*4b22b933Srs200217 
769*4b22b933Srs200217 Revision 1.316  2003/11/08 23:37:54  cheshire
770*4b22b933Srs200217 Give explicit zero initializers to blank static structure, required by certain compilers.
771*4b22b933Srs200217 (Thanks to ramaprasad.kr@hp.com for reporting this.)
772*4b22b933Srs200217 
773*4b22b933Srs200217 Revision 1.315  2003/11/07 03:32:56  cheshire
774*4b22b933Srs200217 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
775*4b22b933Srs200217 This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
776*4b22b933Srs200217 purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
777*4b22b933Srs200217 to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
778*4b22b933Srs200217 can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
779*4b22b933Srs200217 
780*4b22b933Srs200217 Revision 1.314  2003/11/07 03:19:49  cheshire
781*4b22b933Srs200217 Minor variable renaming for clarity
782*4b22b933Srs200217 
783*4b22b933Srs200217 Revision 1.313  2003/11/07 03:14:49  cheshire
784*4b22b933Srs200217 Previous checkin proved to be overly simplistic; reversing
785*4b22b933Srs200217 
786*4b22b933Srs200217 Revision 1.312  2003/11/03 23:45:15  cheshire
787*4b22b933Srs200217 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
788*4b22b933Srs200217 Build cache lists in FIFO order, not customary C LIFO order
789*4b22b933Srs200217 (Append new elements to tail of cache list, instead of prepending at the head.)
790*4b22b933Srs200217 
791*4b22b933Srs200217 Revision 1.311  2003/10/09 18:00:11  cheshire
792*4b22b933Srs200217 Another compiler warning fix.
793*4b22b933Srs200217 
794*4b22b933Srs200217 Revision 1.310  2003/10/07 20:27:05  cheshire
795*4b22b933Srs200217 Patch from Bob Bradley, to fix warning and compile error on Windows
796*4b22b933Srs200217 
797*4b22b933Srs200217 Revision 1.309  2003/09/26 01:06:36  cheshire
798*4b22b933Srs200217 <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
799*4b22b933Srs200217 Made new routine HaveSentEntireRRSet() to check if flag should be set
800*4b22b933Srs200217 
801*4b22b933Srs200217 Revision 1.308  2003/09/23 01:05:01  cheshire
802*4b22b933Srs200217 Minor changes to comments and debugf() message
803*4b22b933Srs200217 
804*4b22b933Srs200217 Revision 1.307  2003/09/09 20:13:30  cheshire
805*4b22b933Srs200217 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
806*4b22b933Srs200217 Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
807*4b22b933Srs200217 rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
808*4b22b933Srs200217 
809*4b22b933Srs200217 Revision 1.306  2003/09/09 03:00:03  cheshire
810*4b22b933Srs200217 <rdar://problem/3413099> Services take a long time to disappear when switching networks.
811*4b22b933Srs200217 Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
812*4b22b933Srs200217 
813*4b22b933Srs200217 Revision 1.305  2003/09/09 02:49:31  cheshire
814*4b22b933Srs200217 <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
815*4b22b933Srs200217 
816*4b22b933Srs200217 Revision 1.304  2003/09/09 02:41:19  cheshire
817*4b22b933Srs200217 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
818*4b22b933Srs200217 
819*4b22b933Srs200217 Revision 1.303  2003/09/05 19:55:02  cheshire
820*4b22b933Srs200217 <rdar://problem/3409533> Include address records when announcing SRV records
821*4b22b933Srs200217 
822*4b22b933Srs200217 Revision 1.302  2003/09/05 00:01:36  cheshire
823*4b22b933Srs200217 <rdar://problem/3407549> Don't accelerate queries that have large KA lists
824*4b22b933Srs200217 
825*4b22b933Srs200217 Revision 1.301  2003/09/04 22:51:13  cheshire
826*4b22b933Srs200217 <rdar://problem/3398213> Group probes and goodbyes better
827*4b22b933Srs200217 
828*4b22b933Srs200217 Revision 1.300  2003/09/03 02:40:37  cheshire
829*4b22b933Srs200217 <rdar://problem/3404842> mDNSResponder complains about '_'s
830*4b22b933Srs200217 Underscores are not supposed to be legal in standard DNS names, but IANA appears
831*4b22b933Srs200217 to have allowed them in previous service name registrations, so we should too.
832*4b22b933Srs200217 
833*4b22b933Srs200217 Revision 1.299  2003/09/03 02:33:09  cheshire
834*4b22b933Srs200217 <rdar://problem/3404795> CacheRecordRmv ERROR
835*4b22b933Srs200217 Don't update m->NewQuestions until *after* CheckCacheExpiration();
836*4b22b933Srs200217 
837*4b22b933Srs200217 Revision 1.298  2003/09/03 01:47:01  cheshire
838*4b22b933Srs200217 <rdar://problem/3319418> Services always in a state of flux
839*4b22b933Srs200217 Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
840*4b22b933Srs200217 
841*4b22b933Srs200217 Revision 1.297  2003/08/29 19:44:15  cheshire
842*4b22b933Srs200217 <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
843*4b22b933Srs200217 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
844*4b22b933Srs200217    that already have at least one unique answer in the cache
845*4b22b933Srs200217 2. For these queries, go straight to QM, skipping QU
846*4b22b933Srs200217 
847*4b22b933Srs200217 Revision 1.296  2003/08/29 19:08:21  cheshire
848*4b22b933Srs200217 <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
849*4b22b933Srs200217 Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
850*4b22b933Srs200217 
851*4b22b933Srs200217 Revision 1.295  2003/08/28 01:10:59  cheshire
852*4b22b933Srs200217 <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
853*4b22b933Srs200217 
854*4b22b933Srs200217 Revision 1.294  2003/08/27 02:30:22  cheshire
855*4b22b933Srs200217 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
856*4b22b933Srs200217 One more change: "query->GotTXT" is now a straightforward bi-state boolean again
857*4b22b933Srs200217 
858*4b22b933Srs200217 Revision 1.293  2003/08/27 02:25:31  cheshire
859*4b22b933Srs200217 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
860*4b22b933Srs200217 
861*4b22b933Srs200217 Revision 1.292  2003/08/21 19:27:36  cheshire
862*4b22b933Srs200217 <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
863*4b22b933Srs200217 
864*4b22b933Srs200217 Revision 1.291  2003/08/21 18:57:44  cheshire
865*4b22b933Srs200217 <rdar://problem/3387140> Synchronized queries on the network
866*4b22b933Srs200217 
867*4b22b933Srs200217 Revision 1.290  2003/08/21 02:25:23  cheshire
868*4b22b933Srs200217 Minor changes to comments and debugf() messages
869*4b22b933Srs200217 
870*4b22b933Srs200217 Revision 1.289  2003/08/21 02:21:50  cheshire
871*4b22b933Srs200217 <rdar://problem/3386473> Efficiency: Reduce repeated queries
872*4b22b933Srs200217 
873*4b22b933Srs200217 Revision 1.288  2003/08/20 23:39:30  cheshire
874*4b22b933Srs200217 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
875*4b22b933Srs200217 
876*4b22b933Srs200217 Revision 1.287  2003/08/20 20:47:18  cheshire
877*4b22b933Srs200217 Fix compiler warning
878*4b22b933Srs200217 
879*4b22b933Srs200217 Revision 1.286  2003/08/20 02:18:51  cheshire
880*4b22b933Srs200217 <rdar://problem/3344098> Cleanup: Review syslog messages
881*4b22b933Srs200217 
882*4b22b933Srs200217 Revision 1.285  2003/08/20 01:59:06  cheshire
883*4b22b933Srs200217 <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
884*4b22b933Srs200217 Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
885*4b22b933Srs200217 
886*4b22b933Srs200217 Revision 1.284  2003/08/19 22:20:00  cheshire
887*4b22b933Srs200217 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
888*4b22b933Srs200217 More minor refinements
889*4b22b933Srs200217 
890*4b22b933Srs200217 Revision 1.283  2003/08/19 22:16:27  cheshire
891*4b22b933Srs200217 Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
892*4b22b933Srs200217 
893*4b22b933Srs200217 Revision 1.282  2003/08/19 06:48:25  cheshire
894*4b22b933Srs200217 <rdar://problem/3376552> Guard against excessive record updates
895*4b22b933Srs200217 Each record starts with 10 UpdateCredits.
896*4b22b933Srs200217 Every update consumes one UpdateCredit.
897*4b22b933Srs200217 UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
898*4b22b933Srs200217 As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
899*4b22b933Srs200217 When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
900*4b22b933Srs200217 
901*4b22b933Srs200217 Revision 1.281  2003/08/19 04:49:28  cheshire
902*4b22b933Srs200217 <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
903*4b22b933Srs200217 1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6.
904*4b22b933Srs200217 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
905*4b22b933Srs200217 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
906*4b22b933Srs200217 
907*4b22b933Srs200217 Revision 1.280  2003/08/19 02:33:36  cheshire
908*4b22b933Srs200217 Update comments
909*4b22b933Srs200217 
910*4b22b933Srs200217 Revision 1.279  2003/08/19 02:31:11  cheshire
911*4b22b933Srs200217 <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
912*4b22b933Srs200217 Final expiration queries now only mark the question for sending on the particular interface
913*4b22b933Srs200217 pertaining to the record that's expiring.
914*4b22b933Srs200217 
915*4b22b933Srs200217 Revision 1.278  2003/08/18 22:53:37  cheshire
916*4b22b933Srs200217 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
917*4b22b933Srs200217 
918*4b22b933Srs200217 Revision 1.277  2003/08/18 19:05:44  cheshire
919*4b22b933Srs200217 <rdar://problem/3382423> UpdateRecord not working right
920*4b22b933Srs200217 Added "newrdlength" field to hold new length of updated rdata
921*4b22b933Srs200217 
922*4b22b933Srs200217 Revision 1.276  2003/08/16 03:39:00  cheshire
923*4b22b933Srs200217 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
924*4b22b933Srs200217 
925*4b22b933Srs200217 Revision 1.275  2003/08/16 02:51:27  cheshire
926*4b22b933Srs200217 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
927*4b22b933Srs200217 Don't try to compute namehash etc, until *after* validating the name
928*4b22b933Srs200217 
929*4b22b933Srs200217 Revision 1.274  2003/08/16 01:12:40  cheshire
930*4b22b933Srs200217 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
931*4b22b933Srs200217 Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
932*4b22b933Srs200217 simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
933*4b22b933Srs200217 and in the process of copying it, the C compiler may run off the end of the rdata object into
934*4b22b933Srs200217 unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
935*4b22b933Srs200217 call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
936*4b22b933Srs200217 
937*4b22b933Srs200217 Revision 1.273  2003/08/15 20:16:02  cheshire
938*4b22b933Srs200217 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
939*4b22b933Srs200217 We want to avoid touching the rdata pages, so we don't page them in.
940*4b22b933Srs200217 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
941*4b22b933Srs200217    Moved this from the RData to the ResourceRecord object.
942*4b22b933Srs200217 2. To avoid unnecessarily touching the rdata just to compare it,
943*4b22b933Srs200217    compute a hash of the rdata and store the hash in the ResourceRecord object.
944*4b22b933Srs200217 
945*4b22b933Srs200217 Revision 1.272  2003/08/14 19:29:04  cheshire
946*4b22b933Srs200217 <rdar://problem/3378473> Include cache records in SIGINFO output
947*4b22b933Srs200217 Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
948*4b22b933Srs200217 
949*4b22b933Srs200217 Revision 1.271  2003/08/14 02:17:05  cheshire
950*4b22b933Srs200217 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
951*4b22b933Srs200217 
952*4b22b933Srs200217 Revision 1.270  2003/08/13 17:07:28  ksekar
953*4b22b933Srs200217 <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
954*4b22b933Srs200217 Added check to result of mDNS_Register() before linking extra record into list.
955*4b22b933Srs200217 
956*4b22b933Srs200217 Revision 1.269  2003/08/12 19:56:23  cheshire
957*4b22b933Srs200217 Update to APSL 2.0
958*4b22b933Srs200217 
959*4b22b933Srs200217 Revision 1.268  2003/08/12 15:01:10  cheshire
960*4b22b933Srs200217 Add comments
961*4b22b933Srs200217 
962*4b22b933Srs200217 Revision 1.267  2003/08/12 14:59:27  cheshire
963*4b22b933Srs200217 <rdar://problem/3374490> Rate-limiting blocks some legitimate responses
964*4b22b933Srs200217 When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
965*4b22b933Srs200217 whether to suppress the response, also check LastMCInterface to see if it matches.
966*4b22b933Srs200217 
967*4b22b933Srs200217 Revision 1.266  2003/08/12 12:47:16  cheshire
968*4b22b933Srs200217 In mDNSCoreMachineSleep debugf message, display value of m->timenow
969*4b22b933Srs200217 
970*4b22b933Srs200217 Revision 1.265  2003/08/11 20:04:28  cheshire
971*4b22b933Srs200217 <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
972*4b22b933Srs200217 
973*4b22b933Srs200217 Revision 1.264  2003/08/09 00:55:02  cheshire
974*4b22b933Srs200217 <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
975*4b22b933Srs200217 Don't scan the whole cache after every packet.
976*4b22b933Srs200217 
977*4b22b933Srs200217 Revision 1.263  2003/08/09 00:35:29  cheshire
978*4b22b933Srs200217 Moved AnswerNewQuestion() later in the file, in preparation for next checkin
979*4b22b933Srs200217 
980*4b22b933Srs200217 Revision 1.262  2003/08/08 19:50:33  cheshire
981*4b22b933Srs200217 <rdar://problem/3370332> Remove "Cache size now xxx" messages
982*4b22b933Srs200217 
983*4b22b933Srs200217 Revision 1.261  2003/08/08 19:18:45  cheshire
984*4b22b933Srs200217 <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
985*4b22b933Srs200217 
986*4b22b933Srs200217 Revision 1.260  2003/08/08 18:55:48  cheshire
987*4b22b933Srs200217 <rdar://problem/3370365> Guard against time going backwards
988*4b22b933Srs200217 
989*4b22b933Srs200217 Revision 1.259  2003/08/08 18:36:04  cheshire
990*4b22b933Srs200217 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
991*4b22b933Srs200217 
992*4b22b933Srs200217 Revision 1.258  2003/08/08 16:22:05  cheshire
993*4b22b933Srs200217 <rdar://problem/3335473> Need to check validity of TXT (and other) records
994*4b22b933Srs200217 Remove unneeded LogMsg
995*4b22b933Srs200217 
996*4b22b933Srs200217 Revision 1.257  2003/08/07 01:41:08  cheshire
997*4b22b933Srs200217 <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
998*4b22b933Srs200217 
999*4b22b933Srs200217 Revision 1.256  2003/08/06 23:25:51  cheshire
1000*4b22b933Srs200217 <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
1001*4b22b933Srs200217 
1002*4b22b933Srs200217 Revision 1.255  2003/08/06 23:22:50  cheshire
1003*4b22b933Srs200217 Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
1004*4b22b933Srs200217 
1005*4b22b933Srs200217 Revision 1.254  2003/08/06 21:33:39  cheshire
1006*4b22b933Srs200217 Fix compiler warnings on PocketPC 2003 (Windows CE)
1007*4b22b933Srs200217 
1008*4b22b933Srs200217 Revision 1.253  2003/08/06 20:43:57  cheshire
1009*4b22b933Srs200217 <rdar://problem/3335473> Need to check validity of TXT (and other) records
1010*4b22b933Srs200217 Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
1011*4b22b933Srs200217 
1012*4b22b933Srs200217 Revision 1.252  2003/08/06 20:35:47  cheshire
1013*4b22b933Srs200217 Enhance debugging routine GetRRDisplayString() so it can also be used to display
1014*4b22b933Srs200217 other RDataBody objects, not just the one currently attached the given ResourceRecord
1015*4b22b933Srs200217 
1016*4b22b933Srs200217 Revision 1.251  2003/08/06 19:07:34  cheshire
1017*4b22b933Srs200217 <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
1018*4b22b933Srs200217 Was checking LastAPTime instead of LastMCTime
1019*4b22b933Srs200217 
1020*4b22b933Srs200217 Revision 1.250  2003/08/06 19:01:55  cheshire
1021*4b22b933Srs200217 Update comments
1022*4b22b933Srs200217 
1023*4b22b933Srs200217 Revision 1.249  2003/08/06 00:13:28  cheshire
1024*4b22b933Srs200217 Tidy up debugf messages
1025*4b22b933Srs200217 
1026*4b22b933Srs200217 Revision 1.248  2003/08/05 22:20:15  cheshire
1027*4b22b933Srs200217 <rdar://problem/3330324> Need to check IP TTL on responses
1028*4b22b933Srs200217 
1029*4b22b933Srs200217 Revision 1.247  2003/08/05 00:56:39  cheshire
1030*4b22b933Srs200217 <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
1031*4b22b933Srs200217 
1032*4b22b933Srs200217 Revision 1.246  2003/08/04 19:20:49  cheshire
1033*4b22b933Srs200217 Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
1034*4b22b933Srs200217 
1035*4b22b933Srs200217 Revision 1.245  2003/08/02 01:56:29  cheshire
1036*4b22b933Srs200217 For debugging: log message if we ever get more than one question in a truncated packet
1037*4b22b933Srs200217 
1038*4b22b933Srs200217 Revision 1.244  2003/08/01 23:55:32  cheshire
1039*4b22b933Srs200217 Fix for compiler warnings on Windows, submitted by Bob Bradley
1040*4b22b933Srs200217 
1041*4b22b933Srs200217 Revision 1.243  2003/07/25 02:26:09  cheshire
1042*4b22b933Srs200217 Typo: FIxed missing semicolon
1043*4b22b933Srs200217 
1044*4b22b933Srs200217 Revision 1.242  2003/07/25 01:18:41  cheshire
1045*4b22b933Srs200217 Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1046*4b22b933Srs200217 
1047*4b22b933Srs200217 Revision 1.241  2003/07/23 21:03:42  cheshire
1048*4b22b933Srs200217 Only show "Found record..." debugf message in verbose mode
1049*4b22b933Srs200217 
1050*4b22b933Srs200217 Revision 1.240  2003/07/23 21:01:11  cheshire
1051*4b22b933Srs200217 <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
1052*4b22b933Srs200217 After sending a packet, suppress further sending for the next 100ms.
1053*4b22b933Srs200217 
1054*4b22b933Srs200217 Revision 1.239  2003/07/22 01:30:05  cheshire
1055*4b22b933Srs200217 <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
1056*4b22b933Srs200217 
1057*4b22b933Srs200217 Revision 1.238  2003/07/22 00:10:20  cheshire
1058*4b22b933Srs200217 <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1059*4b22b933Srs200217 
1060*4b22b933Srs200217 Revision 1.237  2003/07/19 03:23:13  cheshire
1061*4b22b933Srs200217 <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1062*4b22b933Srs200217 
1063*4b22b933Srs200217 Revision 1.236  2003/07/19 03:04:55  cheshire
1064*4b22b933Srs200217 Fix warnings; some debugf message improvements
1065*4b22b933Srs200217 
1066*4b22b933Srs200217 Revision 1.235  2003/07/19 00:03:32  cheshire
1067*4b22b933Srs200217 <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
1068*4b22b933Srs200217 ScheduleNextTask is quite an expensive operation.
1069*4b22b933Srs200217 We don't need to do all that work after receiving a no-op packet that didn't change our state.
1070*4b22b933Srs200217 
1071*4b22b933Srs200217 Revision 1.234  2003/07/18 23:52:11  cheshire
1072*4b22b933Srs200217 To improve consistency of field naming, global search-and-replace:
1073*4b22b933Srs200217 NextProbeTime    -> NextScheduledProbe
1074*4b22b933Srs200217 NextResponseTime -> NextScheduledResponse
1075*4b22b933Srs200217 
1076*4b22b933Srs200217 Revision 1.233  2003/07/18 00:29:59  cheshire
1077*4b22b933Srs200217 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
1078*4b22b933Srs200217 
1079*4b22b933Srs200217 Revision 1.232  2003/07/18 00:11:38  cheshire
1080*4b22b933Srs200217 Add extra case to switch statements to handle HINFO data for Get, Put and Display
1081*4b22b933Srs200217 (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
1082*4b22b933Srs200217 
1083*4b22b933Srs200217 Revision 1.231  2003/07/18 00:06:37  cheshire
1084*4b22b933Srs200217 To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
1085*4b22b933Srs200217 
1086*4b22b933Srs200217 Revision 1.230  2003/07/17 18:16:54  cheshire
1087*4b22b933Srs200217 <rdar://problem/3319418> Services always in a state of flux
1088*4b22b933Srs200217 In preparation for working on this, made some debugf messages a little more selective
1089*4b22b933Srs200217 
1090*4b22b933Srs200217 Revision 1.229  2003/07/17 17:35:04  cheshire
1091*4b22b933Srs200217 <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1092*4b22b933Srs200217 
1093*4b22b933Srs200217 Revision 1.228  2003/07/16 20:50:27  cheshire
1094*4b22b933Srs200217 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1095*4b22b933Srs200217 
1096*4b22b933Srs200217 Revision 1.227  2003/07/16 05:01:36  cheshire
1097*4b22b933Srs200217 Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
1098*4b22b933Srs200217 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1099*4b22b933Srs200217 
1100*4b22b933Srs200217 Revision 1.226  2003/07/16 04:51:44  cheshire
1101*4b22b933Srs200217 Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1102*4b22b933Srs200217 
1103*4b22b933Srs200217 Revision 1.225  2003/07/16 04:46:41  cheshire
1104*4b22b933Srs200217 Minor wording cleanup: The correct DNS term is "response", not "reply"
1105*4b22b933Srs200217 
1106*4b22b933Srs200217 Revision 1.224  2003/07/16 04:39:02  cheshire
1107*4b22b933Srs200217 Textual cleanup (no change to functionality):
1108*4b22b933Srs200217 Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
1109*4b22b933Srs200217 
1110*4b22b933Srs200217 Revision 1.223  2003/07/16 00:09:22  cheshire
1111*4b22b933Srs200217 Textual cleanup (no change to functionality):
1112*4b22b933Srs200217 Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
1113*4b22b933Srs200217 replace with macro "TicksTTL(rr)"
1114*4b22b933Srs200217 Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
1115*4b22b933Srs200217 replaced with macro "RRExpireTime(rr)"
1116*4b22b933Srs200217 
1117*4b22b933Srs200217 Revision 1.222  2003/07/15 23:40:46  cheshire
1118*4b22b933Srs200217 Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1119*4b22b933Srs200217 
1120*4b22b933Srs200217 Revision 1.221  2003/07/15 22:17:56  cheshire
1121*4b22b933Srs200217 <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1122*4b22b933Srs200217 
1123*4b22b933Srs200217 Revision 1.220  2003/07/15 02:12:51  cheshire
1124*4b22b933Srs200217 Slight tidy-up of debugf messages and comments
1125*4b22b933Srs200217 
1126*4b22b933Srs200217 Revision 1.219  2003/07/15 01:55:12  cheshire
1127*4b22b933Srs200217 <rdar://problem/3315777> Need to implement service registration with subtypes
1128*4b22b933Srs200217 
1129*4b22b933Srs200217 Revision 1.218  2003/07/14 16:26:06  cheshire
1130*4b22b933Srs200217 <rdar://problem/3324795> Duplicate query suppression not working right
1131*4b22b933Srs200217 Refinement: Don't record DS information for a question in the first quarter second
1132*4b22b933Srs200217 right after we send it -- in the case where a question happens to be accelerated by
1133*4b22b933Srs200217 the maximum allowed amount, we don't want it to then be suppressed because the previous
1134*4b22b933Srs200217 time *we* sent that question falls (just) within the valid duplicate suppression window.
1135*4b22b933Srs200217 
1136*4b22b933Srs200217 Revision 1.217  2003/07/13 04:43:53  cheshire
1137*4b22b933Srs200217 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1138*4b22b933Srs200217 Minor refinement: No need to make address query broader than the original SRV query that provoked it
1139*4b22b933Srs200217 
1140*4b22b933Srs200217 Revision 1.216  2003/07/13 03:13:17  cheshire
1141*4b22b933Srs200217 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1142*4b22b933Srs200217 If we get an identical SRV on a second interface, convert address queries to non-specific
1143*4b22b933Srs200217 
1144*4b22b933Srs200217 Revision 1.215  2003/07/13 02:28:00  cheshire
1145*4b22b933Srs200217 <rdar://problem/3325166> SendResponses didn't all its responses
1146*4b22b933Srs200217 Delete all references to RRInterfaceActive -- it's now superfluous
1147*4b22b933Srs200217 
1148*4b22b933Srs200217 Revision 1.214  2003/07/13 01:47:53  cheshire
1149*4b22b933Srs200217 Fix one error and one warning in the Windows build
1150*4b22b933Srs200217 
1151*4b22b933Srs200217 Revision 1.213  2003/07/12 04:25:48  cheshire
1152*4b22b933Srs200217 Fix minor signed/unsigned warnings
1153*4b22b933Srs200217 
1154*4b22b933Srs200217 Revision 1.212  2003/07/12 01:59:11  cheshire
1155*4b22b933Srs200217 Minor changes to debugf messages
1156*4b22b933Srs200217 
1157*4b22b933Srs200217 Revision 1.211  2003/07/12 01:47:01  cheshire
1158*4b22b933Srs200217 <rdar://problem/3324495> After name conflict, appended number should be higher than previous number
1159*4b22b933Srs200217 
1160*4b22b933Srs200217 Revision 1.210  2003/07/12 01:43:28  cheshire
1161*4b22b933Srs200217 <rdar://problem/3324795> Duplicate query suppression not working right
1162*4b22b933Srs200217 The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
1163*4b22b933Srs200217 The code was incorrectly using the last query time plus one-half the query interval.
1164*4b22b933Srs200217 This was only correct in the case where query acceleration was not in effect.
1165*4b22b933Srs200217 
1166*4b22b933Srs200217 Revision 1.209  2003/07/12 01:27:50  cheshire
1167*4b22b933Srs200217 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1168*4b22b933Srs200217 Fix missing "-1" in RemoveLabelSuffix()
1169*4b22b933Srs200217 
1170*4b22b933Srs200217 Revision 1.208  2003/07/11 01:32:38  cheshire
1171*4b22b933Srs200217 Syntactic cleanup (no change to funcationality): Now that we only have one host name,
1172*4b22b933Srs200217 rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
1173*4b22b933Srs200217 
1174*4b22b933Srs200217 Revision 1.207  2003/07/11 01:28:00  cheshire
1175*4b22b933Srs200217 <rdar://problem/3161289> No more local.arpa
1176*4b22b933Srs200217 
1177*4b22b933Srs200217 Revision 1.206  2003/07/11 00:45:02  cheshire
1178*4b22b933Srs200217 <rdar://problem/3321909> Client should get callback confirming successful host name registration
1179*4b22b933Srs200217 
1180*4b22b933Srs200217 Revision 1.205  2003/07/11 00:40:18  cheshire
1181*4b22b933Srs200217 Tidy up debug message in HostNameCallback()
1182*4b22b933Srs200217 
1183*4b22b933Srs200217 Revision 1.204  2003/07/11 00:20:32  cheshire
1184*4b22b933Srs200217 <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1185*4b22b933Srs200217 
1186*4b22b933Srs200217 Revision 1.203  2003/07/10 23:53:41  cheshire
1187*4b22b933Srs200217 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1188*4b22b933Srs200217 
1189*4b22b933Srs200217 Revision 1.202  2003/07/04 02:23:20  cheshire
1190*4b22b933Srs200217 <rdar://problem/3311955> Responder too aggressive at flushing stale data
1191*4b22b933Srs200217 Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
1192*4b22b933Srs200217 
1193*4b22b933Srs200217 Revision 1.201  2003/07/04 01:09:41  cheshire
1194*4b22b933Srs200217 <rdar://problem/3315775> Need to implement subtype queries
1195*4b22b933Srs200217 Modified ConstructServiceName() to allow three-part service types
1196*4b22b933Srs200217 
1197*4b22b933Srs200217 Revision 1.200  2003/07/03 23:55:26  cheshire
1198*4b22b933Srs200217 Minor change to wording of syslog warning messages
1199*4b22b933Srs200217 
1200*4b22b933Srs200217 Revision 1.199  2003/07/03 23:51:13  cheshire
1201*4b22b933Srs200217 <rdar://problem/3315652>:	Lots of "have given xxx answers" syslog warnings
1202*4b22b933Srs200217 Added more detailed debugging information
1203*4b22b933Srs200217 
1204*4b22b933Srs200217 Revision 1.198  2003/07/03 22:19:30  cheshire
1205*4b22b933Srs200217 <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
1206*4b22b933Srs200217 Make exception to allow _tivo_servemedia._tcp.
1207*4b22b933Srs200217 
1208*4b22b933Srs200217 Revision 1.197  2003/07/02 22:33:05  cheshire
1209*4b22b933Srs200217 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1210*4b22b933Srs200217 Minor refinements:
1211*4b22b933Srs200217 When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
1212*4b22b933Srs200217 Allow cache to grow to 512 records before considering it a potential denial-of-service attack
1213*4b22b933Srs200217 
1214*4b22b933Srs200217 Revision 1.196  2003/07/02 21:19:45  cheshire
1215*4b22b933Srs200217 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
1216*4b22b933Srs200217 
1217*4b22b933Srs200217 Revision 1.195  2003/07/02 19:56:58  cheshire
1218*4b22b933Srs200217 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1219*4b22b933Srs200217 Minor refinement: m->rrcache_active was not being decremented when
1220*4b22b933Srs200217 an active record was deleted because its TTL expired
1221*4b22b933Srs200217 
1222*4b22b933Srs200217 Revision 1.194  2003/07/02 18:47:40  cheshire
1223*4b22b933Srs200217 Minor wording change to log messages
1224*4b22b933Srs200217 
1225*4b22b933Srs200217 Revision 1.193  2003/07/02 02:44:13  cheshire
1226*4b22b933Srs200217 Fix warning in non-debug build
1227*4b22b933Srs200217 
1228*4b22b933Srs200217 Revision 1.192  2003/07/02 02:41:23  cheshire
1229*4b22b933Srs200217 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1230*4b22b933Srs200217 
1231*4b22b933Srs200217 Revision 1.191  2003/07/02 02:30:51  cheshire
1232*4b22b933Srs200217 HashSlot() returns an array index. It can't be negative; hence it should not be signed.
1233*4b22b933Srs200217 
1234*4b22b933Srs200217 Revision 1.190  2003/06/27 00:03:05  vlubet
1235*4b22b933Srs200217 <rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1236*4b22b933Srs200217 
1237*4b22b933Srs200217 Revision 1.189  2003/06/11 19:24:03  cheshire
1238*4b22b933Srs200217 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1239*4b22b933Srs200217 Slight refinement to previous checkin
1240*4b22b933Srs200217 
1241*4b22b933Srs200217 Revision 1.188  2003/06/10 20:33:28  cheshire
1242*4b22b933Srs200217 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1243*4b22b933Srs200217 
1244*4b22b933Srs200217 Revision 1.187  2003/06/10 04:30:44  cheshire
1245*4b22b933Srs200217 <rdar://problem/3286234> Need to re-probe/re-announce on configuration change
1246*4b22b933Srs200217 Only interface-specific records were re-probing and re-announcing, not non-specific records.
1247*4b22b933Srs200217 
1248*4b22b933Srs200217 Revision 1.186  2003/06/10 04:24:39  cheshire
1249*4b22b933Srs200217 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1250*4b22b933Srs200217 Some additional refinements:
1251*4b22b933Srs200217 Don't try to do this for unicast-response queries
1252*4b22b933Srs200217 better tracking of Qs and KAs in multi-packet KA lists
1253*4b22b933Srs200217 
1254*4b22b933Srs200217 Revision 1.185  2003/06/10 03:52:49  cheshire
1255*4b22b933Srs200217 Update comments and debug messages
1256*4b22b933Srs200217 
1257*4b22b933Srs200217 Revision 1.184  2003/06/10 02:26:39  cheshire
1258*4b22b933Srs200217 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1259*4b22b933Srs200217 Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
1260*4b22b933Srs200217 
1261*4b22b933Srs200217 Revision 1.183  2003/06/09 18:53:13  cheshire
1262*4b22b933Srs200217 Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1263*4b22b933Srs200217 
1264*4b22b933Srs200217 Revision 1.182  2003/06/09 18:38:42  cheshire
1265*4b22b933Srs200217 <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
1266*4b22b933Srs200217 Only issue a correction if the TTL in the proxy packet is less than half the correct value.
1267*4b22b933Srs200217 
1268*4b22b933Srs200217 Revision 1.181  2003/06/07 06:45:05  cheshire
1269*4b22b933Srs200217 <rdar://problem/3283666> No need for multiple machines to all be sending the same queries
1270*4b22b933Srs200217 
1271*4b22b933Srs200217 Revision 1.180  2003/06/07 06:31:07  cheshire
1272*4b22b933Srs200217 Create little four-line helper function "FindIdenticalRecordInCache()"
1273*4b22b933Srs200217 
1274*4b22b933Srs200217 Revision 1.179  2003/06/07 06:28:13  cheshire
1275*4b22b933Srs200217 For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1276*4b22b933Srs200217 
1277*4b22b933Srs200217 Revision 1.178  2003/06/07 06:25:12  cheshire
1278*4b22b933Srs200217 Update some comments
1279*4b22b933Srs200217 
1280*4b22b933Srs200217 Revision 1.177  2003/06/07 04:50:53  cheshire
1281*4b22b933Srs200217 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1282*4b22b933Srs200217 
1283*4b22b933Srs200217 Revision 1.176  2003/06/07 04:33:26  cheshire
1284*4b22b933Srs200217 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1285*4b22b933Srs200217 Minor change: Increment/decrement logic for q->CurrentAnswers should be in
1286*4b22b933Srs200217 CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
1287*4b22b933Srs200217 
1288*4b22b933Srs200217 Revision 1.175  2003/06/07 04:11:52  cheshire
1289*4b22b933Srs200217 Minor changes to comments and debug messages
1290*4b22b933Srs200217 
1291*4b22b933Srs200217 Revision 1.174  2003/06/07 01:46:38  cheshire
1292*4b22b933Srs200217 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1293*4b22b933Srs200217 
1294*4b22b933Srs200217 Revision 1.173  2003/06/07 01:22:13  cheshire
1295*4b22b933Srs200217 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1296*4b22b933Srs200217 
1297*4b22b933Srs200217 Revision 1.172  2003/06/07 00:59:42  cheshire
1298*4b22b933Srs200217 <rdar://problem/3283454> Need some randomness to spread queries on the network
1299*4b22b933Srs200217 
1300*4b22b933Srs200217 Revision 1.171  2003/06/06 21:41:10  cheshire
1301*4b22b933Srs200217 For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
1302*4b22b933Srs200217 
1303*4b22b933Srs200217 Revision 1.170  2003/06/06 21:38:55  cheshire
1304*4b22b933Srs200217 Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
1305*4b22b933Srs200217 already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
1306*4b22b933Srs200217 
1307*4b22b933Srs200217 Revision 1.169  2003/06/06 21:35:55  cheshire
1308*4b22b933Srs200217 Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
1309*4b22b933Srs200217 (the target is a domain name, but not necessarily a host name)
1310*4b22b933Srs200217 
1311*4b22b933Srs200217 Revision 1.168  2003/06/06 21:33:31  cheshire
1312*4b22b933Srs200217 Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
1313*4b22b933Srs200217 
1314*4b22b933Srs200217 Revision 1.167  2003/06/06 21:30:42  cheshire
1315*4b22b933Srs200217 <rdar://problem/3282962> Don't delay queries for shared record types
1316*4b22b933Srs200217 
1317*4b22b933Srs200217 Revision 1.166  2003/06/06 17:20:14  cheshire
1318*4b22b933Srs200217 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
1319*4b22b933Srs200217 (Global search-and-replace; no functional change to code execution.)
1320*4b22b933Srs200217 
1321*4b22b933Srs200217 Revision 1.165  2003/06/04 02:53:21  cheshire
1322*4b22b933Srs200217 Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1323*4b22b933Srs200217 
1324*4b22b933Srs200217 Revision 1.164  2003/06/04 01:25:33  cheshire
1325*4b22b933Srs200217 <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
1326*4b22b933Srs200217 Display time interval between first and subsequent queries
1327*4b22b933Srs200217 
1328*4b22b933Srs200217 Revision 1.163  2003/06/03 19:58:14  cheshire
1329*4b22b933Srs200217 <rdar://problem/3277665> mDNS_DeregisterService() fixes:
1330*4b22b933Srs200217 When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
1331*4b22b933Srs200217 Guard against a couple of possible mDNS_DeregisterService() race conditions.
1332*4b22b933Srs200217 
1333*4b22b933Srs200217 Revision 1.162  2003/06/03 19:30:39  cheshire
1334*4b22b933Srs200217 Minor addition refinements for
1335*4b22b933Srs200217 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1336*4b22b933Srs200217 
1337*4b22b933Srs200217 Revision 1.161  2003/06/03 18:29:03  cheshire
1338*4b22b933Srs200217 Minor changes to comments and debugf() messages
1339*4b22b933Srs200217 
1340*4b22b933Srs200217 Revision 1.160  2003/06/03 05:02:16  cheshire
1341*4b22b933Srs200217 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1342*4b22b933Srs200217 
1343*4b22b933Srs200217 Revision 1.159  2003/06/03 03:31:57  cheshire
1344*4b22b933Srs200217 <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
1345*4b22b933Srs200217 
1346*4b22b933Srs200217 Revision 1.158  2003/06/02 22:57:09  cheshire
1347*4b22b933Srs200217 Minor clarifying changes to comments and log messages;
1348*4b22b933Srs200217 IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
1349*4b22b933Srs200217 
1350*4b22b933Srs200217 Revision 1.157  2003/05/31 00:09:49  cheshire
1351*4b22b933Srs200217 <rdar://problem/3274862> Add ability to discover what services are on a network
1352*4b22b933Srs200217 
1353*4b22b933Srs200217 Revision 1.156  2003/05/30 23:56:49  cheshire
1354*4b22b933Srs200217 <rdar://problem/3274847> Crash after error in mDNS_RegisterService()
1355*4b22b933Srs200217 Need to set "sr->Extras = mDNSNULL" before returning
1356*4b22b933Srs200217 
1357*4b22b933Srs200217 Revision 1.155  2003/05/30 23:48:00  cheshire
1358*4b22b933Srs200217 <rdar://problem/3274832> Announcements not properly grouped
1359*4b22b933Srs200217 Due to inconsistent setting of rr->LastAPTime at different places in the
1360*4b22b933Srs200217 code, announcements were not properly grouped into a single packet.
1361*4b22b933Srs200217 Fixed by creating a single routine called InitializeLastAPTime().
1362*4b22b933Srs200217 
1363*4b22b933Srs200217 Revision 1.154  2003/05/30 23:38:14  cheshire
1364*4b22b933Srs200217 <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
1365*4b22b933Srs200217 Wrote buffer[32] where it should have said buffer[64]
1366*4b22b933Srs200217 
1367*4b22b933Srs200217 Revision 1.153  2003/05/30 19:10:56  cheshire
1368*4b22b933Srs200217 <rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1369*4b22b933Srs200217 
1370*4b22b933Srs200217 Revision 1.152  2003/05/29 22:39:16  cheshire
1371*4b22b933Srs200217 <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
1372*4b22b933Srs200217 
1373*4b22b933Srs200217 Revision 1.151  2003/05/29 06:35:42  cheshire
1374*4b22b933Srs200217 <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1375*4b22b933Srs200217 
1376*4b22b933Srs200217 Revision 1.150  2003/05/29 06:25:45  cheshire
1377*4b22b933Srs200217 <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1378*4b22b933Srs200217 
1379*4b22b933Srs200217 Revision 1.149  2003/05/29 06:18:39  cheshire
1380*4b22b933Srs200217 <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1381*4b22b933Srs200217 
1382*4b22b933Srs200217 Revision 1.148  2003/05/29 06:11:34  cheshire
1383*4b22b933Srs200217 <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
1384*4b22b933Srs200217 
1385*4b22b933Srs200217 Revision 1.147  2003/05/29 06:01:18  cheshire
1386*4b22b933Srs200217 Change some debugf() calls to LogMsg() calls to help with debugging
1387*4b22b933Srs200217 
1388*4b22b933Srs200217 Revision 1.146  2003/05/28 21:00:44  cheshire
1389*4b22b933Srs200217 Re-enable "immediate answer burst" debugf message
1390*4b22b933Srs200217 
1391*4b22b933Srs200217 Revision 1.145  2003/05/28 20:57:44  cheshire
1392*4b22b933Srs200217 <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
1393*4b22b933Srs200217 known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
1394*4b22b933Srs200217 version of mDNSResponder, so for now we should suppress this warning message.
1395*4b22b933Srs200217 
1396*4b22b933Srs200217 Revision 1.144  2003/05/28 18:05:12  cheshire
1397*4b22b933Srs200217 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1398*4b22b933Srs200217 Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
1399*4b22b933Srs200217 
1400*4b22b933Srs200217 Revision 1.143  2003/05/28 04:31:29  cheshire
1401*4b22b933Srs200217 <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1402*4b22b933Srs200217 
1403*4b22b933Srs200217 Revision 1.142  2003/05/28 03:13:07  cheshire
1404*4b22b933Srs200217 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1405*4b22b933Srs200217 Require that the transport protocol be _udp or _tcp
1406*4b22b933Srs200217 
1407*4b22b933Srs200217 Revision 1.141  2003/05/28 02:19:12  cheshire
1408*4b22b933Srs200217 <rdar://problem/3270634> Misleading messages generated by iChat
1409*4b22b933Srs200217 Better fix: Only generate the log message for queries where the TC bit is set.
1410*4b22b933Srs200217 
1411*4b22b933Srs200217 Revision 1.140  2003/05/28 01:55:24  cheshire
1412*4b22b933Srs200217 Minor change to log messages
1413*4b22b933Srs200217 
1414*4b22b933Srs200217 Revision 1.139  2003/05/28 01:52:51  cheshire
1415*4b22b933Srs200217 <rdar://problem/3270634> Misleading messages generated by iChat
1416*4b22b933Srs200217 
1417*4b22b933Srs200217 Revision 1.138  2003/05/27 22:35:00  cheshire
1418*4b22b933Srs200217 <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1419*4b22b933Srs200217 
1420*4b22b933Srs200217 Revision 1.137  2003/05/27 20:04:33  cheshire
1421*4b22b933Srs200217 <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1422*4b22b933Srs200217 
1423*4b22b933Srs200217 Revision 1.136  2003/05/27 18:50:07  cheshire
1424*4b22b933Srs200217 <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
1425*4b22b933Srs200217 
1426*4b22b933Srs200217 Revision 1.135  2003/05/26 04:57:28  cheshire
1427*4b22b933Srs200217 <rdar://problem/3268953> Delay queries when there are already answers in the cache
1428*4b22b933Srs200217 
1429*4b22b933Srs200217 Revision 1.134  2003/05/26 04:54:54  cheshire
1430*4b22b933Srs200217 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1431*4b22b933Srs200217 Accidentally deleted '%' case from the switch statement
1432*4b22b933Srs200217 
1433*4b22b933Srs200217 Revision 1.133  2003/05/26 03:21:27  cheshire
1434*4b22b933Srs200217 Tidy up address structure naming:
1435*4b22b933Srs200217 mDNSIPAddr         => mDNSv4Addr (for consistency with mDNSv6Addr)
1436*4b22b933Srs200217 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
1437*4b22b933Srs200217 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
1438*4b22b933Srs200217 
1439*4b22b933Srs200217 Revision 1.132  2003/05/26 03:01:26  cheshire
1440*4b22b933Srs200217 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1441*4b22b933Srs200217 
1442*4b22b933Srs200217 Revision 1.131  2003/05/26 00:42:05  cheshire
1443*4b22b933Srs200217 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1444*4b22b933Srs200217 
1445*4b22b933Srs200217 Revision 1.130  2003/05/24 16:39:48  cheshire
1446*4b22b933Srs200217 <rdar://problem/3268631> SendResponses also needs to handle multihoming better
1447*4b22b933Srs200217 
1448*4b22b933Srs200217 Revision 1.129  2003/05/23 02:15:37  cheshire
1449*4b22b933Srs200217 Fixed misleading use of the term "duplicate suppression" where it should have
1450*4b22b933Srs200217 said "known answer suppression". (Duplicate answer suppression is something
1451*4b22b933Srs200217 different, and duplicate question suppression is yet another thing, so the use
1452*4b22b933Srs200217 of the completely vague term "duplicate suppression" was particularly bad.)
1453*4b22b933Srs200217 
1454*4b22b933Srs200217 Revision 1.128  2003/05/23 01:55:13  cheshire
1455*4b22b933Srs200217 <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
1456*4b22b933Srs200217 
1457*4b22b933Srs200217 Revision 1.127  2003/05/23 01:02:15  ksekar
1458*4b22b933Srs200217 <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
1459*4b22b933Srs200217 
1460*4b22b933Srs200217 Revision 1.126  2003/05/22 02:29:22  cheshire
1461*4b22b933Srs200217 <rdar://problem/2984918> SendQueries needs to handle multihoming better
1462*4b22b933Srs200217 Complete rewrite of SendQueries. Works much better now :-)
1463*4b22b933Srs200217 
1464*4b22b933Srs200217 Revision 1.125  2003/05/22 01:50:45  cheshire
1465*4b22b933Srs200217 Fix warnings, and improve log messages
1466*4b22b933Srs200217 
1467*4b22b933Srs200217 Revision 1.124  2003/05/22 01:41:50  cheshire
1468*4b22b933Srs200217 DiscardDeregistrations doesn't need InterfaceID parameter
1469*4b22b933Srs200217 
1470*4b22b933Srs200217 Revision 1.123  2003/05/22 01:38:55  cheshire
1471*4b22b933Srs200217 Change bracketing of #pragma mark
1472*4b22b933Srs200217 
1473*4b22b933Srs200217 Revision 1.122  2003/05/21 19:59:04  cheshire
1474*4b22b933Srs200217 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1475*4b22b933Srs200217 Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
1476*4b22b933Srs200217 
1477*4b22b933Srs200217 Revision 1.121  2003/05/21 17:54:07  ksekar
1478*4b22b933Srs200217 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1479*4b22b933Srs200217 New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
1480*4b22b933Srs200217 
1481*4b22b933Srs200217 Revision 1.120  2003/05/19 22:14:14  ksekar
1482*4b22b933Srs200217 <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
1483*4b22b933Srs200217 
1484*4b22b933Srs200217 Revision 1.119  2003/05/16 01:34:10  cheshire
1485*4b22b933Srs200217 Fix some warnings
1486*4b22b933Srs200217 
1487*4b22b933Srs200217 Revision 1.118  2003/05/14 18:48:40  cheshire
1488*4b22b933Srs200217 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1489*4b22b933Srs200217 More minor refinements:
1490*4b22b933Srs200217 mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
1491*4b22b933Srs200217 mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
1492*4b22b933Srs200217 
1493*4b22b933Srs200217 Revision 1.117  2003/05/14 07:08:36  cheshire
1494*4b22b933Srs200217 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1495*4b22b933Srs200217 Previously, when there was any network configuration change, mDNSResponder
1496*4b22b933Srs200217 would tear down the entire list of active interfaces and start again.
1497*4b22b933Srs200217 That was very disruptive, and caused the entire cache to be flushed,
1498*4b22b933Srs200217 and caused lots of extra network traffic. Now it only removes interfaces
1499*4b22b933Srs200217 that have really gone, and only adds new ones that weren't there before.
1500*4b22b933Srs200217 
1501*4b22b933Srs200217 Revision 1.116  2003/05/14 06:51:56  cheshire
1502*4b22b933Srs200217 <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
1503*4b22b933Srs200217 
1504*4b22b933Srs200217 Revision 1.115  2003/05/14 06:44:31  cheshire
1505*4b22b933Srs200217 Improve debugging message
1506*4b22b933Srs200217 
1507*4b22b933Srs200217 Revision 1.114  2003/05/07 01:47:03  cheshire
1508*4b22b933Srs200217 <rdar://problem/3250330> Also protect against NULL domainlabels
1509*4b22b933Srs200217 
1510*4b22b933Srs200217 Revision 1.113  2003/05/07 00:28:18  cheshire
1511*4b22b933Srs200217 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1512*4b22b933Srs200217 
1513*4b22b933Srs200217 Revision 1.112  2003/05/06 00:00:46  cheshire
1514*4b22b933Srs200217 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1515*4b22b933Srs200217 
1516*4b22b933Srs200217 Revision 1.111  2003/05/05 23:42:08  cheshire
1517*4b22b933Srs200217 <rdar://problem/3245631> Resolves never succeed
1518*4b22b933Srs200217 Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
1519*4b22b933Srs200217 instead of  "rr->LastAPTime = timenow - rr->ThisAPInterval"
1520*4b22b933Srs200217 
1521*4b22b933Srs200217 Revision 1.110  2003/04/30 21:09:59  cheshire
1522*4b22b933Srs200217 <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
1523*4b22b933Srs200217 
1524*4b22b933Srs200217 Revision 1.109  2003/04/26 02:41:56  cheshire
1525*4b22b933Srs200217 <rdar://problem/3241281> Change timenow from a local variable to a structure member
1526*4b22b933Srs200217 
1527*4b22b933Srs200217 Revision 1.108  2003/04/25 01:45:56  cheshire
1528*4b22b933Srs200217 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1529*4b22b933Srs200217 
1530*4b22b933Srs200217 Revision 1.107  2003/04/25 00:41:31  cheshire
1531*4b22b933Srs200217 <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1532*4b22b933Srs200217 
1533*4b22b933Srs200217 Revision 1.106  2003/04/22 03:14:45  cheshire
1534*4b22b933Srs200217 <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1535*4b22b933Srs200217 
1536*4b22b933Srs200217 Revision 1.105  2003/04/22 01:07:43  cheshire
1537*4b22b933Srs200217 <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
1538*4b22b933Srs200217 If TTL parameter is zero, leave record TTL unchanged
1539*4b22b933Srs200217 
1540*4b22b933Srs200217 Revision 1.104  2003/04/21 19:15:52  cheshire
1541*4b22b933Srs200217 Fix some compiler warnings
1542*4b22b933Srs200217 
1543*4b22b933Srs200217 Revision 1.103  2003/04/19 02:26:35  cheshire
1544*4b22b933Srs200217 <rdar://problem/3233804> Incorrect goodbye packet after conflict
1545*4b22b933Srs200217 
1546*4b22b933Srs200217 Revision 1.102  2003/04/17 03:06:28  cheshire
1547*4b22b933Srs200217 <rdar://problem/3231321> No need to query again when a service goes away
1548*4b22b933Srs200217 Set UnansweredQueries to 2 when receiving a "goodbye" packet
1549*4b22b933Srs200217 
1550*4b22b933Srs200217 Revision 1.101  2003/04/15 20:58:31  jgraessl
1551*4b22b933Srs200217 <rdar://problem/3229014> Added a hash to lookup records in the cache.
1552*4b22b933Srs200217 
1553*4b22b933Srs200217 Revision 1.100  2003/04/15 18:53:14  cheshire
1554*4b22b933Srs200217 <rdar://problem/3229064> Bug in ScheduleNextTask
1555*4b22b933Srs200217 mDNS.c 1.94 incorrectly combined two "if" statements into one.
1556*4b22b933Srs200217 
1557*4b22b933Srs200217 Revision 1.99  2003/04/15 18:09:13  jgraessl
1558*4b22b933Srs200217 <rdar://problem/3228892>
1559*4b22b933Srs200217 Reviewed by: Stuart Cheshire
1560*4b22b933Srs200217 Added code to keep track of when the next cache item will expire so we can
1561*4b22b933Srs200217 call TidyRRCache only when necessary.
1562*4b22b933Srs200217 
1563*4b22b933Srs200217 Revision 1.98  2003/04/03 03:43:55  cheshire
1564*4b22b933Srs200217 <rdar://problem/3216837> Off-by-one error in probe rate limiting
1565*4b22b933Srs200217 
1566*4b22b933Srs200217 Revision 1.97  2003/04/02 01:48:17  cheshire
1567*4b22b933Srs200217 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1568*4b22b933Srs200217 Additional fix pointed out by Josh:
1569*4b22b933Srs200217 Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
1570*4b22b933Srs200217 
1571*4b22b933Srs200217 Revision 1.96  2003/04/01 23:58:55  cheshire
1572*4b22b933Srs200217 Minor comment changes
1573*4b22b933Srs200217 
1574*4b22b933Srs200217 Revision 1.95  2003/04/01 23:46:05  cheshire
1575*4b22b933Srs200217 <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
1576*4b22b933Srs200217 mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
1577*4b22b933Srs200217 to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
1578*4b22b933Srs200217 window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
1579*4b22b933Srs200217 FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
1580*4b22b933Srs200217 
1581*4b22b933Srs200217 Revision 1.94  2003/03/29 01:55:19  cheshire
1582*4b22b933Srs200217 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1583*4b22b933Srs200217 Solution: Major cleanup of packet timing and conflict handling rules
1584*4b22b933Srs200217 
1585*4b22b933Srs200217 Revision 1.93  2003/03/28 01:54:36  cheshire
1586*4b22b933Srs200217 Minor tidyup of IPv6 (AAAA) code
1587*4b22b933Srs200217 
1588*4b22b933Srs200217 Revision 1.92  2003/03/27 03:30:55  cheshire
1589*4b22b933Srs200217 <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
1590*4b22b933Srs200217 Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
1591*4b22b933Srs200217 Fixes:
1592*4b22b933Srs200217 1. Make mDNS_DeregisterInterface() safe to call from a callback
1593*4b22b933Srs200217 2. Make HostNameCallback() use DeadvertiseInterface() instead
1594*4b22b933Srs200217    (it never really needed to deregister the interface at all)
1595*4b22b933Srs200217 
1596*4b22b933Srs200217 Revision 1.91  2003/03/15 04:40:36  cheshire
1597*4b22b933Srs200217 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1598*4b22b933Srs200217 
1599*4b22b933Srs200217 Revision 1.90  2003/03/14 20:26:37  cheshire
1600*4b22b933Srs200217 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1601*4b22b933Srs200217 
1602*4b22b933Srs200217 Revision 1.89  2003/03/12 19:57:50  cheshire
1603*4b22b933Srs200217 Fixed typo in debug message
1604*4b22b933Srs200217 
1605*4b22b933Srs200217 Revision 1.88  2003/03/12 00:17:44  cheshire
1606*4b22b933Srs200217 <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
1607*4b22b933Srs200217 
1608*4b22b933Srs200217 Revision 1.87  2003/03/11 01:27:20  cheshire
1609*4b22b933Srs200217 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1610*4b22b933Srs200217 
1611*4b22b933Srs200217 Revision 1.86  2003/03/06 20:44:33  cheshire
1612*4b22b933Srs200217 Comment tidyup
1613*4b22b933Srs200217 
1614*4b22b933Srs200217 Revision 1.85  2003/03/05 03:38:35  cheshire
1615*4b22b933Srs200217 <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
1616*4b22b933Srs200217 Fixed by leaving client in list after conflict, until client explicitly deallocates
1617*4b22b933Srs200217 
1618*4b22b933Srs200217 Revision 1.84  2003/03/05 01:27:30  cheshire
1619*4b22b933Srs200217 <rdar://problem/3185482> Different TTL for multicast versus unicast responses
1620*4b22b933Srs200217 When building unicast responses, record TTLs are capped to 10 seconds
1621*4b22b933Srs200217 
1622*4b22b933Srs200217 Revision 1.83  2003/03/04 23:48:52  cheshire
1623*4b22b933Srs200217 <rdar://problem/3188865> Double probes after wake from sleep
1624*4b22b933Srs200217 Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1625*4b22b933Srs200217 
1626*4b22b933Srs200217 Revision 1.82  2003/03/04 23:38:29  cheshire
1627*4b22b933Srs200217 <rdar://problem/3099194> mDNSResponder needs performance improvements
1628*4b22b933Srs200217 Only set rr->CRActiveQuestion to point to the
1629*4b22b933Srs200217 currently active representative of a question set
1630*4b22b933Srs200217 
1631*4b22b933Srs200217 Revision 1.81  2003/02/21 03:35:34  cheshire
1632*4b22b933Srs200217 <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
1633*4b22b933Srs200217 
1634*4b22b933Srs200217 Revision 1.80  2003/02/21 02:47:53  cheshire
1635*4b22b933Srs200217 <rdar://problem/3099194> mDNSResponder needs performance improvements
1636*4b22b933Srs200217 Several places in the code were calling CacheRRActive(), which searched the entire
1637*4b22b933Srs200217 question list every time, to see if this cache resource record answers any question.
1638*4b22b933Srs200217 Instead, we now have a field "CRActiveQuestion" in the resource record structure
1639*4b22b933Srs200217 
1640*4b22b933Srs200217 Revision 1.79  2003/02/21 01:54:07  cheshire
1641*4b22b933Srs200217 <rdar://problem/3099194> mDNSResponder needs performance improvements
1642*4b22b933Srs200217 Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1643*4b22b933Srs200217 
1644*4b22b933Srs200217 Revision 1.78  2003/02/20 06:48:32  cheshire
1645*4b22b933Srs200217 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
1646*4b22b933Srs200217 Reviewed by: Josh Graessley, Bob Bradley
1647*4b22b933Srs200217 
1648*4b22b933Srs200217 Revision 1.77  2003/01/31 03:35:59  cheshire
1649*4b22b933Srs200217 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1650*4b22b933Srs200217 When there were *two* active questions in the list, they were incorrectly
1651*4b22b933Srs200217 finding *each other* and *both* being marked as duplicates of another question
1652*4b22b933Srs200217 
1653*4b22b933Srs200217 Revision 1.76  2003/01/29 02:46:37  cheshire
1654*4b22b933Srs200217 Fix for IPv6:
1655*4b22b933Srs200217 A physical interface is identified solely by its InterfaceID (not by IP and type).
1656*4b22b933Srs200217 On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
1657*4b22b933Srs200217 In cases where the requested outbound protocol (v4 or v6) is not supported on
1658*4b22b933Srs200217 that InterfaceID, the platform support layer should simply discard that packet.
1659*4b22b933Srs200217 
1660*4b22b933Srs200217 Revision 1.75  2003/01/29 01:47:40  cheshire
1661*4b22b933Srs200217 Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1662*4b22b933Srs200217 
1663*4b22b933Srs200217 Revision 1.74  2003/01/28 05:26:25  cheshire
1664*4b22b933Srs200217 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1665*4b22b933Srs200217 Add 'Active' flag for interfaces
1666*4b22b933Srs200217 
1667*4b22b933Srs200217 Revision 1.73  2003/01/28 03:45:12  cheshire
1668*4b22b933Srs200217 Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1669*4b22b933Srs200217 
1670*4b22b933Srs200217 Revision 1.72  2003/01/28 01:49:48  cheshire
1671*4b22b933Srs200217 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1672*4b22b933Srs200217 FindDuplicateQuestion() was incorrectly finding the question itself in the list,
1673*4b22b933Srs200217 and incorrectly marking it as a duplicate (of itself), so that it became inactive.
1674*4b22b933Srs200217 
1675*4b22b933Srs200217 Revision 1.71  2003/01/28 01:41:44  cheshire
1676*4b22b933Srs200217 <rdar://problem/3153091> Race condition when network change causes bad stuff
1677*4b22b933Srs200217 When an interface goes away, interface-specific questions on that interface become orphaned.
1678*4b22b933Srs200217 Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
1679*4b22b933Srs200217 Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
1680*4b22b933Srs200217 
1681*4b22b933Srs200217 Revision 1.70  2003/01/23 19:00:20  cheshire
1682*4b22b933Srs200217 Protect against infinite loops in mDNS_Execute
1683*4b22b933Srs200217 
1684*4b22b933Srs200217 Revision 1.69  2003/01/21 22:56:32  jgraessl
1685*4b22b933Srs200217 <rdar://problem/3124348>  service name changes are not properly handled
1686*4b22b933Srs200217 Submitted by: Stuart Cheshire
1687*4b22b933Srs200217 Reviewed by: Joshua Graessley
1688*4b22b933Srs200217 Applying changes for 3124348 to main branch. 3124348 changes went in to a
1689*4b22b933Srs200217 branch for SU.
1690*4b22b933Srs200217 
1691*4b22b933Srs200217 Revision 1.68  2003/01/17 04:09:27  cheshire
1692*4b22b933Srs200217 <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
1693*4b22b933Srs200217 
1694*4b22b933Srs200217 Revision 1.67  2003/01/17 03:56:45  cheshire
1695*4b22b933Srs200217 Default 24-hour TTL is far too long. Changing to two hours.
1696*4b22b933Srs200217 
1697*4b22b933Srs200217 Revision 1.66  2003/01/13 23:49:41  jgraessl
1698*4b22b933Srs200217 Merged changes for the following fixes in to top of tree:
1699*4b22b933Srs200217 <rdar://problem/3086540>  computer name changes not handled properly
1700*4b22b933Srs200217 <rdar://problem/3124348>  service name changes are not properly handled
1701*4b22b933Srs200217 <rdar://problem/3124352>  announcements sent in pairs, failing chattiness test
1702*4b22b933Srs200217 
1703*4b22b933Srs200217 Revision 1.65  2002/12/23 22:13:28  jgraessl
1704*4b22b933Srs200217 Reviewed by: Stuart Cheshire
1705*4b22b933Srs200217 Initial IPv6 support for mDNSResponder.
1706*4b22b933Srs200217 
1707*4b22b933Srs200217 Revision 1.64  2002/11/26 20:49:06  cheshire
1708*4b22b933Srs200217 <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
1709*4b22b933Srs200217 
1710*4b22b933Srs200217 Revision 1.63  2002/09/21 20:44:49  zarzycki
1711*4b22b933Srs200217 Added APSL info
1712*4b22b933Srs200217 
1713*4b22b933Srs200217 Revision 1.62  2002/09/20 03:25:37  cheshire
1714*4b22b933Srs200217 Fix some compiler warnings
1715*4b22b933Srs200217 
1716*4b22b933Srs200217 Revision 1.61  2002/09/20 01:05:24  cheshire
1717*4b22b933Srs200217 Don't kill the Extras list in mDNS_DeregisterService()
1718*4b22b933Srs200217 
1719*4b22b933Srs200217 Revision 1.60  2002/09/19 23:47:35  cheshire
1720*4b22b933Srs200217 Added mDNS_RegisterNoSuchService() function for assertion of non-existence
1721*4b22b933Srs200217 of a particular named service
1722*4b22b933Srs200217 
1723*4b22b933Srs200217 Revision 1.59  2002/09/19 21:25:34  cheshire
1724*4b22b933Srs200217 mDNS_snprintf() doesn't need to be in a separate file
1725*4b22b933Srs200217 
1726*4b22b933Srs200217 Revision 1.58  2002/09/19 04:20:43  cheshire
1727*4b22b933Srs200217 Remove high-ascii characters that confuse some systems
1728*4b22b933Srs200217 
1729*4b22b933Srs200217 Revision 1.57  2002/09/17 01:07:08  cheshire
1730*4b22b933Srs200217 Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1731*4b22b933Srs200217 
1732*4b22b933Srs200217 Revision 1.56  2002/09/16 19:44:17  cheshire
1733*4b22b933Srs200217 Merge in license terms from Quinn's copy, in preparation for Darwin release
1734*4b22b933Srs200217 */
1735*4b22b933Srs200217 
1736*4b22b933Srs200217 #pragma ident	"%Z%%M%	%I%	%E% SMI"
1737*4b22b933Srs200217 
1738*4b22b933Srs200217 #include "DNSCommon.h"                  // Defines general DNS untility routines
1739*4b22b933Srs200217 #include "uDNS.h"						// Defines entry points into unicast-specific routines
1740*4b22b933Srs200217 // Disable certain benign warnings with Microsoft compilers
1741*4b22b933Srs200217 #if(defined(_MSC_VER))
1742*4b22b933Srs200217 	// Disable "conditional expression is constant" warning for debug macros.
1743*4b22b933Srs200217 	// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
1744*4b22b933Srs200217 	// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
1745*4b22b933Srs200217 	#pragma warning(disable:4127)
1746*4b22b933Srs200217 
1747*4b22b933Srs200217 	// Disable "assignment within conditional expression".
1748*4b22b933Srs200217 	// Other compilers understand the convention that if you place the assignment expression within an extra pair
1749*4b22b933Srs200217 	// of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
1750*4b22b933Srs200217 	// The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
1751*4b22b933Srs200217 	// to the compiler that the assignment is intentional, we have to just turn this warning off completely.
1752*4b22b933Srs200217 	#pragma warning(disable:4706)
1753*4b22b933Srs200217 #endif
1754*4b22b933Srs200217 
1755*4b22b933Srs200217 // ***************************************************************************
1756*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
1757*4b22b933Srs200217 #pragma mark -
1758*4b22b933Srs200217 #pragma mark - Program Constants
1759*4b22b933Srs200217 #endif
1760*4b22b933Srs200217 
1761*4b22b933Srs200217 mDNSexport const mDNSIPPort      zeroIPPort        = { { 0 } };
1762*4b22b933Srs200217 mDNSexport const mDNSv4Addr      zerov4Addr        = { { 0 } };
1763*4b22b933Srs200217 mDNSexport const mDNSv6Addr      zerov6Addr        = { { 0 } };
1764*4b22b933Srs200217 mDNSexport const mDNSEthAddr     zeroEthAddr       = { { 0 } };
1765*4b22b933Srs200217 mDNSexport const mDNSv4Addr      onesIPv4Addr      = { { 255, 255, 255, 255 } };
1766*4b22b933Srs200217 mDNSexport const mDNSv6Addr      onesIPv6Addr      = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
1767*4b22b933Srs200217 mDNSexport const mDNSAddr        zeroAddr          = { mDNSAddrType_None, {{{ 0 }}} };
1768*4b22b933Srs200217 
1769*4b22b933Srs200217 mDNSexport const mDNSInterfaceID mDNSInterface_Any        = 0;
1770*4b22b933Srs200217 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly  = (mDNSInterfaceID)1;
1771*4b22b933Srs200217 
1772*4b22b933Srs200217 mDNSlocal  const mDNSInterfaceID mDNSInterfaceMark        = (mDNSInterfaceID)~0;
1773*4b22b933Srs200217 
1774*4b22b933Srs200217 #define UnicastDNSPortAsNumber   53
1775*4b22b933Srs200217 #define NATPMPPortAsNumber       5351
1776*4b22b933Srs200217 #define DNSEXTPortAsNumber       5352		// Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
1777*4b22b933Srs200217 #define MulticastDNSPortAsNumber 5353
1778*4b22b933Srs200217 #define LoopbackIPCPortAsNumber  5354
1779*4b22b933Srs200217 
1780*4b22b933Srs200217 mDNSexport const mDNSIPPort UnicastDNSPort     = { { UnicastDNSPortAsNumber   >> 8, UnicastDNSPortAsNumber   & 0xFF } };
1781*4b22b933Srs200217 mDNSexport const mDNSIPPort NATPMPPort         = { { NATPMPPortAsNumber       >> 8, NATPMPPortAsNumber       & 0xFF } };
1782*4b22b933Srs200217 mDNSexport const mDNSIPPort DNSEXTPort         = { { DNSEXTPortAsNumber       >> 8, DNSEXTPortAsNumber       & 0xFF } };
1783*4b22b933Srs200217 mDNSexport const mDNSIPPort MulticastDNSPort   = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
1784*4b22b933Srs200217 mDNSexport const mDNSIPPort LoopbackIPCPort    = { { LoopbackIPCPortAsNumber  >> 8, LoopbackIPCPortAsNumber  & 0xFF } };
1785*4b22b933Srs200217 
1786*4b22b933Srs200217 mDNSexport const mDNSv4Addr AllDNSAdminGroup   = { { 239, 255, 255, 251 } };
1787*4b22b933Srs200217 mDNSexport const mDNSAddr   AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224,   0,   0, 251 } } } };
1788*4b22b933Srs200217 mDNSexport const mDNSAddr   AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
1789*4b22b933Srs200217 
1790*4b22b933Srs200217 mDNSexport const mDNSOpaque16 zeroID          = { { 0, 0 } };
1791*4b22b933Srs200217 mDNSexport const mDNSOpaque16 QueryFlags      = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery,                0 } };
1792*4b22b933Srs200217 mDNSexport const mDNSOpaque16 uQueryFlags     = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
1793*4b22b933Srs200217 mDNSexport const mDNSOpaque16 ResponseFlags   = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
1794*4b22b933Srs200217 mDNSexport const mDNSOpaque16 UpdateReqFlags  = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_Update,                  0 } };
1795*4b22b933Srs200217 mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update,                  0 } };
1796*4b22b933Srs200217 
1797*4b22b933Srs200217 // Any records bigger than this are considered 'large' records
1798*4b22b933Srs200217 #define SmallRecordLimit 1024
1799*4b22b933Srs200217 
1800*4b22b933Srs200217 #define kMaxUpdateCredits 10
1801*4b22b933Srs200217 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1802*4b22b933Srs200217 
1803*4b22b933Srs200217 mDNSexport const char *const mDNS_DomainTypeNames[] =
1804*4b22b933Srs200217 	{
1805*4b22b933Srs200217 	 "b._dns-sd._udp.",		// Browse
1806*4b22b933Srs200217 	"db._dns-sd._udp.",		// Default Browse
1807*4b22b933Srs200217 	"lb._dns-sd._udp.",		// Legacy Browse
1808*4b22b933Srs200217 	 "r._dns-sd._udp.",		// Registration
1809*4b22b933Srs200217 	"dr._dns-sd._udp."		// Default Registration
1810*4b22b933Srs200217 	};
1811*4b22b933Srs200217 
1812*4b22b933Srs200217 #ifdef UNICAST_DISABLED
1813*4b22b933Srs200217 #define uDNS_IsActiveQuery(q, u) mDNSfalse
1814*4b22b933Srs200217 #endif
1815*4b22b933Srs200217 
1816*4b22b933Srs200217 // ***************************************************************************
1817*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
1818*4b22b933Srs200217 #pragma mark -
1819*4b22b933Srs200217 #pragma mark - Specialized mDNS version of vsnprintf
1820*4b22b933Srs200217 #endif
1821*4b22b933Srs200217 
1822*4b22b933Srs200217 static const struct mDNSprintf_format
1823*4b22b933Srs200217 	{
1824*4b22b933Srs200217 	unsigned      leftJustify : 1;
1825*4b22b933Srs200217 	unsigned      forceSign : 1;
1826*4b22b933Srs200217 	unsigned      zeroPad : 1;
1827*4b22b933Srs200217 	unsigned      havePrecision : 1;
1828*4b22b933Srs200217 	unsigned      hSize : 1;
1829*4b22b933Srs200217 	unsigned      lSize : 1;
1830*4b22b933Srs200217 	char          altForm;
1831*4b22b933Srs200217 	char          sign;		// +, - or space
1832*4b22b933Srs200217 	unsigned int  fieldWidth;
1833*4b22b933Srs200217 	unsigned int  precision;
1834*4b22b933Srs200217 	} mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1835*4b22b933Srs200217 
mDNS_vsnprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,va_list arg)1836*4b22b933Srs200217 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
1837*4b22b933Srs200217 	{
1838*4b22b933Srs200217 	mDNSu32 nwritten = 0;
1839*4b22b933Srs200217 	int c;
1840*4b22b933Srs200217 	if (buflen == 0) return(0);
1841*4b22b933Srs200217 	buflen--;		// Pre-reserve one space in the buffer for the terminating null
1842*4b22b933Srs200217 	if (buflen == 0) goto exit;
1843*4b22b933Srs200217 
1844*4b22b933Srs200217 	for (c = *fmt; c != 0; c = *++fmt)
1845*4b22b933Srs200217 		{
1846*4b22b933Srs200217 		if (c != '%')
1847*4b22b933Srs200217 			{
1848*4b22b933Srs200217 			*sbuffer++ = (char)c;
1849*4b22b933Srs200217 			if (++nwritten >= buflen) goto exit;
1850*4b22b933Srs200217 			}
1851*4b22b933Srs200217 		else
1852*4b22b933Srs200217 			{
1853*4b22b933Srs200217 			unsigned int i=0, j;
1854*4b22b933Srs200217 			// The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
1855*4b22b933Srs200217 			// generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
1856*4b22b933Srs200217 			// The size needs to be enough for a 256-byte domain name plus some error text.
1857*4b22b933Srs200217 			#define mDNS_VACB_Size 300
1858*4b22b933Srs200217 			char mDNS_VACB[mDNS_VACB_Size];
1859*4b22b933Srs200217 			#define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
1860*4b22b933Srs200217 			#define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
1861*4b22b933Srs200217 			char *s = mDNS_VACB_Lim, *digits;
1862*4b22b933Srs200217 			struct mDNSprintf_format F = mDNSprintf_format_default;
1863*4b22b933Srs200217 
1864*4b22b933Srs200217 			while (1)	//  decode flags
1865*4b22b933Srs200217 				{
1866*4b22b933Srs200217 				c = *++fmt;
1867*4b22b933Srs200217 				if      (c == '-') F.leftJustify = 1;
1868*4b22b933Srs200217 				else if (c == '+') F.forceSign = 1;
1869*4b22b933Srs200217 				else if (c == ' ') F.sign = ' ';
1870*4b22b933Srs200217 				else if (c == '#') F.altForm++;
1871*4b22b933Srs200217 				else if (c == '0') F.zeroPad = 1;
1872*4b22b933Srs200217 				else break;
1873*4b22b933Srs200217 				}
1874*4b22b933Srs200217 
1875*4b22b933Srs200217 			if (c == '*')	//  decode field width
1876*4b22b933Srs200217 				{
1877*4b22b933Srs200217 				int f = va_arg(arg, int);
1878*4b22b933Srs200217 				if (f < 0) { f = -f; F.leftJustify = 1; }
1879*4b22b933Srs200217 				F.fieldWidth = (unsigned int)f;
1880*4b22b933Srs200217 				c = *++fmt;
1881*4b22b933Srs200217 				}
1882*4b22b933Srs200217 			else
1883*4b22b933Srs200217 				{
1884*4b22b933Srs200217 				for (; c >= '0' && c <= '9'; c = *++fmt)
1885*4b22b933Srs200217 					F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
1886*4b22b933Srs200217 				}
1887*4b22b933Srs200217 
1888*4b22b933Srs200217 			if (c == '.')	//  decode precision
1889*4b22b933Srs200217 				{
1890*4b22b933Srs200217 				if ((c = *++fmt) == '*')
1891*4b22b933Srs200217 					{ F.precision = va_arg(arg, unsigned int); c = *++fmt; }
1892*4b22b933Srs200217 				else for (; c >= '0' && c <= '9'; c = *++fmt)
1893*4b22b933Srs200217 						F.precision = (10 * F.precision) + (c - '0');
1894*4b22b933Srs200217 				F.havePrecision = 1;
1895*4b22b933Srs200217 				}
1896*4b22b933Srs200217 
1897*4b22b933Srs200217 			if (F.leftJustify) F.zeroPad = 0;
1898*4b22b933Srs200217 
1899*4b22b933Srs200217 			conv:
1900*4b22b933Srs200217 			switch (c)	//  perform appropriate conversion
1901*4b22b933Srs200217 				{
1902*4b22b933Srs200217 				unsigned long n;
1903*4b22b933Srs200217 				case 'h' :	F.hSize = 1; c = *++fmt; goto conv;
1904*4b22b933Srs200217 				case 'l' :	// fall through
1905*4b22b933Srs200217 				case 'L' :	F.lSize = 1; c = *++fmt; goto conv;
1906*4b22b933Srs200217 				case 'd' :
1907*4b22b933Srs200217 				case 'i' :	if (F.lSize) n = (unsigned long)va_arg(arg, long);
1908*4b22b933Srs200217 							else n = (unsigned long)va_arg(arg, int);
1909*4b22b933Srs200217 							if (F.hSize) n = (short) n;
1910*4b22b933Srs200217 							if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
1911*4b22b933Srs200217 							else if (F.forceSign) F.sign = '+';
1912*4b22b933Srs200217 							goto decimal;
1913*4b22b933Srs200217 				case 'u' :	if (F.lSize) n = va_arg(arg, unsigned long);
1914*4b22b933Srs200217 							else n = va_arg(arg, unsigned int);
1915*4b22b933Srs200217 							if (F.hSize) n = (unsigned short) n;
1916*4b22b933Srs200217 							F.sign = 0;
1917*4b22b933Srs200217 							goto decimal;
1918*4b22b933Srs200217 				decimal:	if (!F.havePrecision)
1919*4b22b933Srs200217 								{
1920*4b22b933Srs200217 								if (F.zeroPad)
1921*4b22b933Srs200217 									{
1922*4b22b933Srs200217 									F.precision = F.fieldWidth;
1923*4b22b933Srs200217 									if (F.sign) --F.precision;
1924*4b22b933Srs200217 									}
1925*4b22b933Srs200217 								if (F.precision < 1) F.precision = 1;
1926*4b22b933Srs200217 								}
1927*4b22b933Srs200217 							if (F.precision > mDNS_VACB_Size - 1)
1928*4b22b933Srs200217 								F.precision = mDNS_VACB_Size - 1;
1929*4b22b933Srs200217 							for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
1930*4b22b933Srs200217 							for (; i < F.precision; i++) *--s = '0';
1931*4b22b933Srs200217 							if (F.sign) { *--s = F.sign; i++; }
1932*4b22b933Srs200217 							break;
1933*4b22b933Srs200217 
1934*4b22b933Srs200217 				case 'o' :	if (F.lSize) n = va_arg(arg, unsigned long);
1935*4b22b933Srs200217 							else n = va_arg(arg, unsigned int);
1936*4b22b933Srs200217 							if (F.hSize) n = (unsigned short) n;
1937*4b22b933Srs200217 							if (!F.havePrecision)
1938*4b22b933Srs200217 								{
1939*4b22b933Srs200217 								if (F.zeroPad) F.precision = F.fieldWidth;
1940*4b22b933Srs200217 								if (F.precision < 1) F.precision = 1;
1941*4b22b933Srs200217 								}
1942*4b22b933Srs200217 							if (F.precision > mDNS_VACB_Size - 1)
1943*4b22b933Srs200217 								F.precision = mDNS_VACB_Size - 1;
1944*4b22b933Srs200217 							for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
1945*4b22b933Srs200217 							if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
1946*4b22b933Srs200217 							for (; i < F.precision; i++) *--s = '0';
1947*4b22b933Srs200217 							break;
1948*4b22b933Srs200217 
1949*4b22b933Srs200217 				case 'a' :	{
1950*4b22b933Srs200217 							unsigned char *a = va_arg(arg, unsigned char *);
1951*4b22b933Srs200217 							if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
1952*4b22b933Srs200217 							else
1953*4b22b933Srs200217 								{
1954*4b22b933Srs200217 								s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
1955*4b22b933Srs200217 								if (F.altForm)
1956*4b22b933Srs200217 									{
1957*4b22b933Srs200217 									mDNSAddr *ip = (mDNSAddr*)a;
1958*4b22b933Srs200217 									switch (ip->type)
1959*4b22b933Srs200217 										{
1960*4b22b933Srs200217 										case mDNSAddrType_IPv4: F.precision =  4; a = (unsigned char *)&ip->ip.v4; break;
1961*4b22b933Srs200217 										case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
1962*4b22b933Srs200217 										default:                F.precision =  0; break;
1963*4b22b933Srs200217 										}
1964*4b22b933Srs200217 									}
1965*4b22b933Srs200217 								switch (F.precision)
1966*4b22b933Srs200217 									{
1967*4b22b933Srs200217 									case  4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
1968*4b22b933Srs200217 														a[0], a[1], a[2], a[3]); break;
1969*4b22b933Srs200217 									case  6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
1970*4b22b933Srs200217 														a[0], a[1], a[2], a[3], a[4], a[5]); break;
1971*4b22b933Srs200217 									case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
1972*4b22b933Srs200217 														"%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
1973*4b22b933Srs200217 														a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
1974*4b22b933Srs200217 														a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
1975*4b22b933Srs200217 									default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
1976*4b22b933Srs200217 														" address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
1977*4b22b933Srs200217 									}
1978*4b22b933Srs200217 								}
1979*4b22b933Srs200217 							}
1980*4b22b933Srs200217 							break;
1981*4b22b933Srs200217 
1982*4b22b933Srs200217 				case 'p' :	F.havePrecision = F.lSize = 1;
1983*4b22b933Srs200217 							F.precision = 8;
1984*4b22b933Srs200217 				case 'X' :	digits = "0123456789ABCDEF";
1985*4b22b933Srs200217 							goto hexadecimal;
1986*4b22b933Srs200217 				case 'x' :	digits = "0123456789abcdef";
1987*4b22b933Srs200217 				hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
1988*4b22b933Srs200217 							else n = va_arg(arg, unsigned int);
1989*4b22b933Srs200217 							if (F.hSize) n = (unsigned short) n;
1990*4b22b933Srs200217 							if (!F.havePrecision)
1991*4b22b933Srs200217 								{
1992*4b22b933Srs200217 								if (F.zeroPad)
1993*4b22b933Srs200217 									{
1994*4b22b933Srs200217 									F.precision = F.fieldWidth;
1995*4b22b933Srs200217 									if (F.altForm) F.precision -= 2;
1996*4b22b933Srs200217 									}
1997*4b22b933Srs200217 								if (F.precision < 1) F.precision = 1;
1998*4b22b933Srs200217 								}
1999*4b22b933Srs200217 							if (F.precision > mDNS_VACB_Size - 1)
2000*4b22b933Srs200217 								F.precision = mDNS_VACB_Size - 1;
2001*4b22b933Srs200217 							for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
2002*4b22b933Srs200217 							for (; i < F.precision; i++) *--s = '0';
2003*4b22b933Srs200217 							if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
2004*4b22b933Srs200217 							break;
2005*4b22b933Srs200217 
2006*4b22b933Srs200217 				case 'c' :	*--s = (char)va_arg(arg, int); i = 1; break;
2007*4b22b933Srs200217 
2008*4b22b933Srs200217 				case 's' :	s = va_arg(arg, char *);
2009*4b22b933Srs200217 							if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
2010*4b22b933Srs200217 							else switch (F.altForm)
2011*4b22b933Srs200217 								{
2012*4b22b933Srs200217 								case 0: i=0;
2013*4b22b933Srs200217 										if (!F.havePrecision)				// C string
2014*4b22b933Srs200217 											while(s[i]) i++;
2015*4b22b933Srs200217 										else
2016*4b22b933Srs200217 											{
2017*4b22b933Srs200217 											while ((i < F.precision) && s[i]) i++;
2018*4b22b933Srs200217 											// Make sure we don't truncate in the middle of a UTF-8 character
2019*4b22b933Srs200217 											// If last character we got was any kind of UTF-8 multi-byte character,
2020*4b22b933Srs200217 											// then see if we have to back up.
2021*4b22b933Srs200217 											// This is not as easy as the similar checks below, because
2022*4b22b933Srs200217 											// here we can't assume it's safe to examine the *next* byte, so we
2023*4b22b933Srs200217 											// have to confine ourselves to working only backwards in the string.
2024*4b22b933Srs200217 											j = i;		// Record where we got to
2025*4b22b933Srs200217 											// Now, back up until we find first non-continuation-char
2026*4b22b933Srs200217 											while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
2027*4b22b933Srs200217 											// Now s[i-1] is the first non-continuation-char
2028*4b22b933Srs200217 											// and (j-i) is the number of continuation-chars we found
2029*4b22b933Srs200217 											if (i>0 && (s[i-1] & 0xC0) == 0xC0)	// If we found a start-char
2030*4b22b933Srs200217 												{
2031*4b22b933Srs200217 												i--;		// Tentatively eliminate this start-char as well
2032*4b22b933Srs200217 												// Now (j-i) is the number of characters we're considering eliminating.
2033*4b22b933Srs200217 												// To be legal UTF-8, the start-char must contain (j-i) one-bits,
2034*4b22b933Srs200217 												// followed by a zero bit. If we shift it right by (7-(j-i)) bits
2035*4b22b933Srs200217 												// (with sign extension) then the result has to be 0xFE.
2036*4b22b933Srs200217 												// If this is right, then we reinstate the tentatively eliminated bytes.
2037*4b22b933Srs200217 												if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
2038*4b22b933Srs200217 												}
2039*4b22b933Srs200217 											}
2040*4b22b933Srs200217 										break;
2041*4b22b933Srs200217 								case 1: i = (unsigned char) *s++; break;	// Pascal string
2042*4b22b933Srs200217 								case 2: {									// DNS label-sequence name
2043*4b22b933Srs200217 										unsigned char *a = (unsigned char *)s;
2044*4b22b933Srs200217 										s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
2045*4b22b933Srs200217 										if (*a == 0) *s++ = '.';	// Special case for root DNS name
2046*4b22b933Srs200217 										while (*a)
2047*4b22b933Srs200217 											{
2048*4b22b933Srs200217 											if (*a > 63)
2049*4b22b933Srs200217 												{ s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
2050*4b22b933Srs200217 											if (s + *a >= &mDNS_VACB[254])
2051*4b22b933Srs200217 												{ s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
2052*4b22b933Srs200217 											s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a);
2053*4b22b933Srs200217 											a += 1 + *a;
2054*4b22b933Srs200217 											}
2055*4b22b933Srs200217 										i = (mDNSu32)(s - mDNS_VACB);
2056*4b22b933Srs200217 										s = mDNS_VACB;	// Reset s back to the start of the buffer
2057*4b22b933Srs200217 										break;
2058*4b22b933Srs200217 										}
2059*4b22b933Srs200217 								}
2060*4b22b933Srs200217 							// Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
2061*4b22b933Srs200217 							if (F.havePrecision && i > F.precision)
2062*4b22b933Srs200217 								{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
2063*4b22b933Srs200217 							break;
2064*4b22b933Srs200217 
2065*4b22b933Srs200217 				case 'n' :	s = va_arg(arg, char *);
2066*4b22b933Srs200217 							if      (F.hSize) * (short *) s = (short)nwritten;
2067*4b22b933Srs200217 							else if (F.lSize) * (long  *) s = (long)nwritten;
2068*4b22b933Srs200217 							else              * (int   *) s = (int)nwritten;
2069*4b22b933Srs200217 							continue;
2070*4b22b933Srs200217 
2071*4b22b933Srs200217 				default:	s = mDNS_VACB;
2072*4b22b933Srs200217 							i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
2073*4b22b933Srs200217 
2074*4b22b933Srs200217 				case '%' :	*sbuffer++ = (char)c;
2075*4b22b933Srs200217 							if (++nwritten >= buflen) goto exit;
2076*4b22b933Srs200217 							break;
2077*4b22b933Srs200217 				}
2078*4b22b933Srs200217 
2079*4b22b933Srs200217 			if (i < F.fieldWidth && !F.leftJustify)			// Pad on the left
2080*4b22b933Srs200217 				do	{
2081*4b22b933Srs200217 					*sbuffer++ = ' ';
2082*4b22b933Srs200217 					if (++nwritten >= buflen) goto exit;
2083*4b22b933Srs200217 					} while (i < --F.fieldWidth);
2084*4b22b933Srs200217 
2085*4b22b933Srs200217 			// Make sure we don't truncate in the middle of a UTF-8 character.
2086*4b22b933Srs200217 			// Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
2087*4b22b933Srs200217 			// allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
2088*4b22b933Srs200217 			// so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
2089*4b22b933Srs200217 			// formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
2090*4b22b933Srs200217 			if (i > buflen - nwritten)
2091*4b22b933Srs200217 				{ i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
2092*4b22b933Srs200217 			for (j=0; j<i; j++) *sbuffer++ = *s++;			// Write the converted result
2093*4b22b933Srs200217 			nwritten += i;
2094*4b22b933Srs200217 			if (nwritten >= buflen) goto exit;
2095*4b22b933Srs200217 
2096*4b22b933Srs200217 			for (; i < F.fieldWidth; i++)					// Pad on the right
2097*4b22b933Srs200217 				{
2098*4b22b933Srs200217 				*sbuffer++ = ' ';
2099*4b22b933Srs200217 				if (++nwritten >= buflen) goto exit;
2100*4b22b933Srs200217 				}
2101*4b22b933Srs200217 			}
2102*4b22b933Srs200217 		}
2103*4b22b933Srs200217 	exit:
2104*4b22b933Srs200217 	*sbuffer++ = 0;
2105*4b22b933Srs200217 	return(nwritten);
2106*4b22b933Srs200217 	}
2107*4b22b933Srs200217 
mDNS_snprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,...)2108*4b22b933Srs200217 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
2109*4b22b933Srs200217 	{
2110*4b22b933Srs200217 	mDNSu32 length;
2111*4b22b933Srs200217 
2112*4b22b933Srs200217 	va_list ptr;
2113*4b22b933Srs200217 	va_start(ptr,fmt);
2114*4b22b933Srs200217 	length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
2115*4b22b933Srs200217 	va_end(ptr);
2116*4b22b933Srs200217 
2117*4b22b933Srs200217 	return(length);
2118*4b22b933Srs200217 	}
2119*4b22b933Srs200217 
2120*4b22b933Srs200217 // ***************************************************************************
2121*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
2122*4b22b933Srs200217 #pragma mark -
2123*4b22b933Srs200217 #pragma mark - General Utility Functions
2124*4b22b933Srs200217 #endif
2125*4b22b933Srs200217 
2126*4b22b933Srs200217 #define InitialQuestionInterval (mDNSPlatformOneSecond/2)
2127*4b22b933Srs200217 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
2128*4b22b933Srs200217 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
2129*4b22b933Srs200217 
SetNextQueryTime(mDNS * const m,const DNSQuestion * const q)2130*4b22b933Srs200217 mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
2131*4b22b933Srs200217 	{
2132*4b22b933Srs200217 	if (ActiveQuestion(q))
2133*4b22b933Srs200217 		if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
2134*4b22b933Srs200217 			m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
2135*4b22b933Srs200217 	}
2136*4b22b933Srs200217 
CacheGroupForName(const mDNS * const m,const mDNSu32 slot,const mDNSu32 namehash,const domainname * const name)2137*4b22b933Srs200217 mDNSlocal CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
2138*4b22b933Srs200217 	{
2139*4b22b933Srs200217 	CacheGroup *cg;
2140*4b22b933Srs200217 	for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
2141*4b22b933Srs200217 		if (cg->namehash == namehash && SameDomainName(cg->name, name))
2142*4b22b933Srs200217 			break;
2143*4b22b933Srs200217 	return(cg);
2144*4b22b933Srs200217 	}
2145*4b22b933Srs200217 
CacheGroupForRecord(const mDNS * const m,const mDNSu32 slot,const ResourceRecord * const rr)2146*4b22b933Srs200217 mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
2147*4b22b933Srs200217 	{
2148*4b22b933Srs200217 	return(CacheGroupForName(m, slot, rr->namehash, rr->name));
2149*4b22b933Srs200217 	}
2150*4b22b933Srs200217 
AddressIsLocalSubnet(mDNS * const m,const mDNSInterfaceID InterfaceID,const mDNSAddr * addr)2151*4b22b933Srs200217 mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
2152*4b22b933Srs200217 	{
2153*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
2154*4b22b933Srs200217 
2155*4b22b933Srs200217 	if (addr->type == mDNSAddrType_IPv4)
2156*4b22b933Srs200217 		{
2157*4b22b933Srs200217 		if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue);
2158*4b22b933Srs200217 		for (intf = m->HostInterfaces; intf; intf = intf->next)
2159*4b22b933Srs200217 			if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
2160*4b22b933Srs200217 				if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
2161*4b22b933Srs200217 					return(mDNStrue);
2162*4b22b933Srs200217 		}
2163*4b22b933Srs200217 
2164*4b22b933Srs200217 	if (addr->type == mDNSAddrType_IPv6)
2165*4b22b933Srs200217 		{
2166*4b22b933Srs200217 		if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue);
2167*4b22b933Srs200217 		for (intf = m->HostInterfaces; intf; intf = intf->next)
2168*4b22b933Srs200217 			if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
2169*4b22b933Srs200217 				if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
2170*4b22b933Srs200217 					(((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
2171*4b22b933Srs200217 					(((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
2172*4b22b933Srs200217 					(((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
2173*4b22b933Srs200217 						return(mDNStrue);
2174*4b22b933Srs200217 		}
2175*4b22b933Srs200217 
2176*4b22b933Srs200217 	return(mDNSfalse);
2177*4b22b933Srs200217 	}
2178*4b22b933Srs200217 
2179*4b22b933Srs200217 // Set up a AuthRecord with sensible default values.
2180*4b22b933Srs200217 // These defaults may be overwritten with new values before mDNS_Register is called
mDNS_SetupResourceRecord(AuthRecord * rr,RData * RDataStorage,mDNSInterfaceID InterfaceID,mDNSu16 rrtype,mDNSu32 ttl,mDNSu8 RecordType,mDNSRecordCallback Callback,void * Context)2181*4b22b933Srs200217 mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
2182*4b22b933Srs200217 	mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
2183*4b22b933Srs200217 	{
2184*4b22b933Srs200217 	mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
2185*4b22b933Srs200217 	// Don't try to store a TTL bigger than we can represent in platform time units
2186*4b22b933Srs200217 	if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
2187*4b22b933Srs200217 		ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
2188*4b22b933Srs200217 	else if (ttl == 0)		// And Zero TTL is illegal
2189*4b22b933Srs200217 		ttl = DefaultTTLforRRType(rrtype);
2190*4b22b933Srs200217 
2191*4b22b933Srs200217 	// Field Group 1: The actual information pertaining to this resource record
2192*4b22b933Srs200217 	rr->resrec.RecordType        = RecordType;
2193*4b22b933Srs200217 	rr->resrec.InterfaceID       = InterfaceID;
2194*4b22b933Srs200217 	rr->resrec.name              = &rr->namestorage;
2195*4b22b933Srs200217 	rr->resrec.rrtype            = rrtype;
2196*4b22b933Srs200217 	rr->resrec.rrclass           = kDNSClass_IN;
2197*4b22b933Srs200217 	rr->resrec.rroriginalttl     = ttl;
2198*4b22b933Srs200217 //	rr->resrec.rdlength          = MUST set by client and/or in mDNS_Register_internal
2199*4b22b933Srs200217 //	rr->resrec.rdestimate        = set in mDNS_Register_internal
2200*4b22b933Srs200217 //	rr->resrec.rdata             = MUST be set by client
2201*4b22b933Srs200217 
2202*4b22b933Srs200217 	if (RDataStorage)
2203*4b22b933Srs200217 		rr->resrec.rdata = RDataStorage;
2204*4b22b933Srs200217 	else
2205*4b22b933Srs200217 		{
2206*4b22b933Srs200217 		rr->resrec.rdata = &rr->rdatastorage;
2207*4b22b933Srs200217 		rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
2208*4b22b933Srs200217 		}
2209*4b22b933Srs200217 
2210*4b22b933Srs200217 	// Field Group 2: Persistent metadata for Authoritative Records
2211*4b22b933Srs200217 	rr->Additional1       = mDNSNULL;
2212*4b22b933Srs200217 	rr->Additional2       = mDNSNULL;
2213*4b22b933Srs200217 	rr->DependentOn       = mDNSNULL;
2214*4b22b933Srs200217 	rr->RRSet             = mDNSNULL;
2215*4b22b933Srs200217 	rr->RecordCallback    = Callback;
2216*4b22b933Srs200217 	rr->RecordContext     = Context;
2217*4b22b933Srs200217 
2218*4b22b933Srs200217 	rr->HostTarget        = mDNSfalse;
2219*4b22b933Srs200217 	rr->AllowRemoteQuery  = mDNSfalse;
2220*4b22b933Srs200217 	rr->ForceMCast        = mDNSfalse;
2221*4b22b933Srs200217 
2222*4b22b933Srs200217 	// Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
2223*4b22b933Srs200217 
2224*4b22b933Srs200217 	rr->namestorage.c[0]  = 0;		// MUST be set by client before calling mDNS_Register()
2225*4b22b933Srs200217 	}
2226*4b22b933Srs200217 
2227*4b22b933Srs200217 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
2228*4b22b933Srs200217 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
AnswerLocalOnlyQuestionWithResourceRecord(mDNS * const m,DNSQuestion * q,AuthRecord * rr,mDNSBool AddRecord)2229*4b22b933Srs200217 mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord)
2230*4b22b933Srs200217 	{
2231*4b22b933Srs200217 	// Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
2232*4b22b933Srs200217 	if (AddRecord) rr->LocalAnswer = mDNStrue;
2233*4b22b933Srs200217 	m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
2234*4b22b933Srs200217 	if (q->QuestionCallback)
2235*4b22b933Srs200217 		q->QuestionCallback(m, q, &rr->resrec, AddRecord);
2236*4b22b933Srs200217 	m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2237*4b22b933Srs200217 	}
2238*4b22b933Srs200217 
2239*4b22b933Srs200217 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers
2240*4b22b933Srs200217 // to each, stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
2241*4b22b933Srs200217 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
2242*4b22b933Srs200217 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
AnswerLocalQuestions(mDNS * const m,AuthRecord * rr,mDNSBool AddRecord)2243*4b22b933Srs200217 mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord)
2244*4b22b933Srs200217 	{
2245*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2246*4b22b933Srs200217 
2247*4b22b933Srs200217 	m->CurrentQuestion = m->LocalOnlyQuestions;
2248*4b22b933Srs200217 	while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
2249*4b22b933Srs200217 		{
2250*4b22b933Srs200217 		DNSQuestion *q = m->CurrentQuestion;
2251*4b22b933Srs200217 		m->CurrentQuestion = q->next;
2252*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
2253*4b22b933Srs200217 			AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord);			// MUST NOT dereference q again
2254*4b22b933Srs200217 		}
2255*4b22b933Srs200217 
2256*4b22b933Srs200217 	// If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
2257*4b22b933Srs200217 	if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
2258*4b22b933Srs200217 		{
2259*4b22b933Srs200217 		m->CurrentQuestion = m->Questions;
2260*4b22b933Srs200217 		while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
2261*4b22b933Srs200217 			{
2262*4b22b933Srs200217 			DNSQuestion *q = m->CurrentQuestion;
2263*4b22b933Srs200217 			m->CurrentQuestion = q->next;
2264*4b22b933Srs200217 			if (ResourceRecordAnswersQuestion(&rr->resrec, q))
2265*4b22b933Srs200217 				AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord);		// MUST NOT dereference q again
2266*4b22b933Srs200217 			}
2267*4b22b933Srs200217 		}
2268*4b22b933Srs200217 
2269*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
2270*4b22b933Srs200217 	}
2271*4b22b933Srs200217 
2272*4b22b933Srs200217 // ***************************************************************************
2273*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
2274*4b22b933Srs200217 #pragma mark -
2275*4b22b933Srs200217 #pragma mark - Resource Record Utility Functions
2276*4b22b933Srs200217 #endif
2277*4b22b933Srs200217 
2278*4b22b933Srs200217 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2279*4b22b933Srs200217 
2280*4b22b933Srs200217 #define ResourceRecordIsValidAnswer(RR) ( ((RR)->             resrec.RecordType & kDNSRecordTypeActiveMask)  && \
2281*4b22b933Srs200217 		((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2282*4b22b933Srs200217 		((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2283*4b22b933Srs200217 		((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask))  )
2284*4b22b933Srs200217 
2285*4b22b933Srs200217 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2286*4b22b933Srs200217 	(ResourceRecordIsValidAnswer(RR) && \
2287*4b22b933Srs200217 	((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2288*4b22b933Srs200217 
2289*4b22b933Srs200217 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2290*4b22b933Srs200217 #define DefaultProbeCountForRecordType(X)      ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2291*4b22b933Srs200217 
2292*4b22b933Srs200217 #define InitialAnnounceCount ((mDNSu8)10)
2293*4b22b933Srs200217 
2294*4b22b933Srs200217 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
2295*4b22b933Srs200217 // This means that because the announce interval is doubled after sending the first packet, the first
2296*4b22b933Srs200217 // observed on-the-wire inter-packet interval between announcements is actually one second.
2297*4b22b933Srs200217 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
2298*4b22b933Srs200217 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
2299*4b22b933Srs200217 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
2300*4b22b933Srs200217 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
2301*4b22b933Srs200217 
2302*4b22b933Srs200217 #define DefaultAPIntervalForRecordType(X)  ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared     ) ? DefaultAnnounceIntervalForTypeShared : \
2303*4b22b933Srs200217 											(X) & (kDNSRecordTypeUnique                              ) ? DefaultProbeIntervalForTypeUnique    : \
2304*4b22b933Srs200217 											(X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2305*4b22b933Srs200217 
2306*4b22b933Srs200217 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
2307*4b22b933Srs200217 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
2308*4b22b933Srs200217 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
2309*4b22b933Srs200217 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
2310*4b22b933Srs200217 
2311*4b22b933Srs200217 #define MaxUnansweredQueries 4
2312*4b22b933Srs200217 
2313*4b22b933Srs200217 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
2314*4b22b933Srs200217 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
2315*4b22b933Srs200217 // TTL and rdata may differ.
2316*4b22b933Srs200217 // This is used for cache flush management:
2317*4b22b933Srs200217 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
2318*4b22b933Srs200217 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
SameResourceRecordSignature(const ResourceRecord * const r1,const ResourceRecord * const r2)2319*4b22b933Srs200217 mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
2320*4b22b933Srs200217 	{
2321*4b22b933Srs200217 	if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
2322*4b22b933Srs200217 	if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
2323*4b22b933Srs200217 	if (r1->InterfaceID &&
2324*4b22b933Srs200217 		r2->InterfaceID &&
2325*4b22b933Srs200217 		r1->InterfaceID != r2->InterfaceID) return(mDNSfalse);
2326*4b22b933Srs200217 	return(mDNSBool)(
2327*4b22b933Srs200217 		r1->rrtype == r2->rrtype &&
2328*4b22b933Srs200217 		r1->rrclass == r2->rrclass &&
2329*4b22b933Srs200217 		r1->namehash == r2->namehash &&
2330*4b22b933Srs200217 		SameDomainName(r1->name, r2->name));
2331*4b22b933Srs200217 	}
2332*4b22b933Srs200217 
2333*4b22b933Srs200217 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
2334*4b22b933Srs200217 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
2335*4b22b933Srs200217 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
2336*4b22b933Srs200217 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
2337*4b22b933Srs200217 // so a response of any type should match, even if it is not actually the type the client plans to use.
PacketRRMatchesSignature(const CacheRecord * const pktrr,const AuthRecord * const authrr)2338*4b22b933Srs200217 mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
2339*4b22b933Srs200217 	{
2340*4b22b933Srs200217 	if (!pktrr)  { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); }
2341*4b22b933Srs200217 	if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); }
2342*4b22b933Srs200217 	if (pktrr->resrec.InterfaceID &&
2343*4b22b933Srs200217 		authrr->resrec.InterfaceID &&
2344*4b22b933Srs200217 		pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse);
2345*4b22b933Srs200217 	if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
2346*4b22b933Srs200217 	return(mDNSBool)(
2347*4b22b933Srs200217 		pktrr->resrec.rrclass == authrr->resrec.rrclass &&
2348*4b22b933Srs200217 		pktrr->resrec.namehash == authrr->resrec.namehash &&
2349*4b22b933Srs200217 		SameDomainName(pktrr->resrec.name, authrr->resrec.name));
2350*4b22b933Srs200217 	}
2351*4b22b933Srs200217 
2352*4b22b933Srs200217 // IdenticalResourceRecord returns true if two resources records have
2353*4b22b933Srs200217 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
IdenticalResourceRecord(const ResourceRecord * const r1,const ResourceRecord * const r2)2354*4b22b933Srs200217 mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
2355*4b22b933Srs200217 	{
2356*4b22b933Srs200217 	if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
2357*4b22b933Srs200217 	if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
2358*4b22b933Srs200217 	if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name))
2359*4b22b933Srs200217 		return(mDNSfalse);
2360*4b22b933Srs200217 	return(SameRData(r1, r2));
2361*4b22b933Srs200217 	}
2362*4b22b933Srs200217 
2363*4b22b933Srs200217 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
2364*4b22b933Srs200217 // This is the information that the requester believes to be correct.
2365*4b22b933Srs200217 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
2366*4b22b933Srs200217 // This is the information that we believe to be correct.
2367*4b22b933Srs200217 // We've already determined that we plan to give this answer on this interface
2368*4b22b933Srs200217 // (either the record is non-specific, or it is specific to this interface)
2369*4b22b933Srs200217 // so now we just need to check the name, type, class, rdata and TTL.
ShouldSuppressKnownAnswer(const CacheRecord * const ka,const AuthRecord * const rr)2370*4b22b933Srs200217 mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
2371*4b22b933Srs200217 	{
2372*4b22b933Srs200217 	// If RR signature is different, or data is different, then don't suppress our answer
2373*4b22b933Srs200217 	if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
2374*4b22b933Srs200217 
2375*4b22b933Srs200217 	// If the requester's indicated TTL is less than half the real TTL,
2376*4b22b933Srs200217 	// we need to give our answer before the requester's copy expires.
2377*4b22b933Srs200217 	// If the requester's indicated TTL is at least half the real TTL,
2378*4b22b933Srs200217 	// then we can suppress our answer this time.
2379*4b22b933Srs200217 	// If the requester's indicated TTL is greater than the TTL we believe,
2380*4b22b933Srs200217 	// then that's okay, and we don't need to do anything about it.
2381*4b22b933Srs200217 	// (If two responders on the network are offering the same information,
2382*4b22b933Srs200217 	// that's okay, and if they are offering the information with different TTLs,
2383*4b22b933Srs200217 	// the one offering the lower TTL should defer to the one offering the higher TTL.)
2384*4b22b933Srs200217 	return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2);
2385*4b22b933Srs200217 	}
2386*4b22b933Srs200217 
SetNextAnnounceProbeTime(mDNS * const m,const AuthRecord * const rr)2387*4b22b933Srs200217 mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
2388*4b22b933Srs200217 	{
2389*4b22b933Srs200217 	if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2390*4b22b933Srs200217 		{
2391*4b22b933Srs200217 		//LogMsg("ProbeCount %d Next %ld %s",
2392*4b22b933Srs200217 		//	rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
2393*4b22b933Srs200217 		if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2394*4b22b933Srs200217 			m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
2395*4b22b933Srs200217 		}
2396*4b22b933Srs200217 	else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr))
2397*4b22b933Srs200217 		{
2398*4b22b933Srs200217 		if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2399*4b22b933Srs200217 			m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
2400*4b22b933Srs200217 		}
2401*4b22b933Srs200217 	}
2402*4b22b933Srs200217 
InitializeLastAPTime(mDNS * const m,AuthRecord * const rr)2403*4b22b933Srs200217 mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
2404*4b22b933Srs200217 	{
2405*4b22b933Srs200217 	// To allow us to aggregate probes when a group of services are registered together,
2406*4b22b933Srs200217 	// the first probe is delayed 1/4 second. This means the common-case behaviour is:
2407*4b22b933Srs200217 	// 1/4 second wait; probe
2408*4b22b933Srs200217 	// 1/4 second wait; probe
2409*4b22b933Srs200217 	// 1/4 second wait; probe
2410*4b22b933Srs200217 	// 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
2411*4b22b933Srs200217 
2412*4b22b933Srs200217 	// If we have no probe suppression time set, or it is in the past, set it now
2413*4b22b933Srs200217 	if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0)
2414*4b22b933Srs200217 		{
2415*4b22b933Srs200217 		m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique);
2416*4b22b933Srs200217 		// If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
2417*4b22b933Srs200217 		if (m->SuppressProbes - m->NextScheduledProbe >= 0)
2418*4b22b933Srs200217 			m->SuppressProbes = m->NextScheduledProbe;
2419*4b22b933Srs200217 		// If we already have a query scheduled to go out sooner, then use that time to get better aggregation
2420*4b22b933Srs200217 		if (m->SuppressProbes - m->NextScheduledQuery >= 0)
2421*4b22b933Srs200217 			m->SuppressProbes = m->NextScheduledQuery;
2422*4b22b933Srs200217 		}
2423*4b22b933Srs200217 
2424*4b22b933Srs200217 	// We announce to flush stale data from other caches. It is a reasonable assumption that any
2425*4b22b933Srs200217 	// old stale copies will probably have the same TTL we're using, so announcing longer than
2426*4b22b933Srs200217 	// this serves no purpose -- any stale copies of that record will have expired by then anyway.
2427*4b22b933Srs200217 	rr->AnnounceUntil   = m->timenow + TicksTTL(rr);
2428*4b22b933Srs200217 	rr->LastAPTime      = m->SuppressProbes - rr->ThisAPInterval;
2429*4b22b933Srs200217 	// Set LastMCTime to now, to inhibit multicast responses
2430*4b22b933Srs200217 	// (no need to send additional multicast responses when we're announcing anyway)
2431*4b22b933Srs200217 	rr->LastMCTime      = m->timenow;
2432*4b22b933Srs200217 	rr->LastMCInterface = mDNSInterfaceMark;
2433*4b22b933Srs200217 
2434*4b22b933Srs200217 	// If this is a record type that's not going to probe, then delay its first announcement so that
2435*4b22b933Srs200217 	// it will go out synchronized with the first announcement for the other records that *are* probing.
2436*4b22b933Srs200217 	// This is a minor performance tweak that helps keep groups of related records synchronized together.
2437*4b22b933Srs200217 	// The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
2438*4b22b933Srs200217 	// delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
2439*4b22b933Srs200217 	// When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
2440*4b22b933Srs200217 	// because they will meet the criterion of being at least half-way to their scheduled announcement time.
2441*4b22b933Srs200217 	if (rr->resrec.RecordType != kDNSRecordTypeUnique)
2442*4b22b933Srs200217 		rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
2443*4b22b933Srs200217 
2444*4b22b933Srs200217 	SetNextAnnounceProbeTime(m, rr);
2445*4b22b933Srs200217 	}
2446*4b22b933Srs200217 
2447*4b22b933Srs200217 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
2448*4b22b933Srs200217 
SetTargetToHostName(mDNS * const m,AuthRecord * const rr)2449*4b22b933Srs200217 mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
2450*4b22b933Srs200217 	{
2451*4b22b933Srs200217 	domainname *target = GetRRDomainNameTarget(&rr->resrec);
2452*4b22b933Srs200217 
2453*4b22b933Srs200217 	if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype);
2454*4b22b933Srs200217 
2455*4b22b933Srs200217 	if (target && SameDomainName(target, &m->MulticastHostname))
2456*4b22b933Srs200217 		debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
2457*4b22b933Srs200217 
2458*4b22b933Srs200217 	if (target && !SameDomainName(target, &m->MulticastHostname))
2459*4b22b933Srs200217 		{
2460*4b22b933Srs200217 		AssignDomainName(target, &m->MulticastHostname);
2461*4b22b933Srs200217 		SetNewRData(&rr->resrec, mDNSNULL, 0);
2462*4b22b933Srs200217 
2463*4b22b933Srs200217 		// If we're in the middle of probing this record, we need to start again,
2464*4b22b933Srs200217 		// because changing its rdata may change the outcome of the tie-breaker.
2465*4b22b933Srs200217 		// (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
2466*4b22b933Srs200217 		rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
2467*4b22b933Srs200217 
2468*4b22b933Srs200217 		// If we've announced this record, we really should send a goodbye packet for the old rdata before
2469*4b22b933Srs200217 		// changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
2470*4b22b933Srs200217 		// so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
2471*4b22b933Srs200217 		if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
2472*4b22b933Srs200217 			debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating",
2473*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2474*4b22b933Srs200217 
2475*4b22b933Srs200217 		rr->AnnounceCount  = InitialAnnounceCount;
2476*4b22b933Srs200217 		rr->RequireGoodbye = mDNSfalse;
2477*4b22b933Srs200217 		rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2478*4b22b933Srs200217 		InitializeLastAPTime(m,rr);
2479*4b22b933Srs200217 		}
2480*4b22b933Srs200217 	}
2481*4b22b933Srs200217 
AcknowledgeRecord(mDNS * const m,AuthRecord * const rr)2482*4b22b933Srs200217 mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
2483*4b22b933Srs200217 	{
2484*4b22b933Srs200217 	if (!rr->Acknowledged && rr->RecordCallback)
2485*4b22b933Srs200217 		{
2486*4b22b933Srs200217 		// CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2487*4b22b933Srs200217 		// is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2488*4b22b933Srs200217 		rr->Acknowledged = mDNStrue;
2489*4b22b933Srs200217 		m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
2490*4b22b933Srs200217 		rr->RecordCallback(m, rr, mStatus_NoError);
2491*4b22b933Srs200217 		m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2492*4b22b933Srs200217 		}
2493*4b22b933Srs200217 	}
2494*4b22b933Srs200217 
2495*4b22b933Srs200217 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
2496*4b22b933Srs200217 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
2497*4b22b933Srs200217 	((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
2498*4b22b933Srs200217 #define RecordIsLocalDuplicate(A,B) \
2499*4b22b933Srs200217 	((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
2500*4b22b933Srs200217 
mDNS_Register_internal(mDNS * const m,AuthRecord * const rr)2501*4b22b933Srs200217 mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
2502*4b22b933Srs200217 	{
2503*4b22b933Srs200217 	domainname *target = GetRRDomainNameTarget(&rr->resrec);
2504*4b22b933Srs200217 	AuthRecord *r;
2505*4b22b933Srs200217 	AuthRecord **p = &m->ResourceRecords;
2506*4b22b933Srs200217 	AuthRecord **d = &m->DuplicateRecords;
2507*4b22b933Srs200217 
2508*4b22b933Srs200217 	mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
2509*4b22b933Srs200217 
2510*4b22b933Srs200217 	if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
2511*4b22b933Srs200217 		{ LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
2512*4b22b933Srs200217 
2513*4b22b933Srs200217 #ifndef UNICAST_DISABLED
2514*4b22b933Srs200217     if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
2515*4b22b933Srs200217     	rr->uDNS_info.id = zeroID;
2516*4b22b933Srs200217     else return uDNS_RegisterRecord(m, rr);
2517*4b22b933Srs200217 #endif
2518*4b22b933Srs200217 
2519*4b22b933Srs200217 	while (*p && *p != rr) p=&(*p)->next;
2520*4b22b933Srs200217 	while (*d && *d != rr) d=&(*d)->next;
2521*4b22b933Srs200217 	if (*d || *p)
2522*4b22b933Srs200217 		{
2523*4b22b933Srs200217 		LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list",
2524*4b22b933Srs200217 			rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2525*4b22b933Srs200217 		return(mStatus_AlreadyRegistered);
2526*4b22b933Srs200217 		}
2527*4b22b933Srs200217 
2528*4b22b933Srs200217 	if (rr->DependentOn)
2529*4b22b933Srs200217 		{
2530*4b22b933Srs200217 		if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2531*4b22b933Srs200217 			rr->resrec.RecordType =  kDNSRecordTypeVerified;
2532*4b22b933Srs200217 		else
2533*4b22b933Srs200217 			{
2534*4b22b933Srs200217 			LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
2535*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2536*4b22b933Srs200217 			return(mStatus_Invalid);
2537*4b22b933Srs200217 			}
2538*4b22b933Srs200217 		if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
2539*4b22b933Srs200217 			{
2540*4b22b933Srs200217 			LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
2541*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
2542*4b22b933Srs200217 			return(mStatus_Invalid);
2543*4b22b933Srs200217 			}
2544*4b22b933Srs200217 		}
2545*4b22b933Srs200217 
2546*4b22b933Srs200217 	// If this resource record is referencing a specific interface, make sure it exists
2547*4b22b933Srs200217 	if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
2548*4b22b933Srs200217 		{
2549*4b22b933Srs200217 		NetworkInterfaceInfo *intf;
2550*4b22b933Srs200217 		for (intf = m->HostInterfaces; intf; intf = intf->next)
2551*4b22b933Srs200217 			if (intf->InterfaceID == rr->resrec.InterfaceID) break;
2552*4b22b933Srs200217 		if (!intf)
2553*4b22b933Srs200217 			{
2554*4b22b933Srs200217 			debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
2555*4b22b933Srs200217 			return(mStatus_BadReferenceErr);
2556*4b22b933Srs200217 			}
2557*4b22b933Srs200217 		}
2558*4b22b933Srs200217 
2559*4b22b933Srs200217 	rr->next = mDNSNULL;
2560*4b22b933Srs200217 
2561*4b22b933Srs200217 	// Field Group 1: Persistent metadata for Authoritative Records
2562*4b22b933Srs200217 //	rr->Additional1       = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2563*4b22b933Srs200217 //	rr->Additional2       = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2564*4b22b933Srs200217 //	rr->DependentOn       = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2565*4b22b933Srs200217 //	rr->RRSet             = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2566*4b22b933Srs200217 //	rr->Callback          = already set in mDNS_SetupResourceRecord
2567*4b22b933Srs200217 //	rr->Context           = already set in mDNS_SetupResourceRecord
2568*4b22b933Srs200217 //	rr->RecordType        = already set in mDNS_SetupResourceRecord
2569*4b22b933Srs200217 //	rr->HostTarget        = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2570*4b22b933Srs200217 //	rr->AllowRemoteQuery  = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2571*4b22b933Srs200217 	// Make sure target is not uninitialized data, or we may crash writing debugging log messages
2572*4b22b933Srs200217 	if (rr->HostTarget && target) target->c[0] = 0;
2573*4b22b933Srs200217 
2574*4b22b933Srs200217 	// Field Group 2: Transient state for Authoritative Records
2575*4b22b933Srs200217 	rr->Acknowledged      = mDNSfalse;
2576*4b22b933Srs200217 	rr->ProbeCount        = DefaultProbeCountForRecordType(rr->resrec.RecordType);
2577*4b22b933Srs200217 	rr->AnnounceCount     = InitialAnnounceCount;
2578*4b22b933Srs200217 	rr->RequireGoodbye    = mDNSfalse;
2579*4b22b933Srs200217 	rr->LocalAnswer       = mDNSfalse;
2580*4b22b933Srs200217 	rr->IncludeInProbe    = mDNSfalse;
2581*4b22b933Srs200217 	rr->ImmedAnswer       = mDNSNULL;
2582*4b22b933Srs200217 	rr->ImmedUnicast      = mDNSfalse;
2583*4b22b933Srs200217 	rr->ImmedAdditional   = mDNSNULL;
2584*4b22b933Srs200217 	rr->SendRNow          = mDNSNULL;
2585*4b22b933Srs200217 	rr->v4Requester       = zerov4Addr;
2586*4b22b933Srs200217 	rr->v6Requester       = zerov6Addr;
2587*4b22b933Srs200217 	rr->NextResponse      = mDNSNULL;
2588*4b22b933Srs200217 	rr->NR_AnswerTo       = mDNSNULL;
2589*4b22b933Srs200217 	rr->NR_AdditionalTo   = mDNSNULL;
2590*4b22b933Srs200217 	rr->ThisAPInterval    = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2591*4b22b933Srs200217 	if (!rr->HostTarget) InitializeLastAPTime(m, rr);
2592*4b22b933Srs200217 //	rr->AnnounceUntil     = Set for us in InitializeLastAPTime()
2593*4b22b933Srs200217 //	rr->LastAPTime        = Set for us in InitializeLastAPTime()
2594*4b22b933Srs200217 //	rr->LastMCTime        = Set for us in InitializeLastAPTime()
2595*4b22b933Srs200217 //	rr->LastMCInterface   = Set for us in InitializeLastAPTime()
2596*4b22b933Srs200217 	rr->NewRData          = mDNSNULL;
2597*4b22b933Srs200217 	rr->newrdlength       = 0;
2598*4b22b933Srs200217 	rr->UpdateCallback    = mDNSNULL;
2599*4b22b933Srs200217 	rr->UpdateCredits     = kMaxUpdateCredits;
2600*4b22b933Srs200217 	rr->NextUpdateCredit  = 0;
2601*4b22b933Srs200217 	rr->UpdateBlocked     = 0;
2602*4b22b933Srs200217 
2603*4b22b933Srs200217 //	rr->resrec.interface         = already set in mDNS_SetupResourceRecord
2604*4b22b933Srs200217 //	rr->resrec.name->c            = MUST be set by client
2605*4b22b933Srs200217 //	rr->resrec.rrtype            = already set in mDNS_SetupResourceRecord
2606*4b22b933Srs200217 //	rr->resrec.rrclass           = already set in mDNS_SetupResourceRecord
2607*4b22b933Srs200217 //	rr->resrec.rroriginalttl     = already set in mDNS_SetupResourceRecord
2608*4b22b933Srs200217 //	rr->resrec.rdata             = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
2609*4b22b933Srs200217 
2610*4b22b933Srs200217 	if (rr->HostTarget)
2611*4b22b933Srs200217 		SetTargetToHostName(m, rr);	// Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2612*4b22b933Srs200217 	else
2613*4b22b933Srs200217 		{
2614*4b22b933Srs200217 		rr->resrec.rdlength   = GetRDLength(&rr->resrec, mDNSfalse);
2615*4b22b933Srs200217 		rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
2616*4b22b933Srs200217 		}
2617*4b22b933Srs200217 
2618*4b22b933Srs200217 	if (!ValidateDomainName(rr->resrec.name))
2619*4b22b933Srs200217 		{ LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2620*4b22b933Srs200217 
2621*4b22b933Srs200217 	// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2622*4b22b933Srs200217 	// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2623*4b22b933Srs200217 	// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2624*4b22b933Srs200217 	if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
2625*4b22b933Srs200217 
2626*4b22b933Srs200217 	// Don't do this until *after* we've set rr->resrec.rdlength
2627*4b22b933Srs200217 	if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata))
2628*4b22b933Srs200217 		{ LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2629*4b22b933Srs200217 
2630*4b22b933Srs200217 	rr->resrec.namehash   = DomainNameHashValue(rr->resrec.name);
2631*4b22b933Srs200217 	rr->resrec.rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
2632*4b22b933Srs200217 
2633*4b22b933Srs200217 	if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
2634*4b22b933Srs200217 		{
2635*4b22b933Srs200217 		// If this is supposed to be unique, make sure we don't have any name conflicts
2636*4b22b933Srs200217 		if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2637*4b22b933Srs200217 			{
2638*4b22b933Srs200217 			const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
2639*4b22b933Srs200217 			for (r = m->ResourceRecords; r; r=r->next)
2640*4b22b933Srs200217 				{
2641*4b22b933Srs200217 				const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
2642*4b22b933Srs200217 				if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec))
2643*4b22b933Srs200217 					break;
2644*4b22b933Srs200217 				}
2645*4b22b933Srs200217 			if (r)	// If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2646*4b22b933Srs200217 				{
2647*4b22b933Srs200217 				debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2648*4b22b933Srs200217 				rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
2649*4b22b933Srs200217 				rr->resrec.rroriginalttl = 0;
2650*4b22b933Srs200217 				rr->ImmedAnswer          = mDNSInterfaceMark;
2651*4b22b933Srs200217 				m->NextScheduledResponse = m->timenow;
2652*4b22b933Srs200217 				}
2653*4b22b933Srs200217 			}
2654*4b22b933Srs200217 		}
2655*4b22b933Srs200217 
2656*4b22b933Srs200217 	// Now that we've finished building our new record, make sure it's not identical to one we already have
2657*4b22b933Srs200217 	for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break;
2658*4b22b933Srs200217 
2659*4b22b933Srs200217 	if (r)
2660*4b22b933Srs200217 		{
2661*4b22b933Srs200217 		debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr));
2662*4b22b933Srs200217 		*d = rr;
2663*4b22b933Srs200217 		// If the previous copy of this record is already verified unique,
2664*4b22b933Srs200217 		// then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
2665*4b22b933Srs200217 		// Setting ProbeCount to zero will cause SendQueries() to advance this record to
2666*4b22b933Srs200217 		// kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
2667*4b22b933Srs200217 		if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified)
2668*4b22b933Srs200217 			rr->ProbeCount = 0;
2669*4b22b933Srs200217 		}
2670*4b22b933Srs200217 	else
2671*4b22b933Srs200217 		{
2672*4b22b933Srs200217 		debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
2673*4b22b933Srs200217 		if (!m->NewLocalRecords) m->NewLocalRecords = rr;
2674*4b22b933Srs200217 		*p = rr;
2675*4b22b933Srs200217 		}
2676*4b22b933Srs200217 
2677*4b22b933Srs200217 	// For records that are not going to probe, acknowledge them right away
2678*4b22b933Srs200217 	if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
2679*4b22b933Srs200217 		AcknowledgeRecord(m, rr);
2680*4b22b933Srs200217 
2681*4b22b933Srs200217 	return(mStatus_NoError);
2682*4b22b933Srs200217 	}
2683*4b22b933Srs200217 
RecordProbeFailure(mDNS * const m,const AuthRecord * const rr)2684*4b22b933Srs200217 mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
2685*4b22b933Srs200217 	{
2686*4b22b933Srs200217 	m->ProbeFailTime = m->timenow;
2687*4b22b933Srs200217 	m->NumFailedProbes++;
2688*4b22b933Srs200217 	// If we've had fifteen or more probe failures, rate-limit to one every five seconds.
2689*4b22b933Srs200217 	// If a bunch of hosts have all been configured with the same name, then they'll all
2690*4b22b933Srs200217 	// conflict and run through the same series of names: name-2, name-3, name-4, etc.,
2691*4b22b933Srs200217 	// up to name-10. After that they'll start adding random increments in the range 1-100,
2692*4b22b933Srs200217 	// so they're more likely to branch out in the available namespace and settle on a set of
2693*4b22b933Srs200217 	// unique names quickly. If after five more tries the host is still conflicting, then we
2694*4b22b933Srs200217 	// may have a serious problem, so we start rate-limiting so we don't melt down the network.
2695*4b22b933Srs200217 	if (m->NumFailedProbes >= 15)
2696*4b22b933Srs200217 		{
2697*4b22b933Srs200217 		m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
2698*4b22b933Srs200217 		LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
2699*4b22b933Srs200217 			m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2700*4b22b933Srs200217 		}
2701*4b22b933Srs200217 	}
2702*4b22b933Srs200217 
CompleteRDataUpdate(mDNS * const m,AuthRecord * const rr)2703*4b22b933Srs200217 mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr)
2704*4b22b933Srs200217 	{
2705*4b22b933Srs200217 	RData *OldRData = rr->resrec.rdata;
2706*4b22b933Srs200217 	SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);	// Update our rdata
2707*4b22b933Srs200217 	rr->NewRData = mDNSNULL;									// Clear the NewRData pointer ...
2708*4b22b933Srs200217 	if (rr->UpdateCallback)
2709*4b22b933Srs200217 		rr->UpdateCallback(m, rr, OldRData);					// ... and let the client know
2710*4b22b933Srs200217 	}
2711*4b22b933Srs200217 
2712*4b22b933Srs200217 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
2713*4b22b933Srs200217 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
2714*4b22b933Srs200217 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
2715*4b22b933Srs200217 typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type;
2716*4b22b933Srs200217 
2717*4b22b933Srs200217 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
2718*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNS_Deregister_internal(mDNS * const m,AuthRecord * const rr,mDNS_Dereg_type drt)2719*4b22b933Srs200217 mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
2720*4b22b933Srs200217 	{
2721*4b22b933Srs200217 	AuthRecord *r2;
2722*4b22b933Srs200217 	mDNSu8 RecordType = rr->resrec.RecordType;
2723*4b22b933Srs200217 	AuthRecord **p = &m->ResourceRecords;	// Find this record in our list of active records
2724*4b22b933Srs200217 
2725*4b22b933Srs200217 #ifndef UNICAST_DISABLED
2726*4b22b933Srs200217     if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)))
2727*4b22b933Srs200217 		return uDNS_DeregisterRecord(m, rr);
2728*4b22b933Srs200217 #endif
2729*4b22b933Srs200217 
2730*4b22b933Srs200217 	while (*p && *p != rr) p=&(*p)->next;
2731*4b22b933Srs200217 
2732*4b22b933Srs200217 	if (*p)
2733*4b22b933Srs200217 		{
2734*4b22b933Srs200217 		// We found our record on the main list. See if there are any duplicates that need special handling.
2735*4b22b933Srs200217 		if (drt == mDNS_Dereg_conflict)		// If this was a conflict, see that all duplicates get the same treatment
2736*4b22b933Srs200217 			{
2737*4b22b933Srs200217 			// Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
2738*4b22b933Srs200217 			// deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
2739*4b22b933Srs200217 			for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF;
2740*4b22b933Srs200217 			}
2741*4b22b933Srs200217 		else
2742*4b22b933Srs200217 			{
2743*4b22b933Srs200217 			// Before we delete the record (and potentially send a goodbye packet)
2744*4b22b933Srs200217 			// first see if we have a record on the duplicate list ready to take over from it.
2745*4b22b933Srs200217 			AuthRecord **d = &m->DuplicateRecords;
2746*4b22b933Srs200217 			while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next;
2747*4b22b933Srs200217 			if (*d)
2748*4b22b933Srs200217 				{
2749*4b22b933Srs200217 				AuthRecord *dup = *d;
2750*4b22b933Srs200217 				debugf("Duplicate record %p taking over from %p %##s (%s)",
2751*4b22b933Srs200217 					dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2752*4b22b933Srs200217 				*d        = dup->next;		// Cut replacement record from DuplicateRecords list
2753*4b22b933Srs200217 				dup->next = rr->next;		// And then...
2754*4b22b933Srs200217 				rr->next  = dup;			// ... splice it in right after the record we're about to delete
2755*4b22b933Srs200217 				dup->resrec.RecordType        = rr->resrec.RecordType;
2756*4b22b933Srs200217 				dup->ProbeCount      = rr->ProbeCount;
2757*4b22b933Srs200217 				dup->AnnounceCount   = rr->AnnounceCount;
2758*4b22b933Srs200217 				dup->RequireGoodbye  = rr->RequireGoodbye;
2759*4b22b933Srs200217 				dup->ImmedAnswer     = rr->ImmedAnswer;
2760*4b22b933Srs200217 				dup->ImmedUnicast    = rr->ImmedUnicast;
2761*4b22b933Srs200217 				dup->ImmedAdditional = rr->ImmedAdditional;
2762*4b22b933Srs200217 				dup->v4Requester     = rr->v4Requester;
2763*4b22b933Srs200217 				dup->v6Requester     = rr->v6Requester;
2764*4b22b933Srs200217 				dup->ThisAPInterval  = rr->ThisAPInterval;
2765*4b22b933Srs200217 				dup->AnnounceUntil   = rr->AnnounceUntil;
2766*4b22b933Srs200217 				dup->LastAPTime      = rr->LastAPTime;
2767*4b22b933Srs200217 				dup->LastMCTime      = rr->LastMCTime;
2768*4b22b933Srs200217 				dup->LastMCInterface = rr->LastMCInterface;
2769*4b22b933Srs200217 				rr->RequireGoodbye = mDNSfalse;
2770*4b22b933Srs200217 				}
2771*4b22b933Srs200217 			}
2772*4b22b933Srs200217 		}
2773*4b22b933Srs200217 	else
2774*4b22b933Srs200217 		{
2775*4b22b933Srs200217 		// We didn't find our record on the main list; try the DuplicateRecords list instead.
2776*4b22b933Srs200217 		p = &m->DuplicateRecords;
2777*4b22b933Srs200217 		while (*p && *p != rr) p=&(*p)->next;
2778*4b22b933Srs200217 		// If we found our record on the duplicate list, then make sure we don't send a goodbye for it
2779*4b22b933Srs200217 		if (*p) rr->RequireGoodbye = mDNSfalse;
2780*4b22b933Srs200217 		if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
2781*4b22b933Srs200217 			rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2782*4b22b933Srs200217 		}
2783*4b22b933Srs200217 
2784*4b22b933Srs200217 	if (!*p)
2785*4b22b933Srs200217 		{
2786*4b22b933Srs200217 		// No need to log an error message if we already know this is a potentially repeated deregistration
2787*4b22b933Srs200217 		if (drt != mDNS_Dereg_repeat)
2788*4b22b933Srs200217 			LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list",
2789*4b22b933Srs200217 				rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2790*4b22b933Srs200217 		return(mStatus_BadReferenceErr);
2791*4b22b933Srs200217 		}
2792*4b22b933Srs200217 
2793*4b22b933Srs200217 	// If this is a shared record and we've announced it at least once,
2794*4b22b933Srs200217 	// we need to retract that announcement before we delete the record
2795*4b22b933Srs200217 	if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
2796*4b22b933Srs200217 		{
2797*4b22b933Srs200217 		verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)",
2798*4b22b933Srs200217 			rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2799*4b22b933Srs200217 		rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
2800*4b22b933Srs200217 		rr->resrec.rroriginalttl = 0;
2801*4b22b933Srs200217 		rr->ImmedAnswer          = mDNSInterfaceMark;
2802*4b22b933Srs200217 		if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
2803*4b22b933Srs200217 			m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
2804*4b22b933Srs200217 		}
2805*4b22b933Srs200217 	else
2806*4b22b933Srs200217 		{
2807*4b22b933Srs200217 		*p = rr->next;					// Cut this record from the list
2808*4b22b933Srs200217 		// If someone is about to look at this, bump the pointer forward
2809*4b22b933Srs200217 		if (m->CurrentRecord   == rr) m->CurrentRecord   = rr->next;
2810*4b22b933Srs200217 		if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
2811*4b22b933Srs200217 		rr->next = mDNSNULL;
2812*4b22b933Srs200217 
2813*4b22b933Srs200217 		if      (RecordType == kDNSRecordTypeUnregistered)
2814*4b22b933Srs200217 			debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
2815*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2816*4b22b933Srs200217 		else if (RecordType == kDNSRecordTypeDeregistering)
2817*4b22b933Srs200217 			debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
2818*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2819*4b22b933Srs200217 		else
2820*4b22b933Srs200217 			{
2821*4b22b933Srs200217 			verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)",
2822*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2823*4b22b933Srs200217 			rr->resrec.RecordType = kDNSRecordTypeUnregistered;
2824*4b22b933Srs200217 			}
2825*4b22b933Srs200217 
2826*4b22b933Srs200217 		if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared)
2827*4b22b933Srs200217 			debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
2828*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2829*4b22b933Srs200217 
2830*4b22b933Srs200217 		// If we have an update queued up which never executed, give the client a chance to free that memory
2831*4b22b933Srs200217 		if (rr->NewRData) CompleteRDataUpdate(m, rr);	// Update our rdata, clear the NewRData pointer, and return memory to the client
2832*4b22b933Srs200217 
2833*4b22b933Srs200217 		if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse);
2834*4b22b933Srs200217 
2835*4b22b933Srs200217 		// CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2836*4b22b933Srs200217 		// is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2837*4b22b933Srs200217 		// In this case the likely client action to the mStatus_MemFree message is to free the memory,
2838*4b22b933Srs200217 		// so any attempt to touch rr after this is likely to lead to a crash.
2839*4b22b933Srs200217 		m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
2840*4b22b933Srs200217 		if (drt != mDNS_Dereg_conflict)
2841*4b22b933Srs200217 			{
2842*4b22b933Srs200217 			if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree);			// MUST NOT touch rr after this
2843*4b22b933Srs200217 			}
2844*4b22b933Srs200217 		else
2845*4b22b933Srs200217 			{
2846*4b22b933Srs200217 			RecordProbeFailure(m, rr);
2847*4b22b933Srs200217 			if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict);	// MUST NOT touch rr after this
2848*4b22b933Srs200217 			// Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
2849*4b22b933Srs200217 			// Note that with all the client callbacks going on, by the time we get here all the
2850*4b22b933Srs200217 			// records we marked may have been explicitly deregistered by the client anyway.
2851*4b22b933Srs200217 			r2 = m->DuplicateRecords;
2852*4b22b933Srs200217 			while (r2)
2853*4b22b933Srs200217 				{
2854*4b22b933Srs200217 				if (r2->ProbeCount != 0xFF) r2 = r2->next;
2855*4b22b933Srs200217 				else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; }
2856*4b22b933Srs200217 				}
2857*4b22b933Srs200217 			}
2858*4b22b933Srs200217 		m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2859*4b22b933Srs200217 		}
2860*4b22b933Srs200217 	return(mStatus_NoError);
2861*4b22b933Srs200217 	}
2862*4b22b933Srs200217 
2863*4b22b933Srs200217 // ***************************************************************************
2864*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
2865*4b22b933Srs200217 #pragma mark -
2866*4b22b933Srs200217 #pragma mark -
2867*4b22b933Srs200217 #pragma mark - Packet Sending Functions
2868*4b22b933Srs200217 #endif
2869*4b22b933Srs200217 
AddRecordToResponseList(AuthRecord *** nrpp,AuthRecord * rr,AuthRecord * add)2870*4b22b933Srs200217 mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add)
2871*4b22b933Srs200217 	{
2872*4b22b933Srs200217 	if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse)
2873*4b22b933Srs200217 		{
2874*4b22b933Srs200217 		**nrpp = rr;
2875*4b22b933Srs200217 		// NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
2876*4b22b933Srs200217 		// If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
2877*4b22b933Srs200217 		// The referenced record will definitely be acceptable (by recursive application of this rule)
2878*4b22b933Srs200217 		if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo;
2879*4b22b933Srs200217 		rr->NR_AdditionalTo = add;
2880*4b22b933Srs200217 		*nrpp = &rr->NextResponse;
2881*4b22b933Srs200217 		}
2882*4b22b933Srs200217 	debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2883*4b22b933Srs200217 	}
2884*4b22b933Srs200217 
AddAdditionalsToResponseList(mDNS * const m,AuthRecord * ResponseRecords,AuthRecord *** nrpp,const mDNSInterfaceID InterfaceID)2885*4b22b933Srs200217 mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID)
2886*4b22b933Srs200217 	{
2887*4b22b933Srs200217 	AuthRecord  *rr, *rr2;
2888*4b22b933Srs200217 	for (rr=ResponseRecords; rr; rr=rr->NextResponse)			// For each record we plan to put
2889*4b22b933Srs200217 		{
2890*4b22b933Srs200217 		// (Note: This is an "if", not a "while". If we add a record, we'll find it again
2891*4b22b933Srs200217 		// later in the "for" loop, and we will follow further "additional" links then.)
2892*4b22b933Srs200217 		if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID))
2893*4b22b933Srs200217 			AddRecordToResponseList(nrpp, rr->Additional1, rr);
2894*4b22b933Srs200217 
2895*4b22b933Srs200217 		if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID))
2896*4b22b933Srs200217 			AddRecordToResponseList(nrpp, rr->Additional2, rr);
2897*4b22b933Srs200217 
2898*4b22b933Srs200217 		// For SRV records, automatically add the Address record(s) for the target host
2899*4b22b933Srs200217 		if (rr->resrec.rrtype == kDNSType_SRV)
2900*4b22b933Srs200217 			for (rr2=m->ResourceRecords; rr2; rr2=rr2->next)					// Scan list of resource records
2901*4b22b933Srs200217 				if (RRTypeIsAddressType(rr2->resrec.rrtype) &&					// For all address records (A/AAAA) ...
2902*4b22b933Srs200217 					ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) &&	// ... which are valid for answer ...
2903*4b22b933Srs200217 					rr->resrec.rdatahash == rr2->resrec.namehash &&			// ... whose name is the name of the SRV target
2904*4b22b933Srs200217 					SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name))
2905*4b22b933Srs200217 					AddRecordToResponseList(nrpp, rr2, rr);
2906*4b22b933Srs200217 		}
2907*4b22b933Srs200217 	}
2908*4b22b933Srs200217 
SendDelayedUnicastResponse(mDNS * const m,const mDNSAddr * const dest,const mDNSInterfaceID InterfaceID)2909*4b22b933Srs200217 mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
2910*4b22b933Srs200217 	{
2911*4b22b933Srs200217 	AuthRecord *rr;
2912*4b22b933Srs200217 	AuthRecord  *ResponseRecords = mDNSNULL;
2913*4b22b933Srs200217 	AuthRecord **nrp             = &ResponseRecords;
2914*4b22b933Srs200217 
2915*4b22b933Srs200217 	// Make a list of all our records that need to be unicast to this destination
2916*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
2917*4b22b933Srs200217 		{
2918*4b22b933Srs200217 		// If we find we can no longer unicast this answer, clear ImmedUnicast
2919*4b22b933Srs200217 		if (rr->ImmedAnswer == mDNSInterfaceMark               ||
2920*4b22b933Srs200217 			mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) ||
2921*4b22b933Srs200217 			mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr)  )
2922*4b22b933Srs200217 			rr->ImmedUnicast = mDNSfalse;
2923*4b22b933Srs200217 
2924*4b22b933Srs200217 		if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID)
2925*4b22b933Srs200217 			if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) ||
2926*4b22b933Srs200217 				(dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6)))
2927*4b22b933Srs200217 				{
2928*4b22b933Srs200217 				rr->ImmedAnswer  = mDNSNULL;				// Clear the state fields
2929*4b22b933Srs200217 				rr->ImmedUnicast = mDNSfalse;
2930*4b22b933Srs200217 				rr->v4Requester  = zerov4Addr;
2931*4b22b933Srs200217 				rr->v6Requester  = zerov6Addr;
2932*4b22b933Srs200217 				if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse)	// rr->NR_AnswerTo
2933*4b22b933Srs200217 					{ rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; }
2934*4b22b933Srs200217 				}
2935*4b22b933Srs200217 		}
2936*4b22b933Srs200217 
2937*4b22b933Srs200217 	AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
2938*4b22b933Srs200217 
2939*4b22b933Srs200217 	while (ResponseRecords)
2940*4b22b933Srs200217 		{
2941*4b22b933Srs200217 		mDNSu8 *responseptr = m->omsg.data;
2942*4b22b933Srs200217 		mDNSu8 *newptr;
2943*4b22b933Srs200217 		InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
2944*4b22b933Srs200217 
2945*4b22b933Srs200217 		// Put answers in the packet
2946*4b22b933Srs200217 		while (ResponseRecords && ResponseRecords->NR_AnswerTo)
2947*4b22b933Srs200217 			{
2948*4b22b933Srs200217 			rr = ResponseRecords;
2949*4b22b933Srs200217 			if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2950*4b22b933Srs200217 				rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
2951*4b22b933Srs200217 			newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
2952*4b22b933Srs200217 			rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
2953*4b22b933Srs200217 			if (!newptr && m->omsg.h.numAnswers) break;	// If packet full, send it now
2954*4b22b933Srs200217 			if (newptr) responseptr = newptr;
2955*4b22b933Srs200217 			ResponseRecords = rr->NextResponse;
2956*4b22b933Srs200217 			rr->NextResponse    = mDNSNULL;
2957*4b22b933Srs200217 			rr->NR_AnswerTo     = mDNSNULL;
2958*4b22b933Srs200217 			rr->NR_AdditionalTo = mDNSNULL;
2959*4b22b933Srs200217 			rr->RequireGoodbye  = mDNStrue;
2960*4b22b933Srs200217 			}
2961*4b22b933Srs200217 
2962*4b22b933Srs200217 		// Add additionals, if there's space
2963*4b22b933Srs200217 		while (ResponseRecords && !ResponseRecords->NR_AnswerTo)
2964*4b22b933Srs200217 			{
2965*4b22b933Srs200217 			rr = ResponseRecords;
2966*4b22b933Srs200217 			if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2967*4b22b933Srs200217 				rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
2968*4b22b933Srs200217 			newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec);
2969*4b22b933Srs200217 			rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
2970*4b22b933Srs200217 
2971*4b22b933Srs200217 			if (newptr) responseptr = newptr;
2972*4b22b933Srs200217 			if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue;
2973*4b22b933Srs200217 			else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark;
2974*4b22b933Srs200217 			ResponseRecords = rr->NextResponse;
2975*4b22b933Srs200217 			rr->NextResponse    = mDNSNULL;
2976*4b22b933Srs200217 			rr->NR_AnswerTo     = mDNSNULL;
2977*4b22b933Srs200217 			rr->NR_AdditionalTo = mDNSNULL;
2978*4b22b933Srs200217 			}
2979*4b22b933Srs200217 
2980*4b22b933Srs200217 		if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL);
2981*4b22b933Srs200217 		}
2982*4b22b933Srs200217 	}
2983*4b22b933Srs200217 
CompleteDeregistration(mDNS * const m,AuthRecord * rr)2984*4b22b933Srs200217 mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
2985*4b22b933Srs200217 	{
2986*4b22b933Srs200217 	// Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
2987*4b22b933Srs200217 	// that it should go ahead and immediately dispose of this registration
2988*4b22b933Srs200217 	rr->resrec.RecordType = kDNSRecordTypeShared;
2989*4b22b933Srs200217 	rr->RequireGoodbye    = mDNSfalse;
2990*4b22b933Srs200217 	mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);		// Don't touch rr after this
2991*4b22b933Srs200217 	}
2992*4b22b933Srs200217 
2993*4b22b933Srs200217 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
2994*4b22b933Srs200217 // the record list and/or question list.
2995*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
DiscardDeregistrations(mDNS * const m)2996*4b22b933Srs200217 mDNSlocal void DiscardDeregistrations(mDNS *const m)
2997*4b22b933Srs200217 	{
2998*4b22b933Srs200217 	if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2999*4b22b933Srs200217 	m->CurrentRecord = m->ResourceRecords;
3000*4b22b933Srs200217 
3001*4b22b933Srs200217 	while (m->CurrentRecord)
3002*4b22b933Srs200217 		{
3003*4b22b933Srs200217 		AuthRecord *rr = m->CurrentRecord;
3004*4b22b933Srs200217 		if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3005*4b22b933Srs200217 			CompleteDeregistration(m, rr);		// Don't touch rr after this
3006*4b22b933Srs200217 		else
3007*4b22b933Srs200217 			m->CurrentRecord = rr->next;
3008*4b22b933Srs200217 		}
3009*4b22b933Srs200217 	}
3010*4b22b933Srs200217 
GrantUpdateCredit(AuthRecord * rr)3011*4b22b933Srs200217 mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
3012*4b22b933Srs200217 	{
3013*4b22b933Srs200217 	if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
3014*4b22b933Srs200217 	else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
3015*4b22b933Srs200217 	}
3016*4b22b933Srs200217 
3017*4b22b933Srs200217 // Note about acceleration of announcements to facilitate automatic coalescing of
3018*4b22b933Srs200217 // multiple independent threads of announcements into a single synchronized thread:
3019*4b22b933Srs200217 // The announcements in the packet may be at different stages of maturity;
3020*4b22b933Srs200217 // One-second interval, two-second interval, four-second interval, and so on.
3021*4b22b933Srs200217 // After we've put in all the announcements that are due, we then consider
3022*4b22b933Srs200217 // whether there are other nearly-due announcements that are worth accelerating.
3023*4b22b933Srs200217 // To be eligible for acceleration, a record MUST NOT be older (further along
3024*4b22b933Srs200217 // its timeline) than the most mature record we've already put in the packet.
3025*4b22b933Srs200217 // In other words, younger records can have their timelines accelerated to catch up
3026*4b22b933Srs200217 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
3027*4b22b933Srs200217 // Older records cannot have their timelines accelerated; this would just widen
3028*4b22b933Srs200217 // the gap between them and their younger bretheren and get them even more out of sync.
3029*4b22b933Srs200217 
3030*4b22b933Srs200217 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
3031*4b22b933Srs200217 // the record list and/or question list.
3032*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
SendResponses(mDNS * const m)3033*4b22b933Srs200217 mDNSlocal void SendResponses(mDNS *const m)
3034*4b22b933Srs200217 	{
3035*4b22b933Srs200217 	int pktcount = 0;
3036*4b22b933Srs200217 	AuthRecord *rr, *r2;
3037*4b22b933Srs200217 	mDNSs32 maxExistingAnnounceInterval = 0;
3038*4b22b933Srs200217 	const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
3039*4b22b933Srs200217 
3040*4b22b933Srs200217 	m->NextScheduledResponse = m->timenow + 0x78000000;
3041*4b22b933Srs200217 
3042*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
3043*4b22b933Srs200217 		if (rr->ImmedUnicast)
3044*4b22b933Srs200217 			{
3045*4b22b933Srs200217 			mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} };
3046*4b22b933Srs200217 			mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} };
3047*4b22b933Srs200217 			v4.ip.v4 = rr->v4Requester;
3048*4b22b933Srs200217 			v6.ip.v6 = rr->v6Requester;
3049*4b22b933Srs200217 			if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer);
3050*4b22b933Srs200217 			if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer);
3051*4b22b933Srs200217 			if (rr->ImmedUnicast)
3052*4b22b933Srs200217 				{
3053*4b22b933Srs200217 				LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr));
3054*4b22b933Srs200217 				rr->ImmedUnicast = mDNSfalse;
3055*4b22b933Srs200217 				}
3056*4b22b933Srs200217 			}
3057*4b22b933Srs200217 
3058*4b22b933Srs200217 	// ***
3059*4b22b933Srs200217 	// *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
3060*4b22b933Srs200217 	// ***
3061*4b22b933Srs200217 
3062*4b22b933Srs200217 	// Run through our list of records, and decide which ones we're going to announce on all interfaces
3063*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
3064*4b22b933Srs200217 		{
3065*4b22b933Srs200217 		while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
3066*4b22b933Srs200217 		if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr))
3067*4b22b933Srs200217 			{
3068*4b22b933Srs200217 			rr->ImmedAnswer = mDNSInterfaceMark;		// Send on all interfaces
3069*4b22b933Srs200217 			if (maxExistingAnnounceInterval < rr->ThisAPInterval)
3070*4b22b933Srs200217 				maxExistingAnnounceInterval = rr->ThisAPInterval;
3071*4b22b933Srs200217 			if (rr->UpdateBlocked) rr->UpdateBlocked = 0;
3072*4b22b933Srs200217 			}
3073*4b22b933Srs200217 		}
3074*4b22b933Srs200217 
3075*4b22b933Srs200217 	// Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
3076*4b22b933Srs200217 	// Eligible records that are more than half-way to their announcement time are accelerated
3077*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
3078*4b22b933Srs200217 		if ((rr->resrec.InterfaceID && rr->ImmedAnswer) ||
3079*4b22b933Srs200217 			(rr->ThisAPInterval <= maxExistingAnnounceInterval &&
3080*4b22b933Srs200217 			TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) &&
3081*4b22b933Srs200217 			ResourceRecordIsValidAnswer(rr)))
3082*4b22b933Srs200217 			rr->ImmedAnswer = mDNSInterfaceMark;		// Send on all interfaces
3083*4b22b933Srs200217 
3084*4b22b933Srs200217 	// When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
3085*4b22b933Srs200217 	// NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
3086*4b22b933Srs200217 	// which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
3087*4b22b933Srs200217 	// then all that means is that it won't get sent -- which would not be the end of the world.
3088*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
3089*4b22b933Srs200217 		if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV)
3090*4b22b933Srs200217 			for (r2=m->ResourceRecords; r2; r2=r2->next)				// Scan list of resource records
3091*4b22b933Srs200217 				if (RRTypeIsAddressType(r2->resrec.rrtype) &&			// For all address records (A/AAAA) ...
3092*4b22b933Srs200217 					ResourceRecordIsValidAnswer(r2) &&					// ... which are valid for answer ...
3093*4b22b933Srs200217 					rr->LastMCTime - r2->LastMCTime >= 0 &&				// ... which we have not sent recently ...
3094*4b22b933Srs200217 					rr->resrec.rdatahash == r2->resrec.namehash &&		// ... whose name is the name of the SRV target
3095*4b22b933Srs200217 					SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) &&
3096*4b22b933Srs200217 					(rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID))
3097*4b22b933Srs200217 					r2->ImmedAdditional = r2->resrec.InterfaceID;		// ... then mark this address record for sending too
3098*4b22b933Srs200217 
3099*4b22b933Srs200217 	// If there's a record which is supposed to be unique that we're going to send, then make sure that we give
3100*4b22b933Srs200217 	// the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
3101*4b22b933Srs200217 	// then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
3102*4b22b933Srs200217 	// record, then other RRSet members that have not been sent recently will get flushed out of client caches.
3103*4b22b933Srs200217 	// -- 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
3104*4b22b933Srs200217 	// -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
3105*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
3106*4b22b933Srs200217 		if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3107*4b22b933Srs200217 			{
3108*4b22b933Srs200217 			if (rr->ImmedAnswer)			// If we're sending this as answer, see that its whole RRSet is similarly marked
3109*4b22b933Srs200217 				{
3110*4b22b933Srs200217 				for (r2 = m->ResourceRecords; r2; r2=r2->next)
3111*4b22b933Srs200217 					if (ResourceRecordIsValidAnswer(r2))
3112*4b22b933Srs200217 						if (r2->ImmedAnswer != mDNSInterfaceMark &&
3113*4b22b933Srs200217 							r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
3114*4b22b933Srs200217 							r2->ImmedAnswer = rr->ImmedAnswer;
3115*4b22b933Srs200217 				}
3116*4b22b933Srs200217 			else if (rr->ImmedAdditional)	// If we're sending this as additional, see that its whole RRSet is similarly marked
3117*4b22b933Srs200217 				{
3118*4b22b933Srs200217 				for (r2 = m->ResourceRecords; r2; r2=r2->next)
3119*4b22b933Srs200217 					if (ResourceRecordIsValidAnswer(r2))
3120*4b22b933Srs200217 						if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
3121*4b22b933Srs200217 							r2->ImmedAdditional = rr->ImmedAdditional;
3122*4b22b933Srs200217 				}
3123*4b22b933Srs200217 			}
3124*4b22b933Srs200217 
3125*4b22b933Srs200217 	// Now set SendRNow state appropriately
3126*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
3127*4b22b933Srs200217 		{
3128*4b22b933Srs200217 		if (rr->ImmedAnswer == mDNSInterfaceMark)		// Sending this record on all appropriate interfaces
3129*4b22b933Srs200217 			{
3130*4b22b933Srs200217 			rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
3131*4b22b933Srs200217 			rr->ImmedAdditional = mDNSNULL;				// No need to send as additional if sending as answer
3132*4b22b933Srs200217 			rr->LastMCTime      = m->timenow;
3133*4b22b933Srs200217 			rr->LastMCInterface = rr->ImmedAnswer;
3134*4b22b933Srs200217 			// If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
3135*4b22b933Srs200217 			if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2))
3136*4b22b933Srs200217 				{
3137*4b22b933Srs200217 				rr->AnnounceCount--;
3138*4b22b933Srs200217 				rr->ThisAPInterval *= 2;
3139*4b22b933Srs200217 				rr->LastAPTime = m->timenow;
3140*4b22b933Srs200217 				if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0;
3141*4b22b933Srs200217 				debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount);
3142*4b22b933Srs200217 				}
3143*4b22b933Srs200217 			}
3144*4b22b933Srs200217 		else if (rr->ImmedAnswer)						// Else, just respond to a single query on single interface:
3145*4b22b933Srs200217 			{
3146*4b22b933Srs200217 			rr->SendRNow        = rr->ImmedAnswer;		// Just respond on that interface
3147*4b22b933Srs200217 			rr->ImmedAdditional = mDNSNULL;				// No need to send as additional too
3148*4b22b933Srs200217 			rr->LastMCTime      = m->timenow;
3149*4b22b933Srs200217 			rr->LastMCInterface = rr->ImmedAnswer;
3150*4b22b933Srs200217 			}
3151*4b22b933Srs200217 		SetNextAnnounceProbeTime(m, rr);
3152*4b22b933Srs200217 		//if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
3153*4b22b933Srs200217 		}
3154*4b22b933Srs200217 
3155*4b22b933Srs200217 	// ***
3156*4b22b933Srs200217 	// *** 2. Loop through interface list, sending records as appropriate
3157*4b22b933Srs200217 	// ***
3158*4b22b933Srs200217 
3159*4b22b933Srs200217 	while (intf)
3160*4b22b933Srs200217 		{
3161*4b22b933Srs200217 		int numDereg    = 0;
3162*4b22b933Srs200217 		int numAnnounce = 0;
3163*4b22b933Srs200217 		int numAnswer   = 0;
3164*4b22b933Srs200217 		mDNSu8 *responseptr = m->omsg.data;
3165*4b22b933Srs200217 		mDNSu8 *newptr;
3166*4b22b933Srs200217 		InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
3167*4b22b933Srs200217 
3168*4b22b933Srs200217 		// First Pass. Look for:
3169*4b22b933Srs200217 		// 1. Deregistering records that need to send their goodbye packet
3170*4b22b933Srs200217 		// 2. Updated records that need to retract their old data
3171*4b22b933Srs200217 		// 3. Answers and announcements we need to send
3172*4b22b933Srs200217 		// In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
3173*4b22b933Srs200217 		// send this packet and then try again.
3174*4b22b933Srs200217 		// If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
3175*4b22b933Srs200217 		// because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
3176*4b22b933Srs200217 		for (rr = m->ResourceRecords; rr; rr=rr->next)
3177*4b22b933Srs200217 			if (rr->SendRNow == intf->InterfaceID)
3178*4b22b933Srs200217 				{
3179*4b22b933Srs200217 				if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3180*4b22b933Srs200217 					{
3181*4b22b933Srs200217 					newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3182*4b22b933Srs200217 					if (!newptr && m->omsg.h.numAnswers) break;
3183*4b22b933Srs200217 					numDereg++;
3184*4b22b933Srs200217 					responseptr = newptr;
3185*4b22b933Srs200217 					}
3186*4b22b933Srs200217 				else if (rr->NewRData && !m->SleepState)					// If we have new data for this record
3187*4b22b933Srs200217 					{
3188*4b22b933Srs200217 					RData *OldRData     = rr->resrec.rdata;
3189*4b22b933Srs200217 					mDNSu16 oldrdlength = rr->resrec.rdlength;
3190*4b22b933Srs200217 					// See if we should send a courtesy "goodbye" for the old data before we replace it.
3191*4b22b933Srs200217 					if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye)
3192*4b22b933Srs200217 						{
3193*4b22b933Srs200217 						newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3194*4b22b933Srs200217 						if (!newptr && m->omsg.h.numAnswers) break;
3195*4b22b933Srs200217 						numDereg++;
3196*4b22b933Srs200217 						responseptr = newptr;
3197*4b22b933Srs200217 						rr->RequireGoodbye = mDNSfalse;
3198*4b22b933Srs200217 						}
3199*4b22b933Srs200217 					// Now try to see if we can fit the update in the same packet (not fatal if we can't)
3200*4b22b933Srs200217 					SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
3201*4b22b933Srs200217 					if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3202*4b22b933Srs200217 						rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
3203*4b22b933Srs200217 					newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
3204*4b22b933Srs200217 					rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
3205*4b22b933Srs200217 					if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; }
3206*4b22b933Srs200217 					SetNewRData(&rr->resrec, OldRData, oldrdlength);
3207*4b22b933Srs200217 					}
3208*4b22b933Srs200217 				else
3209*4b22b933Srs200217 					{
3210*4b22b933Srs200217 					if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3211*4b22b933Srs200217 						rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
3212*4b22b933Srs200217 					newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl);
3213*4b22b933Srs200217 					rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
3214*4b22b933Srs200217 					if (!newptr && m->omsg.h.numAnswers) break;
3215*4b22b933Srs200217 					rr->RequireGoodbye = (mDNSu8) (!m->SleepState);
3216*4b22b933Srs200217 					if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
3217*4b22b933Srs200217 					responseptr = newptr;
3218*4b22b933Srs200217 					}
3219*4b22b933Srs200217 				// If sending on all interfaces, go to next interface; else we're finished now
3220*4b22b933Srs200217 				if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any)
3221*4b22b933Srs200217 					rr->SendRNow = GetNextActiveInterfaceID(intf);
3222*4b22b933Srs200217 				else
3223*4b22b933Srs200217 					rr->SendRNow = mDNSNULL;
3224*4b22b933Srs200217 				}
3225*4b22b933Srs200217 
3226*4b22b933Srs200217 		// Second Pass. Add additional records, if there's space.
3227*4b22b933Srs200217 		newptr = responseptr;
3228*4b22b933Srs200217 		for (rr = m->ResourceRecords; rr; rr=rr->next)
3229*4b22b933Srs200217 			if (rr->ImmedAdditional == intf->InterfaceID)
3230*4b22b933Srs200217 				if (ResourceRecordIsValidAnswer(rr))
3231*4b22b933Srs200217 					{
3232*4b22b933Srs200217 					// If we have at least one answer already in the packet, then plan to add additionals too
3233*4b22b933Srs200217 					mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0);
3234*4b22b933Srs200217 
3235*4b22b933Srs200217 					// If we're not planning to send any additionals, but this record is a unique one, then
3236*4b22b933Srs200217 					// make sure we haven't already sent any other members of its RRSet -- if we have, then they
3237*4b22b933Srs200217 					// will have had the cache flush bit set, so now we need to finish the job and send the rest.
3238*4b22b933Srs200217 					if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask))
3239*4b22b933Srs200217 						{
3240*4b22b933Srs200217 						const AuthRecord *a;
3241*4b22b933Srs200217 						for (a = m->ResourceRecords; a; a=a->next)
3242*4b22b933Srs200217 							if (a->LastMCTime      == m->timenow &&
3243*4b22b933Srs200217 								a->LastMCInterface == intf->InterfaceID &&
3244*4b22b933Srs200217 								SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; }
3245*4b22b933Srs200217 						}
3246*4b22b933Srs200217 					if (!SendAdditional)					// If we don't want to send this after all,
3247*4b22b933Srs200217 						rr->ImmedAdditional = mDNSNULL;		// then cancel its ImmedAdditional field
3248*4b22b933Srs200217 					else if (newptr)						// Else, try to add it if we can
3249*4b22b933Srs200217 						{
3250*4b22b933Srs200217 						if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
3251*4b22b933Srs200217 							rr->resrec.rrclass |= kDNSClass_UniqueRRSet;	// Temporarily set the cache flush bit so PutResourceRecord will set it
3252*4b22b933Srs200217 						newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec);
3253*4b22b933Srs200217 						rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;		// Make sure to clear cache flush bit back to normal state
3254*4b22b933Srs200217 						if (newptr)
3255*4b22b933Srs200217 							{
3256*4b22b933Srs200217 							responseptr = newptr;
3257*4b22b933Srs200217 							rr->ImmedAdditional = mDNSNULL;
3258*4b22b933Srs200217 							rr->RequireGoodbye = mDNStrue;
3259*4b22b933Srs200217 							// If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
3260*4b22b933Srs200217 							// This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
3261*4b22b933Srs200217 							// when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
3262*4b22b933Srs200217 							// all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
3263*4b22b933Srs200217 							rr->LastMCTime      = m->timenow;
3264*4b22b933Srs200217 							rr->LastMCInterface = intf->InterfaceID;
3265*4b22b933Srs200217 							}
3266*4b22b933Srs200217 						}
3267*4b22b933Srs200217 					}
3268*4b22b933Srs200217 
3269*4b22b933Srs200217 		if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals)
3270*4b22b933Srs200217 			{
3271*4b22b933Srs200217 			debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
3272*4b22b933Srs200217 				numDereg,                  numDereg                  == 1 ? "" : "s",
3273*4b22b933Srs200217 				numAnnounce,               numAnnounce               == 1 ? "" : "s",
3274*4b22b933Srs200217 				numAnswer,                 numAnswer                 == 1 ? "" : "s",
3275*4b22b933Srs200217 				m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID);
3276*4b22b933Srs200217 			if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL);
3277*4b22b933Srs200217 			if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL);
3278*4b22b933Srs200217 			if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
3279*4b22b933Srs200217 			if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; }
3280*4b22b933Srs200217 			// There might be more things to send on this interface, so go around one more time and try again.
3281*4b22b933Srs200217 			}
3282*4b22b933Srs200217 		else	// Nothing more to send on this interface; go to next
3283*4b22b933Srs200217 			{
3284*4b22b933Srs200217 			const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
3285*4b22b933Srs200217 			#if MDNS_DEBUGMSGS && 0
3286*4b22b933Srs200217 			const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
3287*4b22b933Srs200217 			debugf(msg, intf, next);
3288*4b22b933Srs200217 			#endif
3289*4b22b933Srs200217 			intf = next;
3290*4b22b933Srs200217 			}
3291*4b22b933Srs200217 		}
3292*4b22b933Srs200217 
3293*4b22b933Srs200217 	// ***
3294*4b22b933Srs200217 	// *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3295*4b22b933Srs200217 	// ***
3296*4b22b933Srs200217 
3297*4b22b933Srs200217 	if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
3298*4b22b933Srs200217 	m->CurrentRecord = m->ResourceRecords;
3299*4b22b933Srs200217 	while (m->CurrentRecord)
3300*4b22b933Srs200217 		{
3301*4b22b933Srs200217 		rr = m->CurrentRecord;
3302*4b22b933Srs200217 		m->CurrentRecord = rr->next;
3303*4b22b933Srs200217 
3304*4b22b933Srs200217 		if (rr->SendRNow)
3305*4b22b933Srs200217 			{
3306*4b22b933Srs200217 			if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
3307*4b22b933Srs200217 				LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr));
3308*4b22b933Srs200217 			rr->SendRNow = mDNSNULL;
3309*4b22b933Srs200217 			}
3310*4b22b933Srs200217 
3311*4b22b933Srs200217 		if (rr->ImmedAnswer)
3312*4b22b933Srs200217 			{
3313*4b22b933Srs200217 			if (rr->NewRData) CompleteRDataUpdate(m,rr);	// Update our rdata, clear the NewRData pointer, and return memory to the client
3314*4b22b933Srs200217 
3315*4b22b933Srs200217 			if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3316*4b22b933Srs200217 				CompleteDeregistration(m, rr);		// Don't touch rr after this
3317*4b22b933Srs200217 			else
3318*4b22b933Srs200217 				{
3319*4b22b933Srs200217 				rr->ImmedAnswer  = mDNSNULL;
3320*4b22b933Srs200217 				rr->ImmedUnicast = mDNSfalse;
3321*4b22b933Srs200217 				rr->v4Requester  = zerov4Addr;
3322*4b22b933Srs200217 				rr->v6Requester  = zerov6Addr;
3323*4b22b933Srs200217 				}
3324*4b22b933Srs200217 			}
3325*4b22b933Srs200217 		}
3326*4b22b933Srs200217 	verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow);
3327*4b22b933Srs200217 	}
3328*4b22b933Srs200217 
3329*4b22b933Srs200217 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
3330*4b22b933Srs200217 // so we want to be lazy about how frequently we do it.
3331*4b22b933Srs200217 // 1. If a cache record is currently referenced by *no* active questions,
3332*4b22b933Srs200217 //    then we don't mind expiring it up to a minute late (who will know?)
3333*4b22b933Srs200217 // 2. Else, if a cache record is due for some of its final expiration queries,
3334*4b22b933Srs200217 //    we'll allow them to be late by up to 2% of the TTL
3335*4b22b933Srs200217 // 3. Else, if a cache record has completed all its final expiration queries without success,
3336*4b22b933Srs200217 //    and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
3337*4b22b933Srs200217 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
3338*4b22b933Srs200217 //    so allow at most 1/10 second lateness
3339*4b22b933Srs200217 #define CacheCheckGracePeriod(RR) (                                                   \
3340*4b22b933Srs200217 	((RR)->DelayDelivery                           ) ? (mDNSPlatformOneSecond/10)   : \
3341*4b22b933Srs200217 	((RR)->CRActiveQuestion == mDNSNULL            ) ? (60 * mDNSPlatformOneSecond) : \
3342*4b22b933Srs200217 	((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50)            : \
3343*4b22b933Srs200217 	((RR)->resrec.rroriginalttl > 10               ) ? (mDNSPlatformOneSecond)      : (mDNSPlatformOneSecond/10))
3344*4b22b933Srs200217 
3345*4b22b933Srs200217 // Note: MUST call SetNextCacheCheckTime any time we change:
3346*4b22b933Srs200217 // rr->TimeRcvd
3347*4b22b933Srs200217 // rr->resrec.rroriginalttl
3348*4b22b933Srs200217 // rr->UnansweredQueries
3349*4b22b933Srs200217 // rr->CRActiveQuestion
3350*4b22b933Srs200217 // Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary
3351*4b22b933Srs200217 // Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime
SetNextCacheCheckTime(mDNS * const m,CacheRecord * const rr)3352*4b22b933Srs200217 mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr)
3353*4b22b933Srs200217 	{
3354*4b22b933Srs200217 	rr->NextRequiredQuery = RRExpireTime(rr);
3355*4b22b933Srs200217 
3356*4b22b933Srs200217 	// If we have an active question, then see if we want to schedule a refresher query for this record.
3357*4b22b933Srs200217 	// Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
3358*4b22b933Srs200217 	if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
3359*4b22b933Srs200217 		{
3360*4b22b933Srs200217 		rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries);
3361*4b22b933Srs200217 		rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50);
3362*4b22b933Srs200217 		verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks",
3363*4b22b933Srs200217 			rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype),
3364*4b22b933Srs200217 			(rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr));
3365*4b22b933Srs200217 		}
3366*4b22b933Srs200217 
3367*4b22b933Srs200217 	if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
3368*4b22b933Srs200217 		m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
3369*4b22b933Srs200217 
3370*4b22b933Srs200217 	if (rr->DelayDelivery)
3371*4b22b933Srs200217 		if (m->NextCacheCheck - rr->DelayDelivery > 0)
3372*4b22b933Srs200217 			m->NextCacheCheck = rr->DelayDelivery;
3373*4b22b933Srs200217 	}
3374*4b22b933Srs200217 
3375*4b22b933Srs200217 #define kMinimumReconfirmTime                     ((mDNSu32)mDNSPlatformOneSecond *  5)
3376*4b22b933Srs200217 #define kDefaultReconfirmTimeForWake              ((mDNSu32)mDNSPlatformOneSecond *  5)
3377*4b22b933Srs200217 #define kDefaultReconfirmTimeForNoAnswer          ((mDNSu32)mDNSPlatformOneSecond * 15)
3378*4b22b933Srs200217 #define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30)
3379*4b22b933Srs200217 
mDNS_Reconfirm_internal(mDNS * const m,CacheRecord * const rr,mDNSu32 interval)3380*4b22b933Srs200217 mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
3381*4b22b933Srs200217 	{
3382*4b22b933Srs200217 	if (interval < kMinimumReconfirmTime)
3383*4b22b933Srs200217 		interval = kMinimumReconfirmTime;
3384*4b22b933Srs200217 	if (interval > 0x10000000)	// Make sure interval doesn't overflow when we multiply by four below
3385*4b22b933Srs200217 		interval = 0x10000000;
3386*4b22b933Srs200217 
3387*4b22b933Srs200217 	// If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
3388*4b22b933Srs200217 	if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3))
3389*4b22b933Srs200217 		{
3390*4b22b933Srs200217 		// Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
3391*4b22b933Srs200217 		// For all the reconfirmations in a given batch, we want to use the same random value
3392*4b22b933Srs200217 		// so that the reconfirmation questions can be grouped into a single query packet
3393*4b22b933Srs200217 		if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF);
3394*4b22b933Srs200217 		interval += mDNSRandomFromFixedSeed(m->RandomReconfirmDelay, interval/3);
3395*4b22b933Srs200217 		rr->TimeRcvd          = m->timenow - (mDNSs32)interval * 3;
3396*4b22b933Srs200217 		rr->resrec.rroriginalttl     = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond;
3397*4b22b933Srs200217 		SetNextCacheCheckTime(m, rr);
3398*4b22b933Srs200217 		}
3399*4b22b933Srs200217 	debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr));
3400*4b22b933Srs200217 	return(mStatus_NoError);
3401*4b22b933Srs200217 	}
3402*4b22b933Srs200217 
3403*4b22b933Srs200217 #define MaxQuestionInterval         (3600 * mDNSPlatformOneSecond)
3404*4b22b933Srs200217 
3405*4b22b933Srs200217 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
3406*4b22b933Srs200217 // It also appends to the list of known answer records that need to be included,
3407*4b22b933Srs200217 // and updates the forcast for the size of the known answer section.
BuildQuestion(mDNS * const m,DNSMessage * query,mDNSu8 ** queryptr,DNSQuestion * q,CacheRecord *** kalistptrptr,mDNSu32 * answerforecast)3408*4b22b933Srs200217 mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
3409*4b22b933Srs200217 	CacheRecord ***kalistptrptr, mDNSu32 *answerforecast)
3410*4b22b933Srs200217 	{
3411*4b22b933Srs200217 	mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
3412*4b22b933Srs200217 	mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
3413*4b22b933Srs200217 	const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData;
3414*4b22b933Srs200217 	mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit));
3415*4b22b933Srs200217 	if (!newptr)
3416*4b22b933Srs200217 		{
3417*4b22b933Srs200217 		debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c);
3418*4b22b933Srs200217 		return(mDNSfalse);
3419*4b22b933Srs200217 		}
3420*4b22b933Srs200217 	else if (newptr + *answerforecast >= limit)
3421*4b22b933Srs200217 		{
3422*4b22b933Srs200217 		verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d",
3423*4b22b933Srs200217 			q->qname.c, newptr + *answerforecast - query->data);
3424*4b22b933Srs200217 		query->h.numQuestions--;
3425*4b22b933Srs200217 		return(mDNSfalse);
3426*4b22b933Srs200217 		}
3427*4b22b933Srs200217 	else
3428*4b22b933Srs200217 		{
3429*4b22b933Srs200217 		mDNSu32 forecast = *answerforecast;
3430*4b22b933Srs200217 		const mDNSu32 slot = HashSlot(&q->qname);
3431*4b22b933Srs200217 		CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3432*4b22b933Srs200217 		CacheRecord *rr;
3433*4b22b933Srs200217 		CacheRecord **ka = *kalistptrptr;	// Make a working copy of the pointer we're going to update
3434*4b22b933Srs200217 
3435*4b22b933Srs200217 		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)				// If we have a resource record in our cache,
3436*4b22b933Srs200217 			if (rr->resrec.InterfaceID == q->SendQNow &&					// received on this interface
3437*4b22b933Srs200217 				rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList &&	// which is not already in the known answer list
3438*4b22b933Srs200217 				rr->resrec.rdlength <= SmallRecordLimit &&					// which is small enough to sensibly fit in the packet
3439*4b22b933Srs200217 				ResourceRecordAnswersQuestion(&rr->resrec, q) &&			// which answers our question
3440*4b22b933Srs200217 				rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >				// and its half-way-to-expiry time is at least 1 second away
3441*4b22b933Srs200217 												mDNSPlatformOneSecond)		// (also ensures we never include goodbye records with TTL=1)
3442*4b22b933Srs200217 				{
3443*4b22b933Srs200217 				*ka = rr;	// Link this record into our known answer chain
3444*4b22b933Srs200217 				ka = &rr->NextInKAList;
3445*4b22b933Srs200217 				// We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3446*4b22b933Srs200217 				forecast += 12 + rr->resrec.rdestimate;
3447*4b22b933Srs200217 				// If we're trying to put more than one question in this packet, and it doesn't fit
3448*4b22b933Srs200217 				// then undo that last question and try again next time
3449*4b22b933Srs200217 				if (query->h.numQuestions > 1 && newptr + forecast >= limit)
3450*4b22b933Srs200217 					{
3451*4b22b933Srs200217 					debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
3452*4b22b933Srs200217 						q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data);
3453*4b22b933Srs200217 					query->h.numQuestions--;
3454*4b22b933Srs200217 					ka = *kalistptrptr;		// Go back to where we started and retract these answer records
3455*4b22b933Srs200217 					while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
3456*4b22b933Srs200217 					return(mDNSfalse);		// Return false, so we'll try again in the next packet
3457*4b22b933Srs200217 					}
3458*4b22b933Srs200217 				}
3459*4b22b933Srs200217 
3460*4b22b933Srs200217 		// Traffic reduction:
3461*4b22b933Srs200217 		// If we already have at least one unique answer in the cache,
3462*4b22b933Srs200217 		// OR we have so many shared answers that the KA list is too big to fit in one packet
3463*4b22b933Srs200217 		// The we suppress queries number 3 and 5:
3464*4b22b933Srs200217 		// Query 1 (immediately;      ThisQInterval =  1 sec; request unicast replies)
3465*4b22b933Srs200217 		// Query 2 (after  1 second;  ThisQInterval =  2 sec; send normally)
3466*4b22b933Srs200217 		// Query 3 (after  2 seconds; ThisQInterval =  4 sec; may suppress)
3467*4b22b933Srs200217 		// Query 4 (after  4 seconds; ThisQInterval =  8 sec; send normally)
3468*4b22b933Srs200217 		// Query 5 (after  8 seconds; ThisQInterval = 16 sec; may suppress)
3469*4b22b933Srs200217 		// Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
3470*4b22b933Srs200217 		if (q->UniqueAnswers || newptr + forecast >= limit)
3471*4b22b933Srs200217 			if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32)
3472*4b22b933Srs200217 				{
3473*4b22b933Srs200217 				query->h.numQuestions--;
3474*4b22b933Srs200217 				ka = *kalistptrptr;		// Go back to where we started and retract these answer records
3475*4b22b933Srs200217 				while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
3476*4b22b933Srs200217 				return(mDNStrue);		// Return true: pretend we succeeded, even though we actually suppressed this question
3477*4b22b933Srs200217 				}
3478*4b22b933Srs200217 
3479*4b22b933Srs200217 		// Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
3480*4b22b933Srs200217 		*queryptr        = newptr;				// Update the packet pointer
3481*4b22b933Srs200217 		*answerforecast  = forecast;			// Update the forecast
3482*4b22b933Srs200217 		*kalistptrptr    = ka;					// Update the known answer list pointer
3483*4b22b933Srs200217 		if (ucast) m->ExpectUnicastResponse = m->timenow;
3484*4b22b933Srs200217 
3485*4b22b933Srs200217 		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)				// For every resource record in our cache,
3486*4b22b933Srs200217 			if (rr->resrec.InterfaceID == q->SendQNow &&					// received on this interface
3487*4b22b933Srs200217 				rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList &&	// which is not in the known answer list
3488*4b22b933Srs200217 				ResourceRecordAnswersQuestion(&rr->resrec, q))				// which answers our question
3489*4b22b933Srs200217 					{
3490*4b22b933Srs200217 					rr->UnansweredQueries++;								// indicate that we're expecting a response
3491*4b22b933Srs200217 					rr->LastUnansweredTime = m->timenow;
3492*4b22b933Srs200217 					SetNextCacheCheckTime(m, rr);
3493*4b22b933Srs200217 					}
3494*4b22b933Srs200217 
3495*4b22b933Srs200217 		return(mDNStrue);
3496*4b22b933Srs200217 		}
3497*4b22b933Srs200217 	}
3498*4b22b933Srs200217 
ReconfirmAntecedents(mDNS * const m,DNSQuestion * q)3499*4b22b933Srs200217 mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
3500*4b22b933Srs200217 	{
3501*4b22b933Srs200217 	mDNSu32 slot;
3502*4b22b933Srs200217 	CacheGroup *cg;
3503*4b22b933Srs200217 	CacheRecord *rr;
3504*4b22b933Srs200217 	domainname *target;
3505*4b22b933Srs200217 	FORALL_CACHERECORDS(slot, cg, rr)
3506*4b22b933Srs200217 		if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname))
3507*4b22b933Srs200217 			mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
3508*4b22b933Srs200217 	}
3509*4b22b933Srs200217 
3510*4b22b933Srs200217 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize],mDNSs32 time)3511*4b22b933Srs200217 mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time)
3512*4b22b933Srs200217 	{
3513*4b22b933Srs200217 	int i;
3514*4b22b933Srs200217 	for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
3515*4b22b933Srs200217 	}
3516*4b22b933Srs200217 
ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize],mDNSs32 time,mDNSInterfaceID InterfaceID)3517*4b22b933Srs200217 mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID)
3518*4b22b933Srs200217 	{
3519*4b22b933Srs200217 	int i;
3520*4b22b933Srs200217 	for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
3521*4b22b933Srs200217 	}
3522*4b22b933Srs200217 
SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize],const NetworkInterfaceInfo * const intf)3523*4b22b933Srs200217 mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf)
3524*4b22b933Srs200217 	{
3525*4b22b933Srs200217 	int i;
3526*4b22b933Srs200217 	mDNSBool v4 = !intf->IPv4Available;		// If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
3527*4b22b933Srs200217 	mDNSBool v6 = !intf->IPv6Available;		// If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
3528*4b22b933Srs200217 	for (i=0; i<DupSuppressInfoSize; i++)
3529*4b22b933Srs200217 		if (ds[i].InterfaceID == intf->InterfaceID)
3530*4b22b933Srs200217 			{
3531*4b22b933Srs200217 			if      (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue;
3532*4b22b933Srs200217 			else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue;
3533*4b22b933Srs200217 			if (v4 && v6) return(mDNStrue);
3534*4b22b933Srs200217 			}
3535*4b22b933Srs200217 	return(mDNSfalse);
3536*4b22b933Srs200217 	}
3537*4b22b933Srs200217 
RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize],mDNSs32 Time,mDNSInterfaceID InterfaceID,mDNSs32 Type)3538*4b22b933Srs200217 mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type)
3539*4b22b933Srs200217 	{
3540*4b22b933Srs200217 	int i, j;
3541*4b22b933Srs200217 
3542*4b22b933Srs200217 	// See if we have this one in our list somewhere already
3543*4b22b933Srs200217 	for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break;
3544*4b22b933Srs200217 
3545*4b22b933Srs200217 	// If not, find a slot we can re-use
3546*4b22b933Srs200217 	if (i >= DupSuppressInfoSize)
3547*4b22b933Srs200217 		{
3548*4b22b933Srs200217 		i = 0;
3549*4b22b933Srs200217 		for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++)
3550*4b22b933Srs200217 			if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0)
3551*4b22b933Srs200217 				i = j;
3552*4b22b933Srs200217 		}
3553*4b22b933Srs200217 
3554*4b22b933Srs200217 	// Record the info about this query we saw
3555*4b22b933Srs200217 	ds[i].Time        = Time;
3556*4b22b933Srs200217 	ds[i].InterfaceID = InterfaceID;
3557*4b22b933Srs200217 	ds[i].Type        = Type;
3558*4b22b933Srs200217 
3559*4b22b933Srs200217 	return(i);
3560*4b22b933Srs200217 	}
3561*4b22b933Srs200217 
AccelerateThisQuery(mDNS * const m,DNSQuestion * q)3562*4b22b933Srs200217 mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
3563*4b22b933Srs200217 	{
3564*4b22b933Srs200217 	// If more than 90% of the way to the query time, we should unconditionally accelerate it
3565*4b22b933Srs200217 	if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10))
3566*4b22b933Srs200217 		return(mDNStrue);
3567*4b22b933Srs200217 
3568*4b22b933Srs200217 	// If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
3569*4b22b933Srs200217 	if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2))
3570*4b22b933Srs200217 		{
3571*4b22b933Srs200217 		// We forecast: qname (n) type (2) class (2)
3572*4b22b933Srs200217 		mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4;
3573*4b22b933Srs200217 		const mDNSu32 slot = HashSlot(&q->qname);
3574*4b22b933Srs200217 		CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3575*4b22b933Srs200217 		CacheRecord *rr;
3576*4b22b933Srs200217 		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)				// If we have a resource record in our cache,
3577*4b22b933Srs200217 			if (rr->resrec.rdlength <= SmallRecordLimit &&					// which is small enough to sensibly fit in the packet
3578*4b22b933Srs200217 				ResourceRecordAnswersQuestion(&rr->resrec, q) &&			// which answers our question
3579*4b22b933Srs200217 				rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 &&			// and it is less than half-way to expiry
3580*4b22b933Srs200217 				rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery
3581*4b22b933Srs200217 				{
3582*4b22b933Srs200217 				// We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3583*4b22b933Srs200217 				forecast += 12 + rr->resrec.rdestimate;
3584*4b22b933Srs200217 				if (forecast >= 512) return(mDNSfalse);	// If this would add 512 bytes or more to the packet, don't accelerate
3585*4b22b933Srs200217 				}
3586*4b22b933Srs200217 		return(mDNStrue);
3587*4b22b933Srs200217 		}
3588*4b22b933Srs200217 
3589*4b22b933Srs200217 	return(mDNSfalse);
3590*4b22b933Srs200217 	}
3591*4b22b933Srs200217 
3592*4b22b933Srs200217 // How Standard Queries are generated:
3593*4b22b933Srs200217 // 1. The Question Section contains the question
3594*4b22b933Srs200217 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
3595*4b22b933Srs200217 
3596*4b22b933Srs200217 // How Probe Queries are generated:
3597*4b22b933Srs200217 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
3598*4b22b933Srs200217 // if some other host is already using *any* records with this name, we want to know about it.
3599*4b22b933Srs200217 // 2. The Authority Section contains the proposed values we intend to use for one or more
3600*4b22b933Srs200217 // of our records with that name (analogous to the Update section of DNS Update packets)
3601*4b22b933Srs200217 // because if some other host is probing at the same time, we each want to know what the other is
3602*4b22b933Srs200217 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
3603*4b22b933Srs200217 
SendQueries(mDNS * const m)3604*4b22b933Srs200217 mDNSlocal void SendQueries(mDNS *const m)
3605*4b22b933Srs200217 	{
3606*4b22b933Srs200217 	mDNSu32 slot;
3607*4b22b933Srs200217 	CacheGroup *cg;
3608*4b22b933Srs200217 	CacheRecord *cr;
3609*4b22b933Srs200217 	AuthRecord *ar;
3610*4b22b933Srs200217 	int pktcount = 0;
3611*4b22b933Srs200217 	DNSQuestion *q;
3612*4b22b933Srs200217 	// For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
3613*4b22b933Srs200217 	mDNSs32 maxExistingQuestionInterval = 0;
3614*4b22b933Srs200217 	const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
3615*4b22b933Srs200217 	CacheRecord *KnownAnswerList = mDNSNULL;
3616*4b22b933Srs200217 
3617*4b22b933Srs200217 	// 1. If time for a query, work out what we need to do
3618*4b22b933Srs200217 	if (m->timenow - m->NextScheduledQuery >= 0)
3619*4b22b933Srs200217 		{
3620*4b22b933Srs200217 		CacheRecord *rr;
3621*4b22b933Srs200217 		m->NextScheduledQuery = m->timenow + 0x78000000;
3622*4b22b933Srs200217 
3623*4b22b933Srs200217 		// We're expecting to send a query anyway, so see if any expiring cache records are close enough
3624*4b22b933Srs200217 		// to their NextRequiredQuery to be worth batching them together with this one
3625*4b22b933Srs200217 		FORALL_CACHERECORDS(slot, cg, rr)
3626*4b22b933Srs200217 			if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
3627*4b22b933Srs200217 				if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0)
3628*4b22b933Srs200217 					{
3629*4b22b933Srs200217 					q = rr->CRActiveQuestion;
3630*4b22b933Srs200217 					ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID);
3631*4b22b933Srs200217 					if (q->Target.type) q->SendQNow = mDNSInterfaceMark;	// If unicast query, mark it
3632*4b22b933Srs200217 					else if (q->SendQNow == mDNSNULL)               q->SendQNow = rr->resrec.InterfaceID;
3633*4b22b933Srs200217 					else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
3634*4b22b933Srs200217 					}
3635*4b22b933Srs200217 
3636*4b22b933Srs200217 		// Scan our list of questions to see which *unicast* queries need to be sent
3637*4b22b933Srs200217 		for (q = m->Questions; q; q=q->next)
3638*4b22b933Srs200217 			if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow)))
3639*4b22b933Srs200217 				{
3640*4b22b933Srs200217 				mDNSu8       *qptr        = m->omsg.data;
3641*4b22b933Srs200217 				const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data);
3642*4b22b933Srs200217 				InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags);
3643*4b22b933Srs200217 				qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass);
3644*4b22b933Srs200217 				mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL);
3645*4b22b933Srs200217 				q->ThisQInterval   *= 2;
3646*4b22b933Srs200217 				if (q->ThisQInterval > MaxQuestionInterval)
3647*4b22b933Srs200217 					q->ThisQInterval = MaxQuestionInterval;
3648*4b22b933Srs200217 				q->LastQTime        = m->timenow;
3649*4b22b933Srs200217 				q->LastQTxTime      = m->timenow;
3650*4b22b933Srs200217 				q->RecentAnswerPkts = 0;
3651*4b22b933Srs200217 				q->SendQNow         = mDNSNULL;
3652*4b22b933Srs200217 				m->ExpectUnicastResponse = m->timenow;
3653*4b22b933Srs200217 				}
3654*4b22b933Srs200217 
3655*4b22b933Srs200217 		// Scan our list of questions to see which *multicast* queries we're definitely going to send
3656*4b22b933Srs200217 		for (q = m->Questions; q; q=q->next)
3657*4b22b933Srs200217 			if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow))
3658*4b22b933Srs200217 				{
3659*4b22b933Srs200217 				q->SendQNow = mDNSInterfaceMark;		// Mark this question for sending on all interfaces
3660*4b22b933Srs200217 				if (maxExistingQuestionInterval < q->ThisQInterval)
3661*4b22b933Srs200217 					maxExistingQuestionInterval = q->ThisQInterval;
3662*4b22b933Srs200217 				}
3663*4b22b933Srs200217 
3664*4b22b933Srs200217 		// Scan our list of questions
3665*4b22b933Srs200217 		// (a) to see if there are any more that are worth accelerating, and
3666*4b22b933Srs200217 		// (b) to update the state variables for *all* the questions we're going to send
3667*4b22b933Srs200217 		for (q = m->Questions; q; q=q->next)
3668*4b22b933Srs200217 			{
3669*4b22b933Srs200217 			if (q->SendQNow ||
3670*4b22b933Srs200217 				(!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))
3671*4b22b933Srs200217 				{
3672*4b22b933Srs200217 				// If at least halfway to next query time, advance to next interval
3673*4b22b933Srs200217 				// If less than halfway to next query time, then
3674*4b22b933Srs200217 				// treat this as logically a repeat of the last transmission, without advancing the interval
3675*4b22b933Srs200217 				if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0)
3676*4b22b933Srs200217 					{
3677*4b22b933Srs200217 					q->SendQNow = mDNSInterfaceMark;	// Mark this question for sending on all interfaces
3678*4b22b933Srs200217 					q->ThisQInterval *= 2;
3679*4b22b933Srs200217 					if (q->ThisQInterval > MaxQuestionInterval)
3680*4b22b933Srs200217 						q->ThisQInterval = MaxQuestionInterval;
3681*4b22b933Srs200217 					else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8)
3682*4b22b933Srs200217 						{
3683*4b22b933Srs200217 						debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents",
3684*4b22b933Srs200217 							q->qname.c, DNSTypeName(q->qtype));
3685*4b22b933Srs200217 						ReconfirmAntecedents(m, q);		// Sending third query, and no answers yet; time to begin doubting the source
3686*4b22b933Srs200217 						}
3687*4b22b933Srs200217 					}
3688*4b22b933Srs200217 
3689*4b22b933Srs200217 				// Mark for sending. (If no active interfaces, then don't even try.)
3690*4b22b933Srs200217 				q->SendOnAll = (q->SendQNow == mDNSInterfaceMark);
3691*4b22b933Srs200217 				if (q->SendOnAll)
3692*4b22b933Srs200217 					{
3693*4b22b933Srs200217 					q->SendQNow  = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID;
3694*4b22b933Srs200217 					q->LastQTime = m->timenow;
3695*4b22b933Srs200217 					}
3696*4b22b933Srs200217 
3697*4b22b933Srs200217 				// If we recorded a duplicate suppression for this question less than half an interval ago,
3698*4b22b933Srs200217 				// then we consider it recent enough that we don't need to do an identical query ourselves.
3699*4b22b933Srs200217 				ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2);
3700*4b22b933Srs200217 
3701*4b22b933Srs200217 				q->LastQTxTime      = m->timenow;
3702*4b22b933Srs200217 				q->RecentAnswerPkts = 0;
3703*4b22b933Srs200217 				if (q->RequestUnicast) q->RequestUnicast--;
3704*4b22b933Srs200217 				}
3705*4b22b933Srs200217 			// For all questions (not just the ones we're sending) check what the next scheduled event will be
3706*4b22b933Srs200217 			SetNextQueryTime(m,q);
3707*4b22b933Srs200217 			}
3708*4b22b933Srs200217 		}
3709*4b22b933Srs200217 
3710*4b22b933Srs200217 	// 2. Scan our authoritative RR list to see what probes we might need to send
3711*4b22b933Srs200217 	if (m->timenow - m->NextScheduledProbe >= 0)
3712*4b22b933Srs200217 		{
3713*4b22b933Srs200217 		m->NextScheduledProbe = m->timenow + 0x78000000;
3714*4b22b933Srs200217 
3715*4b22b933Srs200217 		if (m->CurrentRecord) LogMsg("SendQueries:   ERROR m->CurrentRecord already set");
3716*4b22b933Srs200217 		m->CurrentRecord = m->ResourceRecords;
3717*4b22b933Srs200217 		while (m->CurrentRecord)
3718*4b22b933Srs200217 			{
3719*4b22b933Srs200217 			AuthRecord *rr = m->CurrentRecord;
3720*4b22b933Srs200217 			m->CurrentRecord = rr->next;
3721*4b22b933Srs200217 			if (rr->resrec.RecordType == kDNSRecordTypeUnique)			// For all records that are still probing...
3722*4b22b933Srs200217 				{
3723*4b22b933Srs200217 				// 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
3724*4b22b933Srs200217 				if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
3725*4b22b933Srs200217 					{
3726*4b22b933Srs200217 					SetNextAnnounceProbeTime(m, rr);
3727*4b22b933Srs200217 					}
3728*4b22b933Srs200217 				// 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
3729*4b22b933Srs200217 				else if (rr->ProbeCount)
3730*4b22b933Srs200217 					{
3731*4b22b933Srs200217 					// Mark for sending. (If no active interfaces, then don't even try.)
3732*4b22b933Srs200217 					rr->SendRNow   = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
3733*4b22b933Srs200217 					rr->LastAPTime = m->timenow;
3734*4b22b933Srs200217 					rr->ProbeCount--;
3735*4b22b933Srs200217 					SetNextAnnounceProbeTime(m, rr);
3736*4b22b933Srs200217 					}
3737*4b22b933Srs200217 				// else, if it has now finished probing, move it to state Verified,
3738*4b22b933Srs200217 				// and update m->NextScheduledResponse so it will be announced
3739*4b22b933Srs200217 				else
3740*4b22b933Srs200217 					{
3741*4b22b933Srs200217 					AuthRecord *r2;
3742*4b22b933Srs200217 					rr->resrec.RecordType     = kDNSRecordTypeVerified;
3743*4b22b933Srs200217 					rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique;
3744*4b22b933Srs200217 					rr->LastAPTime     = m->timenow - DefaultAnnounceIntervalForTypeUnique;
3745*4b22b933Srs200217 					SetNextAnnounceProbeTime(m, rr);
3746*4b22b933Srs200217 					// If we have any records on our duplicate list that match this one, they have now also completed probing
3747*4b22b933Srs200217 					for (r2 = m->DuplicateRecords; r2; r2=r2->next)
3748*4b22b933Srs200217 						if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr))
3749*4b22b933Srs200217 							r2->ProbeCount = 0;
3750*4b22b933Srs200217 					AcknowledgeRecord(m, rr);
3751*4b22b933Srs200217 					}
3752*4b22b933Srs200217 				}
3753*4b22b933Srs200217 			}
3754*4b22b933Srs200217 		m->CurrentRecord = m->DuplicateRecords;
3755*4b22b933Srs200217 		while (m->CurrentRecord)
3756*4b22b933Srs200217 			{
3757*4b22b933Srs200217 			AuthRecord *rr = m->CurrentRecord;
3758*4b22b933Srs200217 			m->CurrentRecord = rr->next;
3759*4b22b933Srs200217 			if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
3760*4b22b933Srs200217 				AcknowledgeRecord(m, rr);
3761*4b22b933Srs200217 			}
3762*4b22b933Srs200217 		}
3763*4b22b933Srs200217 
3764*4b22b933Srs200217 	// 3. Now we know which queries and probes we're sending,
3765*4b22b933Srs200217 	// go through our interface list sending the appropriate queries on each interface
3766*4b22b933Srs200217 	while (intf)
3767*4b22b933Srs200217 		{
3768*4b22b933Srs200217 		AuthRecord *rr;
3769*4b22b933Srs200217 		mDNSu8 *queryptr = m->omsg.data;
3770*4b22b933Srs200217 		InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags);
3771*4b22b933Srs200217 		if (KnownAnswerList) verbosedebugf("SendQueries:   KnownAnswerList set... Will continue from previous packet");
3772*4b22b933Srs200217 		if (!KnownAnswerList)
3773*4b22b933Srs200217 			{
3774*4b22b933Srs200217 			// Start a new known-answer list
3775*4b22b933Srs200217 			CacheRecord **kalistptr = &KnownAnswerList;
3776*4b22b933Srs200217 			mDNSu32 answerforecast = 0;
3777*4b22b933Srs200217 
3778*4b22b933Srs200217 			// Put query questions in this packet
3779*4b22b933Srs200217 			for (q = m->Questions; q; q=q->next)
3780*4b22b933Srs200217 				if (q->SendQNow == intf->InterfaceID)
3781*4b22b933Srs200217 					{
3782*4b22b933Srs200217 					debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
3783*4b22b933Srs200217 						SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting    ",
3784*4b22b933Srs200217 						q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
3785*4b22b933Srs200217 					// If we're suppressing this question, or we successfully put it, update its SendQNow state
3786*4b22b933Srs200217 					if (SuppressOnThisInterface(q->DupSuppress, intf) ||
3787*4b22b933Srs200217 						BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
3788*4b22b933Srs200217 							q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
3789*4b22b933Srs200217 					}
3790*4b22b933Srs200217 
3791*4b22b933Srs200217 			// Put probe questions in this packet
3792*4b22b933Srs200217 			for (rr = m->ResourceRecords; rr; rr=rr->next)
3793*4b22b933Srs200217 				if (rr->SendRNow == intf->InterfaceID)
3794*4b22b933Srs200217 					{
3795*4b22b933Srs200217 					mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353;
3796*4b22b933Srs200217 					mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
3797*4b22b933Srs200217 					const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
3798*4b22b933Srs200217 					mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit));
3799*4b22b933Srs200217 					// We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3800*4b22b933Srs200217 					mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate;
3801*4b22b933Srs200217 					if (newptr && newptr + forecast < limit)
3802*4b22b933Srs200217 						{
3803*4b22b933Srs200217 						queryptr       = newptr;
3804*4b22b933Srs200217 						answerforecast = forecast;
3805*4b22b933Srs200217 						rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf);
3806*4b22b933Srs200217 						rr->IncludeInProbe = mDNStrue;
3807*4b22b933Srs200217 						verbosedebugf("SendQueries:   Put Question %##s (%s) probecount %d",
3808*4b22b933Srs200217 							rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount);
3809*4b22b933Srs200217 						}
3810*4b22b933Srs200217 					else
3811*4b22b933Srs200217 						{
3812*4b22b933Srs200217 						verbosedebugf("SendQueries:   Retracting Question %##s (%s)",
3813*4b22b933Srs200217 							rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3814*4b22b933Srs200217 						m->omsg.h.numQuestions--;
3815*4b22b933Srs200217 						}
3816*4b22b933Srs200217 					}
3817*4b22b933Srs200217 				}
3818*4b22b933Srs200217 
3819*4b22b933Srs200217 		// Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
3820*4b22b933Srs200217 		while (KnownAnswerList)
3821*4b22b933Srs200217 			{
3822*4b22b933Srs200217 			CacheRecord *rr = KnownAnswerList;
3823*4b22b933Srs200217 			mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
3824*4b22b933Srs200217 			mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd);
3825*4b22b933Srs200217 			if (newptr)
3826*4b22b933Srs200217 				{
3827*4b22b933Srs200217 				verbosedebugf("SendQueries:   Put %##s (%s) at %d - %d",
3828*4b22b933Srs200217 					rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data);
3829*4b22b933Srs200217 				queryptr = newptr;
3830*4b22b933Srs200217 				KnownAnswerList = rr->NextInKAList;
3831*4b22b933Srs200217 				rr->NextInKAList = mDNSNULL;
3832*4b22b933Srs200217 				}
3833*4b22b933Srs200217 			else
3834*4b22b933Srs200217 				{
3835*4b22b933Srs200217 				// If we ran out of space and we have more than one question in the packet, that's an error --
3836*4b22b933Srs200217 				// we shouldn't have put more than one question if there was a risk of us running out of space.
3837*4b22b933Srs200217 				if (m->omsg.h.numQuestions > 1)
3838*4b22b933Srs200217 					LogMsg("SendQueries:   Put %d answers; No more space for known answers", m->omsg.h.numAnswers);
3839*4b22b933Srs200217 				m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
3840*4b22b933Srs200217 				break;
3841*4b22b933Srs200217 				}
3842*4b22b933Srs200217 			}
3843*4b22b933Srs200217 
3844*4b22b933Srs200217 		for (rr = m->ResourceRecords; rr; rr=rr->next)
3845*4b22b933Srs200217 			if (rr->IncludeInProbe)
3846*4b22b933Srs200217 				{
3847*4b22b933Srs200217 				mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec);
3848*4b22b933Srs200217 				rr->IncludeInProbe = mDNSfalse;
3849*4b22b933Srs200217 				if (newptr) queryptr = newptr;
3850*4b22b933Srs200217 				else LogMsg("SendQueries:   How did we fail to have space for the Update record %##s (%s)?",
3851*4b22b933Srs200217 					rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3852*4b22b933Srs200217 				}
3853*4b22b933Srs200217 
3854*4b22b933Srs200217 		if (queryptr > m->omsg.data)
3855*4b22b933Srs200217 			{
3856*4b22b933Srs200217 			if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1)
3857*4b22b933Srs200217 				LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions);
3858*4b22b933Srs200217 			debugf("SendQueries:   Sending %d Question%s %d Answer%s %d Update%s on %p",
3859*4b22b933Srs200217 				m->omsg.h.numQuestions,   m->omsg.h.numQuestions   == 1 ? "" : "s",
3860*4b22b933Srs200217 				m->omsg.h.numAnswers,     m->omsg.h.numAnswers     == 1 ? "" : "s",
3861*4b22b933Srs200217 				m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID);
3862*4b22b933Srs200217 			if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL);
3863*4b22b933Srs200217 			if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL);
3864*4b22b933Srs200217 			if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
3865*4b22b933Srs200217 			if (++pktcount >= 1000)
3866*4b22b933Srs200217 				{ LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; }
3867*4b22b933Srs200217 			// There might be more records left in the known answer list, or more questions to send
3868*4b22b933Srs200217 			// on this interface, so go around one more time and try again.
3869*4b22b933Srs200217 			}
3870*4b22b933Srs200217 		else	// Nothing more to send on this interface; go to next
3871*4b22b933Srs200217 			{
3872*4b22b933Srs200217 			const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
3873*4b22b933Srs200217 			#if MDNS_DEBUGMSGS && 0
3874*4b22b933Srs200217 			const char *const msg = next ? "SendQueries:   Nothing more on %p; moving to %p" : "SendQueries:   Nothing more on %p";
3875*4b22b933Srs200217 			debugf(msg, intf, next);
3876*4b22b933Srs200217 			#endif
3877*4b22b933Srs200217 			intf = next;
3878*4b22b933Srs200217 			}
3879*4b22b933Srs200217 		}
3880*4b22b933Srs200217 
3881*4b22b933Srs200217 	// 4. Final housekeeping
3882*4b22b933Srs200217 
3883*4b22b933Srs200217 	// 4a. Debugging check: Make sure we announced all our records
3884*4b22b933Srs200217 	for (ar = m->ResourceRecords; ar; ar=ar->next)
3885*4b22b933Srs200217 		if (ar->SendRNow)
3886*4b22b933Srs200217 			{
3887*4b22b933Srs200217 			if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly)
3888*4b22b933Srs200217 				LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, ar));
3889*4b22b933Srs200217 			ar->SendRNow = mDNSNULL;
3890*4b22b933Srs200217 			}
3891*4b22b933Srs200217 
3892*4b22b933Srs200217 	// 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope
3893*4b22b933Srs200217 	// that their interface which went away might come back again, the logic will want to send queries
3894*4b22b933Srs200217 	// for those records, but we can't because their interface isn't here any more, so to keep the
3895*4b22b933Srs200217 	// state machine ticking over we just pretend we did so.
3896*4b22b933Srs200217 	// If the interface does not come back in time, the cache record will expire naturally
3897*4b22b933Srs200217 	FORALL_CACHERECORDS(slot, cg, cr)
3898*4b22b933Srs200217 		if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries && m->timenow - cr->NextRequiredQuery >= 0)
3899*4b22b933Srs200217 			{
3900*4b22b933Srs200217 			cr->UnansweredQueries++;
3901*4b22b933Srs200217 			cr->CRActiveQuestion->SendQNow = mDNSNULL;
3902*4b22b933Srs200217 			SetNextCacheCheckTime(m, cr);
3903*4b22b933Srs200217 			}
3904*4b22b933Srs200217 
3905*4b22b933Srs200217 	// 4c. Debugging check: Make sure we sent all our planned questions
3906*4b22b933Srs200217 	// Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions
3907*4b22b933Srs200217 	// we legitimately couldn't send because the interface is no longer available
3908*4b22b933Srs200217 	for (q = m->Questions; q; q=q->next)
3909*4b22b933Srs200217 		if (q->SendQNow)
3910*4b22b933Srs200217 			{
3911*4b22b933Srs200217 			LogMsg("SendQueries: No active interface to send: %##s %s", q->qname.c, DNSTypeName(q->qtype));
3912*4b22b933Srs200217 			q->SendQNow = mDNSNULL;
3913*4b22b933Srs200217 			}
3914*4b22b933Srs200217 	}
3915*4b22b933Srs200217 
3916*4b22b933Srs200217 // ***************************************************************************
3917*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
3918*4b22b933Srs200217 #pragma mark -
3919*4b22b933Srs200217 #pragma mark - RR List Management & Task Management
3920*4b22b933Srs200217 #endif
3921*4b22b933Srs200217 
3922*4b22b933Srs200217 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
3923*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
AnswerQuestionWithResourceRecord(mDNS * const m,DNSQuestion * q,CacheRecord * rr,mDNSBool AddRecord)3924*4b22b933Srs200217 mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord)
3925*4b22b933Srs200217 	{
3926*4b22b933Srs200217 	verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
3927*4b22b933Srs200217 		q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3928*4b22b933Srs200217 
3929*4b22b933Srs200217 	// Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue)
3930*4b22b933Srs200217 	// may be called twice, once when the record is received, and again when it's time to notify local clients.
3931*4b22b933Srs200217 	// If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
3932*4b22b933Srs200217 
3933*4b22b933Srs200217 	rr->LastUsed = m->timenow;
3934*4b22b933Srs200217 	if (ActiveQuestion(q) && rr->CRActiveQuestion != q)
3935*4b22b933Srs200217 		{
3936*4b22b933Srs200217 		if (!rr->CRActiveQuestion) m->rrcache_active++;	// If not previously active, increment rrcache_active count
3937*4b22b933Srs200217 		rr->CRActiveQuestion = q;						// We know q is non-null
3938*4b22b933Srs200217 		SetNextCacheCheckTime(m, rr);
3939*4b22b933Srs200217 		}
3940*4b22b933Srs200217 
3941*4b22b933Srs200217 	// If this is:
3942*4b22b933Srs200217 	// (a) a no-cache add, where we've already done at least one 'QM' query, or
3943*4b22b933Srs200217 	// (b) a normal add, where we have at least one unique-type answer,
3944*4b22b933Srs200217 	// then there's no need to keep polling the network.
3945*4b22b933Srs200217 	// (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
3946*4b22b933Srs200217 	if ((AddRecord == 2 && !q->RequestUnicast) ||
3947*4b22b933Srs200217 		(AddRecord == 1 && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))))
3948*4b22b933Srs200217 		if (ActiveQuestion(q))
3949*4b22b933Srs200217 			{
3950*4b22b933Srs200217 			q->LastQTime      = m->timenow;
3951*4b22b933Srs200217 			q->LastQTxTime    = m->timenow;
3952*4b22b933Srs200217 			q->RecentAnswerPkts = 0;
3953*4b22b933Srs200217 			q->ThisQInterval  = MaxQuestionInterval;
3954*4b22b933Srs200217 			q->RequestUnicast = mDNSfalse;
3955*4b22b933Srs200217 			}
3956*4b22b933Srs200217 
3957*4b22b933Srs200217 	if (rr->DelayDelivery) return;		// We'll come back later when CacheRecordDeferredAdd() calls us
3958*4b22b933Srs200217 
3959*4b22b933Srs200217 	m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
3960*4b22b933Srs200217 	if (q->QuestionCallback)
3961*4b22b933Srs200217 		q->QuestionCallback(m, q, &rr->resrec, AddRecord);
3962*4b22b933Srs200217 	m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
3963*4b22b933Srs200217 	// CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function
3964*4b22b933Srs200217 	// is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
3965*4b22b933Srs200217 	// Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv()
3966*4b22b933Srs200217 	// and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions
3967*4b22b933Srs200217 	// being deleted out from under them.
3968*4b22b933Srs200217 	}
3969*4b22b933Srs200217 
CacheRecordDeferredAdd(mDNS * const m,CacheRecord * rr)3970*4b22b933Srs200217 mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
3971*4b22b933Srs200217 	{
3972*4b22b933Srs200217 	rr->DelayDelivery = 0;		// Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
3973*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set");
3974*4b22b933Srs200217 	m->CurrentQuestion = m->Questions;
3975*4b22b933Srs200217 	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3976*4b22b933Srs200217 		{
3977*4b22b933Srs200217 		DNSQuestion *q = m->CurrentQuestion;
3978*4b22b933Srs200217 		m->CurrentQuestion = q->next;
3979*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3980*4b22b933Srs200217 			AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3981*4b22b933Srs200217 		}
3982*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
3983*4b22b933Srs200217 	}
3984*4b22b933Srs200217 
CheckForSoonToExpireRecords(mDNS * const m,const domainname * const name,const mDNSu32 namehash,const mDNSu32 slot)3985*4b22b933Srs200217 mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot)
3986*4b22b933Srs200217 	{
3987*4b22b933Srs200217 	const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond;	// See if there are any records expiring within one second
3988*4b22b933Srs200217 	const mDNSs32 start      = m->timenow - 0x10000000;
3989*4b22b933Srs200217 	mDNSs32 delay = start;
3990*4b22b933Srs200217 	CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
3991*4b22b933Srs200217 	CacheRecord *rr;
3992*4b22b933Srs200217 	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
3993*4b22b933Srs200217 		if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name))
3994*4b22b933Srs200217 			if (threshhold - RRExpireTime(rr) >= 0)		// If we have records about to expire within a second
3995*4b22b933Srs200217 				if (delay - RRExpireTime(rr) < 0)		// then delay until after they've been deleted
3996*4b22b933Srs200217 					delay = RRExpireTime(rr);
3997*4b22b933Srs200217 	if (delay - start > 0) return(NonZeroTime(delay));
3998*4b22b933Srs200217 	else return(0);
3999*4b22b933Srs200217 	}
4000*4b22b933Srs200217 
4001*4b22b933Srs200217 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
4002*4b22b933Srs200217 // If new questions are created as a result of invoking client callbacks, they will be added to
4003*4b22b933Srs200217 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
4004*4b22b933Srs200217 // rr is a new CacheRecord just received into our cache
4005*4b22b933Srs200217 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
4006*4b22b933Srs200217 // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback,
4007*4b22b933Srs200217 // which may change the record list and/or question list.
4008*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
CacheRecordAdd(mDNS * const m,CacheRecord * rr)4009*4b22b933Srs200217 mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
4010*4b22b933Srs200217 	{
4011*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
4012*4b22b933Srs200217 	m->CurrentQuestion = m->Questions;
4013*4b22b933Srs200217 	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
4014*4b22b933Srs200217 		{
4015*4b22b933Srs200217 		DNSQuestion *q = m->CurrentQuestion;
4016*4b22b933Srs200217 		m->CurrentQuestion = q->next;
4017*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4018*4b22b933Srs200217 			{
4019*4b22b933Srs200217 			// If this question is one that's actively sending queries, and it's received ten answers within one
4020*4b22b933Srs200217 			// second of sending the last query packet, then that indicates some radical network topology change,
4021*4b22b933Srs200217 			// so reset its exponential backoff back to the start. We must be at least at the eight-second interval
4022*4b22b933Srs200217 			// to do this. If we're at the four-second interval, or less, there's not much benefit accelerating
4023*4b22b933Srs200217 			// because we will anyway send another query within a few seconds. The first reset query is sent out
4024*4b22b933Srs200217 			// randomized over the next four seconds to reduce possible synchronization between machines.
4025*4b22b933Srs200217 			if (q->LastAnswerPktNum != m->PktNum)
4026*4b22b933Srs200217 				{
4027*4b22b933Srs200217 				q->LastAnswerPktNum = m->PktNum;
4028*4b22b933Srs200217 				if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
4029*4b22b933Srs200217 					q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
4030*4b22b933Srs200217 					{
4031*4b22b933Srs200217 					LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
4032*4b22b933Srs200217 						q->qname.c, DNSTypeName(q->qtype));
4033*4b22b933Srs200217 					q->LastQTime      = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4);
4034*4b22b933Srs200217 					q->ThisQInterval  = InitialQuestionInterval;
4035*4b22b933Srs200217 					SetNextQueryTime(m,q);
4036*4b22b933Srs200217 					}
4037*4b22b933Srs200217 				}
4038*4b22b933Srs200217 			verbosedebugf("CacheRecordAdd %p %##s (%s) %lu",
4039*4b22b933Srs200217 				rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl);
4040*4b22b933Srs200217 			q->CurrentAnswers++;
4041*4b22b933Srs200217 			if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
4042*4b22b933Srs200217 			if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
4043*4b22b933Srs200217 			if (q->CurrentAnswers > 4000)
4044*4b22b933Srs200217 				{
4045*4b22b933Srs200217 				static int msgcount = 0;
4046*4b22b933Srs200217 				if (msgcount++ < 10)
4047*4b22b933Srs200217 					LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
4048*4b22b933Srs200217 						q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers);
4049*4b22b933Srs200217 				rr->resrec.rroriginalttl = 1;
4050*4b22b933Srs200217 				rr->UnansweredQueries = MaxUnansweredQueries;
4051*4b22b933Srs200217 				}
4052*4b22b933Srs200217 			AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
4053*4b22b933Srs200217 			// MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4054*4b22b933Srs200217 			}
4055*4b22b933Srs200217 		}
4056*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
4057*4b22b933Srs200217 	SetNextCacheCheckTime(m, rr);
4058*4b22b933Srs200217 	}
4059*4b22b933Srs200217 
4060*4b22b933Srs200217 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
4061*4b22b933Srs200217 // If new questions are created as a result of invoking client callbacks, they will be added to
4062*4b22b933Srs200217 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
4063*4b22b933Srs200217 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
4064*4b22b933Srs200217 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
4065*4b22b933Srs200217 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
4066*4b22b933Srs200217 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
4067*4b22b933Srs200217 // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback,
4068*4b22b933Srs200217 // which may change the record list and/or question list.
4069*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
NoCacheAnswer(mDNS * const m,CacheRecord * rr)4070*4b22b933Srs200217 mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr)
4071*4b22b933Srs200217 	{
4072*4b22b933Srs200217 	LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c);
4073*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set");
4074*4b22b933Srs200217 	m->CurrentQuestion = m->Questions;
4075*4b22b933Srs200217 	while (m->CurrentQuestion)
4076*4b22b933Srs200217 		{
4077*4b22b933Srs200217 		DNSQuestion *q = m->CurrentQuestion;
4078*4b22b933Srs200217 		m->CurrentQuestion = q->next;
4079*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4080*4b22b933Srs200217 			AnswerQuestionWithResourceRecord(m, q, rr, 2);	// Value '2' indicates "don't expect 'remove' events for this"
4081*4b22b933Srs200217 		// MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4082*4b22b933Srs200217 		}
4083*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
4084*4b22b933Srs200217 	}
4085*4b22b933Srs200217 
4086*4b22b933Srs200217 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
4087*4b22b933Srs200217 // If new questions are created as a result of invoking client callbacks, they will be added to
4088*4b22b933Srs200217 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
4089*4b22b933Srs200217 // rr is an existing cache CacheRecord that just expired and is being deleted
4090*4b22b933Srs200217 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
4091*4b22b933Srs200217 // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback,
4092*4b22b933Srs200217 // which may change the record list and/or question list.
4093*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
CacheRecordRmv(mDNS * const m,CacheRecord * rr)4094*4b22b933Srs200217 mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
4095*4b22b933Srs200217 	{
4096*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set");
4097*4b22b933Srs200217 	m->CurrentQuestion = m->Questions;
4098*4b22b933Srs200217 	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
4099*4b22b933Srs200217 		{
4100*4b22b933Srs200217 		DNSQuestion *q = m->CurrentQuestion;
4101*4b22b933Srs200217 		m->CurrentQuestion = q->next;
4102*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4103*4b22b933Srs200217 			{
4104*4b22b933Srs200217 			verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr));
4105*4b22b933Srs200217 			if (q->CurrentAnswers == 0)
4106*4b22b933Srs200217 				LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?",
4107*4b22b933Srs200217 					q, q->qname.c, DNSTypeName(q->qtype));
4108*4b22b933Srs200217 			else
4109*4b22b933Srs200217 				{
4110*4b22b933Srs200217 				q->CurrentAnswers--;
4111*4b22b933Srs200217 				if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
4112*4b22b933Srs200217 				if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
4113*4b22b933Srs200217 				}
4114*4b22b933Srs200217 			if (q->CurrentAnswers == 0)
4115*4b22b933Srs200217 				{
4116*4b22b933Srs200217 				debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents",
4117*4b22b933Srs200217 					q->qname.c, DNSTypeName(q->qtype));
4118*4b22b933Srs200217 				ReconfirmAntecedents(m, q);
4119*4b22b933Srs200217 				}
4120*4b22b933Srs200217 			q->FlappingInterface = mDNSNULL;
4121*4b22b933Srs200217 			AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
4122*4b22b933Srs200217 			// MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4123*4b22b933Srs200217 			}
4124*4b22b933Srs200217 		}
4125*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
4126*4b22b933Srs200217 	}
4127*4b22b933Srs200217 
ReleaseCacheEntity(mDNS * const m,CacheEntity * e)4128*4b22b933Srs200217 mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
4129*4b22b933Srs200217 	{
4130*4b22b933Srs200217 #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
4131*4b22b933Srs200217 	unsigned int i;
4132*4b22b933Srs200217 	for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
4133*4b22b933Srs200217 #endif
4134*4b22b933Srs200217 	e->next = m->rrcache_free;
4135*4b22b933Srs200217 	m->rrcache_free = e;
4136*4b22b933Srs200217 	m->rrcache_totalused--;
4137*4b22b933Srs200217 	}
4138*4b22b933Srs200217 
ReleaseCacheGroup(mDNS * const m,CacheGroup ** cp)4139*4b22b933Srs200217 mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp)
4140*4b22b933Srs200217 	{
4141*4b22b933Srs200217 	CacheEntity *e = (CacheEntity *)(*cp);
4142*4b22b933Srs200217 	//LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
4143*4b22b933Srs200217 	if ((*cp)->rrcache_tail != &(*cp)->members)
4144*4b22b933Srs200217 		LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
4145*4b22b933Srs200217 	//if ((*cp)->name != (domainname*)((*cp)->namestorage))
4146*4b22b933Srs200217 	//	LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
4147*4b22b933Srs200217 	if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
4148*4b22b933Srs200217 	(*cp)->name = mDNSNULL;
4149*4b22b933Srs200217 	*cp = (*cp)->next;			// Cut record from list
4150*4b22b933Srs200217 	ReleaseCacheEntity(m, e);
4151*4b22b933Srs200217 	}
4152*4b22b933Srs200217 
ReleaseCacheRecord(mDNS * const m,CacheRecord * r)4153*4b22b933Srs200217 mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
4154*4b22b933Srs200217 	{
4155*4b22b933Srs200217 	if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) mDNSPlatformMemFree(r->resrec.rdata);
4156*4b22b933Srs200217 	r->resrec.rdata = mDNSNULL;
4157*4b22b933Srs200217 	ReleaseCacheEntity(m, (CacheEntity *)r);
4158*4b22b933Srs200217 	}
4159*4b22b933Srs200217 
4160*4b22b933Srs200217 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering
4161*4b22b933Srs200217 // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all
4162*4b22b933Srs200217 // callbacks for old records are delivered before callbacks for newer records.
CheckCacheExpiration(mDNS * const m,CacheGroup * cg)4163*4b22b933Srs200217 mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg)
4164*4b22b933Srs200217 	{
4165*4b22b933Srs200217 	CacheRecord **rp = &cg->members;
4166*4b22b933Srs200217 
4167*4b22b933Srs200217 	if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4168*4b22b933Srs200217 	m->lock_rrcache = 1;
4169*4b22b933Srs200217 
4170*4b22b933Srs200217 	while (*rp)
4171*4b22b933Srs200217 		{
4172*4b22b933Srs200217 		CacheRecord *const rr = *rp;
4173*4b22b933Srs200217 		mDNSs32 event = RRExpireTime(rr);
4174*4b22b933Srs200217 		if (m->timenow - event >= 0)	// If expired, delete it
4175*4b22b933Srs200217 			{
4176*4b22b933Srs200217 			*rp = rr->next;				// Cut it from the list
4177*4b22b933Srs200217 			verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr));
4178*4b22b933Srs200217 			if (rr->CRActiveQuestion)	// If this record has one or more active questions, tell them it's going away
4179*4b22b933Srs200217 				{
4180*4b22b933Srs200217 				CacheRecordRmv(m, rr);
4181*4b22b933Srs200217 				m->rrcache_active--;
4182*4b22b933Srs200217 				}
4183*4b22b933Srs200217 			ReleaseCacheRecord(m, rr);
4184*4b22b933Srs200217 			}
4185*4b22b933Srs200217 		else							// else, not expired; see if we need to query
4186*4b22b933Srs200217 			{
4187*4b22b933Srs200217 			if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
4188*4b22b933Srs200217 				event = rr->DelayDelivery;
4189*4b22b933Srs200217 			else
4190*4b22b933Srs200217 				{
4191*4b22b933Srs200217 				if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr);
4192*4b22b933Srs200217 				if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
4193*4b22b933Srs200217 					{
4194*4b22b933Srs200217 					if (m->timenow - rr->NextRequiredQuery < 0)		// If not yet time for next query
4195*4b22b933Srs200217 						event = rr->NextRequiredQuery;				// then just record when we want the next query
4196*4b22b933Srs200217 					else											// else trigger our question to go out now
4197*4b22b933Srs200217 						{
4198*4b22b933Srs200217 						// Set NextScheduledQuery to timenow so that SendQueries() will run.
4199*4b22b933Srs200217 						// SendQueries() will see that we have records close to expiration, and send FEQs for them.
4200*4b22b933Srs200217 						m->NextScheduledQuery = m->timenow;
4201*4b22b933Srs200217 						// After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
4202*4b22b933Srs200217 						// which will correctly update m->NextCacheCheck for us.
4203*4b22b933Srs200217 						event = m->timenow + 0x3FFFFFFF;
4204*4b22b933Srs200217 						}
4205*4b22b933Srs200217 					}
4206*4b22b933Srs200217 				}
4207*4b22b933Srs200217 			verbosedebugf("CheckCacheExpiration:%6d %5d %s",
4208*4b22b933Srs200217 				(event-m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr));
4209*4b22b933Srs200217 			if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0)
4210*4b22b933Srs200217 				m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
4211*4b22b933Srs200217 			rp = &rr->next;
4212*4b22b933Srs200217 			}
4213*4b22b933Srs200217 		}
4214*4b22b933Srs200217 	if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp);
4215*4b22b933Srs200217 	cg->rrcache_tail = rp;
4216*4b22b933Srs200217 	m->lock_rrcache = 0;
4217*4b22b933Srs200217 	}
4218*4b22b933Srs200217 
AnswerNewQuestion(mDNS * const m)4219*4b22b933Srs200217 mDNSlocal void AnswerNewQuestion(mDNS *const m)
4220*4b22b933Srs200217 	{
4221*4b22b933Srs200217 	mDNSBool ShouldQueryImmediately = mDNStrue;
4222*4b22b933Srs200217 	CacheRecord *rr;
4223*4b22b933Srs200217 	DNSQuestion *q = m->NewQuestions;		// Grab the question we're going to answer
4224*4b22b933Srs200217 	const mDNSu32 slot = HashSlot(&q->qname);
4225*4b22b933Srs200217 	CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
4226*4b22b933Srs200217 
4227*4b22b933Srs200217 	verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4228*4b22b933Srs200217 
4229*4b22b933Srs200217 	if (cg) CheckCacheExpiration(m, cg);
4230*4b22b933Srs200217 	m->NewQuestions = q->next;				// Advance NewQuestions to the next *after* calling CheckCacheExpiration();
4231*4b22b933Srs200217 
4232*4b22b933Srs200217 	if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
4233*4b22b933Srs200217 	// This should be safe, because calling the client's question callback may cause the
4234*4b22b933Srs200217 	// question list to be modified, but should not ever cause the rrcache list to be modified.
4235*4b22b933Srs200217 	// If the client's question callback deletes the question, then m->CurrentQuestion will
4236*4b22b933Srs200217 	// be advanced, and we'll exit out of the loop
4237*4b22b933Srs200217 	m->lock_rrcache = 1;
4238*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
4239*4b22b933Srs200217 	m->CurrentQuestion = q;		// Indicate which question we're answering, so we'll know if it gets deleted
4240*4b22b933Srs200217 
4241*4b22b933Srs200217 	if (q->InterfaceID == mDNSInterface_Any)	// If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
4242*4b22b933Srs200217 		{
4243*4b22b933Srs200217 		if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4244*4b22b933Srs200217 		m->CurrentRecord = m->ResourceRecords;
4245*4b22b933Srs200217 		while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
4246*4b22b933Srs200217 			{
4247*4b22b933Srs200217 			AuthRecord *rr = m->CurrentRecord;
4248*4b22b933Srs200217 			m->CurrentRecord = rr->next;
4249*4b22b933Srs200217 			if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
4250*4b22b933Srs200217 				if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4251*4b22b933Srs200217 					{
4252*4b22b933Srs200217 					AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
4253*4b22b933Srs200217 					// MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4254*4b22b933Srs200217 					if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
4255*4b22b933Srs200217 					}
4256*4b22b933Srs200217 			}
4257*4b22b933Srs200217 		m->CurrentRecord   = mDNSNULL;
4258*4b22b933Srs200217 		}
4259*4b22b933Srs200217 
4260*4b22b933Srs200217 	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4261*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4262*4b22b933Srs200217 			{
4263*4b22b933Srs200217 			// SecsSinceRcvd is whole number of elapsed seconds, rounded down
4264*4b22b933Srs200217 			mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
4265*4b22b933Srs200217 			if (rr->resrec.rroriginalttl <= SecsSinceRcvd)
4266*4b22b933Srs200217 				{
4267*4b22b933Srs200217 				LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)",
4268*4b22b933Srs200217 					rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
4269*4b22b933Srs200217 				continue;	// Go to next one in loop
4270*4b22b933Srs200217 				}
4271*4b22b933Srs200217 
4272*4b22b933Srs200217 			// If this record set is marked unique, then that means we can reasonably assume we have the whole set
4273*4b22b933Srs200217 			// -- we don't need to rush out on the network and query immediately to see if there are more answers out there
4274*4b22b933Srs200217 			if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique))
4275*4b22b933Srs200217 				ShouldQueryImmediately = mDNSfalse;
4276*4b22b933Srs200217 			q->CurrentAnswers++;
4277*4b22b933Srs200217 			if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
4278*4b22b933Srs200217 			if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
4279*4b22b933Srs200217 			AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
4280*4b22b933Srs200217 			// MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4281*4b22b933Srs200217 			if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
4282*4b22b933Srs200217 			}
4283*4b22b933Srs200217 		else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype))
4284*4b22b933Srs200217 			if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname))
4285*4b22b933Srs200217 				ShouldQueryImmediately = mDNSfalse;
4286*4b22b933Srs200217 
4287*4b22b933Srs200217 	if (ShouldQueryImmediately && m->CurrentQuestion == q)
4288*4b22b933Srs200217 		{
4289*4b22b933Srs200217 		q->ThisQInterval  = InitialQuestionInterval;
4290*4b22b933Srs200217 		q->LastQTime      = m->timenow - q->ThisQInterval;
4291*4b22b933Srs200217 		m->NextScheduledQuery = m->timenow;
4292*4b22b933Srs200217 		}
4293*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
4294*4b22b933Srs200217 	m->lock_rrcache = 0;
4295*4b22b933Srs200217 	}
4296*4b22b933Srs200217 
4297*4b22b933Srs200217 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any
4298*4b22b933Srs200217 // appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
AnswerNewLocalOnlyQuestion(mDNS * const m)4299*4b22b933Srs200217 mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
4300*4b22b933Srs200217 	{
4301*4b22b933Srs200217 	DNSQuestion *q = m->NewLocalOnlyQuestions;		// Grab the question we're going to answer
4302*4b22b933Srs200217 	m->NewLocalOnlyQuestions = q->next;				// Advance NewQuestions to the next (if any)
4303*4b22b933Srs200217 
4304*4b22b933Srs200217 	debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4305*4b22b933Srs200217 
4306*4b22b933Srs200217 	if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
4307*4b22b933Srs200217 	m->CurrentQuestion = q;		// Indicate which question we're answering, so we'll know if it gets deleted
4308*4b22b933Srs200217 
4309*4b22b933Srs200217 	if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4310*4b22b933Srs200217 	m->CurrentRecord = m->ResourceRecords;
4311*4b22b933Srs200217 	while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
4312*4b22b933Srs200217 		{
4313*4b22b933Srs200217 		AuthRecord *rr = m->CurrentRecord;
4314*4b22b933Srs200217 		m->CurrentRecord = rr->next;
4315*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4316*4b22b933Srs200217 			{
4317*4b22b933Srs200217 			AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
4318*4b22b933Srs200217 			// MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4319*4b22b933Srs200217 			if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
4320*4b22b933Srs200217 			}
4321*4b22b933Srs200217 		}
4322*4b22b933Srs200217 
4323*4b22b933Srs200217 	m->CurrentQuestion = mDNSNULL;
4324*4b22b933Srs200217 	m->CurrentRecord   = mDNSNULL;
4325*4b22b933Srs200217 	}
4326*4b22b933Srs200217 
GetCacheEntity(mDNS * const m,const CacheGroup * const PreserveCG)4327*4b22b933Srs200217 mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG)
4328*4b22b933Srs200217 	{
4329*4b22b933Srs200217 	CacheEntity *e = mDNSNULL;
4330*4b22b933Srs200217 
4331*4b22b933Srs200217 	if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
4332*4b22b933Srs200217 	m->lock_rrcache = 1;
4333*4b22b933Srs200217 
4334*4b22b933Srs200217 	// If we have no free records, ask the client layer to give us some more memory
4335*4b22b933Srs200217 	if (!m->rrcache_free && m->MainCallback)
4336*4b22b933Srs200217 		{
4337*4b22b933Srs200217 		if (m->rrcache_totalused != m->rrcache_size)
4338*4b22b933Srs200217 			LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
4339*4b22b933Srs200217 				m->rrcache_totalused, m->rrcache_size);
4340*4b22b933Srs200217 
4341*4b22b933Srs200217 		// We don't want to be vulnerable to a malicious attacker flooding us with an infinite
4342*4b22b933Srs200217 		// number of bogus records so that we keep growing our cache until the machine runs out of memory.
4343*4b22b933Srs200217 		// To guard against this, if we're actively using less than 1/32 of our cache, then we
4344*4b22b933Srs200217 		// purge all the unused records and recycle them, instead of allocating more memory.
4345*4b22b933Srs200217 		if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active)
4346*4b22b933Srs200217 			debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
4347*4b22b933Srs200217 				m->rrcache_size, m->rrcache_active);
4348*4b22b933Srs200217 		else
4349*4b22b933Srs200217 			{
4350*4b22b933Srs200217 			m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
4351*4b22b933Srs200217 			m->MainCallback(m, mStatus_GrowCache);
4352*4b22b933Srs200217 			m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
4353*4b22b933Srs200217 			}
4354*4b22b933Srs200217 		}
4355*4b22b933Srs200217 
4356*4b22b933Srs200217 	// If we still have no free records, recycle all the records we can.
4357*4b22b933Srs200217 	// Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
4358*4b22b933Srs200217 	if (!m->rrcache_free)
4359*4b22b933Srs200217 		{
4360*4b22b933Srs200217 		#if MDNS_DEBUGMSGS
4361*4b22b933Srs200217 		mDNSu32 oldtotalused = m->rrcache_totalused;
4362*4b22b933Srs200217 		#endif
4363*4b22b933Srs200217 		mDNSu32 slot;
4364*4b22b933Srs200217 		for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4365*4b22b933Srs200217 			{
4366*4b22b933Srs200217 			CacheGroup **cp = &m->rrcache_hash[slot];
4367*4b22b933Srs200217 			while (*cp)
4368*4b22b933Srs200217 				{
4369*4b22b933Srs200217 				CacheRecord **rp = &(*cp)->members;
4370*4b22b933Srs200217 				while (*rp)
4371*4b22b933Srs200217 					{
4372*4b22b933Srs200217 					// Records that answer still-active questions are not candidates for recycling
4373*4b22b933Srs200217 					// Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
4374*4b22b933Srs200217 					if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList)
4375*4b22b933Srs200217 						rp=&(*rp)->next;
4376*4b22b933Srs200217 					else
4377*4b22b933Srs200217 						{
4378*4b22b933Srs200217 						CacheRecord *rr = *rp;
4379*4b22b933Srs200217 						*rp = (*rp)->next;			// Cut record from list
4380*4b22b933Srs200217 						ReleaseCacheRecord(m, rr);
4381*4b22b933Srs200217 						}
4382*4b22b933Srs200217 					}
4383*4b22b933Srs200217 				if ((*cp)->rrcache_tail != rp)
4384*4b22b933Srs200217 					verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp);
4385*4b22b933Srs200217 				(*cp)->rrcache_tail = rp;
4386*4b22b933Srs200217 				if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next;
4387*4b22b933Srs200217 				else ReleaseCacheGroup(m, cp);
4388*4b22b933Srs200217 				}
4389*4b22b933Srs200217 			}
4390*4b22b933Srs200217 		#if MDNS_DEBUGMSGS
4391*4b22b933Srs200217 		debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
4392*4b22b933Srs200217 		#endif
4393*4b22b933Srs200217 		}
4394*4b22b933Srs200217 
4395*4b22b933Srs200217 	if (m->rrcache_free)	// If there are records in the free list, take one
4396*4b22b933Srs200217 		{
4397*4b22b933Srs200217 		e = m->rrcache_free;
4398*4b22b933Srs200217 		m->rrcache_free = e->next;
4399*4b22b933Srs200217 		if (++m->rrcache_totalused >= m->rrcache_report)
4400*4b22b933Srs200217 			{
4401*4b22b933Srs200217 			debugf("RR Cache now using %ld objects", m->rrcache_totalused);
4402*4b22b933Srs200217 			if (m->rrcache_report < 100) m->rrcache_report += 10;
4403*4b22b933Srs200217 			else                         m->rrcache_report += 100;
4404*4b22b933Srs200217 			}
4405*4b22b933Srs200217 		mDNSPlatformMemZero(e, sizeof(*e));
4406*4b22b933Srs200217 		}
4407*4b22b933Srs200217 
4408*4b22b933Srs200217 	m->lock_rrcache = 0;
4409*4b22b933Srs200217 
4410*4b22b933Srs200217 	return(e);
4411*4b22b933Srs200217 	}
4412*4b22b933Srs200217 
GetCacheRecord(mDNS * const m,CacheGroup * cg,mDNSu16 RDLength)4413*4b22b933Srs200217 mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength)
4414*4b22b933Srs200217 	{
4415*4b22b933Srs200217 	CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg);
4416*4b22b933Srs200217 	if (r)
4417*4b22b933Srs200217 		{
4418*4b22b933Srs200217 		r->resrec.rdata = (RData*)&r->rdatastorage;	// By default, assume we're usually going to be using local storage
4419*4b22b933Srs200217 		if (RDLength > InlineCacheRDSize)			// If RDLength is too big, allocate extra storage
4420*4b22b933Srs200217 			{
4421*4b22b933Srs200217 			r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength);
4422*4b22b933Srs200217 			if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength;
4423*4b22b933Srs200217 			else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; }
4424*4b22b933Srs200217 			}
4425*4b22b933Srs200217 		}
4426*4b22b933Srs200217 	return(r);
4427*4b22b933Srs200217 	}
4428*4b22b933Srs200217 
GetCacheGroup(mDNS * const m,const mDNSu32 slot,const ResourceRecord * const rr)4429*4b22b933Srs200217 mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
4430*4b22b933Srs200217 	{
4431*4b22b933Srs200217 	mDNSu16 namelen = DomainNameLength(rr->name);
4432*4b22b933Srs200217 	CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL);
4433*4b22b933Srs200217 	if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
4434*4b22b933Srs200217 	cg->next         = m->rrcache_hash[slot];
4435*4b22b933Srs200217 	cg->namehash     = rr->namehash;
4436*4b22b933Srs200217 	cg->members      = mDNSNULL;
4437*4b22b933Srs200217 	cg->rrcache_tail = &cg->members;
4438*4b22b933Srs200217 	cg->name         = (domainname*)cg->namestorage;
4439*4b22b933Srs200217 	//LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s",
4440*4b22b933Srs200217 	//	(namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
4441*4b22b933Srs200217 	if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen);
4442*4b22b933Srs200217 	if (!cg->name)
4443*4b22b933Srs200217 		{
4444*4b22b933Srs200217 		LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c);
4445*4b22b933Srs200217 		ReleaseCacheEntity(m, (CacheEntity*)cg);
4446*4b22b933Srs200217 		return(mDNSNULL);
4447*4b22b933Srs200217 		}
4448*4b22b933Srs200217 	AssignDomainName(cg->name, rr->name);
4449*4b22b933Srs200217 
4450*4b22b933Srs200217 	if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c);
4451*4b22b933Srs200217 	m->rrcache_hash[slot] = cg;
4452*4b22b933Srs200217 	if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c);
4453*4b22b933Srs200217 
4454*4b22b933Srs200217 	return(cg);
4455*4b22b933Srs200217 	}
4456*4b22b933Srs200217 
PurgeCacheResourceRecord(mDNS * const m,CacheRecord * rr)4457*4b22b933Srs200217 mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
4458*4b22b933Srs200217 	{
4459*4b22b933Srs200217 	// Make sure we mark this record as thoroughly expired -- we don't ever want to give
4460*4b22b933Srs200217 	// a positive answer using an expired record (e.g. from an interface that has gone away).
4461*4b22b933Srs200217 	// We don't want to clear CRActiveQuestion here, because that would leave the record subject to
4462*4b22b933Srs200217 	// summary deletion without giving the proper callback to any questions that are monitoring it.
4463*4b22b933Srs200217 	// By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
4464*4b22b933Srs200217 	rr->TimeRcvd          = m->timenow - mDNSPlatformOneSecond * 60;
4465*4b22b933Srs200217 	rr->UnansweredQueries = MaxUnansweredQueries;
4466*4b22b933Srs200217 	rr->resrec.rroriginalttl     = 0;
4467*4b22b933Srs200217 	SetNextCacheCheckTime(m, rr);
4468*4b22b933Srs200217 	}
4469*4b22b933Srs200217 
mDNS_TimeNow(const mDNS * const m)4470*4b22b933Srs200217 mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m)
4471*4b22b933Srs200217 	{
4472*4b22b933Srs200217 	mDNSs32 time;
4473*4b22b933Srs200217 	mDNSPlatformLock(m);
4474*4b22b933Srs200217 	if (m->mDNS_busy)
4475*4b22b933Srs200217 		{
4476*4b22b933Srs200217 		LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
4477*4b22b933Srs200217 		if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
4478*4b22b933Srs200217 		}
4479*4b22b933Srs200217 
4480*4b22b933Srs200217 	if (m->timenow) time = m->timenow;
4481*4b22b933Srs200217 	else            time = mDNS_TimeNow_NoLock(m);
4482*4b22b933Srs200217 	mDNSPlatformUnlock(m);
4483*4b22b933Srs200217 	return(time);
4484*4b22b933Srs200217 	}
4485*4b22b933Srs200217 
mDNS_Execute(mDNS * const m)4486*4b22b933Srs200217 mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
4487*4b22b933Srs200217 	{
4488*4b22b933Srs200217 	mDNS_Lock(m);	// Must grab lock before trying to read m->timenow
4489*4b22b933Srs200217 
4490*4b22b933Srs200217 	if (m->timenow - m->NextScheduledEvent >= 0)
4491*4b22b933Srs200217 		{
4492*4b22b933Srs200217 		int i;
4493*4b22b933Srs200217 
4494*4b22b933Srs200217 		verbosedebugf("mDNS_Execute");
4495*4b22b933Srs200217 		if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
4496*4b22b933Srs200217 
4497*4b22b933Srs200217 		// 1. If we're past the probe suppression time, we can clear it
4498*4b22b933Srs200217 		if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0;
4499*4b22b933Srs200217 
4500*4b22b933Srs200217 		// 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
4501*4b22b933Srs200217 		if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0;
4502*4b22b933Srs200217 
4503*4b22b933Srs200217 		// 3. Purge our cache of stale old records
4504*4b22b933Srs200217 		if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
4505*4b22b933Srs200217 			{
4506*4b22b933Srs200217 			mDNSu32 slot;
4507*4b22b933Srs200217 			m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
4508*4b22b933Srs200217 			for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4509*4b22b933Srs200217 				{
4510*4b22b933Srs200217 				CacheGroup **cp = &m->rrcache_hash[slot];
4511*4b22b933Srs200217 				while (*cp)
4512*4b22b933Srs200217 					{
4513*4b22b933Srs200217 					CheckCacheExpiration(m, *cp);
4514*4b22b933Srs200217 					if ((*cp)->members) cp=&(*cp)->next;
4515*4b22b933Srs200217 					else ReleaseCacheGroup(m, cp);
4516*4b22b933Srs200217 					}
4517*4b22b933Srs200217 				}
4518*4b22b933Srs200217 			LogOperation("Cache checked. Next in %ld ticks", m->NextCacheCheck - m->timenow);
4519*4b22b933Srs200217 			}
4520*4b22b933Srs200217 
4521*4b22b933Srs200217 		// 4. See if we can answer any of our new local questions from the cache
4522*4b22b933Srs200217 		for (i=0; m->NewQuestions && i<1000; i++)
4523*4b22b933Srs200217 			{
4524*4b22b933Srs200217 			if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break;
4525*4b22b933Srs200217 			AnswerNewQuestion(m);
4526*4b22b933Srs200217 			}
4527*4b22b933Srs200217 		if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4528*4b22b933Srs200217 
4529*4b22b933Srs200217 		for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
4530*4b22b933Srs200217 		if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4531*4b22b933Srs200217 
4532*4b22b933Srs200217 		for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++)
4533*4b22b933Srs200217 			{
4534*4b22b933Srs200217 			AuthRecord *rr = m->NewLocalRecords;
4535*4b22b933Srs200217 			m->NewLocalRecords = m->NewLocalRecords->next;
4536*4b22b933Srs200217 			AnswerLocalQuestions(m, rr, mDNStrue);
4537*4b22b933Srs200217 			}
4538*4b22b933Srs200217 		if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
4539*4b22b933Srs200217 
4540*4b22b933Srs200217 		// 5. See what packets we need to send
4541*4b22b933Srs200217 		if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m);
4542*4b22b933Srs200217 		else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)
4543*4b22b933Srs200217 			{
4544*4b22b933Srs200217 			// If the platform code is ready, and we're not suppressing packet generation right now
4545*4b22b933Srs200217 			// then send our responses, probes, and questions.
4546*4b22b933Srs200217 			// We check the cache first, because there might be records close to expiring that trigger questions to refresh them.
4547*4b22b933Srs200217 			// We send queries next, because there might be final-stage probes that complete their probing here, causing
4548*4b22b933Srs200217 			// them to advance to announcing state, and we want those to be included in any announcements we send out.
4549*4b22b933Srs200217 			// Finally, we send responses, including the previously mentioned records that just completed probing.
4550*4b22b933Srs200217 			m->SuppressSending = 0;
4551*4b22b933Srs200217 
4552*4b22b933Srs200217 			// 6. Send Query packets. This may cause some probing records to advance to announcing state
4553*4b22b933Srs200217 			if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m);
4554*4b22b933Srs200217 			if (m->timenow - m->NextScheduledQuery >= 0)
4555*4b22b933Srs200217 				{
4556*4b22b933Srs200217 				LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
4557*4b22b933Srs200217 				m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond;
4558*4b22b933Srs200217 				}
4559*4b22b933Srs200217 			if (m->timenow - m->NextScheduledProbe >= 0)
4560*4b22b933Srs200217 				{
4561*4b22b933Srs200217 				LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
4562*4b22b933Srs200217 				m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond;
4563*4b22b933Srs200217 				}
4564*4b22b933Srs200217 
4565*4b22b933Srs200217 			// 7. Send Response packets, including probing records just advanced to announcing state
4566*4b22b933Srs200217 			if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m);
4567*4b22b933Srs200217 			if (m->timenow - m->NextScheduledResponse >= 0)
4568*4b22b933Srs200217 				{
4569*4b22b933Srs200217 				LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
4570*4b22b933Srs200217 				m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond;
4571*4b22b933Srs200217 				}
4572*4b22b933Srs200217 			}
4573*4b22b933Srs200217 
4574*4b22b933Srs200217 		// Clear RandomDelay values, ready to pick a new different value next time
4575*4b22b933Srs200217 		m->RandomQueryDelay     = 0;
4576*4b22b933Srs200217 		m->RandomReconfirmDelay = 0;
4577*4b22b933Srs200217 		}
4578*4b22b933Srs200217 
4579*4b22b933Srs200217 	// Note about multi-threaded systems:
4580*4b22b933Srs200217 	// On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
4581*4b22b933Srs200217 	// performing mDNS API operations that change our next scheduled event time.
4582*4b22b933Srs200217 	//
4583*4b22b933Srs200217 	// On multi-threaded systems (like the current Windows implementation) that have a single main thread
4584*4b22b933Srs200217 	// calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
4585*4b22b933Srs200217 	// of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
4586*4b22b933Srs200217 	// signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
4587*4b22b933Srs200217 	// more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
4588*4b22b933Srs200217 	// in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
4589*4b22b933Srs200217 	// does, the state of the signal will be noticed, causing the blocking primitive to return immediately
4590*4b22b933Srs200217 	// without blocking. This avoids the race condition between the signal from the other thread arriving
4591*4b22b933Srs200217 	// just *before* or just *after* the main thread enters the blocking primitive.
4592*4b22b933Srs200217 	//
4593*4b22b933Srs200217 	// On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
4594*4b22b933Srs200217 	// with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
4595*4b22b933Srs200217 	// set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
4596*4b22b933Srs200217 	// callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
4597*4b22b933Srs200217 	// by the time it gets to the timer callback function).
4598*4b22b933Srs200217 
4599*4b22b933Srs200217 #ifndef UNICAST_DISABLED
4600*4b22b933Srs200217 	uDNS_Execute(m);
4601*4b22b933Srs200217 #endif
4602*4b22b933Srs200217 	mDNS_Unlock(m);		// Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4603*4b22b933Srs200217 	return(m->NextScheduledEvent);
4604*4b22b933Srs200217 	}
4605*4b22b933Srs200217 
4606*4b22b933Srs200217 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
4607*4b22b933Srs200217 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
4608*4b22b933Srs200217 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
4609*4b22b933Srs200217 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
4610*4b22b933Srs200217 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
4611*4b22b933Srs200217 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
4612*4b22b933Srs200217 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
4613*4b22b933Srs200217 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
4614*4b22b933Srs200217 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
4615*4b22b933Srs200217 // traffic, so it should only be called when there is legitimate reason to believe the machine
4616*4b22b933Srs200217 // may have become attached to a new network.
mDNSCoreMachineSleep(mDNS * const m,mDNSBool sleepstate)4617*4b22b933Srs200217 mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
4618*4b22b933Srs200217 	{
4619*4b22b933Srs200217 	AuthRecord *rr;
4620*4b22b933Srs200217 
4621*4b22b933Srs200217 	mDNS_Lock(m);
4622*4b22b933Srs200217 
4623*4b22b933Srs200217 	m->SleepState = sleepstate;
4624*4b22b933Srs200217 	LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
4625*4b22b933Srs200217 
4626*4b22b933Srs200217 	if (sleepstate)
4627*4b22b933Srs200217 		{
4628*4b22b933Srs200217 #ifndef UNICAST_DISABLED
4629*4b22b933Srs200217 		uDNS_Sleep(m);
4630*4b22b933Srs200217 #endif
4631*4b22b933Srs200217 		// Mark all the records we need to deregister and send them
4632*4b22b933Srs200217 		for (rr = m->ResourceRecords; rr; rr=rr->next)
4633*4b22b933Srs200217 			if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
4634*4b22b933Srs200217 				rr->ImmedAnswer = mDNSInterfaceMark;
4635*4b22b933Srs200217 		SendResponses(m);
4636*4b22b933Srs200217 		}
4637*4b22b933Srs200217 	else
4638*4b22b933Srs200217 		{
4639*4b22b933Srs200217 		DNSQuestion *q;
4640*4b22b933Srs200217 		mDNSu32 slot;
4641*4b22b933Srs200217 		CacheGroup *cg;
4642*4b22b933Srs200217 		CacheRecord *cr;
4643*4b22b933Srs200217 
4644*4b22b933Srs200217 #ifndef UNICAST_DISABLED
4645*4b22b933Srs200217 		uDNS_Wake(m);
4646*4b22b933Srs200217 #endif
4647*4b22b933Srs200217         // 1. Retrigger all our questions
4648*4b22b933Srs200217 		for (q = m->Questions; q; q=q->next)				// Scan our list of questions
4649*4b22b933Srs200217 			if (ActiveQuestion(q))
4650*4b22b933Srs200217 				{
4651*4b22b933Srs200217 				q->ThisQInterval    = InitialQuestionInterval;	// MUST be > zero for an active question
4652*4b22b933Srs200217 				q->RequestUnicast   = 2;						// Set to 2 because is decremented once *before* we check it
4653*4b22b933Srs200217 				q->LastQTime        = m->timenow - q->ThisQInterval;
4654*4b22b933Srs200217 				q->RecentAnswerPkts = 0;
4655*4b22b933Srs200217 				ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
4656*4b22b933Srs200217 				m->NextScheduledQuery = m->timenow;
4657*4b22b933Srs200217 				}
4658*4b22b933Srs200217 
4659*4b22b933Srs200217 		// 2. Re-validate our cache records
4660*4b22b933Srs200217 		m->NextCacheCheck  = m->timenow;
4661*4b22b933Srs200217 		FORALL_CACHERECORDS(slot, cg, cr)
4662*4b22b933Srs200217 			mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake);
4663*4b22b933Srs200217 
4664*4b22b933Srs200217 		// 3. Retrigger probing and announcing for all our authoritative records
4665*4b22b933Srs200217 		for (rr = m->ResourceRecords; rr; rr=rr->next)
4666*4b22b933Srs200217 			{
4667*4b22b933Srs200217 			if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
4668*4b22b933Srs200217 			rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
4669*4b22b933Srs200217 			rr->AnnounceCount  = InitialAnnounceCount;
4670*4b22b933Srs200217 			rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
4671*4b22b933Srs200217 			InitializeLastAPTime(m, rr);
4672*4b22b933Srs200217 			}
4673*4b22b933Srs200217 		}
4674*4b22b933Srs200217 
4675*4b22b933Srs200217 	mDNS_Unlock(m);
4676*4b22b933Srs200217 	}
4677*4b22b933Srs200217 
4678*4b22b933Srs200217 // ***************************************************************************
4679*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
4680*4b22b933Srs200217 #pragma mark -
4681*4b22b933Srs200217 #pragma mark - Packet Reception Functions
4682*4b22b933Srs200217 #endif
4683*4b22b933Srs200217 
4684*4b22b933Srs200217 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4685*4b22b933Srs200217 
GenerateUnicastResponse(const DNSMessage * const query,const mDNSu8 * const end,const mDNSInterfaceID InterfaceID,mDNSBool LegacyQuery,DNSMessage * const response,AuthRecord * ResponseRecords)4686*4b22b933Srs200217 mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
4687*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
4688*4b22b933Srs200217 	{
4689*4b22b933Srs200217 	mDNSu8          *responseptr     = response->data;
4690*4b22b933Srs200217 	const mDNSu8    *const limit     = response->data + sizeof(response->data);
4691*4b22b933Srs200217 	const mDNSu8    *ptr             = query->data;
4692*4b22b933Srs200217 	AuthRecord  *rr;
4693*4b22b933Srs200217 	mDNSu32          maxttl = 0x70000000;
4694*4b22b933Srs200217 	int i;
4695*4b22b933Srs200217 
4696*4b22b933Srs200217 	// Initialize the response fields so we can answer the questions
4697*4b22b933Srs200217 	InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
4698*4b22b933Srs200217 
4699*4b22b933Srs200217 	// ***
4700*4b22b933Srs200217 	// *** 1. Write out the list of questions we are actually going to answer with this packet
4701*4b22b933Srs200217 	// ***
4702*4b22b933Srs200217 	if (LegacyQuery)
4703*4b22b933Srs200217 		{
4704*4b22b933Srs200217 		maxttl = 10;
4705*4b22b933Srs200217 		for (i=0; i<query->h.numQuestions; i++)						// For each question...
4706*4b22b933Srs200217 			{
4707*4b22b933Srs200217 			DNSQuestion q;
4708*4b22b933Srs200217 			ptr = getQuestion(query, ptr, end, InterfaceID, &q);	// get the question...
4709*4b22b933Srs200217 			if (!ptr) return(mDNSNULL);
4710*4b22b933Srs200217 
4711*4b22b933Srs200217 			for (rr=ResponseRecords; rr; rr=rr->NextResponse)		// and search our list of proposed answers
4712*4b22b933Srs200217 				{
4713*4b22b933Srs200217 				if (rr->NR_AnswerTo == ptr)							// If we're going to generate a record answering this question
4714*4b22b933Srs200217 					{												// then put the question in the question section
4715*4b22b933Srs200217 					responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass);
4716*4b22b933Srs200217 					if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); }
4717*4b22b933Srs200217 					break;		// break out of the ResponseRecords loop, and go on to the next question
4718*4b22b933Srs200217 					}
4719*4b22b933Srs200217 				}
4720*4b22b933Srs200217 			}
4721*4b22b933Srs200217 
4722*4b22b933Srs200217 		if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
4723*4b22b933Srs200217 		}
4724*4b22b933Srs200217 
4725*4b22b933Srs200217 	// ***
4726*4b22b933Srs200217 	// *** 2. Write Answers
4727*4b22b933Srs200217 	// ***
4728*4b22b933Srs200217 	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4729*4b22b933Srs200217 		if (rr->NR_AnswerTo)
4730*4b22b933Srs200217 			{
4731*4b22b933Srs200217 			mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl);
4732*4b22b933Srs200217 			if (p) responseptr = p;
4733*4b22b933Srs200217 			else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; }
4734*4b22b933Srs200217 			}
4735*4b22b933Srs200217 
4736*4b22b933Srs200217 	// ***
4737*4b22b933Srs200217 	// *** 3. Write Additionals
4738*4b22b933Srs200217 	// ***
4739*4b22b933Srs200217 	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4740*4b22b933Srs200217 		if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
4741*4b22b933Srs200217 			{
4742*4b22b933Srs200217 			mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl);
4743*4b22b933Srs200217 			if (p) responseptr = p;
4744*4b22b933Srs200217 			else debugf("GenerateUnicastResponse: No more space for additionals");
4745*4b22b933Srs200217 			}
4746*4b22b933Srs200217 
4747*4b22b933Srs200217 	return(responseptr);
4748*4b22b933Srs200217 	}
4749*4b22b933Srs200217 
4750*4b22b933Srs200217 // AuthRecord *our is our Resource Record
4751*4b22b933Srs200217 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
4752*4b22b933Srs200217 // Returns 0 if there is no conflict
4753*4b22b933Srs200217 // Returns +1 if there was a conflict and we won
4754*4b22b933Srs200217 // Returns -1 if there was a conflict and we lost and have to rename
CompareRData(AuthRecord * our,CacheRecord * pkt)4755*4b22b933Srs200217 mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt)
4756*4b22b933Srs200217 	{
4757*4b22b933Srs200217 	mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
4758*4b22b933Srs200217 	mDNSu8 pktdata[256], *pktptr = pktdata, *pktend;
4759*4b22b933Srs200217 	if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
4760*4b22b933Srs200217 	if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
4761*4b22b933Srs200217 
4762*4b22b933Srs200217 	ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec);
4763*4b22b933Srs200217 	pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec);
4764*4b22b933Srs200217 	while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; }
4765*4b22b933Srs200217 	if (ourptr >= ourend && pktptr >= pktend) return(0);			// If data identical, not a conflict
4766*4b22b933Srs200217 
4767*4b22b933Srs200217 	if (ourptr >= ourend) return(-1);								// Our data ran out first; We lost
4768*4b22b933Srs200217 	if (pktptr >= pktend) return(+1);								// Packet data ran out first; We won
4769*4b22b933Srs200217 	if (*pktptr > *ourptr) return(-1);								// Our data is numerically lower; We lost
4770*4b22b933Srs200217 	if (*pktptr < *ourptr) return(+1);								// Packet data is numerically lower; We won
4771*4b22b933Srs200217 
4772*4b22b933Srs200217 	LogMsg("CompareRData ERROR: Invalid state");
4773*4b22b933Srs200217 	return(-1);
4774*4b22b933Srs200217 	}
4775*4b22b933Srs200217 
4776*4b22b933Srs200217 // See if we have an authoritative record that's identical to this packet record,
4777*4b22b933Srs200217 // whose canonical DependentOn record is the specified master record.
4778*4b22b933Srs200217 // The DependentOn pointer is typically used for the TXT record of service registrations
4779*4b22b933Srs200217 // It indicates that there is no inherent conflict detection for the TXT record
4780*4b22b933Srs200217 // -- it depends on the SRV record to resolve name conflicts
4781*4b22b933Srs200217 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
4782*4b22b933Srs200217 // pointer chain (if any) to make sure we reach the canonical DependentOn record
4783*4b22b933Srs200217 // If the record has no DependentOn, then just return that record's pointer
4784*4b22b933Srs200217 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
MatchDependentOn(const mDNS * const m,const CacheRecord * const pktrr,const AuthRecord * const master)4785*4b22b933Srs200217 mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master)
4786*4b22b933Srs200217 	{
4787*4b22b933Srs200217 	const AuthRecord *r1;
4788*4b22b933Srs200217 	for (r1 = m->ResourceRecords; r1; r1=r1->next)
4789*4b22b933Srs200217 		{
4790*4b22b933Srs200217 		if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4791*4b22b933Srs200217 			{
4792*4b22b933Srs200217 			const AuthRecord *r2 = r1;
4793*4b22b933Srs200217 			while (r2->DependentOn) r2 = r2->DependentOn;
4794*4b22b933Srs200217 			if (r2 == master) return(mDNStrue);
4795*4b22b933Srs200217 			}
4796*4b22b933Srs200217 		}
4797*4b22b933Srs200217 	for (r1 = m->DuplicateRecords; r1; r1=r1->next)
4798*4b22b933Srs200217 		{
4799*4b22b933Srs200217 		if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4800*4b22b933Srs200217 			{
4801*4b22b933Srs200217 			const AuthRecord *r2 = r1;
4802*4b22b933Srs200217 			while (r2->DependentOn) r2 = r2->DependentOn;
4803*4b22b933Srs200217 			if (r2 == master) return(mDNStrue);
4804*4b22b933Srs200217 			}
4805*4b22b933Srs200217 		}
4806*4b22b933Srs200217 	return(mDNSfalse);
4807*4b22b933Srs200217 	}
4808*4b22b933Srs200217 
4809*4b22b933Srs200217 // Find the canonical RRSet pointer for this RR received in a packet.
4810*4b22b933Srs200217 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
4811*4b22b933Srs200217 // pointers (if any) to make sure we return the canonical member of this name/type/class
4812*4b22b933Srs200217 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
FindRRSet(const mDNS * const m,const CacheRecord * const pktrr)4813*4b22b933Srs200217 mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr)
4814*4b22b933Srs200217 	{
4815*4b22b933Srs200217 	const AuthRecord *rr;
4816*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)
4817*4b22b933Srs200217 		{
4818*4b22b933Srs200217 		if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
4819*4b22b933Srs200217 			{
4820*4b22b933Srs200217 			while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
4821*4b22b933Srs200217 			return(rr);
4822*4b22b933Srs200217 			}
4823*4b22b933Srs200217 		}
4824*4b22b933Srs200217 	return(mDNSNULL);
4825*4b22b933Srs200217 	}
4826*4b22b933Srs200217 
4827*4b22b933Srs200217 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
4828*4b22b933Srs200217 // as one of our records (our) but different rdata.
4829*4b22b933Srs200217 // 1. If our record is not a type that's supposed to be unique, we don't care.
4830*4b22b933Srs200217 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
4831*4b22b933Srs200217 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
4832*4b22b933Srs200217 //     points to our record, ignore this conflict (e.g. the packet record matches one of our
4833*4b22b933Srs200217 //     TXT records, and that record is marked as dependent on 'our', its SRV record).
4834*4b22b933Srs200217 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
4835*4b22b933Srs200217 //    are members of the same RRSet, then this is not a conflict.
PacketRRConflict(const mDNS * const m,const AuthRecord * const our,const CacheRecord * const pktrr)4836*4b22b933Srs200217 mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr)
4837*4b22b933Srs200217 	{
4838*4b22b933Srs200217 	const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
4839*4b22b933Srs200217 
4840*4b22b933Srs200217 	// If not supposed to be unique, not a conflict
4841*4b22b933Srs200217 	if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
4842*4b22b933Srs200217 
4843*4b22b933Srs200217 	// If a dependent record, not a conflict
4844*4b22b933Srs200217 	if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
4845*4b22b933Srs200217 
4846*4b22b933Srs200217 	// If the pktrr matches a member of ourset, not a conflict
4847*4b22b933Srs200217 	if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse);
4848*4b22b933Srs200217 
4849*4b22b933Srs200217 	// Okay, this is a conflict
4850*4b22b933Srs200217 	return(mDNStrue);
4851*4b22b933Srs200217 	}
4852*4b22b933Srs200217 
4853*4b22b933Srs200217 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
4854*4b22b933Srs200217 // the record list and/or question list.
4855*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
ResolveSimultaneousProbe(mDNS * const m,const DNSMessage * const query,const mDNSu8 * const end,DNSQuestion * q,AuthRecord * our)4856*4b22b933Srs200217 mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
4857*4b22b933Srs200217 	DNSQuestion *q, AuthRecord *our)
4858*4b22b933Srs200217 	{
4859*4b22b933Srs200217 	int i;
4860*4b22b933Srs200217 	const mDNSu8 *ptr = LocateAuthorities(query, end);
4861*4b22b933Srs200217 	mDNSBool FoundUpdate = mDNSfalse;
4862*4b22b933Srs200217 
4863*4b22b933Srs200217 	for (i = 0; i < query->h.numAuthorities; i++)
4864*4b22b933Srs200217 		{
4865*4b22b933Srs200217 		ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
4866*4b22b933Srs200217 		if (!ptr) break;
4867*4b22b933Srs200217 		if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
4868*4b22b933Srs200217 			{
4869*4b22b933Srs200217 			FoundUpdate = mDNStrue;
4870*4b22b933Srs200217 			if (PacketRRConflict(m, our, &m->rec.r))
4871*4b22b933Srs200217 				{
4872*4b22b933Srs200217 				int result          = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass;
4873*4b22b933Srs200217 				if (!result) result = (int)our->resrec.rrtype  - (int)m->rec.r.resrec.rrtype;
4874*4b22b933Srs200217 				if (!result) result = CompareRData(our, &m->rec.r);
4875*4b22b933Srs200217 				if (result > 0)
4876*4b22b933Srs200217 					debugf("ResolveSimultaneousProbe: %##s (%s): We won",  our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4877*4b22b933Srs200217 				else if (result < 0)
4878*4b22b933Srs200217 					{
4879*4b22b933Srs200217 					debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4880*4b22b933Srs200217 					mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict);
4881*4b22b933Srs200217 					goto exit;
4882*4b22b933Srs200217 					}
4883*4b22b933Srs200217 				}
4884*4b22b933Srs200217 			}
4885*4b22b933Srs200217 		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
4886*4b22b933Srs200217 		}
4887*4b22b933Srs200217 	if (!FoundUpdate)
4888*4b22b933Srs200217 		debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4889*4b22b933Srs200217 exit:
4890*4b22b933Srs200217 	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
4891*4b22b933Srs200217 	}
4892*4b22b933Srs200217 
FindIdenticalRecordInCache(const mDNS * const m,ResourceRecord * pktrr)4893*4b22b933Srs200217 mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr)
4894*4b22b933Srs200217 	{
4895*4b22b933Srs200217 	mDNSu32 slot = HashSlot(pktrr->name);
4896*4b22b933Srs200217 	CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
4897*4b22b933Srs200217 	CacheRecord *rr;
4898*4b22b933Srs200217 	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4899*4b22b933Srs200217 		if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
4900*4b22b933Srs200217 	return(rr);
4901*4b22b933Srs200217 	}
4902*4b22b933Srs200217 
4903*4b22b933Srs200217 // ProcessQuery examines a received query to see if we have any answers to give
ProcessQuery(mDNS * const m,const DNSMessage * const query,const mDNSu8 * const end,const mDNSAddr * srcaddr,const mDNSInterfaceID InterfaceID,mDNSBool LegacyQuery,mDNSBool QueryWasMulticast,mDNSBool QueryWasLocalUnicast,DNSMessage * const response)4904*4b22b933Srs200217 mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
4905*4b22b933Srs200217 	const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast,
4906*4b22b933Srs200217 	mDNSBool QueryWasLocalUnicast, DNSMessage *const response)
4907*4b22b933Srs200217 	{
4908*4b22b933Srs200217 	mDNSBool      FromLocalSubnet    = AddressIsLocalSubnet(m, InterfaceID, srcaddr);
4909*4b22b933Srs200217 	AuthRecord   *ResponseRecords    = mDNSNULL;
4910*4b22b933Srs200217 	AuthRecord  **nrp                = &ResponseRecords;
4911*4b22b933Srs200217 	CacheRecord  *ExpectedAnswers    = mDNSNULL;			// Records in our cache we expect to see updated
4912*4b22b933Srs200217 	CacheRecord **eap                = &ExpectedAnswers;
4913*4b22b933Srs200217 	DNSQuestion  *DupQuestions       = mDNSNULL;			// Our questions that are identical to questions in this packet
4914*4b22b933Srs200217 	DNSQuestion **dqp                = &DupQuestions;
4915*4b22b933Srs200217 	mDNSs32       delayresponse      = 0;
4916*4b22b933Srs200217 	mDNSBool      SendLegacyResponse = mDNSfalse;
4917*4b22b933Srs200217 	const mDNSu8 *ptr                = query->data;
4918*4b22b933Srs200217 	mDNSu8       *responseptr        = mDNSNULL;
4919*4b22b933Srs200217 	AuthRecord   *rr;
4920*4b22b933Srs200217 	int i;
4921*4b22b933Srs200217 
4922*4b22b933Srs200217 	// ***
4923*4b22b933Srs200217 	// *** 1. Parse Question Section and mark potential answers
4924*4b22b933Srs200217 	// ***
4925*4b22b933Srs200217 	for (i=0; i<query->h.numQuestions; i++)						// For each question...
4926*4b22b933Srs200217 		{
4927*4b22b933Srs200217 		mDNSBool QuestionNeedsMulticastResponse;
4928*4b22b933Srs200217 		int NumAnswersForThisQuestion = 0;
4929*4b22b933Srs200217 		DNSQuestion pktq, *q;
4930*4b22b933Srs200217 		ptr = getQuestion(query, ptr, end, InterfaceID, &pktq);	// get the question...
4931*4b22b933Srs200217 		if (!ptr) goto exit;
4932*4b22b933Srs200217 
4933*4b22b933Srs200217 		// The only queries that *need* a multicast response are:
4934*4b22b933Srs200217 		// * Queries sent via multicast
4935*4b22b933Srs200217 		// * from port 5353
4936*4b22b933Srs200217 		// * that don't have the kDNSQClass_UnicastResponse bit set
4937*4b22b933Srs200217 		// These queries need multicast responses because other clients will:
4938*4b22b933Srs200217 		// * suppress their own identical questions when they see these questions, and
4939*4b22b933Srs200217 		// * expire their cache records if they don't see the expected responses
4940*4b22b933Srs200217 		// For other queries, we may still choose to send the occasional multicast response anyway,
4941*4b22b933Srs200217 		// to keep our neighbours caches warm, and for ongoing conflict detection.
4942*4b22b933Srs200217 		QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse);
4943*4b22b933Srs200217 		// Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
4944*4b22b933Srs200217 		pktq.qclass &= ~kDNSQClass_UnicastResponse;
4945*4b22b933Srs200217 
4946*4b22b933Srs200217 		// Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
4947*4b22b933Srs200217 		// can result in user callbacks which may change the record list and/or question list.
4948*4b22b933Srs200217 		// Also note: we just mark potential answer records here, without trying to build the
4949*4b22b933Srs200217 		// "ResponseRecords" list, because we don't want to risk user callbacks deleting records
4950*4b22b933Srs200217 		// from that list while we're in the middle of trying to build it.
4951*4b22b933Srs200217 		if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set");
4952*4b22b933Srs200217 		m->CurrentRecord = m->ResourceRecords;
4953*4b22b933Srs200217 		while (m->CurrentRecord)
4954*4b22b933Srs200217 			{
4955*4b22b933Srs200217 			rr = m->CurrentRecord;
4956*4b22b933Srs200217 			m->CurrentRecord = rr->next;
4957*4b22b933Srs200217 			if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
4958*4b22b933Srs200217 				{
4959*4b22b933Srs200217 				if (rr->resrec.RecordType == kDNSRecordTypeUnique)
4960*4b22b933Srs200217 					ResolveSimultaneousProbe(m, query, end, &pktq, rr);
4961*4b22b933Srs200217 				else if (ResourceRecordIsValidAnswer(rr))
4962*4b22b933Srs200217 					{
4963*4b22b933Srs200217 					NumAnswersForThisQuestion++;
4964*4b22b933Srs200217 					// Notes:
4965*4b22b933Srs200217 					// NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
4966*4b22b933Srs200217 					// NR_AnswerTo == (mDNSu8*)~1             means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
4967*4b22b933Srs200217 					// NR_AnswerTo == (mDNSu8*)~0             means "definitely answer via multicast" (can't downgrade to unicast later)
4968*4b22b933Srs200217 					// If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
4969*4b22b933Srs200217 					// but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
4970*4b22b933Srs200217 					// then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
4971*4b22b933Srs200217 					if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery))
4972*4b22b933Srs200217 						{
4973*4b22b933Srs200217 						// We only mark this question for sending if it is at least one second since the last time we multicast it
4974*4b22b933Srs200217 						// on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
4975*4b22b933Srs200217 						// This is to guard against the case where someone blasts us with queries as fast as they can.
4976*4b22b933Srs200217 						if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
4977*4b22b933Srs200217 							(rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
4978*4b22b933Srs200217 							rr->NR_AnswerTo = (mDNSu8*)~0;
4979*4b22b933Srs200217 						}
4980*4b22b933Srs200217 					else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
4981*4b22b933Srs200217 					}
4982*4b22b933Srs200217 				}
4983*4b22b933Srs200217 			}
4984*4b22b933Srs200217 
4985*4b22b933Srs200217 		// If we couldn't answer this question, someone else might be able to,
4986*4b22b933Srs200217 		// so use random delay on response to reduce collisions
4987*4b22b933Srs200217 		if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond;	// Divided by 50 = 20ms
4988*4b22b933Srs200217 
4989*4b22b933Srs200217 		// We only do the following accelerated cache expiration processing and duplicate question suppression processing
4990*4b22b933Srs200217 		// for multicast queries with multicast responses.
4991*4b22b933Srs200217 		// For any query generating a unicast response we don't do this because we can't assume we will see the response
4992*4b22b933Srs200217 		if (QuestionNeedsMulticastResponse)
4993*4b22b933Srs200217 			{
4994*4b22b933Srs200217 			const mDNSu32 slot = HashSlot(&pktq.qname);
4995*4b22b933Srs200217 			CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
4996*4b22b933Srs200217 			CacheRecord *rr;
4997*4b22b933Srs200217 
4998*4b22b933Srs200217 			// Make a list indicating which of our own cache records we expect to see updated as a result of this query
4999*4b22b933Srs200217 			// Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
5000*4b22b933Srs200217 			for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5001*4b22b933Srs200217 				if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit)
5002*4b22b933Srs200217 					if (!rr->NextInKAList && eap != &rr->NextInKAList)
5003*4b22b933Srs200217 						{
5004*4b22b933Srs200217 						*eap = rr;
5005*4b22b933Srs200217 						eap = &rr->NextInKAList;
5006*4b22b933Srs200217 						if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond)
5007*4b22b933Srs200217 							{
5008*4b22b933Srs200217 							// Although MPUnansweredQ is only really used for multi-packet query processing,
5009*4b22b933Srs200217 							// we increment it for both single-packet and multi-packet queries, so that it stays in sync
5010*4b22b933Srs200217 							// with the MPUnansweredKA value, which by necessity is incremented for both query types.
5011*4b22b933Srs200217 							rr->MPUnansweredQ++;
5012*4b22b933Srs200217 							rr->MPLastUnansweredQT = m->timenow;
5013*4b22b933Srs200217 							rr->MPExpectingKA = mDNStrue;
5014*4b22b933Srs200217 							}
5015*4b22b933Srs200217 						}
5016*4b22b933Srs200217 
5017*4b22b933Srs200217 			// Check if this question is the same as any of mine.
5018*4b22b933Srs200217 			// We only do this for non-truncated queries. Right now it would be too complicated to try
5019*4b22b933Srs200217 			// to keep track of duplicate suppression state between multiple packets, especially when we
5020*4b22b933Srs200217 			// can't guarantee to receive all of the Known Answer packets that go with a particular query.
5021*4b22b933Srs200217 			if (!(query->h.flags.b[0] & kDNSFlag0_TC))
5022*4b22b933Srs200217 				for (q = m->Questions; q; q=q->next)
5023*4b22b933Srs200217 					if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
5024*4b22b933Srs200217 						if (!q->InterfaceID || q->InterfaceID == InterfaceID)
5025*4b22b933Srs200217 							if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
5026*4b22b933Srs200217 								if (q->qtype == pktq.qtype &&
5027*4b22b933Srs200217 									q->qclass == pktq.qclass &&
5028*4b22b933Srs200217 									q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
5029*4b22b933Srs200217 									{ *dqp = q; dqp = &q->NextInDQList; }
5030*4b22b933Srs200217 			}
5031*4b22b933Srs200217 		}
5032*4b22b933Srs200217 
5033*4b22b933Srs200217 	// ***
5034*4b22b933Srs200217 	// *** 2. Now we can safely build the list of marked answers
5035*4b22b933Srs200217 	// ***
5036*4b22b933Srs200217 	for (rr = m->ResourceRecords; rr; rr=rr->next)				// Now build our list of potential answers
5037*4b22b933Srs200217 		if (rr->NR_AnswerTo)									// If we marked the record...
5038*4b22b933Srs200217 			AddRecordToResponseList(&nrp, rr, mDNSNULL);		// ... add it to the list
5039*4b22b933Srs200217 
5040*4b22b933Srs200217 	// ***
5041*4b22b933Srs200217 	// *** 3. Add additional records
5042*4b22b933Srs200217 	// ***
5043*4b22b933Srs200217 	AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
5044*4b22b933Srs200217 
5045*4b22b933Srs200217 	// ***
5046*4b22b933Srs200217 	// *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
5047*4b22b933Srs200217 	// ***
5048*4b22b933Srs200217 	for (i=0; i<query->h.numAnswers; i++)						// For each record in the query's answer section...
5049*4b22b933Srs200217 		{
5050*4b22b933Srs200217 		// Get the record...
5051*4b22b933Srs200217 		AuthRecord *rr;
5052*4b22b933Srs200217 		CacheRecord *ourcacherr;
5053*4b22b933Srs200217 		ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
5054*4b22b933Srs200217 		if (!ptr) goto exit;
5055*4b22b933Srs200217 
5056*4b22b933Srs200217 		// See if this Known-Answer suppresses any of our currently planned answers
5057*4b22b933Srs200217 		for (rr=ResponseRecords; rr; rr=rr->NextResponse)
5058*4b22b933Srs200217 			if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
5059*4b22b933Srs200217 				{ rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
5060*4b22b933Srs200217 
5061*4b22b933Srs200217 		// See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
5062*4b22b933Srs200217 		for (rr=m->ResourceRecords; rr; rr=rr->next)
5063*4b22b933Srs200217 			{
5064*4b22b933Srs200217 			// If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
5065*4b22b933Srs200217 			if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr))
5066*4b22b933Srs200217 				{
5067*4b22b933Srs200217 				if (srcaddr->type == mDNSAddrType_IPv4)
5068*4b22b933Srs200217 					{
5069*4b22b933Srs200217 					if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr;
5070*4b22b933Srs200217 					}
5071*4b22b933Srs200217 				else if (srcaddr->type == mDNSAddrType_IPv6)
5072*4b22b933Srs200217 					{
5073*4b22b933Srs200217 					if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
5074*4b22b933Srs200217 					}
5075*4b22b933Srs200217 				if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
5076*4b22b933Srs200217 					{
5077*4b22b933Srs200217 					rr->ImmedAnswer  = mDNSNULL;
5078*4b22b933Srs200217 					rr->ImmedUnicast = mDNSfalse;
5079*4b22b933Srs200217 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5080*4b22b933Srs200217 					LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr));
5081*4b22b933Srs200217 #endif
5082*4b22b933Srs200217 					}
5083*4b22b933Srs200217 				}
5084*4b22b933Srs200217 			}
5085*4b22b933Srs200217 
5086*4b22b933Srs200217 		// See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
5087*4b22b933Srs200217 		// 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).
5088*4b22b933Srs200217 		ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
5089*4b22b933Srs200217 		if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond)
5090*4b22b933Srs200217 			{
5091*4b22b933Srs200217 			ourcacherr->MPUnansweredKA++;
5092*4b22b933Srs200217 			ourcacherr->MPExpectingKA = mDNSfalse;
5093*4b22b933Srs200217 			}
5094*4b22b933Srs200217 
5095*4b22b933Srs200217 		// Having built our ExpectedAnswers list from the questions in this packet, we can definitively
5096*4b22b933Srs200217 		// remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
5097*4b22b933Srs200217 		// For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
5098*4b22b933Srs200217 		eap = &ExpectedAnswers;
5099*4b22b933Srs200217 		while (*eap)
5100*4b22b933Srs200217 			{
5101*4b22b933Srs200217 			CacheRecord *rr = *eap;
5102*4b22b933Srs200217 			if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
5103*4b22b933Srs200217 				{ *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; }
5104*4b22b933Srs200217 			else eap = &rr->NextInKAList;
5105*4b22b933Srs200217 			}
5106*4b22b933Srs200217 
5107*4b22b933Srs200217 		// See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
5108*4b22b933Srs200217 		if (!ourcacherr)
5109*4b22b933Srs200217 			{
5110*4b22b933Srs200217 			dqp = &DupQuestions;
5111*4b22b933Srs200217 			while (*dqp)
5112*4b22b933Srs200217 				{
5113*4b22b933Srs200217 				DNSQuestion *q = *dqp;
5114*4b22b933Srs200217 				if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
5115*4b22b933Srs200217 					{ *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
5116*4b22b933Srs200217 				else dqp = &q->NextInDQList;
5117*4b22b933Srs200217 				}
5118*4b22b933Srs200217 			}
5119*4b22b933Srs200217 		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
5120*4b22b933Srs200217 		}
5121*4b22b933Srs200217 
5122*4b22b933Srs200217 	// ***
5123*4b22b933Srs200217 	// *** 5. Cancel any additionals that were added because of now-deleted records
5124*4b22b933Srs200217 	// ***
5125*4b22b933Srs200217 	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
5126*4b22b933Srs200217 		if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo))
5127*4b22b933Srs200217 			{ rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
5128*4b22b933Srs200217 
5129*4b22b933Srs200217 	// ***
5130*4b22b933Srs200217 	// *** 6. Mark the send flags on the records we plan to send
5131*4b22b933Srs200217 	// ***
5132*4b22b933Srs200217 	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
5133*4b22b933Srs200217 		{
5134*4b22b933Srs200217 		if (rr->NR_AnswerTo)
5135*4b22b933Srs200217 			{
5136*4b22b933Srs200217 			mDNSBool SendMulticastResponse = mDNSfalse;		// Send modern multicast response
5137*4b22b933Srs200217 			mDNSBool SendUnicastResponse   = mDNSfalse;		// Send modern unicast response (not legacy unicast response)
5138*4b22b933Srs200217 
5139*4b22b933Srs200217 			// If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
5140*4b22b933Srs200217 			if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0)
5141*4b22b933Srs200217 				{
5142*4b22b933Srs200217 				SendMulticastResponse = mDNStrue;
5143*4b22b933Srs200217 				// If this record was marked for modern (delayed) unicast response, then mark it as promoted to
5144*4b22b933Srs200217 				// multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
5145*4b22b933Srs200217 				// If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
5146*4b22b933Srs200217 				if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0;
5147*4b22b933Srs200217 				}
5148*4b22b933Srs200217 
5149*4b22b933Srs200217 			// If the client insists on a multicast response, then we'd better send one
5150*4b22b933Srs200217 			if      (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue;
5151*4b22b933Srs200217 			else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse   = mDNStrue;
5152*4b22b933Srs200217 			else if (rr->NR_AnswerTo)                SendLegacyResponse    = mDNStrue;
5153*4b22b933Srs200217 
5154*4b22b933Srs200217 			if (SendMulticastResponse || SendUnicastResponse)
5155*4b22b933Srs200217 				{
5156*4b22b933Srs200217 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5157*4b22b933Srs200217 				rr->ImmedAnswerMarkTime = m->timenow;
5158*4b22b933Srs200217 #endif
5159*4b22b933Srs200217 				m->NextScheduledResponse = m->timenow;
5160*4b22b933Srs200217 				// If we're already planning to send this on another interface, just send it on all interfaces
5161*4b22b933Srs200217 				if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID)
5162*4b22b933Srs200217 					rr->ImmedAnswer = mDNSInterfaceMark;
5163*4b22b933Srs200217 				else
5164*4b22b933Srs200217 					{
5165*4b22b933Srs200217 					rr->ImmedAnswer = InterfaceID;			// Record interface to send it on
5166*4b22b933Srs200217 					if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue;
5167*4b22b933Srs200217 					if (srcaddr->type == mDNSAddrType_IPv4)
5168*4b22b933Srs200217 						{
5169*4b22b933Srs200217 						if      (mDNSIPv4AddressIsZero(rr->v4Requester))                rr->v4Requester = srcaddr->ip.v4;
5170*4b22b933Srs200217 						else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr;
5171*4b22b933Srs200217 						}
5172*4b22b933Srs200217 					else if (srcaddr->type == mDNSAddrType_IPv6)
5173*4b22b933Srs200217 						{
5174*4b22b933Srs200217 						if      (mDNSIPv6AddressIsZero(rr->v6Requester))                rr->v6Requester = srcaddr->ip.v6;
5175*4b22b933Srs200217 						else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr;
5176*4b22b933Srs200217 						}
5177*4b22b933Srs200217 					}
5178*4b22b933Srs200217 				}
5179*4b22b933Srs200217 			// If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
5180*4b22b933Srs200217 			// so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
5181*4b22b933Srs200217 			// else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
5182*4b22b933Srs200217 			// else, for a simple unique record reply, we can reply immediately; no need for delay
5183*4b22b933Srs200217 			if      (query->h.flags.b[0] & kDNSFlag0_TC)            delayresponse = mDNSPlatformOneSecond * 20;	// Divided by 50 = 400ms
5184*4b22b933Srs200217 			else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond;		// Divided by 50 = 20ms
5185*4b22b933Srs200217 			}
5186*4b22b933Srs200217 		else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0)
5187*4b22b933Srs200217 			{
5188*4b22b933Srs200217 			// Since additional records are an optimization anyway, we only ever send them on one interface at a time
5189*4b22b933Srs200217 			// If two clients on different interfaces do queries that invoke the same optional additional answer,
5190*4b22b933Srs200217 			// then the earlier client is out of luck
5191*4b22b933Srs200217 			rr->ImmedAdditional = InterfaceID;
5192*4b22b933Srs200217 			// No need to set m->NextScheduledResponse here
5193*4b22b933Srs200217 			// We'll send these additional records when we send them, or not, as the case may be
5194*4b22b933Srs200217 			}
5195*4b22b933Srs200217 		}
5196*4b22b933Srs200217 
5197*4b22b933Srs200217 	// ***
5198*4b22b933Srs200217 	// *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5199*4b22b933Srs200217 	// ***
5200*4b22b933Srs200217 	if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50))
5201*4b22b933Srs200217 		{
5202*4b22b933Srs200217 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5203*4b22b933Srs200217 		mDNSs32 oldss = m->SuppressSending;
5204*4b22b933Srs200217 		if (oldss && delayresponse)
5205*4b22b933Srs200217 			LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50);
5206*4b22b933Srs200217 #endif
5207*4b22b933Srs200217 		// Pick a random delay:
5208*4b22b933Srs200217 		// We start with the base delay chosen above (typically either 1 second or 20 seconds),
5209*4b22b933Srs200217 		// and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
5210*4b22b933Srs200217 		// This is an integer value, with resolution determined by the platform clock rate.
5211*4b22b933Srs200217 		// We then divide that by 50 to get the delay value in ticks. We defer the division until last
5212*4b22b933Srs200217 		// to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
5213*4b22b933Srs200217 		// The +49 before dividing is to ensure we round up, not down, to ensure that even
5214*4b22b933Srs200217 		// on platforms where the native clock rate is less than fifty ticks per second,
5215*4b22b933Srs200217 		// we still guarantee that the final calculated delay is at least one platform tick.
5216*4b22b933Srs200217 		// We want to make sure we don't ever allow the delay to be zero ticks,
5217*4b22b933Srs200217 		// because if that happens we'll fail the Bonjour Conformance Test.
5218*4b22b933Srs200217 		// Our final computed delay is 20-120ms for normal delayed replies,
5219*4b22b933Srs200217 		// or 400-500ms in the case of multi-packet known-answer lists.
5220*4b22b933Srs200217 		m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50;
5221*4b22b933Srs200217 		if (m->SuppressSending == 0) m->SuppressSending = 1;
5222*4b22b933Srs200217 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5223*4b22b933Srs200217 		if (oldss && delayresponse)
5224*4b22b933Srs200217 			LogMsg("Set     SuppressSending to   %5ld", m->SuppressSending - m->timenow);
5225*4b22b933Srs200217 #endif
5226*4b22b933Srs200217 		}
5227*4b22b933Srs200217 
5228*4b22b933Srs200217 	// ***
5229*4b22b933Srs200217 	// *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5230*4b22b933Srs200217 	// ***
5231*4b22b933Srs200217 	if (SendLegacyResponse)
5232*4b22b933Srs200217 		responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
5233*4b22b933Srs200217 
5234*4b22b933Srs200217 exit:
5235*4b22b933Srs200217 	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
5236*4b22b933Srs200217 
5237*4b22b933Srs200217 	// ***
5238*4b22b933Srs200217 	// *** 9. Finally, clear our link chains ready for use next time
5239*4b22b933Srs200217 	// ***
5240*4b22b933Srs200217 	while (ResponseRecords)
5241*4b22b933Srs200217 		{
5242*4b22b933Srs200217 		rr = ResponseRecords;
5243*4b22b933Srs200217 		ResponseRecords = rr->NextResponse;
5244*4b22b933Srs200217 		rr->NextResponse    = mDNSNULL;
5245*4b22b933Srs200217 		rr->NR_AnswerTo     = mDNSNULL;
5246*4b22b933Srs200217 		rr->NR_AdditionalTo = mDNSNULL;
5247*4b22b933Srs200217 		}
5248*4b22b933Srs200217 
5249*4b22b933Srs200217 	while (ExpectedAnswers)
5250*4b22b933Srs200217 		{
5251*4b22b933Srs200217 		CacheRecord *rr;
5252*4b22b933Srs200217 		rr = ExpectedAnswers;
5253*4b22b933Srs200217 		ExpectedAnswers = rr->NextInKAList;
5254*4b22b933Srs200217 		rr->NextInKAList = mDNSNULL;
5255*4b22b933Srs200217 
5256*4b22b933Srs200217 		// For non-truncated queries, we can definitively say that we should expect
5257*4b22b933Srs200217 		// to be seeing a response for any records still left in the ExpectedAnswers list
5258*4b22b933Srs200217 		if (!(query->h.flags.b[0] & kDNSFlag0_TC))
5259*4b22b933Srs200217 			if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond)
5260*4b22b933Srs200217 				{
5261*4b22b933Srs200217 				rr->UnansweredQueries++;
5262*4b22b933Srs200217 				rr->LastUnansweredTime = m->timenow;
5263*4b22b933Srs200217 				if (rr->UnansweredQueries > 1)
5264*4b22b933Srs200217 					debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
5265*4b22b933Srs200217 						rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
5266*4b22b933Srs200217 				SetNextCacheCheckTime(m, rr);
5267*4b22b933Srs200217 				}
5268*4b22b933Srs200217 
5269*4b22b933Srs200217 		// If we've seen multiple unanswered queries for this record,
5270*4b22b933Srs200217 		// then mark it to expire in five seconds if we don't get a response by then.
5271*4b22b933Srs200217 		if (rr->UnansweredQueries >= MaxUnansweredQueries)
5272*4b22b933Srs200217 			{
5273*4b22b933Srs200217 			// Only show debugging message if this record was not about to expire anyway
5274*4b22b933Srs200217 			if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond)
5275*4b22b933Srs200217 				debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5276*4b22b933Srs200217 					rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
5277*4b22b933Srs200217 			mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
5278*4b22b933Srs200217 			}
5279*4b22b933Srs200217 		// Make a guess, based on the multi-packet query / known answer counts, whether we think we
5280*4b22b933Srs200217 		// should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
5281*4b22b933Srs200217 		// possible packet loss of up to 20% of the additional KA packets.)
5282*4b22b933Srs200217 		else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8)
5283*4b22b933Srs200217 			{
5284*4b22b933Srs200217 			// We want to do this conservatively.
5285*4b22b933Srs200217 			// If there are so many machines on the network that they have to use multi-packet known-answer lists,
5286*4b22b933Srs200217 			// then we don't want them to all hit the network simultaneously with their final expiration queries.
5287*4b22b933Srs200217 			// By setting the record to expire in four minutes, we achieve two things:
5288*4b22b933Srs200217 			// (a) the 90-95% final expiration queries will be less bunched together
5289*4b22b933Srs200217 			// (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
5290*4b22b933Srs200217 			mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4;
5291*4b22b933Srs200217 			if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond)
5292*4b22b933Srs200217 				remain = 240 * (mDNSu32)mDNSPlatformOneSecond;
5293*4b22b933Srs200217 
5294*4b22b933Srs200217 			// Only show debugging message if this record was not about to expire anyway
5295*4b22b933Srs200217 			if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond)
5296*4b22b933Srs200217 				debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5297*4b22b933Srs200217 					rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
5298*4b22b933Srs200217 
5299*4b22b933Srs200217 			if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond)
5300*4b22b933Srs200217 				rr->UnansweredQueries++;	// Treat this as equivalent to one definite unanswered query
5301*4b22b933Srs200217 			rr->MPUnansweredQ  = 0;			// Clear MPQ/MPKA statistics
5302*4b22b933Srs200217 			rr->MPUnansweredKA = 0;
5303*4b22b933Srs200217 			rr->MPExpectingKA  = mDNSfalse;
5304*4b22b933Srs200217 
5305*4b22b933Srs200217 			if (remain < kDefaultReconfirmTimeForNoAnswer)
5306*4b22b933Srs200217 				remain = kDefaultReconfirmTimeForNoAnswer;
5307*4b22b933Srs200217 			mDNS_Reconfirm_internal(m, rr, remain);
5308*4b22b933Srs200217 			}
5309*4b22b933Srs200217 		}
5310*4b22b933Srs200217 
5311*4b22b933Srs200217 	while (DupQuestions)
5312*4b22b933Srs200217 		{
5313*4b22b933Srs200217 		int i;
5314*4b22b933Srs200217 		DNSQuestion *q = DupQuestions;
5315*4b22b933Srs200217 		DupQuestions = q->NextInDQList;
5316*4b22b933Srs200217 		q->NextInDQList = mDNSNULL;
5317*4b22b933Srs200217 		i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type);
5318*4b22b933Srs200217 		debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID,
5319*4b22b933Srs200217 			srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i);
5320*4b22b933Srs200217 		}
5321*4b22b933Srs200217 
5322*4b22b933Srs200217 	return(responseptr);
5323*4b22b933Srs200217 	}
5324*4b22b933Srs200217 
mDNSCoreReceiveQuery(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * const end,const mDNSAddr * srcaddr,const mDNSIPPort srcport,const mDNSAddr * dstaddr,mDNSIPPort dstport,const mDNSInterfaceID InterfaceID)5325*4b22b933Srs200217 mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
5326*4b22b933Srs200217 	const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5327*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID)
5328*4b22b933Srs200217 	{
5329*4b22b933Srs200217 	mDNSu8    *responseend = mDNSNULL;
5330*4b22b933Srs200217 	mDNSBool   QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr);
5331*4b22b933Srs200217 
5332*4b22b933Srs200217 	if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))
5333*4b22b933Srs200217 		{
5334*4b22b933Srs200217 		LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
5335*4b22b933Srs200217 			"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)",
5336*4b22b933Srs200217 			srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
5337*4b22b933Srs200217 			msg->h.numQuestions,   msg->h.numQuestions   == 1 ? ", " : "s,",
5338*4b22b933Srs200217 			msg->h.numAnswers,     msg->h.numAnswers     == 1 ? ", " : "s,",
5339*4b22b933Srs200217 			msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y,  " : "ies,",
5340*4b22b933Srs200217 			msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
5341*4b22b933Srs200217 		return;
5342*4b22b933Srs200217 		}
5343*4b22b933Srs200217 
5344*4b22b933Srs200217 	verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
5345*4b22b933Srs200217 		"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5346*4b22b933Srs200217 		srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
5347*4b22b933Srs200217 		msg->h.numQuestions,   msg->h.numQuestions   == 1 ? ", " : "s,",
5348*4b22b933Srs200217 		msg->h.numAnswers,     msg->h.numAnswers     == 1 ? ", " : "s,",
5349*4b22b933Srs200217 		msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y,  " : "ies,",
5350*4b22b933Srs200217 		msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
5351*4b22b933Srs200217 
5352*4b22b933Srs200217 	responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID,
5353*4b22b933Srs200217 		(srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg);
5354*4b22b933Srs200217 
5355*4b22b933Srs200217 	if (responseend)	// If responseend is non-null, that means we built a unicast response packet
5356*4b22b933Srs200217 		{
5357*4b22b933Srs200217 		debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
5358*4b22b933Srs200217 			m->omsg.h.numQuestions,   m->omsg.h.numQuestions   == 1 ? "" : "s",
5359*4b22b933Srs200217 			m->omsg.h.numAnswers,     m->omsg.h.numAnswers     == 1 ? "" : "s",
5360*4b22b933Srs200217 			m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s",
5361*4b22b933Srs200217 			srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type);
5362*4b22b933Srs200217 		mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL);
5363*4b22b933Srs200217 		}
5364*4b22b933Srs200217 	}
5365*4b22b933Srs200217 
5366*4b22b933Srs200217 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
5367*4b22b933Srs200217 // the record list and/or question list.
5368*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNSCoreReceiveResponse(mDNS * const m,const DNSMessage * const response,const mDNSu8 * end,const mDNSAddr * srcaddr,const mDNSIPPort srcport,const mDNSAddr * dstaddr,mDNSIPPort dstport,const mDNSInterfaceID InterfaceID)5369*4b22b933Srs200217 mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
5370*4b22b933Srs200217 	const DNSMessage *const response, const mDNSu8 *end,
5371*4b22b933Srs200217 	const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
5372*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID)
5373*4b22b933Srs200217 	{
5374*4b22b933Srs200217 	int i;
5375*4b22b933Srs200217 
5376*4b22b933Srs200217 	// We ignore questions (if any) in a DNS response packet
5377*4b22b933Srs200217 	const mDNSu8 *ptr = LocateAnswers(response, end);
5378*4b22b933Srs200217 
5379*4b22b933Srs200217 	// "(CacheRecord*)1" is a special (non-zero) end-of-list marker
5380*4b22b933Srs200217 	// We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList
5381*4b22b933Srs200217 	// set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling.
5382*4b22b933Srs200217 	CacheRecord *CacheFlushRecords = (CacheRecord*)1;
5383*4b22b933Srs200217 	CacheRecord **cfp = &CacheFlushRecords;
5384*4b22b933Srs200217 
5385*4b22b933Srs200217 	// All records in a DNS response packet are treated as equally valid statements of truth. If we want
5386*4b22b933Srs200217 	// to guard against spoof responses, then the only credible protection against that is cryptographic
5387*4b22b933Srs200217 	// security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
5388*4b22b933Srs200217 	int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals;
5389*4b22b933Srs200217 
5390*4b22b933Srs200217 	(void)srcaddr;	// Currently used only for display in debugging message
5391*4b22b933Srs200217 	(void)srcport;
5392*4b22b933Srs200217 	(void)dstport;
5393*4b22b933Srs200217 
5394*4b22b933Srs200217 	verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with "
5395*4b22b933Srs200217 		"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5396*4b22b933Srs200217 		srcaddr, dstaddr, InterfaceID,
5397*4b22b933Srs200217 		response->h.numQuestions,   response->h.numQuestions   == 1 ? ", " : "s,",
5398*4b22b933Srs200217 		response->h.numAnswers,     response->h.numAnswers     == 1 ? ", " : "s,",
5399*4b22b933Srs200217 		response->h.numAuthorities, response->h.numAuthorities == 1 ? "y,  " : "ies,",
5400*4b22b933Srs200217 		response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
5401*4b22b933Srs200217 
5402*4b22b933Srs200217 	// If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
5403*4b22b933Srs200217 	if (!mDNSAddrIsDNSMulticast(dstaddr))
5404*4b22b933Srs200217 		{
5405*4b22b933Srs200217 		if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))
5406*4b22b933Srs200217 			return;
5407*4b22b933Srs200217 		// For now we don't put standard wide-area unicast responses in our main cache
5408*4b22b933Srs200217 		// (Later we should fix this and cache all known results in a unified manner.)
5409*4b22b933Srs200217 		if (response->h.id.NotAnInteger != 0 || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger)
5410*4b22b933Srs200217 			return;
5411*4b22b933Srs200217 		}
5412*4b22b933Srs200217 
5413*4b22b933Srs200217 	for (i = 0; i < totalrecords && ptr && ptr < end; i++)
5414*4b22b933Srs200217 		{
5415*4b22b933Srs200217 		const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd);
5416*4b22b933Srs200217 		ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec);
5417*4b22b933Srs200217 		if (!ptr) goto exit;		// Break out of the loop and clean up our CacheFlushRecords list before exiting
5418*4b22b933Srs200217 
5419*4b22b933Srs200217 		// 1. Check that this packet resource record does not conflict with any of ours
5420*4b22b933Srs200217 		if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
5421*4b22b933Srs200217 		m->CurrentRecord = m->ResourceRecords;
5422*4b22b933Srs200217 		while (m->CurrentRecord)
5423*4b22b933Srs200217 			{
5424*4b22b933Srs200217 			AuthRecord *rr = m->CurrentRecord;
5425*4b22b933Srs200217 			m->CurrentRecord = rr->next;
5426*4b22b933Srs200217 			if (PacketRRMatchesSignature(&m->rec.r, rr))		// If interface, name, type (if shared record) and class match...
5427*4b22b933Srs200217 				{
5428*4b22b933Srs200217 				// ... check to see if type and rdata are identical
5429*4b22b933Srs200217 				if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec))
5430*4b22b933Srs200217 					{
5431*4b22b933Srs200217 					// If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
5432*4b22b933Srs200217 					if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
5433*4b22b933Srs200217 						{
5434*4b22b933Srs200217 						// If we were planning to send on this -- and only this -- interface, then we don't need to any more
5435*4b22b933Srs200217 						if      (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; }
5436*4b22b933Srs200217 						}
5437*4b22b933Srs200217 					else
5438*4b22b933Srs200217 						{
5439*4b22b933Srs200217 						if      (rr->ImmedAnswer == mDNSNULL)    { rr->ImmedAnswer = InterfaceID;       m->NextScheduledResponse = m->timenow; }
5440*4b22b933Srs200217 						else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
5441*4b22b933Srs200217 						}
5442*4b22b933Srs200217 					}
5443*4b22b933Srs200217 				// else, the packet RR has different type or different rdata -- check to see if this is a conflict
5444*4b22b933Srs200217 				else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r))
5445*4b22b933Srs200217 					{
5446*4b22b933Srs200217 					debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr->     resrec.rdatahash, ARDisplayString(m, rr));
5447*4b22b933Srs200217 					debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
5448*4b22b933Srs200217 
5449*4b22b933Srs200217 					// If this record is marked DependentOn another record for conflict detection purposes,
5450*4b22b933Srs200217 					// then *that* record has to be bumped back to probing state to resolve the conflict
5451*4b22b933Srs200217 					while (rr->DependentOn) rr = rr->DependentOn;
5452*4b22b933Srs200217 
5453*4b22b933Srs200217 					// If we've just whacked this record's ProbeCount, don't need to do it again
5454*4b22b933Srs200217 					if (rr->ProbeCount <= DefaultProbeCountForTypeUnique)
5455*4b22b933Srs200217 						{
5456*4b22b933Srs200217 						// If we'd previously verified this record, put it back to probing state and try again
5457*4b22b933Srs200217 						if (rr->resrec.RecordType == kDNSRecordTypeVerified)
5458*4b22b933Srs200217 							{
5459*4b22b933Srs200217 							debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5460*4b22b933Srs200217 							rr->resrec.RecordType     = kDNSRecordTypeUnique;
5461*4b22b933Srs200217 							rr->ProbeCount     = DefaultProbeCountForTypeUnique + 1;
5462*4b22b933Srs200217 							rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique);
5463*4b22b933Srs200217 							InitializeLastAPTime(m, rr);
5464*4b22b933Srs200217 							RecordProbeFailure(m, rr);	// Repeated late conflicts also cause us to back off to the slower probing rate
5465*4b22b933Srs200217 							}
5466*4b22b933Srs200217 						// If we're probing for this record, we just failed
5467*4b22b933Srs200217 						else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
5468*4b22b933Srs200217 							{
5469*4b22b933Srs200217 							debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5470*4b22b933Srs200217 							mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
5471*4b22b933Srs200217 							}
5472*4b22b933Srs200217 						// We assumed this record must be unique, but we were wrong.
5473*4b22b933Srs200217 						// (e.g. There are two mDNSResponders on the same machine giving
5474*4b22b933Srs200217 						// different answers for the reverse mapping record.)
5475*4b22b933Srs200217 						// This is simply a misconfiguration, and we don't try to recover from it.
5476*4b22b933Srs200217 						else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
5477*4b22b933Srs200217 							{
5478*4b22b933Srs200217 							debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
5479*4b22b933Srs200217 								rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5480*4b22b933Srs200217 							mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
5481*4b22b933Srs200217 							}
5482*4b22b933Srs200217 						else
5483*4b22b933Srs200217 							debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5484*4b22b933Srs200217 								rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5485*4b22b933Srs200217 						}
5486*4b22b933Srs200217 					}
5487*4b22b933Srs200217 				// Else, matching signature, different type or rdata, but not a considered a conflict.
5488*4b22b933Srs200217 				// If the packet record has the cache-flush bit set, then we check to see if we
5489*4b22b933Srs200217 				// have any record(s) of the same type that we should re-assert to rescue them
5490*4b22b933Srs200217 				// (see note about "multi-homing and bridged networks" at the end of this function).
5491*4b22b933Srs200217 				else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype)
5492*4b22b933Srs200217 					if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2)
5493*4b22b933Srs200217 						{ rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
5494*4b22b933Srs200217 				}
5495*4b22b933Srs200217 			}
5496*4b22b933Srs200217 
5497*4b22b933Srs200217 		// 2. See if we want to add this packet resource record to our cache
5498*4b22b933Srs200217 		if (m->rrcache_size)	// Only try to cache answers if we have a cache to put them in
5499*4b22b933Srs200217 			{
5500*4b22b933Srs200217 			const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
5501*4b22b933Srs200217 			CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
5502*4b22b933Srs200217 			CacheRecord *rr;
5503*4b22b933Srs200217 			// 2a. Check if this packet resource record is already in our cache
5504*4b22b933Srs200217 			for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5505*4b22b933Srs200217 				{
5506*4b22b933Srs200217 				// If we found this exact resource record, refresh its TTL
5507*4b22b933Srs200217 				if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
5508*4b22b933Srs200217 					{
5509*4b22b933Srs200217 					if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5510*4b22b933Srs200217 						verbosedebugf("Found record size %5d interface %p already in cache: %s",
5511*4b22b933Srs200217 							m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
5512*4b22b933Srs200217 					rr->TimeRcvd  = m->timenow;
5513*4b22b933Srs200217 
5514*4b22b933Srs200217 					if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
5515*4b22b933Srs200217 						{
5516*4b22b933Srs200217 						// If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
5517*4b22b933Srs200217 						if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList)
5518*4b22b933Srs200217 							{ *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
5519*4b22b933Srs200217 
5520*4b22b933Srs200217 						// If this packet record is marked unique, and our previous cached copy was not, then fix it
5521*4b22b933Srs200217 						if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
5522*4b22b933Srs200217 							{
5523*4b22b933Srs200217 							DNSQuestion *q;
5524*4b22b933Srs200217 							for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++;
5525*4b22b933Srs200217 							rr->resrec.RecordType = m->rec.r.resrec.RecordType;
5526*4b22b933Srs200217 							}
5527*4b22b933Srs200217 						}
5528*4b22b933Srs200217 
5529*4b22b933Srs200217 					if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength))
5530*4b22b933Srs200217 						{
5531*4b22b933Srs200217 						// If the rdata of the packet record differs in name capitalization from the record in our cache
5532*4b22b933Srs200217 						// then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
5533*4b22b933Srs200217 						// a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
5534*4b22b933Srs200217 						rr->resrec.rroriginalttl = 0;
5535*4b22b933Srs200217 						rr->UnansweredQueries = MaxUnansweredQueries;
5536*4b22b933Srs200217 						SetNextCacheCheckTime(m, rr);
5537*4b22b933Srs200217 						// DO NOT break out here -- we want to continue as if we never found it
5538*4b22b933Srs200217 						}
5539*4b22b933Srs200217 					else if (m->rec.r.resrec.rroriginalttl > 0)
5540*4b22b933Srs200217 						{
5541*4b22b933Srs200217 						rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl;
5542*4b22b933Srs200217 						rr->UnansweredQueries = 0;
5543*4b22b933Srs200217 						rr->MPUnansweredQ     = 0;
5544*4b22b933Srs200217 						rr->MPUnansweredKA    = 0;
5545*4b22b933Srs200217 						rr->MPExpectingKA     = mDNSfalse;
5546*4b22b933Srs200217 						SetNextCacheCheckTime(m, rr);
5547*4b22b933Srs200217 						break;
5548*4b22b933Srs200217 						}
5549*4b22b933Srs200217 					else
5550*4b22b933Srs200217 						{
5551*4b22b933Srs200217 						// If the packet TTL is zero, that means we're deleting this record.
5552*4b22b933Srs200217 						// To give other hosts on the network a chance to protest, we push the deletion
5553*4b22b933Srs200217 						// out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
5554*4b22b933Srs200217 						// Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
5555*4b22b933Srs200217 						// lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
5556*4b22b933Srs200217 						rr->resrec.rroriginalttl = 1;
5557*4b22b933Srs200217 						rr->UnansweredQueries = MaxUnansweredQueries;
5558*4b22b933Srs200217 						SetNextCacheCheckTime(m, rr);
5559*4b22b933Srs200217 						break;
5560*4b22b933Srs200217 						}
5561*4b22b933Srs200217 					}
5562*4b22b933Srs200217 				}
5563*4b22b933Srs200217 
5564*4b22b933Srs200217 			// If packet resource record not in our cache, add it now
5565*4b22b933Srs200217 			// (unless it is just a deletion of a record we never had, in which case we don't care)
5566*4b22b933Srs200217 			if (!rr && m->rec.r.resrec.rroriginalttl > 0)
5567*4b22b933Srs200217 				{
5568*4b22b933Srs200217 				// If we don't have a CacheGroup for this name, make one now
5569*4b22b933Srs200217 				if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec);
5570*4b22b933Srs200217 				if (cg) rr = GetCacheRecord(m, cg, m->rec.r.resrec.rdlength);	// Make a cache record, being careful not to recycle cg
5571*4b22b933Srs200217 				if (!rr) NoCacheAnswer(m, &m->rec.r);
5572*4b22b933Srs200217 				else
5573*4b22b933Srs200217 					{
5574*4b22b933Srs200217 					RData *saveptr = rr->resrec.rdata;		// Save the rr->resrec.rdata pointer
5575*4b22b933Srs200217 					*rr = m->rec.r;							// Block copy the CacheRecord object
5576*4b22b933Srs200217 					rr->resrec.rdata = saveptr;				// Restore rr->resrec.rdata after the structure assignment
5577*4b22b933Srs200217 					rr->resrec.name  = cg->name;			// And set rr->resrec.name to point into our CacheGroup header
5578*4b22b933Srs200217 					if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
5579*4b22b933Srs200217 						{ *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
5580*4b22b933Srs200217 					// If this is an oversized record with external storage allocated, copy rdata to external storage
5581*4b22b933Srs200217 					if (rr->resrec.rdata != (RData*)&rr->rdatastorage && !(m->rec.r.resrec.rdlength > InlineCacheRDSize))
5582*4b22b933Srs200217 						LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c);
5583*4b22b933Srs200217 					if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
5584*4b22b933Srs200217 						mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength);
5585*4b22b933Srs200217 					rr->next = mDNSNULL;					// Clear 'next' pointer
5586*4b22b933Srs200217 					*(cg->rrcache_tail) = rr;				// Append this record to tail of cache slot list
5587*4b22b933Srs200217 					cg->rrcache_tail = &(rr->next);			// Advance tail pointer
5588*4b22b933Srs200217 					if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)	// If marked unique, assume we may have
5589*4b22b933Srs200217 						rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond;	// to delay delivery of this 'add' event
5590*4b22b933Srs200217 					else
5591*4b22b933Srs200217 						rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot);
5592*4b22b933Srs200217 					CacheRecordAdd(m, rr);  // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us
5593*4b22b933Srs200217 					}
5594*4b22b933Srs200217 				}
5595*4b22b933Srs200217 			}
5596*4b22b933Srs200217 		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
5597*4b22b933Srs200217 		}
5598*4b22b933Srs200217 
5599*4b22b933Srs200217 exit:
5600*4b22b933Srs200217 	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
5601*4b22b933Srs200217 
5602*4b22b933Srs200217 	// If we've just received one or more records with their cache flush bits set,
5603*4b22b933Srs200217 	// then scan that cache slot to see if there are any old stale records we need to flush
5604*4b22b933Srs200217 	while (CacheFlushRecords != (CacheRecord*)1)
5605*4b22b933Srs200217 		{
5606*4b22b933Srs200217 		CacheRecord *r1 = CacheFlushRecords, *r2;
5607*4b22b933Srs200217 		const mDNSu32 slot = HashSlot(r1->resrec.name);
5608*4b22b933Srs200217 		CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
5609*4b22b933Srs200217 		CacheFlushRecords = CacheFlushRecords->NextInCFList;
5610*4b22b933Srs200217 		r1->NextInCFList = mDNSNULL;
5611*4b22b933Srs200217 		for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
5612*4b22b933Srs200217 			if (SameResourceRecordSignature(&r1->resrec, &r2->resrec))
5613*4b22b933Srs200217 				{
5614*4b22b933Srs200217 				// If record was recently positively received
5615*4b22b933Srs200217 				// (i.e. not counting goodbye packets or cache flush events that set the TTL to 1)
5616*4b22b933Srs200217 				// then we need to ensure the whole RRSet has the same TTL (as required by DNS semantics)
5617*4b22b933Srs200217 				if (r2->resrec.rroriginalttl > 1 && m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond)
5618*4b22b933Srs200217 					{
5619*4b22b933Srs200217 					if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl)
5620*4b22b933Srs200217 						LogMsg("Correcting TTL from %4d to %4d for %s",
5621*4b22b933Srs200217 							r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2));
5622*4b22b933Srs200217 					r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
5623*4b22b933Srs200217 					r2->TimeRcvd = m->timenow;
5624*4b22b933Srs200217 					}
5625*4b22b933Srs200217 				else				// else, if record is old, mark it to be flushed
5626*4b22b933Srs200217 					{
5627*4b22b933Srs200217 					verbosedebugf("Cache flush %p X %p %s", r1, r2, CRDisplayString(m, r2));
5628*4b22b933Srs200217 					// We set stale records to expire in one second.
5629*4b22b933Srs200217 					// This gives the owner a chance to rescue it if necessary.
5630*4b22b933Srs200217 					// This is important in the case of multi-homing and bridged networks:
5631*4b22b933Srs200217 					//   Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
5632*4b22b933Srs200217 					//   bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
5633*4b22b933Srs200217 					//   set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
5634*4b22b933Srs200217 					//   will promptly delete their cached copies of the (still valid) Ethernet IP address record.
5635*4b22b933Srs200217 					//   By delaying the deletion by one second, we give X a change to notice that this bridging has
5636*4b22b933Srs200217 					//   happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
5637*4b22b933Srs200217 					// We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
5638*4b22b933Srs200217 					// final expiration queries for this record.
5639*4b22b933Srs200217 					r2->resrec.rroriginalttl = 1;
5640*4b22b933Srs200217 					r2->TimeRcvd          = m->timenow;
5641*4b22b933Srs200217 					r2->UnansweredQueries = MaxUnansweredQueries;
5642*4b22b933Srs200217 					}
5643*4b22b933Srs200217 				SetNextCacheCheckTime(m, r2);
5644*4b22b933Srs200217 				}
5645*4b22b933Srs200217 		if (r1->DelayDelivery)	// If we were planning to delay delivery of this record, see if we still need to
5646*4b22b933Srs200217 			{
5647*4b22b933Srs200217 			// Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
5648*4b22b933Srs200217 			r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
5649*4b22b933Srs200217 			if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
5650*4b22b933Srs200217 			}
5651*4b22b933Srs200217 		}
5652*4b22b933Srs200217 	}
5653*4b22b933Srs200217 
mDNSCoreReceive(mDNS * const m,void * const pkt,const mDNSu8 * const end,const mDNSAddr * const srcaddr,const mDNSIPPort srcport,const mDNSAddr * const dstaddr,const mDNSIPPort dstport,const mDNSInterfaceID InterfaceID)5654*4b22b933Srs200217 mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
5655*4b22b933Srs200217 	const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
5656*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID)
5657*4b22b933Srs200217 	{
5658*4b22b933Srs200217 	DNSMessage  *msg  = (DNSMessage *)pkt;
5659*4b22b933Srs200217 	const mDNSu8 StdQ = kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery;
5660*4b22b933Srs200217 	const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
5661*4b22b933Srs200217 	mDNSu8 QR_OP;
5662*4b22b933Srs200217 	mDNSu8 *ptr = mDNSNULL;
5663*4b22b933Srs200217 	const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
5664*4b22b933Srs200217 
5665*4b22b933Srs200217 #ifndef UNICAST_DISABLED
5666*4b22b933Srs200217 	if (srcport.NotAnInteger == NATPMPPort.NotAnInteger)
5667*4b22b933Srs200217 		{
5668*4b22b933Srs200217 		mDNS_Lock(m);
5669*4b22b933Srs200217 		uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
5670*4b22b933Srs200217 		mDNS_Unlock(m);
5671*4b22b933Srs200217 		return;
5672*4b22b933Srs200217 		}
5673*4b22b933Srs200217 #endif
5674*4b22b933Srs200217 	if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; }
5675*4b22b933Srs200217 	QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
5676*4b22b933Srs200217 	// Read the integer parts which are in IETF byte-order (MSB first, LSB second)
5677*4b22b933Srs200217 	ptr = (mDNSu8 *)&msg->h.numQuestions;
5678*4b22b933Srs200217 	msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
5679*4b22b933Srs200217 	msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
5680*4b22b933Srs200217 	msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
5681*4b22b933Srs200217 	msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] <<  8 | ptr[7]);
5682*4b22b933Srs200217 
5683*4b22b933Srs200217 	if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
5684*4b22b933Srs200217 
5685*4b22b933Srs200217 	// We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
5686*4b22b933Srs200217 	// If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
5687*4b22b933Srs200217 	if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; }
5688*4b22b933Srs200217 
5689*4b22b933Srs200217 	mDNS_Lock(m);
5690*4b22b933Srs200217 	m->PktNum++;
5691*4b22b933Srs200217 #ifndef UNICAST_DISABLED
5692*4b22b933Srs200217 	if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR))
5693*4b22b933Srs200217 		uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
5694*4b22b933Srs200217 		// Note: mDNSCore also needs to get access to received unicast responses
5695*4b22b933Srs200217 #endif
5696*4b22b933Srs200217 	if      (QR_OP == StdQ) mDNSCoreReceiveQuery   (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
5697*4b22b933Srs200217 	else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
5698*4b22b933Srs200217 	else if (QR_OP != UpdateR)
5699*4b22b933Srs200217 		LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
5700*4b22b933Srs200217 			msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID);
5701*4b22b933Srs200217 
5702*4b22b933Srs200217 	// Packet reception often causes a change to the task list:
5703*4b22b933Srs200217 	// 1. Inbound queries can cause us to need to send responses
5704*4b22b933Srs200217 	// 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
5705*4b22b933Srs200217 	// 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
5706*4b22b933Srs200217 	// 4. Response packets that answer questions may cause our client to issue new questions
5707*4b22b933Srs200217 	mDNS_Unlock(m);
5708*4b22b933Srs200217 	}
5709*4b22b933Srs200217 
5710*4b22b933Srs200217 // ***************************************************************************
5711*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
5712*4b22b933Srs200217 #pragma mark -
5713*4b22b933Srs200217 #pragma mark -
5714*4b22b933Srs200217 #pragma mark - Searcher Functions
5715*4b22b933Srs200217 #endif
5716*4b22b933Srs200217 
5717*4b22b933Srs200217 #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5718*4b22b933Srs200217 
FindDuplicateQuestion(const mDNS * const m,const DNSQuestion * const question)5719*4b22b933Srs200217 mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
5720*4b22b933Srs200217 	{
5721*4b22b933Srs200217 	DNSQuestion *q;
5722*4b22b933Srs200217 	// Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
5723*4b22b933Srs200217 	// This prevents circular references, where two questions are each marked as a duplicate of the other.
5724*4b22b933Srs200217 	// Accordingly, we break out of the loop when we get to 'question', because there's no point searching
5725*4b22b933Srs200217 	// further in the list.
5726*4b22b933Srs200217 	for (q = m->Questions; q && q != question; q=q->next)		// Scan our list of questions
5727*4b22b933Srs200217 		if (q->InterfaceID == question->InterfaceID &&			// for another question with the same InterfaceID,
5728*4b22b933Srs200217 			SameQTarget(q, question)                &&			// and same unicast/multicast target settings
5729*4b22b933Srs200217 			q->qtype       == question->qtype       &&			// type,
5730*4b22b933Srs200217 			q->qclass      == question->qclass      &&			// class,
5731*4b22b933Srs200217 			q->qnamehash   == question->qnamehash   &&
5732*4b22b933Srs200217 			SameDomainName(&q->qname, &question->qname))		// and name
5733*4b22b933Srs200217 			return(q);
5734*4b22b933Srs200217 	return(mDNSNULL);
5735*4b22b933Srs200217 	}
5736*4b22b933Srs200217 
5737*4b22b933Srs200217 // This is called after a question is deleted, in case other identical questions were being
5738*4b22b933Srs200217 // suppressed as duplicates
UpdateQuestionDuplicates(mDNS * const m,const DNSQuestion * const question)5739*4b22b933Srs200217 mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question)
5740*4b22b933Srs200217 	{
5741*4b22b933Srs200217 	DNSQuestion *q;
5742*4b22b933Srs200217 	for (q = m->Questions; q; q=q->next)		// Scan our list of questions
5743*4b22b933Srs200217 		if (q->DuplicateOf == question)			// To see if any questions were referencing this as their duplicate
5744*4b22b933Srs200217 			{
5745*4b22b933Srs200217 			q->ThisQInterval    = question->ThisQInterval;
5746*4b22b933Srs200217 			q->RequestUnicast   = question->RequestUnicast;
5747*4b22b933Srs200217 			q->LastQTime        = question->LastQTime;
5748*4b22b933Srs200217 			q->RecentAnswerPkts = 0;
5749*4b22b933Srs200217 			q->DuplicateOf      = FindDuplicateQuestion(m, q);
5750*4b22b933Srs200217 			q->LastQTxTime      = question->LastQTxTime;
5751*4b22b933Srs200217 			SetNextQueryTime(m,q);
5752*4b22b933Srs200217 			}
5753*4b22b933Srs200217 	}
5754*4b22b933Srs200217 
5755*4b22b933Srs200217 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
5756*4b22b933Srs200217 	((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger))
5757*4b22b933Srs200217 
mDNS_StartQuery_internal(mDNS * const m,DNSQuestion * const question)5758*4b22b933Srs200217 mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
5759*4b22b933Srs200217 	{
5760*4b22b933Srs200217 	if (question->Target.type && !ValidQuestionTarget(question))
5761*4b22b933Srs200217 		{
5762*4b22b933Srs200217 		LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5763*4b22b933Srs200217 			question->Target.type, mDNSVal16(question->TargetPort));
5764*4b22b933Srs200217 		question->Target.type = mDNSAddrType_None;
5765*4b22b933Srs200217 		}
5766*4b22b933Srs200217 
5767*4b22b933Srs200217 	if (!question->Target.type)		// No question->Target specified, so clear TargetPort and TargetQID
5768*4b22b933Srs200217 		{
5769*4b22b933Srs200217 		question->TargetPort = zeroIPPort;
5770*4b22b933Srs200217 		question->TargetQID  = zeroID;
5771*4b22b933Srs200217 		}
5772*4b22b933Srs200217 
5773*4b22b933Srs200217 #ifndef UNICAST_DISABLED
5774*4b22b933Srs200217 	// If the client has specified 'kDNSServiceFlagsForceMulticast'
5775*4b22b933Srs200217 	// then we do a multicast query on that interface, even for unicast domains.
5776*4b22b933Srs200217     if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5777*4b22b933Srs200217     	question->uDNS_info.id = zeroID;
5778*4b22b933Srs200217     else return uDNS_StartQuery(m, question);
5779*4b22b933Srs200217 #else
5780*4b22b933Srs200217     question->uDNS_info.id = zeroID;
5781*4b22b933Srs200217 #endif // UNICAST_DISABLED
5782*4b22b933Srs200217 
5783*4b22b933Srs200217 	//LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5784*4b22b933Srs200217 
5785*4b22b933Srs200217 	if (m->rrcache_size == 0)	// Can't do queries if we have no cache space allocated
5786*4b22b933Srs200217 		return(mStatus_NoCache);
5787*4b22b933Srs200217 	else
5788*4b22b933Srs200217 		{
5789*4b22b933Srs200217 		int i;
5790*4b22b933Srs200217 		// Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5791*4b22b933Srs200217 		DNSQuestion **q = &m->Questions;
5792*4b22b933Srs200217 		if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5793*4b22b933Srs200217 		while (*q && *q != question) q=&(*q)->next;
5794*4b22b933Srs200217 
5795*4b22b933Srs200217 		if (*q)
5796*4b22b933Srs200217 			{
5797*4b22b933Srs200217 			LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5798*4b22b933Srs200217 				question->qname.c, DNSTypeName(question->qtype));
5799*4b22b933Srs200217 			return(mStatus_AlreadyRegistered);
5800*4b22b933Srs200217 			}
5801*4b22b933Srs200217 
5802*4b22b933Srs200217 		// If this question is referencing a specific interface, verify it exists
5803*4b22b933Srs200217 		if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly)
5804*4b22b933Srs200217 			{
5805*4b22b933Srs200217 			NetworkInterfaceInfo *intf;
5806*4b22b933Srs200217 			for (intf = m->HostInterfaces; intf; intf = intf->next)
5807*4b22b933Srs200217 				if (intf->InterfaceID == question->InterfaceID) break;
5808*4b22b933Srs200217 			if (!intf)
5809*4b22b933Srs200217 				LogMsg("Note: InterfaceID %p for question %##s not currently found in active interface list",
5810*4b22b933Srs200217 					question->InterfaceID, question->qname.c);
5811*4b22b933Srs200217 			}
5812*4b22b933Srs200217 
5813*4b22b933Srs200217 		if (!ValidateDomainName(&question->qname))
5814*4b22b933Srs200217 			{
5815*4b22b933Srs200217 			LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5816*4b22b933Srs200217 			return(mStatus_Invalid);
5817*4b22b933Srs200217 			}
5818*4b22b933Srs200217 
5819*4b22b933Srs200217 		// Note: In the case where we already have the answer to this question in our cache, that may be all the client
5820*4b22b933Srs200217 		// wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5821*4b22b933Srs200217 		// be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds
5822*4b22b933Srs200217 		// that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately.
5823*4b22b933Srs200217 		if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
5824*4b22b933Srs200217 
5825*4b22b933Srs200217 		question->next              = mDNSNULL;
5826*4b22b933Srs200217 		question->qnamehash         = DomainNameHashValue(&question->qname);	// MUST do this before FindDuplicateQuestion()
5827*4b22b933Srs200217 		question->DelayAnswering    = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname));
5828*4b22b933Srs200217 		question->ThisQInterval     = InitialQuestionInterval * 2;			// MUST be > zero for an active question
5829*4b22b933Srs200217 		question->RequestUnicast    = 2;										// Set to 2 because is decremented once *before* we check it
5830*4b22b933Srs200217 		question->LastQTime         = m->timenow - m->RandomQueryDelay;		// Avoid inter-machine synchronization
5831*4b22b933Srs200217 		question->LastAnswerPktNum  = m->PktNum;
5832*4b22b933Srs200217 		question->RecentAnswerPkts  = 0;
5833*4b22b933Srs200217 		question->CurrentAnswers    = 0;
5834*4b22b933Srs200217 		question->LargeAnswers      = 0;
5835*4b22b933Srs200217 		question->UniqueAnswers     = 0;
5836*4b22b933Srs200217 		question->FlappingInterface = mDNSNULL;
5837*4b22b933Srs200217 		question->DuplicateOf       = FindDuplicateQuestion(m, question);
5838*4b22b933Srs200217 		question->NextInDQList      = mDNSNULL;
5839*4b22b933Srs200217 		for (i=0; i<DupSuppressInfoSize; i++)
5840*4b22b933Srs200217 			question->DupSuppress[i].InterfaceID = mDNSNULL;
5841*4b22b933Srs200217 		// question->InterfaceID must be already set by caller
5842*4b22b933Srs200217 		question->SendQNow          = mDNSNULL;
5843*4b22b933Srs200217 		question->SendOnAll         = mDNSfalse;
5844*4b22b933Srs200217 		question->LastQTxTime       = m->timenow;
5845*4b22b933Srs200217 
5846*4b22b933Srs200217 		if (!question->DuplicateOf)
5847*4b22b933Srs200217 			verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
5848*4b22b933Srs200217 				question->qname.c, DNSTypeName(question->qtype), question->InterfaceID,
5849*4b22b933Srs200217 				question->LastQTime + question->ThisQInterval - m->timenow, question);
5850*4b22b933Srs200217 		else
5851*4b22b933Srs200217 			verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5852*4b22b933Srs200217 				question->qname.c, DNSTypeName(question->qtype), question->InterfaceID,
5853*4b22b933Srs200217 				question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf);
5854*4b22b933Srs200217 
5855*4b22b933Srs200217 		*q = question;
5856*4b22b933Srs200217 		if (question->InterfaceID == mDNSInterface_LocalOnly)
5857*4b22b933Srs200217 			{
5858*4b22b933Srs200217 			if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
5859*4b22b933Srs200217 			}
5860*4b22b933Srs200217 		else
5861*4b22b933Srs200217 			{
5862*4b22b933Srs200217 			if (!m->NewQuestions) m->NewQuestions = question;
5863*4b22b933Srs200217 			SetNextQueryTime(m,question);
5864*4b22b933Srs200217 			}
5865*4b22b933Srs200217 
5866*4b22b933Srs200217 		return(mStatus_NoError);
5867*4b22b933Srs200217 		}
5868*4b22b933Srs200217 	}
5869*4b22b933Srs200217 
mDNS_StopQuery_internal(mDNS * const m,DNSQuestion * const question)5870*4b22b933Srs200217 mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
5871*4b22b933Srs200217 	{
5872*4b22b933Srs200217 	const mDNSu32 slot = HashSlot(&question->qname);
5873*4b22b933Srs200217 	CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
5874*4b22b933Srs200217 	CacheRecord *rr;
5875*4b22b933Srs200217 	DNSQuestion **q = &m->Questions;
5876*4b22b933Srs200217 
5877*4b22b933Srs200217     if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question);
5878*4b22b933Srs200217 
5879*4b22b933Srs200217 	if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5880*4b22b933Srs200217 	while (*q && *q != question) q=&(*q)->next;
5881*4b22b933Srs200217 	if (*q) *q = (*q)->next;
5882*4b22b933Srs200217 	else
5883*4b22b933Srs200217 		{
5884*4b22b933Srs200217 		if (question->ThisQInterval >= 0)	// Only log error message if the query was supposed to be active
5885*4b22b933Srs200217 			LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5886*4b22b933Srs200217 				question->qname.c, DNSTypeName(question->qtype));
5887*4b22b933Srs200217 		return(mStatus_BadReferenceErr);
5888*4b22b933Srs200217 		}
5889*4b22b933Srs200217 
5890*4b22b933Srs200217 	// Take care to cut question from list *before* calling UpdateQuestionDuplicates
5891*4b22b933Srs200217 	UpdateQuestionDuplicates(m, question);
5892*4b22b933Srs200217 	// But don't trash ThisQInterval until afterwards.
5893*4b22b933Srs200217 	question->ThisQInterval = -1;
5894*4b22b933Srs200217 
5895*4b22b933Srs200217 	// If there are any cache records referencing this as their active question, then see if any other
5896*4b22b933Srs200217 	// question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5897*4b22b933Srs200217 	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5898*4b22b933Srs200217 		{
5899*4b22b933Srs200217 		if (rr->CRActiveQuestion == question)
5900*4b22b933Srs200217 			{
5901*4b22b933Srs200217 			DNSQuestion *q;
5902*4b22b933Srs200217 			for (q = m->Questions; q; q=q->next)		// Scan our list of questions
5903*4b22b933Srs200217 				if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
5904*4b22b933Srs200217 					break;
5905*4b22b933Srs200217 			verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p",
5906*4b22b933Srs200217 				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q);
5907*4b22b933Srs200217 			rr->CRActiveQuestion = q;		// Question used to be active; new value may or may not be null
5908*4b22b933Srs200217 			if (!q) m->rrcache_active--;	// If no longer active, decrement rrcache_active count
5909*4b22b933Srs200217 			}
5910*4b22b933Srs200217 		}
5911*4b22b933Srs200217 
5912*4b22b933Srs200217 	// If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at,
5913*4b22b933Srs200217 	// bump its pointer forward one question.
5914*4b22b933Srs200217 	if (m->CurrentQuestion == question)
5915*4b22b933Srs200217 		{
5916*4b22b933Srs200217 		debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5917*4b22b933Srs200217 			question->qname.c, DNSTypeName(question->qtype));
5918*4b22b933Srs200217 		m->CurrentQuestion = question->next;
5919*4b22b933Srs200217 		}
5920*4b22b933Srs200217 
5921*4b22b933Srs200217 	if (m->NewQuestions == question)
5922*4b22b933Srs200217 		{
5923*4b22b933Srs200217 		debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5924*4b22b933Srs200217 			question->qname.c, DNSTypeName(question->qtype));
5925*4b22b933Srs200217 		m->NewQuestions = question->next;
5926*4b22b933Srs200217 		}
5927*4b22b933Srs200217 
5928*4b22b933Srs200217 	if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
5929*4b22b933Srs200217 
5930*4b22b933Srs200217 	// Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5931*4b22b933Srs200217 	question->next = mDNSNULL;
5932*4b22b933Srs200217 	return(mStatus_NoError);
5933*4b22b933Srs200217 	}
5934*4b22b933Srs200217 
mDNS_StartQuery(mDNS * const m,DNSQuestion * const question)5935*4b22b933Srs200217 mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
5936*4b22b933Srs200217 	{
5937*4b22b933Srs200217 	mStatus status;
5938*4b22b933Srs200217 	mDNS_Lock(m);
5939*4b22b933Srs200217 	status = mDNS_StartQuery_internal(m, question);
5940*4b22b933Srs200217 	mDNS_Unlock(m);
5941*4b22b933Srs200217 	return(status);
5942*4b22b933Srs200217 	}
5943*4b22b933Srs200217 
mDNS_StopQuery(mDNS * const m,DNSQuestion * const question)5944*4b22b933Srs200217 mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
5945*4b22b933Srs200217 	{
5946*4b22b933Srs200217 	mStatus status;
5947*4b22b933Srs200217 	mDNS_Lock(m);
5948*4b22b933Srs200217 	status = mDNS_StopQuery_internal(m, question);
5949*4b22b933Srs200217 	mDNS_Unlock(m);
5950*4b22b933Srs200217 	return(status);
5951*4b22b933Srs200217 	}
5952*4b22b933Srs200217 
mDNS_Reconfirm(mDNS * const m,CacheRecord * const rr)5953*4b22b933Srs200217 mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr)
5954*4b22b933Srs200217 	{
5955*4b22b933Srs200217 	mStatus status;
5956*4b22b933Srs200217 	mDNS_Lock(m);
5957*4b22b933Srs200217 	status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
5958*4b22b933Srs200217 	mDNS_Unlock(m);
5959*4b22b933Srs200217 	return(status);
5960*4b22b933Srs200217 	}
5961*4b22b933Srs200217 
mDNS_ReconfirmByValue(mDNS * const m,ResourceRecord * const rr)5962*4b22b933Srs200217 mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
5963*4b22b933Srs200217 	{
5964*4b22b933Srs200217 	mStatus status = mStatus_BadReferenceErr;
5965*4b22b933Srs200217 	CacheRecord *cr;
5966*4b22b933Srs200217 	mDNS_Lock(m);
5967*4b22b933Srs200217 	cr = FindIdenticalRecordInCache(m, rr);
5968*4b22b933Srs200217 	if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
5969*4b22b933Srs200217 	mDNS_Unlock(m);
5970*4b22b933Srs200217 	return(status);
5971*4b22b933Srs200217 	}
5972*4b22b933Srs200217 
mDNS_StartBrowse(mDNS * const m,DNSQuestion * const question,const domainname * const srv,const domainname * const domain,const mDNSInterfaceID InterfaceID,mDNSBool ForceMCast,mDNSQuestionCallback * Callback,void * Context)5973*4b22b933Srs200217 mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
5974*4b22b933Srs200217 	const domainname *const srv, const domainname *const domain,
5975*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context)
5976*4b22b933Srs200217 	{
5977*4b22b933Srs200217 	question->InterfaceID      = InterfaceID;
5978*4b22b933Srs200217 	question->Target           = zeroAddr;
5979*4b22b933Srs200217 	question->qtype            = kDNSType_PTR;
5980*4b22b933Srs200217 	question->qclass           = kDNSClass_IN;
5981*4b22b933Srs200217 	question->LongLived        = mDNSfalse;
5982*4b22b933Srs200217 	question->ExpectUnique     = mDNSfalse;
5983*4b22b933Srs200217 	question->ForceMCast       = ForceMCast;
5984*4b22b933Srs200217 	question->QuestionCallback = Callback;
5985*4b22b933Srs200217 	question->QuestionContext  = Context;
5986*4b22b933Srs200217 	if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
5987*4b22b933Srs200217 
5988*4b22b933Srs200217 #ifndef UNICAST_DISABLED
5989*4b22b933Srs200217     if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5990*4b22b933Srs200217     	{
5991*4b22b933Srs200217 		question->LongLived = mDNSfalse;
5992*4b22b933Srs200217 		question->uDNS_info.id = zeroID;
5993*4b22b933Srs200217 		return(mDNS_StartQuery(m, question));
5994*4b22b933Srs200217 		}
5995*4b22b933Srs200217 	else
5996*4b22b933Srs200217 		{
5997*4b22b933Srs200217 		mStatus status;
5998*4b22b933Srs200217 		// Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5999*4b22b933Srs200217 		mDNS_Lock(m);
6000*4b22b933Srs200217 		question->LongLived = mDNStrue;
6001*4b22b933Srs200217 		status = uDNS_StartQuery(m, question);
6002*4b22b933Srs200217 		mDNS_Unlock(m);
6003*4b22b933Srs200217 		return(status);
6004*4b22b933Srs200217 		}
6005*4b22b933Srs200217 #else
6006*4b22b933Srs200217 	return(mDNS_StartQuery(m, question));
6007*4b22b933Srs200217 #endif // UNICAST_DISABLED
6008*4b22b933Srs200217 	}
6009*4b22b933Srs200217 
MachineHasActiveIPv6(mDNS * const m)6010*4b22b933Srs200217 mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m)
6011*4b22b933Srs200217 	{
6012*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
6013*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
6014*4b22b933Srs200217 	if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue);
6015*4b22b933Srs200217 	return(mDNSfalse);
6016*4b22b933Srs200217 	}
6017*4b22b933Srs200217 
FoundServiceInfoSRV(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)6018*4b22b933Srs200217 mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6019*4b22b933Srs200217 	{
6020*4b22b933Srs200217 	ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
6021*4b22b933Srs200217 	mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger);
6022*4b22b933Srs200217 	if (!AddRecord) return;
6023*4b22b933Srs200217 	if (answer->rrtype != kDNSType_SRV) return;
6024*4b22b933Srs200217 
6025*4b22b933Srs200217 	query->info->port = answer->rdata->u.srv.port;
6026*4b22b933Srs200217 
6027*4b22b933Srs200217 	// If this is our first answer, then set the GotSRV flag and start the address query
6028*4b22b933Srs200217 	if (!query->GotSRV)
6029*4b22b933Srs200217 		{
6030*4b22b933Srs200217 		query->GotSRV             = mDNStrue;
6031*4b22b933Srs200217 		query->qAv4.InterfaceID   = answer->InterfaceID;
6032*4b22b933Srs200217 		AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
6033*4b22b933Srs200217 		query->qAv6.InterfaceID   = answer->InterfaceID;
6034*4b22b933Srs200217 		AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
6035*4b22b933Srs200217 		mDNS_StartQuery(m, &query->qAv4);
6036*4b22b933Srs200217 		// Only do the AAAA query if this machine actually has IPv6 active
6037*4b22b933Srs200217 		if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6);
6038*4b22b933Srs200217 		}
6039*4b22b933Srs200217 	// If this is not our first answer, only re-issue the address query if the target host name has changed
6040*4b22b933Srs200217 	else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) ||
6041*4b22b933Srs200217 		!SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target))
6042*4b22b933Srs200217 		{
6043*4b22b933Srs200217 		mDNS_StopQuery(m, &query->qAv4);
6044*4b22b933Srs200217 		if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6);
6045*4b22b933Srs200217 		if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged)
6046*4b22b933Srs200217 			{
6047*4b22b933Srs200217 			// If we get here, it means:
6048*4b22b933Srs200217 			// 1. This is not our first SRV answer
6049*4b22b933Srs200217 			// 2. The interface ID is different, but the target host and port are the same
6050*4b22b933Srs200217 			// This implies that we're seeing the exact same SRV record on more than one interface, so we should
6051*4b22b933Srs200217 			// make our address queries at least as broad as the original SRV query so that we catch all the answers.
6052*4b22b933Srs200217 			query->qAv4.InterfaceID = query->qSRV.InterfaceID;	// Will be mDNSInterface_Any, or a specific interface
6053*4b22b933Srs200217 			query->qAv6.InterfaceID = query->qSRV.InterfaceID;
6054*4b22b933Srs200217 			}
6055*4b22b933Srs200217 		else
6056*4b22b933Srs200217 			{
6057*4b22b933Srs200217 			query->qAv4.InterfaceID   = answer->InterfaceID;
6058*4b22b933Srs200217 			AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
6059*4b22b933Srs200217 			query->qAv6.InterfaceID   = answer->InterfaceID;
6060*4b22b933Srs200217 			AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
6061*4b22b933Srs200217 			}
6062*4b22b933Srs200217 		debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c);
6063*4b22b933Srs200217 		mDNS_StartQuery(m, &query->qAv4);
6064*4b22b933Srs200217 		// Only do the AAAA query if this machine actually has IPv6 active
6065*4b22b933Srs200217 		if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6);
6066*4b22b933Srs200217 		}
6067*4b22b933Srs200217 	else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
6068*4b22b933Srs200217 		{
6069*4b22b933Srs200217 		if (++query->Answers >= 100)
6070*4b22b933Srs200217 			debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
6071*4b22b933Srs200217 				query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c,
6072*4b22b933Srs200217 				mDNSVal16(answer->rdata->u.srv.port));
6073*4b22b933Srs200217 		query->ServiceInfoQueryCallback(m, query);
6074*4b22b933Srs200217 		}
6075*4b22b933Srs200217 	// CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
6076*4b22b933Srs200217 	// callback function is allowed to do anything, including deleting this query and freeing its memory.
6077*4b22b933Srs200217 	}
6078*4b22b933Srs200217 
FoundServiceInfoTXT(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)6079*4b22b933Srs200217 mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6080*4b22b933Srs200217 	{
6081*4b22b933Srs200217 	ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
6082*4b22b933Srs200217 	if (!AddRecord) return;
6083*4b22b933Srs200217 	if (answer->rrtype != kDNSType_TXT) return;
6084*4b22b933Srs200217 	if (answer->rdlength > sizeof(query->info->TXTinfo)) return;
6085*4b22b933Srs200217 
6086*4b22b933Srs200217 	query->GotTXT       = mDNStrue;
6087*4b22b933Srs200217 	query->info->TXTlen = answer->rdlength;
6088*4b22b933Srs200217 	query->info->TXTinfo[0] = 0;		// In case answer->rdlength is zero
6089*4b22b933Srs200217 	mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength);
6090*4b22b933Srs200217 
6091*4b22b933Srs200217 	verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
6092*4b22b933Srs200217 
6093*4b22b933Srs200217 	// CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
6094*4b22b933Srs200217 	// callback function is allowed to do anything, including deleting this query and freeing its memory.
6095*4b22b933Srs200217 	if (query->ServiceInfoQueryCallback && query->GotADD)
6096*4b22b933Srs200217 		{
6097*4b22b933Srs200217 		if (++query->Answers >= 100)
6098*4b22b933Srs200217 			debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
6099*4b22b933Srs200217 				query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c);
6100*4b22b933Srs200217 		query->ServiceInfoQueryCallback(m, query);
6101*4b22b933Srs200217 		}
6102*4b22b933Srs200217 	}
6103*4b22b933Srs200217 
FoundServiceInfo(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)6104*4b22b933Srs200217 mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
6105*4b22b933Srs200217 	{
6106*4b22b933Srs200217 	ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
6107*4b22b933Srs200217 	//LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
6108*4b22b933Srs200217 	if (!AddRecord) return;
6109*4b22b933Srs200217 
6110*4b22b933Srs200217 	if (answer->rrtype == kDNSType_A)
6111*4b22b933Srs200217 		{
6112*4b22b933Srs200217 		query->info->ip.type = mDNSAddrType_IPv4;
6113*4b22b933Srs200217 		query->info->ip.ip.v4 = answer->rdata->u.ipv4;
6114*4b22b933Srs200217 		}
6115*4b22b933Srs200217 	else if (answer->rrtype == kDNSType_AAAA)
6116*4b22b933Srs200217 		{
6117*4b22b933Srs200217 		query->info->ip.type = mDNSAddrType_IPv6;
6118*4b22b933Srs200217 		query->info->ip.ip.v6 = answer->rdata->u.ipv6;
6119*4b22b933Srs200217 		}
6120*4b22b933Srs200217 	else
6121*4b22b933Srs200217 		{
6122*4b22b933Srs200217 		debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype));
6123*4b22b933Srs200217 		return;
6124*4b22b933Srs200217 		}
6125*4b22b933Srs200217 
6126*4b22b933Srs200217 	query->GotADD = mDNStrue;
6127*4b22b933Srs200217 	query->info->InterfaceID = answer->InterfaceID;
6128*4b22b933Srs200217 
6129*4b22b933Srs200217 	verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
6130*4b22b933Srs200217 
6131*4b22b933Srs200217 	// CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
6132*4b22b933Srs200217 	// callback function is allowed to do anything, including deleting this query and freeing its memory.
6133*4b22b933Srs200217 	if (query->ServiceInfoQueryCallback && query->GotTXT)
6134*4b22b933Srs200217 		{
6135*4b22b933Srs200217 		if (++query->Answers >= 100)
6136*4b22b933Srs200217 			debugf(answer->rrtype == kDNSType_A ?
6137*4b22b933Srs200217 				"**** WARNING **** have given %lu answers for %##s (A) %.4a" :
6138*4b22b933Srs200217 				"**** WARNING **** have given %lu answers for %##s (AAAA) %.16a",
6139*4b22b933Srs200217 				query->Answers, query->qSRV.qname.c, &answer->rdata->u.data);
6140*4b22b933Srs200217 		query->ServiceInfoQueryCallback(m, query);
6141*4b22b933Srs200217 		}
6142*4b22b933Srs200217 	}
6143*4b22b933Srs200217 
6144*4b22b933Srs200217 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
6145*4b22b933Srs200217 // If the query is not interface-specific, then InterfaceID may be zero
6146*4b22b933Srs200217 // Each time the Callback is invoked, the remainder of the fields will have been filled in
6147*4b22b933Srs200217 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
mDNS_StartResolveService(mDNS * const m,ServiceInfoQuery * query,ServiceInfo * info,mDNSServiceInfoQueryCallback * Callback,void * Context)6148*4b22b933Srs200217 mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
6149*4b22b933Srs200217 	ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context)
6150*4b22b933Srs200217 	{
6151*4b22b933Srs200217 	mStatus status;
6152*4b22b933Srs200217 	mDNS_Lock(m);
6153*4b22b933Srs200217 
6154*4b22b933Srs200217 	query->qSRV.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
6155*4b22b933Srs200217 	query->qSRV.InterfaceID         = info->InterfaceID;
6156*4b22b933Srs200217 	query->qSRV.Target              = zeroAddr;
6157*4b22b933Srs200217 	AssignDomainName(&query->qSRV.qname, &info->name);
6158*4b22b933Srs200217 	query->qSRV.qtype               = kDNSType_SRV;
6159*4b22b933Srs200217 	query->qSRV.qclass              = kDNSClass_IN;
6160*4b22b933Srs200217 	query->qSRV.LongLived           = mDNSfalse;
6161*4b22b933Srs200217 	query->qSRV.ExpectUnique        = mDNStrue;
6162*4b22b933Srs200217 	query->qSRV.ForceMCast          = mDNSfalse;
6163*4b22b933Srs200217 	query->qSRV.QuestionCallback    = FoundServiceInfoSRV;
6164*4b22b933Srs200217 	query->qSRV.QuestionContext     = query;
6165*4b22b933Srs200217 
6166*4b22b933Srs200217 	query->qTXT.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
6167*4b22b933Srs200217 	query->qTXT.InterfaceID         = info->InterfaceID;
6168*4b22b933Srs200217 	query->qTXT.Target              = zeroAddr;
6169*4b22b933Srs200217 	AssignDomainName(&query->qTXT.qname, &info->name);
6170*4b22b933Srs200217 	query->qTXT.qtype               = kDNSType_TXT;
6171*4b22b933Srs200217 	query->qTXT.qclass              = kDNSClass_IN;
6172*4b22b933Srs200217 	query->qTXT.LongLived           = mDNSfalse;
6173*4b22b933Srs200217 	query->qTXT.ExpectUnique        = mDNStrue;
6174*4b22b933Srs200217 	query->qTXT.ForceMCast          = mDNSfalse;
6175*4b22b933Srs200217 	query->qTXT.QuestionCallback    = FoundServiceInfoTXT;
6176*4b22b933Srs200217 	query->qTXT.QuestionContext     = query;
6177*4b22b933Srs200217 
6178*4b22b933Srs200217 	query->qAv4.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
6179*4b22b933Srs200217 	query->qAv4.InterfaceID         = info->InterfaceID;
6180*4b22b933Srs200217 	query->qAv4.Target              = zeroAddr;
6181*4b22b933Srs200217 	query->qAv4.qname.c[0]          = 0;
6182*4b22b933Srs200217 	query->qAv4.qtype               = kDNSType_A;
6183*4b22b933Srs200217 	query->qAv4.qclass              = kDNSClass_IN;
6184*4b22b933Srs200217 	query->qAv4.LongLived           = mDNSfalse;
6185*4b22b933Srs200217 	query->qAv4.ExpectUnique        = mDNStrue;
6186*4b22b933Srs200217 	query->qAv4.ForceMCast          = mDNSfalse;
6187*4b22b933Srs200217 	query->qAv4.QuestionCallback    = FoundServiceInfo;
6188*4b22b933Srs200217 	query->qAv4.QuestionContext     = query;
6189*4b22b933Srs200217 
6190*4b22b933Srs200217 	query->qAv6.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
6191*4b22b933Srs200217 	query->qAv6.InterfaceID         = info->InterfaceID;
6192*4b22b933Srs200217 	query->qAv6.Target              = zeroAddr;
6193*4b22b933Srs200217 	query->qAv6.qname.c[0]          = 0;
6194*4b22b933Srs200217 	query->qAv6.qtype               = kDNSType_AAAA;
6195*4b22b933Srs200217 	query->qAv6.qclass              = kDNSClass_IN;
6196*4b22b933Srs200217 	query->qAv6.LongLived           = mDNSfalse;
6197*4b22b933Srs200217 	query->qAv6.ExpectUnique        = mDNStrue;
6198*4b22b933Srs200217 	query->qAv6.ForceMCast          = mDNSfalse;
6199*4b22b933Srs200217 	query->qAv6.QuestionCallback    = FoundServiceInfo;
6200*4b22b933Srs200217 	query->qAv6.QuestionContext     = query;
6201*4b22b933Srs200217 
6202*4b22b933Srs200217 	query->GotSRV                   = mDNSfalse;
6203*4b22b933Srs200217 	query->GotTXT                   = mDNSfalse;
6204*4b22b933Srs200217 	query->GotADD                   = mDNSfalse;
6205*4b22b933Srs200217 	query->Answers                  = 0;
6206*4b22b933Srs200217 
6207*4b22b933Srs200217 	query->info                     = info;
6208*4b22b933Srs200217 	query->ServiceInfoQueryCallback = Callback;
6209*4b22b933Srs200217 	query->ServiceInfoQueryContext  = Context;
6210*4b22b933Srs200217 
6211*4b22b933Srs200217 //	info->name      = Must already be set up by client
6212*4b22b933Srs200217 //	info->interface = Must already be set up by client
6213*4b22b933Srs200217 	info->ip        = zeroAddr;
6214*4b22b933Srs200217 	info->port      = zeroIPPort;
6215*4b22b933Srs200217 	info->TXTlen    = 0;
6216*4b22b933Srs200217 
6217*4b22b933Srs200217 	// We use mDNS_StartQuery_internal here because we're already holding the lock
6218*4b22b933Srs200217 	status = mDNS_StartQuery_internal(m, &query->qSRV);
6219*4b22b933Srs200217 	if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT);
6220*4b22b933Srs200217 	if (status != mStatus_NoError) mDNS_StopResolveService(m, query);
6221*4b22b933Srs200217 
6222*4b22b933Srs200217 	mDNS_Unlock(m);
6223*4b22b933Srs200217 	return(status);
6224*4b22b933Srs200217 	}
6225*4b22b933Srs200217 
mDNS_StopResolveService(mDNS * const m,ServiceInfoQuery * q)6226*4b22b933Srs200217 mDNSexport void    mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q)
6227*4b22b933Srs200217 	{
6228*4b22b933Srs200217 	mDNS_Lock(m);
6229*4b22b933Srs200217 	// We use mDNS_StopQuery_internal here because we're already holding the lock
6230*4b22b933Srs200217 	if (q->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qSRV);
6231*4b22b933Srs200217 	if (q->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qTXT);
6232*4b22b933Srs200217 	if (q->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv4);
6233*4b22b933Srs200217 	if (q->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv6);
6234*4b22b933Srs200217 	mDNS_Unlock(m);
6235*4b22b933Srs200217 	}
6236*4b22b933Srs200217 
mDNS_GetDomains(mDNS * const m,DNSQuestion * const question,mDNS_DomainType DomainType,const domainname * dom,const mDNSInterfaceID InterfaceID,mDNSQuestionCallback * Callback,void * Context)6237*4b22b933Srs200217 mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
6238*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
6239*4b22b933Srs200217 	{
6240*4b22b933Srs200217 	question->InterfaceID      = InterfaceID;
6241*4b22b933Srs200217 	question->Target           = zeroAddr;
6242*4b22b933Srs200217 	question->qtype            = kDNSType_PTR;
6243*4b22b933Srs200217 	question->qclass           = kDNSClass_IN;
6244*4b22b933Srs200217 	question->LongLived        = mDNSfalse;
6245*4b22b933Srs200217 	question->ExpectUnique     = mDNSfalse;
6246*4b22b933Srs200217 	question->ForceMCast       = mDNSfalse;
6247*4b22b933Srs200217 	question->QuestionCallback = Callback;
6248*4b22b933Srs200217 	question->QuestionContext  = Context;
6249*4b22b933Srs200217 	if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
6250*4b22b933Srs200217 	if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
6251*4b22b933Srs200217 	if (!dom) dom = &localdomain;
6252*4b22b933Srs200217 	if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr);
6253*4b22b933Srs200217 	return(mDNS_StartQuery(m, question));
6254*4b22b933Srs200217 	}
6255*4b22b933Srs200217 
6256*4b22b933Srs200217 // ***************************************************************************
6257*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
6258*4b22b933Srs200217 #pragma mark -
6259*4b22b933Srs200217 #pragma mark - Responder Functions
6260*4b22b933Srs200217 #endif
6261*4b22b933Srs200217 
mDNS_Register(mDNS * const m,AuthRecord * const rr)6262*4b22b933Srs200217 mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
6263*4b22b933Srs200217 	{
6264*4b22b933Srs200217 	mStatus status;
6265*4b22b933Srs200217 	mDNS_Lock(m);
6266*4b22b933Srs200217 	status = mDNS_Register_internal(m, rr);
6267*4b22b933Srs200217 	mDNS_Unlock(m);
6268*4b22b933Srs200217 	return(status);
6269*4b22b933Srs200217 	}
6270*4b22b933Srs200217 
mDNS_Update(mDNS * const m,AuthRecord * const rr,mDNSu32 newttl,const mDNSu16 newrdlength,RData * const newrdata,mDNSRecordUpdateCallback * Callback)6271*4b22b933Srs200217 mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
6272*4b22b933Srs200217 	const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback)
6273*4b22b933Srs200217 	{
6274*4b22b933Srs200217 #ifndef UNICAST_DISABLED
6275*4b22b933Srs200217 	mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name));
6276*4b22b933Srs200217 #else
6277*4b22b933Srs200217 	mDNSBool unicast = mDNSfalse;
6278*4b22b933Srs200217 #endif
6279*4b22b933Srs200217 
6280*4b22b933Srs200217 	if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata))
6281*4b22b933Srs200217 		{
6282*4b22b933Srs200217 		LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer));
6283*4b22b933Srs200217 		return(mStatus_Invalid);
6284*4b22b933Srs200217 		}
6285*4b22b933Srs200217 
6286*4b22b933Srs200217 	mDNS_Lock(m);
6287*4b22b933Srs200217 
6288*4b22b933Srs200217 	// If TTL is unspecified, leave TTL unchanged
6289*4b22b933Srs200217 	if (newttl == 0) newttl = rr->resrec.rroriginalttl;
6290*4b22b933Srs200217 
6291*4b22b933Srs200217 	// If we already have an update queued up which has not gone through yet,
6292*4b22b933Srs200217 	// give the client a chance to free that memory
6293*4b22b933Srs200217 	if (!unicast && rr->NewRData)
6294*4b22b933Srs200217 		{
6295*4b22b933Srs200217 		RData *n = rr->NewRData;
6296*4b22b933Srs200217 		rr->NewRData = mDNSNULL;			// Clear the NewRData pointer ...
6297*4b22b933Srs200217 		if (rr->UpdateCallback)
6298*4b22b933Srs200217 			rr->UpdateCallback(m, rr, n);	// ...and let the client free this memory, if necessary
6299*4b22b933Srs200217 		}
6300*4b22b933Srs200217 
6301*4b22b933Srs200217 	rr->NewRData             = newrdata;
6302*4b22b933Srs200217 	rr->newrdlength          = newrdlength;
6303*4b22b933Srs200217 	rr->UpdateCallback       = Callback;
6304*4b22b933Srs200217 
6305*4b22b933Srs200217 	if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); }
6306*4b22b933Srs200217 
6307*4b22b933Srs200217 	if (rr->resrec.rroriginalttl == newttl &&
6308*4b22b933Srs200217 		rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
6309*4b22b933Srs200217 		CompleteRDataUpdate(m, rr);
6310*4b22b933Srs200217 	else
6311*4b22b933Srs200217 		{
6312*4b22b933Srs200217 		domainlabel name;
6313*4b22b933Srs200217 		domainname type, domain;
6314*4b22b933Srs200217 		DeconstructServiceName(rr->resrec.name, &name, &type, &domain);
6315*4b22b933Srs200217 		rr->AnnounceCount = InitialAnnounceCount;
6316*4b22b933Srs200217 		// iChat often does suprious record updates where no data has changed. For the _presence service type, using
6317*4b22b933Srs200217 		// name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
6318*4b22b933Srs200217 		// update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
6319*4b22b933Srs200217 		// even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
6320*4b22b933Srs200217 		// To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
6321*4b22b933Srs200217 		if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1;
6322*4b22b933Srs200217 		rr->ThisAPInterval       = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
6323*4b22b933Srs200217 		InitializeLastAPTime(m, rr);
6324*4b22b933Srs200217 		while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
6325*4b22b933Srs200217 		if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--;
6326*4b22b933Srs200217 		if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval);
6327*4b22b933Srs200217 		if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1);
6328*4b22b933Srs200217 		if (rr->UpdateCredits <= 5)
6329*4b22b933Srs200217 			{
6330*4b22b933Srs200217 			mDNSu32 delay = 6 - rr->UpdateCredits;		// Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
6331*4b22b933Srs200217 			if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond);
6332*4b22b933Srs200217 			rr->ThisAPInterval *= 4;
6333*4b22b933Srs200217 			rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval;
6334*4b22b933Srs200217 			LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s",
6335*4b22b933Srs200217 				rr->resrec.name->c, delay, delay > 1 ? "s" : "");
6336*4b22b933Srs200217 			}
6337*4b22b933Srs200217 		rr->resrec.rroriginalttl = newttl;
6338*4b22b933Srs200217 		}
6339*4b22b933Srs200217 
6340*4b22b933Srs200217 	mDNS_Unlock(m);
6341*4b22b933Srs200217 	return(mStatus_NoError);
6342*4b22b933Srs200217 	}
6343*4b22b933Srs200217 
6344*4b22b933Srs200217 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
6345*4b22b933Srs200217 // the record list and/or question list.
6346*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNS_Deregister(mDNS * const m,AuthRecord * const rr)6347*4b22b933Srs200217 mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr)
6348*4b22b933Srs200217 	{
6349*4b22b933Srs200217 	mStatus status;
6350*4b22b933Srs200217 	mDNS_Lock(m);
6351*4b22b933Srs200217 	status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
6352*4b22b933Srs200217 	mDNS_Unlock(m);
6353*4b22b933Srs200217 	return(status);
6354*4b22b933Srs200217 	}
6355*4b22b933Srs200217 
6356*4b22b933Srs200217 mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
6357*4b22b933Srs200217 
FindFirstAdvertisedInterface(mDNS * const m)6358*4b22b933Srs200217 mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
6359*4b22b933Srs200217 	{
6360*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
6361*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
6362*4b22b933Srs200217 		if (intf->Advertise) break;
6363*4b22b933Srs200217 	return(intf);
6364*4b22b933Srs200217 	}
6365*4b22b933Srs200217 
AdvertiseInterface(mDNS * const m,NetworkInterfaceInfo * set)6366*4b22b933Srs200217 mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6367*4b22b933Srs200217 	{
6368*4b22b933Srs200217 	char buffer[256];
6369*4b22b933Srs200217 	NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
6370*4b22b933Srs200217 	if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
6371*4b22b933Srs200217 
6372*4b22b933Srs200217 	// Send dynamic update for non-linklocal IPv4 Addresses
6373*4b22b933Srs200217 	mDNS_SetupResourceRecord(&set->RR_A,     mDNSNULL, set->InterfaceID, kDNSType_A,     kHostNameTTL, kDNSRecordTypeUnique,      mDNS_HostNameCallback, set);
6374*4b22b933Srs200217 	mDNS_SetupResourceRecord(&set->RR_PTR,   mDNSNULL, set->InterfaceID, kDNSType_PTR,   kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
6375*4b22b933Srs200217 	mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique,      mDNSNULL, mDNSNULL);
6376*4b22b933Srs200217 
6377*4b22b933Srs200217 #if ANSWER_REMOTE_HOSTNAME_QUERIES
6378*4b22b933Srs200217 	set->RR_A    .AllowRemoteQuery  = mDNStrue;
6379*4b22b933Srs200217 	set->RR_PTR  .AllowRemoteQuery  = mDNStrue;
6380*4b22b933Srs200217 	set->RR_HINFO.AllowRemoteQuery  = mDNStrue;
6381*4b22b933Srs200217 #endif
6382*4b22b933Srs200217 	// 1. Set up Address record to map from host name ("foo.local.") to IP address
6383*4b22b933Srs200217 	// 2. Set up reverse-lookup PTR record to map from our address back to our host name
6384*4b22b933Srs200217 	AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname);
6385*4b22b933Srs200217 	if (set->ip.type == mDNSAddrType_IPv4)
6386*4b22b933Srs200217 		{
6387*4b22b933Srs200217 		set->RR_A.resrec.rrtype = kDNSType_A;
6388*4b22b933Srs200217 		set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4;
6389*4b22b933Srs200217 		// Note: This is reverse order compared to a normal dotted-decimal IP address
6390*4b22b933Srs200217 		mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.",
6391*4b22b933Srs200217 			set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]);
6392*4b22b933Srs200217 		}
6393*4b22b933Srs200217 	else if (set->ip.type == mDNSAddrType_IPv6)
6394*4b22b933Srs200217 		{
6395*4b22b933Srs200217 		int i;
6396*4b22b933Srs200217 		set->RR_A.resrec.rrtype = kDNSType_AAAA;
6397*4b22b933Srs200217 		set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6;
6398*4b22b933Srs200217 		for (i = 0; i < 16; i++)
6399*4b22b933Srs200217 			{
6400*4b22b933Srs200217 			static const char hexValues[] = "0123456789ABCDEF";
6401*4b22b933Srs200217 			buffer[i * 4    ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F];
6402*4b22b933Srs200217 			buffer[i * 4 + 1] = '.';
6403*4b22b933Srs200217 			buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4];
6404*4b22b933Srs200217 			buffer[i * 4 + 3] = '.';
6405*4b22b933Srs200217 			}
6406*4b22b933Srs200217 		mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
6407*4b22b933Srs200217 		}
6408*4b22b933Srs200217 
6409*4b22b933Srs200217 	MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer);
6410*4b22b933Srs200217 	set->RR_PTR.HostTarget = mDNStrue;	// Tell mDNS that the target of this PTR is to be kept in sync with our host name
6411*4b22b933Srs200217 	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
6412*4b22b933Srs200217 
6413*4b22b933Srs200217 	set->RR_A.RRSet = &primary->RR_A;	// May refer to self
6414*4b22b933Srs200217 
6415*4b22b933Srs200217 	mDNS_Register_internal(m, &set->RR_A);
6416*4b22b933Srs200217 	mDNS_Register_internal(m, &set->RR_PTR);
6417*4b22b933Srs200217 
6418*4b22b933Srs200217 	if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254)
6419*4b22b933Srs200217 		{
6420*4b22b933Srs200217 		mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data;
6421*4b22b933Srs200217 		AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname);
6422*4b22b933Srs200217 		set->RR_HINFO.DependentOn = &set->RR_A;
6423*4b22b933Srs200217 		mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]);
6424*4b22b933Srs200217 		p += 1 + (int)p[0];
6425*4b22b933Srs200217 		mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]);
6426*4b22b933Srs200217 		mDNS_Register_internal(m, &set->RR_HINFO);
6427*4b22b933Srs200217 		}
6428*4b22b933Srs200217 	else
6429*4b22b933Srs200217 		{
6430*4b22b933Srs200217 		debugf("Not creating HINFO record: platform support layer provided no information");
6431*4b22b933Srs200217 		set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered;
6432*4b22b933Srs200217 		}
6433*4b22b933Srs200217 	}
6434*4b22b933Srs200217 
DeadvertiseInterface(mDNS * const m,NetworkInterfaceInfo * set)6435*4b22b933Srs200217 mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6436*4b22b933Srs200217 	{
6437*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
6438*4b22b933Srs200217 
6439*4b22b933Srs200217     // If we still have address records referring to this one, update them
6440*4b22b933Srs200217 	NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
6441*4b22b933Srs200217 	AuthRecord *A = primary ? &primary->RR_A : mDNSNULL;
6442*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
6443*4b22b933Srs200217 		if (intf->RR_A.RRSet == &set->RR_A)
6444*4b22b933Srs200217 			intf->RR_A.RRSet = A;
6445*4b22b933Srs200217 
6446*4b22b933Srs200217 	// Unregister these records.
6447*4b22b933Srs200217 	// When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
6448*4b22b933Srs200217 	// support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
6449*4b22b933Srs200217 	// Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
6450*4b22b933Srs200217 	// To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
6451*4b22b933Srs200217 	if (set->RR_A.    resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A,     mDNS_Dereg_normal);
6452*4b22b933Srs200217 	if (set->RR_PTR.  resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR,   mDNS_Dereg_normal);
6453*4b22b933Srs200217 	if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal);
6454*4b22b933Srs200217 	}
6455*4b22b933Srs200217 
mDNS_SetFQDN(mDNS * const m)6456*4b22b933Srs200217 mDNSexport void mDNS_SetFQDN(mDNS *const m)
6457*4b22b933Srs200217 	{
6458*4b22b933Srs200217 	domainname newmname;
6459*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
6460*4b22b933Srs200217 	AuthRecord *rr;
6461*4b22b933Srs200217 	newmname.c[0] = 0;
6462*4b22b933Srs200217 
6463*4b22b933Srs200217 	if (!AppendDomainLabel(&newmname, &m->hostlabel))  { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6464*4b22b933Srs200217 	if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6465*4b22b933Srs200217 	if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
6466*4b22b933Srs200217 
6467*4b22b933Srs200217 	mDNS_Lock(m);
6468*4b22b933Srs200217 	AssignDomainName(&m->MulticastHostname, &newmname);
6469*4b22b933Srs200217 
6470*4b22b933Srs200217 	// 1. Stop advertising our address records on all interfaces
6471*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
6472*4b22b933Srs200217 		if (intf->Advertise) DeadvertiseInterface(m, intf);
6473*4b22b933Srs200217 
6474*4b22b933Srs200217 	// 2. Start advertising our address records using the new name
6475*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
6476*4b22b933Srs200217 		if (intf->Advertise) AdvertiseInterface(m, intf);
6477*4b22b933Srs200217 
6478*4b22b933Srs200217 	// 3. Make sure that any SRV records (and the like) that reference our
6479*4b22b933Srs200217 	// host name in their rdata get updated to reference this new host name
6480*4b22b933Srs200217 	for (rr = m->ResourceRecords;  rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr);
6481*4b22b933Srs200217 	for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr);
6482*4b22b933Srs200217 
6483*4b22b933Srs200217 	mDNS_Unlock(m);
6484*4b22b933Srs200217 	}
6485*4b22b933Srs200217 
mDNS_HostNameCallback(mDNS * const m,AuthRecord * const rr,mStatus result)6486*4b22b933Srs200217 mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6487*4b22b933Srs200217 	{
6488*4b22b933Srs200217 	(void)rr;	// Unused parameter
6489*4b22b933Srs200217 
6490*4b22b933Srs200217 	#if MDNS_DEBUGMSGS
6491*4b22b933Srs200217 		{
6492*4b22b933Srs200217 		char *msg = "Unknown result";
6493*4b22b933Srs200217 		if      (result == mStatus_NoError)      msg = "Name registered";
6494*4b22b933Srs200217 		else if (result == mStatus_NameConflict) msg = "Name conflict";
6495*4b22b933Srs200217 		debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6496*4b22b933Srs200217 		}
6497*4b22b933Srs200217 	#endif
6498*4b22b933Srs200217 
6499*4b22b933Srs200217 	if (result == mStatus_NoError)
6500*4b22b933Srs200217 		{
6501*4b22b933Srs200217 		// Notify the client that the host name is successfully registered
6502*4b22b933Srs200217 		if (m->MainCallback)
6503*4b22b933Srs200217 			{
6504*4b22b933Srs200217 			m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
6505*4b22b933Srs200217 			m->MainCallback(m, result);
6506*4b22b933Srs200217 			m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6507*4b22b933Srs200217 			}
6508*4b22b933Srs200217 		}
6509*4b22b933Srs200217 	else if (result == mStatus_NameConflict)
6510*4b22b933Srs200217 		{
6511*4b22b933Srs200217 		domainlabel oldlabel = m->hostlabel;
6512*4b22b933Srs200217 
6513*4b22b933Srs200217 		// 1. First give the client callback a chance to pick a new name
6514*4b22b933Srs200217 		if (m->MainCallback)
6515*4b22b933Srs200217 			{
6516*4b22b933Srs200217 			m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
6517*4b22b933Srs200217 			m->MainCallback(m, mStatus_NameConflict);
6518*4b22b933Srs200217 			m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
6519*4b22b933Srs200217 			}
6520*4b22b933Srs200217 
6521*4b22b933Srs200217 		// 2. If the client callback didn't do it, add (or increment) an index ourselves
6522*4b22b933Srs200217 		if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
6523*4b22b933Srs200217 			IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
6524*4b22b933Srs200217 
6525*4b22b933Srs200217 		// 3. Generate the FQDNs from the hostlabel,
6526*4b22b933Srs200217 		// and make sure all SRV records, etc., are updated to reference our new hostname
6527*4b22b933Srs200217 		mDNS_SetFQDN(m);
6528*4b22b933Srs200217 		LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c);
6529*4b22b933Srs200217 		}
6530*4b22b933Srs200217 	else if (result == mStatus_MemFree)
6531*4b22b933Srs200217 		{
6532*4b22b933Srs200217 		// .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
6533*4b22b933Srs200217 		// mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
6534*4b22b933Srs200217 		debugf("mDNS_HostNameCallback: MemFree (ignored)");
6535*4b22b933Srs200217 		}
6536*4b22b933Srs200217 	else
6537*4b22b933Srs200217 		LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result,  rr->resrec.name->c);
6538*4b22b933Srs200217 	}
6539*4b22b933Srs200217 
UpdateInterfaceProtocols(mDNS * const m,NetworkInterfaceInfo * active)6540*4b22b933Srs200217 mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
6541*4b22b933Srs200217 	{
6542*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
6543*4b22b933Srs200217 	active->IPv4Available = mDNSfalse;
6544*4b22b933Srs200217 	active->IPv6Available = mDNSfalse;
6545*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
6546*4b22b933Srs200217 		if (intf->InterfaceID == active->InterfaceID)
6547*4b22b933Srs200217 			{
6548*4b22b933Srs200217 			if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue;
6549*4b22b933Srs200217 			if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue;
6550*4b22b933Srs200217 			}
6551*4b22b933Srs200217 	}
6552*4b22b933Srs200217 
mDNS_RegisterInterface(mDNS * const m,NetworkInterfaceInfo * set,mDNSBool flapping)6553*4b22b933Srs200217 mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
6554*4b22b933Srs200217 	{
6555*4b22b933Srs200217 	mDNSBool FirstOfType = mDNStrue;
6556*4b22b933Srs200217 	NetworkInterfaceInfo **p = &m->HostInterfaces;
6557*4b22b933Srs200217 
6558*4b22b933Srs200217 	if (!set->InterfaceID)
6559*4b22b933Srs200217 		{ LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
6560*4b22b933Srs200217 
6561*4b22b933Srs200217 	if (!mDNSAddressIsValidNonZero(&set->mask))
6562*4b22b933Srs200217 		{ LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
6563*4b22b933Srs200217 
6564*4b22b933Srs200217 	mDNS_Lock(m);
6565*4b22b933Srs200217 
6566*4b22b933Srs200217 	// Assume this interface will be active now, unless we find a duplicate already in the list
6567*4b22b933Srs200217 	set->InterfaceActive = mDNStrue;
6568*4b22b933Srs200217 	set->IPv4Available   = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx);
6569*4b22b933Srs200217 	set->IPv6Available   = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx);
6570*4b22b933Srs200217 
6571*4b22b933Srs200217 	// Scan list to see if this InterfaceID is already represented
6572*4b22b933Srs200217 	while (*p)
6573*4b22b933Srs200217 		{
6574*4b22b933Srs200217 		if (*p == set)
6575*4b22b933Srs200217 			{
6576*4b22b933Srs200217 			LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6577*4b22b933Srs200217 			mDNS_Unlock(m);
6578*4b22b933Srs200217 			return(mStatus_AlreadyRegistered);
6579*4b22b933Srs200217 			}
6580*4b22b933Srs200217 
6581*4b22b933Srs200217 		if ((*p)->InterfaceID == set->InterfaceID)
6582*4b22b933Srs200217 			{
6583*4b22b933Srs200217 			// This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now
6584*4b22b933Srs200217 			set->InterfaceActive = mDNSfalse;
6585*4b22b933Srs200217 			if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse;
6586*4b22b933Srs200217 			if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue;
6587*4b22b933Srs200217 			if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue;
6588*4b22b933Srs200217 			}
6589*4b22b933Srs200217 
6590*4b22b933Srs200217 		p=&(*p)->next;
6591*4b22b933Srs200217 		}
6592*4b22b933Srs200217 
6593*4b22b933Srs200217 	set->next = mDNSNULL;
6594*4b22b933Srs200217 	*p = set;
6595*4b22b933Srs200217 
6596*4b22b933Srs200217 	if (set->Advertise)
6597*4b22b933Srs200217 		AdvertiseInterface(m, set);
6598*4b22b933Srs200217 
6599*4b22b933Srs200217 	LogOperation("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set->InterfaceID, set->ifname, &set->ip,
6600*4b22b933Srs200217 		set->InterfaceActive ?
6601*4b22b933Srs200217 			"not represented in list; marking active and retriggering queries" :
6602*4b22b933Srs200217 			"already represented in list; marking inactive for now");
6603*4b22b933Srs200217 
6604*4b22b933Srs200217 	// In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6605*4b22b933Srs200217 	// giving the false impression that there's an active representative of this interface when there really isn't.
6606*4b22b933Srs200217 	// Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
6607*4b22b933Srs200217 	// even if we believe that we previously had an active representative of this interface.
6608*4b22b933Srs200217 	if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
6609*4b22b933Srs200217 		{
6610*4b22b933Srs200217 		DNSQuestion *q;
6611*4b22b933Srs200217 		AuthRecord *rr;
6612*4b22b933Srs200217 		// If flapping, delay between first and second queries is eight seconds instead of one
6613*4b22b933Srs200217 		mDNSs32 delay    = flapping ? mDNSPlatformOneSecond   * 5 : 0;
6614*4b22b933Srs200217 		mDNSu8  announce = flapping ? (mDNSu8)1                   : InitialAnnounceCount;
6615*4b22b933Srs200217 
6616*4b22b933Srs200217 		// Use a small amount of randomness:
6617*4b22b933Srs200217 		// In the case of a network administrator turning on an Ethernet hub so that all the
6618*4b22b933Srs200217 		// connected machines establish link at exactly the same time, we don't want them all
6619*4b22b933Srs200217 		// to go and hit the network with identical queries at exactly the same moment.
6620*4b22b933Srs200217 		if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
6621*4b22b933Srs200217 
6622*4b22b933Srs200217 		if (flapping)
6623*4b22b933Srs200217 			{
6624*4b22b933Srs200217 			LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", set->ifname, &set->ip);
6625*4b22b933Srs200217 			if (!m->SuppressProbes ||
6626*4b22b933Srs200217 				m->SuppressProbes - (m->timenow + delay) < 0)
6627*4b22b933Srs200217 				m->SuppressProbes = (m->timenow + delay);
6628*4b22b933Srs200217 			}
6629*4b22b933Srs200217 
6630*4b22b933Srs200217 		for (q = m->Questions; q; q=q->next)							// Scan our list of questions
6631*4b22b933Srs200217 			if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)	// If non-specific Q, or Q on this specific interface,
6632*4b22b933Srs200217 				{														// then reactivate this question
6633*4b22b933Srs200217 				mDNSs32 initial  = (flapping && q->FlappingInterface != set->InterfaceID) ? InitialQuestionInterval * 8 : InitialQuestionInterval;
6634*4b22b933Srs200217 				mDNSs32 qdelay   = (flapping && q->FlappingInterface != set->InterfaceID) ? mDNSPlatformOneSecond   * 5 : 0;
6635*4b22b933Srs200217 				if (flapping && q->FlappingInterface == set->InterfaceID)
6636*4b22b933Srs200217 					LogOperation("No cache records for %##s (%s) expired; no need for immediate question", q->qname.c, DNSTypeName(q->qtype));
6637*4b22b933Srs200217 
6638*4b22b933Srs200217 				if (!q->ThisQInterval || q->ThisQInterval > initial)
6639*4b22b933Srs200217 					{
6640*4b22b933Srs200217 					q->ThisQInterval = initial;
6641*4b22b933Srs200217 					q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
6642*4b22b933Srs200217 					}
6643*4b22b933Srs200217 				if (q->LastQTime - (m->timenow - q->ThisQInterval + qdelay) > 0)
6644*4b22b933Srs200217 					q->LastQTime = (m->timenow - q->ThisQInterval + qdelay);
6645*4b22b933Srs200217 				q->RecentAnswerPkts = 0;
6646*4b22b933Srs200217 				SetNextQueryTime(m,q);
6647*4b22b933Srs200217 				}
6648*4b22b933Srs200217 
6649*4b22b933Srs200217 		// For all our non-specific authoritative resource records (and any dormant records specific to this interface)
6650*4b22b933Srs200217 		// we now need them to re-probe if necessary, and then re-announce.
6651*4b22b933Srs200217 		for (rr = m->ResourceRecords; rr; rr=rr->next)
6652*4b22b933Srs200217 			if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
6653*4b22b933Srs200217 				{
6654*4b22b933Srs200217 				if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
6655*4b22b933Srs200217 				rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
6656*4b22b933Srs200217 				if (rr->AnnounceCount < announce) rr->AnnounceCount  = announce;
6657*4b22b933Srs200217 				rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
6658*4b22b933Srs200217 				InitializeLastAPTime(m, rr);
6659*4b22b933Srs200217 				}
6660*4b22b933Srs200217 		}
6661*4b22b933Srs200217 
6662*4b22b933Srs200217 	mDNS_Unlock(m);
6663*4b22b933Srs200217 	return(mStatus_NoError);
6664*4b22b933Srs200217 	}
6665*4b22b933Srs200217 
6666*4b22b933Srs200217 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6667*4b22b933Srs200217 // the record list and/or question list.
6668*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNS_DeregisterInterface(mDNS * const m,NetworkInterfaceInfo * set,mDNSBool flapping)6669*4b22b933Srs200217 mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
6670*4b22b933Srs200217 	{
6671*4b22b933Srs200217 	NetworkInterfaceInfo **p = &m->HostInterfaces;
6672*4b22b933Srs200217 
6673*4b22b933Srs200217 	mDNSBool revalidate = mDNSfalse;
6674*4b22b933Srs200217 	// If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6675*4b22b933Srs200217 	// time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6676*4b22b933Srs200217 	// still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6677*4b22b933Srs200217 	if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue;
6678*4b22b933Srs200217 
6679*4b22b933Srs200217 	mDNS_Lock(m);
6680*4b22b933Srs200217 
6681*4b22b933Srs200217 	// Find this record in our list
6682*4b22b933Srs200217 	while (*p && *p != set) p=&(*p)->next;
6683*4b22b933Srs200217 	if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; }
6684*4b22b933Srs200217 
6685*4b22b933Srs200217 	// Unlink this record from our list
6686*4b22b933Srs200217 	*p = (*p)->next;
6687*4b22b933Srs200217 	set->next = mDNSNULL;
6688*4b22b933Srs200217 
6689*4b22b933Srs200217 	if (!set->InterfaceActive)
6690*4b22b933Srs200217 		{
6691*4b22b933Srs200217 		// If this interface not the active member of its set, update the v4/v6Available flags for the active member
6692*4b22b933Srs200217 		NetworkInterfaceInfo *intf;
6693*4b22b933Srs200217 		for (intf = m->HostInterfaces; intf; intf = intf->next)
6694*4b22b933Srs200217 			if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID)
6695*4b22b933Srs200217 				UpdateInterfaceProtocols(m, intf);
6696*4b22b933Srs200217 		}
6697*4b22b933Srs200217 	else
6698*4b22b933Srs200217 		{
6699*4b22b933Srs200217 		NetworkInterfaceInfo *intf;
6700*4b22b933Srs200217 		for (intf = m->HostInterfaces; intf; intf = intf->next)
6701*4b22b933Srs200217 			if (intf->InterfaceID == set->InterfaceID)
6702*4b22b933Srs200217 				break;
6703*4b22b933Srs200217 		if (intf)
6704*4b22b933Srs200217 			{
6705*4b22b933Srs200217 			LogOperation("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;"
6706*4b22b933Srs200217 				" making it active", set->InterfaceID, set->ifname, &set->ip);
6707*4b22b933Srs200217 			intf->InterfaceActive = mDNStrue;
6708*4b22b933Srs200217 			UpdateInterfaceProtocols(m, intf);
6709*4b22b933Srs200217 
6710*4b22b933Srs200217 			// See if another representative *of the same type* exists. If not, we mave have gone from
6711*4b22b933Srs200217 			// dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6712*4b22b933Srs200217 			for (intf = m->HostInterfaces; intf; intf = intf->next)
6713*4b22b933Srs200217 				if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type)
6714*4b22b933Srs200217 					break;
6715*4b22b933Srs200217 			if (!intf) revalidate = mDNStrue;
6716*4b22b933Srs200217 			}
6717*4b22b933Srs200217 		else
6718*4b22b933Srs200217 			{
6719*4b22b933Srs200217 			mDNSu32 slot;
6720*4b22b933Srs200217 			CacheGroup *cg;
6721*4b22b933Srs200217 			CacheRecord *rr;
6722*4b22b933Srs200217 			DNSQuestion *q;
6723*4b22b933Srs200217 			LogOperation("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
6724*4b22b933Srs200217 				" marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip);
6725*4b22b933Srs200217 
6726*4b22b933Srs200217 			if (flapping)
6727*4b22b933Srs200217 				LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect",
6728*4b22b933Srs200217 					set->ifname, &set->ip);
6729*4b22b933Srs200217 
6730*4b22b933Srs200217 			// 1. Deactivate any questions specific to this interface, and tag appropriate questions
6731*4b22b933Srs200217 			// so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them
6732*4b22b933Srs200217 			for (q = m->Questions; q; q=q->next)
6733*4b22b933Srs200217 				{
6734*4b22b933Srs200217 				if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0;
6735*4b22b933Srs200217 				if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)
6736*4b22b933Srs200217 					q->FlappingInterface = set->InterfaceID;
6737*4b22b933Srs200217 				}
6738*4b22b933Srs200217 
6739*4b22b933Srs200217 			// 2. Flush any cache records received on this interface
6740*4b22b933Srs200217 			revalidate = mDNSfalse;		// Don't revalidate if we're flushing the records
6741*4b22b933Srs200217 			FORALL_CACHERECORDS(slot, cg, rr)
6742*4b22b933Srs200217 				if (rr->resrec.InterfaceID == set->InterfaceID)
6743*4b22b933Srs200217 					{
6744*4b22b933Srs200217 					// If this interface is deemed flapping,
6745*4b22b933Srs200217 					// postpone deleting the cache records in case the interface comes back again
6746*4b22b933Srs200217 					if (!flapping) PurgeCacheResourceRecord(m, rr);
6747*4b22b933Srs200217 					else mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
6748*4b22b933Srs200217 					}
6749*4b22b933Srs200217 			}
6750*4b22b933Srs200217 		}
6751*4b22b933Srs200217 
6752*4b22b933Srs200217 	// If we were advertising on this interface, deregister those address and reverse-lookup records now
6753*4b22b933Srs200217 	if (set->Advertise) DeadvertiseInterface(m, set);
6754*4b22b933Srs200217 
6755*4b22b933Srs200217 	// If we have any cache records received on this interface that went away, then re-verify them.
6756*4b22b933Srs200217 	// In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6757*4b22b933Srs200217 	// giving the false impression that there's an active representative of this interface when there really isn't.
6758*4b22b933Srs200217 	// Don't need to do this when shutting down, because *all* interfaces are about to go away
6759*4b22b933Srs200217 	if (revalidate && !m->mDNS_shutdown)
6760*4b22b933Srs200217 		{
6761*4b22b933Srs200217 		mDNSu32 slot;
6762*4b22b933Srs200217 		CacheGroup *cg;
6763*4b22b933Srs200217 		CacheRecord *rr;
6764*4b22b933Srs200217 		m->NextCacheCheck = m->timenow;
6765*4b22b933Srs200217 		FORALL_CACHERECORDS(slot, cg, rr)
6766*4b22b933Srs200217 			if (rr->resrec.InterfaceID == set->InterfaceID)
6767*4b22b933Srs200217 				mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
6768*4b22b933Srs200217 		}
6769*4b22b933Srs200217 
6770*4b22b933Srs200217 	mDNS_Unlock(m);
6771*4b22b933Srs200217 	}
6772*4b22b933Srs200217 
ServiceCallback(mDNS * const m,AuthRecord * const rr,mStatus result)6773*4b22b933Srs200217 mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6774*4b22b933Srs200217 	{
6775*4b22b933Srs200217 	ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6776*4b22b933Srs200217 	(void)m;	// Unused parameter
6777*4b22b933Srs200217 
6778*4b22b933Srs200217 	#if MDNS_DEBUGMSGS
6779*4b22b933Srs200217 		{
6780*4b22b933Srs200217 		char *msg = "Unknown result";
6781*4b22b933Srs200217 		if      (result == mStatus_NoError)      msg = "Name Registered";
6782*4b22b933Srs200217 		else if (result == mStatus_NameConflict) msg = "Name Conflict";
6783*4b22b933Srs200217 		else if (result == mStatus_MemFree)      msg = "Memory Free";
6784*4b22b933Srs200217 		debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
6785*4b22b933Srs200217 		}
6786*4b22b933Srs200217 	#endif
6787*4b22b933Srs200217 
6788*4b22b933Srs200217 	// Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6789*4b22b933Srs200217 	if (result == mStatus_NoError && rr != &sr->RR_SRV) return;
6790*4b22b933Srs200217 
6791*4b22b933Srs200217 	// If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6792*4b22b933Srs200217 	if (result == mStatus_NameConflict)
6793*4b22b933Srs200217 		{
6794*4b22b933Srs200217 		sr->Conflict = mDNStrue;				// Record that this service set had a conflict
6795*4b22b933Srs200217 		mDNS_DeregisterService(m, sr);			// Unlink the records from our list
6796*4b22b933Srs200217 		return;
6797*4b22b933Srs200217 		}
6798*4b22b933Srs200217 
6799*4b22b933Srs200217 	if (result == mStatus_MemFree)
6800*4b22b933Srs200217 		{
6801*4b22b933Srs200217 		// If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6802*4b22b933Srs200217 		// don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6803*4b22b933Srs200217 		mDNSu32 i;
6804*4b22b933Srs200217 		if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return;
6805*4b22b933Srs200217 		for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return;
6806*4b22b933Srs200217 
6807*4b22b933Srs200217 		// If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6808*4b22b933Srs200217 		// then we can now report the NameConflict to the client
6809*4b22b933Srs200217 		if (sr->Conflict) result = mStatus_NameConflict;
6810*4b22b933Srs200217 		}
6811*4b22b933Srs200217 
6812*4b22b933Srs200217 	// CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6813*4b22b933Srs200217 	// function is allowed to do anything, including deregistering this service and freeing its memory.
6814*4b22b933Srs200217 	if (sr->ServiceCallback)
6815*4b22b933Srs200217 		sr->ServiceCallback(m, sr, result);
6816*4b22b933Srs200217 	}
6817*4b22b933Srs200217 
NSSCallback(mDNS * const m,AuthRecord * const rr,mStatus result)6818*4b22b933Srs200217 mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6819*4b22b933Srs200217 	{
6820*4b22b933Srs200217 	ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6821*4b22b933Srs200217 	if (sr->ServiceCallback)
6822*4b22b933Srs200217 		sr->ServiceCallback(m, sr, result);
6823*4b22b933Srs200217 	}
6824*4b22b933Srs200217 
6825*4b22b933Srs200217 // Note:
6826*4b22b933Srs200217 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6827*4b22b933Srs200217 // Type is service type (e.g. "_ipp._tcp.")
6828*4b22b933Srs200217 // Domain is fully qualified domain name (i.e. ending with a null label)
6829*4b22b933Srs200217 // We always register a TXT, even if it is empty (so that clients are not
6830*4b22b933Srs200217 // left waiting forever looking for a nonexistent record.)
6831*4b22b933Srs200217 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6832*4b22b933Srs200217 // then the default host name (m->MulticastHostname) is automatically used
mDNS_RegisterService(mDNS * const m,ServiceRecordSet * sr,const domainlabel * const name,const domainname * const type,const domainname * const domain,const domainname * const host,mDNSIPPort port,const mDNSu8 txtinfo[],mDNSu16 txtlen,AuthRecord * SubTypes,mDNSu32 NumSubTypes,const mDNSInterfaceID InterfaceID,mDNSServiceCallback Callback,void * Context)6833*4b22b933Srs200217 mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
6834*4b22b933Srs200217 	const domainlabel *const name, const domainname *const type, const domainname *const domain,
6835*4b22b933Srs200217 	const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
6836*4b22b933Srs200217 	AuthRecord *SubTypes, mDNSu32 NumSubTypes,
6837*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
6838*4b22b933Srs200217 	{
6839*4b22b933Srs200217 	mStatus err;
6840*4b22b933Srs200217 	mDNSu32 i;
6841*4b22b933Srs200217 
6842*4b22b933Srs200217 	sr->ServiceCallback = Callback;
6843*4b22b933Srs200217 	sr->ServiceContext  = Context;
6844*4b22b933Srs200217 	sr->Extras          = mDNSNULL;
6845*4b22b933Srs200217 	sr->NumSubTypes     = NumSubTypes;
6846*4b22b933Srs200217 	sr->SubTypes        = SubTypes;
6847*4b22b933Srs200217 	sr->Conflict        = mDNSfalse;
6848*4b22b933Srs200217 	if (host && host->c[0]) sr->Host = *host;
6849*4b22b933Srs200217 	else sr->Host.c[0] = 0;
6850*4b22b933Srs200217 
6851*4b22b933Srs200217 	// If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6852*4b22b933Srs200217 	if (!port.NotAnInteger)
6853*4b22b933Srs200217 		return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
6854*4b22b933Srs200217 
6855*4b22b933Srs200217 	// Initialize the AuthRecord objects to sane values
6856*4b22b933Srs200217 	mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr);
6857*4b22b933Srs200217 	mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared,   ServiceCallback, sr);
6858*4b22b933Srs200217 	mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique,   ServiceCallback, sr);
6859*4b22b933Srs200217 	mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique,   ServiceCallback, sr);
6860*4b22b933Srs200217 
6861*4b22b933Srs200217 	// If the client is registering an oversized TXT record,
6862*4b22b933Srs200217 	// it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6863*4b22b933Srs200217 	if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen)
6864*4b22b933Srs200217 		sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen;
6865*4b22b933Srs200217 
6866*4b22b933Srs200217 	// Set up the record names
6867*4b22b933Srs200217 	// For now we only create an advisory record for the main type, not for subtypes
6868*4b22b933Srs200217 	// We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6869*4b22b933Srs200217 	if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL)
6870*4b22b933Srs200217 		return(mStatus_BadParamErr);
6871*4b22b933Srs200217 	if (ConstructServiceName(sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
6872*4b22b933Srs200217 	if (ConstructServiceName(sr->RR_SRV.resrec.name, name,     type, domain) == mDNSNULL) return(mStatus_BadParamErr);
6873*4b22b933Srs200217 	AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name);
6874*4b22b933Srs200217 
6875*4b22b933Srs200217 	// 1. Set up the ADV record rdata to advertise our service type
6876*4b22b933Srs200217 	AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name);
6877*4b22b933Srs200217 
6878*4b22b933Srs200217 	// 2. Set up the PTR record rdata to point to our service name
6879*4b22b933Srs200217 	// We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6880*4b22b933Srs200217 	AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name);
6881*4b22b933Srs200217 	sr->RR_PTR.Additional1 = &sr->RR_SRV;
6882*4b22b933Srs200217 	sr->RR_PTR.Additional2 = &sr->RR_TXT;
6883*4b22b933Srs200217 
6884*4b22b933Srs200217 	// 2a. Set up any subtype PTRs to point to our service name
6885*4b22b933Srs200217 	// If the client is using subtypes, it is the client's responsibility to have
6886*4b22b933Srs200217 	// already set the first label of the record name to the subtype being registered
6887*4b22b933Srs200217 	for (i=0; i<NumSubTypes; i++)
6888*4b22b933Srs200217 		{
6889*4b22b933Srs200217 		domainname st;
6890*4b22b933Srs200217 		AssignDomainName(&st, sr->SubTypes[i].resrec.name);
6891*4b22b933Srs200217 		st.c[1+st.c[0]] = 0;			// Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6892*4b22b933Srs200217 		AppendDomainName(&st, type);
6893*4b22b933Srs200217 		mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
6894*4b22b933Srs200217 		if (ConstructServiceName(sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr);
6895*4b22b933Srs200217 		AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name);
6896*4b22b933Srs200217 		sr->SubTypes[i].Additional1 = &sr->RR_SRV;
6897*4b22b933Srs200217 		sr->SubTypes[i].Additional2 = &sr->RR_TXT;
6898*4b22b933Srs200217 		}
6899*4b22b933Srs200217 
6900*4b22b933Srs200217 	// 3. Set up the SRV record rdata.
6901*4b22b933Srs200217 	sr->RR_SRV.resrec.rdata->u.srv.priority = 0;
6902*4b22b933Srs200217 	sr->RR_SRV.resrec.rdata->u.srv.weight   = 0;
6903*4b22b933Srs200217 	sr->RR_SRV.resrec.rdata->u.srv.port     = port;
6904*4b22b933Srs200217 
6905*4b22b933Srs200217 	// Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6906*4b22b933Srs200217 	if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host);
6907*4b22b933Srs200217 	else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; }
6908*4b22b933Srs200217 
6909*4b22b933Srs200217 	// 4. Set up the TXT record rdata,
6910*4b22b933Srs200217 	// and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6911*4b22b933Srs200217 	if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0;
6912*4b22b933Srs200217 	else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c)
6913*4b22b933Srs200217 		{
6914*4b22b933Srs200217 		sr->RR_TXT.resrec.rdlength = txtlen;
6915*4b22b933Srs200217 		if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr);
6916*4b22b933Srs200217 		mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen);
6917*4b22b933Srs200217 		}
6918*4b22b933Srs200217 	sr->RR_TXT.DependentOn = &sr->RR_SRV;
6919*4b22b933Srs200217 
6920*4b22b933Srs200217 #ifndef UNICAST_DISABLED
6921*4b22b933Srs200217 	// If the client has specified an explicit InterfaceID,
6922*4b22b933Srs200217 	// then we do a multicast registration on that interface, even for unicast domains.
6923*4b22b933Srs200217 	if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6924*4b22b933Srs200217 		{
6925*4b22b933Srs200217 		mStatus status;
6926*4b22b933Srs200217 		mDNS_Lock(m);
6927*4b22b933Srs200217 		// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6928*4b22b933Srs200217 		// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6929*4b22b933Srs200217 		// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6930*4b22b933Srs200217 		// (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6931*4b22b933Srs200217 		if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; }
6932*4b22b933Srs200217 		status = uDNS_RegisterService(m, sr);
6933*4b22b933Srs200217 		mDNS_Unlock(m);
6934*4b22b933Srs200217 		return(status);
6935*4b22b933Srs200217 		}
6936*4b22b933Srs200217 #endif
6937*4b22b933Srs200217 	mDNS_Lock(m);
6938*4b22b933Srs200217 	err = mDNS_Register_internal(m, &sr->RR_SRV);
6939*4b22b933Srs200217 	if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
6940*4b22b933Srs200217 	// We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6941*4b22b933Srs200217 	// mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6942*4b22b933Srs200217 	// the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6943*4b22b933Srs200217 	// the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6944*4b22b933Srs200217 	// make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6945*4b22b933Srs200217 	if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV);
6946*4b22b933Srs200217 	for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]);
6947*4b22b933Srs200217 	if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR);
6948*4b22b933Srs200217 
6949*4b22b933Srs200217 	mDNS_Unlock(m);
6950*4b22b933Srs200217 
6951*4b22b933Srs200217 	if (err) mDNS_DeregisterService(m, sr);
6952*4b22b933Srs200217 	return(err);
6953*4b22b933Srs200217 	}
6954*4b22b933Srs200217 
mDNS_AddRecordToService(mDNS * const m,ServiceRecordSet * sr,ExtraResourceRecord * extra,RData * rdata,mDNSu32 ttl)6955*4b22b933Srs200217 mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
6956*4b22b933Srs200217 	ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
6957*4b22b933Srs200217 	{
6958*4b22b933Srs200217 	ExtraResourceRecord **e;
6959*4b22b933Srs200217 	mStatus status;
6960*4b22b933Srs200217 
6961*4b22b933Srs200217 	extra->next = mDNSNULL;
6962*4b22b933Srs200217 	mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID,
6963*4b22b933Srs200217 		extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
6964*4b22b933Srs200217 	AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name);
6965*4b22b933Srs200217 
6966*4b22b933Srs200217 #ifndef UNICAST_DISABLED
6967*4b22b933Srs200217 	if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6968*4b22b933Srs200217 		{
6969*4b22b933Srs200217 		mDNS_Lock(m);
6970*4b22b933Srs200217 		// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6971*4b22b933Srs200217 		// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6972*4b22b933Srs200217 		// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6973*4b22b933Srs200217 		// (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck)
6974*4b22b933Srs200217 		if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0)
6975*4b22b933Srs200217 			{ extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; }
6976*4b22b933Srs200217 		status = uDNS_AddRecordToService(m, sr, extra);
6977*4b22b933Srs200217 		mDNS_Unlock(m);
6978*4b22b933Srs200217 		return status;
6979*4b22b933Srs200217 		}
6980*4b22b933Srs200217 #endif
6981*4b22b933Srs200217 
6982*4b22b933Srs200217 	mDNS_Lock(m);
6983*4b22b933Srs200217 	e = &sr->Extras;
6984*4b22b933Srs200217 	while (*e) e = &(*e)->next;
6985*4b22b933Srs200217 
6986*4b22b933Srs200217 	if (ttl == 0) ttl = kStandardTTL;
6987*4b22b933Srs200217 
6988*4b22b933Srs200217 	extra->r.DependentOn = &sr->RR_SRV;
6989*4b22b933Srs200217 
6990*4b22b933Srs200217 	debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c);
6991*4b22b933Srs200217 
6992*4b22b933Srs200217 	status = mDNS_Register_internal(m, &extra->r);
6993*4b22b933Srs200217 	if (status == mStatus_NoError) *e = extra;
6994*4b22b933Srs200217 	mDNS_Unlock(m);
6995*4b22b933Srs200217 	return(status);
6996*4b22b933Srs200217 	}
6997*4b22b933Srs200217 
mDNS_RemoveRecordFromService(mDNS * const m,ServiceRecordSet * sr,ExtraResourceRecord * extra,mDNSRecordCallback MemFreeCallback,void * Context)6998*4b22b933Srs200217 mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra,
6999*4b22b933Srs200217 	mDNSRecordCallback MemFreeCallback, void *Context)
7000*4b22b933Srs200217 	{
7001*4b22b933Srs200217 	ExtraResourceRecord **e;
7002*4b22b933Srs200217 	mStatus status;
7003*4b22b933Srs200217 
7004*4b22b933Srs200217 	mDNS_Lock(m);
7005*4b22b933Srs200217 	e = &sr->Extras;
7006*4b22b933Srs200217 	while (*e && *e != extra) e = &(*e)->next;
7007*4b22b933Srs200217 	if (!*e)
7008*4b22b933Srs200217 		{
7009*4b22b933Srs200217 		debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c);
7010*4b22b933Srs200217 		status = mStatus_BadReferenceErr;
7011*4b22b933Srs200217 		}
7012*4b22b933Srs200217 	else
7013*4b22b933Srs200217 		{
7014*4b22b933Srs200217 		debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c);
7015*4b22b933Srs200217 		extra->r.RecordCallback = MemFreeCallback;
7016*4b22b933Srs200217 		extra->r.RecordContext  = Context;
7017*4b22b933Srs200217 		*e = (*e)->next;
7018*4b22b933Srs200217 #ifndef UNICAST_DISABLED
7019*4b22b933Srs200217 		if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
7020*4b22b933Srs200217 			status = uDNS_DeregisterRecord(m, &extra->r);
7021*4b22b933Srs200217 		else
7022*4b22b933Srs200217 #endif
7023*4b22b933Srs200217 		status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal);
7024*4b22b933Srs200217 		}
7025*4b22b933Srs200217 	mDNS_Unlock(m);
7026*4b22b933Srs200217 	return(status);
7027*4b22b933Srs200217 	}
7028*4b22b933Srs200217 
mDNS_RenameAndReregisterService(mDNS * const m,ServiceRecordSet * const sr,const domainlabel * newname)7029*4b22b933Srs200217 mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
7030*4b22b933Srs200217 	{
7031*4b22b933Srs200217 	// NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
7032*4b22b933Srs200217 	// mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
7033*4b22b933Srs200217 	domainlabel name1, name2;
7034*4b22b933Srs200217 	domainname type, domain;
7035*4b22b933Srs200217 	domainname *host = mDNSNULL;
7036*4b22b933Srs200217 	ExtraResourceRecord *extras = sr->Extras;
7037*4b22b933Srs200217 	mStatus err;
7038*4b22b933Srs200217 
7039*4b22b933Srs200217 	DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain);
7040*4b22b933Srs200217 	if (!newname)
7041*4b22b933Srs200217 		{
7042*4b22b933Srs200217 		name2 = name1;
7043*4b22b933Srs200217 		IncrementLabelSuffix(&name2, mDNStrue);
7044*4b22b933Srs200217 		newname = &name2;
7045*4b22b933Srs200217 		}
7046*4b22b933Srs200217 
7047*4b22b933Srs200217 	if (SameDomainName(&domain, &localdomain))
7048*4b22b933Srs200217 		LogMsg("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c);
7049*4b22b933Srs200217 	else LogMsg("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c);
7050*4b22b933Srs200217 
7051*4b22b933Srs200217 	if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
7052*4b22b933Srs200217 
7053*4b22b933Srs200217 	err = mDNS_RegisterService(m, sr, newname, &type, &domain,
7054*4b22b933Srs200217 		host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
7055*4b22b933Srs200217 		sr->SubTypes, sr->NumSubTypes,
7056*4b22b933Srs200217 		sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext);
7057*4b22b933Srs200217 
7058*4b22b933Srs200217 	// mDNS_RegisterService() just reset sr->Extras to NULL.
7059*4b22b933Srs200217 	// Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
7060*4b22b933Srs200217 	// through the old list of extra records, and re-add them to our freshly created service registration
7061*4b22b933Srs200217 	while (!err && extras)
7062*4b22b933Srs200217 		{
7063*4b22b933Srs200217 		ExtraResourceRecord *e = extras;
7064*4b22b933Srs200217 		extras = extras->next;
7065*4b22b933Srs200217 		err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
7066*4b22b933Srs200217 		}
7067*4b22b933Srs200217 
7068*4b22b933Srs200217 	return(err);
7069*4b22b933Srs200217 	}
7070*4b22b933Srs200217 
7071*4b22b933Srs200217 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
7072*4b22b933Srs200217 // which may change the record list and/or question list.
7073*4b22b933Srs200217 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNS_DeregisterService(mDNS * const m,ServiceRecordSet * sr)7074*4b22b933Srs200217 mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
7075*4b22b933Srs200217 	{
7076*4b22b933Srs200217 	// If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
7077*4b22b933Srs200217 	if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV));
7078*4b22b933Srs200217 
7079*4b22b933Srs200217 #ifndef UNICAST_DISABLED
7080*4b22b933Srs200217 	if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
7081*4b22b933Srs200217 		{
7082*4b22b933Srs200217 		mStatus status;
7083*4b22b933Srs200217 		mDNS_Lock(m);
7084*4b22b933Srs200217 		status = uDNS_DeregisterService(m, sr);
7085*4b22b933Srs200217 		mDNS_Unlock(m);
7086*4b22b933Srs200217 		return(status);
7087*4b22b933Srs200217 		}
7088*4b22b933Srs200217 #endif
7089*4b22b933Srs200217 	if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
7090*4b22b933Srs200217 		{
7091*4b22b933Srs200217 		debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c);
7092*4b22b933Srs200217 		return(mStatus_BadReferenceErr);
7093*4b22b933Srs200217 		}
7094*4b22b933Srs200217 	else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
7095*4b22b933Srs200217 		{
7096*4b22b933Srs200217 		debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
7097*4b22b933Srs200217 		return(mStatus_NoError);
7098*4b22b933Srs200217 		}
7099*4b22b933Srs200217 	else
7100*4b22b933Srs200217 		{
7101*4b22b933Srs200217 		mDNSu32 i;
7102*4b22b933Srs200217 		mStatus status;
7103*4b22b933Srs200217 		ExtraResourceRecord *e;
7104*4b22b933Srs200217 		mDNS_Lock(m);
7105*4b22b933Srs200217 		e = sr->Extras;
7106*4b22b933Srs200217 
7107*4b22b933Srs200217 		// We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
7108*4b22b933Srs200217 		// SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
7109*4b22b933Srs200217 		mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat);
7110*4b22b933Srs200217 		mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat);
7111*4b22b933Srs200217 
7112*4b22b933Srs200217 		mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal);
7113*4b22b933Srs200217 
7114*4b22b933Srs200217 		// We deregister all of the extra records, but we leave the sr->Extras list intact
7115*4b22b933Srs200217 		// in case the client wants to do a RenameAndReregister and reinstate the registration
7116*4b22b933Srs200217 		while (e)
7117*4b22b933Srs200217 			{
7118*4b22b933Srs200217 			mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat);
7119*4b22b933Srs200217 			e = e->next;
7120*4b22b933Srs200217 			}
7121*4b22b933Srs200217 
7122*4b22b933Srs200217 		for (i=0; i<sr->NumSubTypes; i++)
7123*4b22b933Srs200217 			mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal);
7124*4b22b933Srs200217 
7125*4b22b933Srs200217 		// Be sure to deregister the PTR last!
7126*4b22b933Srs200217 		// Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
7127*4b22b933Srs200217 		// which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
7128*4b22b933Srs200217 		// which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
7129*4b22b933Srs200217 		// we've deregistered all our records and done any other necessary cleanup before that happens.
7130*4b22b933Srs200217 		status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal);
7131*4b22b933Srs200217 		mDNS_Unlock(m);
7132*4b22b933Srs200217 		return(status);
7133*4b22b933Srs200217 		}
7134*4b22b933Srs200217 	}
7135*4b22b933Srs200217 
7136*4b22b933Srs200217 // Create a registration that asserts that no such service exists with this name.
7137*4b22b933Srs200217 // This can be useful where there is a given function is available through several protocols.
7138*4b22b933Srs200217 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
7139*4b22b933Srs200217 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
7140*4b22b933Srs200217 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
7141*4b22b933Srs200217 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
mDNS_RegisterNoSuchService(mDNS * const m,AuthRecord * const rr,const domainlabel * const name,const domainname * const type,const domainname * const domain,const domainname * const host,const mDNSInterfaceID InterfaceID,mDNSRecordCallback Callback,void * Context)7142*4b22b933Srs200217 mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
7143*4b22b933Srs200217 	const domainlabel *const name, const domainname *const type, const domainname *const domain,
7144*4b22b933Srs200217 	const domainname *const host,
7145*4b22b933Srs200217 	const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context)
7146*4b22b933Srs200217 	{
7147*4b22b933Srs200217 	mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
7148*4b22b933Srs200217 	if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
7149*4b22b933Srs200217 	rr->resrec.rdata->u.srv.priority    = 0;
7150*4b22b933Srs200217 	rr->resrec.rdata->u.srv.weight      = 0;
7151*4b22b933Srs200217 	rr->resrec.rdata->u.srv.port        = zeroIPPort;
7152*4b22b933Srs200217 	if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host);
7153*4b22b933Srs200217 	else rr->HostTarget = mDNStrue;
7154*4b22b933Srs200217 	return(mDNS_Register(m, rr));
7155*4b22b933Srs200217 	}
7156*4b22b933Srs200217 
mDNS_AdvertiseDomains(mDNS * const m,AuthRecord * rr,mDNS_DomainType DomainType,const mDNSInterfaceID InterfaceID,char * domname)7157*4b22b933Srs200217 mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
7158*4b22b933Srs200217 	mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
7159*4b22b933Srs200217 	{
7160*4b22b933Srs200217 	mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
7161*4b22b933Srs200217 	if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
7162*4b22b933Srs200217 	if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname))                 return(mStatus_BadParamErr);
7163*4b22b933Srs200217 	return(mDNS_Register(m, rr));
7164*4b22b933Srs200217 	}
7165*4b22b933Srs200217 
7166*4b22b933Srs200217 // ***************************************************************************
7167*4b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
7168*4b22b933Srs200217 #pragma mark -
7169*4b22b933Srs200217 #pragma mark -
7170*4b22b933Srs200217 #pragma mark - Startup and Shutdown
7171*4b22b933Srs200217 #endif
7172*4b22b933Srs200217 
mDNS_GrowCache_internal(mDNS * const m,CacheEntity * storage,mDNSu32 numrecords)7173*4b22b933Srs200217 mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7174*4b22b933Srs200217 	{
7175*4b22b933Srs200217 	if (storage && numrecords)
7176*4b22b933Srs200217 		{
7177*4b22b933Srs200217 		mDNSu32 i;
7178*4b22b933Srs200217 		debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity));
7179*4b22b933Srs200217 		for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1];
7180*4b22b933Srs200217 		storage[numrecords-1].next = m->rrcache_free;
7181*4b22b933Srs200217 		m->rrcache_free = storage;
7182*4b22b933Srs200217 		m->rrcache_size += numrecords;
7183*4b22b933Srs200217 		}
7184*4b22b933Srs200217 	}
7185*4b22b933Srs200217 
mDNS_GrowCache(mDNS * const m,CacheEntity * storage,mDNSu32 numrecords)7186*4b22b933Srs200217 mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7187*4b22b933Srs200217 	{
7188*4b22b933Srs200217 	mDNS_Lock(m);
7189*4b22b933Srs200217 	mDNS_GrowCache_internal(m, storage, numrecords);
7190*4b22b933Srs200217 	mDNS_Unlock(m);
7191*4b22b933Srs200217 	}
7192*4b22b933Srs200217 
mDNS_Init(mDNS * const m,mDNS_PlatformSupport * const p,CacheEntity * rrcachestorage,mDNSu32 rrcachesize,mDNSBool AdvertiseLocalAddresses,mDNSCallback * Callback,void * Context)7193*4b22b933Srs200217 mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
7194*4b22b933Srs200217 	CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
7195*4b22b933Srs200217 	mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
7196*4b22b933Srs200217 	{
7197*4b22b933Srs200217 	mDNSu32 slot;
7198*4b22b933Srs200217 	mDNSs32 timenow;
7199*4b22b933Srs200217 	mStatus result;
7200*4b22b933Srs200217 
7201*4b22b933Srs200217 	if (!rrcachestorage) rrcachesize = 0;
7202*4b22b933Srs200217 
7203*4b22b933Srs200217 	m->p                       = p;
7204*4b22b933Srs200217 	m->KnownBugs               = 0;
7205*4b22b933Srs200217 	m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
7206*4b22b933Srs200217 	m->AdvertiseLocalAddresses = AdvertiseLocalAddresses;
7207*4b22b933Srs200217 	m->mDNSPlatformStatus      = mStatus_Waiting;
7208*4b22b933Srs200217 	m->UnicastPort4            = zeroIPPort;
7209*4b22b933Srs200217 	m->UnicastPort6            = zeroIPPort;
7210*4b22b933Srs200217 	m->MainCallback            = Callback;
7211*4b22b933Srs200217 	m->MainContext             = Context;
7212*4b22b933Srs200217 	m->rec.r.resrec.RecordType = 0;
7213*4b22b933Srs200217 
7214*4b22b933Srs200217 	// For debugging: To catch and report locking failures
7215*4b22b933Srs200217 	m->mDNS_busy               = 0;
7216*4b22b933Srs200217 	m->mDNS_reentrancy         = 0;
7217*4b22b933Srs200217 	m->mDNS_shutdown           = mDNSfalse;
7218*4b22b933Srs200217 	m->lock_rrcache            = 0;
7219*4b22b933Srs200217 	m->lock_Questions          = 0;
7220*4b22b933Srs200217 	m->lock_Records            = 0;
7221*4b22b933Srs200217 
7222*4b22b933Srs200217 	// Task Scheduling variables
7223*4b22b933Srs200217 	result = mDNSPlatformTimeInit();
7224*4b22b933Srs200217 	if (result != mStatus_NoError) return(result);
7225*4b22b933Srs200217 	m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF);
7226*4b22b933Srs200217 	timenow = mDNS_TimeNow_NoLock(m);
7227*4b22b933Srs200217 
7228*4b22b933Srs200217 	m->timenow                 = 0;		// MUST only be set within mDNS_Lock/mDNS_Unlock section
7229*4b22b933Srs200217 	m->timenow_last            = timenow;
7230*4b22b933Srs200217 	m->NextScheduledEvent      = timenow;
7231*4b22b933Srs200217 	m->SuppressSending         = timenow;
7232*4b22b933Srs200217 	m->NextCacheCheck          = timenow + 0x78000000;
7233*4b22b933Srs200217 	m->NextScheduledQuery      = timenow + 0x78000000;
7234*4b22b933Srs200217 	m->NextScheduledProbe      = timenow + 0x78000000;
7235*4b22b933Srs200217 	m->NextScheduledResponse   = timenow + 0x78000000;
7236*4b22b933Srs200217 	m->ExpectUnicastResponse   = timenow + 0x78000000;
7237*4b22b933Srs200217 	m->RandomQueryDelay        = 0;
7238*4b22b933Srs200217 	m->RandomReconfirmDelay    = 0;
7239*4b22b933Srs200217 	m->PktNum                  = 0;
7240*4b22b933Srs200217 	m->SendDeregistrations     = mDNSfalse;
7241*4b22b933Srs200217 	m->SendImmediateAnswers    = mDNSfalse;
7242*4b22b933Srs200217 	m->SleepState              = mDNSfalse;
7243*4b22b933Srs200217 
7244*4b22b933Srs200217 	// These fields only required for mDNS Searcher...
7245*4b22b933Srs200217 	m->Questions               = mDNSNULL;
7246*4b22b933Srs200217 	m->NewQuestions            = mDNSNULL;
7247*4b22b933Srs200217 	m->CurrentQuestion         = mDNSNULL;
7248*4b22b933Srs200217 	m->LocalOnlyQuestions      = mDNSNULL;
7249*4b22b933Srs200217 	m->NewLocalOnlyQuestions   = mDNSNULL;
7250*4b22b933Srs200217 	m->rrcache_size            = 0;
7251*4b22b933Srs200217 	m->rrcache_totalused       = 0;
7252*4b22b933Srs200217 	m->rrcache_active          = 0;
7253*4b22b933Srs200217 	m->rrcache_report          = 10;
7254*4b22b933Srs200217 	m->rrcache_free            = mDNSNULL;
7255*4b22b933Srs200217 
7256*4b22b933Srs200217 	for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL;
7257*4b22b933Srs200217 
7258*4b22b933Srs200217 	mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
7259*4b22b933Srs200217 
7260*4b22b933Srs200217 	// Fields below only required for mDNS Responder...
7261*4b22b933Srs200217 	m->hostlabel.c[0]          = 0;
7262*4b22b933Srs200217 	m->nicelabel.c[0]          = 0;
7263*4b22b933Srs200217 	m->MulticastHostname.c[0]  = 0;
7264*4b22b933Srs200217 	m->HIHardware.c[0]         = 0;
7265*4b22b933Srs200217 	m->HISoftware.c[0]         = 0;
7266*4b22b933Srs200217 	m->ResourceRecords         = mDNSNULL;
7267*4b22b933Srs200217 	m->DuplicateRecords        = mDNSNULL;
7268*4b22b933Srs200217 	m->NewLocalRecords         = mDNSNULL;
7269*4b22b933Srs200217 	m->CurrentRecord           = mDNSNULL;
7270*4b22b933Srs200217 	m->HostInterfaces          = mDNSNULL;
7271*4b22b933Srs200217 	m->ProbeFailTime           = 0;
7272*4b22b933Srs200217 	m->NumFailedProbes         = 0;
7273*4b22b933Srs200217 	m->SuppressProbes          = 0;
7274*4b22b933Srs200217 
7275*4b22b933Srs200217 #ifndef UNICAST_DISABLED
7276*4b22b933Srs200217 	uDNS_Init(m);
7277*4b22b933Srs200217 	m->SuppressStdPort53Queries = 0;
7278*4b22b933Srs200217 #endif
7279*4b22b933Srs200217 	result = mDNSPlatformInit(m);
7280*4b22b933Srs200217 
7281*4b22b933Srs200217 	return(result);
7282*4b22b933Srs200217 	}
7283*4b22b933Srs200217 
mDNSCoreInitComplete(mDNS * const m,mStatus result)7284*4b22b933Srs200217 mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
7285*4b22b933Srs200217 	{
7286*4b22b933Srs200217 	m->mDNSPlatformStatus = result;
7287*4b22b933Srs200217 	if (m->MainCallback)
7288*4b22b933Srs200217 		{
7289*4b22b933Srs200217 		mDNS_Lock(m);
7290*4b22b933Srs200217 		m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
7291*4b22b933Srs200217 		m->MainCallback(m, mStatus_NoError);
7292*4b22b933Srs200217 		m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
7293*4b22b933Srs200217 		mDNS_Unlock(m);
7294*4b22b933Srs200217 		}
7295*4b22b933Srs200217 	}
7296*4b22b933Srs200217 
mDNS_Close(mDNS * const m)7297*4b22b933Srs200217 mDNSexport void mDNS_Close(mDNS *const m)
7298*4b22b933Srs200217 	{
7299*4b22b933Srs200217 	mDNSu32 rrcache_active = 0;
7300*4b22b933Srs200217 	mDNSu32 rrcache_totalused = 0;
7301*4b22b933Srs200217 	mDNSu32 slot;
7302*4b22b933Srs200217 	NetworkInterfaceInfo *intf;
7303*4b22b933Srs200217 	mDNS_Lock(m);
7304*4b22b933Srs200217 
7305*4b22b933Srs200217 	m->mDNS_shutdown = mDNStrue;
7306*4b22b933Srs200217 
7307*4b22b933Srs200217 #ifndef UNICAST_DISABLED
7308*4b22b933Srs200217 	uDNS_Close(m);
7309*4b22b933Srs200217 #endif
7310*4b22b933Srs200217 	rrcache_totalused = m->rrcache_totalused;
7311*4b22b933Srs200217 	for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
7312*4b22b933Srs200217 		{
7313*4b22b933Srs200217 		while(m->rrcache_hash[slot])
7314*4b22b933Srs200217 			{
7315*4b22b933Srs200217 			CacheGroup *cg = m->rrcache_hash[slot];
7316*4b22b933Srs200217 			while (cg->members)
7317*4b22b933Srs200217 				{
7318*4b22b933Srs200217 				CacheRecord *rr = cg->members;
7319*4b22b933Srs200217 				cg->members = cg->members->next;
7320*4b22b933Srs200217 				if (rr->CRActiveQuestion) rrcache_active++;
7321*4b22b933Srs200217 				ReleaseCacheRecord(m, rr);
7322*4b22b933Srs200217 				}
7323*4b22b933Srs200217 			cg->rrcache_tail = &cg->members;
7324*4b22b933Srs200217 			ReleaseCacheGroup(m, &m->rrcache_hash[slot]);
7325*4b22b933Srs200217 			}
7326*4b22b933Srs200217 		}
7327*4b22b933Srs200217 	debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active);
7328*4b22b933Srs200217 	if (rrcache_active != m->rrcache_active)
7329*4b22b933Srs200217 		LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active);
7330*4b22b933Srs200217 
7331*4b22b933Srs200217 	for (intf = m->HostInterfaces; intf; intf = intf->next)
7332*4b22b933Srs200217 		if (intf->Advertise)
7333*4b22b933Srs200217 			DeadvertiseInterface(m, intf);
7334*4b22b933Srs200217 
7335*4b22b933Srs200217 	// Make sure there are nothing but deregistering records remaining in the list
7336*4b22b933Srs200217 	if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
7337*4b22b933Srs200217 	m->CurrentRecord = m->ResourceRecords;
7338*4b22b933Srs200217 	while (m->CurrentRecord)
7339*4b22b933Srs200217 		{
7340*4b22b933Srs200217 		AuthRecord *rr = m->CurrentRecord;
7341*4b22b933Srs200217 		if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
7342*4b22b933Srs200217 			{
7343*4b22b933Srs200217 			debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c);
7344*4b22b933Srs200217 			mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
7345*4b22b933Srs200217 			}
7346*4b22b933Srs200217 		else
7347*4b22b933Srs200217 			m->CurrentRecord = rr->next;
7348*4b22b933Srs200217 		}
7349*4b22b933Srs200217 
7350*4b22b933Srs200217 	if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
7351*4b22b933Srs200217 	else debugf("mDNS_Close: No deregistering records remain");
7352*4b22b933Srs200217 
7353*4b22b933Srs200217 	// If any deregistering records remain, send their deregistration announcements before we exit
7354*4b22b933Srs200217 	if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m);
7355*4b22b933Srs200217 	else if (m->ResourceRecords) SendResponses(m);
7356*4b22b933Srs200217 	if (m->ResourceRecords) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m, m->ResourceRecords));
7357*4b22b933Srs200217 
7358*4b22b933Srs200217 	mDNS_Unlock(m);
7359*4b22b933Srs200217 	debugf("mDNS_Close: mDNSPlatformClose");
7360*4b22b933Srs200217 	mDNSPlatformClose(m);
7361*4b22b933Srs200217 	debugf("mDNS_Close: done");
7362*4b22b933Srs200217 	}
7363