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