]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
cc340f17 | 3 | * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. |
c9b9ae52 | 4 | * |
c9d2d929 | 5 | * @APPLE_LICENSE_HEADER_START@ |
c9b9ae52 | 6 | * |
c9d2d929 A |
7 | * This file contains Original Code and/or Modifications of Original Code |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
c9b9ae52 | 13 | * |
c9d2d929 A |
14 | * The Original Code and all software distributed under the License are |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
c9b9ae52 | 20 | * limitations under the License. |
c9d2d929 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
c9b9ae52 A |
23 | * |
24 | * This code is completely 100% portable C. It does not depend on any external header files | |
25 | * from outside the mDNS project -- all the types it expects to find are defined right here. | |
26 | * | |
27 | * The previous point is very important: This file does not depend on any external | |
28 | * header files. It should complile on *any* platform that has a C compiler, without | |
29 | * making *any* assumptions about availability of so-called "standard" C functions, | |
30 | * routines, or types (which may or may not be present on any given platform). | |
31 | ||
6528fe3e A |
32 | * Formatting notes: |
33 | * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion | |
34 | * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, | |
35 | * but for the sake of brevity here I will say just this: Curly braces are not syntactially | |
36 | * part of an "if" statement; they are the beginning and ending markers of a compound statement; | |
37 | * therefore common sense dictates that if they are part of a compound statement then they | |
38 | * should be indented to the same level as everything else in that compound statement. | |
39 | * Indenting curly braces at the same level as the "if" implies that curly braces are | |
40 | * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" | |
41 | * thinking that variables x and y are both of type "char*" -- and anyone who doesn't | |
42 | * understand why variable y is not of type "char*" just proves the point that poor code | |
43 | * layout leads people to unfortunate misunderstandings about how the C language really works.) | |
c9b9ae52 A |
44 | |
45 | Change History (most recent first): | |
46 | ||
47 | $Log: mDNS.c,v $ | |
cc340f17 A |
48 | Revision 1.535.2.3 2006/11/10 19:36:42 cheshire |
49 | Further refinement: Only harmonize TTL if the value we're adjusting it to is at least 2 seconds | |
50 | ||
51 | Revision 1.535.2.2 2006/10/31 02:11:26 cheshire | |
52 | Compile error: Need to put back AllDNSLinkGroupv4 definition | |
53 | ||
54 | Revision 1.535.2.1 2006/10/31 01:28:06 cheshire | |
55 | <rdar://problem/4456945> After service restarts on different port, for a few seconds DNS-SD may return stale port number | |
56 | ||
57 | Revision 1.535 2006/03/02 20:41:17 cheshire | |
58 | <rdar://problem/4111464> After record update, old record sometimes remains in cache | |
59 | Minor code tidying and comments to reduce the risk of similar programming errors in future | |
60 | ||
61 | Revision 1.534 2006/03/02 03:25:46 cheshire | |
62 | <rdar://problem/4111464> After record update, old record sometimes remains in cache | |
63 | Code to harmonize RRSet TTLs was inadvertently rescuing expiring records | |
64 | ||
65 | Revision 1.533 2006/02/26 00:54:41 cheshire | |
66 | Fixes to avoid code generation warning/error on FreeBSD 7 | |
67 | ||
68 | Revision 1.532 2005/12/02 20:24:36 cheshire | |
69 | <rdar://problem/4363209> Adjust cutoff time for KA list by one second | |
70 | ||
71 | Revision 1.531 2005/12/02 19:05:42 cheshire | |
72 | Tidy up constants | |
73 | ||
74 | Revision 1.530 2005/11/07 01:49:48 cheshire | |
75 | For consistency, use NonZeroTime() function instead of ?: expression | |
76 | ||
77 | Revision 1.529 2005/10/25 23:42:24 cheshire | |
78 | <rdar://problem/4316057> Error in ResolveSimultaneousProbe() when type or class don't match | |
79 | Changed switch statement to an "if" | |
80 | ||
81 | Revision 1.528 2005/10/25 23:34:22 cheshire | |
82 | <rdar://problem/4316048> RequireGoodbye state not set/respected sometimes when machine going to sleep | |
83 | ||
84 | Revision 1.527 2005/10/25 22:43:59 cheshire | |
85 | Add clarifying comments | |
86 | ||
28f7d060 A |
87 | Revision 1.526 2005/10/20 00:10:33 cheshire |
88 | <rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code | |
89 | ||
90 | Revision 1.525 2005/09/24 00:47:17 cheshire | |
91 | Fix comment typos | |
92 | ||
93 | Revision 1.524 2005/09/16 21:06:49 cheshire | |
94 | Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place | |
95 | ||
96 | Revision 1.523 2005/03/21 00:33:51 shersche | |
97 | <rdar://problem/4021486> Fix build warnings on Win32 platform | |
98 | ||
7cb34e5c A |
99 | Revision 1.522 2005/03/04 21:48:12 cheshire |
100 | <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity | |
101 | ||
102 | Revision 1.521 2005/02/25 04:21:00 cheshire | |
103 | <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing | |
104 | ||
105 | Revision 1.520 2005/02/16 01:14:11 cheshire | |
106 | Convert RR Cache LogOperation() calls to debugf() | |
107 | ||
108 | Revision 1.519 2005/02/15 01:57:20 cheshire | |
109 | When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero | |
110 | ||
111 | Revision 1.518 2005/02/10 22:35:17 cheshire | |
112 | <rdar://problem/3727944> Update name | |
113 | ||
114 | Revision 1.517 2005/02/03 00:21:21 cheshire | |
115 | Update comments about BIND named and zero-length TXT records | |
116 | ||
283ee3ff A |
117 | Revision 1.516 2005/01/28 06:06:32 cheshire |
118 | Update comment | |
119 | ||
120 | Revision 1.515 2005/01/27 00:21:49 cheshire | |
121 | <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message | |
122 | ||
123 | Revision 1.514 2005/01/21 01:33:45 cheshire | |
124 | <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM | |
125 | ||
126 | Revision 1.513 2005/01/21 00:07:54 cheshire | |
127 | <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict | |
128 | ||
129 | Revision 1.512 2005/01/20 00:37:45 cheshire | |
130 | <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse | |
131 | Take care not to recycle records while they are on the CacheFlushRecords list | |
132 | ||
133 | Revision 1.511 2005/01/19 22:48:53 cheshire | |
134 | <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService() | |
135 | ||
136 | Revision 1.510 2005/01/19 03:12:45 cheshire | |
137 | Move LocalRecordReady() macro from mDNS.c to DNSCommon.h | |
138 | ||
139 | Revision 1.509 2005/01/19 03:08:49 cheshire | |
140 | <rdar://problem/3961051> CPU Spin in mDNSResponder | |
141 | Log messages to help catch and report CPU spins | |
142 | ||
143 | Revision 1.508 2005/01/18 18:56:32 cheshire | |
144 | <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate | |
145 | ||
146 | Revision 1.507 2005/01/18 01:12:07 cheshire | |
147 | <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes | |
148 | ||
149 | Revision 1.506 2005/01/17 23:28:53 cheshire | |
150 | Fix compile error | |
151 | ||
152 | Revision 1.505 2005/01/11 02:02:56 shersche | |
153 | Move variable declaration to the beginning of statement block | |
154 | ||
155 | Revision 1.504 2004/12/20 20:24:35 cheshire | |
156 | <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer | |
157 | ||
158 | Revision 1.503 2004/12/20 18:41:47 cheshire | |
159 | <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space | |
160 | ||
161 | Revision 1.502 2004/12/20 18:04:08 cheshire | |
162 | <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache | |
163 | ||
164 | Revision 1.501 2004/12/19 23:50:18 cheshire | |
165 | <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records | |
166 | Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services | |
167 | ||
168 | Revision 1.500 2004/12/18 03:13:46 cheshire | |
169 | <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records | |
170 | ||
171 | Revision 1.499 2004/12/17 23:37:45 cheshire | |
172 | <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association | |
173 | (and other repetitive configuration changes) | |
174 | ||
175 | Revision 1.498 2004/12/17 05:25:46 cheshire | |
176 | <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs | |
177 | ||
178 | Revision 1.497 2004/12/17 03:20:58 cheshire | |
179 | <rdar://problem/3925168> Don't send unicast replies we know will be ignored | |
180 | ||
181 | Revision 1.496 2004/12/16 22:18:26 cheshire | |
182 | Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces | |
183 | ||
184 | Revision 1.495 2004/12/16 21:27:37 ksekar | |
185 | Fixed build failures when compiled with verbose debugging messages | |
186 | ||
187 | Revision 1.494 2004/12/16 20:46:56 cheshire | |
188 | Fix compiler warnings | |
189 | ||
190 | Revision 1.493 2004/12/16 20:13:00 cheshire | |
191 | <rdar://problem/3324626> Cache memory management improvements | |
192 | ||
193 | Revision 1.492 2004/12/16 08:03:24 shersche | |
194 | Fix compilation error when UNICAST_DISABLED is set | |
195 | ||
7f0064bd A |
196 | Revision 1.491 2004/12/11 01:52:11 cheshire |
197 | <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too | |
198 | ||
199 | Revision 1.490 2004/12/10 20:06:25 cheshire | |
200 | <rdar://problem/3915074> Reduce egregious stack space usage | |
201 | Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes | |
202 | ||
203 | Revision 1.489 2004/12/10 20:03:43 cheshire | |
204 | <rdar://problem/3915074> Reduce egregious stack space usage | |
205 | Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes | |
206 | ||
207 | Revision 1.488 2004/12/10 19:50:41 cheshire | |
208 | <rdar://problem/3915074> Reduce egregious stack space usage | |
209 | Reduced SendResponses() stack frame from 9K to 176 bytes | |
210 | ||
211 | Revision 1.487 2004/12/10 19:39:13 cheshire | |
212 | <rdar://problem/3915074> Reduce egregious stack space usage | |
213 | Reduced SendQueries() stack frame from 18K to 112 bytes | |
214 | ||
215 | Revision 1.486 2004/12/10 14:16:17 cheshire | |
216 | <rdar://problem/3889788> Relax update rate limiting | |
217 | We now allow an average rate of ten updates per minute. | |
218 | Updates in excess of that are rate limited, but more gently than before. | |
219 | ||
220 | Revision 1.485 2004/12/10 02:09:24 cheshire | |
221 | <rdar://problem/3898376> Modify default TTLs | |
222 | ||
223 | Revision 1.484 2004/12/09 03:15:40 ksekar | |
224 | <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains | |
225 | ||
226 | Revision 1.483 2004/12/07 23:00:14 ksekar | |
227 | <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration: | |
228 | Call RecordProbeFailure even if there is no record callback | |
229 | ||
230 | Revision 1.482 2004/12/07 22:49:06 cheshire | |
7cb34e5c | 231 | <rdar://problem/3908850> BIND doesn't allow zero-length TXT records |
7f0064bd A |
232 | |
233 | Revision 1.481 2004/12/07 21:26:04 ksekar | |
234 | <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration | |
235 | ||
236 | Revision 1.480 2004/12/07 20:42:33 cheshire | |
237 | Add explicit context parameter to mDNS_RemoveRecordFromService() | |
238 | ||
239 | Revision 1.479 2004/12/07 17:50:49 ksekar | |
7cb34e5c | 240 | <rdar://problem/3908850> BIND doesn't allow zero-length TXT records |
7f0064bd A |
241 | |
242 | Revision 1.478 2004/12/06 21:15:22 ksekar | |
243 | <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations | |
244 | ||
245 | Revision 1.477 2004/12/04 02:12:45 cheshire | |
246 | <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack | |
247 | ||
248 | Revision 1.476 2004/11/29 23:34:31 cheshire | |
249 | On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero | |
250 | is crude, and effectively halves the time resolution. The more selective NonZeroTime() function | |
251 | only nudges the time value to 1 if the interval calculation happens to result in the value zero. | |
252 | ||
253 | Revision 1.475 2004/11/29 23:13:31 cheshire | |
254 | <rdar://problem/3484552> All unique records in a set should have the cache flush bit set | |
255 | Additional check: Make sure we don't unnecessarily send packets containing only additionals. | |
256 | (This could occur with multi-packet KA lists, if the answer and additionals were marked | |
257 | by the query packet, and then the answer were later suppressed in a subsequent KA packet.) | |
258 | ||
259 | Revision 1.474 2004/11/29 17:18:12 cheshire | |
260 | Remove "Unknown DNS packet type" message for update responses | |
261 | ||
262 | Revision 1.473 2004/11/25 01:57:52 cheshire | |
263 | <rdar://problem/3484552> All unique records in a set should have the cache flush bit set | |
264 | ||
265 | Revision 1.472 2004/11/25 01:28:09 cheshire | |
266 | <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) | |
267 | ||
268 | Revision 1.471 2004/11/25 01:10:13 cheshire | |
269 | Move code to add additional records to a subroutine called AddAdditionalsToResponseList() | |
270 | ||
271 | Revision 1.470 2004/11/24 21:54:44 cheshire | |
272 | <rdar://problem/3894475> mDNSCore not receiving unicast responses properly | |
273 | ||
274 | Revision 1.469 2004/11/24 04:50:39 cheshire | |
275 | Minor tidying | |
276 | ||
277 | Revision 1.468 2004/11/24 01:47:07 cheshire | |
278 | <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success. | |
279 | ||
280 | Revision 1.467 2004/11/24 01:41:28 cheshire | |
281 | Rename CompleteProbing() to AcknowledgeRecord() | |
282 | ||
283 | Revision 1.466 2004/11/23 21:08:07 ksekar | |
284 | Don't use ID to demux multicast/unicast now that unicast uses random IDs | |
285 | ||
286 | Revision 1.465 2004/11/15 20:09:21 ksekar | |
287 | <rdar://problem/3719050> Wide Area support for Add/Remove record | |
288 | ||
289 | Revision 1.464 2004/11/03 01:44:36 cheshire | |
290 | Update debugging messages | |
291 | ||
292 | Revision 1.463 2004/10/29 02:38:48 cheshire | |
293 | Fix Windows compile errors | |
294 | ||
295 | Revision 1.462 2004/10/28 19:21:07 cheshire | |
296 | Guard against registering interface with zero InterfaceID | |
297 | ||
298 | Revision 1.461 2004/10/28 19:02:16 cheshire | |
299 | Remove \n from LogMsg() call | |
300 | ||
301 | Revision 1.460 2004/10/28 03:24:40 cheshire | |
302 | Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 | |
303 | ||
304 | Revision 1.459 2004/10/26 22:34:37 cheshire | |
305 | <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding | |
306 | ||
307 | Revision 1.458 2004/10/26 20:45:28 cheshire | |
308 | Show mask in "invalid mask" message | |
309 | ||
310 | Revision 1.457 2004/10/26 06:28:36 cheshire | |
311 | Now that we don't check IP TTL any more, remove associated log message | |
312 | ||
313 | Revision 1.456 2004/10/26 06:21:42 cheshire | |
314 | Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address) | |
315 | ||
316 | Revision 1.455 2004/10/26 06:11:40 cheshire | |
317 | Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed | |
318 | ||
319 | Revision 1.454 2004/10/23 01:16:00 cheshire | |
320 | <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts | |
321 | ||
322 | Revision 1.453 2004/10/22 20:52:06 ksekar | |
323 | <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries | |
324 | ||
325 | Revision 1.452 2004/10/20 01:50:40 cheshire | |
326 | <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API | |
327 | Implemented ForceMCast mode for AuthRecords as well as for Questions | |
328 | ||
329 | Revision 1.451 2004/10/19 21:33:15 cheshire | |
330 | <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API | |
331 | Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name | |
332 | doesn't force multicast unless you set this flag to indicate explicitly that this is what you want | |
333 | ||
334 | Revision 1.450 2004/10/19 17:42:59 ksekar | |
335 | Fixed compiler warnings for non-debug builds. | |
336 | ||
337 | Revision 1.449 2004/10/18 22:57:07 cheshire | |
338 | <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1 | |
339 | ||
340 | Revision 1.448 2004/10/16 00:16:59 cheshire | |
341 | <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check | |
342 | ||
343 | Revision 1.447 2004/10/15 00:51:21 cheshire | |
344 | <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1 | |
345 | ||
346 | Revision 1.446 2004/10/14 00:43:34 cheshire | |
347 | <rdar://problem/3815984> Services continue to announce SRV and HINFO | |
348 | ||
349 | Revision 1.445 2004/10/12 21:07:09 cheshire | |
350 | Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit() | |
351 | ||
352 | Revision 1.444 2004/10/11 17:54:16 ksekar | |
353 | Changed hashtable pointer output from debugf to verbosedebugf. | |
354 | ||
355 | Revision 1.443 2004/10/10 07:05:45 cheshire | |
356 | For consistency, use symbol "localdomain" instead of literal string | |
357 | ||
358 | Revision 1.442 2004/10/08 20:25:10 cheshire | |
359 | Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time | |
360 | ||
361 | Revision 1.441 2004/10/08 03:25:01 ksekar | |
362 | <rdar://problem/3831716> domain enumeration should use LLQs | |
363 | ||
364 | Revision 1.440 2004/10/06 01:44:19 cheshire | |
365 | <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record | |
366 | ||
367 | Revision 1.439 2004/10/03 23:14:11 cheshire | |
368 | Add "mDNSEthAddr" type and "zeroEthAddr" constant | |
369 | ||
370 | Revision 1.438 2004/09/29 23:07:04 cheshire | |
371 | Patch from Pavel Repin to fix compile error on Windows | |
372 | ||
373 | Revision 1.437 2004/09/28 02:23:50 cheshire | |
374 | <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events | |
375 | Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot | |
376 | For records with the cache flush bit set, defer the decision until the end of the packet | |
377 | ||
378 | Revision 1.436 2004/09/28 01:27:04 cheshire | |
379 | Update incorrect log message | |
380 | ||
381 | Revision 1.435 2004/09/25 02:41:39 cheshire | |
382 | <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events | |
383 | ||
384 | Revision 1.434 2004/09/25 02:32:06 cheshire | |
385 | Update comments | |
386 | ||
387 | Revision 1.433 2004/09/25 02:24:27 cheshire | |
388 | Removed unused rr->UseCount | |
389 | ||
390 | Revision 1.432 2004/09/24 21:35:17 cheshire | |
391 | <rdar://problem/3561220> Browses are no longer piggybacking on other browses | |
392 | TargetPort and TargetQID are allowed to be undefined if no question->Target is set | |
393 | ||
394 | Revision 1.431 2004/09/24 21:33:12 cheshire | |
395 | Adjust comment | |
396 | ||
397 | Revision 1.430 2004/09/24 02:15:49 cheshire | |
398 | <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces | |
399 | ||
400 | Revision 1.429 2004/09/24 00:20:21 cheshire | |
401 | <rdar://problem/3483349> Any rrtype is a conflict for unique records | |
402 | ||
403 | Revision 1.428 2004/09/24 00:12:25 cheshire | |
404 | Get rid of unused RRUniqueOrKnownUnique(RR) | |
405 | ||
406 | Revision 1.427 2004/09/23 20:44:11 cheshire | |
407 | <rdar://problem/3813148> Reduce timeout before expiring records on failure | |
408 | ||
409 | Revision 1.426 2004/09/23 20:21:07 cheshire | |
410 | <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic | |
411 | Associate a unique sequence number with each received packet, and only increment the count of recent answer | |
412 | packets if the packet sequence number for this answer record is not one we've already seen and counted. | |
413 | ||
414 | Revision 1.425 2004/09/23 20:14:38 cheshire | |
415 | Rename "question->RecentAnswers" to "question->RecentAnswerPkts" | |
416 | ||
417 | Revision 1.424 2004/09/23 00:58:36 cheshire | |
418 | <rdar://problem/3781269> Rate limiting interferes with updating TXT records | |
419 | ||
420 | Revision 1.423 2004/09/23 00:50:53 cheshire | |
421 | <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep | |
422 | ||
423 | Revision 1.422 2004/09/22 02:34:46 cheshire | |
424 | Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h | |
425 | ||
426 | Revision 1.421 2004/09/21 23:29:49 cheshire | |
427 | <rdar://problem/3680045> DNSServiceResolve should delay sending packets | |
428 | ||
429 | Revision 1.420 2004/09/21 23:01:42 cheshire | |
430 | Update debugf messages | |
431 | ||
432 | Revision 1.419 2004/09/21 19:51:14 cheshire | |
433 | Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c | |
434 | ||
435 | Revision 1.418 2004/09/21 18:40:17 cheshire | |
436 | <rdar://problem/3376752> Adjust default record TTLs | |
437 | ||
438 | Revision 1.417 2004/09/21 17:32:16 cheshire | |
439 | <rdar://problem/3809484> Rate limiting imposed too soon | |
440 | ||
441 | Revision 1.416 2004/09/20 23:52:01 cheshire | |
442 | CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c | |
443 | ||
444 | Revision 1.415 2004/09/18 01:14:09 cheshire | |
445 | <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces | |
446 | ||
447 | Revision 1.414 2004/09/18 01:06:48 cheshire | |
448 | Add comments | |
449 | ||
450 | Revision 1.413 2004/09/17 01:08:48 cheshire | |
451 | Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h | |
452 | The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces | |
453 | declared in that file are ONLY appropriate to single-address-space embedded applications. | |
454 | For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. | |
455 | ||
456 | Revision 1.412 2004/09/17 00:46:33 cheshire | |
457 | mDNS_TimeNow should take const mDNS parameter | |
458 | ||
459 | Revision 1.411 2004/09/17 00:31:51 cheshire | |
460 | For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' | |
461 | ||
462 | Revision 1.410 2004/09/17 00:19:10 cheshire | |
463 | For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 | |
464 | ||
465 | Revision 1.409 2004/09/16 21:59:15 cheshire | |
466 | For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr | |
467 | ||
468 | Revision 1.408 2004/09/16 21:36:36 cheshire | |
469 | <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() | |
470 | Changes to add necessary locking calls around unicast DNS operations | |
471 | ||
472 | Revision 1.407 2004/09/16 02:29:39 cheshire | |
473 | Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around | |
474 | uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService | |
475 | ||
476 | Revision 1.406 2004/09/16 01:58:14 cheshire | |
477 | Fix compiler warnings | |
478 | ||
479 | Revision 1.405 2004/09/16 00:24:48 cheshire | |
480 | <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() | |
481 | ||
482 | Revision 1.404 2004/09/15 21:44:11 cheshire | |
483 | <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init | |
484 | Show time value in log to help diagnose errors | |
485 | ||
486 | Revision 1.403 2004/09/15 00:46:32 ksekar | |
487 | Changed debugf to verbosedebugf in CheckCacheExpiration | |
488 | ||
489 | Revision 1.402 2004/09/14 23:59:55 cheshire | |
490 | <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init | |
491 | ||
492 | Revision 1.401 2004/09/14 23:27:46 cheshire | |
493 | Fix compile errors | |
494 | ||
495 | Revision 1.400 2004/09/02 03:48:47 cheshire | |
496 | <rdar://problem/3709039> Disable targeted unicast query support by default | |
497 | 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record | |
498 | 2. New field AllowRemoteQuery in AuthRecord structure | |
499 | 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set | |
500 | 4. mDNS.c only answers remote queries if AllowRemoteQuery is set | |
501 | ||
502 | Revision 1.399 2004/09/02 01:39:40 cheshire | |
503 | For better readability, follow consistent convention that QR bit comes first, followed by OP bits | |
504 | ||
505 | Revision 1.398 2004/09/01 03:59:29 ksekar | |
506 | <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows | |
507 | ||
508 | Revision 1.397 2004/08/25 22:04:25 rpantos | |
509 | Fix the standard Windows compile error. | |
510 | ||
511 | Revision 1.396 2004/08/25 00:37:27 ksekar | |
512 | <rdar://problem/3774635>: Cleanup DynDNS hostname registration code | |
513 | ||
514 | Revision 1.395 2004/08/18 17:21:18 ksekar | |
515 | Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs() | |
516 | ||
517 | Revision 1.394 2004/08/14 03:22:41 cheshire | |
518 | <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue | |
519 | Add GetUserSpecifiedDDNSName() routine | |
520 | Convert ServiceRegDomain to domainname instead of C string | |
521 | Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs | |
522 | ||
523 | Revision 1.393 2004/08/13 23:42:52 cheshire | |
524 | Removed unused "zeroDomainNamePtr" | |
525 | ||
526 | Revision 1.392 2004/08/13 23:37:02 cheshire | |
527 | Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with | |
528 | "uDNS_info.UnicastHostname" for clarity | |
529 | ||
530 | Revision 1.391 2004/08/13 23:25:00 cheshire | |
531 | Now that we do both uDNS and mDNS, global replace "m->hostname" with | |
532 | "m->MulticastHostname" for clarity | |
533 | ||
534 | Revision 1.390 2004/08/11 02:17:01 cheshire | |
535 | <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record | |
536 | ||
537 | Revision 1.389 2004/08/10 23:19:14 ksekar | |
7cb34e5c | 538 | <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery |
7f0064bd A |
539 | Moved routines/constants to allow extern access for garbage collection daemon |
540 | ||
541 | Revision 1.388 2004/07/30 17:40:06 ksekar | |
542 | <rdar://problem/3739115>: TXT Record updates not available for wide-area services | |
543 | ||
544 | Revision 1.387 2004/07/26 22:49:30 ksekar | |
cc340f17 | 545 | <rdar://problem/3651409>: Feature #9516: Need support for NAT-PMP in client |
7f0064bd A |
546 | |
547 | Revision 1.386 2004/07/13 21:24:24 rpantos | |
548 | Fix for <rdar://problem/3701120>. | |
549 | ||
550 | Revision 1.385 2004/06/18 19:09:59 cheshire | |
551 | <rdar://problem/3588761> Current method of doing subtypes causes name collisions | |
552 | ||
553 | Revision 1.384 2004/06/15 04:31:23 cheshire | |
554 | Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion() | |
555 | ||
556 | Revision 1.383 2004/06/11 00:04:59 cheshire | |
557 | <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord | |
558 | ||
8e92c31c A |
559 | Revision 1.382 2004/06/08 04:59:40 cheshire |
560 | Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it | |
561 | ||
562 | Revision 1.381 2004/06/05 00:57:30 cheshire | |
563 | Remove incorrect LogMsg() | |
564 | ||
565 | Revision 1.380 2004/06/05 00:04:26 cheshire | |
566 | <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration | |
567 | ||
568 | Revision 1.379 2004/05/28 23:42:36 ksekar | |
569 | <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) | |
570 | ||
571 | Revision 1.378 2004/05/25 17:25:25 cheshire | |
572 | Remove extraneous blank lines and white space | |
573 | ||
574 | Revision 1.377 2004/05/18 23:51:25 cheshire | |
575 | Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers | |
576 | ||
577 | Revision 1.376 2004/05/05 18:30:44 ksekar | |
578 | Restored surpressed Cache Tail debug messages. | |
579 | ||
580 | Revision 1.375 2004/04/26 21:36:25 cheshire | |
581 | Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive | |
582 | is indicated as being available on that interface | |
583 | ||
584 | Revision 1.374 2004/04/21 02:53:26 cheshire | |
585 | Typo in debugf statement | |
586 | ||
587 | Revision 1.373 2004/04/21 02:49:11 cheshire | |
588 | To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' | |
589 | ||
590 | Revision 1.372 2004/04/21 02:38:51 cheshire | |
591 | Add debugging checks | |
592 | ||
593 | Revision 1.371 2004/04/14 23:09:28 ksekar | |
594 | Support for TSIG signed dynamic updates. | |
595 | ||
596 | Revision 1.370 2004/04/09 17:40:26 cheshire | |
597 | Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field | |
598 | ||
599 | Revision 1.369 2004/04/09 16:34:00 cheshire | |
600 | Debugging code for later; currently unused | |
601 | ||
602 | Revision 1.368 2004/04/02 19:19:48 cheshire | |
603 | Add code to do optional logging of multi-packet KA list time intervals | |
604 | ||
605 | Revision 1.367 2004/03/20 03:16:10 cheshire | |
606 | Minor refinement to "Excessive update rate" message | |
b7388343 | 607 | |
8e92c31c A |
608 | Revision 1.366 2004/03/20 03:12:57 cheshire |
609 | <rdar://problem/3587619>: UpdateCredits not granted promptly enough | |
b7388343 | 610 | |
8e92c31c A |
611 | Revision 1.365 2004/03/19 23:51:22 cheshire |
612 | Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60) | |
b7388343 | 613 | |
8e92c31c A |
614 | Revision 1.364 2004/03/13 01:57:33 ksekar |
615 | <rdar://problem/3192546>: DynDNS: Dynamic update of service records | |
b7388343 | 616 | |
8e92c31c A |
617 | Revision 1.363 2004/03/12 21:00:51 cheshire |
618 | Also show port numbers when logging "apparent spoof mDNS Response" messages | |
b7388343 | 619 | |
8e92c31c A |
620 | Revision 1.362 2004/03/12 08:58:18 cheshire |
621 | Guard against empty TXT records | |
73792575 | 622 | |
8e92c31c A |
623 | Revision 1.361 2004/03/09 03:00:46 cheshire |
624 | <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good. | |
73792575 | 625 | |
8e92c31c A |
626 | Revision 1.360 2004/03/08 02:52:41 cheshire |
627 | Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages | |
73792575 | 628 | |
8e92c31c | 629 | Revision 1.359 2004/03/02 03:21:56 cheshire |
8d1ca615 A |
630 | <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries |
631 | ||
8e92c31c A |
632 | Revision 1.358 2004/02/20 08:18:34 cheshire |
633 | <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily | |
8d1ca615 | 634 | |
8e92c31c A |
635 | Revision 1.357 2004/02/18 01:47:41 cheshire |
636 | <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms | |
54811a41 | 637 | |
8e92c31c A |
638 | Revision 1.356 2004/02/06 23:04:19 ksekar |
639 | Basic Dynamic Update support via mDNS_Register (dissabled via | |
640 | UNICAST_REGISTRATION #define) | |
641 | ||
642 | Revision 1.355 2004/02/05 09:32:33 cheshire | |
643 | Fix from Bob Bradley: When using the "%.*s" string form, | |
644 | guard against truncating in the middle of a multi-byte UTF-8 character. | |
645 | ||
646 | Revision 1.354 2004/02/05 09:30:22 cheshire | |
647 | Update comments | |
648 | ||
649 | Revision 1.353 2004/01/28 03:41:00 cheshire | |
650 | <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries | |
651 | ||
652 | Revision 1.352 2004/01/28 02:30:07 ksekar | |
653 | Added default Search Domains to unicast browsing, controlled via | |
654 | Networking sharing prefs pane. Stopped sending unicast messages on | |
655 | every interface. Fixed unicast resolving via mach-port API. | |
656 | ||
657 | Revision 1.351 2004/01/27 20:15:22 cheshire | |
658 | <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 | |
659 | ||
660 | Revision 1.350 2004/01/24 23:38:16 cheshire | |
661 | Use mDNSVal16() instead of shifting and ORing operations | |
662 | ||
663 | Revision 1.349 2004/01/23 23:23:14 ksekar | |
664 | Added TCP support for truncated unicast messages. | |
665 | ||
666 | Revision 1.348 2004/01/22 03:54:11 cheshire | |
667 | Create special meta-interface 'mDNSInterface_ForceMCast' (-2), | |
668 | which means "do this query via multicast, even if it's apparently a unicast domain" | |
669 | ||
670 | Revision 1.347 2004/01/22 03:50:49 cheshire | |
671 | If the client has specified an explicit InterfaceID, then do query by multicast, not unicast | |
672 | ||
673 | Revision 1.346 2004/01/22 03:48:41 cheshire | |
674 | Make sure uDNS client doesn't accidentally use query ID zero | |
675 | ||
676 | Revision 1.345 2004/01/22 03:43:08 cheshire | |
677 | Export constants like mDNSInterface_LocalOnly so that the client layers can use them | |
678 | ||
679 | Revision 1.344 2004/01/21 21:53:18 cheshire | |
680 | <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port | |
681 | ||
682 | Revision 1.343 2003/12/23 00:07:47 cheshire | |
683 | Make port number in debug message be five-character field, left justified | |
684 | ||
685 | Revision 1.342 2003/12/20 01:34:28 cheshire | |
8abd1236 A |
686 | <rdar://problem/3515876>: Error putting additional records into packets |
687 | Another fix from Rampi: responseptr needs to be updated inside the "for" loop, | |
688 | after every record, not once at the end. | |
689 | ||
8e92c31c A |
690 | Revision 1.341 2003/12/18 22:56:12 cheshire |
691 | <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets | |
692 | ||
693 | Revision 1.340 2003/12/16 02:31:37 cheshire | |
694 | Minor update to comments | |
695 | ||
696 | Revision 1.339 2003/12/13 05:50:33 bradley | |
697 | Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform | |
698 | layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to | |
699 | fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf. | |
700 | ||
701 | Revision 1.338 2003/12/13 03:05:27 ksekar | |
702 | <rdar://problem/3192548>: DynDNS: Unicast query of service records | |
703 | ||
704 | Revision 1.337 2003/12/01 21:46:05 cheshire | |
705 | mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist | |
706 | ||
707 | Revision 1.336 2003/12/01 21:26:19 cheshire | |
708 | Guard against zero-length sbuffer in mDNS_vsnprintf() | |
709 | ||
710 | Revision 1.335 2003/12/01 20:27:48 cheshire | |
711 | Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors | |
712 | ||
713 | Revision 1.334 2003/11/20 22:59:53 cheshire | |
7f0064bd | 714 | Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h |
8e92c31c A |
715 | Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. |
716 | ||
717 | Revision 1.333 2003/11/20 20:49:53 cheshire | |
718 | Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures | |
719 | ||
720 | Revision 1.332 2003/11/20 05:47:37 cheshire | |
721 | <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query | |
722 | Now that we only include answers in the known answer list if they are less than | |
723 | halfway to expiry, the check to also see if we have another query scheduled | |
724 | before the record expires is no longer necessary (and in fact, not correct). | |
725 | ||
726 | Revision 1.331 2003/11/19 22:31:48 cheshire | |
727 | When automatically adding A records to SRVs, add them as additionals, not answers | |
728 | ||
729 | Revision 1.330 2003/11/19 22:28:50 cheshire | |
730 | Increment/Decrement mDNS_reentrancy around calls to m->MainCallback() | |
731 | to allow client to make mDNS calls (specifically the call to mDNS_GrowCache()) | |
732 | ||
733 | Revision 1.329 2003/11/19 22:19:24 cheshire | |
734 | Show log message when ignoring packets with bad TTL. | |
735 | This is to help diagnose problems on Linux versions that may not report the TTL reliably. | |
736 | ||
737 | Revision 1.328 2003/11/19 22:06:38 cheshire | |
738 | Show log messages when a service or hostname is renamed | |
739 | ||
740 | Revision 1.327 2003/11/19 22:03:44 cheshire | |
741 | Move common "m->NextScheduledResponse = m->timenow" to before "if" statement | |
742 | ||
743 | Revision 1.326 2003/11/17 22:27:02 cheshire | |
744 | Another fix from ramaprasad.kr@hp.com: Improve reply delay computation | |
745 | on platforms that have native clock rates below fifty ticks per second. | |
746 | ||
747 | Revision 1.325 2003/11/17 20:41:44 cheshire | |
748 | Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls. | |
749 | ||
750 | Revision 1.324 2003/11/17 20:36:32 cheshire | |
751 | Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and | |
752 | DeadvertiseInterface() -- they're internal private routines, not API routines. | |
753 | ||
754 | Revision 1.323 2003/11/14 20:59:08 cheshire | |
755 | Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. | |
7f0064bd | 756 | Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. |
8e92c31c A |
757 | |
758 | Revision 1.322 2003/11/14 19:47:52 cheshire | |
759 | Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString | |
760 | ||
761 | Revision 1.321 2003/11/14 19:18:34 cheshire | |
7f0064bd | 762 | Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too |
8e92c31c A |
763 | |
764 | Revision 1.320 2003/11/13 06:45:04 cheshire | |
765 | Fix compiler warning on certain compilers | |
766 | ||
767 | Revision 1.319 2003/11/13 00:47:40 cheshire | |
768 | <rdar://problem/3437556> We should delay AAAA record query if A record already in cache. | |
769 | ||
770 | Revision 1.318 2003/11/13 00:33:26 cheshire | |
771 | Change macro "RRIsAddressType" to "RRTypeIsAddressType" | |
772 | ||
773 | Revision 1.317 2003/11/13 00:10:49 cheshire | |
774 | <rdar://problem/3436412>: Verify that rr data is different before updating. | |
775 | ||
776 | Revision 1.316 2003/11/08 23:37:54 cheshire | |
777 | Give explicit zero initializers to blank static structure, required by certain compilers. | |
778 | (Thanks to ramaprasad.kr@hp.com for reporting this.) | |
779 | ||
780 | Revision 1.315 2003/11/07 03:32:56 cheshire | |
781 | <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order | |
782 | This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes | |
783 | purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is | |
784 | to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() | |
785 | can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. | |
786 | ||
787 | Revision 1.314 2003/11/07 03:19:49 cheshire | |
788 | Minor variable renaming for clarity | |
789 | ||
790 | Revision 1.313 2003/11/07 03:14:49 cheshire | |
791 | Previous checkin proved to be overly simplistic; reversing | |
792 | ||
793 | Revision 1.312 2003/11/03 23:45:15 cheshire | |
794 | <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order | |
795 | Build cache lists in FIFO order, not customary C LIFO order | |
796 | (Append new elements to tail of cache list, instead of prepending at the head.) | |
797 | ||
798 | Revision 1.311 2003/10/09 18:00:11 cheshire | |
799 | Another compiler warning fix. | |
800 | ||
801 | Revision 1.310 2003/10/07 20:27:05 cheshire | |
802 | Patch from Bob Bradley, to fix warning and compile error on Windows | |
803 | ||
804 | Revision 1.309 2003/09/26 01:06:36 cheshire | |
805 | <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too | |
806 | Made new routine HaveSentEntireRRSet() to check if flag should be set | |
807 | ||
808 | Revision 1.308 2003/09/23 01:05:01 cheshire | |
809 | Minor changes to comments and debugf() message | |
716635cc | 810 | |
c9b9ae52 A |
811 | Revision 1.307 2003/09/09 20:13:30 cheshire |
812 | <rdar://problem/3411105> Don't send a Goodbye record if we never announced it | |
813 | Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented | |
814 | rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount | |
815 | ||
816 | Revision 1.306 2003/09/09 03:00:03 cheshire | |
817 | <rdar://problem/3413099> Services take a long time to disappear when switching networks. | |
818 | Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect | |
819 | ||
820 | Revision 1.305 2003/09/09 02:49:31 cheshire | |
821 | <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep | |
822 | ||
823 | Revision 1.304 2003/09/09 02:41:19 cheshire | |
824 | <rdar://problem/3411105> Don't send a Goodbye record if we never announced it | |
825 | ||
826 | Revision 1.303 2003/09/05 19:55:02 cheshire | |
827 | <rdar://problem/3409533> Include address records when announcing SRV records | |
828 | ||
829 | Revision 1.302 2003/09/05 00:01:36 cheshire | |
830 | <rdar://problem/3407549> Don't accelerate queries that have large KA lists | |
831 | ||
832 | Revision 1.301 2003/09/04 22:51:13 cheshire | |
833 | <rdar://problem/3398213> Group probes and goodbyes better | |
834 | ||
835 | Revision 1.300 2003/09/03 02:40:37 cheshire | |
836 | <rdar://problem/3404842> mDNSResponder complains about '_'s | |
837 | Underscores are not supposed to be legal in standard DNS names, but IANA appears | |
838 | to have allowed them in previous service name registrations, so we should too. | |
839 | ||
840 | Revision 1.299 2003/09/03 02:33:09 cheshire | |
841 | <rdar://problem/3404795> CacheRecordRmv ERROR | |
842 | Don't update m->NewQuestions until *after* CheckCacheExpiration(); | |
843 | ||
844 | Revision 1.298 2003/09/03 01:47:01 cheshire | |
7f0064bd | 845 | <rdar://problem/3319418> Services always in a state of flux |
c9b9ae52 A |
846 | Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds |
847 | ||
848 | Revision 1.297 2003/08/29 19:44:15 cheshire | |
849 | <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears | |
850 | 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries | |
851 | that already have at least one unique answer in the cache | |
852 | 2. For these queries, go straight to QM, skipping QU | |
853 | ||
854 | Revision 1.296 2003/08/29 19:08:21 cheshire | |
855 | <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep | |
856 | Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time. | |
857 | ||
858 | Revision 1.295 2003/08/28 01:10:59 cheshire | |
859 | <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst | |
860 | ||
861 | Revision 1.294 2003/08/27 02:30:22 cheshire | |
862 | <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() | |
863 | One more change: "query->GotTXT" is now a straightforward bi-state boolean again | |
864 | ||
865 | Revision 1.293 2003/08/27 02:25:31 cheshire | |
866 | <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() | |
867 | ||
868 | Revision 1.292 2003/08/21 19:27:36 cheshire | |
869 | <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL | |
870 | ||
871 | Revision 1.291 2003/08/21 18:57:44 cheshire | |
872 | <rdar://problem/3387140> Synchronized queries on the network | |
873 | ||
874 | Revision 1.290 2003/08/21 02:25:23 cheshire | |
875 | Minor changes to comments and debugf() messages | |
876 | ||
877 | Revision 1.289 2003/08/21 02:21:50 cheshire | |
878 | <rdar://problem/3386473> Efficiency: Reduce repeated queries | |
879 | ||
880 | Revision 1.288 2003/08/20 23:39:30 cheshire | |
881 | <rdar://problem/3344098> Review syslog messages, and remove as appropriate | |
882 | ||
883 | Revision 1.287 2003/08/20 20:47:18 cheshire | |
884 | Fix compiler warning | |
885 | ||
886 | Revision 1.286 2003/08/20 02:18:51 cheshire | |
887 | <rdar://problem/3344098> Cleanup: Review syslog messages | |
888 | ||
889 | Revision 1.285 2003/08/20 01:59:06 cheshire | |
890 | <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata | |
891 | Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place | |
892 | ||
893 | Revision 1.284 2003/08/19 22:20:00 cheshire | |
894 | <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured | |
895 | More minor refinements | |
896 | ||
897 | Revision 1.283 2003/08/19 22:16:27 cheshire | |
898 | Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case. | |
899 | ||
900 | Revision 1.282 2003/08/19 06:48:25 cheshire | |
901 | <rdar://problem/3376552> Guard against excessive record updates | |
902 | Each record starts with 10 UpdateCredits. | |
903 | Every update consumes one UpdateCredit. | |
904 | UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. | |
905 | As the number of UpdateCredits declines, the number of announcements is similarly scaled back. | |
906 | When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. | |
907 | ||
908 | Revision 1.281 2003/08/19 04:49:28 cheshire | |
909 | <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right | |
910 | 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. | |
911 | 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. | |
912 | 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. | |
913 | ||
914 | Revision 1.280 2003/08/19 02:33:36 cheshire | |
915 | Update comments | |
916 | ||
917 | Revision 1.279 2003/08/19 02:31:11 cheshire | |
918 | <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries | |
919 | Final expiration queries now only mark the question for sending on the particular interface | |
920 | pertaining to the record that's expiring. | |
921 | ||
922 | Revision 1.278 2003/08/18 22:53:37 cheshire | |
7f0064bd | 923 | <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime() |
c9b9ae52 A |
924 | |
925 | Revision 1.277 2003/08/18 19:05:44 cheshire | |
926 | <rdar://problem/3382423> UpdateRecord not working right | |
927 | Added "newrdlength" field to hold new length of updated rdata | |
928 | ||
929 | Revision 1.276 2003/08/16 03:39:00 cheshire | |
930 | <rdar://problem/3338440> InterfaceID -1 indicates "local only" | |
931 | ||
932 | Revision 1.275 2003/08/16 02:51:27 cheshire | |
933 | <rdar://problem/3366590> mDNSResponder takes too much RPRVT | |
934 | Don't try to compute namehash etc, until *after* validating the name | |
935 | ||
936 | Revision 1.274 2003/08/16 01:12:40 cheshire | |
937 | <rdar://problem/3366590> mDNSResponder takes too much RPRVT | |
938 | Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a | |
939 | simple C structure assignment of a domainname, because that object is defined to be 256 bytes long, | |
940 | and in the process of copying it, the C compiler may run off the end of the rdata object into | |
941 | unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a | |
942 | call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid. | |
943 | ||
944 | Revision 1.273 2003/08/15 20:16:02 cheshire | |
945 | <rdar://problem/3366590> mDNSResponder takes too much RPRVT | |
946 | We want to avoid touching the rdata pages, so we don't page them in. | |
947 | 1. RDLength was stored with the rdata, which meant touching the page just to find the length. | |
948 | Moved this from the RData to the ResourceRecord object. | |
949 | 2. To avoid unnecessarily touching the rdata just to compare it, | |
950 | compute a hash of the rdata and store the hash in the ResourceRecord object. | |
951 | ||
952 | Revision 1.272 2003/08/14 19:29:04 cheshire | |
953 | <rdar://problem/3378473> Include cache records in SIGINFO output | |
7f0064bd | 954 | Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them |
c9b9ae52 A |
955 | |
956 | Revision 1.271 2003/08/14 02:17:05 cheshire | |
957 | <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord | |
958 | ||
959 | Revision 1.270 2003/08/13 17:07:28 ksekar | |
8e92c31c | 960 | <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash |
c9b9ae52 A |
961 | Added check to result of mDNS_Register() before linking extra record into list. |
962 | ||
963 | Revision 1.269 2003/08/12 19:56:23 cheshire | |
964 | Update to APSL 2.0 | |
965 | ||
966 | Revision 1.268 2003/08/12 15:01:10 cheshire | |
967 | Add comments | |
968 | ||
969 | Revision 1.267 2003/08/12 14:59:27 cheshire | |
970 | <rdar://problem/3374490> Rate-limiting blocks some legitimate responses | |
971 | When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine | |
972 | whether to suppress the response, also check LastMCInterface to see if it matches. | |
973 | ||
974 | Revision 1.266 2003/08/12 12:47:16 cheshire | |
975 | In mDNSCoreMachineSleep debugf message, display value of m->timenow | |
976 | ||
977 | Revision 1.265 2003/08/11 20:04:28 cheshire | |
978 | <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache | |
979 | ||
980 | Revision 1.264 2003/08/09 00:55:02 cheshire | |
981 | <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU | |
982 | Don't scan the whole cache after every packet. | |
983 | ||
984 | Revision 1.263 2003/08/09 00:35:29 cheshire | |
985 | Moved AnswerNewQuestion() later in the file, in preparation for next checkin | |
986 | ||
987 | Revision 1.262 2003/08/08 19:50:33 cheshire | |
988 | <rdar://problem/3370332> Remove "Cache size now xxx" messages | |
989 | ||
990 | Revision 1.261 2003/08/08 19:18:45 cheshire | |
991 | <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug | |
992 | ||
993 | Revision 1.260 2003/08/08 18:55:48 cheshire | |
994 | <rdar://problem/3370365> Guard against time going backwards | |
995 | ||
996 | Revision 1.259 2003/08/08 18:36:04 cheshire | |
997 | <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug | |
998 | ||
999 | Revision 1.258 2003/08/08 16:22:05 cheshire | |
1000 | <rdar://problem/3335473> Need to check validity of TXT (and other) records | |
1001 | Remove unneeded LogMsg | |
1002 | ||
1003 | Revision 1.257 2003/08/07 01:41:08 cheshire | |
1004 | <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones) | |
1005 | ||
1006 | Revision 1.256 2003/08/06 23:25:51 cheshire | |
1007 | <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four | |
1008 | ||
1009 | Revision 1.255 2003/08/06 23:22:50 cheshire | |
1010 | Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours) | |
1011 | ||
1012 | Revision 1.254 2003/08/06 21:33:39 cheshire | |
1013 | Fix compiler warnings on PocketPC 2003 (Windows CE) | |
1014 | ||
1015 | Revision 1.253 2003/08/06 20:43:57 cheshire | |
1016 | <rdar://problem/3335473> Need to check validity of TXT (and other) records | |
1017 | Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update() | |
1018 | ||
1019 | Revision 1.252 2003/08/06 20:35:47 cheshire | |
1020 | Enhance debugging routine GetRRDisplayString() so it can also be used to display | |
1021 | other RDataBody objects, not just the one currently attached the given ResourceRecord | |
1022 | ||
1023 | Revision 1.251 2003/08/06 19:07:34 cheshire | |
1024 | <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should | |
1025 | Was checking LastAPTime instead of LastMCTime | |
1026 | ||
1027 | Revision 1.250 2003/08/06 19:01:55 cheshire | |
1028 | Update comments | |
1029 | ||
1030 | Revision 1.249 2003/08/06 00:13:28 cheshire | |
1031 | Tidy up debugf messages | |
1032 | ||
1033 | Revision 1.248 2003/08/05 22:20:15 cheshire | |
1034 | <rdar://problem/3330324> Need to check IP TTL on responses | |
1035 | ||
1036 | Revision 1.247 2003/08/05 00:56:39 cheshire | |
1037 | <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed | |
1038 | ||
1039 | Revision 1.246 2003/08/04 19:20:49 cheshire | |
1040 | Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages | |
1041 | ||
1042 | Revision 1.245 2003/08/02 01:56:29 cheshire | |
1043 | For debugging: log message if we ever get more than one question in a truncated packet | |
1044 | ||
1045 | Revision 1.244 2003/08/01 23:55:32 cheshire | |
1046 | Fix for compiler warnings on Windows, submitted by Bob Bradley | |
1047 | ||
1048 | Revision 1.243 2003/07/25 02:26:09 cheshire | |
1049 | Typo: FIxed missing semicolon | |
1050 | ||
1051 | Revision 1.242 2003/07/25 01:18:41 cheshire | |
1052 | Fix memory leak on shutdown in mDNS_Close() (detected in Windows version) | |
1053 | ||
1054 | Revision 1.241 2003/07/23 21:03:42 cheshire | |
1055 | Only show "Found record..." debugf message in verbose mode | |
1056 | ||
1057 | Revision 1.240 2003/07/23 21:01:11 cheshire | |
1058 | <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one | |
1059 | After sending a packet, suppress further sending for the next 100ms. | |
1060 | ||
1061 | Revision 1.239 2003/07/22 01:30:05 cheshire | |
1062 | <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once | |
1063 | ||
1064 | Revision 1.238 2003/07/22 00:10:20 cheshire | |
1065 | <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters | |
1066 | ||
1067 | Revision 1.237 2003/07/19 03:23:13 cheshire | |
1068 | <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records | |
1069 | ||
1070 | Revision 1.236 2003/07/19 03:04:55 cheshire | |
1071 | Fix warnings; some debugf message improvements | |
1072 | ||
1073 | Revision 1.235 2003/07/19 00:03:32 cheshire | |
1074 | <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received | |
1075 | ScheduleNextTask is quite an expensive operation. | |
1076 | We don't need to do all that work after receiving a no-op packet that didn't change our state. | |
1077 | ||
1078 | Revision 1.234 2003/07/18 23:52:11 cheshire | |
1079 | To improve consistency of field naming, global search-and-replace: | |
1080 | NextProbeTime -> NextScheduledProbe | |
1081 | NextResponseTime -> NextScheduledResponse | |
1082 | ||
1083 | Revision 1.233 2003/07/18 00:29:59 cheshire | |
1084 | <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead | |
1085 | ||
1086 | Revision 1.232 2003/07/18 00:11:38 cheshire | |
1087 | Add extra case to switch statements to handle HINFO data for Get, Put and Display | |
1088 | (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT) | |
1089 | ||
1090 | Revision 1.231 2003/07/18 00:06:37 cheshire | |
1091 | To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->" | |
1092 | ||
1093 | Revision 1.230 2003/07/17 18:16:54 cheshire | |
7f0064bd | 1094 | <rdar://problem/3319418> Services always in a state of flux |
c9b9ae52 A |
1095 | In preparation for working on this, made some debugf messages a little more selective |
1096 | ||
1097 | Revision 1.229 2003/07/17 17:35:04 cheshire | |
1098 | <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding | |
1099 | ||
1100 | Revision 1.228 2003/07/16 20:50:27 cheshire | |
1101 | <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass | |
1102 | ||
1103 | Revision 1.227 2003/07/16 05:01:36 cheshire | |
1104 | Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for | |
1105 | <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass | |
1106 | ||
1107 | Revision 1.226 2003/07/16 04:51:44 cheshire | |
1108 | Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval' | |
1109 | ||
1110 | Revision 1.225 2003/07/16 04:46:41 cheshire | |
1111 | Minor wording cleanup: The correct DNS term is "response", not "reply" | |
1112 | ||
1113 | Revision 1.224 2003/07/16 04:39:02 cheshire | |
1114 | Textual cleanup (no change to functionality): | |
1115 | Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)" | |
1116 | ||
1117 | Revision 1.223 2003/07/16 00:09:22 cheshire | |
1118 | Textual cleanup (no change to functionality): | |
1119 | Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places; | |
1120 | replace with macro "TicksTTL(rr)" | |
1121 | Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" | |
1122 | replaced with macro "RRExpireTime(rr)" | |
1123 | ||
1124 | Revision 1.222 2003/07/15 23:40:46 cheshire | |
1125 | Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo() | |
1126 | ||
1127 | Revision 1.221 2003/07/15 22:17:56 cheshire | |
1128 | <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries | |
1129 | ||
1130 | Revision 1.220 2003/07/15 02:12:51 cheshire | |
1131 | Slight tidy-up of debugf messages and comments | |
1132 | ||
1133 | Revision 1.219 2003/07/15 01:55:12 cheshire | |
1134 | <rdar://problem/3315777> Need to implement service registration with subtypes | |
1135 | ||
1136 | Revision 1.218 2003/07/14 16:26:06 cheshire | |
1137 | <rdar://problem/3324795> Duplicate query suppression not working right | |
1138 | Refinement: Don't record DS information for a question in the first quarter second | |
1139 | right after we send it -- in the case where a question happens to be accelerated by | |
1140 | the maximum allowed amount, we don't want it to then be suppressed because the previous | |
1141 | time *we* sent that question falls (just) within the valid duplicate suppression window. | |
1142 | ||
1143 | Revision 1.217 2003/07/13 04:43:53 cheshire | |
1144 | <rdar://problem/3325169> Services on multiple interfaces not always resolving | |
1145 | Minor refinement: No need to make address query broader than the original SRV query that provoked it | |
1146 | ||
1147 | Revision 1.216 2003/07/13 03:13:17 cheshire | |
1148 | <rdar://problem/3325169> Services on multiple interfaces not always resolving | |
1149 | If we get an identical SRV on a second interface, convert address queries to non-specific | |
1150 | ||
1151 | Revision 1.215 2003/07/13 02:28:00 cheshire | |
1152 | <rdar://problem/3325166> SendResponses didn't all its responses | |
1153 | Delete all references to RRInterfaceActive -- it's now superfluous | |
1154 | ||
1155 | Revision 1.214 2003/07/13 01:47:53 cheshire | |
1156 | Fix one error and one warning in the Windows build | |
1157 | ||
1158 | Revision 1.213 2003/07/12 04:25:48 cheshire | |
1159 | Fix minor signed/unsigned warnings | |
1160 | ||
1161 | Revision 1.212 2003/07/12 01:59:11 cheshire | |
1162 | Minor changes to debugf messages | |
1163 | ||
1164 | Revision 1.211 2003/07/12 01:47:01 cheshire | |
1165 | <rdar://problem/3324495> After name conflict, appended number should be higher than previous number | |
1166 | ||
1167 | Revision 1.210 2003/07/12 01:43:28 cheshire | |
1168 | <rdar://problem/3324795> Duplicate query suppression not working right | |
1169 | The correct cutoff time for duplicate query suppression is timenow less one-half the query interval. | |
1170 | The code was incorrectly using the last query time plus one-half the query interval. | |
1171 | This was only correct in the case where query acceleration was not in effect. | |
1172 | ||
1173 | Revision 1.209 2003/07/12 01:27:50 cheshire | |
1174 | <rdar://problem/3320079> Hostname conflict naming should not use two hyphens | |
1175 | Fix missing "-1" in RemoveLabelSuffix() | |
1176 | ||
1177 | Revision 1.208 2003/07/11 01:32:38 cheshire | |
1178 | Syntactic cleanup (no change to funcationality): Now that we only have one host name, | |
1179 | rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". | |
1180 | ||
1181 | Revision 1.207 2003/07/11 01:28:00 cheshire | |
1182 | <rdar://problem/3161289> No more local.arpa | |
1183 | ||
1184 | Revision 1.206 2003/07/11 00:45:02 cheshire | |
1185 | <rdar://problem/3321909> Client should get callback confirming successful host name registration | |
1186 | ||
1187 | Revision 1.205 2003/07/11 00:40:18 cheshire | |
1188 | Tidy up debug message in HostNameCallback() | |
1189 | ||
1190 | Revision 1.204 2003/07/11 00:20:32 cheshire | |
1191 | <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes | |
1192 | ||
1193 | Revision 1.203 2003/07/10 23:53:41 cheshire | |
1194 | <rdar://problem/3320079> Hostname conflict naming should not use two hyphens | |
1195 | ||
1196 | Revision 1.202 2003/07/04 02:23:20 cheshire | |
1197 | <rdar://problem/3311955> Responder too aggressive at flushing stale data | |
1198 | Changed mDNSResponder to require four unanswered queries before purging a record, instead of two. | |
1199 | ||
1200 | Revision 1.201 2003/07/04 01:09:41 cheshire | |
1201 | <rdar://problem/3315775> Need to implement subtype queries | |
1202 | Modified ConstructServiceName() to allow three-part service types | |
1203 | ||
1204 | Revision 1.200 2003/07/03 23:55:26 cheshire | |
1205 | Minor change to wording of syslog warning messages | |
1206 | ||
1207 | Revision 1.199 2003/07/03 23:51:13 cheshire | |
1208 | <rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings | |
1209 | Added more detailed debugging information | |
1210 | ||
1211 | Revision 1.198 2003/07/03 22:19:30 cheshire | |
1212 | <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo | |
1213 | Make exception to allow _tivo_servemedia._tcp. | |
1214 | ||
1215 | Revision 1.197 2003/07/02 22:33:05 cheshire | |
1216 | <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed | |
1217 | Minor refinements: | |
1218 | When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not | |
1219 | Allow cache to grow to 512 records before considering it a potential denial-of-service attack | |
1220 | ||
1221 | Revision 1.196 2003/07/02 21:19:45 cheshire | |
1222 | <rdar://problem/3313413> Update copyright notices, etc., in source code comments | |
1223 | ||
1224 | Revision 1.195 2003/07/02 19:56:58 cheshire | |
1225 | <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed | |
1226 | Minor refinement: m->rrcache_active was not being decremented when | |
1227 | an active record was deleted because its TTL expired | |
1228 | ||
1229 | Revision 1.194 2003/07/02 18:47:40 cheshire | |
1230 | Minor wording change to log messages | |
1231 | ||
1232 | Revision 1.193 2003/07/02 02:44:13 cheshire | |
1233 | Fix warning in non-debug build | |
1234 | ||
1235 | Revision 1.192 2003/07/02 02:41:23 cheshire | |
1236 | <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed | |
1237 | ||
1238 | Revision 1.191 2003/07/02 02:30:51 cheshire | |
1239 | HashSlot() returns an array index. It can't be negative; hence it should not be signed. | |
1240 | ||
1241 | Revision 1.190 2003/06/27 00:03:05 vlubet | |
1242 | <rdar://problem/3304625> Merge of build failure fix for gcc 3.3 | |
1243 | ||
1244 | Revision 1.189 2003/06/11 19:24:03 cheshire | |
1245 | <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces | |
1246 | Slight refinement to previous checkin | |
1247 | ||
1248 | Revision 1.188 2003/06/10 20:33:28 cheshire | |
1249 | <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces | |
1250 | ||
1251 | Revision 1.187 2003/06/10 04:30:44 cheshire | |
1252 | <rdar://problem/3286234> Need to re-probe/re-announce on configuration change | |
1253 | Only interface-specific records were re-probing and re-announcing, not non-specific records. | |
1254 | ||
1255 | Revision 1.186 2003/06/10 04:24:39 cheshire | |
1256 | <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache | |
1257 | Some additional refinements: | |
1258 | Don't try to do this for unicast-response queries | |
1259 | better tracking of Qs and KAs in multi-packet KA lists | |
1260 | ||
1261 | Revision 1.185 2003/06/10 03:52:49 cheshire | |
1262 | Update comments and debug messages | |
1263 | ||
1264 | Revision 1.184 2003/06/10 02:26:39 cheshire | |
1265 | <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function | |
1266 | Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines | |
1267 | ||
1268 | Revision 1.183 2003/06/09 18:53:13 cheshire | |
1269 | Simplify some debugf() statements (replaced block of 25 lines with 2 lines) | |
1270 | ||
1271 | Revision 1.182 2003/06/09 18:38:42 cheshire | |
1272 | <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network | |
1273 | Only issue a correction if the TTL in the proxy packet is less than half the correct value. | |
1274 | ||
1275 | Revision 1.181 2003/06/07 06:45:05 cheshire | |
1276 | <rdar://problem/3283666> No need for multiple machines to all be sending the same queries | |
1277 | ||
1278 | Revision 1.180 2003/06/07 06:31:07 cheshire | |
1279 | Create little four-line helper function "FindIdenticalRecordInCache()" | |
1280 | ||
1281 | Revision 1.179 2003/06/07 06:28:13 cheshire | |
1282 | For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq" | |
1283 | ||
1284 | Revision 1.178 2003/06/07 06:25:12 cheshire | |
1285 | Update some comments | |
1286 | ||
1287 | Revision 1.177 2003/06/07 04:50:53 cheshire | |
1288 | <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache | |
1289 | ||
1290 | Revision 1.176 2003/06/07 04:33:26 cheshire | |
1291 | <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records | |
1292 | Minor change: Increment/decrement logic for q->CurrentAnswers should be in | |
1293 | CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord() | |
1294 | ||
1295 | Revision 1.175 2003/06/07 04:11:52 cheshire | |
1296 | Minor changes to comments and debug messages | |
1297 | ||
1298 | Revision 1.174 2003/06/07 01:46:38 cheshire | |
1299 | <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records | |
1300 | ||
1301 | Revision 1.173 2003/06/07 01:22:13 cheshire | |
1302 | <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function | |
1303 | ||
1304 | Revision 1.172 2003/06/07 00:59:42 cheshire | |
1305 | <rdar://problem/3283454> Need some randomness to spread queries on the network | |
1306 | ||
1307 | Revision 1.171 2003/06/06 21:41:10 cheshire | |
1308 | For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines | |
1309 | ||
1310 | Revision 1.170 2003/06/06 21:38:55 cheshire | |
1311 | Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we | |
1312 | already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) | |
1313 | ||
1314 | Revision 1.169 2003/06/06 21:35:55 cheshire | |
1315 | Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget | |
1316 | (the target is a domain name, but not necessarily a host name) | |
1317 | ||
1318 | Revision 1.168 2003/06/06 21:33:31 cheshire | |
1319 | Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval" | |
1320 | ||
1321 | Revision 1.167 2003/06/06 21:30:42 cheshire | |
1322 | <rdar://problem/3282962> Don't delay queries for shared record types | |
1323 | ||
1324 | Revision 1.166 2003/06/06 17:20:14 cheshire | |
1325 | For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass | |
1326 | (Global search-and-replace; no functional change to code execution.) | |
1327 | ||
1328 | Revision 1.165 2003/06/04 02:53:21 cheshire | |
1329 | Add some "#pragma warning" lines so it compiles clean on Microsoft compilers | |
1330 | ||
1331 | Revision 1.164 2003/06/04 01:25:33 cheshire | |
1332 | <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages | |
1333 | Display time interval between first and subsequent queries | |
1334 | ||
1335 | Revision 1.163 2003/06/03 19:58:14 cheshire | |
1336 | <rdar://problem/3277665> mDNS_DeregisterService() fixes: | |
1337 | When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet. | |
1338 | Guard against a couple of possible mDNS_DeregisterService() race conditions. | |
1339 | ||
1340 | Revision 1.162 2003/06/03 19:30:39 cheshire | |
1341 | Minor addition refinements for | |
1342 | <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be | |
1343 | ||
1344 | Revision 1.161 2003/06/03 18:29:03 cheshire | |
1345 | Minor changes to comments and debugf() messages | |
1346 | ||
1347 | Revision 1.160 2003/06/03 05:02:16 cheshire | |
1348 | <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be | |
1349 | ||
1350 | Revision 1.159 2003/06/03 03:31:57 cheshire | |
1351 | <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine | |
1352 | ||
1353 | Revision 1.158 2003/06/02 22:57:09 cheshire | |
1354 | Minor clarifying changes to comments and log messages; | |
1355 | IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord() | |
1356 | ||
1357 | Revision 1.157 2003/05/31 00:09:49 cheshire | |
1358 | <rdar://problem/3274862> Add ability to discover what services are on a network | |
1359 | ||
1360 | Revision 1.156 2003/05/30 23:56:49 cheshire | |
1361 | <rdar://problem/3274847> Crash after error in mDNS_RegisterService() | |
1362 | Need to set "sr->Extras = mDNSNULL" before returning | |
1363 | ||
1364 | Revision 1.155 2003/05/30 23:48:00 cheshire | |
1365 | <rdar://problem/3274832> Announcements not properly grouped | |
1366 | Due to inconsistent setting of rr->LastAPTime at different places in the | |
1367 | code, announcements were not properly grouped into a single packet. | |
1368 | Fixed by creating a single routine called InitializeLastAPTime(). | |
1369 | ||
1370 | Revision 1.154 2003/05/30 23:38:14 cheshire | |
1371 | <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records | |
1372 | Wrote buffer[32] where it should have said buffer[64] | |
1373 | ||
1374 | Revision 1.153 2003/05/30 19:10:56 cheshire | |
1375 | <rdar://problem/3274153> ConstructServiceName needs to be more restrictive | |
1376 | ||
1377 | Revision 1.152 2003/05/29 22:39:16 cheshire | |
1378 | <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character | |
1379 | ||
1380 | Revision 1.151 2003/05/29 06:35:42 cheshire | |
1381 | <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record | |
1382 | ||
1383 | Revision 1.150 2003/05/29 06:25:45 cheshire | |
1384 | <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion() | |
1385 | ||
1386 | Revision 1.149 2003/05/29 06:18:39 cheshire | |
1387 | <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv | |
1388 | ||
1389 | Revision 1.148 2003/05/29 06:11:34 cheshire | |
1390 | <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks | |
1391 | ||
1392 | Revision 1.147 2003/05/29 06:01:18 cheshire | |
1393 | Change some debugf() calls to LogMsg() calls to help with debugging | |
1394 | ||
1395 | Revision 1.146 2003/05/28 21:00:44 cheshire | |
1396 | Re-enable "immediate answer burst" debugf message | |
1397 | ||
1398 | Revision 1.145 2003/05/28 20:57:44 cheshire | |
1399 | <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet | |
1400 | known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2 | |
1401 | version of mDNSResponder, so for now we should suppress this warning message. | |
1402 | ||
1403 | Revision 1.144 2003/05/28 18:05:12 cheshire | |
1404 | <rdar://problem/3009899> mDNSResponder allows invalid service registrations | |
1405 | Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names | |
1406 | ||
1407 | Revision 1.143 2003/05/28 04:31:29 cheshire | |
1408 | <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time | |
1409 | ||
1410 | Revision 1.142 2003/05/28 03:13:07 cheshire | |
1411 | <rdar://problem/3009899> mDNSResponder allows invalid service registrations | |
1412 | Require that the transport protocol be _udp or _tcp | |
1413 | ||
1414 | Revision 1.141 2003/05/28 02:19:12 cheshire | |
1415 | <rdar://problem/3270634> Misleading messages generated by iChat | |
1416 | Better fix: Only generate the log message for queries where the TC bit is set. | |
1417 | ||
1418 | Revision 1.140 2003/05/28 01:55:24 cheshire | |
1419 | Minor change to log messages | |
1420 | ||
1421 | Revision 1.139 2003/05/28 01:52:51 cheshire | |
1422 | <rdar://problem/3270634> Misleading messages generated by iChat | |
1423 | ||
1424 | Revision 1.138 2003/05/27 22:35:00 cheshire | |
1425 | <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions | |
1426 | ||
1427 | Revision 1.137 2003/05/27 20:04:33 cheshire | |
1428 | <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf() | |
1429 | ||
1430 | Revision 1.136 2003/05/27 18:50:07 cheshire | |
1431 | <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes | |
1432 | ||
1433 | Revision 1.135 2003/05/26 04:57:28 cheshire | |
1434 | <rdar://problem/3268953> Delay queries when there are already answers in the cache | |
1435 | ||
1436 | Revision 1.134 2003/05/26 04:54:54 cheshire | |
1437 | <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead | |
1438 | Accidentally deleted '%' case from the switch statement | |
1439 | ||
1440 | Revision 1.133 2003/05/26 03:21:27 cheshire | |
1441 | Tidy up address structure naming: | |
1442 | mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) | |
1443 | mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 | |
1444 | mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 | |
1445 | ||
1446 | Revision 1.132 2003/05/26 03:01:26 cheshire | |
1447 | <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead | |
1448 | ||
1449 | Revision 1.131 2003/05/26 00:42:05 cheshire | |
1450 | <rdar://problem/3268876> Temporarily include mDNSResponder version in packets | |
1451 | ||
1452 | Revision 1.130 2003/05/24 16:39:48 cheshire | |
1453 | <rdar://problem/3268631> SendResponses also needs to handle multihoming better | |
1454 | ||
1455 | Revision 1.129 2003/05/23 02:15:37 cheshire | |
1456 | Fixed misleading use of the term "duplicate suppression" where it should have | |
1457 | said "known answer suppression". (Duplicate answer suppression is something | |
1458 | different, and duplicate question suppression is yet another thing, so the use | |
1459 | of the completely vague term "duplicate suppression" was particularly bad.) | |
1460 | ||
1461 | Revision 1.128 2003/05/23 01:55:13 cheshire | |
1462 | <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness | |
1463 | ||
1464 | Revision 1.127 2003/05/23 01:02:15 ksekar | |
8e92c31c | 1465 | <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name |
c9b9ae52 A |
1466 | |
1467 | Revision 1.126 2003/05/22 02:29:22 cheshire | |
1468 | <rdar://problem/2984918> SendQueries needs to handle multihoming better | |
1469 | Complete rewrite of SendQueries. Works much better now :-) | |
1470 | ||
1471 | Revision 1.125 2003/05/22 01:50:45 cheshire | |
1472 | Fix warnings, and improve log messages | |
1473 | ||
1474 | Revision 1.124 2003/05/22 01:41:50 cheshire | |
1475 | DiscardDeregistrations doesn't need InterfaceID parameter | |
1476 | ||
1477 | Revision 1.123 2003/05/22 01:38:55 cheshire | |
1478 | Change bracketing of #pragma mark | |
1479 | ||
1480 | Revision 1.122 2003/05/21 19:59:04 cheshire | |
1481 | <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior | |
1482 | Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character | |
1483 | ||
1484 | Revision 1.121 2003/05/21 17:54:07 ksekar | |
8e92c31c | 1485 | <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior |
c9b9ae52 A |
1486 | New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)" |
1487 | ||
1488 | Revision 1.120 2003/05/19 22:14:14 ksekar | |
1489 | <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type | |
1490 | ||
1491 | Revision 1.119 2003/05/16 01:34:10 cheshire | |
1492 | Fix some warnings | |
1493 | ||
1494 | Revision 1.118 2003/05/14 18:48:40 cheshire | |
1495 | <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations | |
1496 | More minor refinements: | |
7f0064bd | 1497 | mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory |
c9b9ae52 A |
1498 | mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away |
1499 | ||
1500 | Revision 1.117 2003/05/14 07:08:36 cheshire | |
1501 | <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations | |
1502 | Previously, when there was any network configuration change, mDNSResponder | |
1503 | would tear down the entire list of active interfaces and start again. | |
1504 | That was very disruptive, and caused the entire cache to be flushed, | |
1505 | and caused lots of extra network traffic. Now it only removes interfaces | |
1506 | that have really gone, and only adds new ones that weren't there before. | |
1507 | ||
1508 | Revision 1.116 2003/05/14 06:51:56 cheshire | |
7f0064bd | 1509 | <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep |
c9b9ae52 A |
1510 | |
1511 | Revision 1.115 2003/05/14 06:44:31 cheshire | |
1512 | Improve debugging message | |
1513 | ||
1514 | Revision 1.114 2003/05/07 01:47:03 cheshire | |
1515 | <rdar://problem/3250330> Also protect against NULL domainlabels | |
1516 | ||
1517 | Revision 1.113 2003/05/07 00:28:18 cheshire | |
1518 | <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients | |
1519 | ||
1520 | Revision 1.112 2003/05/06 00:00:46 cheshire | |
1521 | <rdar://problem/3248914> Rationalize naming of domainname manipulation functions | |
1522 | ||
1523 | Revision 1.111 2003/05/05 23:42:08 cheshire | |
1524 | <rdar://problem/3245631> Resolves never succeed | |
1525 | Was setting "rr->LastAPTime = timenow - rr->LastAPTime" | |
1526 | instead of "rr->LastAPTime = timenow - rr->ThisAPInterval" | |
1527 | ||
1528 | Revision 1.110 2003/04/30 21:09:59 cheshire | |
1529 | <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names | |
1530 | ||
1531 | Revision 1.109 2003/04/26 02:41:56 cheshire | |
1532 | <rdar://problem/3241281> Change timenow from a local variable to a structure member | |
1533 | ||
1534 | Revision 1.108 2003/04/25 01:45:56 cheshire | |
1535 | <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name | |
1536 | ||
1537 | Revision 1.107 2003/04/25 00:41:31 cheshire | |
1538 | <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future | |
1539 | ||
1540 | Revision 1.106 2003/04/22 03:14:45 cheshire | |
1541 | <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now | |
1542 | ||
1543 | Revision 1.105 2003/04/22 01:07:43 cheshire | |
1544 | <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl | |
1545 | If TTL parameter is zero, leave record TTL unchanged | |
1546 | ||
1547 | Revision 1.104 2003/04/21 19:15:52 cheshire | |
1548 | Fix some compiler warnings | |
1549 | ||
1550 | Revision 1.103 2003/04/19 02:26:35 cheshire | |
8e92c31c | 1551 | <rdar://problem/3233804> Incorrect goodbye packet after conflict |
c9b9ae52 A |
1552 | |
1553 | Revision 1.102 2003/04/17 03:06:28 cheshire | |
8e92c31c | 1554 | <rdar://problem/3231321> No need to query again when a service goes away |
c9b9ae52 A |
1555 | Set UnansweredQueries to 2 when receiving a "goodbye" packet |
1556 | ||
1557 | Revision 1.101 2003/04/15 20:58:31 jgraessl | |
8e92c31c | 1558 | <rdar://problem/3229014> Added a hash to lookup records in the cache. |
c9b9ae52 A |
1559 | |
1560 | Revision 1.100 2003/04/15 18:53:14 cheshire | |
8e92c31c | 1561 | <rdar://problem/3229064> Bug in ScheduleNextTask |
c9b9ae52 A |
1562 | mDNS.c 1.94 incorrectly combined two "if" statements into one. |
1563 | ||
1564 | Revision 1.99 2003/04/15 18:09:13 jgraessl | |
8e92c31c | 1565 | <rdar://problem/3228892> |
c9b9ae52 A |
1566 | Reviewed by: Stuart Cheshire |
1567 | Added code to keep track of when the next cache item will expire so we can | |
1568 | call TidyRRCache only when necessary. | |
1569 | ||
1570 | Revision 1.98 2003/04/03 03:43:55 cheshire | |
1571 | <rdar://problem/3216837> Off-by-one error in probe rate limiting | |
1572 | ||
1573 | Revision 1.97 2003/04/02 01:48:17 cheshire | |
1574 | <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets | |
1575 | Additional fix pointed out by Josh: | |
1576 | Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state | |
1577 | ||
1578 | Revision 1.96 2003/04/01 23:58:55 cheshire | |
1579 | Minor comment changes | |
1580 | ||
1581 | Revision 1.95 2003/04/01 23:46:05 cheshire | |
1582 | <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles | |
1583 | mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface | |
1584 | to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second | |
1585 | window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in | |
1586 | FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed. | |
1587 | ||
1588 | Revision 1.94 2003/03/29 01:55:19 cheshire | |
1589 | <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets | |
1590 | Solution: Major cleanup of packet timing and conflict handling rules | |
1591 | ||
1592 | Revision 1.93 2003/03/28 01:54:36 cheshire | |
1593 | Minor tidyup of IPv6 (AAAA) code | |
1594 | ||
1595 | Revision 1.92 2003/03/27 03:30:55 cheshire | |
1596 | <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash | |
1597 | Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback | |
1598 | Fixes: | |
1599 | 1. Make mDNS_DeregisterInterface() safe to call from a callback | |
8e92c31c | 1600 | 2. Make HostNameCallback() use DeadvertiseInterface() instead |
c9b9ae52 A |
1601 | (it never really needed to deregister the interface at all) |
1602 | ||
1603 | Revision 1.91 2003/03/15 04:40:36 cheshire | |
1604 | Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" | |
1605 | ||
1606 | Revision 1.90 2003/03/14 20:26:37 cheshire | |
1607 | Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") | |
1608 | ||
1609 | Revision 1.89 2003/03/12 19:57:50 cheshire | |
1610 | Fixed typo in debug message | |
1611 | ||
1612 | Revision 1.88 2003/03/12 00:17:44 cheshire | |
1613 | <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records | |
1614 | ||
1615 | Revision 1.87 2003/03/11 01:27:20 cheshire | |
1616 | Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") | |
1617 | ||
1618 | Revision 1.86 2003/03/06 20:44:33 cheshire | |
1619 | Comment tidyup | |
1620 | ||
1621 | Revision 1.85 2003/03/05 03:38:35 cheshire | |
8e92c31c | 1622 | <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found! |
c9b9ae52 A |
1623 | Fixed by leaving client in list after conflict, until client explicitly deallocates |
1624 | ||
1625 | Revision 1.84 2003/03/05 01:27:30 cheshire | |
8e92c31c | 1626 | <rdar://problem/3185482> Different TTL for multicast versus unicast responses |
c9b9ae52 A |
1627 | When building unicast responses, record TTLs are capped to 10 seconds |
1628 | ||
1629 | Revision 1.83 2003/03/04 23:48:52 cheshire | |
8e92c31c | 1630 | <rdar://problem/3188865> Double probes after wake from sleep |
c9b9ae52 A |
1631 | Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another |
1632 | ||
1633 | Revision 1.82 2003/03/04 23:38:29 cheshire | |
8e92c31c | 1634 | <rdar://problem/3099194> mDNSResponder needs performance improvements |
c9b9ae52 A |
1635 | Only set rr->CRActiveQuestion to point to the |
1636 | currently active representative of a question set | |
1637 | ||
1638 | Revision 1.81 2003/02/21 03:35:34 cheshire | |
8e92c31c | 1639 | <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section |
c9b9ae52 A |
1640 | |
1641 | Revision 1.80 2003/02/21 02:47:53 cheshire | |
8e92c31c | 1642 | <rdar://problem/3099194> mDNSResponder needs performance improvements |
c9b9ae52 A |
1643 | Several places in the code were calling CacheRRActive(), which searched the entire |
1644 | question list every time, to see if this cache resource record answers any question. | |
1645 | Instead, we now have a field "CRActiveQuestion" in the resource record structure | |
1646 | ||
1647 | Revision 1.79 2003/02/21 01:54:07 cheshire | |
8e92c31c | 1648 | <rdar://problem/3099194> mDNSResponder needs performance improvements |
c9b9ae52 A |
1649 | Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") |
1650 | ||
1651 | Revision 1.78 2003/02/20 06:48:32 cheshire | |
8e92c31c | 1652 | <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations |
c9b9ae52 A |
1653 | Reviewed by: Josh Graessley, Bob Bradley |
1654 | ||
1655 | Revision 1.77 2003/01/31 03:35:59 cheshire | |
8e92c31c | 1656 | <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results |
c9b9ae52 A |
1657 | When there were *two* active questions in the list, they were incorrectly |
1658 | finding *each other* and *both* being marked as duplicates of another question | |
1659 | ||
1660 | Revision 1.76 2003/01/29 02:46:37 cheshire | |
1661 | Fix for IPv6: | |
1662 | A physical interface is identified solely by its InterfaceID (not by IP and type). | |
1663 | On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. | |
1664 | In cases where the requested outbound protocol (v4 or v6) is not supported on | |
1665 | that InterfaceID, the platform support layer should simply discard that packet. | |
1666 | ||
1667 | Revision 1.75 2003/01/29 01:47:40 cheshire | |
1668 | Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity | |
1669 | ||
1670 | Revision 1.74 2003/01/28 05:26:25 cheshire | |
8e92c31c | 1671 | <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results |
c9b9ae52 A |
1672 | Add 'Active' flag for interfaces |
1673 | ||
1674 | Revision 1.73 2003/01/28 03:45:12 cheshire | |
1675 | Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)" | |
1676 | ||
1677 | Revision 1.72 2003/01/28 01:49:48 cheshire | |
8e92c31c | 1678 | <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results |
c9b9ae52 A |
1679 | FindDuplicateQuestion() was incorrectly finding the question itself in the list, |
1680 | and incorrectly marking it as a duplicate (of itself), so that it became inactive. | |
1681 | ||
1682 | Revision 1.71 2003/01/28 01:41:44 cheshire | |
8e92c31c | 1683 | <rdar://problem/3153091> Race condition when network change causes bad stuff |
c9b9ae52 A |
1684 | When an interface goes away, interface-specific questions on that interface become orphaned. |
1685 | Orphan questions cause HaveQueries to return true, but there's no interface to send them on. | |
1686 | Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions() | |
1687 | ||
1688 | Revision 1.70 2003/01/23 19:00:20 cheshire | |
1689 | Protect against infinite loops in mDNS_Execute | |
1690 | ||
1691 | Revision 1.69 2003/01/21 22:56:32 jgraessl | |
8e92c31c | 1692 | <rdar://problem/3124348> service name changes are not properly handled |
c9b9ae52 A |
1693 | Submitted by: Stuart Cheshire |
1694 | Reviewed by: Joshua Graessley | |
1695 | Applying changes for 3124348 to main branch. 3124348 changes went in to a | |
1696 | branch for SU. | |
1697 | ||
1698 | Revision 1.68 2003/01/17 04:09:27 cheshire | |
8e92c31c | 1699 | <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts |
c9b9ae52 A |
1700 | |
1701 | Revision 1.67 2003/01/17 03:56:45 cheshire | |
1702 | Default 24-hour TTL is far too long. Changing to two hours. | |
1703 | ||
1704 | Revision 1.66 2003/01/13 23:49:41 jgraessl | |
1705 | Merged changes for the following fixes in to top of tree: | |
8e92c31c A |
1706 | <rdar://problem/3086540> computer name changes not handled properly |
1707 | <rdar://problem/3124348> service name changes are not properly handled | |
1708 | <rdar://problem/3124352> announcements sent in pairs, failing chattiness test | |
c9b9ae52 A |
1709 | |
1710 | Revision 1.65 2002/12/23 22:13:28 jgraessl | |
1711 | Reviewed by: Stuart Cheshire | |
1712 | Initial IPv6 support for mDNSResponder. | |
1713 | ||
1714 | Revision 1.64 2002/11/26 20:49:06 cheshire | |
8e92c31c | 1715 | <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit |
c9b9ae52 A |
1716 | |
1717 | Revision 1.63 2002/09/21 20:44:49 zarzycki | |
1718 | Added APSL info | |
1719 | ||
1720 | Revision 1.62 2002/09/20 03:25:37 cheshire | |
1721 | Fix some compiler warnings | |
1722 | ||
1723 | Revision 1.61 2002/09/20 01:05:24 cheshire | |
1724 | Don't kill the Extras list in mDNS_DeregisterService() | |
1725 | ||
1726 | Revision 1.60 2002/09/19 23:47:35 cheshire | |
7f0064bd | 1727 | Added mDNS_RegisterNoSuchService() function for assertion of non-existence |
c9b9ae52 A |
1728 | of a particular named service |
1729 | ||
1730 | Revision 1.59 2002/09/19 21:25:34 cheshire | |
1731 | mDNS_snprintf() doesn't need to be in a separate file | |
1732 | ||
1733 | Revision 1.58 2002/09/19 04:20:43 cheshire | |
1734 | Remove high-ascii characters that confuse some systems | |
1735 | ||
1736 | Revision 1.57 2002/09/17 01:07:08 cheshire | |
1737 | Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() | |
1738 | ||
1739 | Revision 1.56 2002/09/16 19:44:17 cheshire | |
1740 | Merge in license terms from Quinn's copy, in preparation for Darwin release | |
1741 | */ | |
1742 | ||
8e92c31c A |
1743 | #include "DNSCommon.h" // Defines general DNS untility routines |
1744 | #include "uDNS.h" // Defines entry points into unicast-specific routines | |
c9b9ae52 | 1745 | // Disable certain benign warnings with Microsoft compilers |
6528fe3e | 1746 | #if(defined(_MSC_VER)) |
c9b9ae52 A |
1747 | // Disable "conditional expression is constant" warning for debug macros. |
1748 | // Otherwise, this generates warnings for the perfectly natural construct "while(1)" | |
1749 | // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know | |
1750 | #pragma warning(disable:4127) | |
1751 | ||
c9b9ae52 A |
1752 | // Disable "assignment within conditional expression". |
1753 | // Other compilers understand the convention that if you place the assignment expression within an extra pair | |
1754 | // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. | |
1755 | // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal | |
1756 | // to the compiler that the assignment is intentional, we have to just turn this warning off completely. | |
1757 | #pragma warning(disable:4706) | |
6528fe3e A |
1758 | #endif |
1759 | ||
6528fe3e | 1760 | // *************************************************************************** |
c9b9ae52 | 1761 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
1762 | #pragma mark - |
1763 | #pragma mark - Program Constants | |
1764 | #endif | |
1765 | ||
c9b9ae52 | 1766 | mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; |
7f0064bd | 1767 | mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; |
c9b9ae52 | 1768 | mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; |
7f0064bd | 1769 | mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; |
c9b9ae52 A |
1770 | mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; |
1771 | mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; | |
8e92c31c A |
1772 | mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; |
1773 | ||
1774 | mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; | |
283ee3ff | 1775 | mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1; |
c9b9ae52 | 1776 | |
8e92c31c | 1777 | mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; |
6528fe3e | 1778 | |
cc340f17 A |
1779 | #define UnicastDNSPortAsNumber 53 |
1780 | #define NATPMPPortAsNumber 5351 | |
1781 | #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc. | |
6528fe3e | 1782 | #define MulticastDNSPortAsNumber 5353 |
cc340f17 A |
1783 | #define LoopbackIPCPortAsNumber 5354 |
1784 | ||
6528fe3e | 1785 | mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; |
cc340f17 A |
1786 | mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } }; |
1787 | mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } }; | |
6528fe3e | 1788 | mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; |
cc340f17 A |
1789 | mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } }; |
1790 | ||
c9b9ae52 | 1791 | mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; |
c9d2d929 A |
1792 | mDNSexport const mDNSv4Addr AllDNSLinkGroupv4 = { { 224, 0, 0, 251 } }; |
1793 | mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } }; | |
c9b9ae52 A |
1794 | mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; |
1795 | mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; | |
6528fe3e | 1796 | |
cc340f17 A |
1797 | mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; |
1798 | mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; | |
1799 | mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; | |
1800 | mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; | |
1801 | mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; | |
1802 | mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; | |
6528fe3e | 1803 | |
c9b9ae52 A |
1804 | // Any records bigger than this are considered 'large' records |
1805 | #define SmallRecordLimit 1024 | |
1806 | ||
c9b9ae52 | 1807 | #define kMaxUpdateCredits 10 |
7f0064bd | 1808 | #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) |
c9b9ae52 | 1809 | |
283ee3ff | 1810 | mDNSexport const char *const mDNS_DomainTypeNames[] = |
6528fe3e | 1811 | { |
283ee3ff A |
1812 | "b._dns-sd._udp.", // Browse |
1813 | "db._dns-sd._udp.", // Default Browse | |
1814 | "lb._dns-sd._udp.", // Legacy Browse | |
1815 | "r._dns-sd._udp.", // Registration | |
1816 | "dr._dns-sd._udp." // Default Registration | |
6528fe3e A |
1817 | }; |
1818 | ||
7f0064bd A |
1819 | #ifdef UNICAST_DISABLED |
1820 | #define uDNS_IsActiveQuery(q, u) mDNSfalse | |
1821 | #endif | |
1822 | ||
6528fe3e | 1823 | // *************************************************************************** |
c9b9ae52 A |
1824 | #if COMPILER_LIKES_PRAGMA_MARK |
1825 | #pragma mark - | |
1826 | #pragma mark - Specialized mDNS version of vsnprintf | |
1827 | #endif | |
1828 | ||
1829 | static const struct mDNSprintf_format | |
1830 | { | |
1831 | unsigned leftJustify : 1; | |
1832 | unsigned forceSign : 1; | |
1833 | unsigned zeroPad : 1; | |
1834 | unsigned havePrecision : 1; | |
1835 | unsigned hSize : 1; | |
1836 | unsigned lSize : 1; | |
1837 | char altForm; | |
1838 | char sign; // +, - or space | |
1839 | unsigned int fieldWidth; | |
1840 | unsigned int precision; | |
8e92c31c | 1841 | } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
c9b9ae52 A |
1842 | |
1843 | mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) | |
1844 | { | |
1845 | mDNSu32 nwritten = 0; | |
1846 | int c; | |
8e92c31c | 1847 | if (buflen == 0) return(0); |
28f7d060 | 1848 | buflen--; // Pre-reserve one space in the buffer for the terminating null |
8e92c31c | 1849 | if (buflen == 0) goto exit; |
c9b9ae52 A |
1850 | |
1851 | for (c = *fmt; c != 0; c = *++fmt) | |
1852 | { | |
1853 | if (c != '%') | |
1854 | { | |
1855 | *sbuffer++ = (char)c; | |
1856 | if (++nwritten >= buflen) goto exit; | |
1857 | } | |
1858 | else | |
1859 | { | |
1860 | unsigned int i=0, j; | |
1861 | // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for | |
1862 | // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. | |
1863 | // The size needs to be enough for a 256-byte domain name plus some error text. | |
1864 | #define mDNS_VACB_Size 300 | |
1865 | char mDNS_VACB[mDNS_VACB_Size]; | |
1866 | #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) | |
1867 | #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) | |
1868 | char *s = mDNS_VACB_Lim, *digits; | |
1869 | struct mDNSprintf_format F = mDNSprintf_format_default; | |
1870 | ||
1871 | while (1) // decode flags | |
1872 | { | |
1873 | c = *++fmt; | |
1874 | if (c == '-') F.leftJustify = 1; | |
1875 | else if (c == '+') F.forceSign = 1; | |
1876 | else if (c == ' ') F.sign = ' '; | |
1877 | else if (c == '#') F.altForm++; | |
1878 | else if (c == '0') F.zeroPad = 1; | |
1879 | else break; | |
1880 | } | |
1881 | ||
1882 | if (c == '*') // decode field width | |
1883 | { | |
1884 | int f = va_arg(arg, int); | |
1885 | if (f < 0) { f = -f; F.leftJustify = 1; } | |
1886 | F.fieldWidth = (unsigned int)f; | |
1887 | c = *++fmt; | |
1888 | } | |
1889 | else | |
1890 | { | |
1891 | for (; c >= '0' && c <= '9'; c = *++fmt) | |
1892 | F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); | |
1893 | } | |
1894 | ||
1895 | if (c == '.') // decode precision | |
1896 | { | |
1897 | if ((c = *++fmt) == '*') | |
1898 | { F.precision = va_arg(arg, unsigned int); c = *++fmt; } | |
1899 | else for (; c >= '0' && c <= '9'; c = *++fmt) | |
1900 | F.precision = (10 * F.precision) + (c - '0'); | |
1901 | F.havePrecision = 1; | |
1902 | } | |
1903 | ||
1904 | if (F.leftJustify) F.zeroPad = 0; | |
1905 | ||
1906 | conv: | |
1907 | switch (c) // perform appropriate conversion | |
1908 | { | |
1909 | unsigned long n; | |
1910 | case 'h' : F.hSize = 1; c = *++fmt; goto conv; | |
1911 | case 'l' : // fall through | |
1912 | case 'L' : F.lSize = 1; c = *++fmt; goto conv; | |
1913 | case 'd' : | |
1914 | case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); | |
1915 | else n = (unsigned long)va_arg(arg, int); | |
1916 | if (F.hSize) n = (short) n; | |
1917 | if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } | |
1918 | else if (F.forceSign) F.sign = '+'; | |
1919 | goto decimal; | |
1920 | case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); | |
1921 | else n = va_arg(arg, unsigned int); | |
1922 | if (F.hSize) n = (unsigned short) n; | |
1923 | F.sign = 0; | |
1924 | goto decimal; | |
1925 | decimal: if (!F.havePrecision) | |
1926 | { | |
1927 | if (F.zeroPad) | |
1928 | { | |
1929 | F.precision = F.fieldWidth; | |
1930 | if (F.sign) --F.precision; | |
1931 | } | |
1932 | if (F.precision < 1) F.precision = 1; | |
1933 | } | |
1934 | if (F.precision > mDNS_VACB_Size - 1) | |
1935 | F.precision = mDNS_VACB_Size - 1; | |
1936 | for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); | |
1937 | for (; i < F.precision; i++) *--s = '0'; | |
1938 | if (F.sign) { *--s = F.sign; i++; } | |
1939 | break; | |
1940 | ||
1941 | case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); | |
1942 | else n = va_arg(arg, unsigned int); | |
1943 | if (F.hSize) n = (unsigned short) n; | |
1944 | if (!F.havePrecision) | |
1945 | { | |
1946 | if (F.zeroPad) F.precision = F.fieldWidth; | |
1947 | if (F.precision < 1) F.precision = 1; | |
1948 | } | |
1949 | if (F.precision > mDNS_VACB_Size - 1) | |
1950 | F.precision = mDNS_VACB_Size - 1; | |
1951 | for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); | |
1952 | if (F.altForm && i && *s != '0') { *--s = '0'; i++; } | |
1953 | for (; i < F.precision; i++) *--s = '0'; | |
1954 | break; | |
1955 | ||
1956 | case 'a' : { | |
1957 | unsigned char *a = va_arg(arg, unsigned char *); | |
1958 | if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } | |
1959 | else | |
1960 | { | |
c9b9ae52 A |
1961 | s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end |
1962 | if (F.altForm) | |
1963 | { | |
1964 | mDNSAddr *ip = (mDNSAddr*)a; | |
c9b9ae52 A |
1965 | switch (ip->type) |
1966 | { | |
8e92c31c A |
1967 | case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; |
1968 | case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; | |
c9b9ae52 A |
1969 | default: F.precision = 0; break; |
1970 | } | |
1971 | } | |
1972 | switch (F.precision) | |
1973 | { | |
1974 | case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", | |
1975 | a[0], a[1], a[2], a[3]); break; | |
1976 | case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", | |
1977 | a[0], a[1], a[2], a[3], a[4], a[5]); break; | |
8e92c31c A |
1978 | case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), |
1979 | "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", | |
1980 | a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], | |
1981 | a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; | |
c9d2d929 A |
1982 | default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size " |
1983 | "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; | |
c9b9ae52 A |
1984 | } |
1985 | } | |
1986 | } | |
1987 | break; | |
1988 | ||
1989 | case 'p' : F.havePrecision = F.lSize = 1; | |
1990 | F.precision = 8; | |
1991 | case 'X' : digits = "0123456789ABCDEF"; | |
1992 | goto hexadecimal; | |
1993 | case 'x' : digits = "0123456789abcdef"; | |
1994 | hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); | |
1995 | else n = va_arg(arg, unsigned int); | |
1996 | if (F.hSize) n = (unsigned short) n; | |
1997 | if (!F.havePrecision) | |
1998 | { | |
1999 | if (F.zeroPad) | |
2000 | { | |
2001 | F.precision = F.fieldWidth; | |
2002 | if (F.altForm) F.precision -= 2; | |
2003 | } | |
2004 | if (F.precision < 1) F.precision = 1; | |
2005 | } | |
2006 | if (F.precision > mDNS_VACB_Size - 1) | |
2007 | F.precision = mDNS_VACB_Size - 1; | |
2008 | for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; | |
2009 | for (; i < F.precision; i++) *--s = '0'; | |
2010 | if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } | |
2011 | break; | |
2012 | ||
2013 | case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; | |
2014 | ||
2015 | case 's' : s = va_arg(arg, char *); | |
2016 | if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } | |
2017 | else switch (F.altForm) | |
2018 | { | |
8e92c31c A |
2019 | case 0: i=0; |
2020 | if (!F.havePrecision) // C string | |
2021 | while(s[i]) i++; | |
2022 | else | |
2023 | { | |
2024 | while ((i < F.precision) && s[i]) i++; | |
2025 | // Make sure we don't truncate in the middle of a UTF-8 character | |
2026 | // If last character we got was any kind of UTF-8 multi-byte character, | |
2027 | // then see if we have to back up. | |
2028 | // This is not as easy as the similar checks below, because | |
2029 | // here we can't assume it's safe to examine the *next* byte, so we | |
2030 | // have to confine ourselves to working only backwards in the string. | |
2031 | j = i; // Record where we got to | |
2032 | // Now, back up until we find first non-continuation-char | |
2033 | while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; | |
2034 | // Now s[i-1] is the first non-continuation-char | |
2035 | // and (j-i) is the number of continuation-chars we found | |
2036 | if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char | |
2037 | { | |
2038 | i--; // Tentatively eliminate this start-char as well | |
2039 | // Now (j-i) is the number of characters we're considering eliminating. | |
2040 | // To be legal UTF-8, the start-char must contain (j-i) one-bits, | |
2041 | // followed by a zero bit. If we shift it right by (7-(j-i)) bits | |
2042 | // (with sign extension) then the result has to be 0xFE. | |
2043 | // If this is right, then we reinstate the tentatively eliminated bytes. | |
2044 | if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; | |
2045 | } | |
2046 | } | |
2047 | break; | |
c9b9ae52 A |
2048 | case 1: i = (unsigned char) *s++; break; // Pascal string |
2049 | case 2: { // DNS label-sequence name | |
2050 | unsigned char *a = (unsigned char *)s; | |
2051 | s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end | |
2052 | if (*a == 0) *s++ = '.'; // Special case for root DNS name | |
2053 | while (*a) | |
2054 | { | |
c9d2d929 A |
2055 | if (*a > 63) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; } |
2056 | if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; } | |
c9b9ae52 A |
2057 | s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a); |
2058 | a += 1 + *a; | |
2059 | } | |
2060 | i = (mDNSu32)(s - mDNS_VACB); | |
2061 | s = mDNS_VACB; // Reset s back to the start of the buffer | |
2062 | break; | |
2063 | } | |
2064 | } | |
8e92c31c A |
2065 | // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) |
2066 | if (F.havePrecision && i > F.precision) | |
c9b9ae52 A |
2067 | { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } |
2068 | break; | |
2069 | ||
2070 | case 'n' : s = va_arg(arg, char *); | |
2071 | if (F.hSize) * (short *) s = (short)nwritten; | |
2072 | else if (F.lSize) * (long *) s = (long)nwritten; | |
2073 | else * (int *) s = (int)nwritten; | |
2074 | continue; | |
2075 | ||
2076 | default: s = mDNS_VACB; | |
2077 | i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c); | |
2078 | ||
2079 | case '%' : *sbuffer++ = (char)c; | |
2080 | if (++nwritten >= buflen) goto exit; | |
2081 | break; | |
2082 | } | |
2083 | ||
2084 | if (i < F.fieldWidth && !F.leftJustify) // Pad on the left | |
2085 | do { | |
2086 | *sbuffer++ = ' '; | |
2087 | if (++nwritten >= buflen) goto exit; | |
2088 | } while (i < --F.fieldWidth); | |
2089 | ||
8e92c31c | 2090 | // Make sure we don't truncate in the middle of a UTF-8 character. |
c9d2d929 A |
2091 | // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the allowed output. If s[i] is a |
2092 | // UTF-8 continuation character, then we've cut a unicode character in half, so back up 'i' until s[i] is no longer a UTF-8 continuation | |
2093 | // character. (if the input was proprly formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). | |
8e92c31c | 2094 | if (i > buflen - nwritten) |
c9b9ae52 A |
2095 | { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } |
2096 | for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result | |
2097 | nwritten += i; | |
2098 | if (nwritten >= buflen) goto exit; | |
2099 | ||
2100 | for (; i < F.fieldWidth; i++) // Pad on the right | |
2101 | { | |
2102 | *sbuffer++ = ' '; | |
2103 | if (++nwritten >= buflen) goto exit; | |
2104 | } | |
2105 | } | |
2106 | } | |
2107 | exit: | |
2108 | *sbuffer++ = 0; | |
2109 | return(nwritten); | |
2110 | } | |
2111 | ||
2112 | mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) | |
2113 | { | |
2114 | mDNSu32 length; | |
2115 | ||
2116 | va_list ptr; | |
2117 | va_start(ptr,fmt); | |
2118 | length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); | |
2119 | va_end(ptr); | |
2120 | ||
2121 | return(length); | |
2122 | } | |
2123 | ||
2124 | // *************************************************************************** | |
2125 | #if COMPILER_LIKES_PRAGMA_MARK | |
6528fe3e A |
2126 | #pragma mark - |
2127 | #pragma mark - General Utility Functions | |
2128 | #endif | |
2129 | ||
c9b9ae52 A |
2130 | #define InitialQuestionInterval (mDNSPlatformOneSecond/2) |
2131 | #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) | |
2132 | #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0) | |
2133 | ||
2134 | mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) | |
6528fe3e | 2135 | { |
c9b9ae52 A |
2136 | if (ActiveQuestion(q)) |
2137 | if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0) | |
2138 | m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval); | |
6528fe3e A |
2139 | } |
2140 | ||
283ee3ff A |
2141 | mDNSlocal CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) |
2142 | { | |
2143 | CacheGroup *cg; | |
2144 | for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) | |
2145 | if (cg->namehash == namehash && SameDomainName(cg->name, name)) | |
2146 | break; | |
2147 | return(cg); | |
2148 | } | |
2149 | ||
2150 | mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) | |
2151 | { | |
2152 | return(CacheGroupForName(m, slot, rr->namehash, rr->name)); | |
2153 | } | |
2154 | ||
2155 | mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) | |
2156 | { | |
2157 | NetworkInterfaceInfo *intf; | |
2158 | ||
2159 | if (addr->type == mDNSAddrType_IPv4) | |
2160 | { | |
2161 | if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue); | |
2162 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
2163 | if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) | |
2164 | if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) | |
2165 | return(mDNStrue); | |
2166 | } | |
2167 | ||
2168 | if (addr->type == mDNSAddrType_IPv6) | |
2169 | { | |
2170 | if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue); | |
2171 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
2172 | if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) | |
2173 | if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && | |
2174 | (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && | |
2175 | (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && | |
2176 | (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) | |
2177 | return(mDNStrue); | |
2178 | } | |
2179 | ||
2180 | return(mDNSfalse); | |
2181 | } | |
2182 | ||
2183 | // Set up a AuthRecord with sensible default values. | |
2184 | // These defaults may be overwritten with new values before mDNS_Register is called | |
2185 | mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, | |
2186 | mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) | |
2187 | { | |
2188 | mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); | |
2189 | // Don't try to store a TTL bigger than we can represent in platform time units | |
2190 | if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) | |
2191 | ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; | |
2192 | else if (ttl == 0) // And Zero TTL is illegal | |
2193 | ttl = DefaultTTLforRRType(rrtype); | |
2194 | ||
2195 | // Field Group 1: The actual information pertaining to this resource record | |
2196 | rr->resrec.RecordType = RecordType; | |
2197 | rr->resrec.InterfaceID = InterfaceID; | |
2198 | rr->resrec.name = &rr->namestorage; | |
2199 | rr->resrec.rrtype = rrtype; | |
2200 | rr->resrec.rrclass = kDNSClass_IN; | |
2201 | rr->resrec.rroriginalttl = ttl; | |
2202 | // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal | |
2203 | // rr->resrec.rdestimate = set in mDNS_Register_internal | |
2204 | // rr->resrec.rdata = MUST be set by client | |
2205 | ||
2206 | if (RDataStorage) | |
2207 | rr->resrec.rdata = RDataStorage; | |
2208 | else | |
2209 | { | |
2210 | rr->resrec.rdata = &rr->rdatastorage; | |
2211 | rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); | |
2212 | } | |
2213 | ||
2214 | // Field Group 2: Persistent metadata for Authoritative Records | |
2215 | rr->Additional1 = mDNSNULL; | |
2216 | rr->Additional2 = mDNSNULL; | |
2217 | rr->DependentOn = mDNSNULL; | |
2218 | rr->RRSet = mDNSNULL; | |
2219 | rr->RecordCallback = Callback; | |
2220 | rr->RecordContext = Context; | |
2221 | ||
2222 | rr->HostTarget = mDNSfalse; | |
2223 | rr->AllowRemoteQuery = mDNSfalse; | |
2224 | rr->ForceMCast = mDNSfalse; | |
2225 | ||
2226 | // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) | |
2227 | ||
2228 | rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() | |
2229 | } | |
2230 | ||
2231 | // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord | |
2232 | // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion() | |
2233 | mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord) | |
2234 | { | |
2235 | // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it | |
2236 | if (AddRecord) rr->LocalAnswer = mDNStrue; | |
2237 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
2238 | if (q->QuestionCallback) | |
2239 | q->QuestionCallback(m, q, &rr->resrec, AddRecord); | |
2240 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
2241 | } | |
2242 | ||
c9d2d929 A |
2243 | // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers to each, |
2244 | // stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion(). | |
283ee3ff A |
2245 | // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any. |
2246 | // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal() | |
2247 | mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) | |
2248 | { | |
2249 | if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set"); | |
2250 | ||
2251 | m->CurrentQuestion = m->LocalOnlyQuestions; | |
2252 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) | |
2253 | { | |
2254 | DNSQuestion *q = m->CurrentQuestion; | |
2255 | m->CurrentQuestion = q->next; | |
2256 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
2257 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again | |
2258 | } | |
2259 | ||
2260 | // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions | |
2261 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) | |
2262 | { | |
2263 | m->CurrentQuestion = m->Questions; | |
2264 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) | |
2265 | { | |
2266 | DNSQuestion *q = m->CurrentQuestion; | |
2267 | m->CurrentQuestion = q->next; | |
2268 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
2269 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again | |
2270 | } | |
2271 | } | |
2272 | ||
2273 | m->CurrentQuestion = mDNSNULL; | |
2274 | } | |
2275 | ||
6528fe3e | 2276 | // *************************************************************************** |
c9b9ae52 | 2277 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e | 2278 | #pragma mark - |
8e92c31c | 2279 | #pragma mark - Resource Record Utility Functions |
6528fe3e A |
2280 | #endif |
2281 | ||
8e92c31c | 2282 | #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) |
6528fe3e | 2283 | |
8e92c31c A |
2284 | #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \ |
2285 | ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ | |
2286 | ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ | |
2287 | ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) | |
6528fe3e | 2288 | |
8e92c31c A |
2289 | #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \ |
2290 | (ResourceRecordIsValidAnswer(RR) && \ | |
2291 | ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) | |
6528fe3e | 2292 | |
8e92c31c A |
2293 | #define DefaultProbeCountForTypeUnique ((mDNSu8)3) |
2294 | #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) | |
6528fe3e | 2295 | |
8e92c31c | 2296 | #define InitialAnnounceCount ((mDNSu8)10) |
c9b9ae52 | 2297 | |
8e92c31c A |
2298 | // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. |
2299 | // This means that because the announce interval is doubled after sending the first packet, the first | |
2300 | // observed on-the-wire inter-packet interval between announcements is actually one second. | |
2301 | // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent. | |
2302 | #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4) | |
2303 | #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) | |
2304 | #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) | |
6528fe3e | 2305 | |
8e92c31c A |
2306 | #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ |
2307 | (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ | |
2308 | (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) | |
6528fe3e | 2309 | |
8e92c31c A |
2310 | #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) |
2311 | #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) | |
2312 | #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond) | |
2313 | #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR)) | |
6528fe3e | 2314 | |
8e92c31c | 2315 | #define MaxUnansweredQueries 4 |
c9b9ae52 | 2316 | |
8e92c31c A |
2317 | // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent |
2318 | // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match). | |
2319 | // TTL and rdata may differ. | |
2320 | // This is used for cache flush management: | |
2321 | // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent | |
2322 | // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed | |
2323 | mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2) | |
c9b9ae52 | 2324 | { |
8e92c31c A |
2325 | if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } |
2326 | if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } | |
2327 | if (r1->InterfaceID && | |
2328 | r2->InterfaceID && | |
2329 | r1->InterfaceID != r2->InterfaceID) return(mDNSfalse); | |
c9d2d929 | 2330 | return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(r1->name, r2->name)); |
c9b9ae52 A |
2331 | } |
2332 | ||
7f0064bd A |
2333 | // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our |
2334 | // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have | |
2335 | // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict. | |
2336 | // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY, | |
2337 | // so a response of any type should match, even if it is not actually the type the client plans to use. | |
8e92c31c | 2338 | mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) |
c9b9ae52 | 2339 | { |
8e92c31c A |
2340 | if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } |
2341 | if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } | |
2342 | if (pktrr->resrec.InterfaceID && | |
2343 | authrr->resrec.InterfaceID && | |
2344 | pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); | |
7f0064bd | 2345 | if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); |
c9d2d929 | 2346 | return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(pktrr->resrec.name, authrr->resrec.name)); |
c9b9ae52 | 2347 | } |
6528fe3e | 2348 | |
8e92c31c A |
2349 | // IdenticalResourceRecord returns true if two resources records have |
2350 | // the same name, type, class, and identical rdata (InterfaceID and TTL may differ) | |
2351 | mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2) | |
c9b9ae52 | 2352 | { |
8e92c31c A |
2353 | if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); } |
2354 | if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); } | |
c9d2d929 | 2355 | if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name)) return(mDNSfalse); |
8e92c31c | 2356 | return(SameRData(r1, r2)); |
6528fe3e A |
2357 | } |
2358 | ||
8e92c31c A |
2359 | // CacheRecord *ks is the CacheRecord from the known answer list in the query. |
2360 | // This is the information that the requester believes to be correct. | |
2361 | // AuthRecord *rr is the answer we are proposing to give, if not suppressed. | |
2362 | // This is the information that we believe to be correct. | |
2363 | // We've already determined that we plan to give this answer on this interface | |
2364 | // (either the record is non-specific, or it is specific to this interface) | |
2365 | // so now we just need to check the name, type, class, rdata and TTL. | |
2366 | mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr) | |
c9b9ae52 | 2367 | { |
8e92c31c | 2368 | // If RR signature is different, or data is different, then don't suppress our answer |
283ee3ff | 2369 | if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse); |
8e92c31c A |
2370 | |
2371 | // If the requester's indicated TTL is less than half the real TTL, | |
2372 | // we need to give our answer before the requester's copy expires. | |
2373 | // If the requester's indicated TTL is at least half the real TTL, | |
2374 | // then we can suppress our answer this time. | |
2375 | // If the requester's indicated TTL is greater than the TTL we believe, | |
2376 | // then that's okay, and we don't need to do anything about it. | |
2377 | // (If two responders on the network are offering the same information, | |
2378 | // that's okay, and if they are offering the information with different TTLs, | |
2379 | // the one offering the lower TTL should defer to the one offering the higher TTL.) | |
2380 | return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); | |
c9b9ae52 | 2381 | } |
6528fe3e | 2382 | |
8e92c31c | 2383 | mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) |
6528fe3e | 2384 | { |
8e92c31c | 2385 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) |
6528fe3e | 2386 | { |
c9d2d929 | 2387 | //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); |
8e92c31c A |
2388 | if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) |
2389 | m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); | |
c9b9ae52 A |
2390 | } |
2391 | else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr)) | |
2392 | { | |
2393 | if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) | |
2394 | m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); | |
2395 | } | |
6528fe3e A |
2396 | } |
2397 | ||
c9b9ae52 | 2398 | mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) |
6528fe3e | 2399 | { |
c9b9ae52 A |
2400 | // To allow us to aggregate probes when a group of services are registered together, |
2401 | // the first probe is delayed 1/4 second. This means the common-case behaviour is: | |
2402 | // 1/4 second wait; probe | |
2403 | // 1/4 second wait; probe | |
2404 | // 1/4 second wait; probe | |
2405 | // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) | |
2406 | ||
2407 | // If we have no probe suppression time set, or it is in the past, set it now | |
2408 | if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) | |
6528fe3e | 2409 | { |
7f0064bd | 2410 | m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique); |
c9b9ae52 A |
2411 | // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation |
2412 | if (m->SuppressProbes - m->NextScheduledProbe >= 0) | |
2413 | m->SuppressProbes = m->NextScheduledProbe; | |
2414 | // If we already have a query scheduled to go out sooner, then use that time to get better aggregation | |
2415 | if (m->SuppressProbes - m->NextScheduledQuery >= 0) | |
2416 | m->SuppressProbes = m->NextScheduledQuery; | |
6528fe3e | 2417 | } |
6528fe3e | 2418 | |
c9b9ae52 A |
2419 | // We announce to flush stale data from other caches. It is a reasonable assumption that any |
2420 | // old stale copies will probably have the same TTL we're using, so announcing longer than | |
2421 | // this serves no purpose -- any stale copies of that record will have expired by then anyway. | |
2422 | rr->AnnounceUntil = m->timenow + TicksTTL(rr); | |
2423 | rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; | |
2424 | // Set LastMCTime to now, to inhibit multicast responses | |
2425 | // (no need to send additional multicast responses when we're announcing anyway) | |
2426 | rr->LastMCTime = m->timenow; | |
2427 | rr->LastMCInterface = mDNSInterfaceMark; | |
2428 | ||
2429 | // If this is a record type that's not going to probe, then delay its first announcement so that | |
2430 | // it will go out synchronized with the first announcement for the other records that *are* probing. | |
2431 | // This is a minor performance tweak that helps keep groups of related records synchronized together. | |
2432 | // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are | |
2433 | // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. | |
2434 | // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, | |
2435 | // because they will meet the criterion of being at least half-way to their scheduled announcement time. | |
2436 | if (rr->resrec.RecordType != kDNSRecordTypeUnique) | |
2437 | rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; | |
2438 | ||
2439 | SetNextAnnounceProbeTime(m, rr); | |
6528fe3e A |
2440 | } |
2441 | ||
8e92c31c | 2442 | #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) |
c9b9ae52 A |
2443 | |
2444 | mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) | |
2445 | { | |
2446 | domainname *target = GetRRDomainNameTarget(&rr->resrec); | |
2447 | ||
2448 | if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); | |
2449 | ||
7f0064bd | 2450 | if (target && SameDomainName(target, &m->MulticastHostname)) |
283ee3ff | 2451 | debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c); |
c9b9ae52 | 2452 | |
7f0064bd | 2453 | if (target && !SameDomainName(target, &m->MulticastHostname)) |
c9b9ae52 | 2454 | { |
283ee3ff | 2455 | AssignDomainName(target, &m->MulticastHostname); |
c9b9ae52 A |
2456 | SetNewRData(&rr->resrec, mDNSNULL, 0); |
2457 | ||
2458 | // If we're in the middle of probing this record, we need to start again, | |
2459 | // because changing its rdata may change the outcome of the tie-breaker. | |
2460 | // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) | |
2461 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); | |
2462 | ||
2463 | // If we've announced this record, we really should send a goodbye packet for the old rdata before | |
2464 | // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, | |
2465 | // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. | |
7f0064bd | 2466 | if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared) |
c9d2d929 | 2467 | debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 | 2468 | |
7f0064bd A |
2469 | rr->AnnounceCount = InitialAnnounceCount; |
2470 | rr->RequireGoodbye = mDNSfalse; | |
c9b9ae52 A |
2471 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); |
2472 | InitializeLastAPTime(m,rr); | |
2473 | } | |
2474 | } | |
2475 | ||
7f0064bd | 2476 | mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) |
c9b9ae52 | 2477 | { |
c9b9ae52 A |
2478 | if (!rr->Acknowledged && rr->RecordCallback) |
2479 | { | |
2480 | // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function | |
2481 | // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. | |
2482 | rr->Acknowledged = mDNStrue; | |
2483 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
2484 | rr->RecordCallback(m, rr, mStatus_NoError); | |
2485 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
2486 | } | |
2487 | } | |
2488 | ||
c9b9ae52 | 2489 | // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified |
c9d2d929 A |
2490 | #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified)) |
2491 | #define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec)) | |
c9b9ae52 A |
2492 | |
2493 | mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) | |
6528fe3e | 2494 | { |
c9b9ae52 A |
2495 | domainname *target = GetRRDomainNameTarget(&rr->resrec); |
2496 | AuthRecord *r; | |
2497 | AuthRecord **p = &m->ResourceRecords; | |
2498 | AuthRecord **d = &m->DuplicateRecords; | |
7f0064bd A |
2499 | |
2500 | mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); | |
2501 | ||
2502 | if ((mDNSs32)rr->resrec.rroriginalttl <= 0) | |
2503 | { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } | |
c9b9ae52 | 2504 | |
7f0064bd | 2505 | #ifndef UNICAST_DISABLED |
283ee3ff | 2506 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)) |
8e92c31c A |
2507 | rr->uDNS_info.id = zeroID; |
2508 | else return uDNS_RegisterRecord(m, rr); | |
7f0064bd A |
2509 | #endif |
2510 | ||
6528fe3e | 2511 | while (*p && *p != rr) p=&(*p)->next; |
c9b9ae52 | 2512 | while (*d && *d != rr) d=&(*d)->next; |
283ee3ff | 2513 | if (*d || *p) |
6528fe3e | 2514 | { |
c9d2d929 | 2515 | LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e A |
2516 | return(mStatus_AlreadyRegistered); |
2517 | } | |
2518 | ||
2519 | if (rr->DependentOn) | |
2520 | { | |
c9b9ae52 A |
2521 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) |
2522 | rr->resrec.RecordType = kDNSRecordTypeVerified; | |
6528fe3e A |
2523 | else |
2524 | { | |
c9b9ae52 | 2525 | LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", |
283ee3ff | 2526 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e A |
2527 | return(mStatus_Invalid); |
2528 | } | |
c9b9ae52 | 2529 | if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified))) |
6528fe3e | 2530 | { |
c9b9ae52 | 2531 | LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", |
283ee3ff | 2532 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); |
6528fe3e A |
2533 | return(mStatus_Invalid); |
2534 | } | |
2535 | } | |
2536 | ||
c9b9ae52 | 2537 | // If this resource record is referencing a specific interface, make sure it exists |
8e92c31c | 2538 | if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly) |
c9b9ae52 A |
2539 | { |
2540 | NetworkInterfaceInfo *intf; | |
2541 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
2542 | if (intf->InterfaceID == rr->resrec.InterfaceID) break; | |
2543 | if (!intf) | |
2544 | { | |
2545 | debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID); | |
2546 | return(mStatus_BadReferenceErr); | |
2547 | } | |
2548 | } | |
2549 | ||
6528fe3e A |
2550 | rr->next = mDNSNULL; |
2551 | ||
2552 | // Field Group 1: Persistent metadata for Authoritative Records | |
2553 | // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2554 | // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2555 | // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2556 | // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2557 | // rr->Callback = already set in mDNS_SetupResourceRecord | |
2558 | // rr->Context = already set in mDNS_SetupResourceRecord | |
2559 | // rr->RecordType = already set in mDNS_SetupResourceRecord | |
c9b9ae52 | 2560 | // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client |
7f0064bd | 2561 | // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client |
8e92c31c A |
2562 | // Make sure target is not uninitialized data, or we may crash writing debugging log messages |
2563 | if (rr->HostTarget && target) target->c[0] = 0; | |
6528fe3e A |
2564 | |
2565 | // Field Group 2: Transient state for Authoritative Records | |
2566 | rr->Acknowledged = mDNSfalse; | |
c9b9ae52 A |
2567 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); |
2568 | rr->AnnounceCount = InitialAnnounceCount; | |
7f0064bd | 2569 | rr->RequireGoodbye = mDNSfalse; |
283ee3ff | 2570 | rr->LocalAnswer = mDNSfalse; |
6528fe3e | 2571 | rr->IncludeInProbe = mDNSfalse; |
c9b9ae52 | 2572 | rr->ImmedAnswer = mDNSNULL; |
7f0064bd | 2573 | rr->ImmedUnicast = mDNSfalse; |
c9b9ae52 A |
2574 | rr->ImmedAdditional = mDNSNULL; |
2575 | rr->SendRNow = mDNSNULL; | |
7f0064bd | 2576 | rr->v4Requester = zerov4Addr; |
c9b9ae52 | 2577 | rr->v6Requester = zerov6Addr; |
6528fe3e A |
2578 | rr->NextResponse = mDNSNULL; |
2579 | rr->NR_AnswerTo = mDNSNULL; | |
2580 | rr->NR_AdditionalTo = mDNSNULL; | |
c9b9ae52 | 2581 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); |
8e92c31c | 2582 | if (!rr->HostTarget) InitializeLastAPTime(m, rr); |
c9b9ae52 A |
2583 | // rr->AnnounceUntil = Set for us in InitializeLastAPTime() |
2584 | // rr->LastAPTime = Set for us in InitializeLastAPTime() | |
2585 | // rr->LastMCTime = Set for us in InitializeLastAPTime() | |
2586 | // rr->LastMCInterface = Set for us in InitializeLastAPTime() | |
6528fe3e | 2587 | rr->NewRData = mDNSNULL; |
c9b9ae52 | 2588 | rr->newrdlength = 0; |
6528fe3e | 2589 | rr->UpdateCallback = mDNSNULL; |
c9b9ae52 A |
2590 | rr->UpdateCredits = kMaxUpdateCredits; |
2591 | rr->NextUpdateCredit = 0; | |
2592 | rr->UpdateBlocked = 0; | |
6528fe3e | 2593 | |
c9b9ae52 | 2594 | // rr->resrec.interface = already set in mDNS_SetupResourceRecord |
283ee3ff | 2595 | // rr->resrec.name->c = MUST be set by client |
c9b9ae52 A |
2596 | // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord |
2597 | // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord | |
2598 | // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord | |
2599 | // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set | |
6528fe3e A |
2600 | |
2601 | if (rr->HostTarget) | |
8e92c31c | 2602 | SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); |
6528fe3e A |
2603 | else |
2604 | { | |
c9b9ae52 A |
2605 | rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); |
2606 | rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); | |
6528fe3e | 2607 | } |
6528fe3e | 2608 | |
283ee3ff | 2609 | if (!ValidateDomainName(rr->resrec.name)) |
7f0064bd A |
2610 | { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } |
2611 | ||
7cb34e5c A |
2612 | // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, |
2613 | // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". | |
2614 | // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. | |
7f0064bd | 2615 | if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } |
c9b9ae52 A |
2616 | |
2617 | // Don't do this until *after* we've set rr->resrec.rdlength | |
2618 | if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) | |
7f0064bd | 2619 | { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } |
c9b9ae52 | 2620 | |
283ee3ff | 2621 | rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); |
7cb34e5c | 2622 | rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); |
c9b9ae52 | 2623 | |
8e92c31c | 2624 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) |
c9b9ae52 | 2625 | { |
c9b9ae52 A |
2626 | // If this is supposed to be unique, make sure we don't have any name conflicts |
2627 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
2628 | { | |
2629 | const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; | |
283ee3ff | 2630 | for (r = m->ResourceRecords; r; r=r->next) |
c9b9ae52 A |
2631 | { |
2632 | const AuthRecord *s2 = r->RRSet ? r->RRSet : r; | |
2633 | if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec)) | |
2634 | break; | |
2635 | } | |
283ee3ff | 2636 | if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback |
c9b9ae52 | 2637 | { |
283ee3ff A |
2638 | debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
2639 | rr->resrec.RecordType = kDNSRecordTypeDeregistering; | |
2640 | rr->resrec.rroriginalttl = 0; | |
2641 | rr->ImmedAnswer = mDNSInterfaceMark; | |
2642 | m->NextScheduledResponse = m->timenow; | |
c9b9ae52 A |
2643 | } |
2644 | } | |
2645 | } | |
283ee3ff A |
2646 | |
2647 | // Now that we've finished building our new record, make sure it's not identical to one we already have | |
2648 | for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break; | |
2649 | ||
2650 | if (r) | |
2651 | { | |
7cb34e5c | 2652 | debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr)); |
283ee3ff A |
2653 | *d = rr; |
2654 | // If the previous copy of this record is already verified unique, | |
2655 | // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. | |
2656 | // Setting ProbeCount to zero will cause SendQueries() to advance this record to | |
2657 | // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. | |
2658 | if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) | |
2659 | rr->ProbeCount = 0; | |
2660 | } | |
c9b9ae52 A |
2661 | else |
2662 | { | |
7cb34e5c | 2663 | debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr)); |
283ee3ff A |
2664 | if (!m->NewLocalRecords) m->NewLocalRecords = rr; |
2665 | *p = rr; | |
c9b9ae52 | 2666 | } |
7f0064bd A |
2667 | |
2668 | // For records that are not going to probe, acknowledge them right away | |
283ee3ff A |
2669 | if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) |
2670 | AcknowledgeRecord(m, rr); | |
7f0064bd | 2671 | |
6528fe3e A |
2672 | return(mStatus_NoError); |
2673 | } | |
2674 | ||
c9b9ae52 A |
2675 | mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) |
2676 | { | |
2677 | m->ProbeFailTime = m->timenow; | |
2678 | m->NumFailedProbes++; | |
7f0064bd A |
2679 | // If we've had fifteen or more probe failures, rate-limit to one every five seconds. |
2680 | // If a bunch of hosts have all been configured with the same name, then they'll all | |
2681 | // conflict and run through the same series of names: name-2, name-3, name-4, etc., | |
2682 | // up to name-10. After that they'll start adding random increments in the range 1-100, | |
2683 | // so they're more likely to branch out in the available namespace and settle on a set of | |
2684 | // unique names quickly. If after five more tries the host is still conflicting, then we | |
2685 | // may have a serious problem, so we start rate-limiting so we don't melt down the network. | |
2686 | if (m->NumFailedProbes >= 15) | |
8e92c31c | 2687 | { |
7f0064bd A |
2688 | m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); |
2689 | LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect", | |
283ee3ff | 2690 | m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2691 | } |
2692 | } | |
2693 | ||
2694 | mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr) | |
2695 | { | |
2696 | RData *OldRData = rr->resrec.rdata; | |
2697 | SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata | |
2698 | rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... | |
2699 | if (rr->UpdateCallback) | |
2700 | rr->UpdateCallback(m, rr, OldRData); // ... and let the client know | |
c9b9ae52 A |
2701 | } |
2702 | ||
6528fe3e A |
2703 | // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal |
2704 | // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict | |
2705 | // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered | |
2706 | typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type; | |
2707 | ||
2708 | // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. | |
2709 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 2710 | mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt) |
6528fe3e | 2711 | { |
283ee3ff | 2712 | AuthRecord *r2; |
c9b9ae52 A |
2713 | mDNSu8 RecordType = rr->resrec.RecordType; |
2714 | AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records | |
8e92c31c | 2715 | |
7f0064bd | 2716 | #ifndef UNICAST_DISABLED |
283ee3ff | 2717 | if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))) |
8e92c31c | 2718 | return uDNS_DeregisterRecord(m, rr); |
7f0064bd | 2719 | #endif |
8e92c31c | 2720 | |
c9b9ae52 A |
2721 | while (*p && *p != rr) p=&(*p)->next; |
2722 | ||
2723 | if (*p) | |
6528fe3e | 2724 | { |
c9b9ae52 A |
2725 | // We found our record on the main list. See if there are any duplicates that need special handling. |
2726 | if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment | |
2727 | { | |
283ee3ff A |
2728 | // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished |
2729 | // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory. | |
2730 | for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF; | |
c9b9ae52 A |
2731 | } |
2732 | else | |
2733 | { | |
2734 | // Before we delete the record (and potentially send a goodbye packet) | |
2735 | // first see if we have a record on the duplicate list ready to take over from it. | |
2736 | AuthRecord **d = &m->DuplicateRecords; | |
2737 | while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; | |
2738 | if (*d) | |
2739 | { | |
2740 | AuthRecord *dup = *d; | |
c9d2d929 | 2741 | debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2742 | *d = dup->next; // Cut replacement record from DuplicateRecords list |
2743 | dup->next = rr->next; // And then... | |
2744 | rr->next = dup; // ... splice it in right after the record we're about to delete | |
2745 | dup->resrec.RecordType = rr->resrec.RecordType; | |
7f0064bd A |
2746 | dup->ProbeCount = rr->ProbeCount; |
2747 | dup->AnnounceCount = rr->AnnounceCount; | |
2748 | dup->RequireGoodbye = rr->RequireGoodbye; | |
2749 | dup->ImmedAnswer = rr->ImmedAnswer; | |
2750 | dup->ImmedUnicast = rr->ImmedUnicast; | |
2751 | dup->ImmedAdditional = rr->ImmedAdditional; | |
2752 | dup->v4Requester = rr->v4Requester; | |
2753 | dup->v6Requester = rr->v6Requester; | |
2754 | dup->ThisAPInterval = rr->ThisAPInterval; | |
2755 | dup->AnnounceUntil = rr->AnnounceUntil; | |
2756 | dup->LastAPTime = rr->LastAPTime; | |
2757 | dup->LastMCTime = rr->LastMCTime; | |
2758 | dup->LastMCInterface = rr->LastMCInterface; | |
2759 | rr->RequireGoodbye = mDNSfalse; | |
c9b9ae52 A |
2760 | } |
2761 | } | |
6528fe3e A |
2762 | } |
2763 | else | |
2764 | { | |
c9b9ae52 A |
2765 | // We didn't find our record on the main list; try the DuplicateRecords list instead. |
2766 | p = &m->DuplicateRecords; | |
6528fe3e | 2767 | while (*p && *p != rr) p=&(*p)->next; |
c9b9ae52 | 2768 | // If we found our record on the duplicate list, then make sure we don't send a goodbye for it |
7f0064bd | 2769 | if (*p) rr->RequireGoodbye = mDNSfalse; |
c9d2d929 | 2770 | if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2771 | } |
2772 | ||
2773 | if (!*p) | |
2774 | { | |
2775 | // No need to log an error message if we already know this is a potentially repeated deregistration | |
2776 | if (drt != mDNS_Dereg_repeat) | |
c9d2d929 | 2777 | LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2778 | return(mStatus_BadReferenceErr); |
2779 | } | |
6528fe3e | 2780 | |
c9b9ae52 A |
2781 | // If this is a shared record and we've announced it at least once, |
2782 | // we need to retract that announcement before we delete the record | |
7f0064bd | 2783 | if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) |
c9b9ae52 | 2784 | { |
c9d2d929 | 2785 | verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2786 | rr->resrec.RecordType = kDNSRecordTypeDeregistering; |
2787 | rr->resrec.rroriginalttl = 0; | |
2788 | rr->ImmedAnswer = mDNSInterfaceMark; | |
283ee3ff A |
2789 | if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) |
2790 | m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); | |
8e92c31c A |
2791 | } |
2792 | else | |
2793 | { | |
2794 | *p = rr->next; // Cut this record from the list | |
2795 | // If someone is about to look at this, bump the pointer forward | |
283ee3ff A |
2796 | if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; |
2797 | if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; | |
8e92c31c | 2798 | rr->next = mDNSNULL; |
6528fe3e | 2799 | |
8e92c31c A |
2800 | if (RecordType == kDNSRecordTypeUnregistered) |
2801 | debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", | |
283ee3ff | 2802 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2803 | else if (RecordType == kDNSRecordTypeDeregistering) |
2804 | debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", | |
283ee3ff | 2805 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2806 | else |
2807 | { | |
c9d2d929 | 2808 | verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2809 | rr->resrec.RecordType = kDNSRecordTypeUnregistered; |
2810 | } | |
6528fe3e | 2811 | |
8e92c31c A |
2812 | if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) |
2813 | debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", | |
283ee3ff | 2814 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e | 2815 | |
8e92c31c A |
2816 | // If we have an update queued up which never executed, give the client a chance to free that memory |
2817 | if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client | |
2818 | ||
283ee3ff A |
2819 | if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse); |
2820 | ||
8e92c31c A |
2821 | // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function |
2822 | // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. | |
2823 | // In this case the likely client action to the mStatus_MemFree message is to free the memory, | |
2824 | // so any attempt to touch rr after this is likely to lead to a crash. | |
2825 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
283ee3ff A |
2826 | if (drt != mDNS_Dereg_conflict) |
2827 | { | |
2828 | if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this | |
2829 | } | |
2830 | else | |
8e92c31c A |
2831 | { |
2832 | RecordProbeFailure(m, rr); | |
283ee3ff A |
2833 | if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this |
2834 | // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously. | |
2835 | // Note that with all the client callbacks going on, by the time we get here all the | |
2836 | // records we marked may have been explicitly deregistered by the client anyway. | |
2837 | r2 = m->DuplicateRecords; | |
2838 | while (r2) | |
2839 | { | |
2840 | if (r2->ProbeCount != 0xFF) r2 = r2->next; | |
2841 | else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; } | |
2842 | } | |
8e92c31c A |
2843 | } |
2844 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
2845 | } | |
2846 | return(mStatus_NoError); | |
6528fe3e A |
2847 | } |
2848 | ||
2849 | // *************************************************************************** | |
c9b9ae52 | 2850 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
2851 | #pragma mark - |
2852 | #pragma mark - | |
2853 | #pragma mark - Packet Sending Functions | |
2854 | #endif | |
2855 | ||
7f0064bd A |
2856 | mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) |
2857 | { | |
2858 | if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) | |
2859 | { | |
2860 | **nrpp = rr; | |
2861 | // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) | |
2862 | // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does | |
2863 | // The referenced record will definitely be acceptable (by recursive application of this rule) | |
2864 | if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; | |
2865 | rr->NR_AdditionalTo = add; | |
2866 | *nrpp = &rr->NextResponse; | |
2867 | } | |
283ee3ff | 2868 | debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd A |
2869 | } |
2870 | ||
2871 | mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID) | |
2872 | { | |
2873 | AuthRecord *rr, *rr2; | |
2874 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put | |
2875 | { | |
2876 | // (Note: This is an "if", not a "while". If we add a record, we'll find it again | |
2877 | // later in the "for" loop, and we will follow further "additional" links then.) | |
2878 | if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) | |
2879 | AddRecordToResponseList(nrpp, rr->Additional1, rr); | |
2880 | ||
2881 | if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) | |
2882 | AddRecordToResponseList(nrpp, rr->Additional2, rr); | |
2883 | ||
2884 | // For SRV records, automatically add the Address record(s) for the target host | |
2885 | if (rr->resrec.rrtype == kDNSType_SRV) | |
2886 | for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records | |
2887 | if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... | |
2888 | ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... | |
7cb34e5c | 2889 | rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target |
283ee3ff | 2890 | SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name)) |
7f0064bd A |
2891 | AddRecordToResponseList(nrpp, rr2, rr); |
2892 | } | |
2893 | } | |
2894 | ||
2895 | mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) | |
2896 | { | |
2897 | AuthRecord *rr; | |
2898 | AuthRecord *ResponseRecords = mDNSNULL; | |
2899 | AuthRecord **nrp = &ResponseRecords; | |
2900 | ||
2901 | // Make a list of all our records that need to be unicast to this destination | |
2902 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
2903 | { | |
2904 | // If we find we can no longer unicast this answer, clear ImmedUnicast | |
2905 | if (rr->ImmedAnswer == mDNSInterfaceMark || | |
2906 | mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) || | |
2907 | mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) ) | |
2908 | rr->ImmedUnicast = mDNSfalse; | |
2909 | ||
2910 | if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) | |
2911 | if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || | |
2912 | (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) | |
2913 | { | |
2914 | rr->ImmedAnswer = mDNSNULL; // Clear the state fields | |
2915 | rr->ImmedUnicast = mDNSfalse; | |
2916 | rr->v4Requester = zerov4Addr; | |
2917 | rr->v6Requester = zerov6Addr; | |
2918 | if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo | |
2919 | { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } | |
2920 | } | |
2921 | } | |
2922 | ||
2923 | AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); | |
2924 | ||
2925 | while (ResponseRecords) | |
2926 | { | |
2927 | mDNSu8 *responseptr = m->omsg.data; | |
2928 | mDNSu8 *newptr; | |
2929 | InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); | |
2930 | ||
2931 | // Put answers in the packet | |
2932 | while (ResponseRecords && ResponseRecords->NR_AnswerTo) | |
2933 | { | |
2934 | rr = ResponseRecords; | |
2935 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
2936 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it | |
2937 | newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); | |
2938 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state | |
2939 | if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now | |
2940 | if (newptr) responseptr = newptr; | |
2941 | ResponseRecords = rr->NextResponse; | |
2942 | rr->NextResponse = mDNSNULL; | |
2943 | rr->NR_AnswerTo = mDNSNULL; | |
2944 | rr->NR_AdditionalTo = mDNSNULL; | |
2945 | rr->RequireGoodbye = mDNStrue; | |
2946 | } | |
2947 | ||
2948 | // Add additionals, if there's space | |
2949 | while (ResponseRecords && !ResponseRecords->NR_AnswerTo) | |
2950 | { | |
2951 | rr = ResponseRecords; | |
2952 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
2953 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it | |
2954 | newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec); | |
2955 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state | |
2956 | ||
2957 | if (newptr) responseptr = newptr; | |
2958 | if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue; | |
2959 | else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark; | |
2960 | ResponseRecords = rr->NextResponse; | |
2961 | rr->NextResponse = mDNSNULL; | |
2962 | rr->NR_AnswerTo = mDNSNULL; | |
2963 | rr->NR_AdditionalTo = mDNSNULL; | |
2964 | } | |
2965 | ||
2966 | if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL); | |
2967 | } | |
2968 | } | |
2969 | ||
c9b9ae52 | 2970 | mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) |
6528fe3e | 2971 | { |
7f0064bd | 2972 | // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() |
c9b9ae52 | 2973 | // that it should go ahead and immediately dispose of this registration |
7f0064bd A |
2974 | rr->resrec.RecordType = kDNSRecordTypeShared; |
2975 | rr->RequireGoodbye = mDNSfalse; | |
2976 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this | |
6528fe3e A |
2977 | } |
2978 | ||
2979 | // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change | |
2980 | // the record list and/or question list. | |
2981 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 2982 | mDNSlocal void DiscardDeregistrations(mDNS *const m) |
6528fe3e | 2983 | { |
c9b9ae52 | 2984 | if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set"); |
6528fe3e A |
2985 | m->CurrentRecord = m->ResourceRecords; |
2986 | ||
2987 | while (m->CurrentRecord) | |
2988 | { | |
c9b9ae52 | 2989 | AuthRecord *rr = m->CurrentRecord; |
c9b9ae52 | 2990 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) |
7f0064bd | 2991 | CompleteDeregistration(m, rr); // Don't touch rr after this |
283ee3ff A |
2992 | else |
2993 | m->CurrentRecord = rr->next; | |
6528fe3e A |
2994 | } |
2995 | } | |
2996 | ||
8e92c31c A |
2997 | mDNSlocal void GrantUpdateCredit(AuthRecord *rr) |
2998 | { | |
2999 | if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; | |
7f0064bd | 3000 | else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); |
716635cc A |
3001 | } |
3002 | ||
c9b9ae52 A |
3003 | // Note about acceleration of announcements to facilitate automatic coalescing of |
3004 | // multiple independent threads of announcements into a single synchronized thread: | |
3005 | // The announcements in the packet may be at different stages of maturity; | |
3006 | // One-second interval, two-second interval, four-second interval, and so on. | |
3007 | // After we've put in all the announcements that are due, we then consider | |
3008 | // whether there are other nearly-due announcements that are worth accelerating. | |
3009 | // To be eligible for acceleration, a record MUST NOT be older (further along | |
3010 | // its timeline) than the most mature record we've already put in the packet. | |
3011 | // In other words, younger records can have their timelines accelerated to catch up | |
3012 | // with their elder bretheren; this narrows the age gap and helps them eventually get in sync. | |
3013 | // Older records cannot have their timelines accelerated; this would just widen | |
3014 | // the gap between them and their younger bretheren and get them even more out of sync. | |
3015 | ||
3016 | // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change | |
6528fe3e A |
3017 | // the record list and/or question list. |
3018 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 3019 | mDNSlocal void SendResponses(mDNS *const m) |
6528fe3e | 3020 | { |
c9b9ae52 A |
3021 | int pktcount = 0; |
3022 | AuthRecord *rr, *r2; | |
3023 | mDNSs32 maxExistingAnnounceInterval = 0; | |
3024 | const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); | |
6528fe3e | 3025 | |
c9b9ae52 A |
3026 | m->NextScheduledResponse = m->timenow + 0x78000000; |
3027 | ||
7f0064bd A |
3028 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
3029 | if (rr->ImmedUnicast) | |
3030 | { | |
3031 | mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} }; | |
3032 | mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} }; | |
3033 | v4.ip.v4 = rr->v4Requester; | |
3034 | v6.ip.v6 = rr->v6Requester; | |
3035 | if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer); | |
3036 | if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer); | |
3037 | if (rr->ImmedUnicast) | |
3038 | { | |
3039 | LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr)); | |
3040 | rr->ImmedUnicast = mDNSfalse; | |
3041 | } | |
3042 | } | |
3043 | ||
c9b9ae52 A |
3044 | // *** |
3045 | // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on | |
3046 | // *** | |
4e28aad3 | 3047 | |
c9b9ae52 A |
3048 | // Run through our list of records, and decide which ones we're going to announce on all interfaces |
3049 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6528fe3e | 3050 | { |
8e92c31c | 3051 | while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); |
c9b9ae52 A |
3052 | if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr)) |
3053 | { | |
3054 | rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces | |
3055 | if (maxExistingAnnounceInterval < rr->ThisAPInterval) | |
3056 | maxExistingAnnounceInterval = rr->ThisAPInterval; | |
3057 | if (rr->UpdateBlocked) rr->UpdateBlocked = 0; | |
3058 | } | |
3059 | } | |
3060 | ||
3061 | // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) | |
3062 | // Eligible records that are more than half-way to their announcement time are accelerated | |
3063 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3064 | if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || | |
3065 | (rr->ThisAPInterval <= maxExistingAnnounceInterval && | |
3066 | TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && | |
3067 | ResourceRecordIsValidAnswer(rr))) | |
3068 | rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces | |
3069 | ||
8e92c31c A |
3070 | // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals |
3071 | // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, | |
3072 | // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) | |
3073 | // then all that means is that it won't get sent -- which would not be the end of the world. | |
c9b9ae52 A |
3074 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
3075 | if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) | |
3076 | for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records | |
8e92c31c | 3077 | if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ... |
c9b9ae52 A |
3078 | ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... |
3079 | rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... | |
7cb34e5c | 3080 | rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target |
283ee3ff | 3081 | SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) && |
c9b9ae52 | 3082 | (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) |
8e92c31c | 3083 | r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too |
c9b9ae52 A |
3084 | |
3085 | // If there's a record which is supposed to be unique that we're going to send, then make sure that we give | |
3086 | // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class | |
3087 | // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a | |
3088 | // record, then other RRSet members that have not been sent recently will get flushed out of client caches. | |
3089 | // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface | |
3090 | // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces | |
3091 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3092 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
3093 | { | |
3094 | if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked | |
3095 | { | |
3096 | for (r2 = m->ResourceRecords; r2; r2=r2->next) | |
3097 | if (ResourceRecordIsValidAnswer(r2)) | |
c9d2d929 | 3098 | if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) |
c9b9ae52 A |
3099 | r2->ImmedAnswer = rr->ImmedAnswer; |
3100 | } | |
3101 | else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked | |
6528fe3e | 3102 | { |
c9b9ae52 A |
3103 | for (r2 = m->ResourceRecords; r2; r2=r2->next) |
3104 | if (ResourceRecordIsValidAnswer(r2)) | |
3105 | if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) | |
3106 | r2->ImmedAdditional = rr->ImmedAdditional; | |
6528fe3e A |
3107 | } |
3108 | } | |
c9b9ae52 A |
3109 | |
3110 | // Now set SendRNow state appropriately | |
3111 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6528fe3e | 3112 | { |
c9b9ae52 | 3113 | if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces |
6528fe3e | 3114 | { |
c9b9ae52 A |
3115 | rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; |
3116 | rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer | |
3117 | rr->LastMCTime = m->timenow; | |
3118 | rr->LastMCInterface = rr->ImmedAnswer; | |
3119 | // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done | |
3120 | if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) | |
6528fe3e | 3121 | { |
c9b9ae52 A |
3122 | rr->AnnounceCount--; |
3123 | rr->ThisAPInterval *= 2; | |
3124 | rr->LastAPTime = m->timenow; | |
3125 | if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0; | |
283ee3ff | 3126 | debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); |
6528fe3e A |
3127 | } |
3128 | } | |
c9b9ae52 | 3129 | else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: |
6528fe3e | 3130 | { |
8e92c31c | 3131 | rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface |
c9b9ae52 A |
3132 | rr->ImmedAdditional = mDNSNULL; // No need to send as additional too |
3133 | rr->LastMCTime = m->timenow; | |
3134 | rr->LastMCInterface = rr->ImmedAnswer; | |
6528fe3e | 3135 | } |
c9b9ae52 | 3136 | SetNextAnnounceProbeTime(m, rr); |
7f0064bd | 3137 | //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); |
c9b9ae52 A |
3138 | } |
3139 | ||
3140 | // *** | |
3141 | // *** 2. Loop through interface list, sending records as appropriate | |
3142 | // *** | |
3143 | ||
3144 | while (intf) | |
3145 | { | |
3146 | int numDereg = 0; | |
3147 | int numAnnounce = 0; | |
3148 | int numAnswer = 0; | |
7f0064bd | 3149 | mDNSu8 *responseptr = m->omsg.data; |
c9b9ae52 | 3150 | mDNSu8 *newptr; |
7f0064bd | 3151 | InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); |
6528fe3e | 3152 | |
c9b9ae52 A |
3153 | // First Pass. Look for: |
3154 | // 1. Deregistering records that need to send their goodbye packet | |
3155 | // 2. Updated records that need to retract their old data | |
3156 | // 3. Answers and announcements we need to send | |
3157 | // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can | |
3158 | // send this packet and then try again. | |
3159 | // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway, | |
3160 | // because otherwise we'll end up in an infinite loop trying to send a record that will never fit. | |
377735b0 | 3161 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
c9b9ae52 A |
3162 | if (rr->SendRNow == intf->InterfaceID) |
3163 | { | |
3164 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) | |
3165 | { | |
7f0064bd A |
3166 | newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); |
3167 | if (!newptr && m->omsg.h.numAnswers) break; | |
c9b9ae52 A |
3168 | numDereg++; |
3169 | responseptr = newptr; | |
3170 | } | |
cc340f17 | 3171 | else if (rr->NewRData && !m->SleepState) // If we have new data for this record |
377735b0 | 3172 | { |
c9b9ae52 A |
3173 | RData *OldRData = rr->resrec.rdata; |
3174 | mDNSu16 oldrdlength = rr->resrec.rdlength; | |
8e92c31c | 3175 | // See if we should send a courtesy "goodbye" for the old data before we replace it. |
7f0064bd | 3176 | if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) |
377735b0 | 3177 | { |
7f0064bd A |
3178 | newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); |
3179 | if (!newptr && m->omsg.h.numAnswers) break; | |
c9b9ae52 | 3180 | numDereg++; |
377735b0 | 3181 | responseptr = newptr; |
cc340f17 | 3182 | rr->RequireGoodbye = mDNSfalse; |
377735b0 | 3183 | } |
c9b9ae52 A |
3184 | // Now try to see if we can fit the update in the same packet (not fatal if we can't) |
3185 | SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); | |
7f0064bd | 3186 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) |
716635cc | 3187 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it |
7f0064bd | 3188 | newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); |
716635cc | 3189 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state |
cc340f17 | 3190 | if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; } |
c9b9ae52 A |
3191 | SetNewRData(&rr->resrec, OldRData, oldrdlength); |
3192 | } | |
3193 | else | |
3194 | { | |
7f0064bd | 3195 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) |
716635cc | 3196 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it |
7f0064bd | 3197 | newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); |
c9b9ae52 | 3198 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state |
7f0064bd | 3199 | if (!newptr && m->omsg.h.numAnswers) break; |
28f7d060 | 3200 | rr->RequireGoodbye = (mDNSu8) (!m->SleepState); |
c9b9ae52 A |
3201 | if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; |
3202 | responseptr = newptr; | |
377735b0 | 3203 | } |
c9b9ae52 A |
3204 | // If sending on all interfaces, go to next interface; else we're finished now |
3205 | if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) | |
3206 | rr->SendRNow = GetNextActiveInterfaceID(intf); | |
3207 | else | |
3208 | rr->SendRNow = mDNSNULL; | |
3209 | } | |
377735b0 | 3210 | |
c9b9ae52 A |
3211 | // Second Pass. Add additional records, if there's space. |
3212 | newptr = responseptr; | |
6528fe3e | 3213 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
c9b9ae52 | 3214 | if (rr->ImmedAdditional == intf->InterfaceID) |
7f0064bd | 3215 | if (ResourceRecordIsValidAnswer(rr)) |
6528fe3e | 3216 | { |
7f0064bd A |
3217 | // If we have at least one answer already in the packet, then plan to add additionals too |
3218 | mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0); | |
3219 | ||
3220 | // If we're not planning to send any additionals, but this record is a unique one, then | |
3221 | // make sure we haven't already sent any other members of its RRSet -- if we have, then they | |
3222 | // will have had the cache flush bit set, so now we need to finish the job and send the rest. | |
3223 | if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)) | |
6528fe3e | 3224 | { |
c9b9ae52 A |
3225 | const AuthRecord *a; |
3226 | for (a = m->ResourceRecords; a; a=a->next) | |
7f0064bd A |
3227 | if (a->LastMCTime == m->timenow && |
3228 | a->LastMCInterface == intf->InterfaceID && | |
3229 | SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; } | |
6528fe3e | 3230 | } |
7f0064bd A |
3231 | if (!SendAdditional) // If we don't want to send this after all, |
3232 | rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field | |
3233 | else if (newptr) // Else, try to add it if we can | |
8e92c31c | 3234 | { |
7f0064bd A |
3235 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) |
3236 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it | |
3237 | newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); | |
3238 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state | |
3239 | if (newptr) | |
3240 | { | |
3241 | responseptr = newptr; | |
3242 | rr->ImmedAdditional = mDNSNULL; | |
3243 | rr->RequireGoodbye = mDNStrue; | |
3244 | // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. | |
3245 | // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, | |
3246 | // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get | |
3247 | // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. | |
3248 | rr->LastMCTime = m->timenow; | |
3249 | rr->LastMCInterface = intf->InterfaceID; | |
3250 | } | |
8e92c31c | 3251 | } |
6528fe3e | 3252 | } |
c9b9ae52 | 3253 | |
7f0064bd | 3254 | if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) |
c9b9ae52 A |
3255 | { |
3256 | debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", | |
3257 | numDereg, numDereg == 1 ? "" : "s", | |
3258 | numAnnounce, numAnnounce == 1 ? "" : "s", | |
3259 | numAnswer, numAnswer == 1 ? "" : "s", | |
7f0064bd A |
3260 | m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); |
3261 | if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); | |
3262 | if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); | |
3263 | if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); | |
3264 | if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } | |
c9b9ae52 A |
3265 | // There might be more things to send on this interface, so go around one more time and try again. |
3266 | } | |
3267 | else // Nothing more to send on this interface; go to next | |
3268 | { | |
3269 | const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); | |
3270 | #if MDNS_DEBUGMSGS && 0 | |
3271 | const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; | |
3272 | debugf(msg, intf, next); | |
3273 | #endif | |
3274 | intf = next; | |
3275 | } | |
6528fe3e A |
3276 | } |
3277 | ||
c9b9ae52 A |
3278 | // *** |
3279 | // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables | |
3280 | // *** | |
6528fe3e | 3281 | |
c9b9ae52 A |
3282 | if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set"); |
3283 | m->CurrentRecord = m->ResourceRecords; | |
3284 | while (m->CurrentRecord) | |
3285 | { | |
3286 | rr = m->CurrentRecord; | |
3287 | m->CurrentRecord = rr->next; | |
3288 | ||
8e92c31c | 3289 | if (rr->SendRNow) |
283ee3ff A |
3290 | { |
3291 | if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) | |
3292 | LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr)); | |
3293 | rr->SendRNow = mDNSNULL; | |
3294 | } | |
c9b9ae52 | 3295 | |
7f0064bd | 3296 | if (rr->ImmedAnswer) |
c9b9ae52 | 3297 | { |
7f0064bd A |
3298 | if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client |
3299 | ||
3300 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) | |
3301 | CompleteDeregistration(m, rr); // Don't touch rr after this | |
3302 | else | |
3303 | { | |
3304 | rr->ImmedAnswer = mDNSNULL; | |
3305 | rr->ImmedUnicast = mDNSfalse; | |
3306 | rr->v4Requester = zerov4Addr; | |
3307 | rr->v6Requester = zerov6Addr; | |
3308 | } | |
c9b9ae52 A |
3309 | } |
3310 | } | |
7f0064bd | 3311 | verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow); |
6528fe3e A |
3312 | } |
3313 | ||
c9b9ae52 A |
3314 | // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, |
3315 | // so we want to be lazy about how frequently we do it. | |
3316 | // 1. If a cache record is currently referenced by *no* active questions, | |
3317 | // then we don't mind expiring it up to a minute late (who will know?) | |
3318 | // 2. Else, if a cache record is due for some of its final expiration queries, | |
3319 | // we'll allow them to be late by up to 2% of the TTL | |
3320 | // 3. Else, if a cache record has completed all its final expiration queries without success, | |
3321 | // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late | |
3322 | // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), | |
3323 | // so allow at most 1/10 second lateness | |
3324 | #define CacheCheckGracePeriod(RR) ( \ | |
7f0064bd | 3325 | ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \ |
c9b9ae52 A |
3326 | ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ |
3327 | ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ | |
3328 | ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10)) | |
3329 | ||
3330 | // Note: MUST call SetNextCacheCheckTime any time we change: | |
3331 | // rr->TimeRcvd | |
3332 | // rr->resrec.rroriginalttl | |
3333 | // rr->UnansweredQueries | |
3334 | // rr->CRActiveQuestion | |
cc340f17 A |
3335 | // Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary |
3336 | // Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime | |
c9b9ae52 | 3337 | mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) |
6528fe3e | 3338 | { |
c9b9ae52 | 3339 | rr->NextRequiredQuery = RRExpireTime(rr); |
6528fe3e | 3340 | |
c9b9ae52 A |
3341 | // If we have an active question, then see if we want to schedule a refresher query for this record. |
3342 | // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. | |
3343 | if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) | |
3344 | { | |
3345 | rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); | |
3346 | rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); | |
c9d2d929 A |
3347 | verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec", |
3348 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond); | |
c9b9ae52 A |
3349 | } |
3350 | ||
3351 | if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) | |
3352 | m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); | |
7f0064bd A |
3353 | |
3354 | if (rr->DelayDelivery) | |
3355 | if (m->NextCacheCheck - rr->DelayDelivery > 0) | |
3356 | m->NextCacheCheck = rr->DelayDelivery; | |
c9b9ae52 | 3357 | } |
6528fe3e | 3358 | |
c9d2d929 A |
3359 | #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15) |
3360 | #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5) | |
3361 | #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) | |
6528fe3e | 3362 | |
c9b9ae52 A |
3363 | mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) |
3364 | { | |
3365 | if (interval < kMinimumReconfirmTime) | |
3366 | interval = kMinimumReconfirmTime; | |
3367 | if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below | |
3368 | interval = 0x10000000; | |
3369 | ||
3370 | // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration | |
3371 | if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) | |
6528fe3e | 3372 | { |
c9b9ae52 | 3373 | // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts |
c9d2d929 | 3374 | interval += mDNSRandom(interval/3); |
c9b9ae52 | 3375 | rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; |
c9d2d929 | 3376 | rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond; |
c9b9ae52 | 3377 | SetNextCacheCheckTime(m, rr); |
6528fe3e | 3378 | } |
c9d2d929 | 3379 | debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr)); |
c9b9ae52 | 3380 | return(mStatus_NoError); |
6528fe3e A |
3381 | } |
3382 | ||
c9b9ae52 | 3383 | #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) |
6528fe3e | 3384 | |
c9b9ae52 A |
3385 | // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. |
3386 | // It also appends to the list of known answer records that need to be included, | |
3387 | // and updates the forcast for the size of the known answer section. | |
3388 | mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, | |
3389 | CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) | |
6528fe3e | 3390 | { |
283ee3ff | 3391 | mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; |
c9b9ae52 A |
3392 | mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); |
3393 | const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; | |
3394 | mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); | |
3395 | if (!newptr) | |
3396 | { | |
3397 | debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c); | |
3398 | return(mDNSfalse); | |
3399 | } | |
3400 | else if (newptr + *answerforecast >= limit) | |
3401 | { | |
c9d2d929 | 3402 | verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data); |
c9b9ae52 A |
3403 | query->h.numQuestions--; |
3404 | return(mDNSfalse); | |
3405 | } | |
3406 | else | |
3407 | { | |
3408 | mDNSu32 forecast = *answerforecast; | |
283ee3ff A |
3409 | const mDNSu32 slot = HashSlot(&q->qname); |
3410 | CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); | |
c9b9ae52 A |
3411 | CacheRecord *rr; |
3412 | CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update | |
3413 | ||
283ee3ff | 3414 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, |
c9b9ae52 A |
3415 | if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface |
3416 | rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list | |
3417 | rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet | |
3418 | ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question | |
cc340f17 A |
3419 | rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away |
3420 | mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) | |
c9b9ae52 A |
3421 | { |
3422 | *ka = rr; // Link this record into our known answer chain | |
3423 | ka = &rr->NextInKAList; | |
3424 | // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) | |
3425 | forecast += 12 + rr->resrec.rdestimate; | |
3426 | // If we're trying to put more than one question in this packet, and it doesn't fit | |
3427 | // then undo that last question and try again next time | |
3428 | if (query->h.numQuestions > 1 && newptr + forecast >= limit) | |
3429 | { | |
3430 | debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", | |
3431 | q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); | |
3432 | query->h.numQuestions--; | |
3433 | ka = *kalistptrptr; // Go back to where we started and retract these answer records | |
3434 | while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } | |
3435 | return(mDNSfalse); // Return false, so we'll try again in the next packet | |
3436 | } | |
3437 | } | |
6528fe3e | 3438 | |
c9b9ae52 A |
3439 | // Traffic reduction: |
3440 | // If we already have at least one unique answer in the cache, | |
3441 | // OR we have so many shared answers that the KA list is too big to fit in one packet | |
3442 | // The we suppress queries number 3 and 5: | |
3443 | // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies) | |
3444 | // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally) | |
3445 | // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress) | |
3446 | // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally) | |
3447 | // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress) | |
3448 | // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally) | |
3449 | if (q->UniqueAnswers || newptr + forecast >= limit) | |
3450 | if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32) | |
6528fe3e | 3451 | { |
c9b9ae52 A |
3452 | query->h.numQuestions--; |
3453 | ka = *kalistptrptr; // Go back to where we started and retract these answer records | |
3454 | while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } | |
3455 | return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question | |
6528fe3e | 3456 | } |
6528fe3e | 3457 | |
c9b9ae52 A |
3458 | // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return |
3459 | *queryptr = newptr; // Update the packet pointer | |
3460 | *answerforecast = forecast; // Update the forecast | |
3461 | *kalistptrptr = ka; // Update the known answer list pointer | |
3462 | if (ucast) m->ExpectUnicastResponse = m->timenow; | |
6528fe3e | 3463 | |
283ee3ff | 3464 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache, |
c9b9ae52 A |
3465 | if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface |
3466 | rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list | |
3467 | ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question | |
3468 | { | |
3469 | rr->UnansweredQueries++; // indicate that we're expecting a response | |
3470 | rr->LastUnansweredTime = m->timenow; | |
3471 | SetNextCacheCheckTime(m, rr); | |
3472 | } | |
3473 | ||
3474 | return(mDNStrue); | |
3475 | } | |
6528fe3e A |
3476 | } |
3477 | ||
c9b9ae52 | 3478 | mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q) |
6528fe3e | 3479 | { |
c9b9ae52 | 3480 | mDNSu32 slot; |
283ee3ff | 3481 | CacheGroup *cg; |
c9b9ae52 A |
3482 | CacheRecord *rr; |
3483 | domainname *target; | |
283ee3ff | 3484 | FORALL_CACHERECORDS(slot, cg, rr) |
7cb34e5c | 3485 | if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname)) |
283ee3ff | 3486 | mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); |
c9b9ae52 A |
3487 | } |
3488 | ||
3489 | // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active | |
3490 | mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time) | |
3491 | { | |
3492 | int i; | |
3493 | for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL; | |
3494 | } | |
3495 | ||
3496 | mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID) | |
3497 | { | |
3498 | int i; | |
3499 | for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL; | |
3500 | } | |
3501 | ||
3502 | mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf) | |
3503 | { | |
3504 | int i; | |
3505 | mDNSBool v4 = !intf->IPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query | |
3506 | mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query | |
3507 | for (i=0; i<DupSuppressInfoSize; i++) | |
3508 | if (ds[i].InterfaceID == intf->InterfaceID) | |
6528fe3e | 3509 | { |
c9b9ae52 A |
3510 | if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue; |
3511 | else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue; | |
3512 | if (v4 && v6) return(mDNStrue); | |
6528fe3e | 3513 | } |
c9b9ae52 A |
3514 | return(mDNSfalse); |
3515 | } | |
3516 | ||
3517 | mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) | |
3518 | { | |
3519 | int i, j; | |
3520 | ||
3521 | // See if we have this one in our list somewhere already | |
3522 | for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break; | |
3523 | ||
3524 | // If not, find a slot we can re-use | |
3525 | if (i >= DupSuppressInfoSize) | |
3526 | { | |
3527 | i = 0; | |
3528 | for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++) | |
3529 | if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0) | |
3530 | i = j; | |
6528fe3e | 3531 | } |
c9b9ae52 A |
3532 | |
3533 | // Record the info about this query we saw | |
3534 | ds[i].Time = Time; | |
3535 | ds[i].InterfaceID = InterfaceID; | |
3536 | ds[i].Type = Type; | |
3537 | ||
3538 | return(i); | |
6528fe3e A |
3539 | } |
3540 | ||
c9b9ae52 | 3541 | mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) |
6528fe3e | 3542 | { |
c9b9ae52 A |
3543 | // If more than 90% of the way to the query time, we should unconditionally accelerate it |
3544 | if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) | |
3545 | return(mDNStrue); | |
3546 | ||
3547 | // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet | |
3548 | if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) | |
6528fe3e | 3549 | { |
c9b9ae52 | 3550 | // We forecast: qname (n) type (2) class (2) |
73792575 | 3551 | mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; |
283ee3ff A |
3552 | const mDNSu32 slot = HashSlot(&q->qname); |
3553 | CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); | |
c9b9ae52 | 3554 | CacheRecord *rr; |
283ee3ff | 3555 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, |
c9b9ae52 A |
3556 | if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet |
3557 | ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question | |
3558 | rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry | |
3559 | rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery | |
6528fe3e | 3560 | { |
c9b9ae52 A |
3561 | // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) |
3562 | forecast += 12 + rr->resrec.rdestimate; | |
3563 | if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate | |
6528fe3e | 3564 | } |
c9b9ae52 | 3565 | return(mDNStrue); |
6528fe3e | 3566 | } |
c9b9ae52 A |
3567 | |
3568 | return(mDNSfalse); | |
6528fe3e A |
3569 | } |
3570 | ||
3571 | // How Standard Queries are generated: | |
3572 | // 1. The Question Section contains the question | |
c9b9ae52 | 3573 | // 2. The Additional Section contains answers we already know, to suppress duplicate responses |
6528fe3e A |
3574 | |
3575 | // How Probe Queries are generated: | |
3576 | // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because | |
3577 | // if some other host is already using *any* records with this name, we want to know about it. | |
3578 | // 2. The Authority Section contains the proposed values we intend to use for one or more | |
3579 | // of our records with that name (analogous to the Update section of DNS Update packets) | |
3580 | // because if some other host is probing at the same time, we each want to know what the other is | |
3581 | // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't. | |
3582 | ||
c9b9ae52 | 3583 | mDNSlocal void SendQueries(mDNS *const m) |
6528fe3e | 3584 | { |
c9b9ae52 | 3585 | int pktcount = 0; |
6528fe3e | 3586 | DNSQuestion *q; |
c9b9ae52 A |
3587 | // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval |
3588 | mDNSs32 maxExistingQuestionInterval = 0; | |
3589 | const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); | |
3590 | CacheRecord *KnownAnswerList = mDNSNULL; | |
3591 | ||
3592 | // 1. If time for a query, work out what we need to do | |
3593 | if (m->timenow - m->NextScheduledQuery >= 0) | |
3594 | { | |
c9d2d929 A |
3595 | mDNSu32 slot; |
3596 | CacheGroup *cg; | |
c9b9ae52 A |
3597 | CacheRecord *rr; |
3598 | m->NextScheduledQuery = m->timenow + 0x78000000; | |
3599 | ||
3600 | // We're expecting to send a query anyway, so see if any expiring cache records are close enough | |
3601 | // to their NextRequiredQuery to be worth batching them together with this one | |
283ee3ff A |
3602 | FORALL_CACHERECORDS(slot, cg, rr) |
3603 | if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) | |
3604 | if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0) | |
3605 | { | |
3606 | q = rr->CRActiveQuestion; | |
3607 | ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID); | |
3608 | if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If unicast query, mark it | |
3609 | else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID; | |
3610 | else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; | |
3611 | } | |
c9b9ae52 | 3612 | |
283ee3ff | 3613 | // Scan our list of questions to see which *unicast* queries need to be sent |
8e92c31c A |
3614 | for (q = m->Questions; q; q=q->next) |
3615 | if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) | |
3616 | { | |
7f0064bd A |
3617 | mDNSu8 *qptr = m->omsg.data; |
3618 | const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); | |
3619 | InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); | |
3620 | qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); | |
3621 | mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL); | |
3622 | q->ThisQInterval *= 2; | |
283ee3ff A |
3623 | if (q->ThisQInterval > MaxQuestionInterval) |
3624 | q->ThisQInterval = MaxQuestionInterval; | |
7f0064bd A |
3625 | q->LastQTime = m->timenow; |
3626 | q->LastQTxTime = m->timenow; | |
3627 | q->RecentAnswerPkts = 0; | |
3628 | q->SendQNow = mDNSNULL; | |
8e92c31c A |
3629 | m->ExpectUnicastResponse = m->timenow; |
3630 | } | |
3631 | ||
283ee3ff | 3632 | // Scan our list of questions to see which *multicast* queries we're definitely going to send |
c9b9ae52 | 3633 | for (q = m->Questions; q; q=q->next) |
8e92c31c | 3634 | if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow)) |
c9b9ae52 A |
3635 | { |
3636 | q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces | |
3637 | if (maxExistingQuestionInterval < q->ThisQInterval) | |
3638 | maxExistingQuestionInterval = q->ThisQInterval; | |
3639 | } | |
6528fe3e | 3640 | |
c9b9ae52 A |
3641 | // Scan our list of questions |
3642 | // (a) to see if there are any more that are worth accelerating, and | |
283ee3ff | 3643 | // (b) to update the state variables for *all* the questions we're going to send |
c9b9ae52 A |
3644 | for (q = m->Questions; q; q=q->next) |
3645 | { | |
8e92c31c A |
3646 | if (q->SendQNow || |
3647 | (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))) | |
c9b9ae52 A |
3648 | { |
3649 | // If at least halfway to next query time, advance to next interval | |
c9d2d929 | 3650 | // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval |
c9b9ae52 A |
3651 | if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0) |
3652 | { | |
3653 | q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces | |
3654 | q->ThisQInterval *= 2; | |
3655 | if (q->ThisQInterval > MaxQuestionInterval) | |
3656 | q->ThisQInterval = MaxQuestionInterval; | |
3657 | else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8) | |
3658 | { | |
c9d2d929 A |
3659 | debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); |
3660 | ReconfirmAntecedents(m, q); // If sending third query, and no answers yet, time to begin doubting the source | |
c9b9ae52 A |
3661 | } |
3662 | } | |
6528fe3e | 3663 | |
c9b9ae52 A |
3664 | // Mark for sending. (If no active interfaces, then don't even try.) |
3665 | q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); | |
3666 | if (q->SendOnAll) | |
3667 | { | |
3668 | q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; | |
3669 | q->LastQTime = m->timenow; | |
3670 | } | |
6528fe3e | 3671 | |
c9b9ae52 A |
3672 | // If we recorded a duplicate suppression for this question less than half an interval ago, |
3673 | // then we consider it recent enough that we don't need to do an identical query ourselves. | |
3674 | ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); | |
6528fe3e | 3675 | |
7f0064bd A |
3676 | q->LastQTxTime = m->timenow; |
3677 | q->RecentAnswerPkts = 0; | |
283ee3ff | 3678 | if (q->RequestUnicast) q->RequestUnicast--; |
c9b9ae52 A |
3679 | } |
3680 | // For all questions (not just the ones we're sending) check what the next scheduled event will be | |
3681 | SetNextQueryTime(m,q); | |
3682 | } | |
3683 | } | |
3684 | ||
3685 | // 2. Scan our authoritative RR list to see what probes we might need to send | |
3686 | if (m->timenow - m->NextScheduledProbe >= 0) | |
6528fe3e | 3687 | { |
c9b9ae52 A |
3688 | m->NextScheduledProbe = m->timenow + 0x78000000; |
3689 | ||
3690 | if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set"); | |
3691 | m->CurrentRecord = m->ResourceRecords; | |
3692 | while (m->CurrentRecord) | |
6528fe3e | 3693 | { |
c9b9ae52 A |
3694 | AuthRecord *rr = m->CurrentRecord; |
3695 | m->CurrentRecord = rr->next; | |
3696 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... | |
3697 | { | |
3698 | // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly | |
3699 | if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) | |
3700 | { | |
3701 | SetNextAnnounceProbeTime(m, rr); | |
3702 | } | |
3703 | // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly | |
3704 | else if (rr->ProbeCount) | |
3705 | { | |
3706 | // Mark for sending. (If no active interfaces, then don't even try.) | |
3707 | rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; | |
3708 | rr->LastAPTime = m->timenow; | |
3709 | rr->ProbeCount--; | |
3710 | SetNextAnnounceProbeTime(m, rr); | |
3711 | } | |
c9d2d929 | 3712 | // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced |
c9b9ae52 A |
3713 | else |
3714 | { | |
3715 | AuthRecord *r2; | |
3716 | rr->resrec.RecordType = kDNSRecordTypeVerified; | |
3717 | rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; | |
3718 | rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; | |
3719 | SetNextAnnounceProbeTime(m, rr); | |
3720 | // If we have any records on our duplicate list that match this one, they have now also completed probing | |
3721 | for (r2 = m->DuplicateRecords; r2; r2=r2->next) | |
3722 | if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) | |
3723 | r2->ProbeCount = 0; | |
7f0064bd | 3724 | AcknowledgeRecord(m, rr); |
c9b9ae52 A |
3725 | } |
3726 | } | |
6528fe3e | 3727 | } |
c9b9ae52 A |
3728 | m->CurrentRecord = m->DuplicateRecords; |
3729 | while (m->CurrentRecord) | |
6528fe3e | 3730 | { |
c9b9ae52 A |
3731 | AuthRecord *rr = m->CurrentRecord; |
3732 | m->CurrentRecord = rr->next; | |
3733 | if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0) | |
7f0064bd | 3734 | AcknowledgeRecord(m, rr); |
6528fe3e A |
3735 | } |
3736 | } | |
6528fe3e | 3737 | |
c9d2d929 | 3738 | // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface |
c9b9ae52 | 3739 | while (intf) |
6528fe3e | 3740 | { |
c9b9ae52 | 3741 | AuthRecord *rr; |
7f0064bd A |
3742 | mDNSu8 *queryptr = m->omsg.data; |
3743 | InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); | |
c9b9ae52 A |
3744 | if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); |
3745 | if (!KnownAnswerList) | |
3746 | { | |
3747 | // Start a new known-answer list | |
3748 | CacheRecord **kalistptr = &KnownAnswerList; | |
3749 | mDNSu32 answerforecast = 0; | |
3750 | ||
3751 | // Put query questions in this packet | |
3752 | for (q = m->Questions; q; q=q->next) | |
3753 | if (q->SendQNow == intf->InterfaceID) | |
3754 | { | |
7f0064bd | 3755 | debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d", |
c9b9ae52 | 3756 | SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", |
7f0064bd | 3757 | q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data); |
c9b9ae52 A |
3758 | // If we're suppressing this question, or we successfully put it, update its SendQNow state |
3759 | if (SuppressOnThisInterface(q->DupSuppress, intf) || | |
7f0064bd | 3760 | BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) |
c9b9ae52 A |
3761 | q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); |
3762 | } | |
6528fe3e | 3763 | |
c9b9ae52 A |
3764 | // Put probe questions in this packet |
3765 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3766 | if (rr->SendRNow == intf->InterfaceID) | |
3767 | { | |
7f0064bd | 3768 | mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; |
c9b9ae52 | 3769 | mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); |
7f0064bd | 3770 | const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); |
283ee3ff | 3771 | mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); |
c9b9ae52 A |
3772 | // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) |
3773 | mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; | |
3774 | if (newptr && newptr + forecast < limit) | |
3775 | { | |
3776 | queryptr = newptr; | |
3777 | answerforecast = forecast; | |
3778 | rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); | |
3779 | rr->IncludeInProbe = mDNStrue; | |
c9d2d929 | 3780 | verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount); |
c9b9ae52 A |
3781 | } |
3782 | else | |
3783 | { | |
c9d2d929 | 3784 | verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd | 3785 | m->omsg.h.numQuestions--; |
c9b9ae52 A |
3786 | } |
3787 | } | |
3788 | } | |
3789 | ||
3790 | // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) | |
3791 | while (KnownAnswerList) | |
6528fe3e | 3792 | { |
c9b9ae52 A |
3793 | CacheRecord *rr = KnownAnswerList; |
3794 | mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; | |
7f0064bd | 3795 | mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); |
6528fe3e | 3796 | if (newptr) |
c9b9ae52 | 3797 | { |
c9d2d929 | 3798 | verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); |
6528fe3e | 3799 | queryptr = newptr; |
c9b9ae52 A |
3800 | KnownAnswerList = rr->NextInKAList; |
3801 | rr->NextInKAList = mDNSNULL; | |
3802 | } | |
6528fe3e A |
3803 | else |
3804 | { | |
c9b9ae52 A |
3805 | // If we ran out of space and we have more than one question in the packet, that's an error -- |
3806 | // we shouldn't have put more than one question if there was a risk of us running out of space. | |
7f0064bd A |
3807 | if (m->omsg.h.numQuestions > 1) |
3808 | LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers); | |
3809 | m->omsg.h.flags.b[0] |= kDNSFlag0_TC; | |
6528fe3e A |
3810 | break; |
3811 | } | |
3812 | } | |
6528fe3e | 3813 | |
c9b9ae52 A |
3814 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
3815 | if (rr->IncludeInProbe) | |
3816 | { | |
7f0064bd | 3817 | mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec); |
c9b9ae52 A |
3818 | rr->IncludeInProbe = mDNSfalse; |
3819 | if (newptr) queryptr = newptr; | |
3820 | else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?", | |
283ee3ff | 3821 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
3822 | } |
3823 | ||
7f0064bd | 3824 | if (queryptr > m->omsg.data) |
6528fe3e | 3825 | { |
7f0064bd A |
3826 | if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) |
3827 | LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); | |
c9b9ae52 | 3828 | debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", |
7f0064bd A |
3829 | m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", |
3830 | m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", | |
3831 | m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); | |
3832 | if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); | |
3833 | if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); | |
7cb34e5c | 3834 | if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); |
c9b9ae52 A |
3835 | if (++pktcount >= 1000) |
3836 | { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } | |
3837 | // There might be more records left in the known answer list, or more questions to send | |
3838 | // on this interface, so go around one more time and try again. | |
6528fe3e | 3839 | } |
c9b9ae52 | 3840 | else // Nothing more to send on this interface; go to next |
6528fe3e | 3841 | { |
c9b9ae52 A |
3842 | const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); |
3843 | #if MDNS_DEBUGMSGS && 0 | |
3844 | const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; | |
3845 | debugf(msg, intf, next); | |
3846 | #endif | |
3847 | intf = next; | |
6528fe3e | 3848 | } |
c9b9ae52 | 3849 | } |
8e92c31c | 3850 | |
c9d2d929 A |
3851 | // Final sanity check for debugging purposes |
3852 | { | |
3853 | AuthRecord *rr; | |
3854 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3855 | if (rr->SendRNow) | |
3856 | { | |
3857 | if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) | |
3858 | LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, rr)); | |
3859 | rr->SendRNow = mDNSNULL; | |
3860 | } | |
3861 | } | |
6528fe3e A |
3862 | } |
3863 | ||
3864 | // *************************************************************************** | |
c9b9ae52 | 3865 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
3866 | #pragma mark - |
3867 | #pragma mark - RR List Management & Task Management | |
3868 | #endif | |
3869 | ||
6528fe3e A |
3870 | // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. |
3871 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 3872 | mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord) |
6528fe3e | 3873 | { |
c9b9ae52 | 3874 | verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)", |
283ee3ff | 3875 | q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e | 3876 | |
7f0064bd A |
3877 | // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue) |
3878 | // may be called twice, once when the record is received, and again when it's time to notify local clients. | |
3879 | // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. | |
3880 | ||
c9b9ae52 | 3881 | rr->LastUsed = m->timenow; |
c9b9ae52 | 3882 | if (ActiveQuestion(q) && rr->CRActiveQuestion != q) |
6528fe3e | 3883 | { |
c9b9ae52 A |
3884 | if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count |
3885 | rr->CRActiveQuestion = q; // We know q is non-null | |
3886 | SetNextCacheCheckTime(m, rr); | |
6528fe3e | 3887 | } |
6528fe3e | 3888 | |
283ee3ff A |
3889 | // If this is: |
3890 | // (a) a no-cache add, where we've already done at least one 'QM' query, or | |
3891 | // (b) a normal add, where we have at least one unique-type answer, | |
3892 | // then there's no need to keep polling the network. | |
3893 | // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.) | |
3894 | if ((AddRecord == 2 && !q->RequestUnicast) || | |
3895 | (AddRecord == 1 && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)))) | |
3896 | if (ActiveQuestion(q)) | |
3897 | { | |
3898 | q->LastQTime = m->timenow; | |
3899 | q->LastQTxTime = m->timenow; | |
7cb34e5c | 3900 | q->RecentAnswerPkts = 0; |
283ee3ff A |
3901 | q->ThisQInterval = MaxQuestionInterval; |
3902 | q->RequestUnicast = mDNSfalse; | |
3903 | } | |
3904 | ||
7f0064bd A |
3905 | if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us |
3906 | ||
c9b9ae52 A |
3907 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback |
3908 | if (q->QuestionCallback) | |
3909 | q->QuestionCallback(m, q, &rr->resrec, AddRecord); | |
3910 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
7f0064bd A |
3911 | // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function |
3912 | // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. | |
3913 | // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() | |
3914 | // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions | |
3915 | // being deleted out from under them. | |
3916 | } | |
3917 | ||
3918 | mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) | |
3919 | { | |
cc340f17 | 3920 | rr->DelayDelivery = 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared |
7f0064bd A |
3921 | if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set"); |
3922 | m->CurrentQuestion = m->Questions; | |
3923 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) | |
3924 | { | |
3925 | DNSQuestion *q = m->CurrentQuestion; | |
3926 | m->CurrentQuestion = q->next; | |
3927 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
3928 | AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
3929 | } | |
3930 | m->CurrentQuestion = mDNSNULL; | |
3931 | } | |
3932 | ||
3933 | mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot) | |
3934 | { | |
3935 | const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second | |
3936 | const mDNSs32 start = m->timenow - 0x10000000; | |
3937 | mDNSs32 delay = start; | |
283ee3ff | 3938 | CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); |
7f0064bd | 3939 | CacheRecord *rr; |
283ee3ff A |
3940 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
3941 | if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name)) | |
7f0064bd A |
3942 | if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second |
3943 | if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted | |
3944 | delay = RRExpireTime(rr); | |
cc340f17 | 3945 | if (delay - start > 0) return(NonZeroTime(delay)); |
7f0064bd | 3946 | else return(0); |
6528fe3e A |
3947 | } |
3948 | ||
c9b9ae52 | 3949 | // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. |
6528fe3e A |
3950 | // If new questions are created as a result of invoking client callbacks, they will be added to |
3951 | // the end of the question list, and m->NewQuestions will be set to indicate the first new question. | |
c9b9ae52 A |
3952 | // rr is a new CacheRecord just received into our cache |
3953 | // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). | |
3954 | // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback, | |
3955 | // which may change the record list and/or question list. | |
6528fe3e | 3956 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. |
c9b9ae52 | 3957 | mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) |
6528fe3e | 3958 | { |
c9b9ae52 A |
3959 | if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set"); |
3960 | m->CurrentQuestion = m->Questions; | |
6528fe3e A |
3961 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) |
3962 | { | |
3963 | DNSQuestion *q = m->CurrentQuestion; | |
3964 | m->CurrentQuestion = q->next; | |
c9b9ae52 A |
3965 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) |
3966 | { | |
c9d2d929 A |
3967 | // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last |
3968 | // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start. | |
3969 | // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less, | |
3970 | // there's not much benefit accelerating because we will anyway send another query within a few seconds. | |
3971 | // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines. | |
7f0064bd | 3972 | if (q->LastAnswerPktNum != m->PktNum) |
c9b9ae52 | 3973 | { |
7f0064bd A |
3974 | q->LastAnswerPktNum = m->PktNum; |
3975 | if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && | |
283ee3ff | 3976 | q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) |
7f0064bd A |
3977 | { |
3978 | LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", | |
3979 | q->qname.c, DNSTypeName(q->qtype)); | |
283ee3ff A |
3980 | q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); |
3981 | q->ThisQInterval = InitialQuestionInterval; | |
7f0064bd A |
3982 | SetNextQueryTime(m,q); |
3983 | } | |
c9b9ae52 | 3984 | } |
c9d2d929 | 3985 | verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); |
c9b9ae52 A |
3986 | q->CurrentAnswers++; |
3987 | if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; | |
3988 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; | |
7f0064bd A |
3989 | if (q->CurrentAnswers > 4000) |
3990 | { | |
3991 | static int msgcount = 0; | |
3992 | if (msgcount++ < 10) | |
3993 | LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", | |
3994 | q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); | |
3995 | rr->resrec.rroriginalttl = 1; | |
3996 | rr->UnansweredQueries = MaxUnansweredQueries; | |
3997 | } | |
c9b9ae52 A |
3998 | AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); |
3999 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
4000 | } | |
6528fe3e A |
4001 | } |
4002 | m->CurrentQuestion = mDNSNULL; | |
cc340f17 | 4003 | SetNextCacheCheckTime(m, rr); |
6528fe3e A |
4004 | } |
4005 | ||
283ee3ff A |
4006 | // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. |
4007 | // If new questions are created as a result of invoking client callbacks, they will be added to | |
4008 | // the end of the question list, and m->NewQuestions will be set to indicate the first new question. | |
4009 | // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique) | |
4010 | // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any | |
4011 | // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists, | |
4012 | // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network. | |
4013 | // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback, | |
4014 | // which may change the record list and/or question list. | |
4015 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
4016 | mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) | |
4017 | { | |
4018 | LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); | |
cc340f17 | 4019 | if (m->CurrentQuestion) LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set"); |
283ee3ff A |
4020 | m->CurrentQuestion = m->Questions; |
4021 | while (m->CurrentQuestion) | |
4022 | { | |
4023 | DNSQuestion *q = m->CurrentQuestion; | |
4024 | m->CurrentQuestion = q->next; | |
4025 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4026 | AnswerQuestionWithResourceRecord(m, q, rr, 2); // Value '2' indicates "don't expect 'remove' events for this" | |
4027 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
4028 | } | |
4029 | m->CurrentQuestion = mDNSNULL; | |
4030 | } | |
4031 | ||
c9b9ae52 A |
4032 | // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute |
4033 | // If new questions are created as a result of invoking client callbacks, they will be added to | |
4034 | // the end of the question list, and m->NewQuestions will be set to indicate the first new question. | |
4035 | // rr is an existing cache CacheRecord that just expired and is being deleted | |
4036 | // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). | |
4037 | // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback, | |
4038 | // which may change the record list and/or question list. | |
4039 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
4040 | mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) | |
6528fe3e | 4041 | { |
c9b9ae52 A |
4042 | if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set"); |
4043 | m->CurrentQuestion = m->Questions; | |
4044 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) | |
4045 | { | |
4046 | DNSQuestion *q = m->CurrentQuestion; | |
4047 | m->CurrentQuestion = q->next; | |
4048 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4e28aad3 | 4049 | { |
cc340f17 | 4050 | verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); |
c9b9ae52 | 4051 | if (q->CurrentAnswers == 0) |
c9d2d929 | 4052 | LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype)); |
c9b9ae52 A |
4053 | else |
4054 | { | |
4055 | q->CurrentAnswers--; | |
4056 | if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; | |
4057 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; | |
4058 | } | |
4059 | if (q->CurrentAnswers == 0) | |
4060 | { | |
c9d2d929 | 4061 | debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); |
c9b9ae52 A |
4062 | ReconfirmAntecedents(m, q); |
4063 | } | |
4064 | AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse); | |
4065 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
4e28aad3 | 4066 | } |
c9b9ae52 | 4067 | } |
6528fe3e | 4068 | m->CurrentQuestion = mDNSNULL; |
6528fe3e A |
4069 | } |
4070 | ||
283ee3ff | 4071 | mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e) |
6528fe3e | 4072 | { |
283ee3ff A |
4073 | #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 |
4074 | unsigned int i; | |
4075 | for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; | |
4076 | #endif | |
4077 | e->next = m->rrcache_free; | |
4078 | m->rrcache_free = e; | |
c9b9ae52 | 4079 | m->rrcache_totalused--; |
6528fe3e A |
4080 | } |
4081 | ||
283ee3ff A |
4082 | mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) |
4083 | { | |
4084 | CacheEntity *e = (CacheEntity *)(*cp); | |
4085 | //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c); | |
c9d2d929 A |
4086 | if ((*cp)->rrcache_tail != &(*cp)->members) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); |
4087 | //if ((*cp)->name != (domainname*)((*cp)->namestorage)) LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); | |
283ee3ff A |
4088 | if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); |
4089 | (*cp)->name = mDNSNULL; | |
4090 | *cp = (*cp)->next; // Cut record from list | |
4091 | ReleaseCacheEntity(m, e); | |
4092 | } | |
4093 | ||
4094 | mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) | |
4095 | { | |
4096 | if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) mDNSPlatformMemFree(r->resrec.rdata); | |
4097 | r->resrec.rdata = mDNSNULL; | |
4098 | ReleaseCacheEntity(m, (CacheEntity *)r); | |
4099 | } | |
4100 | ||
c9d2d929 A |
4101 | // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls |
4102 | // The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records | |
283ee3ff | 4103 | mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg) |
6528fe3e | 4104 | { |
283ee3ff | 4105 | CacheRecord **rp = &cg->members; |
c9b9ae52 A |
4106 | |
4107 | if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } | |
6528fe3e | 4108 | m->lock_rrcache = 1; |
c9b9ae52 A |
4109 | |
4110 | while (*rp) | |
6528fe3e | 4111 | { |
c9b9ae52 A |
4112 | CacheRecord *const rr = *rp; |
4113 | mDNSs32 event = RRExpireTime(rr); | |
4114 | if (m->timenow - event >= 0) // If expired, delete it | |
4115 | { | |
4116 | *rp = rr->next; // Cut it from the list | |
7f0064bd | 4117 | verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr)); |
c9b9ae52 A |
4118 | if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away |
4119 | { | |
4120 | CacheRecordRmv(m, rr); | |
4121 | m->rrcache_active--; | |
4122 | } | |
283ee3ff | 4123 | ReleaseCacheRecord(m, rr); |
c9b9ae52 A |
4124 | } |
4125 | else // else, not expired; see if we need to query | |
6528fe3e | 4126 | { |
7f0064bd A |
4127 | if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) |
4128 | event = rr->DelayDelivery; | |
4129 | else | |
c9b9ae52 | 4130 | { |
7f0064bd A |
4131 | if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); |
4132 | if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) | |
c9b9ae52 | 4133 | { |
7f0064bd A |
4134 | if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query |
4135 | event = rr->NextRequiredQuery; // then just record when we want the next query | |
4136 | else // else trigger our question to go out now | |
4137 | { | |
4138 | // Set NextScheduledQuery to timenow so that SendQueries() will run. | |
4139 | // SendQueries() will see that we have records close to expiration, and send FEQs for them. | |
4140 | m->NextScheduledQuery = m->timenow; | |
4141 | // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), | |
c9d2d929 | 4142 | // which will correctly update m->NextCacheCheck for us |
7f0064bd A |
4143 | event = m->timenow + 0x3FFFFFFF; |
4144 | } | |
c9b9ae52 A |
4145 | } |
4146 | } | |
4147 | if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) | |
4148 | m->NextCacheCheck = (event + CacheCheckGracePeriod(rr)); | |
4149 | rp = &rr->next; | |
6528fe3e A |
4150 | } |
4151 | } | |
283ee3ff A |
4152 | if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp); |
4153 | cg->rrcache_tail = rp; | |
6528fe3e | 4154 | m->lock_rrcache = 0; |
6528fe3e A |
4155 | } |
4156 | ||
c9b9ae52 | 4157 | mDNSlocal void AnswerNewQuestion(mDNS *const m) |
6528fe3e | 4158 | { |
c9b9ae52 A |
4159 | mDNSBool ShouldQueryImmediately = mDNStrue; |
4160 | CacheRecord *rr; | |
4161 | DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer | |
7f0064bd | 4162 | const mDNSu32 slot = HashSlot(&q->qname); |
283ee3ff | 4163 | CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); |
c9b9ae52 A |
4164 | |
4165 | verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); | |
6528fe3e | 4166 | |
283ee3ff | 4167 | if (cg) CheckCacheExpiration(m, cg); |
c9b9ae52 A |
4168 | m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration(); |
4169 | ||
4170 | if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); | |
4171 | // This should be safe, because calling the client's question callback may cause the | |
4172 | // question list to be modified, but should not ever cause the rrcache list to be modified. | |
8e92c31c | 4173 | // If the client's question callback deletes the question, then m->CurrentQuestion will |
c9b9ae52 | 4174 | // be advanced, and we'll exit out of the loop |
6528fe3e | 4175 | m->lock_rrcache = 1; |
c9b9ae52 A |
4176 | if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); |
4177 | m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted | |
283ee3ff A |
4178 | |
4179 | if (q->InterfaceID == mDNSInterface_Any) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records | |
4180 | { | |
4181 | if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); | |
4182 | m->CurrentRecord = m->ResourceRecords; | |
4183 | while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) | |
4184 | { | |
4185 | AuthRecord *rr = m->CurrentRecord; | |
4186 | m->CurrentRecord = rr->next; | |
4187 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) | |
4188 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4189 | { | |
4190 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
4191 | // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() | |
4192 | if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here | |
4193 | } | |
4194 | } | |
4195 | m->CurrentRecord = mDNSNULL; | |
4196 | } | |
4197 | ||
4198 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) | |
c9b9ae52 | 4199 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) |
6528fe3e | 4200 | { |
c9b9ae52 A |
4201 | // SecsSinceRcvd is whole number of elapsed seconds, rounded down |
4202 | mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; | |
4203 | if (rr->resrec.rroriginalttl <= SecsSinceRcvd) | |
4204 | { | |
4205 | LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)", | |
283ee3ff | 4206 | rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
4207 | continue; // Go to next one in loop |
4208 | } | |
4209 | ||
4210 | // If this record set is marked unique, then that means we can reasonably assume we have the whole set | |
4211 | // -- we don't need to rush out on the network and query immediately to see if there are more answers out there | |
7f0064bd A |
4212 | if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) |
4213 | ShouldQueryImmediately = mDNSfalse; | |
c9b9ae52 A |
4214 | q->CurrentAnswers++; |
4215 | if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; | |
4216 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; | |
4217 | AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
4218 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
4219 | if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here | |
6528fe3e | 4220 | } |
716635cc | 4221 | else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) |
283ee3ff | 4222 | if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname)) |
716635cc | 4223 | ShouldQueryImmediately = mDNSfalse; |
c9b9ae52 A |
4224 | |
4225 | if (ShouldQueryImmediately && m->CurrentQuestion == q) | |
6528fe3e | 4226 | { |
283ee3ff A |
4227 | q->ThisQInterval = InitialQuestionInterval; |
4228 | q->LastQTime = m->timenow - q->ThisQInterval; | |
c9b9ae52 A |
4229 | m->NextScheduledQuery = m->timenow; |
4230 | } | |
4231 | m->CurrentQuestion = mDNSNULL; | |
4232 | m->lock_rrcache = 0; | |
4233 | } | |
6528fe3e | 4234 | |
c9d2d929 A |
4235 | // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any appropriate answers, |
4236 | // stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions | |
c9b9ae52 A |
4237 | mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) |
4238 | { | |
4239 | DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer | |
4240 | m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions to the next (if any) | |
6528fe3e | 4241 | |
c9b9ae52 A |
4242 | debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); |
4243 | ||
7f0064bd | 4244 | if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set"); |
c9b9ae52 | 4245 | m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted |
6528fe3e | 4246 | |
7f0064bd | 4247 | if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); |
283ee3ff A |
4248 | m->CurrentRecord = m->ResourceRecords; |
4249 | while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) | |
c9b9ae52 A |
4250 | { |
4251 | AuthRecord *rr = m->CurrentRecord; | |
4252 | m->CurrentRecord = rr->next; | |
4253 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4254 | { | |
4255 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
4256 | // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() | |
4257 | if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here | |
6528fe3e | 4258 | } |
c9b9ae52 A |
4259 | } |
4260 | ||
4261 | m->CurrentQuestion = mDNSNULL; | |
7f0064bd | 4262 | m->CurrentRecord = mDNSNULL; |
c9b9ae52 | 4263 | } |
6528fe3e | 4264 | |
283ee3ff | 4265 | mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG) |
6528fe3e | 4266 | { |
283ee3ff | 4267 | CacheEntity *e = mDNSNULL; |
6528fe3e | 4268 | |
c9b9ae52 A |
4269 | if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } |
4270 | m->lock_rrcache = 1; | |
6528fe3e | 4271 | |
c9b9ae52 A |
4272 | // If we have no free records, ask the client layer to give us some more memory |
4273 | if (!m->rrcache_free && m->MainCallback) | |
6528fe3e | 4274 | { |
c9b9ae52 A |
4275 | if (m->rrcache_totalused != m->rrcache_size) |
4276 | LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", | |
4277 | m->rrcache_totalused, m->rrcache_size); | |
4278 | ||
4279 | // We don't want to be vulnerable to a malicious attacker flooding us with an infinite | |
4280 | // number of bogus records so that we keep growing our cache until the machine runs out of memory. | |
4281 | // To guard against this, if we're actively using less than 1/32 of our cache, then we | |
4282 | // purge all the unused records and recycle them, instead of allocating more memory. | |
4283 | if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active) | |
4284 | debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", | |
4285 | m->rrcache_size, m->rrcache_active); | |
4286 | else | |
8e92c31c A |
4287 | { |
4288 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 4289 | m->MainCallback(m, mStatus_GrowCache); |
8e92c31c A |
4290 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
4291 | } | |
6528fe3e A |
4292 | } |
4293 | ||
c9b9ae52 A |
4294 | // If we still have no free records, recycle all the records we can. |
4295 | // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. | |
4296 | if (!m->rrcache_free) | |
6528fe3e | 4297 | { |
c9b9ae52 A |
4298 | #if MDNS_DEBUGMSGS |
4299 | mDNSu32 oldtotalused = m->rrcache_totalused; | |
4300 | #endif | |
4301 | mDNSu32 slot; | |
c9b9ae52 | 4302 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) |
6528fe3e | 4303 | { |
283ee3ff A |
4304 | CacheGroup **cp = &m->rrcache_hash[slot]; |
4305 | while (*cp) | |
6528fe3e | 4306 | { |
283ee3ff A |
4307 | CacheRecord **rp = &(*cp)->members; |
4308 | while (*rp) | |
6528fe3e | 4309 | { |
283ee3ff A |
4310 | // Records that answer still-active questions are not candidates for recycling |
4311 | // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash | |
4312 | if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList) | |
4313 | rp=&(*rp)->next; | |
4314 | else | |
4315 | { | |
4316 | CacheRecord *rr = *rp; | |
4317 | *rp = (*rp)->next; // Cut record from list | |
4318 | ReleaseCacheRecord(m, rr); | |
4319 | } | |
6528fe3e | 4320 | } |
c9d2d929 | 4321 | if ((*cp)->rrcache_tail != rp) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp); |
283ee3ff A |
4322 | (*cp)->rrcache_tail = rp; |
4323 | if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next; | |
4324 | else ReleaseCacheGroup(m, cp); | |
6528fe3e A |
4325 | } |
4326 | } | |
c9b9ae52 A |
4327 | #if MDNS_DEBUGMSGS |
4328 | debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused); | |
4329 | #endif | |
4330 | } | |
c9b9ae52 | 4331 | |
283ee3ff | 4332 | if (m->rrcache_free) // If there are records in the free list, take one |
c9b9ae52 | 4333 | { |
283ee3ff A |
4334 | e = m->rrcache_free; |
4335 | m->rrcache_free = e->next; | |
c9b9ae52 A |
4336 | if (++m->rrcache_totalused >= m->rrcache_report) |
4337 | { | |
7cb34e5c | 4338 | debugf("RR Cache now using %ld objects", m->rrcache_totalused); |
c9b9ae52 A |
4339 | if (m->rrcache_report < 100) m->rrcache_report += 10; |
4340 | else m->rrcache_report += 100; | |
4341 | } | |
283ee3ff A |
4342 | mDNSPlatformMemZero(e, sizeof(*e)); |
4343 | } | |
4344 | ||
4345 | m->lock_rrcache = 0; | |
4346 | ||
4347 | return(e); | |
4348 | } | |
4349 | ||
4350 | mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength) | |
4351 | { | |
4352 | CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); | |
4353 | if (r) | |
4354 | { | |
7f0064bd | 4355 | r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage |
7f0064bd | 4356 | if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage |
c9b9ae52 A |
4357 | { |
4358 | r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); | |
4359 | if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; | |
283ee3ff | 4360 | else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } |
c9b9ae52 | 4361 | } |
6528fe3e | 4362 | } |
283ee3ff A |
4363 | return(r); |
4364 | } | |
6528fe3e | 4365 | |
283ee3ff A |
4366 | mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) |
4367 | { | |
4368 | mDNSu16 namelen = DomainNameLength(rr->name); | |
4369 | CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL); | |
4370 | if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } | |
4371 | cg->next = m->rrcache_hash[slot]; | |
4372 | cg->namehash = rr->namehash; | |
4373 | cg->members = mDNSNULL; | |
4374 | cg->rrcache_tail = &cg->members; | |
4375 | cg->name = (domainname*)cg->namestorage; | |
c9d2d929 | 4376 | //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c); |
283ee3ff A |
4377 | if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen); |
4378 | if (!cg->name) | |
4379 | { | |
4380 | LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); | |
4381 | ReleaseCacheEntity(m, (CacheEntity*)cg); | |
4382 | return(mDNSNULL); | |
4383 | } | |
4384 | AssignDomainName(cg->name, rr->name); | |
c9b9ae52 | 4385 | |
283ee3ff A |
4386 | if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); |
4387 | m->rrcache_hash[slot] = cg; | |
4388 | if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); | |
4389 | ||
4390 | return(cg); | |
c9b9ae52 | 4391 | } |
6528fe3e | 4392 | |
c9b9ae52 A |
4393 | mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) |
4394 | { | |
4395 | // Make sure we mark this record as thoroughly expired -- we don't ever want to give | |
4396 | // a positive answer using an expired record (e.g. from an interface that has gone away). | |
4397 | // We don't want to clear CRActiveQuestion here, because that would leave the record subject to | |
4398 | // summary deletion without giving the proper callback to any questions that are monitoring it. | |
4399 | // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. | |
4400 | rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; | |
4401 | rr->UnansweredQueries = MaxUnansweredQueries; | |
4402 | rr->resrec.rroriginalttl = 0; | |
4403 | SetNextCacheCheckTime(m, rr); | |
6528fe3e A |
4404 | } |
4405 | ||
7f0064bd | 4406 | mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) |
6528fe3e | 4407 | { |
7f0064bd | 4408 | mDNSs32 time; |
6528fe3e | 4409 | mDNSPlatformLock(m); |
7f0064bd | 4410 | if (m->mDNS_busy) |
c9b9ae52 | 4411 | { |
7f0064bd A |
4412 | LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); |
4413 | if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); | |
c9b9ae52 | 4414 | } |
c9b9ae52 | 4415 | |
7f0064bd | 4416 | if (m->timenow) time = m->timenow; |
28f7d060 | 4417 | else time = mDNS_TimeNow_NoLock(m); |
6528fe3e | 4418 | mDNSPlatformUnlock(m); |
7f0064bd | 4419 | return(time); |
6528fe3e A |
4420 | } |
4421 | ||
c9b9ae52 | 4422 | mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) |
6528fe3e | 4423 | { |
c9b9ae52 A |
4424 | mDNS_Lock(m); // Must grab lock before trying to read m->timenow |
4425 | ||
4426 | if (m->timenow - m->NextScheduledEvent >= 0) | |
4427 | { | |
4428 | int i; | |
6528fe3e | 4429 | |
c9b9ae52 A |
4430 | verbosedebugf("mDNS_Execute"); |
4431 | if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set"); | |
4432 | ||
4433 | // 1. If we're past the probe suppression time, we can clear it | |
4434 | if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; | |
4435 | ||
4436 | // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter | |
4437 | if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; | |
4438 | ||
4439 | // 3. Purge our cache of stale old records | |
4440 | if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) | |
4441 | { | |
4442 | mDNSu32 slot; | |
4443 | m->NextCacheCheck = m->timenow + 0x3FFFFFFF; | |
283ee3ff A |
4444 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) |
4445 | { | |
4446 | CacheGroup **cp = &m->rrcache_hash[slot]; | |
4447 | while (*cp) | |
4448 | { | |
4449 | CheckCacheExpiration(m, *cp); | |
4450 | if ((*cp)->members) cp=&(*cp)->next; | |
4451 | else ReleaseCacheGroup(m, cp); | |
4452 | } | |
4453 | } | |
c9b9ae52 | 4454 | } |
6528fe3e | 4455 | |
c9b9ae52 | 4456 | // 4. See if we can answer any of our new local questions from the cache |
7f0064bd A |
4457 | for (i=0; m->NewQuestions && i<1000; i++) |
4458 | { | |
4459 | if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; | |
4460 | AnswerNewQuestion(m); | |
4461 | } | |
283ee3ff | 4462 | if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); |
c9b9ae52 | 4463 | |
c9b9ae52 | 4464 | for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); |
283ee3ff | 4465 | if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); |
4e28aad3 | 4466 | |
283ee3ff A |
4467 | for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++) |
4468 | { | |
4469 | AuthRecord *rr = m->NewLocalRecords; | |
4470 | m->NewLocalRecords = m->NewLocalRecords->next; | |
4471 | AnswerLocalQuestions(m, rr, mDNStrue); | |
4472 | } | |
4473 | if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit"); | |
6528fe3e | 4474 | |
c9b9ae52 A |
4475 | // 5. See what packets we need to send |
4476 | if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m); | |
4477 | else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0) | |
4478 | { | |
4479 | // If the platform code is ready, and we're not suppressing packet generation right now | |
4480 | // then send our responses, probes, and questions. | |
c9d2d929 | 4481 | // We check the cache first, because there might be records close to expiring that trigger questions to refresh them |
c9b9ae52 A |
4482 | // We send queries next, because there might be final-stage probes that complete their probing here, causing |
4483 | // them to advance to announcing state, and we want those to be included in any announcements we send out. | |
c9d2d929 | 4484 | // Finally, we send responses, including the previously mentioned records that just completed probing |
c9b9ae52 A |
4485 | m->SuppressSending = 0; |
4486 | ||
4487 | // 6. Send Query packets. This may cause some probing records to advance to announcing state | |
4488 | if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); | |
4489 | if (m->timenow - m->NextScheduledQuery >= 0) | |
4490 | { | |
4491 | LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second"); | |
4492 | m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; | |
4493 | } | |
4494 | if (m->timenow - m->NextScheduledProbe >= 0) | |
4495 | { | |
4496 | LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second"); | |
4497 | m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; | |
4498 | } | |
4499 | ||
4500 | // 7. Send Response packets, including probing records just advanced to announcing state | |
4501 | if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); | |
4502 | if (m->timenow - m->NextScheduledResponse >= 0) | |
4503 | { | |
4504 | LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second"); | |
4505 | m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond; | |
4506 | } | |
4507 | } | |
6528fe3e | 4508 | |
c9d2d929 | 4509 | m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary |
c9b9ae52 | 4510 | } |
6528fe3e | 4511 | |
c9b9ae52 A |
4512 | // Note about multi-threaded systems: |
4513 | // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), | |
4514 | // performing mDNS API operations that change our next scheduled event time. | |
4515 | // | |
4516 | // On multi-threaded systems (like the current Windows implementation) that have a single main thread | |
4517 | // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility | |
4518 | // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will | |
4519 | // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one | |
4520 | // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful | |
4521 | // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it | |
4522 | // does, the state of the signal will be noticed, causing the blocking primitive to return immediately | |
4523 | // without blocking. This avoids the race condition between the signal from the other thread arriving | |
4524 | // just *before* or just *after* the main thread enters the blocking primitive. | |
4525 | // | |
4526 | // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, | |
4527 | // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to | |
4528 | // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer | |
4529 | // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale | |
4530 | // by the time it gets to the timer callback function). | |
4531 | ||
7f0064bd | 4532 | #ifndef UNICAST_DISABLED |
8e92c31c | 4533 | uDNS_Execute(m); |
7f0064bd | 4534 | #endif |
c9b9ae52 A |
4535 | mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value |
4536 | return(m->NextScheduledEvent); | |
6528fe3e A |
4537 | } |
4538 | ||
c9b9ae52 A |
4539 | // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep. |
4540 | // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up. | |
4541 | // Normally, the platform support layer below mDNSCore should call this, not the client layer above. | |
4542 | // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call | |
4543 | // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just | |
4544 | // found itself in a new network environment. For example, if the Ethernet hardware indicates that the | |
4545 | // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse) | |
4546 | // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc. | |
4547 | // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network | |
4548 | // traffic, so it should only be called when there is legitimate reason to believe the machine | |
4549 | // may have become attached to a new network. | |
4550 | mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) | |
6528fe3e | 4551 | { |
c9b9ae52 A |
4552 | AuthRecord *rr; |
4553 | ||
4554 | mDNS_Lock(m); | |
6528fe3e A |
4555 | |
4556 | m->SleepState = sleepstate; | |
283ee3ff | 4557 | LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); |
6528fe3e A |
4558 | |
4559 | if (sleepstate) | |
4560 | { | |
7f0064bd A |
4561 | #ifndef UNICAST_DISABLED |
4562 | uDNS_Sleep(m); | |
4563 | #endif | |
c9b9ae52 | 4564 | // Mark all the records we need to deregister and send them |
6528fe3e | 4565 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
7f0064bd | 4566 | if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) |
c9b9ae52 A |
4567 | rr->ImmedAnswer = mDNSInterfaceMark; |
4568 | SendResponses(m); | |
6528fe3e A |
4569 | } |
4570 | else | |
4571 | { | |
4572 | DNSQuestion *q; | |
c9b9ae52 | 4573 | mDNSu32 slot; |
283ee3ff | 4574 | CacheGroup *cg; |
c9b9ae52 A |
4575 | CacheRecord *cr; |
4576 | ||
7f0064bd A |
4577 | #ifndef UNICAST_DISABLED |
4578 | uDNS_Wake(m); | |
4579 | #endif | |
8e92c31c | 4580 | // 1. Retrigger all our questions |
c9b9ae52 A |
4581 | for (q = m->Questions; q; q=q->next) // Scan our list of questions |
4582 | if (ActiveQuestion(q)) | |
4583 | { | |
7f0064bd | 4584 | q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question |
283ee3ff | 4585 | q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it |
7f0064bd A |
4586 | q->LastQTime = m->timenow - q->ThisQInterval; |
4587 | q->RecentAnswerPkts = 0; | |
c9b9ae52 A |
4588 | ExpireDupSuppressInfo(q->DupSuppress, m->timenow); |
4589 | m->NextScheduledQuery = m->timenow; | |
4590 | } | |
6528fe3e | 4591 | |
c9b9ae52 A |
4592 | // 2. Re-validate our cache records |
4593 | m->NextCacheCheck = m->timenow; | |
283ee3ff | 4594 | FORALL_CACHERECORDS(slot, cg, cr) |
c9d2d929 | 4595 | mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect); |
c9b9ae52 A |
4596 | |
4597 | // 3. Retrigger probing and announcing for all our authoritative records | |
6528fe3e A |
4598 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
4599 | { | |
c9b9ae52 | 4600 | if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; |
7f0064bd A |
4601 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); |
4602 | rr->AnnounceCount = InitialAnnounceCount; | |
4603 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); | |
c9b9ae52 | 4604 | InitializeLastAPTime(m, rr); |
6528fe3e | 4605 | } |
6528fe3e A |
4606 | } |
4607 | ||
4608 | mDNS_Unlock(m); | |
4609 | } | |
4610 | ||
4611 | // *************************************************************************** | |
c9b9ae52 | 4612 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
4613 | #pragma mark - |
4614 | #pragma mark - Packet Reception Functions | |
4615 | #endif | |
4616 | ||
6528fe3e A |
4617 | #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) |
4618 | ||
4619 | mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, | |
c9b9ae52 | 4620 | const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) |
6528fe3e | 4621 | { |
c9b9ae52 A |
4622 | mDNSu8 *responseptr = response->data; |
4623 | const mDNSu8 *const limit = response->data + sizeof(response->data); | |
6528fe3e | 4624 | const mDNSu8 *ptr = query->data; |
c9b9ae52 A |
4625 | AuthRecord *rr; |
4626 | mDNSu32 maxttl = 0x70000000; | |
6528fe3e A |
4627 | int i; |
4628 | ||
4629 | // Initialize the response fields so we can answer the questions | |
c9b9ae52 | 4630 | InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); |
6528fe3e A |
4631 | |
4632 | // *** | |
4633 | // *** 1. Write out the list of questions we are actually going to answer with this packet | |
4634 | // *** | |
c9b9ae52 | 4635 | if (LegacyQuery) |
6528fe3e | 4636 | { |
c9b9ae52 A |
4637 | maxttl = 10; |
4638 | for (i=0; i<query->h.numQuestions; i++) // For each question... | |
6528fe3e | 4639 | { |
c9b9ae52 A |
4640 | DNSQuestion q; |
4641 | ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... | |
4642 | if (!ptr) return(mDNSNULL); | |
4643 | ||
4644 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers | |
4645 | { | |
4646 | if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question | |
4647 | { // then put the question in the question section | |
4648 | responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); | |
4649 | if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } | |
4650 | break; // break out of the ResponseRecords loop, and go on to the next question | |
4651 | } | |
6528fe3e A |
4652 | } |
4653 | } | |
c9b9ae52 A |
4654 | |
4655 | if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } | |
6528fe3e A |
4656 | } |
4657 | ||
c9b9ae52 A |
4658 | // *** |
4659 | // *** 2. Write Answers | |
4660 | // *** | |
4661 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
4662 | if (rr->NR_AnswerTo) | |
4663 | { | |
4664 | mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl); | |
4665 | if (p) responseptr = p; | |
4666 | else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } | |
4667 | } | |
6528fe3e A |
4668 | |
4669 | // *** | |
c9b9ae52 | 4670 | // *** 3. Write Additionals |
6528fe3e A |
4671 | // *** |
4672 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
c9b9ae52 | 4673 | if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) |
6528fe3e | 4674 | { |
c9b9ae52 A |
4675 | mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl); |
4676 | if (p) responseptr = p; | |
4677 | else debugf("GenerateUnicastResponse: No more space for additionals"); | |
6528fe3e | 4678 | } |
c9b9ae52 | 4679 | |
6528fe3e A |
4680 | return(responseptr); |
4681 | } | |
4682 | ||
c9b9ae52 A |
4683 | // AuthRecord *our is our Resource Record |
4684 | // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network | |
6528fe3e A |
4685 | // Returns 0 if there is no conflict |
4686 | // Returns +1 if there was a conflict and we won | |
4687 | // Returns -1 if there was a conflict and we lost and have to rename | |
c9b9ae52 | 4688 | mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt) |
6528fe3e | 4689 | { |
6528fe3e | 4690 | mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; |
c9b9ae52 A |
4691 | mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; |
4692 | if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } | |
4693 | if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } | |
4694 | ||
4695 | ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); | |
4696 | pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); | |
4697 | while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } | |
4698 | if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict | |
4699 | ||
4700 | if (ourptr >= ourend) return(-1); // Our data ran out first; We lost | |
4701 | if (pktptr >= pktend) return(+1); // Packet data ran out first; We won | |
4702 | if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost | |
4703 | if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won | |
6528fe3e | 4704 | |
cc340f17 | 4705 | LogMsg("CompareRData ERROR: Invalid state"); |
6528fe3e A |
4706 | return(-1); |
4707 | } | |
4708 | ||
c9b9ae52 A |
4709 | // See if we have an authoritative record that's identical to this packet record, |
4710 | // whose canonical DependentOn record is the specified master record. | |
6528fe3e A |
4711 | // The DependentOn pointer is typically used for the TXT record of service registrations |
4712 | // It indicates that there is no inherent conflict detection for the TXT record | |
4713 | // -- it depends on the SRV record to resolve name conflicts | |
c9b9ae52 A |
4714 | // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn |
4715 | // pointer chain (if any) to make sure we reach the canonical DependentOn record | |
6528fe3e A |
4716 | // If the record has no DependentOn, then just return that record's pointer |
4717 | // Returns NULL if we don't have any local RRs that are identical to the one from the packet | |
c9b9ae52 | 4718 | mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master) |
6528fe3e | 4719 | { |
c9b9ae52 A |
4720 | const AuthRecord *r1; |
4721 | for (r1 = m->ResourceRecords; r1; r1=r1->next) | |
6528fe3e | 4722 | { |
c9b9ae52 | 4723 | if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) |
6528fe3e | 4724 | { |
c9b9ae52 A |
4725 | const AuthRecord *r2 = r1; |
4726 | while (r2->DependentOn) r2 = r2->DependentOn; | |
4727 | if (r2 == master) return(mDNStrue); | |
6528fe3e A |
4728 | } |
4729 | } | |
c9b9ae52 A |
4730 | for (r1 = m->DuplicateRecords; r1; r1=r1->next) |
4731 | { | |
4732 | if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) | |
4733 | { | |
4734 | const AuthRecord *r2 = r1; | |
4735 | while (r2->DependentOn) r2 = r2->DependentOn; | |
4736 | if (r2 == master) return(mDNStrue); | |
4737 | } | |
4738 | } | |
4739 | return(mDNSfalse); | |
6528fe3e A |
4740 | } |
4741 | ||
4742 | // Find the canonical RRSet pointer for this RR received in a packet. | |
c9b9ae52 | 4743 | // If we find any identical AuthRecord in our authoritative list, then follow its RRSet |
6528fe3e A |
4744 | // pointers (if any) to make sure we return the canonical member of this name/type/class |
4745 | // Returns NULL if we don't have any local RRs that are identical to the one from the packet | |
c9b9ae52 | 4746 | mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr) |
6528fe3e | 4747 | { |
c9b9ae52 | 4748 | const AuthRecord *rr; |
6528fe3e A |
4749 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
4750 | { | |
c9b9ae52 | 4751 | if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) |
6528fe3e A |
4752 | { |
4753 | while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; | |
4754 | return(rr); | |
4755 | } | |
4756 | } | |
4757 | return(mDNSNULL); | |
4758 | } | |
4759 | ||
4760 | // PacketRRConflict is called when we've received an RR (pktrr) which has the same name | |
4761 | // as one of our records (our) but different rdata. | |
4762 | // 1. If our record is not a type that's supposed to be unique, we don't care. | |
4763 | // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one. | |
4764 | // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer | |
4765 | // points to our record, ignore this conflict (e.g. the packet record matches one of our | |
4766 | // TXT records, and that record is marked as dependent on 'our', its SRV record). | |
4767 | // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record | |
4768 | // are members of the same RRSet, then this is not a conflict. | |
c9b9ae52 | 4769 | mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr) |
6528fe3e | 4770 | { |
c9b9ae52 | 4771 | const AuthRecord *ourset = our->RRSet ? our->RRSet : our; |
6528fe3e A |
4772 | |
4773 | // If not supposed to be unique, not a conflict | |
c9b9ae52 | 4774 | if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); |
6528fe3e A |
4775 | |
4776 | // If a dependent record, not a conflict | |
c9b9ae52 | 4777 | if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); |
6528fe3e A |
4778 | |
4779 | // If the pktrr matches a member of ourset, not a conflict | |
4780 | if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse); | |
4781 | ||
4782 | // Okay, this is a conflict | |
4783 | return(mDNStrue); | |
4784 | } | |
4785 | ||
4786 | // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change | |
4787 | // the record list and/or question list. | |
4788 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
4789 | mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, | |
c9b9ae52 | 4790 | DNSQuestion *q, AuthRecord *our) |
6528fe3e A |
4791 | { |
4792 | int i; | |
4793 | const mDNSu8 *ptr = LocateAuthorities(query, end); | |
4794 | mDNSBool FoundUpdate = mDNSfalse; | |
4795 | ||
4796 | for (i = 0; i < query->h.numAuthorities; i++) | |
4797 | { | |
7f0064bd | 4798 | ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); |
6528fe3e | 4799 | if (!ptr) break; |
7f0064bd | 4800 | if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) |
6528fe3e A |
4801 | { |
4802 | FoundUpdate = mDNStrue; | |
7f0064bd | 4803 | if (PacketRRConflict(m, our, &m->rec.r)) |
6528fe3e | 4804 | { |
7f0064bd A |
4805 | int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; |
4806 | if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; | |
4807 | if (!result) result = CompareRData(our, &m->rec.r); | |
cc340f17 A |
4808 | if (result > 0) |
4809 | debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); | |
4810 | else if (result < 0) | |
6528fe3e | 4811 | { |
cc340f17 A |
4812 | debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); |
4813 | mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); | |
4814 | goto exit; | |
6528fe3e A |
4815 | } |
4816 | } | |
4817 | } | |
7f0064bd | 4818 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
6528fe3e A |
4819 | } |
4820 | if (!FoundUpdate) | |
283ee3ff | 4821 | debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); |
7f0064bd A |
4822 | exit: |
4823 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it | |
c9b9ae52 A |
4824 | } |
4825 | ||
4826 | mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) | |
4827 | { | |
283ee3ff A |
4828 | mDNSu32 slot = HashSlot(pktrr->name); |
4829 | CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); | |
c9b9ae52 | 4830 | CacheRecord *rr; |
283ee3ff | 4831 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
c9b9ae52 A |
4832 | if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break; |
4833 | return(rr); | |
6528fe3e A |
4834 | } |
4835 | ||
4836 | // ProcessQuery examines a received query to see if we have any answers to give | |
4837 | mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, | |
c9d2d929 A |
4838 | const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, |
4839 | DNSMessage *const response) | |
6528fe3e | 4840 | { |
283ee3ff | 4841 | mDNSBool FromLocalSubnet = AddressIsLocalSubnet(m, InterfaceID, srcaddr); |
7f0064bd A |
4842 | AuthRecord *ResponseRecords = mDNSNULL; |
4843 | AuthRecord **nrp = &ResponseRecords; | |
4844 | CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated | |
4845 | CacheRecord **eap = &ExpectedAnswers; | |
4846 | DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet | |
4847 | DNSQuestion **dqp = &DupQuestions; | |
4848 | mDNSs32 delayresponse = 0; | |
4849 | mDNSBool SendLegacyResponse = mDNSfalse; | |
4850 | const mDNSu8 *ptr = query->data; | |
4851 | mDNSu8 *responseptr = mDNSNULL; | |
4852 | AuthRecord *rr; | |
6528fe3e A |
4853 | int i; |
4854 | ||
6528fe3e A |
4855 | // *** |
4856 | // *** 1. Parse Question Section and mark potential answers | |
4857 | // *** | |
4858 | for (i=0; i<query->h.numQuestions; i++) // For each question... | |
4859 | { | |
c9b9ae52 | 4860 | mDNSBool QuestionNeedsMulticastResponse; |
6528fe3e | 4861 | int NumAnswersForThisQuestion = 0; |
c9b9ae52 A |
4862 | DNSQuestion pktq, *q; |
4863 | ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... | |
6528fe3e | 4864 | if (!ptr) goto exit; |
c9b9ae52 A |
4865 | |
4866 | // The only queries that *need* a multicast response are: | |
4867 | // * Queries sent via multicast | |
4868 | // * from port 5353 | |
4869 | // * that don't have the kDNSQClass_UnicastResponse bit set | |
4870 | // These queries need multicast responses because other clients will: | |
4871 | // * suppress their own identical questions when they see these questions, and | |
4872 | // * expire their cache records if they don't see the expected responses | |
4873 | // For other queries, we may still choose to send the occasional multicast response anyway, | |
4874 | // to keep our neighbours caches warm, and for ongoing conflict detection. | |
4875 | QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); | |
4876 | // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later | |
4877 | pktq.qclass &= ~kDNSQClass_UnicastResponse; | |
6528fe3e A |
4878 | |
4879 | // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe | |
4880 | // can result in user callbacks which may change the record list and/or question list. | |
4881 | // Also note: we just mark potential answer records here, without trying to build the | |
4882 | // "ResponseRecords" list, because we don't want to risk user callbacks deleting records | |
c9b9ae52 A |
4883 | // from that list while we're in the middle of trying to build it. |
4884 | if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set"); | |
6528fe3e A |
4885 | m->CurrentRecord = m->ResourceRecords; |
4886 | while (m->CurrentRecord) | |
4887 | { | |
4888 | rr = m->CurrentRecord; | |
4889 | m->CurrentRecord = rr->next; | |
7f0064bd | 4890 | if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) |
6528fe3e | 4891 | { |
c9b9ae52 A |
4892 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) |
4893 | ResolveSimultaneousProbe(m, query, end, &pktq, rr); | |
6528fe3e A |
4894 | else if (ResourceRecordIsValidAnswer(rr)) |
4895 | { | |
4896 | NumAnswersForThisQuestion++; | |
c9b9ae52 | 4897 | // Notes: |
283ee3ff A |
4898 | // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) |
4899 | // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead) | |
7f0064bd | 4900 | // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) |
283ee3ff A |
4901 | // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, |
4902 | // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link) | |
4903 | // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) | |
4904 | if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery)) | |
c9b9ae52 A |
4905 | { |
4906 | // We only mark this question for sending if it is at least one second since the last time we multicast it | |
283ee3ff | 4907 | // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it. |
c9b9ae52 A |
4908 | // This is to guard against the case where someone blasts us with queries as fast as they can. |
4909 | if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || | |
4910 | (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) | |
4911 | rr->NR_AnswerTo = (mDNSu8*)~0; | |
4912 | } | |
7f0064bd | 4913 | else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; |
6528fe3e A |
4914 | } |
4915 | } | |
4916 | } | |
c9b9ae52 | 4917 | |
7f0064bd A |
4918 | // If we couldn't answer this question, someone else might be able to, |
4919 | // so use random delay on response to reduce collisions | |
4920 | if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms | |
4921 | ||
c9b9ae52 A |
4922 | // We only do the following accelerated cache expiration processing and duplicate question suppression processing |
4923 | // for multicast queries with multicast responses. | |
4924 | // For any query generating a unicast response we don't do this because we can't assume we will see the response | |
4925 | if (QuestionNeedsMulticastResponse) | |
4926 | { | |
283ee3ff A |
4927 | const mDNSu32 slot = HashSlot(&pktq.qname); |
4928 | CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname); | |
c9b9ae52 | 4929 | CacheRecord *rr; |
c9b9ae52 A |
4930 | |
4931 | // Make a list indicating which of our own cache records we expect to see updated as a result of this query | |
4932 | // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated | |
283ee3ff | 4933 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
c9b9ae52 A |
4934 | if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit) |
4935 | if (!rr->NextInKAList && eap != &rr->NextInKAList) | |
4936 | { | |
4937 | *eap = rr; | |
4938 | eap = &rr->NextInKAList; | |
4939 | if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond) | |
4940 | { | |
4941 | // Although MPUnansweredQ is only really used for multi-packet query processing, | |
4942 | // we increment it for both single-packet and multi-packet queries, so that it stays in sync | |
4943 | // with the MPUnansweredKA value, which by necessity is incremented for both query types. | |
4944 | rr->MPUnansweredQ++; | |
4945 | rr->MPLastUnansweredQT = m->timenow; | |
4946 | rr->MPExpectingKA = mDNStrue; | |
4947 | } | |
4948 | } | |
4949 | ||
4950 | // Check if this question is the same as any of mine. | |
4951 | // We only do this for non-truncated queries. Right now it would be too complicated to try | |
4952 | // to keep track of duplicate suppression state between multiple packets, especially when we | |
4953 | // can't guarantee to receive all of the Known Answer packets that go with a particular query. | |
4954 | if (!(query->h.flags.b[0] & kDNSFlag0_TC)) | |
4955 | for (q = m->Questions; q; q=q->next) | |
8e92c31c | 4956 | if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) |
c9b9ae52 A |
4957 | if (!q->InterfaceID || q->InterfaceID == InterfaceID) |
4958 | if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) | |
c9d2d929 | 4959 | if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) |
c9b9ae52 A |
4960 | { *dqp = q; dqp = &q->NextInDQList; } |
4961 | } | |
6528fe3e A |
4962 | } |
4963 | ||
4964 | // *** | |
4965 | // *** 2. Now we can safely build the list of marked answers | |
4966 | // *** | |
4967 | for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers | |
4968 | if (rr->NR_AnswerTo) // If we marked the record... | |
c9b9ae52 | 4969 | AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list |
6528fe3e A |
4970 | |
4971 | // *** | |
4972 | // *** 3. Add additional records | |
4973 | // *** | |
7f0064bd | 4974 | AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); |
6528fe3e A |
4975 | |
4976 | // *** | |
c9b9ae52 | 4977 | // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list |
6528fe3e A |
4978 | // *** |
4979 | for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section... | |
4980 | { | |
4981 | // Get the record... | |
c9b9ae52 A |
4982 | AuthRecord *rr; |
4983 | CacheRecord *ourcacherr; | |
7f0064bd | 4984 | ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); |
6528fe3e A |
4985 | if (!ptr) goto exit; |
4986 | ||
c9b9ae52 | 4987 | // See if this Known-Answer suppresses any of our currently planned answers |
6528fe3e | 4988 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) |
7f0064bd | 4989 | if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) |
6528fe3e A |
4990 | { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } |
4991 | ||
c9b9ae52 | 4992 | // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) |
6528fe3e A |
4993 | for (rr=m->ResourceRecords; rr; rr=rr->next) |
4994 | { | |
c9b9ae52 | 4995 | // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression |
7f0064bd | 4996 | if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) |
c9b9ae52 A |
4997 | { |
4998 | if (srcaddr->type == mDNSAddrType_IPv4) | |
6528fe3e | 4999 | { |
7f0064bd | 5000 | if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; |
6528fe3e | 5001 | } |
c9b9ae52 A |
5002 | else if (srcaddr->type == mDNSAddrType_IPv6) |
5003 | { | |
5004 | if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; | |
5005 | } | |
8e92c31c A |
5006 | if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) |
5007 | { | |
7f0064bd A |
5008 | rr->ImmedAnswer = mDNSNULL; |
5009 | rr->ImmedUnicast = mDNSfalse; | |
8e92c31c | 5010 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
7f0064bd | 5011 | LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); |
8e92c31c A |
5012 | #endif |
5013 | } | |
c9b9ae52 A |
5014 | } |
5015 | } | |
5016 | ||
5017 | // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, | |
5018 | // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). | |
7f0064bd | 5019 | ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); |
c9b9ae52 A |
5020 | if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) |
5021 | { | |
5022 | ourcacherr->MPUnansweredKA++; | |
5023 | ourcacherr->MPExpectingKA = mDNSfalse; | |
5024 | } | |
5025 | ||
5026 | // Having built our ExpectedAnswers list from the questions in this packet, we can definitively | |
5027 | // remove from our ExpectedAnswers list any records that are suppressed in the very same packet. | |
5028 | // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them. | |
5029 | eap = &ExpectedAnswers; | |
5030 | while (*eap) | |
5031 | { | |
5032 | CacheRecord *rr = *eap; | |
7f0064bd | 5033 | if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) |
c9b9ae52 A |
5034 | { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; } |
5035 | else eap = &rr->NextInKAList; | |
5036 | } | |
5037 | ||
5038 | // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. | |
5039 | if (!ourcacherr) | |
5040 | { | |
5041 | dqp = &DupQuestions; | |
5042 | while (*dqp) | |
5043 | { | |
5044 | DNSQuestion *q = *dqp; | |
7f0064bd | 5045 | if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) |
c9b9ae52 A |
5046 | { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } |
5047 | else dqp = &q->NextInDQList; | |
5048 | } | |
6528fe3e | 5049 | } |
7f0064bd | 5050 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
6528fe3e A |
5051 | } |
5052 | ||
5053 | // *** | |
5054 | // *** 5. Cancel any additionals that were added because of now-deleted records | |
5055 | // *** | |
5056 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
5057 | if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo)) | |
5058 | { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } | |
5059 | ||
5060 | // *** | |
5061 | // *** 6. Mark the send flags on the records we plan to send | |
5062 | // *** | |
5063 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
5064 | { | |
c9b9ae52 | 5065 | if (rr->NR_AnswerTo) |
6528fe3e | 5066 | { |
7f0064bd A |
5067 | mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response |
5068 | mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) | |
c9b9ae52 A |
5069 | |
5070 | // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. | |
283ee3ff A |
5071 | if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) |
5072 | { | |
5073 | SendMulticastResponse = mDNStrue; | |
5074 | // If this record was marked for modern (delayed) unicast response, then mark it as promoted to | |
5075 | // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below). | |
5076 | // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value. | |
5077 | if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0; | |
5078 | } | |
c9b9ae52 A |
5079 | |
5080 | // If the client insists on a multicast response, then we'd better send one | |
7f0064bd A |
5081 | if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; |
5082 | else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; | |
5083 | else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; | |
c9b9ae52 | 5084 | |
7f0064bd | 5085 | if (SendMulticastResponse || SendUnicastResponse) |
6528fe3e | 5086 | { |
8e92c31c A |
5087 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
5088 | rr->ImmedAnswerMarkTime = m->timenow; | |
5089 | #endif | |
5090 | m->NextScheduledResponse = m->timenow; | |
c9b9ae52 A |
5091 | // If we're already planning to send this on another interface, just send it on all interfaces |
5092 | if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) | |
c9b9ae52 | 5093 | rr->ImmedAnswer = mDNSInterfaceMark; |
6528fe3e A |
5094 | else |
5095 | { | |
c9b9ae52 | 5096 | rr->ImmedAnswer = InterfaceID; // Record interface to send it on |
7f0064bd | 5097 | if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue; |
c9b9ae52 A |
5098 | if (srcaddr->type == mDNSAddrType_IPv4) |
5099 | { | |
5100 | if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4; | |
5101 | else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr; | |
5102 | } | |
5103 | else if (srcaddr->type == mDNSAddrType_IPv6) | |
5104 | { | |
5105 | if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6; | |
5106 | else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr; | |
5107 | } | |
6528fe3e A |
5108 | } |
5109 | } | |
7f0064bd A |
5110 | // If TC flag is set, it means we should expect that additional known answers may be coming in another packet, |
5111 | // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11) | |
5112 | // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses | |
5113 | // else, for a simple unique record reply, we can reply immediately; no need for delay | |
5114 | if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms | |
5115 | else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms | |
6528fe3e | 5116 | } |
c9b9ae52 A |
5117 | else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) |
5118 | { | |
5119 | // Since additional records are an optimization anyway, we only ever send them on one interface at a time | |
5120 | // If two clients on different interfaces do queries that invoke the same optional additional answer, | |
5121 | // then the earlier client is out of luck | |
5122 | rr->ImmedAdditional = InterfaceID; | |
5123 | // No need to set m->NextScheduledResponse here | |
5124 | // We'll send these additional records when we send them, or not, as the case may be | |
5125 | } | |
6528fe3e A |
5126 | } |
5127 | ||
5128 | // *** | |
5129 | // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer | |
5130 | // *** | |
8d1ca615 A |
5131 | if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) |
5132 | { | |
8e92c31c A |
5133 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
5134 | mDNSs32 oldss = m->SuppressSending; | |
5135 | if (oldss && delayresponse) | |
5136 | LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); | |
5137 | #endif | |
8d1ca615 A |
5138 | // Pick a random delay: |
5139 | // We start with the base delay chosen above (typically either 1 second or 20 seconds), | |
5140 | // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds). | |
5141 | // This is an integer value, with resolution determined by the platform clock rate. | |
5142 | // We then divide that by 50 to get the delay value in ticks. We defer the division until last | |
5143 | // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second). | |
5144 | // The +49 before dividing is to ensure we round up, not down, to ensure that even | |
5145 | // on platforms where the native clock rate is less than fifty ticks per second, | |
5146 | // we still guarantee that the final calculated delay is at least one platform tick. | |
5147 | // We want to make sure we don't ever allow the delay to be zero ticks, | |
7cb34e5c | 5148 | // because if that happens we'll fail the Bonjour Conformance Test. |
8d1ca615 A |
5149 | // Our final computed delay is 20-120ms for normal delayed replies, |
5150 | // or 400-500ms in the case of multi-packet known-answer lists. | |
5151 | m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50; | |
6528fe3e | 5152 | if (m->SuppressSending == 0) m->SuppressSending = 1; |
8e92c31c A |
5153 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
5154 | if (oldss && delayresponse) | |
5155 | LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow); | |
5156 | #endif | |
6528fe3e A |
5157 | } |
5158 | ||
5159 | // *** | |
8e92c31c | 5160 | // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too |
6528fe3e | 5161 | // *** |
7f0064bd | 5162 | if (SendLegacyResponse) |
c9b9ae52 | 5163 | responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); |
6528fe3e A |
5164 | |
5165 | exit: | |
7f0064bd A |
5166 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
5167 | ||
6528fe3e | 5168 | // *** |
c9b9ae52 | 5169 | // *** 9. Finally, clear our link chains ready for use next time |
6528fe3e A |
5170 | // *** |
5171 | while (ResponseRecords) | |
5172 | { | |
5173 | rr = ResponseRecords; | |
5174 | ResponseRecords = rr->NextResponse; | |
5175 | rr->NextResponse = mDNSNULL; | |
5176 | rr->NR_AnswerTo = mDNSNULL; | |
5177 | rr->NR_AdditionalTo = mDNSNULL; | |
5178 | } | |
5179 | ||
c9b9ae52 A |
5180 | while (ExpectedAnswers) |
5181 | { | |
5182 | CacheRecord *rr; | |
5183 | rr = ExpectedAnswers; | |
5184 | ExpectedAnswers = rr->NextInKAList; | |
5185 | rr->NextInKAList = mDNSNULL; | |
5186 | ||
5187 | // For non-truncated queries, we can definitively say that we should expect | |
5188 | // to be seeing a response for any records still left in the ExpectedAnswers list | |
5189 | if (!(query->h.flags.b[0] & kDNSFlag0_TC)) | |
5190 | if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond) | |
5191 | { | |
5192 | rr->UnansweredQueries++; | |
5193 | rr->LastUnansweredTime = m->timenow; | |
5194 | if (rr->UnansweredQueries > 1) | |
5195 | debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", | |
7f0064bd | 5196 | rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); |
c9b9ae52 A |
5197 | SetNextCacheCheckTime(m, rr); |
5198 | } | |
5199 | ||
5200 | // If we've seen multiple unanswered queries for this record, | |
5201 | // then mark it to expire in five seconds if we don't get a response by then. | |
5202 | if (rr->UnansweredQueries >= MaxUnansweredQueries) | |
5203 | { | |
5204 | // Only show debugging message if this record was not about to expire anyway | |
5205 | if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) | |
5206 | debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", | |
7f0064bd | 5207 | rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); |
c9b9ae52 A |
5208 | mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); |
5209 | } | |
5210 | // Make a guess, based on the multi-packet query / known answer counts, whether we think we | |
5211 | // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for | |
5212 | // possible packet loss of up to 20% of the additional KA packets.) | |
5213 | else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8) | |
5214 | { | |
5215 | // We want to do this conservatively. | |
5216 | // If there are so many machines on the network that they have to use multi-packet known-answer lists, | |
5217 | // then we don't want them to all hit the network simultaneously with their final expiration queries. | |
5218 | // By setting the record to expire in four minutes, we achieve two things: | |
5219 | // (a) the 90-95% final expiration queries will be less bunched together | |
5220 | // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own | |
5221 | mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4; | |
5222 | if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) | |
5223 | remain = 240 * (mDNSu32)mDNSPlatformOneSecond; | |
5224 | ||
5225 | // Only show debugging message if this record was not about to expire anyway | |
5226 | if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) | |
5227 | debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", | |
7f0064bd | 5228 | rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); |
c9b9ae52 A |
5229 | |
5230 | if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) | |
5231 | rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query | |
5232 | rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics | |
5233 | rr->MPUnansweredKA = 0; | |
5234 | rr->MPExpectingKA = mDNSfalse; | |
5235 | ||
5236 | if (remain < kDefaultReconfirmTimeForNoAnswer) | |
5237 | remain = kDefaultReconfirmTimeForNoAnswer; | |
5238 | mDNS_Reconfirm_internal(m, rr, remain); | |
5239 | } | |
5240 | } | |
5241 | ||
5242 | while (DupQuestions) | |
5243 | { | |
5244 | int i; | |
5245 | DNSQuestion *q = DupQuestions; | |
5246 | DupQuestions = q->NextInDQList; | |
5247 | q->NextInDQList = mDNSNULL; | |
5248 | i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); | |
5249 | debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, | |
5250 | srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); | |
5251 | } | |
5252 | ||
6528fe3e A |
5253 | return(responseptr); |
5254 | } | |
5255 | ||
5256 | mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, | |
c9b9ae52 A |
5257 | const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, |
5258 | const mDNSInterfaceID InterfaceID) | |
6528fe3e | 5259 | { |
7f0064bd A |
5260 | mDNSu8 *responseend = mDNSNULL; |
5261 | mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr); | |
8e92c31c | 5262 | |
7f0064bd | 5263 | if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr)) |
6528fe3e | 5264 | { |
c9d2d929 | 5265 | LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)", |
8e92c31c | 5266 | srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, |
b7388343 A |
5267 | msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", |
5268 | msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", | |
5269 | msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", | |
5270 | msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); | |
8e92c31c A |
5271 | return; |
5272 | } | |
b7388343 | 5273 | |
c9d2d929 | 5274 | verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", |
8e92c31c A |
5275 | srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, |
5276 | msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", | |
5277 | msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", | |
5278 | msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", | |
5279 | msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); | |
5280 | ||
5281 | responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, | |
7f0064bd | 5282 | (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); |
8e92c31c A |
5283 | |
5284 | if (responseend) // If responseend is non-null, that means we built a unicast response packet | |
5285 | { | |
5286 | debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld", | |
7f0064bd A |
5287 | m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", |
5288 | m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", | |
5289 | m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", | |
8e92c31c | 5290 | srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); |
7f0064bd | 5291 | mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL); |
6528fe3e A |
5292 | } |
5293 | } | |
5294 | ||
5295 | // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change | |
5296 | // the record list and/or question list. | |
5297 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
5298 | mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, | |
8e92c31c A |
5299 | const DNSMessage *const response, const mDNSu8 *end, |
5300 | const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, | |
7f0064bd | 5301 | const mDNSInterfaceID InterfaceID) |
6528fe3e A |
5302 | { |
5303 | int i; | |
cc340f17 A |
5304 | |
5305 | // We ignore questions (if any) in a DNS response packet | |
5306 | const mDNSu8 *ptr = LocateAnswers(response, end); | |
5307 | ||
5308 | // "(CacheRecord*)1" is a special (non-zero) end-of-list marker | |
5309 | // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList | |
5310 | // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. | |
5311 | CacheRecord *CacheFlushRecords = (CacheRecord*)1; | |
c9b9ae52 | 5312 | CacheRecord **cfp = &CacheFlushRecords; |
7f0064bd | 5313 | |
6528fe3e | 5314 | // All records in a DNS response packet are treated as equally valid statements of truth. If we want |
c9b9ae52 | 5315 | // to guard against spoof responses, then the only credible protection against that is cryptographic |
6528fe3e A |
5316 | // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record |
5317 | int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals; | |
5318 | ||
c9b9ae52 | 5319 | (void)srcaddr; // Currently used only for display in debugging message |
7f0064bd A |
5320 | (void)srcport; |
5321 | (void)dstport; | |
c9b9ae52 | 5322 | |
c9d2d929 | 5323 | verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", |
7f0064bd | 5324 | srcaddr, dstaddr, InterfaceID, |
c9b9ae52 A |
5325 | response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", |
5326 | response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", | |
5327 | response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", | |
6528fe3e A |
5328 | response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); |
5329 | ||
7f0064bd A |
5330 | // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us |
5331 | if (!mDNSAddrIsDNSMulticast(dstaddr)) | |
283ee3ff | 5332 | { |
7f0064bd A |
5333 | if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2)) |
5334 | return; | |
283ee3ff A |
5335 | // For now we don't put standard wide-area unicast responses in our main cache |
5336 | // (Later we should fix this and cache all known results in a unified manner.) | |
5337 | if (response->h.id.NotAnInteger != 0 || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger) | |
5338 | return; | |
5339 | } | |
6528fe3e A |
5340 | |
5341 | for (i = 0; i < totalrecords && ptr && ptr < end; i++) | |
5342 | { | |
c9b9ae52 | 5343 | const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); |
7f0064bd A |
5344 | ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); |
5345 | if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting | |
6528fe3e A |
5346 | |
5347 | // 1. Check that this packet resource record does not conflict with any of ours | |
c9b9ae52 | 5348 | if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); |
6528fe3e A |
5349 | m->CurrentRecord = m->ResourceRecords; |
5350 | while (m->CurrentRecord) | |
5351 | { | |
c9b9ae52 | 5352 | AuthRecord *rr = m->CurrentRecord; |
6528fe3e | 5353 | m->CurrentRecord = rr->next; |
7f0064bd | 5354 | if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... |
c9b9ae52 | 5355 | { |
7f0064bd A |
5356 | // ... check to see if type and rdata are identical |
5357 | if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec)) | |
6528fe3e A |
5358 | { |
5359 | // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us | |
7f0064bd | 5360 | if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) |
c9b9ae52 A |
5361 | { |
5362 | // If we were planning to send on this -- and only this -- interface, then we don't need to any more | |
7f0064bd | 5363 | if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; } |
c9b9ae52 | 5364 | } |
6528fe3e | 5365 | else |
c9b9ae52 A |
5366 | { |
5367 | if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } | |
5368 | else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } | |
5369 | } | |
6528fe3e | 5370 | } |
7f0064bd A |
5371 | // else, the packet RR has different type or different rdata -- check to see if this is a conflict |
5372 | else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) | |
6528fe3e | 5373 | { |
7cb34e5c A |
5374 | debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr)); |
5375 | debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); | |
c9b9ae52 | 5376 | |
7f0064bd A |
5377 | // If this record is marked DependentOn another record for conflict detection purposes, |
5378 | // then *that* record has to be bumped back to probing state to resolve the conflict | |
5379 | while (rr->DependentOn) rr = rr->DependentOn; | |
c9b9ae52 | 5380 | |
7f0064bd A |
5381 | // If we've just whacked this record's ProbeCount, don't need to do it again |
5382 | if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) | |
5383 | { | |
5384 | // If we'd previously verified this record, put it back to probing state and try again | |
5385 | if (rr->resrec.RecordType == kDNSRecordTypeVerified) | |
6528fe3e | 5386 | { |
283ee3ff | 5387 | debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd A |
5388 | rr->resrec.RecordType = kDNSRecordTypeUnique; |
5389 | rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; | |
5390 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); | |
5391 | InitializeLastAPTime(m, rr); | |
5392 | RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate | |
5393 | } | |
5394 | // If we're probing for this record, we just failed | |
5395 | else if (rr->resrec.RecordType == kDNSRecordTypeUnique) | |
5396 | { | |
283ee3ff | 5397 | debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd A |
5398 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); |
5399 | } | |
5400 | // We assumed this record must be unique, but we were wrong. | |
5401 | // (e.g. There are two mDNSResponders on the same machine giving | |
5402 | // different answers for the reverse mapping record.) | |
5403 | // This is simply a misconfiguration, and we don't try to recover from it. | |
5404 | else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) | |
5405 | { | |
5406 | debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", | |
283ee3ff | 5407 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd | 5408 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); |
6528fe3e | 5409 | } |
7f0064bd A |
5410 | else |
5411 | debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", | |
283ee3ff | 5412 | rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e A |
5413 | } |
5414 | } | |
7f0064bd A |
5415 | // Else, matching signature, different type or rdata, but not a considered a conflict. |
5416 | // If the packet record has the cache-flush bit set, then we check to see if we | |
5417 | // have any record(s) of the same type that we should re-assert to rescue them | |
5418 | // (see note about "multi-homing and bridged networks" at the end of this function). | |
5419 | else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) | |
5420 | if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) | |
5421 | { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } | |
6528fe3e A |
5422 | } |
5423 | } | |
5424 | ||
5425 | // 2. See if we want to add this packet resource record to our cache | |
5426 | if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in | |
5427 | { | |
283ee3ff A |
5428 | const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); |
5429 | CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); | |
c9b9ae52 | 5430 | CacheRecord *rr; |
6528fe3e | 5431 | // 2a. Check if this packet resource record is already in our cache |
283ee3ff | 5432 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
6528fe3e A |
5433 | { |
5434 | // If we found this exact resource record, refresh its TTL | |
7f0064bd | 5435 | if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) |
6528fe3e | 5436 | { |
7f0064bd | 5437 | if (m->rec.r.resrec.rdlength > InlineCacheRDSize) |
c9b9ae52 | 5438 | verbosedebugf("Found record size %5d interface %p already in cache: %s", |
7f0064bd | 5439 | m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); |
c9b9ae52 A |
5440 | rr->TimeRcvd = m->timenow; |
5441 | ||
7f0064bd | 5442 | if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) |
c9b9ae52 A |
5443 | { |
5444 | // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list | |
5445 | if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList) | |
283ee3ff | 5446 | { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } |
c9b9ae52 A |
5447 | |
5448 | // If this packet record is marked unique, and our previous cached copy was not, then fix it | |
5449 | if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) | |
5450 | { | |
5451 | DNSQuestion *q; | |
5452 | for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; | |
7f0064bd | 5453 | rr->resrec.RecordType = m->rec.r.resrec.RecordType; |
c9b9ae52 A |
5454 | } |
5455 | } | |
5456 | ||
7cb34e5c A |
5457 | if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength)) |
5458 | { | |
5459 | // If the rdata of the packet record differs in name capitalization from the record in our cache | |
5460 | // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get | |
5461 | // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. | |
5462 | rr->resrec.rroriginalttl = 0; | |
5463 | rr->UnansweredQueries = MaxUnansweredQueries; | |
5464 | SetNextCacheCheckTime(m, rr); | |
5465 | // DO NOT break out here -- we want to continue as if we never found it | |
5466 | } | |
5467 | else if (m->rec.r.resrec.rroriginalttl > 0) | |
c9b9ae52 | 5468 | { |
7f0064bd | 5469 | rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl; |
c9b9ae52 A |
5470 | rr->UnansweredQueries = 0; |
5471 | rr->MPUnansweredQ = 0; | |
5472 | rr->MPUnansweredKA = 0; | |
5473 | rr->MPExpectingKA = mDNSfalse; | |
7cb34e5c A |
5474 | SetNextCacheCheckTime(m, rr); |
5475 | break; | |
c9b9ae52 A |
5476 | } |
5477 | else | |
5478 | { | |
5479 | // If the packet TTL is zero, that means we're deleting this record. | |
5480 | // To give other hosts on the network a chance to protest, we push the deletion | |
5481 | // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. | |
5482 | // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent | |
5483 | // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. | |
5484 | rr->resrec.rroriginalttl = 1; | |
5485 | rr->UnansweredQueries = MaxUnansweredQueries; | |
7cb34e5c A |
5486 | SetNextCacheCheckTime(m, rr); |
5487 | break; | |
c9b9ae52 | 5488 | } |
6528fe3e A |
5489 | } |
5490 | } | |
5491 | ||
5492 | // If packet resource record not in our cache, add it now | |
5493 | // (unless it is just a deletion of a record we never had, in which case we don't care) | |
7f0064bd | 5494 | if (!rr && m->rec.r.resrec.rroriginalttl > 0) |
6528fe3e | 5495 | { |
283ee3ff A |
5496 | // If we don't have a CacheGroup for this name, make one now |
5497 | if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); | |
5498 | if (cg) rr = GetCacheRecord(m, cg, m->rec.r.resrec.rdlength); // Make a cache record, being careful not to recycle cg | |
5499 | if (!rr) NoCacheAnswer(m, &m->rec.r); | |
6528fe3e A |
5500 | else |
5501 | { | |
c9b9ae52 | 5502 | RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer |
283ee3ff A |
5503 | *rr = m->rec.r; // Block copy the CacheRecord object |
5504 | rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment | |
5505 | rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header | |
c9b9ae52 | 5506 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) |
283ee3ff | 5507 | { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } |
c9b9ae52 | 5508 | // If this is an oversized record with external storage allocated, copy rdata to external storage |
283ee3ff A |
5509 | if (rr->resrec.rdata != (RData*)&rr->rdatastorage && !(m->rec.r.resrec.rdlength > InlineCacheRDSize)) |
5510 | LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c); | |
7f0064bd A |
5511 | if (m->rec.r.resrec.rdlength > InlineCacheRDSize) |
5512 | mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength); | |
716635cc | 5513 | rr->next = mDNSNULL; // Clear 'next' pointer |
283ee3ff A |
5514 | *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list |
5515 | cg->rrcache_tail = &(rr->next); // Advance tail pointer | |
7f0064bd A |
5516 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have |
5517 | rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event | |
5518 | else | |
283ee3ff | 5519 | rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot); |
cc340f17 | 5520 | CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us |
6528fe3e A |
5521 | } |
5522 | } | |
5523 | } | |
7f0064bd | 5524 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
6528fe3e A |
5525 | } |
5526 | ||
7f0064bd A |
5527 | exit: |
5528 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it | |
5529 | ||
c9b9ae52 A |
5530 | // If we've just received one or more records with their cache flush bits set, |
5531 | // then scan that cache slot to see if there are any old stale records we need to flush | |
283ee3ff | 5532 | while (CacheFlushRecords != (CacheRecord*)1) |
6528fe3e | 5533 | { |
c9b9ae52 | 5534 | CacheRecord *r1 = CacheFlushRecords, *r2; |
283ee3ff A |
5535 | const mDNSu32 slot = HashSlot(r1->resrec.name); |
5536 | CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec); | |
c9b9ae52 A |
5537 | CacheFlushRecords = CacheFlushRecords->NextInCFList; |
5538 | r1->NextInCFList = mDNSNULL; | |
283ee3ff A |
5539 | for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) |
5540 | if (SameResourceRecordSignature(&r1->resrec, &r2->resrec)) | |
cc340f17 | 5541 | if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) |
283ee3ff | 5542 | { |
cc340f17 A |
5543 | // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) |
5544 | // else, if record is old, mark it to be flushed | |
5545 | if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond) | |
5546 | { | |
5547 | if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) | |
5548 | r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; | |
5549 | } | |
5550 | else // else, if record is old, mark it to be flushed | |
5551 | { | |
5552 | verbosedebugf("Cache flush %p X %p %s", r1, r2, CRDisplayString(m, r2)); | |
5553 | // We set stale records to expire in one second. | |
5554 | // This gives the owner a chance to rescue it if necessary. | |
5555 | // This is important in the case of multi-homing and bridged networks: | |
5556 | // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be | |
5557 | // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit | |
5558 | // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet | |
5559 | // will promptly delete their cached copies of the (still valid) Ethernet IP address record. | |
5560 | // By delaying the deletion by one second, we give X a change to notice that this bridging has | |
5561 | // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. | |
5562 | // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary | |
5563 | // final expiration queries for this record. | |
5564 | r2->resrec.rroriginalttl = 1; | |
5565 | r2->UnansweredQueries = MaxUnansweredQueries; | |
5566 | } | |
5567 | r2->TimeRcvd = m->timenow; | |
c9d2d929 | 5568 | SetNextCacheCheckTime(m, r2); |
283ee3ff | 5569 | } |
7f0064bd A |
5570 | if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to |
5571 | { | |
cc340f17 | 5572 | // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared |
283ee3ff | 5573 | r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot); |
7f0064bd A |
5574 | if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); |
5575 | } | |
6528fe3e A |
5576 | } |
5577 | } | |
5578 | ||
7f0064bd | 5579 | mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, |
c9b9ae52 | 5580 | const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, |
7f0064bd | 5581 | const mDNSInterfaceID InterfaceID) |
6528fe3e | 5582 | { |
7f0064bd A |
5583 | DNSMessage *msg = (DNSMessage *)pkt; |
5584 | const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; | |
5585 | const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; | |
5586 | mDNSu8 QR_OP; | |
5587 | mDNSu8 *ptr = mDNSNULL; | |
283ee3ff | 5588 | const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; |
7f0064bd A |
5589 | |
5590 | #ifndef UNICAST_DISABLED | |
cc340f17 | 5591 | if (srcport.NotAnInteger == NATPMPPort.NotAnInteger) |
7f0064bd A |
5592 | { |
5593 | mDNS_Lock(m); | |
5594 | uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); | |
5595 | mDNS_Unlock(m); | |
5596 | return; | |
5597 | } | |
5598 | #endif | |
5599 | if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } | |
5600 | QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); | |
6528fe3e | 5601 | // Read the integer parts which are in IETF byte-order (MSB first, LSB second) |
7f0064bd | 5602 | ptr = (mDNSu8 *)&msg->h.numQuestions; |
6528fe3e A |
5603 | msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); |
5604 | msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); | |
5605 | msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); | |
5606 | msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); | |
8e92c31c | 5607 | |
c9b9ae52 A |
5608 | if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } |
5609 | ||
5610 | // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" | |
5611 | // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up | |
5612 | if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } | |
8e92c31c | 5613 | |
6528fe3e | 5614 | mDNS_Lock(m); |
7f0064bd A |
5615 | m->PktNum++; |
5616 | #ifndef UNICAST_DISABLED | |
5617 | if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR)) | |
5618 | uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); | |
5619 | // Note: mDNSCore also needs to get access to received unicast responses | |
5620 | #endif | |
c9b9ae52 | 5621 | if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); |
7f0064bd A |
5622 | else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); |
5623 | else if (QR_OP != UpdateR) | |
5624 | LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)", | |
5625 | msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID); | |
6528fe3e A |
5626 | |
5627 | // Packet reception often causes a change to the task list: | |
5628 | // 1. Inbound queries can cause us to need to send responses | |
5629 | // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses | |
5630 | // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records | |
5631 | // 4. Response packets that answer questions may cause our client to issue new questions | |
5632 | mDNS_Unlock(m); | |
5633 | } | |
5634 | ||
5635 | // *************************************************************************** | |
c9b9ae52 | 5636 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
5637 | #pragma mark - |
5638 | #pragma mark - | |
5639 | #pragma mark - Searcher Functions | |
5640 | #endif | |
5641 | ||
8e92c31c A |
5642 | #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger) |
5643 | ||
6528fe3e A |
5644 | mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question) |
5645 | { | |
5646 | DNSQuestion *q; | |
4e28aad3 A |
5647 | // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list. |
5648 | // This prevents circular references, where two questions are each marked as a duplicate of the other. | |
5649 | // Accordingly, we break out of the loop when we get to 'question', because there's no point searching | |
5650 | // further in the list. | |
c9b9ae52 A |
5651 | for (q = m->Questions; q && q != question; q=q->next) // Scan our list of questions |
5652 | if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID, | |
8e92c31c | 5653 | SameQTarget(q, question) && // and same unicast/multicast target settings |
c9b9ae52 A |
5654 | q->qtype == question->qtype && // type, |
5655 | q->qclass == question->qclass && // class, | |
5656 | q->qnamehash == question->qnamehash && | |
5657 | SameDomainName(&q->qname, &question->qname)) // and name | |
4e28aad3 | 5658 | return(q); |
6528fe3e A |
5659 | return(mDNSNULL); |
5660 | } | |
5661 | ||
5662 | // This is called after a question is deleted, in case other identical questions were being | |
5663 | // suppressed as duplicates | |
c9b9ae52 | 5664 | mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question) |
6528fe3e A |
5665 | { |
5666 | DNSQuestion *q; | |
c9b9ae52 | 5667 | for (q = m->Questions; q; q=q->next) // Scan our list of questions |
6528fe3e A |
5668 | if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate |
5669 | { | |
7f0064bd | 5670 | q->ThisQInterval = question->ThisQInterval; |
283ee3ff | 5671 | q->RequestUnicast = question->RequestUnicast; |
7f0064bd A |
5672 | q->LastQTime = question->LastQTime; |
5673 | q->RecentAnswerPkts = 0; | |
5674 | q->DuplicateOf = FindDuplicateQuestion(m, q); | |
5675 | q->LastQTxTime = question->LastQTxTime; | |
c9b9ae52 | 5676 | SetNextQueryTime(m,q); |
6528fe3e A |
5677 | } |
5678 | } | |
5679 | ||
8e92c31c A |
5680 | #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ |
5681 | ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger)) | |
5682 | ||
c9b9ae52 | 5683 | mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) |
6528fe3e | 5684 | { |
8e92c31c A |
5685 | if (question->Target.type && !ValidQuestionTarget(question)) |
5686 | { | |
7f0064bd | 5687 | LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", |
8e92c31c A |
5688 | question->Target.type, mDNSVal16(question->TargetPort)); |
5689 | question->Target.type = mDNSAddrType_None; | |
5690 | } | |
5691 | ||
7f0064bd A |
5692 | if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID |
5693 | { | |
5694 | question->TargetPort = zeroIPPort; | |
5695 | question->TargetQID = zeroID; | |
5696 | } | |
5697 | ||
5698 | #ifndef UNICAST_DISABLED | |
5699 | // If the client has specified 'kDNSServiceFlagsForceMulticast' | |
8e92c31c | 5700 | // then we do a multicast query on that interface, even for unicast domains. |
7f0064bd | 5701 | if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) |
8e92c31c A |
5702 | question->uDNS_info.id = zeroID; |
5703 | else return uDNS_StartQuery(m, question); | |
7f0064bd A |
5704 | #else |
5705 | question->uDNS_info.id = zeroID; | |
5706 | #endif // UNICAST_DISABLED | |
5707 | ||
5708 | //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); | |
5709 | ||
6528fe3e A |
5710 | if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated |
5711 | return(mStatus_NoCache); | |
5712 | else | |
5713 | { | |
c9b9ae52 A |
5714 | int i; |
5715 | // Note: It important that new questions are appended at the *end* of the list, not prepended at the start | |
5716 | DNSQuestion **q = &m->Questions; | |
8e92c31c | 5717 | if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; |
6528fe3e A |
5718 | while (*q && *q != question) q=&(*q)->next; |
5719 | ||
5720 | if (*q) | |
5721 | { | |
c9b9ae52 A |
5722 | LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list", |
5723 | question->qname.c, DNSTypeName(question->qtype)); | |
6528fe3e A |
5724 | return(mStatus_AlreadyRegistered); |
5725 | } | |
5726 | ||
c9d2d929 | 5727 | // If this question is referencing a specific interface, make sure it exists |
8e92c31c | 5728 | if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly) |
4e28aad3 | 5729 | { |
c9b9ae52 A |
5730 | NetworkInterfaceInfo *intf; |
5731 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
5732 | if (intf->InterfaceID == question->InterfaceID) break; | |
5733 | if (!intf) | |
c9d2d929 A |
5734 | { |
5735 | debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID); | |
5736 | return(mStatus_BadInterfaceErr); | |
5737 | } | |
4e28aad3 A |
5738 | } |
5739 | ||
c9b9ae52 A |
5740 | if (!ValidateDomainName(&question->qname)) |
5741 | { | |
28f7d060 | 5742 | LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); |
c9b9ae52 A |
5743 | return(mStatus_Invalid); |
5744 | } | |
5745 | ||
7f0064bd A |
5746 | // Note: In the case where we already have the answer to this question in our cache, that may be all the client |
5747 | // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would | |
5748 | // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds | |
5749 | // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. | |
c9b9ae52 A |
5750 | if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); |
5751 | ||
c9d2d929 A |
5752 | question->next = mDNSNULL; |
5753 | question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() | |
5754 | question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname)); | |
5755 | question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question | |
5756 | question->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it | |
5757 | question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization | |
5758 | question->LastAnswerPktNum = m->PktNum; | |
5759 | question->RecentAnswerPkts = 0; | |
5760 | question->CurrentAnswers = 0; | |
5761 | question->LargeAnswers = 0; | |
5762 | question->UniqueAnswers = 0; | |
5763 | question->DuplicateOf = FindDuplicateQuestion(m, question); | |
5764 | question->NextInDQList = mDNSNULL; | |
c9b9ae52 A |
5765 | for (i=0; i<DupSuppressInfoSize; i++) |
5766 | question->DupSuppress[i].InterfaceID = mDNSNULL; | |
5767 | // question->InterfaceID must be already set by caller | |
c9d2d929 A |
5768 | question->SendQNow = mDNSNULL; |
5769 | question->SendOnAll = mDNSfalse; | |
5770 | question->LastQTxTime = m->timenow; | |
c9b9ae52 A |
5771 | |
5772 | if (!question->DuplicateOf) | |
28f7d060 | 5773 | verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started", |
c9d2d929 | 5774 | question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question); |
c9b9ae52 | 5775 | else |
28f7d060 | 5776 | verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)", |
c9d2d929 | 5777 | question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf); |
c9b9ae52 | 5778 | |
6528fe3e | 5779 | *q = question; |
8e92c31c | 5780 | if (question->InterfaceID == mDNSInterface_LocalOnly) |
c9b9ae52 A |
5781 | { |
5782 | if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; | |
5783 | } | |
5784 | else | |
5785 | { | |
5786 | if (!m->NewQuestions) m->NewQuestions = question; | |
5787 | SetNextQueryTime(m,question); | |
5788 | } | |
6528fe3e A |
5789 | |
5790 | return(mStatus_NoError); | |
5791 | } | |
5792 | } | |
5793 | ||
c9b9ae52 | 5794 | mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) |
6528fe3e | 5795 | { |
283ee3ff A |
5796 | const mDNSu32 slot = HashSlot(&question->qname); |
5797 | CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); | |
c9b9ae52 A |
5798 | CacheRecord *rr; |
5799 | DNSQuestion **q = &m->Questions; | |
8e92c31c | 5800 | |
7f0064bd A |
5801 | if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); |
5802 | ||
8e92c31c | 5803 | if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; |
6528fe3e A |
5804 | while (*q && *q != question) q=&(*q)->next; |
5805 | if (*q) *q = (*q)->next; | |
c9b9ae52 A |
5806 | else |
5807 | { | |
5808 | if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active | |
5809 | LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", | |
5810 | question->qname.c, DNSTypeName(question->qtype)); | |
5811 | return(mStatus_BadReferenceErr); | |
5812 | } | |
6528fe3e | 5813 | |
c9b9ae52 | 5814 | // Take care to cut question from list *before* calling UpdateQuestionDuplicates |
6528fe3e | 5815 | UpdateQuestionDuplicates(m, question); |
c9b9ae52 | 5816 | // But don't trash ThisQInterval until afterwards. |
4e28aad3 | 5817 | question->ThisQInterval = -1; |
c9b9ae52 A |
5818 | |
5819 | // If there are any cache records referencing this as their active question, then see if any other | |
5820 | // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. | |
283ee3ff | 5821 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
c9b9ae52 A |
5822 | { |
5823 | if (rr->CRActiveQuestion == question) | |
5824 | { | |
5825 | DNSQuestion *q; | |
5826 | for (q = m->Questions; q; q=q->next) // Scan our list of questions | |
5827 | if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
5828 | break; | |
c9d2d929 | 5829 | verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q); |
c9b9ae52 A |
5830 | rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null |
5831 | if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count | |
5832 | } | |
5833 | } | |
5834 | ||
5835 | // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at, | |
6528fe3e A |
5836 | // bump its pointer forward one question. |
5837 | if (m->CurrentQuestion == question) | |
5838 | { | |
c9b9ae52 A |
5839 | debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", |
5840 | question->qname.c, DNSTypeName(question->qtype)); | |
5841 | m->CurrentQuestion = question->next; | |
6528fe3e A |
5842 | } |
5843 | ||
c9b9ae52 | 5844 | if (m->NewQuestions == question) |
6528fe3e | 5845 | { |
c9b9ae52 A |
5846 | debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", |
5847 | question->qname.c, DNSTypeName(question->qtype)); | |
5848 | m->NewQuestions = question->next; | |
6528fe3e | 5849 | } |
c9b9ae52 A |
5850 | |
5851 | if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; | |
5852 | ||
4e28aad3 A |
5853 | // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions |
5854 | question->next = mDNSNULL; | |
c9b9ae52 | 5855 | return(mStatus_NoError); |
6528fe3e A |
5856 | } |
5857 | ||
5858 | mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question) | |
5859 | { | |
c9b9ae52 A |
5860 | mStatus status; |
5861 | mDNS_Lock(m); | |
5862 | status = mDNS_StartQuery_internal(m, question); | |
5863 | mDNS_Unlock(m); | |
5864 | return(status); | |
5865 | } | |
5866 | ||
5867 | mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) | |
5868 | { | |
5869 | mStatus status; | |
5870 | mDNS_Lock(m); | |
5871 | status = mDNS_StopQuery_internal(m, question); | |
5872 | mDNS_Unlock(m); | |
5873 | return(status); | |
5874 | } | |
5875 | ||
5876 | mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr) | |
5877 | { | |
5878 | mStatus status; | |
5879 | mDNS_Lock(m); | |
5880 | status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); | |
6528fe3e A |
5881 | mDNS_Unlock(m); |
5882 | return(status); | |
5883 | } | |
5884 | ||
c9b9ae52 | 5885 | mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr) |
6528fe3e | 5886 | { |
c9b9ae52 A |
5887 | mStatus status = mStatus_BadReferenceErr; |
5888 | CacheRecord *cr; | |
6528fe3e | 5889 | mDNS_Lock(m); |
c9b9ae52 A |
5890 | cr = FindIdenticalRecordInCache(m, rr); |
5891 | if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); | |
6528fe3e | 5892 | mDNS_Unlock(m); |
c9b9ae52 | 5893 | return(status); |
6528fe3e A |
5894 | } |
5895 | ||
5896 | mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, | |
5897 | const domainname *const srv, const domainname *const domain, | |
7f0064bd | 5898 | const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) |
c9b9ae52 | 5899 | { |
8e92c31c A |
5900 | question->InterfaceID = InterfaceID; |
5901 | question->Target = zeroAddr; | |
5902 | question->qtype = kDNSType_PTR; | |
5903 | question->qclass = kDNSClass_IN; | |
7f0064bd A |
5904 | question->LongLived = mDNSfalse; |
5905 | question->ExpectUnique = mDNSfalse; | |
5906 | question->ForceMCast = ForceMCast; | |
8e92c31c A |
5907 | question->QuestionCallback = Callback; |
5908 | question->QuestionContext = Context; | |
c9b9ae52 | 5909 | if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); |
8e92c31c | 5910 | |
7f0064bd A |
5911 | #ifndef UNICAST_DISABLED |
5912 | if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) | |
8e92c31c A |
5913 | { |
5914 | question->LongLived = mDNSfalse; | |
5915 | question->uDNS_info.id = zeroID; | |
5916 | return(mDNS_StartQuery(m, question)); | |
5917 | } | |
5918 | else | |
5919 | { | |
7f0064bd A |
5920 | mStatus status; |
5921 | // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not | |
5922 | mDNS_Lock(m); | |
8e92c31c | 5923 | question->LongLived = mDNStrue; |
7f0064bd A |
5924 | status = uDNS_StartQuery(m, question); |
5925 | mDNS_Unlock(m); | |
5926 | return(status); | |
8e92c31c | 5927 | } |
7f0064bd A |
5928 | #else |
5929 | return(mDNS_StartQuery(m, question)); | |
5930 | #endif // UNICAST_DISABLED | |
5931 | } | |
5932 | ||
5933 | mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) | |
5934 | { | |
5935 | NetworkInterfaceInfo *intf; | |
5936 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
5937 | if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); | |
5938 | return(mDNSfalse); | |
6528fe3e A |
5939 | } |
5940 | ||
c9b9ae52 | 5941 | mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) |
6528fe3e | 5942 | { |
c9b9ae52 A |
5943 | ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; |
5944 | mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger); | |
5945 | if (!AddRecord) return; | |
6528fe3e A |
5946 | if (answer->rrtype != kDNSType_SRV) return; |
5947 | ||
5948 | query->info->port = answer->rdata->u.srv.port; | |
5949 | ||
5950 | // If this is our first answer, then set the GotSRV flag and start the address query | |
5951 | if (!query->GotSRV) | |
5952 | { | |
5953 | query->GotSRV = mDNStrue; | |
c9b9ae52 | 5954 | query->qAv4.InterfaceID = answer->InterfaceID; |
283ee3ff | 5955 | AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); |
c9b9ae52 | 5956 | query->qAv6.InterfaceID = answer->InterfaceID; |
283ee3ff | 5957 | AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); |
7f0064bd A |
5958 | mDNS_StartQuery(m, &query->qAv4); |
5959 | // Only do the AAAA query if this machine actually has IPv6 active | |
5960 | if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); | |
6528fe3e A |
5961 | } |
5962 | // If this is not our first answer, only re-issue the address query if the target host name has changed | |
c9b9ae52 A |
5963 | else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || |
5964 | !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) | |
5965 | { | |
7f0064bd A |
5966 | mDNS_StopQuery(m, &query->qAv4); |
5967 | if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); | |
c9b9ae52 A |
5968 | if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) |
5969 | { | |
5970 | // If we get here, it means: | |
5971 | // 1. This is not our first SRV answer | |
5972 | // 2. The interface ID is different, but the target host and port are the same | |
5973 | // This implies that we're seeing the exact same SRV record on more than one interface, so we should | |
5974 | // make our address queries at least as broad as the original SRV query so that we catch all the answers. | |
5975 | query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface | |
5976 | query->qAv6.InterfaceID = query->qSRV.InterfaceID; | |
5977 | } | |
5978 | else | |
5979 | { | |
5980 | query->qAv4.InterfaceID = answer->InterfaceID; | |
283ee3ff | 5981 | AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); |
c9b9ae52 | 5982 | query->qAv6.InterfaceID = answer->InterfaceID; |
283ee3ff | 5983 | AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); |
c9b9ae52 A |
5984 | } |
5985 | debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c); | |
7f0064bd A |
5986 | mDNS_StartQuery(m, &query->qAv4); |
5987 | // Only do the AAAA query if this machine actually has IPv6 active | |
5988 | if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); | |
c9b9ae52 A |
5989 | } |
5990 | else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) | |
6528fe3e | 5991 | { |
c9b9ae52 A |
5992 | if (++query->Answers >= 100) |
5993 | debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", | |
5994 | query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, | |
8e92c31c | 5995 | mDNSVal16(answer->rdata->u.srv.port)); |
c9b9ae52 | 5996 | query->ServiceInfoQueryCallback(m, query); |
6528fe3e | 5997 | } |
c9b9ae52 A |
5998 | // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's |
5999 | // callback function is allowed to do anything, including deleting this query and freeing its memory. | |
6528fe3e A |
6000 | } |
6001 | ||
c9b9ae52 | 6002 | mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) |
6528fe3e | 6003 | { |
c9b9ae52 A |
6004 | ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; |
6005 | if (!AddRecord) return; | |
6528fe3e | 6006 | if (answer->rrtype != kDNSType_TXT) return; |
c9b9ae52 | 6007 | if (answer->rdlength > sizeof(query->info->TXTinfo)) return; |
6528fe3e | 6008 | |
c9b9ae52 A |
6009 | query->GotTXT = mDNStrue; |
6010 | query->info->TXTlen = answer->rdlength; | |
8e92c31c | 6011 | query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero |
c9b9ae52 | 6012 | mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength); |
6528fe3e | 6013 | |
c9b9ae52 | 6014 | verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); |
6528fe3e | 6015 | |
c9b9ae52 A |
6016 | // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's |
6017 | // callback function is allowed to do anything, including deleting this query and freeing its memory. | |
6018 | if (query->ServiceInfoQueryCallback && query->GotADD) | |
6019 | { | |
6020 | if (++query->Answers >= 100) | |
6021 | debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", | |
6022 | query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); | |
6023 | query->ServiceInfoQueryCallback(m, query); | |
6024 | } | |
6528fe3e A |
6025 | } |
6026 | ||
c9b9ae52 | 6027 | mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) |
6528fe3e | 6028 | { |
c9b9ae52 | 6029 | ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; |
7f0064bd | 6030 | //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); |
c9b9ae52 A |
6031 | if (!AddRecord) return; |
6032 | ||
6033 | if (answer->rrtype == kDNSType_A) | |
6034 | { | |
6035 | query->info->ip.type = mDNSAddrType_IPv4; | |
7f0064bd | 6036 | query->info->ip.ip.v4 = answer->rdata->u.ipv4; |
c9b9ae52 A |
6037 | } |
6038 | else if (answer->rrtype == kDNSType_AAAA) | |
6039 | { | |
6040 | query->info->ip.type = mDNSAddrType_IPv6; | |
6041 | query->info->ip.ip.v6 = answer->rdata->u.ipv6; | |
6042 | } | |
6043 | else | |
6044 | { | |
283ee3ff | 6045 | debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); |
c9b9ae52 A |
6046 | return; |
6047 | } | |
6048 | ||
6528fe3e | 6049 | query->GotADD = mDNStrue; |
c9b9ae52 | 6050 | query->info->InterfaceID = answer->InterfaceID; |
6528fe3e | 6051 | |
7f0064bd | 6052 | verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); |
6528fe3e | 6053 | |
c9b9ae52 A |
6054 | // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's |
6055 | // callback function is allowed to do anything, including deleting this query and freeing its memory. | |
6056 | if (query->ServiceInfoQueryCallback && query->GotTXT) | |
6528fe3e | 6057 | { |
c9b9ae52 | 6058 | if (++query->Answers >= 100) |
c9d2d929 A |
6059 | { |
6060 | if (answer->rrtype == kDNSType_A) | |
6061 | debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv4); | |
6062 | else | |
6063 | debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6); | |
6064 | } | |
c9b9ae52 | 6065 | query->ServiceInfoQueryCallback(m, query); |
6528fe3e | 6066 | } |
6528fe3e A |
6067 | } |
6068 | ||
c9b9ae52 A |
6069 | // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure |
6070 | // If the query is not interface-specific, then InterfaceID may be zero | |
6528fe3e | 6071 | // Each time the Callback is invoked, the remainder of the fields will have been filled in |
c9b9ae52 | 6072 | // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response |
6528fe3e | 6073 | mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, |
c9b9ae52 | 6074 | ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) |
6528fe3e A |
6075 | { |
6076 | mStatus status; | |
c9b9ae52 A |
6077 | mDNS_Lock(m); |
6078 | ||
8e92c31c | 6079 | query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6080 | query->qSRV.InterfaceID = info->InterfaceID; |
8e92c31c | 6081 | query->qSRV.Target = zeroAddr; |
283ee3ff | 6082 | AssignDomainName(&query->qSRV.qname, &info->name); |
c9b9ae52 A |
6083 | query->qSRV.qtype = kDNSType_SRV; |
6084 | query->qSRV.qclass = kDNSClass_IN; | |
7f0064bd A |
6085 | query->qSRV.LongLived = mDNSfalse; |
6086 | query->qSRV.ExpectUnique = mDNStrue; | |
6087 | query->qSRV.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6088 | query->qSRV.QuestionCallback = FoundServiceInfoSRV; |
6089 | query->qSRV.QuestionContext = query; | |
6090 | ||
8e92c31c | 6091 | query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6092 | query->qTXT.InterfaceID = info->InterfaceID; |
8e92c31c | 6093 | query->qTXT.Target = zeroAddr; |
283ee3ff | 6094 | AssignDomainName(&query->qTXT.qname, &info->name); |
c9b9ae52 A |
6095 | query->qTXT.qtype = kDNSType_TXT; |
6096 | query->qTXT.qclass = kDNSClass_IN; | |
7f0064bd A |
6097 | query->qTXT.LongLived = mDNSfalse; |
6098 | query->qTXT.ExpectUnique = mDNStrue; | |
6099 | query->qTXT.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6100 | query->qTXT.QuestionCallback = FoundServiceInfoTXT; |
6101 | query->qTXT.QuestionContext = query; | |
6102 | ||
8e92c31c | 6103 | query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6104 | query->qAv4.InterfaceID = info->InterfaceID; |
8e92c31c | 6105 | query->qAv4.Target = zeroAddr; |
c9b9ae52 A |
6106 | query->qAv4.qname.c[0] = 0; |
6107 | query->qAv4.qtype = kDNSType_A; | |
6108 | query->qAv4.qclass = kDNSClass_IN; | |
7f0064bd A |
6109 | query->qAv4.LongLived = mDNSfalse; |
6110 | query->qAv4.ExpectUnique = mDNStrue; | |
6111 | query->qAv4.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6112 | query->qAv4.QuestionCallback = FoundServiceInfo; |
6113 | query->qAv4.QuestionContext = query; | |
6114 | ||
8e92c31c | 6115 | query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6116 | query->qAv6.InterfaceID = info->InterfaceID; |
8e92c31c | 6117 | query->qAv6.Target = zeroAddr; |
c9b9ae52 A |
6118 | query->qAv6.qname.c[0] = 0; |
6119 | query->qAv6.qtype = kDNSType_AAAA; | |
6120 | query->qAv6.qclass = kDNSClass_IN; | |
7f0064bd A |
6121 | query->qAv6.LongLived = mDNSfalse; |
6122 | query->qAv6.ExpectUnique = mDNStrue; | |
6123 | query->qAv6.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6124 | query->qAv6.QuestionCallback = FoundServiceInfo; |
6125 | query->qAv6.QuestionContext = query; | |
6126 | ||
6127 | query->GotSRV = mDNSfalse; | |
6128 | query->GotTXT = mDNSfalse; | |
6129 | query->GotADD = mDNSfalse; | |
6130 | query->Answers = 0; | |
6131 | ||
6132 | query->info = info; | |
6133 | query->ServiceInfoQueryCallback = Callback; | |
6134 | query->ServiceInfoQueryContext = Context; | |
6528fe3e A |
6135 | |
6136 | // info->name = Must already be set up by client | |
6137 | // info->interface = Must already be set up by client | |
c9b9ae52 | 6138 | info->ip = zeroAddr; |
6528fe3e A |
6139 | info->port = zeroIPPort; |
6140 | info->TXTlen = 0; | |
6141 | ||
7f0064bd | 6142 | // We use mDNS_StartQuery_internal here because we're already holding the lock |
c9b9ae52 A |
6143 | status = mDNS_StartQuery_internal(m, &query->qSRV); |
6144 | if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); | |
6528fe3e A |
6145 | if (status != mStatus_NoError) mDNS_StopResolveService(m, query); |
6146 | ||
6147 | mDNS_Unlock(m); | |
6148 | return(status); | |
6149 | } | |
6150 | ||
283ee3ff | 6151 | mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q) |
6528fe3e A |
6152 | { |
6153 | mDNS_Lock(m); | |
7f0064bd | 6154 | // We use mDNS_StopQuery_internal here because we're already holding the lock |
283ee3ff A |
6155 | if (q->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qSRV); |
6156 | if (q->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qTXT); | |
6157 | if (q->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv4); | |
6158 | if (q->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv6); | |
6528fe3e A |
6159 | mDNS_Unlock(m); |
6160 | } | |
6161 | ||
8e92c31c | 6162 | mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, |
c9b9ae52 | 6163 | const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) |
6528fe3e | 6164 | { |
c9b9ae52 | 6165 | question->InterfaceID = InterfaceID; |
8e92c31c | 6166 | question->Target = zeroAddr; |
c9b9ae52 A |
6167 | question->qtype = kDNSType_PTR; |
6168 | question->qclass = kDNSClass_IN; | |
7f0064bd A |
6169 | question->LongLived = mDNSfalse; |
6170 | question->ExpectUnique = mDNSfalse; | |
6171 | question->ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6172 | question->QuestionCallback = Callback; |
6173 | question->QuestionContext = Context; | |
283ee3ff | 6174 | if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); |
8e92c31c | 6175 | if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); |
7f0064bd | 6176 | if (!dom) dom = &localdomain; |
8e92c31c A |
6177 | if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); |
6178 | return(mDNS_StartQuery(m, question)); | |
6528fe3e A |
6179 | } |
6180 | ||
6181 | // *************************************************************************** | |
c9b9ae52 | 6182 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
6183 | #pragma mark - |
6184 | #pragma mark - Responder Functions | |
6185 | #endif | |
6186 | ||
c9b9ae52 | 6187 | mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) |
6528fe3e | 6188 | { |
c9b9ae52 A |
6189 | mStatus status; |
6190 | mDNS_Lock(m); | |
6191 | status = mDNS_Register_internal(m, rr); | |
6528fe3e A |
6192 | mDNS_Unlock(m); |
6193 | return(status); | |
6194 | } | |
6195 | ||
c9b9ae52 | 6196 | mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, |
7f0064bd | 6197 | const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) |
6528fe3e | 6198 | { |
7f0064bd | 6199 | #ifndef UNICAST_DISABLED |
283ee3ff | 6200 | mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name)); |
7f0064bd A |
6201 | #else |
6202 | mDNSBool unicast = mDNSfalse; | |
6203 | #endif | |
8e92c31c | 6204 | |
c9b9ae52 | 6205 | if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) |
c9d2d929 | 6206 | { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); } |
7f0064bd | 6207 | |
73792575 A |
6208 | mDNS_Lock(m); |
6209 | ||
c9b9ae52 A |
6210 | // If TTL is unspecified, leave TTL unchanged |
6211 | if (newttl == 0) newttl = rr->resrec.rroriginalttl; | |
6528fe3e A |
6212 | |
6213 | // If we already have an update queued up which has not gone through yet, | |
6214 | // give the client a chance to free that memory | |
7f0064bd | 6215 | if (!unicast && rr->NewRData) |
6528fe3e A |
6216 | { |
6217 | RData *n = rr->NewRData; | |
c9b9ae52 A |
6218 | rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... |
6219 | if (rr->UpdateCallback) | |
6220 | rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary | |
6221 | } | |
8e92c31c | 6222 | |
c9b9ae52 A |
6223 | rr->NewRData = newrdata; |
6224 | rr->newrdlength = newrdlength; | |
6225 | rr->UpdateCallback = Callback; | |
7f0064bd A |
6226 | |
6227 | if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); } | |
6228 | ||
c9d2d929 | 6229 | if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) |
8e92c31c A |
6230 | CompleteRDataUpdate(m, rr); |
6231 | else | |
c9b9ae52 | 6232 | { |
8e92c31c A |
6233 | domainlabel name; |
6234 | domainname type, domain; | |
283ee3ff | 6235 | DeconstructServiceName(rr->resrec.name, &name, &type, &domain); |
7f0064bd | 6236 | rr->AnnounceCount = InitialAnnounceCount; |
8e92c31c A |
6237 | // iChat often does suprious record updates where no data has changed. For the _presence service type, using |
6238 | // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful | |
6239 | // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data | |
6240 | // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us. | |
6241 | // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement. | |
6242 | if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1; | |
6243 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); | |
6244 | InitializeLastAPTime(m, rr); | |
6245 | while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); | |
6246 | if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; | |
7f0064bd | 6247 | if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); |
8e92c31c A |
6248 | if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); |
6249 | if (rr->UpdateCredits <= 5) | |
6250 | { | |
283ee3ff A |
6251 | mDNSu32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum |
6252 | if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond); | |
8e92c31c | 6253 | rr->ThisAPInterval *= 4; |
7f0064bd | 6254 | rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval; |
c9d2d929 | 6255 | LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name->c, delay, delay > 1 ? "s" : ""); |
8e92c31c A |
6256 | } |
6257 | rr->resrec.rroriginalttl = newttl; | |
c9b9ae52 | 6258 | } |
8e92c31c | 6259 | |
6528fe3e A |
6260 | mDNS_Unlock(m); |
6261 | return(mStatus_NoError); | |
6262 | } | |
6263 | ||
6264 | // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change | |
6265 | // the record list and/or question list. | |
6266 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 6267 | mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) |
6528fe3e | 6268 | { |
c9b9ae52 A |
6269 | mStatus status; |
6270 | mDNS_Lock(m); | |
6271 | status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); | |
6528fe3e | 6272 | mDNS_Unlock(m); |
c9b9ae52 A |
6273 | return(status); |
6274 | } | |
6275 | ||
8e92c31c | 6276 | mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); |
c9b9ae52 A |
6277 | |
6278 | mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) | |
6279 | { | |
6280 | NetworkInterfaceInfo *intf; | |
6281 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6282 | if (intf->Advertise) break; | |
6283 | return(intf); | |
6284 | } | |
6285 | ||
8e92c31c | 6286 | mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) |
c9b9ae52 A |
6287 | { |
6288 | char buffer[256]; | |
6289 | NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); | |
6290 | if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary | |
8e92c31c A |
6291 | |
6292 | // Send dynamic update for non-linklocal IPv4 Addresses | |
7f0064bd A |
6293 | mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set); |
6294 | mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); | |
6295 | mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); | |
6296 | ||
6297 | #if ANSWER_REMOTE_HOSTNAME_QUERIES | |
6298 | set->RR_A .AllowRemoteQuery = mDNStrue; | |
6299 | set->RR_PTR .AllowRemoteQuery = mDNStrue; | |
6300 | set->RR_HINFO.AllowRemoteQuery = mDNStrue; | |
6301 | #endif | |
c9b9ae52 A |
6302 | // 1. Set up Address record to map from host name ("foo.local.") to IP address |
6303 | // 2. Set up reverse-lookup PTR record to map from our address back to our host name | |
283ee3ff | 6304 | AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname); |
c9b9ae52 A |
6305 | if (set->ip.type == mDNSAddrType_IPv4) |
6306 | { | |
6307 | set->RR_A.resrec.rrtype = kDNSType_A; | |
7f0064bd | 6308 | set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; |
c9b9ae52 A |
6309 | // Note: This is reverse order compared to a normal dotted-decimal IP address |
6310 | mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", | |
6311 | set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); | |
6312 | } | |
6313 | else if (set->ip.type == mDNSAddrType_IPv6) | |
6314 | { | |
6315 | int i; | |
6316 | set->RR_A.resrec.rrtype = kDNSType_AAAA; | |
6317 | set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; | |
6318 | for (i = 0; i < 16; i++) | |
6319 | { | |
6320 | static const char hexValues[] = "0123456789ABCDEF"; | |
6321 | buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; | |
6322 | buffer[i * 4 + 1] = '.'; | |
6323 | buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; | |
6324 | buffer[i * 4 + 3] = '.'; | |
6325 | } | |
6326 | mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); | |
6327 | } | |
6328 | ||
283ee3ff | 6329 | MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer); |
c9b9ae52 | 6330 | set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name |
7f0064bd | 6331 | set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server |
c9b9ae52 A |
6332 | |
6333 | set->RR_A.RRSet = &primary->RR_A; // May refer to self | |
6334 | ||
6335 | mDNS_Register_internal(m, &set->RR_A); | |
6336 | mDNS_Register_internal(m, &set->RR_PTR); | |
6337 | ||
6338 | if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) | |
6339 | { | |
6340 | mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; | |
283ee3ff | 6341 | AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname); |
c9b9ae52 A |
6342 | set->RR_HINFO.DependentOn = &set->RR_A; |
6343 | mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]); | |
6344 | p += 1 + (int)p[0]; | |
6345 | mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]); | |
6346 | mDNS_Register_internal(m, &set->RR_HINFO); | |
6347 | } | |
6348 | else | |
6349 | { | |
6350 | debugf("Not creating HINFO record: platform support layer provided no information"); | |
6351 | set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; | |
6352 | } | |
6353 | } | |
6354 | ||
8e92c31c | 6355 | mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) |
c9b9ae52 A |
6356 | { |
6357 | NetworkInterfaceInfo *intf; | |
8e92c31c A |
6358 | |
6359 | // If we still have address records referring to this one, update them | |
c9b9ae52 A |
6360 | NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); |
6361 | AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; | |
6362 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6363 | if (intf->RR_A.RRSet == &set->RR_A) | |
6364 | intf->RR_A.RRSet = A; | |
6365 | ||
6366 | // Unregister these records. | |
8e92c31c | 6367 | // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform |
c9b9ae52 A |
6368 | // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. |
6369 | // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. | |
6370 | // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). | |
6371 | if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); | |
6372 | if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); | |
6373 | if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); | |
6528fe3e A |
6374 | } |
6375 | ||
7f0064bd | 6376 | mDNSexport void mDNS_SetFQDN(mDNS *const m) |
6528fe3e | 6377 | { |
7f0064bd A |
6378 | domainname newmname; |
6379 | NetworkInterfaceInfo *intf; | |
6380 | AuthRecord *rr; | |
6381 | newmname.c[0] = 0; | |
c9b9ae52 | 6382 | |
7f0064bd A |
6383 | if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } |
6384 | if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } | |
6385 | if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; } | |
c9b9ae52 | 6386 | |
7f0064bd | 6387 | mDNS_Lock(m); |
283ee3ff | 6388 | AssignDomainName(&m->MulticastHostname, &newmname); |
c9b9ae52 | 6389 | |
7f0064bd A |
6390 | // 1. Stop advertising our address records on all interfaces |
6391 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6392 | if (intf->Advertise) DeadvertiseInterface(m, intf); | |
6528fe3e | 6393 | |
7f0064bd A |
6394 | // 2. Start advertising our address records using the new name |
6395 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6396 | if (intf->Advertise) AdvertiseInterface(m, intf); | |
6528fe3e | 6397 | |
7f0064bd A |
6398 | // 3. Make sure that any SRV records (and the like) that reference our |
6399 | // host name in their rdata get updated to reference this new host name | |
6400 | for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); | |
6401 | for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); | |
6402 | ||
6403 | mDNS_Unlock(m); | |
8e92c31c | 6404 | } |
c9b9ae52 | 6405 | |
8e92c31c A |
6406 | mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
6407 | { | |
6408 | (void)rr; // Unused parameter | |
6409 | ||
c9b9ae52 | 6410 | #if MDNS_DEBUGMSGS |
6528fe3e | 6411 | { |
c9b9ae52 A |
6412 | char *msg = "Unknown result"; |
6413 | if (result == mStatus_NoError) msg = "Name registered"; | |
6414 | else if (result == mStatus_NameConflict) msg = "Name conflict"; | |
283ee3ff | 6415 | debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); |
6528fe3e | 6416 | } |
c9b9ae52 | 6417 | #endif |
6528fe3e | 6418 | |
c9b9ae52 A |
6419 | if (result == mStatus_NoError) |
6420 | { | |
6421 | // Notify the client that the host name is successfully registered | |
6422 | if (m->MainCallback) | |
8e92c31c A |
6423 | { |
6424 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 6425 | m->MainCallback(m, result); |
8e92c31c A |
6426 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
6427 | } | |
c9b9ae52 A |
6428 | } |
6429 | else if (result == mStatus_NameConflict) | |
6528fe3e | 6430 | { |
6528fe3e | 6431 | domainlabel oldlabel = m->hostlabel; |
7f0064bd | 6432 | |
c9b9ae52 A |
6433 | // 1. First give the client callback a chance to pick a new name |
6434 | if (m->MainCallback) | |
8e92c31c A |
6435 | { |
6436 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 6437 | m->MainCallback(m, mStatus_NameConflict); |
8e92c31c A |
6438 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
6439 | } | |
c9b9ae52 A |
6440 | |
6441 | // 2. If the client callback didn't do it, add (or increment) an index ourselves | |
6528fe3e A |
6442 | if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) |
6443 | IncrementLabelSuffix(&m->hostlabel, mDNSfalse); | |
8e92c31c | 6444 | |
c9b9ae52 A |
6445 | // 3. Generate the FQDNs from the hostlabel, |
6446 | // and make sure all SRV records, etc., are updated to reference our new hostname | |
7f0064bd A |
6447 | mDNS_SetFQDN(m); |
6448 | LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); | |
6449 | } | |
6450 | else if (result == mStatus_MemFree) | |
6451 | { | |
6452 | // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by | |
6453 | // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface | |
6454 | debugf("mDNS_HostNameCallback: MemFree (ignored)"); | |
6528fe3e | 6455 | } |
7f0064bd | 6456 | else |
283ee3ff | 6457 | LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c); |
6528fe3e A |
6458 | } |
6459 | ||
c9b9ae52 | 6460 | mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) |
6528fe3e | 6461 | { |
c9b9ae52 A |
6462 | NetworkInterfaceInfo *intf; |
6463 | active->IPv4Available = mDNSfalse; | |
6464 | active->IPv6Available = mDNSfalse; | |
6465 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6466 | if (intf->InterfaceID == active->InterfaceID) | |
6467 | { | |
8e92c31c A |
6468 | if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; |
6469 | if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; | |
c9b9ae52 | 6470 | } |
6528fe3e A |
6471 | } |
6472 | ||
c9d2d929 | 6473 | mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSs32 delay) |
6528fe3e | 6474 | { |
c9b9ae52 | 6475 | mDNSBool FirstOfType = mDNStrue; |
6528fe3e | 6476 | NetworkInterfaceInfo **p = &m->HostInterfaces; |
7f0064bd A |
6477 | |
6478 | if (!set->InterfaceID) | |
6479 | { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } | |
6480 | ||
6481 | if (!mDNSAddressIsValidNonZero(&set->mask)) | |
6482 | { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } | |
6483 | ||
c9b9ae52 | 6484 | mDNS_Lock(m); |
6528fe3e | 6485 | |
c9d2d929 | 6486 | // Assume this interface will be active |
c9b9ae52 | 6487 | set->InterfaceActive = mDNStrue; |
8e92c31c A |
6488 | set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); |
6489 | set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); | |
6528fe3e | 6490 | |
c9b9ae52 | 6491 | while (*p) |
6528fe3e | 6492 | { |
c9b9ae52 A |
6493 | if (*p == set) |
6494 | { | |
6495 | LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list"); | |
6496 | mDNS_Unlock(m); | |
6497 | return(mStatus_AlreadyRegistered); | |
6498 | } | |
6528fe3e | 6499 | |
c9d2d929 | 6500 | // This InterfaceID is already in the list, so mark this interface inactive for now |
c9b9ae52 | 6501 | if ((*p)->InterfaceID == set->InterfaceID) |
4e28aad3 | 6502 | { |
c9b9ae52 A |
6503 | set->InterfaceActive = mDNSfalse; |
6504 | if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; | |
8e92c31c A |
6505 | if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; |
6506 | if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; | |
4e28aad3 | 6507 | } |
c9b9ae52 A |
6508 | |
6509 | p=&(*p)->next; | |
6510 | } | |
4e28aad3 | 6511 | |
6528fe3e A |
6512 | set->next = mDNSNULL; |
6513 | *p = set; | |
c9b9ae52 | 6514 | |
283ee3ff A |
6515 | if (set->Advertise) |
6516 | AdvertiseInterface(m, set); | |
6517 | ||
c9d2d929 | 6518 | debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip, |
c9b9ae52 A |
6519 | set->InterfaceActive ? |
6520 | "not represented in list; marking active and retriggering queries" : | |
6521 | "already represented in list; marking inactive for now"); | |
8e92c31c | 6522 | |
c9d2d929 | 6523 | // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, |
c9b9ae52 A |
6524 | // giving the false impression that there's an active representative of this interface when there really isn't. |
6525 | // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, | |
6526 | // even if we believe that we previously had an active representative of this interface. | |
283ee3ff | 6527 | if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive)) |
6528fe3e | 6528 | { |
716635cc A |
6529 | DNSQuestion *q; |
6530 | AuthRecord *rr; | |
c9d2d929 | 6531 | mDNSs32 initial = InitialQuestionInterval; |
283ee3ff | 6532 | |
c9b9ae52 | 6533 | // Use a small amount of randomness: |
c9d2d929 A |
6534 | // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at |
6535 | // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment. | |
c9b9ae52 | 6536 | if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); |
283ee3ff | 6537 | |
c9d2d929 | 6538 | if (delay) |
283ee3ff | 6539 | { |
c9d2d929 A |
6540 | LogMsg("Repeated transitions for interface %s (%#a); delaying packets by %d seconds", |
6541 | set->ifname, &set->ip, delay/mDNSPlatformOneSecond); | |
6542 | initial = InitialQuestionInterval * 8; // Delay between first and second queries is eight seconds | |
283ee3ff A |
6543 | if (!m->SuppressProbes || |
6544 | m->SuppressProbes - (m->timenow + delay) < 0) | |
6545 | m->SuppressProbes = (m->timenow + delay); | |
6546 | } | |
c9b9ae52 A |
6547 | for (q = m->Questions; q; q=q->next) // Scan our list of questions |
6548 | if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, | |
6549 | { // then reactivate this question | |
c9d2d929 A |
6550 | q->ThisQInterval = initial; // MUST be > zero for an active question |
6551 | q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it | |
6552 | q->LastQTime = m->timenow - q->ThisQInterval + delay; | |
7f0064bd | 6553 | q->RecentAnswerPkts = 0; |
283ee3ff | 6554 | SetNextQueryTime(m,q); |
c9b9ae52 A |
6555 | } |
6556 | ||
6557 | // For all our non-specific authoritative resource records (and any dormant records specific to this interface) | |
6558 | // we now need them to re-probe if necessary, and then re-announce. | |
6559 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6560 | if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) | |
6561 | { | |
6562 | if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; | |
7f0064bd | 6563 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); |
c9d2d929 | 6564 | rr->AnnounceCount = delay ? (mDNSu8)1 : InitialAnnounceCount; |
7f0064bd | 6565 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); |
c9b9ae52 A |
6566 | InitializeLastAPTime(m, rr); |
6567 | } | |
6528fe3e A |
6568 | } |
6569 | ||
c9b9ae52 A |
6570 | mDNS_Unlock(m); |
6571 | return(mStatus_NoError); | |
6528fe3e A |
6572 | } |
6573 | ||
6574 | // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change | |
6575 | // the record list and/or question list. | |
6576 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9d2d929 | 6577 | mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set) |
6528fe3e A |
6578 | { |
6579 | NetworkInterfaceInfo **p = &m->HostInterfaces; | |
c9b9ae52 A |
6580 | |
6581 | mDNSBool revalidate = mDNSfalse; | |
6582 | // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every | |
6583 | // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it | |
6584 | // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion. | |
6585 | if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue; | |
6586 | ||
6587 | mDNS_Lock(m); | |
6528fe3e A |
6588 | |
6589 | // Find this record in our list | |
6590 | while (*p && *p != set) p=&(*p)->next; | |
c9b9ae52 | 6591 | if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } |
6528fe3e A |
6592 | |
6593 | // Unlink this record from our list | |
6594 | *p = (*p)->next; | |
6595 | set->next = mDNSNULL; | |
6596 | ||
c9b9ae52 A |
6597 | if (!set->InterfaceActive) |
6598 | { | |
6599 | // If this interface not the active member of its set, update the v4/v6Available flags for the active member | |
6600 | NetworkInterfaceInfo *intf; | |
6601 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6602 | if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) | |
6603 | UpdateInterfaceProtocols(m, intf); | |
6604 | } | |
6605 | else | |
6606 | { | |
6607 | NetworkInterfaceInfo *intf; | |
6608 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6609 | if (intf->InterfaceID == set->InterfaceID) | |
6610 | break; | |
6611 | if (intf) | |
6612 | { | |
c9d2d929 A |
6613 | debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active", |
6614 | set->InterfaceID); | |
c9b9ae52 A |
6615 | intf->InterfaceActive = mDNStrue; |
6616 | UpdateInterfaceProtocols(m, intf); | |
6617 | ||
6618 | // See if another representative *of the same type* exists. If not, we mave have gone from | |
6619 | // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. | |
6620 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6621 | if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) | |
6622 | break; | |
6623 | if (!intf) revalidate = mDNStrue; | |
6624 | } | |
6625 | else | |
6626 | { | |
283ee3ff A |
6627 | mDNSu32 slot; |
6628 | CacheGroup *cg; | |
c9b9ae52 A |
6629 | CacheRecord *rr; |
6630 | DNSQuestion *q; | |
c9d2d929 A |
6631 | debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant", |
6632 | set->InterfaceID); | |
619ee211 | 6633 | |
c9d2d929 | 6634 | // 1. Deactivate any questions specific to this interface |
c9b9ae52 | 6635 | for (q = m->Questions; q; q=q->next) |
c9d2d929 A |
6636 | if (q->InterfaceID == set->InterfaceID) |
6637 | q->ThisQInterval = 0; | |
c9b9ae52 A |
6638 | |
6639 | // 2. Flush any cache records received on this interface | |
6640 | revalidate = mDNSfalse; // Don't revalidate if we're flushing the records | |
283ee3ff A |
6641 | FORALL_CACHERECORDS(slot, cg, rr) |
6642 | if (rr->resrec.InterfaceID == set->InterfaceID) | |
c9d2d929 | 6643 | PurgeCacheResourceRecord(m, rr); |
c9b9ae52 A |
6644 | } |
6645 | } | |
6528fe3e | 6646 | |
c9b9ae52 | 6647 | // If we were advertising on this interface, deregister those address and reverse-lookup records now |
7f0064bd A |
6648 | if (set->Advertise) DeadvertiseInterface(m, set); |
6649 | ||
c9b9ae52 A |
6650 | // If we have any cache records received on this interface that went away, then re-verify them. |
6651 | // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, | |
6652 | // giving the false impression that there's an active representative of this interface when there really isn't. | |
6653 | // Don't need to do this when shutting down, because *all* interfaces are about to go away | |
6654 | if (revalidate && !m->mDNS_shutdown) | |
6655 | { | |
6656 | mDNSu32 slot; | |
283ee3ff | 6657 | CacheGroup *cg; |
c9b9ae52 A |
6658 | CacheRecord *rr; |
6659 | m->NextCacheCheck = m->timenow; | |
283ee3ff A |
6660 | FORALL_CACHERECORDS(slot, cg, rr) |
6661 | if (rr->resrec.InterfaceID == set->InterfaceID) | |
c9d2d929 | 6662 | mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect); |
c9b9ae52 | 6663 | } |
6528fe3e A |
6664 | |
6665 | mDNS_Unlock(m); | |
6666 | } | |
6667 | ||
c9b9ae52 | 6668 | mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
6528fe3e | 6669 | { |
c9b9ae52 A |
6670 | ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; |
6671 | (void)m; // Unused parameter | |
6528fe3e | 6672 | |
c9b9ae52 A |
6673 | #if MDNS_DEBUGMSGS |
6674 | { | |
6675 | char *msg = "Unknown result"; | |
6676 | if (result == mStatus_NoError) msg = "Name Registered"; | |
6677 | else if (result == mStatus_NameConflict) msg = "Name Conflict"; | |
6678 | else if (result == mStatus_MemFree) msg = "Memory Free"; | |
283ee3ff | 6679 | debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); |
6528fe3e | 6680 | } |
c9b9ae52 | 6681 | #endif |
6528fe3e | 6682 | |
7f0064bd A |
6683 | // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing) |
6684 | if (result == mStatus_NoError && rr != &sr->RR_SRV) return; | |
6685 | ||
6528fe3e | 6686 | // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that |
c9b9ae52 A |
6687 | if (result == mStatus_NameConflict) |
6688 | { | |
7f0064bd A |
6689 | sr->Conflict = mDNStrue; // Record that this service set had a conflict |
6690 | mDNS_DeregisterService(m, sr); // Unlink the records from our list | |
c9b9ae52 A |
6691 | return; |
6692 | } | |
6528fe3e | 6693 | |
c9b9ae52 A |
6694 | if (result == mStatus_MemFree) |
6695 | { | |
283ee3ff | 6696 | // If the PTR record or any of the subtype PTR records are still in the process of deregistering, |
c9b9ae52 A |
6697 | // don't pass on the NameConflict/MemFree message until every record is finished cleaning up. |
6698 | mDNSu32 i; | |
6699 | if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; | |
6700 | for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; | |
6701 | ||
6702 | // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, | |
6703 | // then we can now report the NameConflict to the client | |
6704 | if (sr->Conflict) result = mStatus_NameConflict; | |
6705 | } | |
6528fe3e | 6706 | |
c9b9ae52 A |
6707 | // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback |
6708 | // function is allowed to do anything, including deregistering this service and freeing its memory. | |
6709 | if (sr->ServiceCallback) | |
6710 | sr->ServiceCallback(m, sr, result); | |
6528fe3e A |
6711 | } |
6712 | ||
7f0064bd A |
6713 | mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
6714 | { | |
6715 | ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; | |
6716 | if (sr->ServiceCallback) | |
6717 | sr->ServiceCallback(m, sr, result); | |
6718 | } | |
6719 | ||
6528fe3e A |
6720 | // Note: |
6721 | // Name is first label of domain name (any dots in the name are actual dots, not label separators) | |
8e92c31c | 6722 | // Type is service type (e.g. "_ipp._tcp.") |
6528fe3e A |
6723 | // Domain is fully qualified domain name (i.e. ending with a null label) |
6724 | // We always register a TXT, even if it is empty (so that clients are not | |
6725 | // left waiting forever looking for a nonexistent record.) | |
c9b9ae52 | 6726 | // If the host parameter is mDNSNULL or the root domain (ASCII NUL), |
7f0064bd | 6727 | // then the default host name (m->MulticastHostname) is automatically used |
6528fe3e A |
6728 | mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, |
6729 | const domainlabel *const name, const domainname *const type, const domainname *const domain, | |
6730 | const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, | |
c9b9ae52 A |
6731 | AuthRecord *SubTypes, mDNSu32 NumSubTypes, |
6732 | const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context) | |
6528fe3e | 6733 | { |
c9b9ae52 A |
6734 | mStatus err; |
6735 | mDNSu32 i; | |
6528fe3e | 6736 | |
c9b9ae52 A |
6737 | sr->ServiceCallback = Callback; |
6738 | sr->ServiceContext = Context; | |
6739 | sr->Extras = mDNSNULL; | |
6740 | sr->NumSubTypes = NumSubTypes; | |
6741 | sr->SubTypes = SubTypes; | |
6742 | sr->Conflict = mDNSfalse; | |
6528fe3e A |
6743 | if (host && host->c[0]) sr->Host = *host; |
6744 | else sr->Host.c[0] = 0; | |
6745 | ||
7f0064bd | 6746 | // If port number is zero, that means the client is really trying to do a RegisterNoSuchService |
c9d2d929 | 6747 | if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr)); |
7f0064bd | 6748 | |
c9b9ae52 | 6749 | // Initialize the AuthRecord objects to sane values |
7f0064bd A |
6750 | mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr); |
6751 | mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); | |
6752 | mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr); | |
6753 | mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr); | |
8e92c31c | 6754 | |
6528fe3e A |
6755 | // If the client is registering an oversized TXT record, |
6756 | // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it | |
c9b9ae52 A |
6757 | if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) |
6758 | sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; | |
6759 | ||
6760 | // Set up the record names | |
6761 | // For now we only create an advisory record for the main type, not for subtypes | |
6762 | // We need to gain some operational experience before we decide if there's a need to create them for subtypes too | |
283ee3ff | 6763 | if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL) |
c9b9ae52 | 6764 | return(mStatus_BadParamErr); |
283ee3ff A |
6765 | if (ConstructServiceName(sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); |
6766 | if (ConstructServiceName(sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); | |
c9b9ae52 | 6767 | AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name); |
6528fe3e | 6768 | |
c9b9ae52 | 6769 | // 1. Set up the ADV record rdata to advertise our service type |
283ee3ff | 6770 | AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); |
c9b9ae52 A |
6771 | |
6772 | // 2. Set up the PTR record rdata to point to our service name | |
6528fe3e | 6773 | // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too |
283ee3ff | 6774 | AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); |
6528fe3e A |
6775 | sr->RR_PTR.Additional1 = &sr->RR_SRV; |
6776 | sr->RR_PTR.Additional2 = &sr->RR_TXT; | |
6777 | ||
c9b9ae52 A |
6778 | // 2a. Set up any subtype PTRs to point to our service name |
6779 | // If the client is using subtypes, it is the client's responsibility to have | |
6780 | // already set the first label of the record name to the subtype being registered | |
6781 | for (i=0; i<NumSubTypes; i++) | |
6782 | { | |
7f0064bd | 6783 | domainname st; |
283ee3ff A |
6784 | AssignDomainName(&st, sr->SubTypes[i].resrec.name); |
6785 | st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) | |
7f0064bd A |
6786 | AppendDomainName(&st, type); |
6787 | mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); | |
283ee3ff A |
6788 | if (ConstructServiceName(sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); |
6789 | AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name); | |
c9b9ae52 A |
6790 | sr->SubTypes[i].Additional1 = &sr->RR_SRV; |
6791 | sr->SubTypes[i].Additional2 = &sr->RR_TXT; | |
6792 | } | |
6793 | ||
6794 | // 3. Set up the SRV record rdata. | |
6795 | sr->RR_SRV.resrec.rdata->u.srv.priority = 0; | |
6796 | sr->RR_SRV.resrec.rdata->u.srv.weight = 0; | |
6797 | sr->RR_SRV.resrec.rdata->u.srv.port = port; | |
6528fe3e A |
6798 | |
6799 | // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name | |
283ee3ff | 6800 | if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host); |
8e92c31c | 6801 | else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } |
6528fe3e | 6802 | |
c9b9ae52 | 6803 | // 4. Set up the TXT record rdata, |
6528fe3e | 6804 | // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us |
c9b9ae52 A |
6805 | if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; |
6806 | else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) | |
6528fe3e | 6807 | { |
c9b9ae52 A |
6808 | sr->RR_TXT.resrec.rdlength = txtlen; |
6809 | if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); | |
6810 | mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen); | |
6528fe3e A |
6811 | } |
6812 | sr->RR_TXT.DependentOn = &sr->RR_SRV; | |
6813 | ||
7f0064bd | 6814 | #ifndef UNICAST_DISABLED |
8e92c31c | 6815 | // If the client has specified an explicit InterfaceID, |
28f7d060 | 6816 | // then we do a multicast registration on that interface, even for unicast domains. |
283ee3ff | 6817 | if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
7f0064bd A |
6818 | { |
6819 | mStatus status; | |
6820 | mDNS_Lock(m); | |
7cb34e5c A |
6821 | // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, |
6822 | // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". | |
6823 | // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. | |
7f0064bd A |
6824 | // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck) |
6825 | if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; } | |
6826 | status = uDNS_RegisterService(m, sr); | |
6827 | mDNS_Unlock(m); | |
6828 | return(status); | |
6829 | } | |
6830 | #endif | |
c9b9ae52 A |
6831 | mDNS_Lock(m); |
6832 | err = mDNS_Register_internal(m, &sr->RR_SRV); | |
6833 | if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); | |
6528fe3e A |
6834 | // We register the RR_PTR last, because we want to be sure that in the event of a forced call to |
6835 | // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers | |
6836 | // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to | |
6837 | // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to | |
6838 | // make sure we've deregistered all our records and done any other necessary cleanup before that happens. | |
c9b9ae52 A |
6839 | if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); |
6840 | for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]); | |
6841 | if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); | |
6528fe3e | 6842 | |
8e92c31c A |
6843 | mDNS_Unlock(m); |
6844 | ||
c9b9ae52 A |
6845 | if (err) mDNS_DeregisterService(m, sr); |
6846 | return(err); | |
6528fe3e A |
6847 | } |
6848 | ||
c9b9ae52 A |
6849 | mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, |
6850 | ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) | |
6528fe3e | 6851 | { |
8e92c31c A |
6852 | ExtraResourceRecord **e; |
6853 | mStatus status; | |
6854 | ||
7f0064bd | 6855 | extra->next = mDNSNULL; |
c9d2d929 | 6856 | mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); |
7f0064bd A |
6857 | AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); |
6858 | ||
6859 | #ifndef UNICAST_DISABLED | |
283ee3ff | 6860 | if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
8e92c31c | 6861 | { |
7f0064bd | 6862 | mDNS_Lock(m); |
7cb34e5c A |
6863 | // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, |
6864 | // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". | |
6865 | // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. | |
7f0064bd A |
6866 | // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck) |
6867 | if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0) | |
6868 | { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; } | |
6869 | status = uDNS_AddRecordToService(m, sr, extra); | |
6870 | mDNS_Unlock(m); | |
6871 | return status; | |
8e92c31c | 6872 | } |
7f0064bd A |
6873 | #endif |
6874 | ||
8e92c31c A |
6875 | mDNS_Lock(m); |
6876 | e = &sr->Extras; | |
6528fe3e A |
6877 | while (*e) e = &(*e)->next; |
6878 | ||
7f0064bd | 6879 | if (ttl == 0) ttl = kStandardTTL; |
6528fe3e | 6880 | |
6528fe3e A |
6881 | extra->r.DependentOn = &sr->RR_SRV; |
6882 | ||
283ee3ff | 6883 | debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c); |
6528fe3e | 6884 | |
8e92c31c A |
6885 | status = mDNS_Register_internal(m, &extra->r); |
6886 | if (status == mStatus_NoError) *e = extra; | |
6887 | mDNS_Unlock(m); | |
6888 | return(status); | |
6528fe3e A |
6889 | } |
6890 | ||
c9d2d929 | 6891 | mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context) |
6528fe3e | 6892 | { |
8e92c31c A |
6893 | ExtraResourceRecord **e; |
6894 | mStatus status; | |
6895 | ||
8e92c31c A |
6896 | mDNS_Lock(m); |
6897 | e = &sr->Extras; | |
6528fe3e A |
6898 | while (*e && *e != extra) e = &(*e)->next; |
6899 | if (!*e) | |
6900 | { | |
283ee3ff | 6901 | debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c); |
8e92c31c | 6902 | status = mStatus_BadReferenceErr; |
6528fe3e | 6903 | } |
8e92c31c A |
6904 | else |
6905 | { | |
283ee3ff | 6906 | debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c); |
7f0064bd A |
6907 | extra->r.RecordCallback = MemFreeCallback; |
6908 | extra->r.RecordContext = Context; | |
8e92c31c | 6909 | *e = (*e)->next; |
7f0064bd | 6910 | #ifndef UNICAST_DISABLED |
283ee3ff | 6911 | if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
7f0064bd A |
6912 | status = uDNS_DeregisterRecord(m, &extra->r); |
6913 | else | |
6914 | #endif | |
8e92c31c A |
6915 | status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); |
6916 | } | |
6917 | mDNS_Unlock(m); | |
6918 | return(status); | |
6528fe3e A |
6919 | } |
6920 | ||
377735b0 | 6921 | mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) |
6528fe3e | 6922 | { |
8e92c31c A |
6923 | // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines |
6924 | // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. | |
6925 | domainlabel name1, name2; | |
6528fe3e A |
6926 | domainname type, domain; |
6927 | domainname *host = mDNSNULL; | |
6928 | ExtraResourceRecord *extras = sr->Extras; | |
6929 | mStatus err; | |
6930 | ||
283ee3ff | 6931 | DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain); |
377735b0 A |
6932 | if (!newname) |
6933 | { | |
8e92c31c A |
6934 | name2 = name1; |
6935 | IncrementLabelSuffix(&name2, mDNStrue); | |
6936 | newname = &name2; | |
377735b0 | 6937 | } |
c9d2d929 | 6938 | LogMsg("Service \"%##s\" renamed to \"%#s\"", sr->RR_SRV.resrec.name->c, newname->c); |
6528fe3e A |
6939 | if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host; |
6940 | ||
377735b0 | 6941 | err = mDNS_RegisterService(m, sr, newname, &type, &domain, |
c9b9ae52 A |
6942 | host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, |
6943 | sr->SubTypes, sr->NumSubTypes, | |
6944 | sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext); | |
6528fe3e | 6945 | |
c9b9ae52 A |
6946 | // mDNS_RegisterService() just reset sr->Extras to NULL. |
6947 | // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run | |
6948 | // through the old list of extra records, and re-add them to our freshly created service registration | |
6528fe3e A |
6949 | while (!err && extras) |
6950 | { | |
6951 | ExtraResourceRecord *e = extras; | |
6952 | extras = extras->next; | |
c9b9ae52 | 6953 | err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl); |
6528fe3e A |
6954 | } |
6955 | ||
6956 | return(err); | |
6957 | } | |
6958 | ||
6959 | // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, | |
6960 | // which may change the record list and/or question list. | |
6961 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 6962 | mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) |
6528fe3e | 6963 | { |
7f0064bd A |
6964 | // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() |
6965 | if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); | |
8e92c31c | 6966 | |
7f0064bd | 6967 | #ifndef UNICAST_DISABLED |
283ee3ff | 6968 | if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
7f0064bd A |
6969 | { |
6970 | mStatus status; | |
6971 | mDNS_Lock(m); | |
6972 | status = uDNS_DeregisterService(m, sr); | |
6973 | mDNS_Unlock(m); | |
6974 | return(status); | |
6975 | } | |
6976 | #endif | |
c9b9ae52 A |
6977 | if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) |
6978 | { | |
283ee3ff | 6979 | debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); |
c9b9ae52 A |
6980 | return(mStatus_BadReferenceErr); |
6981 | } | |
6982 | else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) | |
6528fe3e | 6983 | { |
283ee3ff | 6984 | debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); |
c9b9ae52 | 6985 | return(mStatus_NoError); |
6528fe3e | 6986 | } |
c9b9ae52 A |
6987 | else |
6988 | { | |
6989 | mDNSu32 i; | |
6990 | mStatus status; | |
6991 | ExtraResourceRecord *e; | |
6992 | mDNS_Lock(m); | |
6993 | e = sr->Extras; | |
6994 | ||
6995 | // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the | |
6996 | // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay | |
6997 | mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); | |
6998 | mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); | |
6999 | ||
7000 | mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal); | |
7001 | ||
7002 | // We deregister all of the extra records, but we leave the sr->Extras list intact | |
7003 | // in case the client wants to do a RenameAndReregister and reinstate the registration | |
7004 | while (e) | |
7005 | { | |
7006 | mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); | |
7007 | e = e->next; | |
7008 | } | |
6528fe3e | 7009 | |
c9b9ae52 A |
7010 | for (i=0; i<sr->NumSubTypes; i++) |
7011 | mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal); | |
6528fe3e | 7012 | |
c9b9ae52 A |
7013 | // Be sure to deregister the PTR last! |
7014 | // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback, | |
7015 | // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback, | |
7016 | // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure | |
7017 | // we've deregistered all our records and done any other necessary cleanup before that happens. | |
7018 | status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal); | |
7019 | mDNS_Unlock(m); | |
7020 | return(status); | |
7021 | } | |
7022 | } | |
7023 | ||
7024 | // Create a registration that asserts that no such service exists with this name. | |
7025 | // This can be useful where there is a given function is available through several protocols. | |
7026 | // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP" | |
7027 | // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an | |
7028 | // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing | |
7029 | // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users. | |
7030 | mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, | |
7031 | const domainlabel *const name, const domainname *const type, const domainname *const domain, | |
7032 | const domainname *const host, | |
7033 | const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context) | |
7034 | { | |
7f0064bd | 7035 | mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context); |
283ee3ff | 7036 | if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); |
c9b9ae52 A |
7037 | rr->resrec.rdata->u.srv.priority = 0; |
7038 | rr->resrec.rdata->u.srv.weight = 0; | |
7039 | rr->resrec.rdata->u.srv.port = zeroIPPort; | |
283ee3ff | 7040 | if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host); |
c9b9ae52 A |
7041 | else rr->HostTarget = mDNStrue; |
7042 | return(mDNS_Register(m, rr)); | |
6528fe3e A |
7043 | } |
7044 | ||
c9b9ae52 A |
7045 | mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, |
7046 | mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) | |
6528fe3e | 7047 | { |
7f0064bd | 7048 | mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); |
283ee3ff | 7049 | if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); |
c9b9ae52 | 7050 | if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); |
6528fe3e A |
7051 | return(mDNS_Register(m, rr)); |
7052 | } | |
7053 | ||
7054 | // *************************************************************************** | |
c9b9ae52 | 7055 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
7056 | #pragma mark - |
7057 | #pragma mark - | |
7058 | #pragma mark - Startup and Shutdown | |
7059 | #endif | |
7060 | ||
283ee3ff | 7061 | mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) |
c9b9ae52 A |
7062 | { |
7063 | if (storage && numrecords) | |
7064 | { | |
7065 | mDNSu32 i; | |
7cb34e5c | 7066 | debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity)); |
c9b9ae52 A |
7067 | for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1]; |
7068 | storage[numrecords-1].next = m->rrcache_free; | |
7069 | m->rrcache_free = storage; | |
7070 | m->rrcache_size += numrecords; | |
7071 | } | |
7072 | } | |
7073 | ||
283ee3ff | 7074 | mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) |
8e92c31c A |
7075 | { |
7076 | mDNS_Lock(m); | |
7077 | mDNS_GrowCache_internal(m, storage, numrecords); | |
7078 | mDNS_Unlock(m); | |
7079 | } | |
7080 | ||
6528fe3e | 7081 | mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, |
283ee3ff | 7082 | CacheEntity *rrcachestorage, mDNSu32 rrcachesize, |
c9b9ae52 | 7083 | mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) |
6528fe3e | 7084 | { |
716635cc | 7085 | mDNSu32 slot; |
28f7d060 | 7086 | mDNSs32 timenow; |
7f0064bd | 7087 | mStatus result; |
6528fe3e A |
7088 | |
7089 | if (!rrcachestorage) rrcachesize = 0; | |
7090 | ||
c9b9ae52 A |
7091 | m->p = p; |
7092 | m->KnownBugs = 0; | |
c9d2d929 | 7093 | m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise |
c9b9ae52 A |
7094 | m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; |
7095 | m->mDNSPlatformStatus = mStatus_Waiting; | |
7f0064bd A |
7096 | m->UnicastPort4 = zeroIPPort; |
7097 | m->UnicastPort6 = zeroIPPort; | |
c9b9ae52 A |
7098 | m->MainCallback = Callback; |
7099 | m->MainContext = Context; | |
7f0064bd | 7100 | m->rec.r.resrec.RecordType = 0; |
c9b9ae52 A |
7101 | |
7102 | // For debugging: To catch and report locking failures | |
7103 | m->mDNS_busy = 0; | |
7104 | m->mDNS_reentrancy = 0; | |
7105 | m->mDNS_shutdown = mDNSfalse; | |
7106 | m->lock_rrcache = 0; | |
7107 | m->lock_Questions = 0; | |
7108 | m->lock_Records = 0; | |
7109 | ||
7110 | // Task Scheduling variables | |
7f0064bd A |
7111 | result = mDNSPlatformTimeInit(); |
7112 | if (result != mStatus_NoError) return(result); | |
28f7d060 A |
7113 | m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); |
7114 | timenow = mDNS_TimeNow_NoLock(m); | |
7f0064bd | 7115 | |
c9b9ae52 A |
7116 | m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section |
7117 | m->timenow_last = timenow; | |
c9b9ae52 A |
7118 | m->NextScheduledEvent = timenow; |
7119 | m->SuppressSending = timenow; | |
7120 | m->NextCacheCheck = timenow + 0x78000000; | |
7121 | m->NextScheduledQuery = timenow + 0x78000000; | |
7122 | m->NextScheduledProbe = timenow + 0x78000000; | |
7123 | m->NextScheduledResponse = timenow + 0x78000000; | |
7124 | m->ExpectUnicastResponse = timenow + 0x78000000; | |
7125 | m->RandomQueryDelay = 0; | |
7f0064bd | 7126 | m->PktNum = 0; |
c9b9ae52 A |
7127 | m->SendDeregistrations = mDNSfalse; |
7128 | m->SendImmediateAnswers = mDNSfalse; | |
7129 | m->SleepState = mDNSfalse; | |
7130 | ||
7131 | // These fields only required for mDNS Searcher... | |
7132 | m->Questions = mDNSNULL; | |
7133 | m->NewQuestions = mDNSNULL; | |
7134 | m->CurrentQuestion = mDNSNULL; | |
7135 | m->LocalOnlyQuestions = mDNSNULL; | |
7136 | m->NewLocalOnlyQuestions = mDNSNULL; | |
7137 | m->rrcache_size = 0; | |
7138 | m->rrcache_totalused = 0; | |
7139 | m->rrcache_active = 0; | |
7140 | m->rrcache_report = 10; | |
7141 | m->rrcache_free = mDNSNULL; | |
7142 | ||
283ee3ff | 7143 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL; |
c9b9ae52 | 7144 | |
8e92c31c | 7145 | mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); |
c9b9ae52 A |
7146 | |
7147 | // Fields below only required for mDNS Responder... | |
7148 | m->hostlabel.c[0] = 0; | |
7149 | m->nicelabel.c[0] = 0; | |
7f0064bd | 7150 | m->MulticastHostname.c[0] = 0; |
c9b9ae52 A |
7151 | m->HIHardware.c[0] = 0; |
7152 | m->HISoftware.c[0] = 0; | |
7153 | m->ResourceRecords = mDNSNULL; | |
7154 | m->DuplicateRecords = mDNSNULL; | |
283ee3ff | 7155 | m->NewLocalRecords = mDNSNULL; |
c9b9ae52 A |
7156 | m->CurrentRecord = mDNSNULL; |
7157 | m->HostInterfaces = mDNSNULL; | |
7158 | m->ProbeFailTime = 0; | |
7159 | m->NumFailedProbes = 0; | |
7160 | m->SuppressProbes = 0; | |
6528fe3e | 7161 | |
7f0064bd | 7162 | #ifndef UNICAST_DISABLED |
8e92c31c | 7163 | uDNS_Init(m); |
28f7d060 | 7164 | m->SuppressStdPort53Queries = 0; |
7f0064bd | 7165 | #endif |
6528fe3e | 7166 | result = mDNSPlatformInit(m); |
c9b9ae52 | 7167 | |
6528fe3e A |
7168 | return(result); |
7169 | } | |
7170 | ||
c9b9ae52 | 7171 | mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) |
6528fe3e A |
7172 | { |
7173 | m->mDNSPlatformStatus = result; | |
c9b9ae52 | 7174 | if (m->MainCallback) |
8e92c31c A |
7175 | { |
7176 | mDNS_Lock(m); | |
7177 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 7178 | m->MainCallback(m, mStatus_NoError); |
8e92c31c A |
7179 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
7180 | mDNS_Unlock(m); | |
7181 | } | |
6528fe3e A |
7182 | } |
7183 | ||
c9b9ae52 | 7184 | mDNSexport void mDNS_Close(mDNS *const m) |
6528fe3e | 7185 | { |
c9b9ae52 A |
7186 | mDNSu32 rrcache_active = 0; |
7187 | mDNSu32 rrcache_totalused = 0; | |
7188 | mDNSu32 slot; | |
7189 | NetworkInterfaceInfo *intf; | |
7190 | mDNS_Lock(m); | |
8e92c31c | 7191 | |
c9b9ae52 | 7192 | m->mDNS_shutdown = mDNStrue; |
6528fe3e | 7193 | |
7f0064bd A |
7194 | #ifndef UNICAST_DISABLED |
7195 | uDNS_Close(m); | |
7196 | #endif | |
c9b9ae52 A |
7197 | rrcache_totalused = m->rrcache_totalused; |
7198 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) | |
716635cc | 7199 | { |
283ee3ff | 7200 | while(m->rrcache_hash[slot]) |
c9b9ae52 | 7201 | { |
283ee3ff A |
7202 | CacheGroup *cg = m->rrcache_hash[slot]; |
7203 | while (cg->members) | |
7204 | { | |
7205 | CacheRecord *rr = cg->members; | |
7206 | cg->members = cg->members->next; | |
7207 | if (rr->CRActiveQuestion) rrcache_active++; | |
7208 | ReleaseCacheRecord(m, rr); | |
7209 | } | |
7210 | cg->rrcache_tail = &cg->members; | |
7211 | ReleaseCacheGroup(m, &m->rrcache_hash[slot]); | |
c9b9ae52 | 7212 | } |
716635cc | 7213 | } |
7f0064bd | 7214 | debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); |
c9b9ae52 A |
7215 | if (rrcache_active != m->rrcache_active) |
7216 | LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); | |
7217 | ||
c9b9ae52 A |
7218 | for (intf = m->HostInterfaces; intf; intf = intf->next) |
7219 | if (intf->Advertise) | |
8e92c31c | 7220 | DeadvertiseInterface(m, intf); |
6528fe3e A |
7221 | |
7222 | // Make sure there are nothing but deregistering records remaining in the list | |
c9b9ae52 | 7223 | if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set"); |
6528fe3e A |
7224 | m->CurrentRecord = m->ResourceRecords; |
7225 | while (m->CurrentRecord) | |
7226 | { | |
c9b9ae52 | 7227 | AuthRecord *rr = m->CurrentRecord; |
c9b9ae52 | 7228 | if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) |
6528fe3e | 7229 | { |
283ee3ff | 7230 | debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c); |
c9b9ae52 | 7231 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); |
6528fe3e | 7232 | } |
283ee3ff A |
7233 | else |
7234 | m->CurrentRecord = rr->next; | |
6528fe3e A |
7235 | } |
7236 | ||
7237 | if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records"); | |
7238 | else debugf("mDNS_Close: No deregistering records remain"); | |
7239 | ||
7240 | // If any deregistering records remain, send their deregistration announcements before we exit | |
283ee3ff A |
7241 | if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); |
7242 | else if (m->ResourceRecords) SendResponses(m); | |
7243 | if (m->ResourceRecords) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m, m->ResourceRecords)); | |
6528fe3e A |
7244 | |
7245 | mDNS_Unlock(m); | |
7246 | debugf("mDNS_Close: mDNSPlatformClose"); | |
7247 | mDNSPlatformClose(m); | |
7248 | debugf("mDNS_Close: done"); | |
7249 | } |