]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
c9d2d929 | 3 | * Copyright (c) 2002-2003 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 $ | |
28f7d060 A |
48 | Revision 1.526 2005/10/20 00:10:33 cheshire |
49 | <rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code | |
50 | ||
51 | Revision 1.525 2005/09/24 00:47:17 cheshire | |
52 | Fix comment typos | |
53 | ||
54 | Revision 1.524 2005/09/16 21:06:49 cheshire | |
55 | Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place | |
56 | ||
57 | Revision 1.523 2005/03/21 00:33:51 shersche | |
58 | <rdar://problem/4021486> Fix build warnings on Win32 platform | |
59 | ||
7cb34e5c A |
60 | Revision 1.522 2005/03/04 21:48:12 cheshire |
61 | <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity | |
62 | ||
63 | Revision 1.521 2005/02/25 04:21:00 cheshire | |
64 | <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing | |
65 | ||
66 | Revision 1.520 2005/02/16 01:14:11 cheshire | |
67 | Convert RR Cache LogOperation() calls to debugf() | |
68 | ||
69 | Revision 1.519 2005/02/15 01:57:20 cheshire | |
70 | When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero | |
71 | ||
72 | Revision 1.518 2005/02/10 22:35:17 cheshire | |
73 | <rdar://problem/3727944> Update name | |
74 | ||
75 | Revision 1.517 2005/02/03 00:21:21 cheshire | |
76 | Update comments about BIND named and zero-length TXT records | |
77 | ||
283ee3ff A |
78 | Revision 1.516 2005/01/28 06:06:32 cheshire |
79 | Update comment | |
80 | ||
81 | Revision 1.515 2005/01/27 00:21:49 cheshire | |
82 | <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message | |
83 | ||
84 | Revision 1.514 2005/01/21 01:33:45 cheshire | |
85 | <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM | |
86 | ||
87 | Revision 1.513 2005/01/21 00:07:54 cheshire | |
88 | <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict | |
89 | ||
90 | Revision 1.512 2005/01/20 00:37:45 cheshire | |
91 | <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse | |
92 | Take care not to recycle records while they are on the CacheFlushRecords list | |
93 | ||
94 | Revision 1.511 2005/01/19 22:48:53 cheshire | |
95 | <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService() | |
96 | ||
97 | Revision 1.510 2005/01/19 03:12:45 cheshire | |
98 | Move LocalRecordReady() macro from mDNS.c to DNSCommon.h | |
99 | ||
100 | Revision 1.509 2005/01/19 03:08:49 cheshire | |
101 | <rdar://problem/3961051> CPU Spin in mDNSResponder | |
102 | Log messages to help catch and report CPU spins | |
103 | ||
104 | Revision 1.508 2005/01/18 18:56:32 cheshire | |
105 | <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate | |
106 | ||
107 | Revision 1.507 2005/01/18 01:12:07 cheshire | |
108 | <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes | |
109 | ||
110 | Revision 1.506 2005/01/17 23:28:53 cheshire | |
111 | Fix compile error | |
112 | ||
113 | Revision 1.505 2005/01/11 02:02:56 shersche | |
114 | Move variable declaration to the beginning of statement block | |
115 | ||
116 | Revision 1.504 2004/12/20 20:24:35 cheshire | |
117 | <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer | |
118 | ||
119 | Revision 1.503 2004/12/20 18:41:47 cheshire | |
120 | <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space | |
121 | ||
122 | Revision 1.502 2004/12/20 18:04:08 cheshire | |
123 | <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache | |
124 | ||
125 | Revision 1.501 2004/12/19 23:50:18 cheshire | |
126 | <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records | |
127 | Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services | |
128 | ||
129 | Revision 1.500 2004/12/18 03:13:46 cheshire | |
130 | <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records | |
131 | ||
132 | Revision 1.499 2004/12/17 23:37:45 cheshire | |
133 | <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association | |
134 | (and other repetitive configuration changes) | |
135 | ||
136 | Revision 1.498 2004/12/17 05:25:46 cheshire | |
137 | <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs | |
138 | ||
139 | Revision 1.497 2004/12/17 03:20:58 cheshire | |
140 | <rdar://problem/3925168> Don't send unicast replies we know will be ignored | |
141 | ||
142 | Revision 1.496 2004/12/16 22:18:26 cheshire | |
143 | Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces | |
144 | ||
145 | Revision 1.495 2004/12/16 21:27:37 ksekar | |
146 | Fixed build failures when compiled with verbose debugging messages | |
147 | ||
148 | Revision 1.494 2004/12/16 20:46:56 cheshire | |
149 | Fix compiler warnings | |
150 | ||
151 | Revision 1.493 2004/12/16 20:13:00 cheshire | |
152 | <rdar://problem/3324626> Cache memory management improvements | |
153 | ||
154 | Revision 1.492 2004/12/16 08:03:24 shersche | |
155 | Fix compilation error when UNICAST_DISABLED is set | |
156 | ||
7f0064bd A |
157 | Revision 1.491 2004/12/11 01:52:11 cheshire |
158 | <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too | |
159 | ||
160 | Revision 1.490 2004/12/10 20:06:25 cheshire | |
161 | <rdar://problem/3915074> Reduce egregious stack space usage | |
162 | Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes | |
163 | ||
164 | Revision 1.489 2004/12/10 20:03:43 cheshire | |
165 | <rdar://problem/3915074> Reduce egregious stack space usage | |
166 | Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes | |
167 | ||
168 | Revision 1.488 2004/12/10 19:50:41 cheshire | |
169 | <rdar://problem/3915074> Reduce egregious stack space usage | |
170 | Reduced SendResponses() stack frame from 9K to 176 bytes | |
171 | ||
172 | Revision 1.487 2004/12/10 19:39:13 cheshire | |
173 | <rdar://problem/3915074> Reduce egregious stack space usage | |
174 | Reduced SendQueries() stack frame from 18K to 112 bytes | |
175 | ||
176 | Revision 1.486 2004/12/10 14:16:17 cheshire | |
177 | <rdar://problem/3889788> Relax update rate limiting | |
178 | We now allow an average rate of ten updates per minute. | |
179 | Updates in excess of that are rate limited, but more gently than before. | |
180 | ||
181 | Revision 1.485 2004/12/10 02:09:24 cheshire | |
182 | <rdar://problem/3898376> Modify default TTLs | |
183 | ||
184 | Revision 1.484 2004/12/09 03:15:40 ksekar | |
185 | <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains | |
186 | ||
187 | Revision 1.483 2004/12/07 23:00:14 ksekar | |
188 | <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration: | |
189 | Call RecordProbeFailure even if there is no record callback | |
190 | ||
191 | Revision 1.482 2004/12/07 22:49:06 cheshire | |
7cb34e5c | 192 | <rdar://problem/3908850> BIND doesn't allow zero-length TXT records |
7f0064bd A |
193 | |
194 | Revision 1.481 2004/12/07 21:26:04 ksekar | |
195 | <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration | |
196 | ||
197 | Revision 1.480 2004/12/07 20:42:33 cheshire | |
198 | Add explicit context parameter to mDNS_RemoveRecordFromService() | |
199 | ||
200 | Revision 1.479 2004/12/07 17:50:49 ksekar | |
7cb34e5c | 201 | <rdar://problem/3908850> BIND doesn't allow zero-length TXT records |
7f0064bd A |
202 | |
203 | Revision 1.478 2004/12/06 21:15:22 ksekar | |
204 | <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations | |
205 | ||
206 | Revision 1.477 2004/12/04 02:12:45 cheshire | |
207 | <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack | |
208 | ||
209 | Revision 1.476 2004/11/29 23:34:31 cheshire | |
210 | On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero | |
211 | is crude, and effectively halves the time resolution. The more selective NonZeroTime() function | |
212 | only nudges the time value to 1 if the interval calculation happens to result in the value zero. | |
213 | ||
214 | Revision 1.475 2004/11/29 23:13:31 cheshire | |
215 | <rdar://problem/3484552> All unique records in a set should have the cache flush bit set | |
216 | Additional check: Make sure we don't unnecessarily send packets containing only additionals. | |
217 | (This could occur with multi-packet KA lists, if the answer and additionals were marked | |
218 | by the query packet, and then the answer were later suppressed in a subsequent KA packet.) | |
219 | ||
220 | Revision 1.474 2004/11/29 17:18:12 cheshire | |
221 | Remove "Unknown DNS packet type" message for update responses | |
222 | ||
223 | Revision 1.473 2004/11/25 01:57:52 cheshire | |
224 | <rdar://problem/3484552> All unique records in a set should have the cache flush bit set | |
225 | ||
226 | Revision 1.472 2004/11/25 01:28:09 cheshire | |
227 | <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) | |
228 | ||
229 | Revision 1.471 2004/11/25 01:10:13 cheshire | |
230 | Move code to add additional records to a subroutine called AddAdditionalsToResponseList() | |
231 | ||
232 | Revision 1.470 2004/11/24 21:54:44 cheshire | |
233 | <rdar://problem/3894475> mDNSCore not receiving unicast responses properly | |
234 | ||
235 | Revision 1.469 2004/11/24 04:50:39 cheshire | |
236 | Minor tidying | |
237 | ||
238 | Revision 1.468 2004/11/24 01:47:07 cheshire | |
239 | <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success. | |
240 | ||
241 | Revision 1.467 2004/11/24 01:41:28 cheshire | |
242 | Rename CompleteProbing() to AcknowledgeRecord() | |
243 | ||
244 | Revision 1.466 2004/11/23 21:08:07 ksekar | |
245 | Don't use ID to demux multicast/unicast now that unicast uses random IDs | |
246 | ||
247 | Revision 1.465 2004/11/15 20:09:21 ksekar | |
248 | <rdar://problem/3719050> Wide Area support for Add/Remove record | |
249 | ||
250 | Revision 1.464 2004/11/03 01:44:36 cheshire | |
251 | Update debugging messages | |
252 | ||
253 | Revision 1.463 2004/10/29 02:38:48 cheshire | |
254 | Fix Windows compile errors | |
255 | ||
256 | Revision 1.462 2004/10/28 19:21:07 cheshire | |
257 | Guard against registering interface with zero InterfaceID | |
258 | ||
259 | Revision 1.461 2004/10/28 19:02:16 cheshire | |
260 | Remove \n from LogMsg() call | |
261 | ||
262 | Revision 1.460 2004/10/28 03:24:40 cheshire | |
263 | Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 | |
264 | ||
265 | Revision 1.459 2004/10/26 22:34:37 cheshire | |
266 | <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding | |
267 | ||
268 | Revision 1.458 2004/10/26 20:45:28 cheshire | |
269 | Show mask in "invalid mask" message | |
270 | ||
271 | Revision 1.457 2004/10/26 06:28:36 cheshire | |
272 | Now that we don't check IP TTL any more, remove associated log message | |
273 | ||
274 | Revision 1.456 2004/10/26 06:21:42 cheshire | |
275 | Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address) | |
276 | ||
277 | Revision 1.455 2004/10/26 06:11:40 cheshire | |
278 | Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed | |
279 | ||
280 | Revision 1.454 2004/10/23 01:16:00 cheshire | |
281 | <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts | |
282 | ||
283 | Revision 1.453 2004/10/22 20:52:06 ksekar | |
284 | <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries | |
285 | ||
286 | Revision 1.452 2004/10/20 01:50:40 cheshire | |
287 | <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API | |
288 | Implemented ForceMCast mode for AuthRecords as well as for Questions | |
289 | ||
290 | Revision 1.451 2004/10/19 21:33:15 cheshire | |
291 | <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API | |
292 | Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name | |
293 | doesn't force multicast unless you set this flag to indicate explicitly that this is what you want | |
294 | ||
295 | Revision 1.450 2004/10/19 17:42:59 ksekar | |
296 | Fixed compiler warnings for non-debug builds. | |
297 | ||
298 | Revision 1.449 2004/10/18 22:57:07 cheshire | |
299 | <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1 | |
300 | ||
301 | Revision 1.448 2004/10/16 00:16:59 cheshire | |
302 | <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check | |
303 | ||
304 | Revision 1.447 2004/10/15 00:51:21 cheshire | |
305 | <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1 | |
306 | ||
307 | Revision 1.446 2004/10/14 00:43:34 cheshire | |
308 | <rdar://problem/3815984> Services continue to announce SRV and HINFO | |
309 | ||
310 | Revision 1.445 2004/10/12 21:07:09 cheshire | |
311 | Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit() | |
312 | ||
313 | Revision 1.444 2004/10/11 17:54:16 ksekar | |
314 | Changed hashtable pointer output from debugf to verbosedebugf. | |
315 | ||
316 | Revision 1.443 2004/10/10 07:05:45 cheshire | |
317 | For consistency, use symbol "localdomain" instead of literal string | |
318 | ||
319 | Revision 1.442 2004/10/08 20:25:10 cheshire | |
320 | Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time | |
321 | ||
322 | Revision 1.441 2004/10/08 03:25:01 ksekar | |
323 | <rdar://problem/3831716> domain enumeration should use LLQs | |
324 | ||
325 | Revision 1.440 2004/10/06 01:44:19 cheshire | |
326 | <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record | |
327 | ||
328 | Revision 1.439 2004/10/03 23:14:11 cheshire | |
329 | Add "mDNSEthAddr" type and "zeroEthAddr" constant | |
330 | ||
331 | Revision 1.438 2004/09/29 23:07:04 cheshire | |
332 | Patch from Pavel Repin to fix compile error on Windows | |
333 | ||
334 | Revision 1.437 2004/09/28 02:23:50 cheshire | |
335 | <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events | |
336 | Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot | |
337 | For records with the cache flush bit set, defer the decision until the end of the packet | |
338 | ||
339 | Revision 1.436 2004/09/28 01:27:04 cheshire | |
340 | Update incorrect log message | |
341 | ||
342 | Revision 1.435 2004/09/25 02:41:39 cheshire | |
343 | <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events | |
344 | ||
345 | Revision 1.434 2004/09/25 02:32:06 cheshire | |
346 | Update comments | |
347 | ||
348 | Revision 1.433 2004/09/25 02:24:27 cheshire | |
349 | Removed unused rr->UseCount | |
350 | ||
351 | Revision 1.432 2004/09/24 21:35:17 cheshire | |
352 | <rdar://problem/3561220> Browses are no longer piggybacking on other browses | |
353 | TargetPort and TargetQID are allowed to be undefined if no question->Target is set | |
354 | ||
355 | Revision 1.431 2004/09/24 21:33:12 cheshire | |
356 | Adjust comment | |
357 | ||
358 | Revision 1.430 2004/09/24 02:15:49 cheshire | |
359 | <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces | |
360 | ||
361 | Revision 1.429 2004/09/24 00:20:21 cheshire | |
362 | <rdar://problem/3483349> Any rrtype is a conflict for unique records | |
363 | ||
364 | Revision 1.428 2004/09/24 00:12:25 cheshire | |
365 | Get rid of unused RRUniqueOrKnownUnique(RR) | |
366 | ||
367 | Revision 1.427 2004/09/23 20:44:11 cheshire | |
368 | <rdar://problem/3813148> Reduce timeout before expiring records on failure | |
369 | ||
370 | Revision 1.426 2004/09/23 20:21:07 cheshire | |
371 | <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic | |
372 | Associate a unique sequence number with each received packet, and only increment the count of recent answer | |
373 | packets if the packet sequence number for this answer record is not one we've already seen and counted. | |
374 | ||
375 | Revision 1.425 2004/09/23 20:14:38 cheshire | |
376 | Rename "question->RecentAnswers" to "question->RecentAnswerPkts" | |
377 | ||
378 | Revision 1.424 2004/09/23 00:58:36 cheshire | |
379 | <rdar://problem/3781269> Rate limiting interferes with updating TXT records | |
380 | ||
381 | Revision 1.423 2004/09/23 00:50:53 cheshire | |
382 | <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep | |
383 | ||
384 | Revision 1.422 2004/09/22 02:34:46 cheshire | |
385 | Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h | |
386 | ||
387 | Revision 1.421 2004/09/21 23:29:49 cheshire | |
388 | <rdar://problem/3680045> DNSServiceResolve should delay sending packets | |
389 | ||
390 | Revision 1.420 2004/09/21 23:01:42 cheshire | |
391 | Update debugf messages | |
392 | ||
393 | Revision 1.419 2004/09/21 19:51:14 cheshire | |
394 | Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c | |
395 | ||
396 | Revision 1.418 2004/09/21 18:40:17 cheshire | |
397 | <rdar://problem/3376752> Adjust default record TTLs | |
398 | ||
399 | Revision 1.417 2004/09/21 17:32:16 cheshire | |
400 | <rdar://problem/3809484> Rate limiting imposed too soon | |
401 | ||
402 | Revision 1.416 2004/09/20 23:52:01 cheshire | |
403 | CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c | |
404 | ||
405 | Revision 1.415 2004/09/18 01:14:09 cheshire | |
406 | <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces | |
407 | ||
408 | Revision 1.414 2004/09/18 01:06:48 cheshire | |
409 | Add comments | |
410 | ||
411 | Revision 1.413 2004/09/17 01:08:48 cheshire | |
412 | Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h | |
413 | The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces | |
414 | declared in that file are ONLY appropriate to single-address-space embedded applications. | |
415 | For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. | |
416 | ||
417 | Revision 1.412 2004/09/17 00:46:33 cheshire | |
418 | mDNS_TimeNow should take const mDNS parameter | |
419 | ||
420 | Revision 1.411 2004/09/17 00:31:51 cheshire | |
421 | For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' | |
422 | ||
423 | Revision 1.410 2004/09/17 00:19:10 cheshire | |
424 | For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 | |
425 | ||
426 | Revision 1.409 2004/09/16 21:59:15 cheshire | |
427 | For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr | |
428 | ||
429 | Revision 1.408 2004/09/16 21:36:36 cheshire | |
430 | <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() | |
431 | Changes to add necessary locking calls around unicast DNS operations | |
432 | ||
433 | Revision 1.407 2004/09/16 02:29:39 cheshire | |
434 | Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around | |
435 | uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService | |
436 | ||
437 | Revision 1.406 2004/09/16 01:58:14 cheshire | |
438 | Fix compiler warnings | |
439 | ||
440 | Revision 1.405 2004/09/16 00:24:48 cheshire | |
441 | <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() | |
442 | ||
443 | Revision 1.404 2004/09/15 21:44:11 cheshire | |
444 | <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init | |
445 | Show time value in log to help diagnose errors | |
446 | ||
447 | Revision 1.403 2004/09/15 00:46:32 ksekar | |
448 | Changed debugf to verbosedebugf in CheckCacheExpiration | |
449 | ||
450 | Revision 1.402 2004/09/14 23:59:55 cheshire | |
451 | <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init | |
452 | ||
453 | Revision 1.401 2004/09/14 23:27:46 cheshire | |
454 | Fix compile errors | |
455 | ||
456 | Revision 1.400 2004/09/02 03:48:47 cheshire | |
457 | <rdar://problem/3709039> Disable targeted unicast query support by default | |
458 | 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record | |
459 | 2. New field AllowRemoteQuery in AuthRecord structure | |
460 | 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set | |
461 | 4. mDNS.c only answers remote queries if AllowRemoteQuery is set | |
462 | ||
463 | Revision 1.399 2004/09/02 01:39:40 cheshire | |
464 | For better readability, follow consistent convention that QR bit comes first, followed by OP bits | |
465 | ||
466 | Revision 1.398 2004/09/01 03:59:29 ksekar | |
467 | <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows | |
468 | ||
469 | Revision 1.397 2004/08/25 22:04:25 rpantos | |
470 | Fix the standard Windows compile error. | |
471 | ||
472 | Revision 1.396 2004/08/25 00:37:27 ksekar | |
473 | <rdar://problem/3774635>: Cleanup DynDNS hostname registration code | |
474 | ||
475 | Revision 1.395 2004/08/18 17:21:18 ksekar | |
476 | Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs() | |
477 | ||
478 | Revision 1.394 2004/08/14 03:22:41 cheshire | |
479 | <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue | |
480 | Add GetUserSpecifiedDDNSName() routine | |
481 | Convert ServiceRegDomain to domainname instead of C string | |
482 | Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs | |
483 | ||
484 | Revision 1.393 2004/08/13 23:42:52 cheshire | |
485 | Removed unused "zeroDomainNamePtr" | |
486 | ||
487 | Revision 1.392 2004/08/13 23:37:02 cheshire | |
488 | Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with | |
489 | "uDNS_info.UnicastHostname" for clarity | |
490 | ||
491 | Revision 1.391 2004/08/13 23:25:00 cheshire | |
492 | Now that we do both uDNS and mDNS, global replace "m->hostname" with | |
493 | "m->MulticastHostname" for clarity | |
494 | ||
495 | Revision 1.390 2004/08/11 02:17:01 cheshire | |
496 | <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record | |
497 | ||
498 | Revision 1.389 2004/08/10 23:19:14 ksekar | |
7cb34e5c | 499 | <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery |
7f0064bd A |
500 | Moved routines/constants to allow extern access for garbage collection daemon |
501 | ||
502 | Revision 1.388 2004/07/30 17:40:06 ksekar | |
503 | <rdar://problem/3739115>: TXT Record updates not available for wide-area services | |
504 | ||
505 | Revision 1.387 2004/07/26 22:49:30 ksekar | |
506 | <rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client | |
507 | ||
508 | Revision 1.386 2004/07/13 21:24:24 rpantos | |
509 | Fix for <rdar://problem/3701120>. | |
510 | ||
511 | Revision 1.385 2004/06/18 19:09:59 cheshire | |
512 | <rdar://problem/3588761> Current method of doing subtypes causes name collisions | |
513 | ||
514 | Revision 1.384 2004/06/15 04:31:23 cheshire | |
515 | Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion() | |
516 | ||
517 | Revision 1.383 2004/06/11 00:04:59 cheshire | |
518 | <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord | |
519 | ||
8e92c31c A |
520 | Revision 1.382 2004/06/08 04:59:40 cheshire |
521 | Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it | |
522 | ||
523 | Revision 1.381 2004/06/05 00:57:30 cheshire | |
524 | Remove incorrect LogMsg() | |
525 | ||
526 | Revision 1.380 2004/06/05 00:04:26 cheshire | |
527 | <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration | |
528 | ||
529 | Revision 1.379 2004/05/28 23:42:36 ksekar | |
530 | <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) | |
531 | ||
532 | Revision 1.378 2004/05/25 17:25:25 cheshire | |
533 | Remove extraneous blank lines and white space | |
534 | ||
535 | Revision 1.377 2004/05/18 23:51:25 cheshire | |
536 | Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers | |
537 | ||
538 | Revision 1.376 2004/05/05 18:30:44 ksekar | |
539 | Restored surpressed Cache Tail debug messages. | |
540 | ||
541 | Revision 1.375 2004/04/26 21:36:25 cheshire | |
542 | Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive | |
543 | is indicated as being available on that interface | |
544 | ||
545 | Revision 1.374 2004/04/21 02:53:26 cheshire | |
546 | Typo in debugf statement | |
547 | ||
548 | Revision 1.373 2004/04/21 02:49:11 cheshire | |
549 | To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' | |
550 | ||
551 | Revision 1.372 2004/04/21 02:38:51 cheshire | |
552 | Add debugging checks | |
553 | ||
554 | Revision 1.371 2004/04/14 23:09:28 ksekar | |
555 | Support for TSIG signed dynamic updates. | |
556 | ||
557 | Revision 1.370 2004/04/09 17:40:26 cheshire | |
558 | Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field | |
559 | ||
560 | Revision 1.369 2004/04/09 16:34:00 cheshire | |
561 | Debugging code for later; currently unused | |
562 | ||
563 | Revision 1.368 2004/04/02 19:19:48 cheshire | |
564 | Add code to do optional logging of multi-packet KA list time intervals | |
565 | ||
566 | Revision 1.367 2004/03/20 03:16:10 cheshire | |
567 | Minor refinement to "Excessive update rate" message | |
b7388343 | 568 | |
8e92c31c A |
569 | Revision 1.366 2004/03/20 03:12:57 cheshire |
570 | <rdar://problem/3587619>: UpdateCredits not granted promptly enough | |
b7388343 | 571 | |
8e92c31c A |
572 | Revision 1.365 2004/03/19 23:51:22 cheshire |
573 | Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60) | |
b7388343 | 574 | |
8e92c31c A |
575 | Revision 1.364 2004/03/13 01:57:33 ksekar |
576 | <rdar://problem/3192546>: DynDNS: Dynamic update of service records | |
b7388343 | 577 | |
8e92c31c A |
578 | Revision 1.363 2004/03/12 21:00:51 cheshire |
579 | Also show port numbers when logging "apparent spoof mDNS Response" messages | |
b7388343 | 580 | |
8e92c31c A |
581 | Revision 1.362 2004/03/12 08:58:18 cheshire |
582 | Guard against empty TXT records | |
73792575 | 583 | |
8e92c31c A |
584 | Revision 1.361 2004/03/09 03:00:46 cheshire |
585 | <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good. | |
73792575 | 586 | |
8e92c31c A |
587 | Revision 1.360 2004/03/08 02:52:41 cheshire |
588 | Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages | |
73792575 | 589 | |
8e92c31c | 590 | Revision 1.359 2004/03/02 03:21:56 cheshire |
8d1ca615 A |
591 | <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries |
592 | ||
8e92c31c A |
593 | Revision 1.358 2004/02/20 08:18:34 cheshire |
594 | <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily | |
8d1ca615 | 595 | |
8e92c31c A |
596 | Revision 1.357 2004/02/18 01:47:41 cheshire |
597 | <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms | |
54811a41 | 598 | |
8e92c31c A |
599 | Revision 1.356 2004/02/06 23:04:19 ksekar |
600 | Basic Dynamic Update support via mDNS_Register (dissabled via | |
601 | UNICAST_REGISTRATION #define) | |
602 | ||
603 | Revision 1.355 2004/02/05 09:32:33 cheshire | |
604 | Fix from Bob Bradley: When using the "%.*s" string form, | |
605 | guard against truncating in the middle of a multi-byte UTF-8 character. | |
606 | ||
607 | Revision 1.354 2004/02/05 09:30:22 cheshire | |
608 | Update comments | |
609 | ||
610 | Revision 1.353 2004/01/28 03:41:00 cheshire | |
611 | <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries | |
612 | ||
613 | Revision 1.352 2004/01/28 02:30:07 ksekar | |
614 | Added default Search Domains to unicast browsing, controlled via | |
615 | Networking sharing prefs pane. Stopped sending unicast messages on | |
616 | every interface. Fixed unicast resolving via mach-port API. | |
617 | ||
618 | Revision 1.351 2004/01/27 20:15:22 cheshire | |
619 | <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 | |
620 | ||
621 | Revision 1.350 2004/01/24 23:38:16 cheshire | |
622 | Use mDNSVal16() instead of shifting and ORing operations | |
623 | ||
624 | Revision 1.349 2004/01/23 23:23:14 ksekar | |
625 | Added TCP support for truncated unicast messages. | |
626 | ||
627 | Revision 1.348 2004/01/22 03:54:11 cheshire | |
628 | Create special meta-interface 'mDNSInterface_ForceMCast' (-2), | |
629 | which means "do this query via multicast, even if it's apparently a unicast domain" | |
630 | ||
631 | Revision 1.347 2004/01/22 03:50:49 cheshire | |
632 | If the client has specified an explicit InterfaceID, then do query by multicast, not unicast | |
633 | ||
634 | Revision 1.346 2004/01/22 03:48:41 cheshire | |
635 | Make sure uDNS client doesn't accidentally use query ID zero | |
636 | ||
637 | Revision 1.345 2004/01/22 03:43:08 cheshire | |
638 | Export constants like mDNSInterface_LocalOnly so that the client layers can use them | |
639 | ||
640 | Revision 1.344 2004/01/21 21:53:18 cheshire | |
641 | <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port | |
642 | ||
643 | Revision 1.343 2003/12/23 00:07:47 cheshire | |
644 | Make port number in debug message be five-character field, left justified | |
645 | ||
646 | Revision 1.342 2003/12/20 01:34:28 cheshire | |
8abd1236 A |
647 | <rdar://problem/3515876>: Error putting additional records into packets |
648 | Another fix from Rampi: responseptr needs to be updated inside the "for" loop, | |
649 | after every record, not once at the end. | |
650 | ||
8e92c31c A |
651 | Revision 1.341 2003/12/18 22:56:12 cheshire |
652 | <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets | |
653 | ||
654 | Revision 1.340 2003/12/16 02:31:37 cheshire | |
655 | Minor update to comments | |
656 | ||
657 | Revision 1.339 2003/12/13 05:50:33 bradley | |
658 | Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform | |
659 | layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to | |
660 | fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf. | |
661 | ||
662 | Revision 1.338 2003/12/13 03:05:27 ksekar | |
663 | <rdar://problem/3192548>: DynDNS: Unicast query of service records | |
664 | ||
665 | Revision 1.337 2003/12/01 21:46:05 cheshire | |
666 | mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist | |
667 | ||
668 | Revision 1.336 2003/12/01 21:26:19 cheshire | |
669 | Guard against zero-length sbuffer in mDNS_vsnprintf() | |
670 | ||
671 | Revision 1.335 2003/12/01 20:27:48 cheshire | |
672 | Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors | |
673 | ||
674 | Revision 1.334 2003/11/20 22:59:53 cheshire | |
7f0064bd | 675 | Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h |
8e92c31c A |
676 | Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. |
677 | ||
678 | Revision 1.333 2003/11/20 20:49:53 cheshire | |
679 | Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures | |
680 | ||
681 | Revision 1.332 2003/11/20 05:47:37 cheshire | |
682 | <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query | |
683 | Now that we only include answers in the known answer list if they are less than | |
684 | halfway to expiry, the check to also see if we have another query scheduled | |
685 | before the record expires is no longer necessary (and in fact, not correct). | |
686 | ||
687 | Revision 1.331 2003/11/19 22:31:48 cheshire | |
688 | When automatically adding A records to SRVs, add them as additionals, not answers | |
689 | ||
690 | Revision 1.330 2003/11/19 22:28:50 cheshire | |
691 | Increment/Decrement mDNS_reentrancy around calls to m->MainCallback() | |
692 | to allow client to make mDNS calls (specifically the call to mDNS_GrowCache()) | |
693 | ||
694 | Revision 1.329 2003/11/19 22:19:24 cheshire | |
695 | Show log message when ignoring packets with bad TTL. | |
696 | This is to help diagnose problems on Linux versions that may not report the TTL reliably. | |
697 | ||
698 | Revision 1.328 2003/11/19 22:06:38 cheshire | |
699 | Show log messages when a service or hostname is renamed | |
700 | ||
701 | Revision 1.327 2003/11/19 22:03:44 cheshire | |
702 | Move common "m->NextScheduledResponse = m->timenow" to before "if" statement | |
703 | ||
704 | Revision 1.326 2003/11/17 22:27:02 cheshire | |
705 | Another fix from ramaprasad.kr@hp.com: Improve reply delay computation | |
706 | on platforms that have native clock rates below fifty ticks per second. | |
707 | ||
708 | Revision 1.325 2003/11/17 20:41:44 cheshire | |
709 | Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls. | |
710 | ||
711 | Revision 1.324 2003/11/17 20:36:32 cheshire | |
712 | Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and | |
713 | DeadvertiseInterface() -- they're internal private routines, not API routines. | |
714 | ||
715 | Revision 1.323 2003/11/14 20:59:08 cheshire | |
716 | Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. | |
7f0064bd | 717 | Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. |
8e92c31c A |
718 | |
719 | Revision 1.322 2003/11/14 19:47:52 cheshire | |
720 | Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString | |
721 | ||
722 | Revision 1.321 2003/11/14 19:18:34 cheshire | |
7f0064bd | 723 | Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too |
8e92c31c A |
724 | |
725 | Revision 1.320 2003/11/13 06:45:04 cheshire | |
726 | Fix compiler warning on certain compilers | |
727 | ||
728 | Revision 1.319 2003/11/13 00:47:40 cheshire | |
729 | <rdar://problem/3437556> We should delay AAAA record query if A record already in cache. | |
730 | ||
731 | Revision 1.318 2003/11/13 00:33:26 cheshire | |
732 | Change macro "RRIsAddressType" to "RRTypeIsAddressType" | |
733 | ||
734 | Revision 1.317 2003/11/13 00:10:49 cheshire | |
735 | <rdar://problem/3436412>: Verify that rr data is different before updating. | |
736 | ||
737 | Revision 1.316 2003/11/08 23:37:54 cheshire | |
738 | Give explicit zero initializers to blank static structure, required by certain compilers. | |
739 | (Thanks to ramaprasad.kr@hp.com for reporting this.) | |
740 | ||
741 | Revision 1.315 2003/11/07 03:32:56 cheshire | |
742 | <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order | |
743 | This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes | |
744 | purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is | |
745 | to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() | |
746 | can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. | |
747 | ||
748 | Revision 1.314 2003/11/07 03:19:49 cheshire | |
749 | Minor variable renaming for clarity | |
750 | ||
751 | Revision 1.313 2003/11/07 03:14:49 cheshire | |
752 | Previous checkin proved to be overly simplistic; reversing | |
753 | ||
754 | Revision 1.312 2003/11/03 23:45:15 cheshire | |
755 | <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order | |
756 | Build cache lists in FIFO order, not customary C LIFO order | |
757 | (Append new elements to tail of cache list, instead of prepending at the head.) | |
758 | ||
759 | Revision 1.311 2003/10/09 18:00:11 cheshire | |
760 | Another compiler warning fix. | |
761 | ||
762 | Revision 1.310 2003/10/07 20:27:05 cheshire | |
763 | Patch from Bob Bradley, to fix warning and compile error on Windows | |
764 | ||
765 | Revision 1.309 2003/09/26 01:06:36 cheshire | |
766 | <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too | |
767 | Made new routine HaveSentEntireRRSet() to check if flag should be set | |
768 | ||
769 | Revision 1.308 2003/09/23 01:05:01 cheshire | |
770 | Minor changes to comments and debugf() message | |
716635cc | 771 | |
c9b9ae52 A |
772 | Revision 1.307 2003/09/09 20:13:30 cheshire |
773 | <rdar://problem/3411105> Don't send a Goodbye record if we never announced it | |
774 | Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented | |
775 | rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount | |
776 | ||
777 | Revision 1.306 2003/09/09 03:00:03 cheshire | |
778 | <rdar://problem/3413099> Services take a long time to disappear when switching networks. | |
779 | Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect | |
780 | ||
781 | Revision 1.305 2003/09/09 02:49:31 cheshire | |
782 | <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep | |
783 | ||
784 | Revision 1.304 2003/09/09 02:41:19 cheshire | |
785 | <rdar://problem/3411105> Don't send a Goodbye record if we never announced it | |
786 | ||
787 | Revision 1.303 2003/09/05 19:55:02 cheshire | |
788 | <rdar://problem/3409533> Include address records when announcing SRV records | |
789 | ||
790 | Revision 1.302 2003/09/05 00:01:36 cheshire | |
791 | <rdar://problem/3407549> Don't accelerate queries that have large KA lists | |
792 | ||
793 | Revision 1.301 2003/09/04 22:51:13 cheshire | |
794 | <rdar://problem/3398213> Group probes and goodbyes better | |
795 | ||
796 | Revision 1.300 2003/09/03 02:40:37 cheshire | |
797 | <rdar://problem/3404842> mDNSResponder complains about '_'s | |
798 | Underscores are not supposed to be legal in standard DNS names, but IANA appears | |
799 | to have allowed them in previous service name registrations, so we should too. | |
800 | ||
801 | Revision 1.299 2003/09/03 02:33:09 cheshire | |
802 | <rdar://problem/3404795> CacheRecordRmv ERROR | |
803 | Don't update m->NewQuestions until *after* CheckCacheExpiration(); | |
804 | ||
805 | Revision 1.298 2003/09/03 01:47:01 cheshire | |
7f0064bd | 806 | <rdar://problem/3319418> Services always in a state of flux |
c9b9ae52 A |
807 | Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds |
808 | ||
809 | Revision 1.297 2003/08/29 19:44:15 cheshire | |
810 | <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears | |
811 | 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries | |
812 | that already have at least one unique answer in the cache | |
813 | 2. For these queries, go straight to QM, skipping QU | |
814 | ||
815 | Revision 1.296 2003/08/29 19:08:21 cheshire | |
816 | <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep | |
817 | Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time. | |
818 | ||
819 | Revision 1.295 2003/08/28 01:10:59 cheshire | |
820 | <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst | |
821 | ||
822 | Revision 1.294 2003/08/27 02:30:22 cheshire | |
823 | <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() | |
824 | One more change: "query->GotTXT" is now a straightforward bi-state boolean again | |
825 | ||
826 | Revision 1.293 2003/08/27 02:25:31 cheshire | |
827 | <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() | |
828 | ||
829 | Revision 1.292 2003/08/21 19:27:36 cheshire | |
830 | <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL | |
831 | ||
832 | Revision 1.291 2003/08/21 18:57:44 cheshire | |
833 | <rdar://problem/3387140> Synchronized queries on the network | |
834 | ||
835 | Revision 1.290 2003/08/21 02:25:23 cheshire | |
836 | Minor changes to comments and debugf() messages | |
837 | ||
838 | Revision 1.289 2003/08/21 02:21:50 cheshire | |
839 | <rdar://problem/3386473> Efficiency: Reduce repeated queries | |
840 | ||
841 | Revision 1.288 2003/08/20 23:39:30 cheshire | |
842 | <rdar://problem/3344098> Review syslog messages, and remove as appropriate | |
843 | ||
844 | Revision 1.287 2003/08/20 20:47:18 cheshire | |
845 | Fix compiler warning | |
846 | ||
847 | Revision 1.286 2003/08/20 02:18:51 cheshire | |
848 | <rdar://problem/3344098> Cleanup: Review syslog messages | |
849 | ||
850 | Revision 1.285 2003/08/20 01:59:06 cheshire | |
851 | <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata | |
852 | Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place | |
853 | ||
854 | Revision 1.284 2003/08/19 22:20:00 cheshire | |
855 | <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured | |
856 | More minor refinements | |
857 | ||
858 | Revision 1.283 2003/08/19 22:16:27 cheshire | |
859 | Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case. | |
860 | ||
861 | Revision 1.282 2003/08/19 06:48:25 cheshire | |
862 | <rdar://problem/3376552> Guard against excessive record updates | |
863 | Each record starts with 10 UpdateCredits. | |
864 | Every update consumes one UpdateCredit. | |
865 | UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. | |
866 | As the number of UpdateCredits declines, the number of announcements is similarly scaled back. | |
867 | When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. | |
868 | ||
869 | Revision 1.281 2003/08/19 04:49:28 cheshire | |
870 | <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right | |
871 | 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. | |
872 | 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. | |
873 | 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. | |
874 | ||
875 | Revision 1.280 2003/08/19 02:33:36 cheshire | |
876 | Update comments | |
877 | ||
878 | Revision 1.279 2003/08/19 02:31:11 cheshire | |
879 | <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries | |
880 | Final expiration queries now only mark the question for sending on the particular interface | |
881 | pertaining to the record that's expiring. | |
882 | ||
883 | Revision 1.278 2003/08/18 22:53:37 cheshire | |
7f0064bd | 884 | <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime() |
c9b9ae52 A |
885 | |
886 | Revision 1.277 2003/08/18 19:05:44 cheshire | |
887 | <rdar://problem/3382423> UpdateRecord not working right | |
888 | Added "newrdlength" field to hold new length of updated rdata | |
889 | ||
890 | Revision 1.276 2003/08/16 03:39:00 cheshire | |
891 | <rdar://problem/3338440> InterfaceID -1 indicates "local only" | |
892 | ||
893 | Revision 1.275 2003/08/16 02:51:27 cheshire | |
894 | <rdar://problem/3366590> mDNSResponder takes too much RPRVT | |
895 | Don't try to compute namehash etc, until *after* validating the name | |
896 | ||
897 | Revision 1.274 2003/08/16 01:12:40 cheshire | |
898 | <rdar://problem/3366590> mDNSResponder takes too much RPRVT | |
899 | Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a | |
900 | simple C structure assignment of a domainname, because that object is defined to be 256 bytes long, | |
901 | and in the process of copying it, the C compiler may run off the end of the rdata object into | |
902 | unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a | |
903 | call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid. | |
904 | ||
905 | Revision 1.273 2003/08/15 20:16:02 cheshire | |
906 | <rdar://problem/3366590> mDNSResponder takes too much RPRVT | |
907 | We want to avoid touching the rdata pages, so we don't page them in. | |
908 | 1. RDLength was stored with the rdata, which meant touching the page just to find the length. | |
909 | Moved this from the RData to the ResourceRecord object. | |
910 | 2. To avoid unnecessarily touching the rdata just to compare it, | |
911 | compute a hash of the rdata and store the hash in the ResourceRecord object. | |
912 | ||
913 | Revision 1.272 2003/08/14 19:29:04 cheshire | |
914 | <rdar://problem/3378473> Include cache records in SIGINFO output | |
7f0064bd | 915 | Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them |
c9b9ae52 A |
916 | |
917 | Revision 1.271 2003/08/14 02:17:05 cheshire | |
918 | <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord | |
919 | ||
920 | Revision 1.270 2003/08/13 17:07:28 ksekar | |
8e92c31c | 921 | <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash |
c9b9ae52 A |
922 | Added check to result of mDNS_Register() before linking extra record into list. |
923 | ||
924 | Revision 1.269 2003/08/12 19:56:23 cheshire | |
925 | Update to APSL 2.0 | |
926 | ||
927 | Revision 1.268 2003/08/12 15:01:10 cheshire | |
928 | Add comments | |
929 | ||
930 | Revision 1.267 2003/08/12 14:59:27 cheshire | |
931 | <rdar://problem/3374490> Rate-limiting blocks some legitimate responses | |
932 | When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine | |
933 | whether to suppress the response, also check LastMCInterface to see if it matches. | |
934 | ||
935 | Revision 1.266 2003/08/12 12:47:16 cheshire | |
936 | In mDNSCoreMachineSleep debugf message, display value of m->timenow | |
937 | ||
938 | Revision 1.265 2003/08/11 20:04:28 cheshire | |
939 | <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache | |
940 | ||
941 | Revision 1.264 2003/08/09 00:55:02 cheshire | |
942 | <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU | |
943 | Don't scan the whole cache after every packet. | |
944 | ||
945 | Revision 1.263 2003/08/09 00:35:29 cheshire | |
946 | Moved AnswerNewQuestion() later in the file, in preparation for next checkin | |
947 | ||
948 | Revision 1.262 2003/08/08 19:50:33 cheshire | |
949 | <rdar://problem/3370332> Remove "Cache size now xxx" messages | |
950 | ||
951 | Revision 1.261 2003/08/08 19:18:45 cheshire | |
952 | <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug | |
953 | ||
954 | Revision 1.260 2003/08/08 18:55:48 cheshire | |
955 | <rdar://problem/3370365> Guard against time going backwards | |
956 | ||
957 | Revision 1.259 2003/08/08 18:36:04 cheshire | |
958 | <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug | |
959 | ||
960 | Revision 1.258 2003/08/08 16:22:05 cheshire | |
961 | <rdar://problem/3335473> Need to check validity of TXT (and other) records | |
962 | Remove unneeded LogMsg | |
963 | ||
964 | Revision 1.257 2003/08/07 01:41:08 cheshire | |
965 | <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones) | |
966 | ||
967 | Revision 1.256 2003/08/06 23:25:51 cheshire | |
968 | <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four | |
969 | ||
970 | Revision 1.255 2003/08/06 23:22:50 cheshire | |
971 | Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours) | |
972 | ||
973 | Revision 1.254 2003/08/06 21:33:39 cheshire | |
974 | Fix compiler warnings on PocketPC 2003 (Windows CE) | |
975 | ||
976 | Revision 1.253 2003/08/06 20:43:57 cheshire | |
977 | <rdar://problem/3335473> Need to check validity of TXT (and other) records | |
978 | Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update() | |
979 | ||
980 | Revision 1.252 2003/08/06 20:35:47 cheshire | |
981 | Enhance debugging routine GetRRDisplayString() so it can also be used to display | |
982 | other RDataBody objects, not just the one currently attached the given ResourceRecord | |
983 | ||
984 | Revision 1.251 2003/08/06 19:07:34 cheshire | |
985 | <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should | |
986 | Was checking LastAPTime instead of LastMCTime | |
987 | ||
988 | Revision 1.250 2003/08/06 19:01:55 cheshire | |
989 | Update comments | |
990 | ||
991 | Revision 1.249 2003/08/06 00:13:28 cheshire | |
992 | Tidy up debugf messages | |
993 | ||
994 | Revision 1.248 2003/08/05 22:20:15 cheshire | |
995 | <rdar://problem/3330324> Need to check IP TTL on responses | |
996 | ||
997 | Revision 1.247 2003/08/05 00:56:39 cheshire | |
998 | <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed | |
999 | ||
1000 | Revision 1.246 2003/08/04 19:20:49 cheshire | |
1001 | Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages | |
1002 | ||
1003 | Revision 1.245 2003/08/02 01:56:29 cheshire | |
1004 | For debugging: log message if we ever get more than one question in a truncated packet | |
1005 | ||
1006 | Revision 1.244 2003/08/01 23:55:32 cheshire | |
1007 | Fix for compiler warnings on Windows, submitted by Bob Bradley | |
1008 | ||
1009 | Revision 1.243 2003/07/25 02:26:09 cheshire | |
1010 | Typo: FIxed missing semicolon | |
1011 | ||
1012 | Revision 1.242 2003/07/25 01:18:41 cheshire | |
1013 | Fix memory leak on shutdown in mDNS_Close() (detected in Windows version) | |
1014 | ||
1015 | Revision 1.241 2003/07/23 21:03:42 cheshire | |
1016 | Only show "Found record..." debugf message in verbose mode | |
1017 | ||
1018 | Revision 1.240 2003/07/23 21:01:11 cheshire | |
1019 | <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one | |
1020 | After sending a packet, suppress further sending for the next 100ms. | |
1021 | ||
1022 | Revision 1.239 2003/07/22 01:30:05 cheshire | |
1023 | <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once | |
1024 | ||
1025 | Revision 1.238 2003/07/22 00:10:20 cheshire | |
1026 | <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters | |
1027 | ||
1028 | Revision 1.237 2003/07/19 03:23:13 cheshire | |
1029 | <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records | |
1030 | ||
1031 | Revision 1.236 2003/07/19 03:04:55 cheshire | |
1032 | Fix warnings; some debugf message improvements | |
1033 | ||
1034 | Revision 1.235 2003/07/19 00:03:32 cheshire | |
1035 | <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received | |
1036 | ScheduleNextTask is quite an expensive operation. | |
1037 | We don't need to do all that work after receiving a no-op packet that didn't change our state. | |
1038 | ||
1039 | Revision 1.234 2003/07/18 23:52:11 cheshire | |
1040 | To improve consistency of field naming, global search-and-replace: | |
1041 | NextProbeTime -> NextScheduledProbe | |
1042 | NextResponseTime -> NextScheduledResponse | |
1043 | ||
1044 | Revision 1.233 2003/07/18 00:29:59 cheshire | |
1045 | <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead | |
1046 | ||
1047 | Revision 1.232 2003/07/18 00:11:38 cheshire | |
1048 | Add extra case to switch statements to handle HINFO data for Get, Put and Display | |
1049 | (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT) | |
1050 | ||
1051 | Revision 1.231 2003/07/18 00:06:37 cheshire | |
1052 | To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->" | |
1053 | ||
1054 | Revision 1.230 2003/07/17 18:16:54 cheshire | |
7f0064bd | 1055 | <rdar://problem/3319418> Services always in a state of flux |
c9b9ae52 A |
1056 | In preparation for working on this, made some debugf messages a little more selective |
1057 | ||
1058 | Revision 1.229 2003/07/17 17:35:04 cheshire | |
1059 | <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding | |
1060 | ||
1061 | Revision 1.228 2003/07/16 20:50:27 cheshire | |
1062 | <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass | |
1063 | ||
1064 | Revision 1.227 2003/07/16 05:01:36 cheshire | |
1065 | Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for | |
1066 | <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass | |
1067 | ||
1068 | Revision 1.226 2003/07/16 04:51:44 cheshire | |
1069 | Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval' | |
1070 | ||
1071 | Revision 1.225 2003/07/16 04:46:41 cheshire | |
1072 | Minor wording cleanup: The correct DNS term is "response", not "reply" | |
1073 | ||
1074 | Revision 1.224 2003/07/16 04:39:02 cheshire | |
1075 | Textual cleanup (no change to functionality): | |
1076 | Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)" | |
1077 | ||
1078 | Revision 1.223 2003/07/16 00:09:22 cheshire | |
1079 | Textual cleanup (no change to functionality): | |
1080 | Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places; | |
1081 | replace with macro "TicksTTL(rr)" | |
1082 | Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" | |
1083 | replaced with macro "RRExpireTime(rr)" | |
1084 | ||
1085 | Revision 1.222 2003/07/15 23:40:46 cheshire | |
1086 | Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo() | |
1087 | ||
1088 | Revision 1.221 2003/07/15 22:17:56 cheshire | |
1089 | <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries | |
1090 | ||
1091 | Revision 1.220 2003/07/15 02:12:51 cheshire | |
1092 | Slight tidy-up of debugf messages and comments | |
1093 | ||
1094 | Revision 1.219 2003/07/15 01:55:12 cheshire | |
1095 | <rdar://problem/3315777> Need to implement service registration with subtypes | |
1096 | ||
1097 | Revision 1.218 2003/07/14 16:26:06 cheshire | |
1098 | <rdar://problem/3324795> Duplicate query suppression not working right | |
1099 | Refinement: Don't record DS information for a question in the first quarter second | |
1100 | right after we send it -- in the case where a question happens to be accelerated by | |
1101 | the maximum allowed amount, we don't want it to then be suppressed because the previous | |
1102 | time *we* sent that question falls (just) within the valid duplicate suppression window. | |
1103 | ||
1104 | Revision 1.217 2003/07/13 04:43:53 cheshire | |
1105 | <rdar://problem/3325169> Services on multiple interfaces not always resolving | |
1106 | Minor refinement: No need to make address query broader than the original SRV query that provoked it | |
1107 | ||
1108 | Revision 1.216 2003/07/13 03:13:17 cheshire | |
1109 | <rdar://problem/3325169> Services on multiple interfaces not always resolving | |
1110 | If we get an identical SRV on a second interface, convert address queries to non-specific | |
1111 | ||
1112 | Revision 1.215 2003/07/13 02:28:00 cheshire | |
1113 | <rdar://problem/3325166> SendResponses didn't all its responses | |
1114 | Delete all references to RRInterfaceActive -- it's now superfluous | |
1115 | ||
1116 | Revision 1.214 2003/07/13 01:47:53 cheshire | |
1117 | Fix one error and one warning in the Windows build | |
1118 | ||
1119 | Revision 1.213 2003/07/12 04:25:48 cheshire | |
1120 | Fix minor signed/unsigned warnings | |
1121 | ||
1122 | Revision 1.212 2003/07/12 01:59:11 cheshire | |
1123 | Minor changes to debugf messages | |
1124 | ||
1125 | Revision 1.211 2003/07/12 01:47:01 cheshire | |
1126 | <rdar://problem/3324495> After name conflict, appended number should be higher than previous number | |
1127 | ||
1128 | Revision 1.210 2003/07/12 01:43:28 cheshire | |
1129 | <rdar://problem/3324795> Duplicate query suppression not working right | |
1130 | The correct cutoff time for duplicate query suppression is timenow less one-half the query interval. | |
1131 | The code was incorrectly using the last query time plus one-half the query interval. | |
1132 | This was only correct in the case where query acceleration was not in effect. | |
1133 | ||
1134 | Revision 1.209 2003/07/12 01:27:50 cheshire | |
1135 | <rdar://problem/3320079> Hostname conflict naming should not use two hyphens | |
1136 | Fix missing "-1" in RemoveLabelSuffix() | |
1137 | ||
1138 | Revision 1.208 2003/07/11 01:32:38 cheshire | |
1139 | Syntactic cleanup (no change to funcationality): Now that we only have one host name, | |
1140 | rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". | |
1141 | ||
1142 | Revision 1.207 2003/07/11 01:28:00 cheshire | |
1143 | <rdar://problem/3161289> No more local.arpa | |
1144 | ||
1145 | Revision 1.206 2003/07/11 00:45:02 cheshire | |
1146 | <rdar://problem/3321909> Client should get callback confirming successful host name registration | |
1147 | ||
1148 | Revision 1.205 2003/07/11 00:40:18 cheshire | |
1149 | Tidy up debug message in HostNameCallback() | |
1150 | ||
1151 | Revision 1.204 2003/07/11 00:20:32 cheshire | |
1152 | <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes | |
1153 | ||
1154 | Revision 1.203 2003/07/10 23:53:41 cheshire | |
1155 | <rdar://problem/3320079> Hostname conflict naming should not use two hyphens | |
1156 | ||
1157 | Revision 1.202 2003/07/04 02:23:20 cheshire | |
1158 | <rdar://problem/3311955> Responder too aggressive at flushing stale data | |
1159 | Changed mDNSResponder to require four unanswered queries before purging a record, instead of two. | |
1160 | ||
1161 | Revision 1.201 2003/07/04 01:09:41 cheshire | |
1162 | <rdar://problem/3315775> Need to implement subtype queries | |
1163 | Modified ConstructServiceName() to allow three-part service types | |
1164 | ||
1165 | Revision 1.200 2003/07/03 23:55:26 cheshire | |
1166 | Minor change to wording of syslog warning messages | |
1167 | ||
1168 | Revision 1.199 2003/07/03 23:51:13 cheshire | |
1169 | <rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings | |
1170 | Added more detailed debugging information | |
1171 | ||
1172 | Revision 1.198 2003/07/03 22:19:30 cheshire | |
1173 | <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo | |
1174 | Make exception to allow _tivo_servemedia._tcp. | |
1175 | ||
1176 | Revision 1.197 2003/07/02 22:33:05 cheshire | |
1177 | <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed | |
1178 | Minor refinements: | |
1179 | When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not | |
1180 | Allow cache to grow to 512 records before considering it a potential denial-of-service attack | |
1181 | ||
1182 | Revision 1.196 2003/07/02 21:19:45 cheshire | |
1183 | <rdar://problem/3313413> Update copyright notices, etc., in source code comments | |
1184 | ||
1185 | Revision 1.195 2003/07/02 19:56:58 cheshire | |
1186 | <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed | |
1187 | Minor refinement: m->rrcache_active was not being decremented when | |
1188 | an active record was deleted because its TTL expired | |
1189 | ||
1190 | Revision 1.194 2003/07/02 18:47:40 cheshire | |
1191 | Minor wording change to log messages | |
1192 | ||
1193 | Revision 1.193 2003/07/02 02:44:13 cheshire | |
1194 | Fix warning in non-debug build | |
1195 | ||
1196 | Revision 1.192 2003/07/02 02:41:23 cheshire | |
1197 | <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed | |
1198 | ||
1199 | Revision 1.191 2003/07/02 02:30:51 cheshire | |
1200 | HashSlot() returns an array index. It can't be negative; hence it should not be signed. | |
1201 | ||
1202 | Revision 1.190 2003/06/27 00:03:05 vlubet | |
1203 | <rdar://problem/3304625> Merge of build failure fix for gcc 3.3 | |
1204 | ||
1205 | Revision 1.189 2003/06/11 19:24:03 cheshire | |
1206 | <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces | |
1207 | Slight refinement to previous checkin | |
1208 | ||
1209 | Revision 1.188 2003/06/10 20:33:28 cheshire | |
1210 | <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces | |
1211 | ||
1212 | Revision 1.187 2003/06/10 04:30:44 cheshire | |
1213 | <rdar://problem/3286234> Need to re-probe/re-announce on configuration change | |
1214 | Only interface-specific records were re-probing and re-announcing, not non-specific records. | |
1215 | ||
1216 | Revision 1.186 2003/06/10 04:24:39 cheshire | |
1217 | <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache | |
1218 | Some additional refinements: | |
1219 | Don't try to do this for unicast-response queries | |
1220 | better tracking of Qs and KAs in multi-packet KA lists | |
1221 | ||
1222 | Revision 1.185 2003/06/10 03:52:49 cheshire | |
1223 | Update comments and debug messages | |
1224 | ||
1225 | Revision 1.184 2003/06/10 02:26:39 cheshire | |
1226 | <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function | |
1227 | Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines | |
1228 | ||
1229 | Revision 1.183 2003/06/09 18:53:13 cheshire | |
1230 | Simplify some debugf() statements (replaced block of 25 lines with 2 lines) | |
1231 | ||
1232 | Revision 1.182 2003/06/09 18:38:42 cheshire | |
1233 | <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network | |
1234 | Only issue a correction if the TTL in the proxy packet is less than half the correct value. | |
1235 | ||
1236 | Revision 1.181 2003/06/07 06:45:05 cheshire | |
1237 | <rdar://problem/3283666> No need for multiple machines to all be sending the same queries | |
1238 | ||
1239 | Revision 1.180 2003/06/07 06:31:07 cheshire | |
1240 | Create little four-line helper function "FindIdenticalRecordInCache()" | |
1241 | ||
1242 | Revision 1.179 2003/06/07 06:28:13 cheshire | |
1243 | For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq" | |
1244 | ||
1245 | Revision 1.178 2003/06/07 06:25:12 cheshire | |
1246 | Update some comments | |
1247 | ||
1248 | Revision 1.177 2003/06/07 04:50:53 cheshire | |
1249 | <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache | |
1250 | ||
1251 | Revision 1.176 2003/06/07 04:33:26 cheshire | |
1252 | <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records | |
1253 | Minor change: Increment/decrement logic for q->CurrentAnswers should be in | |
1254 | CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord() | |
1255 | ||
1256 | Revision 1.175 2003/06/07 04:11:52 cheshire | |
1257 | Minor changes to comments and debug messages | |
1258 | ||
1259 | Revision 1.174 2003/06/07 01:46:38 cheshire | |
1260 | <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records | |
1261 | ||
1262 | Revision 1.173 2003/06/07 01:22:13 cheshire | |
1263 | <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function | |
1264 | ||
1265 | Revision 1.172 2003/06/07 00:59:42 cheshire | |
1266 | <rdar://problem/3283454> Need some randomness to spread queries on the network | |
1267 | ||
1268 | Revision 1.171 2003/06/06 21:41:10 cheshire | |
1269 | For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines | |
1270 | ||
1271 | Revision 1.170 2003/06/06 21:38:55 cheshire | |
1272 | Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we | |
1273 | already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) | |
1274 | ||
1275 | Revision 1.169 2003/06/06 21:35:55 cheshire | |
1276 | Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget | |
1277 | (the target is a domain name, but not necessarily a host name) | |
1278 | ||
1279 | Revision 1.168 2003/06/06 21:33:31 cheshire | |
1280 | Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval" | |
1281 | ||
1282 | Revision 1.167 2003/06/06 21:30:42 cheshire | |
1283 | <rdar://problem/3282962> Don't delay queries for shared record types | |
1284 | ||
1285 | Revision 1.166 2003/06/06 17:20:14 cheshire | |
1286 | For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass | |
1287 | (Global search-and-replace; no functional change to code execution.) | |
1288 | ||
1289 | Revision 1.165 2003/06/04 02:53:21 cheshire | |
1290 | Add some "#pragma warning" lines so it compiles clean on Microsoft compilers | |
1291 | ||
1292 | Revision 1.164 2003/06/04 01:25:33 cheshire | |
1293 | <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages | |
1294 | Display time interval between first and subsequent queries | |
1295 | ||
1296 | Revision 1.163 2003/06/03 19:58:14 cheshire | |
1297 | <rdar://problem/3277665> mDNS_DeregisterService() fixes: | |
1298 | When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet. | |
1299 | Guard against a couple of possible mDNS_DeregisterService() race conditions. | |
1300 | ||
1301 | Revision 1.162 2003/06/03 19:30:39 cheshire | |
1302 | Minor addition refinements for | |
1303 | <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be | |
1304 | ||
1305 | Revision 1.161 2003/06/03 18:29:03 cheshire | |
1306 | Minor changes to comments and debugf() messages | |
1307 | ||
1308 | Revision 1.160 2003/06/03 05:02:16 cheshire | |
1309 | <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be | |
1310 | ||
1311 | Revision 1.159 2003/06/03 03:31:57 cheshire | |
1312 | <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine | |
1313 | ||
1314 | Revision 1.158 2003/06/02 22:57:09 cheshire | |
1315 | Minor clarifying changes to comments and log messages; | |
1316 | IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord() | |
1317 | ||
1318 | Revision 1.157 2003/05/31 00:09:49 cheshire | |
1319 | <rdar://problem/3274862> Add ability to discover what services are on a network | |
1320 | ||
1321 | Revision 1.156 2003/05/30 23:56:49 cheshire | |
1322 | <rdar://problem/3274847> Crash after error in mDNS_RegisterService() | |
1323 | Need to set "sr->Extras = mDNSNULL" before returning | |
1324 | ||
1325 | Revision 1.155 2003/05/30 23:48:00 cheshire | |
1326 | <rdar://problem/3274832> Announcements not properly grouped | |
1327 | Due to inconsistent setting of rr->LastAPTime at different places in the | |
1328 | code, announcements were not properly grouped into a single packet. | |
1329 | Fixed by creating a single routine called InitializeLastAPTime(). | |
1330 | ||
1331 | Revision 1.154 2003/05/30 23:38:14 cheshire | |
1332 | <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records | |
1333 | Wrote buffer[32] where it should have said buffer[64] | |
1334 | ||
1335 | Revision 1.153 2003/05/30 19:10:56 cheshire | |
1336 | <rdar://problem/3274153> ConstructServiceName needs to be more restrictive | |
1337 | ||
1338 | Revision 1.152 2003/05/29 22:39:16 cheshire | |
1339 | <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character | |
1340 | ||
1341 | Revision 1.151 2003/05/29 06:35:42 cheshire | |
1342 | <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record | |
1343 | ||
1344 | Revision 1.150 2003/05/29 06:25:45 cheshire | |
1345 | <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion() | |
1346 | ||
1347 | Revision 1.149 2003/05/29 06:18:39 cheshire | |
1348 | <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv | |
1349 | ||
1350 | Revision 1.148 2003/05/29 06:11:34 cheshire | |
1351 | <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks | |
1352 | ||
1353 | Revision 1.147 2003/05/29 06:01:18 cheshire | |
1354 | Change some debugf() calls to LogMsg() calls to help with debugging | |
1355 | ||
1356 | Revision 1.146 2003/05/28 21:00:44 cheshire | |
1357 | Re-enable "immediate answer burst" debugf message | |
1358 | ||
1359 | Revision 1.145 2003/05/28 20:57:44 cheshire | |
1360 | <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet | |
1361 | known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2 | |
1362 | version of mDNSResponder, so for now we should suppress this warning message. | |
1363 | ||
1364 | Revision 1.144 2003/05/28 18:05:12 cheshire | |
1365 | <rdar://problem/3009899> mDNSResponder allows invalid service registrations | |
1366 | Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names | |
1367 | ||
1368 | Revision 1.143 2003/05/28 04:31:29 cheshire | |
1369 | <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time | |
1370 | ||
1371 | Revision 1.142 2003/05/28 03:13:07 cheshire | |
1372 | <rdar://problem/3009899> mDNSResponder allows invalid service registrations | |
1373 | Require that the transport protocol be _udp or _tcp | |
1374 | ||
1375 | Revision 1.141 2003/05/28 02:19:12 cheshire | |
1376 | <rdar://problem/3270634> Misleading messages generated by iChat | |
1377 | Better fix: Only generate the log message for queries where the TC bit is set. | |
1378 | ||
1379 | Revision 1.140 2003/05/28 01:55:24 cheshire | |
1380 | Minor change to log messages | |
1381 | ||
1382 | Revision 1.139 2003/05/28 01:52:51 cheshire | |
1383 | <rdar://problem/3270634> Misleading messages generated by iChat | |
1384 | ||
1385 | Revision 1.138 2003/05/27 22:35:00 cheshire | |
1386 | <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions | |
1387 | ||
1388 | Revision 1.137 2003/05/27 20:04:33 cheshire | |
1389 | <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf() | |
1390 | ||
1391 | Revision 1.136 2003/05/27 18:50:07 cheshire | |
1392 | <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes | |
1393 | ||
1394 | Revision 1.135 2003/05/26 04:57:28 cheshire | |
1395 | <rdar://problem/3268953> Delay queries when there are already answers in the cache | |
1396 | ||
1397 | Revision 1.134 2003/05/26 04:54:54 cheshire | |
1398 | <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead | |
1399 | Accidentally deleted '%' case from the switch statement | |
1400 | ||
1401 | Revision 1.133 2003/05/26 03:21:27 cheshire | |
1402 | Tidy up address structure naming: | |
1403 | mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) | |
1404 | mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 | |
1405 | mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 | |
1406 | ||
1407 | Revision 1.132 2003/05/26 03:01:26 cheshire | |
1408 | <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead | |
1409 | ||
1410 | Revision 1.131 2003/05/26 00:42:05 cheshire | |
1411 | <rdar://problem/3268876> Temporarily include mDNSResponder version in packets | |
1412 | ||
1413 | Revision 1.130 2003/05/24 16:39:48 cheshire | |
1414 | <rdar://problem/3268631> SendResponses also needs to handle multihoming better | |
1415 | ||
1416 | Revision 1.129 2003/05/23 02:15:37 cheshire | |
1417 | Fixed misleading use of the term "duplicate suppression" where it should have | |
1418 | said "known answer suppression". (Duplicate answer suppression is something | |
1419 | different, and duplicate question suppression is yet another thing, so the use | |
1420 | of the completely vague term "duplicate suppression" was particularly bad.) | |
1421 | ||
1422 | Revision 1.128 2003/05/23 01:55:13 cheshire | |
1423 | <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness | |
1424 | ||
1425 | Revision 1.127 2003/05/23 01:02:15 ksekar | |
8e92c31c | 1426 | <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name |
c9b9ae52 A |
1427 | |
1428 | Revision 1.126 2003/05/22 02:29:22 cheshire | |
1429 | <rdar://problem/2984918> SendQueries needs to handle multihoming better | |
1430 | Complete rewrite of SendQueries. Works much better now :-) | |
1431 | ||
1432 | Revision 1.125 2003/05/22 01:50:45 cheshire | |
1433 | Fix warnings, and improve log messages | |
1434 | ||
1435 | Revision 1.124 2003/05/22 01:41:50 cheshire | |
1436 | DiscardDeregistrations doesn't need InterfaceID parameter | |
1437 | ||
1438 | Revision 1.123 2003/05/22 01:38:55 cheshire | |
1439 | Change bracketing of #pragma mark | |
1440 | ||
1441 | Revision 1.122 2003/05/21 19:59:04 cheshire | |
1442 | <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior | |
1443 | Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character | |
1444 | ||
1445 | Revision 1.121 2003/05/21 17:54:07 ksekar | |
8e92c31c | 1446 | <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior |
c9b9ae52 A |
1447 | New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)" |
1448 | ||
1449 | Revision 1.120 2003/05/19 22:14:14 ksekar | |
1450 | <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type | |
1451 | ||
1452 | Revision 1.119 2003/05/16 01:34:10 cheshire | |
1453 | Fix some warnings | |
1454 | ||
1455 | Revision 1.118 2003/05/14 18:48:40 cheshire | |
1456 | <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations | |
1457 | More minor refinements: | |
7f0064bd | 1458 | mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory |
c9b9ae52 A |
1459 | mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away |
1460 | ||
1461 | Revision 1.117 2003/05/14 07:08:36 cheshire | |
1462 | <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations | |
1463 | Previously, when there was any network configuration change, mDNSResponder | |
1464 | would tear down the entire list of active interfaces and start again. | |
1465 | That was very disruptive, and caused the entire cache to be flushed, | |
1466 | and caused lots of extra network traffic. Now it only removes interfaces | |
1467 | that have really gone, and only adds new ones that weren't there before. | |
1468 | ||
1469 | Revision 1.116 2003/05/14 06:51:56 cheshire | |
7f0064bd | 1470 | <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep |
c9b9ae52 A |
1471 | |
1472 | Revision 1.115 2003/05/14 06:44:31 cheshire | |
1473 | Improve debugging message | |
1474 | ||
1475 | Revision 1.114 2003/05/07 01:47:03 cheshire | |
1476 | <rdar://problem/3250330> Also protect against NULL domainlabels | |
1477 | ||
1478 | Revision 1.113 2003/05/07 00:28:18 cheshire | |
1479 | <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients | |
1480 | ||
1481 | Revision 1.112 2003/05/06 00:00:46 cheshire | |
1482 | <rdar://problem/3248914> Rationalize naming of domainname manipulation functions | |
1483 | ||
1484 | Revision 1.111 2003/05/05 23:42:08 cheshire | |
1485 | <rdar://problem/3245631> Resolves never succeed | |
1486 | Was setting "rr->LastAPTime = timenow - rr->LastAPTime" | |
1487 | instead of "rr->LastAPTime = timenow - rr->ThisAPInterval" | |
1488 | ||
1489 | Revision 1.110 2003/04/30 21:09:59 cheshire | |
1490 | <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names | |
1491 | ||
1492 | Revision 1.109 2003/04/26 02:41:56 cheshire | |
1493 | <rdar://problem/3241281> Change timenow from a local variable to a structure member | |
1494 | ||
1495 | Revision 1.108 2003/04/25 01:45:56 cheshire | |
1496 | <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name | |
1497 | ||
1498 | Revision 1.107 2003/04/25 00:41:31 cheshire | |
1499 | <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future | |
1500 | ||
1501 | Revision 1.106 2003/04/22 03:14:45 cheshire | |
1502 | <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now | |
1503 | ||
1504 | Revision 1.105 2003/04/22 01:07:43 cheshire | |
1505 | <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl | |
1506 | If TTL parameter is zero, leave record TTL unchanged | |
1507 | ||
1508 | Revision 1.104 2003/04/21 19:15:52 cheshire | |
1509 | Fix some compiler warnings | |
1510 | ||
1511 | Revision 1.103 2003/04/19 02:26:35 cheshire | |
8e92c31c | 1512 | <rdar://problem/3233804> Incorrect goodbye packet after conflict |
c9b9ae52 A |
1513 | |
1514 | Revision 1.102 2003/04/17 03:06:28 cheshire | |
8e92c31c | 1515 | <rdar://problem/3231321> No need to query again when a service goes away |
c9b9ae52 A |
1516 | Set UnansweredQueries to 2 when receiving a "goodbye" packet |
1517 | ||
1518 | Revision 1.101 2003/04/15 20:58:31 jgraessl | |
8e92c31c | 1519 | <rdar://problem/3229014> Added a hash to lookup records in the cache. |
c9b9ae52 A |
1520 | |
1521 | Revision 1.100 2003/04/15 18:53:14 cheshire | |
8e92c31c | 1522 | <rdar://problem/3229064> Bug in ScheduleNextTask |
c9b9ae52 A |
1523 | mDNS.c 1.94 incorrectly combined two "if" statements into one. |
1524 | ||
1525 | Revision 1.99 2003/04/15 18:09:13 jgraessl | |
8e92c31c | 1526 | <rdar://problem/3228892> |
c9b9ae52 A |
1527 | Reviewed by: Stuart Cheshire |
1528 | Added code to keep track of when the next cache item will expire so we can | |
1529 | call TidyRRCache only when necessary. | |
1530 | ||
1531 | Revision 1.98 2003/04/03 03:43:55 cheshire | |
1532 | <rdar://problem/3216837> Off-by-one error in probe rate limiting | |
1533 | ||
1534 | Revision 1.97 2003/04/02 01:48:17 cheshire | |
1535 | <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets | |
1536 | Additional fix pointed out by Josh: | |
1537 | Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state | |
1538 | ||
1539 | Revision 1.96 2003/04/01 23:58:55 cheshire | |
1540 | Minor comment changes | |
1541 | ||
1542 | Revision 1.95 2003/04/01 23:46:05 cheshire | |
1543 | <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles | |
1544 | mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface | |
1545 | to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second | |
1546 | window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in | |
1547 | FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed. | |
1548 | ||
1549 | Revision 1.94 2003/03/29 01:55:19 cheshire | |
1550 | <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets | |
1551 | Solution: Major cleanup of packet timing and conflict handling rules | |
1552 | ||
1553 | Revision 1.93 2003/03/28 01:54:36 cheshire | |
1554 | Minor tidyup of IPv6 (AAAA) code | |
1555 | ||
1556 | Revision 1.92 2003/03/27 03:30:55 cheshire | |
1557 | <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash | |
1558 | Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback | |
1559 | Fixes: | |
1560 | 1. Make mDNS_DeregisterInterface() safe to call from a callback | |
8e92c31c | 1561 | 2. Make HostNameCallback() use DeadvertiseInterface() instead |
c9b9ae52 A |
1562 | (it never really needed to deregister the interface at all) |
1563 | ||
1564 | Revision 1.91 2003/03/15 04:40:36 cheshire | |
1565 | Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" | |
1566 | ||
1567 | Revision 1.90 2003/03/14 20:26:37 cheshire | |
1568 | Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") | |
1569 | ||
1570 | Revision 1.89 2003/03/12 19:57:50 cheshire | |
1571 | Fixed typo in debug message | |
1572 | ||
1573 | Revision 1.88 2003/03/12 00:17:44 cheshire | |
1574 | <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records | |
1575 | ||
1576 | Revision 1.87 2003/03/11 01:27:20 cheshire | |
1577 | Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") | |
1578 | ||
1579 | Revision 1.86 2003/03/06 20:44:33 cheshire | |
1580 | Comment tidyup | |
1581 | ||
1582 | Revision 1.85 2003/03/05 03:38:35 cheshire | |
8e92c31c | 1583 | <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found! |
c9b9ae52 A |
1584 | Fixed by leaving client in list after conflict, until client explicitly deallocates |
1585 | ||
1586 | Revision 1.84 2003/03/05 01:27:30 cheshire | |
8e92c31c | 1587 | <rdar://problem/3185482> Different TTL for multicast versus unicast responses |
c9b9ae52 A |
1588 | When building unicast responses, record TTLs are capped to 10 seconds |
1589 | ||
1590 | Revision 1.83 2003/03/04 23:48:52 cheshire | |
8e92c31c | 1591 | <rdar://problem/3188865> Double probes after wake from sleep |
c9b9ae52 A |
1592 | Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another |
1593 | ||
1594 | Revision 1.82 2003/03/04 23:38:29 cheshire | |
8e92c31c | 1595 | <rdar://problem/3099194> mDNSResponder needs performance improvements |
c9b9ae52 A |
1596 | Only set rr->CRActiveQuestion to point to the |
1597 | currently active representative of a question set | |
1598 | ||
1599 | Revision 1.81 2003/02/21 03:35:34 cheshire | |
8e92c31c | 1600 | <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section |
c9b9ae52 A |
1601 | |
1602 | Revision 1.80 2003/02/21 02:47:53 cheshire | |
8e92c31c | 1603 | <rdar://problem/3099194> mDNSResponder needs performance improvements |
c9b9ae52 A |
1604 | Several places in the code were calling CacheRRActive(), which searched the entire |
1605 | question list every time, to see if this cache resource record answers any question. | |
1606 | Instead, we now have a field "CRActiveQuestion" in the resource record structure | |
1607 | ||
1608 | Revision 1.79 2003/02/21 01:54:07 cheshire | |
8e92c31c | 1609 | <rdar://problem/3099194> mDNSResponder needs performance improvements |
c9b9ae52 A |
1610 | Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") |
1611 | ||
1612 | Revision 1.78 2003/02/20 06:48:32 cheshire | |
8e92c31c | 1613 | <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations |
c9b9ae52 A |
1614 | Reviewed by: Josh Graessley, Bob Bradley |
1615 | ||
1616 | Revision 1.77 2003/01/31 03:35:59 cheshire | |
8e92c31c | 1617 | <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results |
c9b9ae52 A |
1618 | When there were *two* active questions in the list, they were incorrectly |
1619 | finding *each other* and *both* being marked as duplicates of another question | |
1620 | ||
1621 | Revision 1.76 2003/01/29 02:46:37 cheshire | |
1622 | Fix for IPv6: | |
1623 | A physical interface is identified solely by its InterfaceID (not by IP and type). | |
1624 | On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. | |
1625 | In cases where the requested outbound protocol (v4 or v6) is not supported on | |
1626 | that InterfaceID, the platform support layer should simply discard that packet. | |
1627 | ||
1628 | Revision 1.75 2003/01/29 01:47:40 cheshire | |
1629 | Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity | |
1630 | ||
1631 | Revision 1.74 2003/01/28 05:26:25 cheshire | |
8e92c31c | 1632 | <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results |
c9b9ae52 A |
1633 | Add 'Active' flag for interfaces |
1634 | ||
1635 | Revision 1.73 2003/01/28 03:45:12 cheshire | |
1636 | Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)" | |
1637 | ||
1638 | Revision 1.72 2003/01/28 01:49:48 cheshire | |
8e92c31c | 1639 | <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results |
c9b9ae52 A |
1640 | FindDuplicateQuestion() was incorrectly finding the question itself in the list, |
1641 | and incorrectly marking it as a duplicate (of itself), so that it became inactive. | |
1642 | ||
1643 | Revision 1.71 2003/01/28 01:41:44 cheshire | |
8e92c31c | 1644 | <rdar://problem/3153091> Race condition when network change causes bad stuff |
c9b9ae52 A |
1645 | When an interface goes away, interface-specific questions on that interface become orphaned. |
1646 | Orphan questions cause HaveQueries to return true, but there's no interface to send them on. | |
1647 | Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions() | |
1648 | ||
1649 | Revision 1.70 2003/01/23 19:00:20 cheshire | |
1650 | Protect against infinite loops in mDNS_Execute | |
1651 | ||
1652 | Revision 1.69 2003/01/21 22:56:32 jgraessl | |
8e92c31c | 1653 | <rdar://problem/3124348> service name changes are not properly handled |
c9b9ae52 A |
1654 | Submitted by: Stuart Cheshire |
1655 | Reviewed by: Joshua Graessley | |
1656 | Applying changes for 3124348 to main branch. 3124348 changes went in to a | |
1657 | branch for SU. | |
1658 | ||
1659 | Revision 1.68 2003/01/17 04:09:27 cheshire | |
8e92c31c | 1660 | <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts |
c9b9ae52 A |
1661 | |
1662 | Revision 1.67 2003/01/17 03:56:45 cheshire | |
1663 | Default 24-hour TTL is far too long. Changing to two hours. | |
1664 | ||
1665 | Revision 1.66 2003/01/13 23:49:41 jgraessl | |
1666 | Merged changes for the following fixes in to top of tree: | |
8e92c31c A |
1667 | <rdar://problem/3086540> computer name changes not handled properly |
1668 | <rdar://problem/3124348> service name changes are not properly handled | |
1669 | <rdar://problem/3124352> announcements sent in pairs, failing chattiness test | |
c9b9ae52 A |
1670 | |
1671 | Revision 1.65 2002/12/23 22:13:28 jgraessl | |
1672 | Reviewed by: Stuart Cheshire | |
1673 | Initial IPv6 support for mDNSResponder. | |
1674 | ||
1675 | Revision 1.64 2002/11/26 20:49:06 cheshire | |
8e92c31c | 1676 | <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit |
c9b9ae52 A |
1677 | |
1678 | Revision 1.63 2002/09/21 20:44:49 zarzycki | |
1679 | Added APSL info | |
1680 | ||
1681 | Revision 1.62 2002/09/20 03:25:37 cheshire | |
1682 | Fix some compiler warnings | |
1683 | ||
1684 | Revision 1.61 2002/09/20 01:05:24 cheshire | |
1685 | Don't kill the Extras list in mDNS_DeregisterService() | |
1686 | ||
1687 | Revision 1.60 2002/09/19 23:47:35 cheshire | |
7f0064bd | 1688 | Added mDNS_RegisterNoSuchService() function for assertion of non-existence |
c9b9ae52 A |
1689 | of a particular named service |
1690 | ||
1691 | Revision 1.59 2002/09/19 21:25:34 cheshire | |
1692 | mDNS_snprintf() doesn't need to be in a separate file | |
1693 | ||
1694 | Revision 1.58 2002/09/19 04:20:43 cheshire | |
1695 | Remove high-ascii characters that confuse some systems | |
1696 | ||
1697 | Revision 1.57 2002/09/17 01:07:08 cheshire | |
1698 | Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() | |
1699 | ||
1700 | Revision 1.56 2002/09/16 19:44:17 cheshire | |
1701 | Merge in license terms from Quinn's copy, in preparation for Darwin release | |
1702 | */ | |
1703 | ||
8e92c31c A |
1704 | #include "DNSCommon.h" // Defines general DNS untility routines |
1705 | #include "uDNS.h" // Defines entry points into unicast-specific routines | |
c9b9ae52 | 1706 | // Disable certain benign warnings with Microsoft compilers |
6528fe3e | 1707 | #if(defined(_MSC_VER)) |
c9b9ae52 A |
1708 | // Disable "conditional expression is constant" warning for debug macros. |
1709 | // Otherwise, this generates warnings for the perfectly natural construct "while(1)" | |
1710 | // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know | |
1711 | #pragma warning(disable:4127) | |
1712 | ||
c9b9ae52 A |
1713 | // Disable "assignment within conditional expression". |
1714 | // Other compilers understand the convention that if you place the assignment expression within an extra pair | |
1715 | // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. | |
1716 | // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal | |
1717 | // to the compiler that the assignment is intentional, we have to just turn this warning off completely. | |
1718 | #pragma warning(disable:4706) | |
6528fe3e A |
1719 | #endif |
1720 | ||
6528fe3e | 1721 | // *************************************************************************** |
c9b9ae52 | 1722 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
1723 | #pragma mark - |
1724 | #pragma mark - Program Constants | |
1725 | #endif | |
1726 | ||
c9b9ae52 | 1727 | mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; |
7f0064bd | 1728 | mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; |
c9b9ae52 | 1729 | mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; |
7f0064bd | 1730 | mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; |
c9b9ae52 A |
1731 | mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; |
1732 | mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; | |
8e92c31c A |
1733 | mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; |
1734 | ||
1735 | mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; | |
283ee3ff | 1736 | mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1; |
c9b9ae52 | 1737 | |
8e92c31c | 1738 | mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; |
6528fe3e | 1739 | |
c9d2d929 | 1740 | #define UnicastDNSPortAsNumber 53 |
6528fe3e A |
1741 | #define MulticastDNSPortAsNumber 5353 |
1742 | mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; | |
1743 | mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; | |
c9b9ae52 | 1744 | mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; |
c9d2d929 A |
1745 | mDNSexport const mDNSv4Addr AllDNSLinkGroupv4 = { { 224, 0, 0, 251 } }; |
1746 | mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } }; | |
c9b9ae52 A |
1747 | mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; |
1748 | mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; | |
6528fe3e | 1749 | |
c9d2d929 A |
1750 | mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; |
1751 | mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; | |
1752 | mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; | |
1753 | mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; | |
1754 | mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; | |
1755 | mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; | |
6528fe3e | 1756 | |
c9b9ae52 A |
1757 | // Any records bigger than this are considered 'large' records |
1758 | #define SmallRecordLimit 1024 | |
1759 | ||
c9b9ae52 | 1760 | #define kMaxUpdateCredits 10 |
7f0064bd | 1761 | #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) |
c9b9ae52 | 1762 | |
283ee3ff | 1763 | mDNSexport const char *const mDNS_DomainTypeNames[] = |
6528fe3e | 1764 | { |
283ee3ff A |
1765 | "b._dns-sd._udp.", // Browse |
1766 | "db._dns-sd._udp.", // Default Browse | |
1767 | "lb._dns-sd._udp.", // Legacy Browse | |
1768 | "r._dns-sd._udp.", // Registration | |
1769 | "dr._dns-sd._udp." // Default Registration | |
6528fe3e A |
1770 | }; |
1771 | ||
7f0064bd A |
1772 | #ifdef UNICAST_DISABLED |
1773 | #define uDNS_IsActiveQuery(q, u) mDNSfalse | |
1774 | #endif | |
1775 | ||
6528fe3e | 1776 | // *************************************************************************** |
c9b9ae52 A |
1777 | #if COMPILER_LIKES_PRAGMA_MARK |
1778 | #pragma mark - | |
1779 | #pragma mark - Specialized mDNS version of vsnprintf | |
1780 | #endif | |
1781 | ||
1782 | static const struct mDNSprintf_format | |
1783 | { | |
1784 | unsigned leftJustify : 1; | |
1785 | unsigned forceSign : 1; | |
1786 | unsigned zeroPad : 1; | |
1787 | unsigned havePrecision : 1; | |
1788 | unsigned hSize : 1; | |
1789 | unsigned lSize : 1; | |
1790 | char altForm; | |
1791 | char sign; // +, - or space | |
1792 | unsigned int fieldWidth; | |
1793 | unsigned int precision; | |
8e92c31c | 1794 | } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
c9b9ae52 A |
1795 | |
1796 | mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) | |
1797 | { | |
1798 | mDNSu32 nwritten = 0; | |
1799 | int c; | |
8e92c31c | 1800 | if (buflen == 0) return(0); |
28f7d060 | 1801 | buflen--; // Pre-reserve one space in the buffer for the terminating null |
8e92c31c | 1802 | if (buflen == 0) goto exit; |
c9b9ae52 A |
1803 | |
1804 | for (c = *fmt; c != 0; c = *++fmt) | |
1805 | { | |
1806 | if (c != '%') | |
1807 | { | |
1808 | *sbuffer++ = (char)c; | |
1809 | if (++nwritten >= buflen) goto exit; | |
1810 | } | |
1811 | else | |
1812 | { | |
1813 | unsigned int i=0, j; | |
1814 | // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for | |
1815 | // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. | |
1816 | // The size needs to be enough for a 256-byte domain name plus some error text. | |
1817 | #define mDNS_VACB_Size 300 | |
1818 | char mDNS_VACB[mDNS_VACB_Size]; | |
1819 | #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) | |
1820 | #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) | |
1821 | char *s = mDNS_VACB_Lim, *digits; | |
1822 | struct mDNSprintf_format F = mDNSprintf_format_default; | |
1823 | ||
1824 | while (1) // decode flags | |
1825 | { | |
1826 | c = *++fmt; | |
1827 | if (c == '-') F.leftJustify = 1; | |
1828 | else if (c == '+') F.forceSign = 1; | |
1829 | else if (c == ' ') F.sign = ' '; | |
1830 | else if (c == '#') F.altForm++; | |
1831 | else if (c == '0') F.zeroPad = 1; | |
1832 | else break; | |
1833 | } | |
1834 | ||
1835 | if (c == '*') // decode field width | |
1836 | { | |
1837 | int f = va_arg(arg, int); | |
1838 | if (f < 0) { f = -f; F.leftJustify = 1; } | |
1839 | F.fieldWidth = (unsigned int)f; | |
1840 | c = *++fmt; | |
1841 | } | |
1842 | else | |
1843 | { | |
1844 | for (; c >= '0' && c <= '9'; c = *++fmt) | |
1845 | F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); | |
1846 | } | |
1847 | ||
1848 | if (c == '.') // decode precision | |
1849 | { | |
1850 | if ((c = *++fmt) == '*') | |
1851 | { F.precision = va_arg(arg, unsigned int); c = *++fmt; } | |
1852 | else for (; c >= '0' && c <= '9'; c = *++fmt) | |
1853 | F.precision = (10 * F.precision) + (c - '0'); | |
1854 | F.havePrecision = 1; | |
1855 | } | |
1856 | ||
1857 | if (F.leftJustify) F.zeroPad = 0; | |
1858 | ||
1859 | conv: | |
1860 | switch (c) // perform appropriate conversion | |
1861 | { | |
1862 | unsigned long n; | |
1863 | case 'h' : F.hSize = 1; c = *++fmt; goto conv; | |
1864 | case 'l' : // fall through | |
1865 | case 'L' : F.lSize = 1; c = *++fmt; goto conv; | |
1866 | case 'd' : | |
1867 | case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); | |
1868 | else n = (unsigned long)va_arg(arg, int); | |
1869 | if (F.hSize) n = (short) n; | |
1870 | if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } | |
1871 | else if (F.forceSign) F.sign = '+'; | |
1872 | goto decimal; | |
1873 | case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); | |
1874 | else n = va_arg(arg, unsigned int); | |
1875 | if (F.hSize) n = (unsigned short) n; | |
1876 | F.sign = 0; | |
1877 | goto decimal; | |
1878 | decimal: if (!F.havePrecision) | |
1879 | { | |
1880 | if (F.zeroPad) | |
1881 | { | |
1882 | F.precision = F.fieldWidth; | |
1883 | if (F.sign) --F.precision; | |
1884 | } | |
1885 | if (F.precision < 1) F.precision = 1; | |
1886 | } | |
1887 | if (F.precision > mDNS_VACB_Size - 1) | |
1888 | F.precision = mDNS_VACB_Size - 1; | |
1889 | for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); | |
1890 | for (; i < F.precision; i++) *--s = '0'; | |
1891 | if (F.sign) { *--s = F.sign; i++; } | |
1892 | break; | |
1893 | ||
1894 | case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); | |
1895 | else n = va_arg(arg, unsigned int); | |
1896 | if (F.hSize) n = (unsigned short) n; | |
1897 | if (!F.havePrecision) | |
1898 | { | |
1899 | if (F.zeroPad) F.precision = F.fieldWidth; | |
1900 | if (F.precision < 1) F.precision = 1; | |
1901 | } | |
1902 | if (F.precision > mDNS_VACB_Size - 1) | |
1903 | F.precision = mDNS_VACB_Size - 1; | |
1904 | for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); | |
1905 | if (F.altForm && i && *s != '0') { *--s = '0'; i++; } | |
1906 | for (; i < F.precision; i++) *--s = '0'; | |
1907 | break; | |
1908 | ||
1909 | case 'a' : { | |
1910 | unsigned char *a = va_arg(arg, unsigned char *); | |
1911 | if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } | |
1912 | else | |
1913 | { | |
c9b9ae52 A |
1914 | s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end |
1915 | if (F.altForm) | |
1916 | { | |
1917 | mDNSAddr *ip = (mDNSAddr*)a; | |
c9b9ae52 A |
1918 | switch (ip->type) |
1919 | { | |
8e92c31c A |
1920 | case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; |
1921 | case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; | |
c9b9ae52 A |
1922 | default: F.precision = 0; break; |
1923 | } | |
1924 | } | |
1925 | switch (F.precision) | |
1926 | { | |
1927 | case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", | |
1928 | a[0], a[1], a[2], a[3]); break; | |
1929 | case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", | |
1930 | a[0], a[1], a[2], a[3], a[4], a[5]); break; | |
8e92c31c A |
1931 | case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), |
1932 | "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", | |
1933 | a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], | |
1934 | a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; | |
c9d2d929 A |
1935 | default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size " |
1936 | "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; | |
c9b9ae52 A |
1937 | } |
1938 | } | |
1939 | } | |
1940 | break; | |
1941 | ||
1942 | case 'p' : F.havePrecision = F.lSize = 1; | |
1943 | F.precision = 8; | |
1944 | case 'X' : digits = "0123456789ABCDEF"; | |
1945 | goto hexadecimal; | |
1946 | case 'x' : digits = "0123456789abcdef"; | |
1947 | hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); | |
1948 | else n = va_arg(arg, unsigned int); | |
1949 | if (F.hSize) n = (unsigned short) n; | |
1950 | if (!F.havePrecision) | |
1951 | { | |
1952 | if (F.zeroPad) | |
1953 | { | |
1954 | F.precision = F.fieldWidth; | |
1955 | if (F.altForm) F.precision -= 2; | |
1956 | } | |
1957 | if (F.precision < 1) F.precision = 1; | |
1958 | } | |
1959 | if (F.precision > mDNS_VACB_Size - 1) | |
1960 | F.precision = mDNS_VACB_Size - 1; | |
1961 | for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; | |
1962 | for (; i < F.precision; i++) *--s = '0'; | |
1963 | if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } | |
1964 | break; | |
1965 | ||
1966 | case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; | |
1967 | ||
1968 | case 's' : s = va_arg(arg, char *); | |
1969 | if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } | |
1970 | else switch (F.altForm) | |
1971 | { | |
8e92c31c A |
1972 | case 0: i=0; |
1973 | if (!F.havePrecision) // C string | |
1974 | while(s[i]) i++; | |
1975 | else | |
1976 | { | |
1977 | while ((i < F.precision) && s[i]) i++; | |
1978 | // Make sure we don't truncate in the middle of a UTF-8 character | |
1979 | // If last character we got was any kind of UTF-8 multi-byte character, | |
1980 | // then see if we have to back up. | |
1981 | // This is not as easy as the similar checks below, because | |
1982 | // here we can't assume it's safe to examine the *next* byte, so we | |
1983 | // have to confine ourselves to working only backwards in the string. | |
1984 | j = i; // Record where we got to | |
1985 | // Now, back up until we find first non-continuation-char | |
1986 | while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; | |
1987 | // Now s[i-1] is the first non-continuation-char | |
1988 | // and (j-i) is the number of continuation-chars we found | |
1989 | if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char | |
1990 | { | |
1991 | i--; // Tentatively eliminate this start-char as well | |
1992 | // Now (j-i) is the number of characters we're considering eliminating. | |
1993 | // To be legal UTF-8, the start-char must contain (j-i) one-bits, | |
1994 | // followed by a zero bit. If we shift it right by (7-(j-i)) bits | |
1995 | // (with sign extension) then the result has to be 0xFE. | |
1996 | // If this is right, then we reinstate the tentatively eliminated bytes. | |
1997 | if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; | |
1998 | } | |
1999 | } | |
2000 | break; | |
c9b9ae52 A |
2001 | case 1: i = (unsigned char) *s++; break; // Pascal string |
2002 | case 2: { // DNS label-sequence name | |
2003 | unsigned char *a = (unsigned char *)s; | |
2004 | s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end | |
2005 | if (*a == 0) *s++ = '.'; // Special case for root DNS name | |
2006 | while (*a) | |
2007 | { | |
c9d2d929 A |
2008 | if (*a > 63) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; } |
2009 | if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; } | |
c9b9ae52 A |
2010 | s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a); |
2011 | a += 1 + *a; | |
2012 | } | |
2013 | i = (mDNSu32)(s - mDNS_VACB); | |
2014 | s = mDNS_VACB; // Reset s back to the start of the buffer | |
2015 | break; | |
2016 | } | |
2017 | } | |
8e92c31c A |
2018 | // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) |
2019 | if (F.havePrecision && i > F.precision) | |
c9b9ae52 A |
2020 | { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } |
2021 | break; | |
2022 | ||
2023 | case 'n' : s = va_arg(arg, char *); | |
2024 | if (F.hSize) * (short *) s = (short)nwritten; | |
2025 | else if (F.lSize) * (long *) s = (long)nwritten; | |
2026 | else * (int *) s = (int)nwritten; | |
2027 | continue; | |
2028 | ||
2029 | default: s = mDNS_VACB; | |
2030 | i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c); | |
2031 | ||
2032 | case '%' : *sbuffer++ = (char)c; | |
2033 | if (++nwritten >= buflen) goto exit; | |
2034 | break; | |
2035 | } | |
2036 | ||
2037 | if (i < F.fieldWidth && !F.leftJustify) // Pad on the left | |
2038 | do { | |
2039 | *sbuffer++ = ' '; | |
2040 | if (++nwritten >= buflen) goto exit; | |
2041 | } while (i < --F.fieldWidth); | |
2042 | ||
8e92c31c | 2043 | // Make sure we don't truncate in the middle of a UTF-8 character. |
c9d2d929 A |
2044 | // 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 |
2045 | // 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 | |
2046 | // 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 | 2047 | if (i > buflen - nwritten) |
c9b9ae52 A |
2048 | { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } |
2049 | for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result | |
2050 | nwritten += i; | |
2051 | if (nwritten >= buflen) goto exit; | |
2052 | ||
2053 | for (; i < F.fieldWidth; i++) // Pad on the right | |
2054 | { | |
2055 | *sbuffer++ = ' '; | |
2056 | if (++nwritten >= buflen) goto exit; | |
2057 | } | |
2058 | } | |
2059 | } | |
2060 | exit: | |
2061 | *sbuffer++ = 0; | |
2062 | return(nwritten); | |
2063 | } | |
2064 | ||
2065 | mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) | |
2066 | { | |
2067 | mDNSu32 length; | |
2068 | ||
2069 | va_list ptr; | |
2070 | va_start(ptr,fmt); | |
2071 | length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); | |
2072 | va_end(ptr); | |
2073 | ||
2074 | return(length); | |
2075 | } | |
2076 | ||
2077 | // *************************************************************************** | |
2078 | #if COMPILER_LIKES_PRAGMA_MARK | |
6528fe3e A |
2079 | #pragma mark - |
2080 | #pragma mark - General Utility Functions | |
2081 | #endif | |
2082 | ||
c9b9ae52 A |
2083 | #define InitialQuestionInterval (mDNSPlatformOneSecond/2) |
2084 | #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) | |
2085 | #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0) | |
2086 | ||
2087 | mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) | |
6528fe3e | 2088 | { |
c9b9ae52 A |
2089 | if (ActiveQuestion(q)) |
2090 | if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0) | |
2091 | m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval); | |
6528fe3e A |
2092 | } |
2093 | ||
283ee3ff A |
2094 | mDNSlocal CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) |
2095 | { | |
2096 | CacheGroup *cg; | |
2097 | for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) | |
2098 | if (cg->namehash == namehash && SameDomainName(cg->name, name)) | |
2099 | break; | |
2100 | return(cg); | |
2101 | } | |
2102 | ||
2103 | mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) | |
2104 | { | |
2105 | return(CacheGroupForName(m, slot, rr->namehash, rr->name)); | |
2106 | } | |
2107 | ||
2108 | mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) | |
2109 | { | |
2110 | NetworkInterfaceInfo *intf; | |
2111 | ||
2112 | if (addr->type == mDNSAddrType_IPv4) | |
2113 | { | |
2114 | if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue); | |
2115 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
2116 | if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) | |
2117 | if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) | |
2118 | return(mDNStrue); | |
2119 | } | |
2120 | ||
2121 | if (addr->type == mDNSAddrType_IPv6) | |
2122 | { | |
2123 | if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue); | |
2124 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
2125 | if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) | |
2126 | if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && | |
2127 | (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && | |
2128 | (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && | |
2129 | (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) | |
2130 | return(mDNStrue); | |
2131 | } | |
2132 | ||
2133 | return(mDNSfalse); | |
2134 | } | |
2135 | ||
2136 | // Set up a AuthRecord with sensible default values. | |
2137 | // These defaults may be overwritten with new values before mDNS_Register is called | |
2138 | mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, | |
2139 | mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) | |
2140 | { | |
2141 | mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); | |
2142 | // Don't try to store a TTL bigger than we can represent in platform time units | |
2143 | if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) | |
2144 | ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; | |
2145 | else if (ttl == 0) // And Zero TTL is illegal | |
2146 | ttl = DefaultTTLforRRType(rrtype); | |
2147 | ||
2148 | // Field Group 1: The actual information pertaining to this resource record | |
2149 | rr->resrec.RecordType = RecordType; | |
2150 | rr->resrec.InterfaceID = InterfaceID; | |
2151 | rr->resrec.name = &rr->namestorage; | |
2152 | rr->resrec.rrtype = rrtype; | |
2153 | rr->resrec.rrclass = kDNSClass_IN; | |
2154 | rr->resrec.rroriginalttl = ttl; | |
2155 | // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal | |
2156 | // rr->resrec.rdestimate = set in mDNS_Register_internal | |
2157 | // rr->resrec.rdata = MUST be set by client | |
2158 | ||
2159 | if (RDataStorage) | |
2160 | rr->resrec.rdata = RDataStorage; | |
2161 | else | |
2162 | { | |
2163 | rr->resrec.rdata = &rr->rdatastorage; | |
2164 | rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); | |
2165 | } | |
2166 | ||
2167 | // Field Group 2: Persistent metadata for Authoritative Records | |
2168 | rr->Additional1 = mDNSNULL; | |
2169 | rr->Additional2 = mDNSNULL; | |
2170 | rr->DependentOn = mDNSNULL; | |
2171 | rr->RRSet = mDNSNULL; | |
2172 | rr->RecordCallback = Callback; | |
2173 | rr->RecordContext = Context; | |
2174 | ||
2175 | rr->HostTarget = mDNSfalse; | |
2176 | rr->AllowRemoteQuery = mDNSfalse; | |
2177 | rr->ForceMCast = mDNSfalse; | |
2178 | ||
2179 | // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) | |
2180 | ||
2181 | rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() | |
2182 | } | |
2183 | ||
2184 | // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord | |
2185 | // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion() | |
2186 | mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord) | |
2187 | { | |
2188 | // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it | |
2189 | if (AddRecord) rr->LocalAnswer = mDNStrue; | |
2190 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
2191 | if (q->QuestionCallback) | |
2192 | q->QuestionCallback(m, q, &rr->resrec, AddRecord); | |
2193 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
2194 | } | |
2195 | ||
c9d2d929 A |
2196 | // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers to each, |
2197 | // stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion(). | |
283ee3ff A |
2198 | // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any. |
2199 | // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal() | |
2200 | mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) | |
2201 | { | |
2202 | if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set"); | |
2203 | ||
2204 | m->CurrentQuestion = m->LocalOnlyQuestions; | |
2205 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) | |
2206 | { | |
2207 | DNSQuestion *q = m->CurrentQuestion; | |
2208 | m->CurrentQuestion = q->next; | |
2209 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
2210 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again | |
2211 | } | |
2212 | ||
2213 | // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions | |
2214 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) | |
2215 | { | |
2216 | m->CurrentQuestion = m->Questions; | |
2217 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) | |
2218 | { | |
2219 | DNSQuestion *q = m->CurrentQuestion; | |
2220 | m->CurrentQuestion = q->next; | |
2221 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
2222 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again | |
2223 | } | |
2224 | } | |
2225 | ||
2226 | m->CurrentQuestion = mDNSNULL; | |
2227 | } | |
2228 | ||
6528fe3e | 2229 | // *************************************************************************** |
c9b9ae52 | 2230 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e | 2231 | #pragma mark - |
8e92c31c | 2232 | #pragma mark - Resource Record Utility Functions |
6528fe3e A |
2233 | #endif |
2234 | ||
8e92c31c | 2235 | #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) |
6528fe3e | 2236 | |
8e92c31c A |
2237 | #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \ |
2238 | ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ | |
2239 | ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ | |
2240 | ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) | |
6528fe3e | 2241 | |
8e92c31c A |
2242 | #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \ |
2243 | (ResourceRecordIsValidAnswer(RR) && \ | |
2244 | ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) | |
6528fe3e | 2245 | |
8e92c31c A |
2246 | #define DefaultProbeCountForTypeUnique ((mDNSu8)3) |
2247 | #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) | |
6528fe3e | 2248 | |
8e92c31c | 2249 | #define InitialAnnounceCount ((mDNSu8)10) |
c9b9ae52 | 2250 | |
8e92c31c A |
2251 | // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. |
2252 | // This means that because the announce interval is doubled after sending the first packet, the first | |
2253 | // observed on-the-wire inter-packet interval between announcements is actually one second. | |
2254 | // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent. | |
2255 | #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4) | |
2256 | #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) | |
2257 | #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) | |
6528fe3e | 2258 | |
8e92c31c A |
2259 | #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ |
2260 | (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ | |
2261 | (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) | |
6528fe3e | 2262 | |
8e92c31c A |
2263 | #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) |
2264 | #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) | |
2265 | #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond) | |
2266 | #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR)) | |
6528fe3e | 2267 | |
8e92c31c | 2268 | #define MaxUnansweredQueries 4 |
c9b9ae52 | 2269 | |
8e92c31c A |
2270 | // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent |
2271 | // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match). | |
2272 | // TTL and rdata may differ. | |
2273 | // This is used for cache flush management: | |
2274 | // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent | |
2275 | // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed | |
2276 | mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2) | |
c9b9ae52 | 2277 | { |
8e92c31c A |
2278 | if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } |
2279 | if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } | |
2280 | if (r1->InterfaceID && | |
2281 | r2->InterfaceID && | |
2282 | r1->InterfaceID != r2->InterfaceID) return(mDNSfalse); | |
c9d2d929 | 2283 | return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(r1->name, r2->name)); |
c9b9ae52 A |
2284 | } |
2285 | ||
7f0064bd A |
2286 | // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our |
2287 | // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have | |
2288 | // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict. | |
2289 | // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY, | |
2290 | // so a response of any type should match, even if it is not actually the type the client plans to use. | |
8e92c31c | 2291 | mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) |
c9b9ae52 | 2292 | { |
8e92c31c A |
2293 | if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } |
2294 | if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } | |
2295 | if (pktrr->resrec.InterfaceID && | |
2296 | authrr->resrec.InterfaceID && | |
2297 | pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); | |
7f0064bd | 2298 | if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); |
c9d2d929 | 2299 | return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(pktrr->resrec.name, authrr->resrec.name)); |
c9b9ae52 | 2300 | } |
6528fe3e | 2301 | |
8e92c31c A |
2302 | // IdenticalResourceRecord returns true if two resources records have |
2303 | // the same name, type, class, and identical rdata (InterfaceID and TTL may differ) | |
2304 | mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2) | |
c9b9ae52 | 2305 | { |
8e92c31c A |
2306 | if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); } |
2307 | if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); } | |
c9d2d929 | 2308 | if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name)) return(mDNSfalse); |
8e92c31c | 2309 | return(SameRData(r1, r2)); |
6528fe3e A |
2310 | } |
2311 | ||
8e92c31c A |
2312 | // CacheRecord *ks is the CacheRecord from the known answer list in the query. |
2313 | // This is the information that the requester believes to be correct. | |
2314 | // AuthRecord *rr is the answer we are proposing to give, if not suppressed. | |
2315 | // This is the information that we believe to be correct. | |
2316 | // We've already determined that we plan to give this answer on this interface | |
2317 | // (either the record is non-specific, or it is specific to this interface) | |
2318 | // so now we just need to check the name, type, class, rdata and TTL. | |
2319 | mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr) | |
c9b9ae52 | 2320 | { |
8e92c31c | 2321 | // If RR signature is different, or data is different, then don't suppress our answer |
283ee3ff | 2322 | if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse); |
8e92c31c A |
2323 | |
2324 | // If the requester's indicated TTL is less than half the real TTL, | |
2325 | // we need to give our answer before the requester's copy expires. | |
2326 | // If the requester's indicated TTL is at least half the real TTL, | |
2327 | // then we can suppress our answer this time. | |
2328 | // If the requester's indicated TTL is greater than the TTL we believe, | |
2329 | // then that's okay, and we don't need to do anything about it. | |
2330 | // (If two responders on the network are offering the same information, | |
2331 | // that's okay, and if they are offering the information with different TTLs, | |
2332 | // the one offering the lower TTL should defer to the one offering the higher TTL.) | |
2333 | return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); | |
c9b9ae52 | 2334 | } |
6528fe3e | 2335 | |
8e92c31c | 2336 | mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) |
6528fe3e | 2337 | { |
8e92c31c | 2338 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) |
6528fe3e | 2339 | { |
c9d2d929 | 2340 | //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); |
8e92c31c A |
2341 | if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) |
2342 | m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); | |
c9b9ae52 A |
2343 | } |
2344 | else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr)) | |
2345 | { | |
2346 | if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) | |
2347 | m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); | |
2348 | } | |
6528fe3e A |
2349 | } |
2350 | ||
c9b9ae52 | 2351 | mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) |
6528fe3e | 2352 | { |
c9b9ae52 A |
2353 | // To allow us to aggregate probes when a group of services are registered together, |
2354 | // the first probe is delayed 1/4 second. This means the common-case behaviour is: | |
2355 | // 1/4 second wait; probe | |
2356 | // 1/4 second wait; probe | |
2357 | // 1/4 second wait; probe | |
2358 | // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) | |
2359 | ||
2360 | // If we have no probe suppression time set, or it is in the past, set it now | |
2361 | if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) | |
6528fe3e | 2362 | { |
7f0064bd | 2363 | m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique); |
c9b9ae52 A |
2364 | // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation |
2365 | if (m->SuppressProbes - m->NextScheduledProbe >= 0) | |
2366 | m->SuppressProbes = m->NextScheduledProbe; | |
2367 | // If we already have a query scheduled to go out sooner, then use that time to get better aggregation | |
2368 | if (m->SuppressProbes - m->NextScheduledQuery >= 0) | |
2369 | m->SuppressProbes = m->NextScheduledQuery; | |
6528fe3e | 2370 | } |
6528fe3e | 2371 | |
c9b9ae52 A |
2372 | // We announce to flush stale data from other caches. It is a reasonable assumption that any |
2373 | // old stale copies will probably have the same TTL we're using, so announcing longer than | |
2374 | // this serves no purpose -- any stale copies of that record will have expired by then anyway. | |
2375 | rr->AnnounceUntil = m->timenow + TicksTTL(rr); | |
2376 | rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; | |
2377 | // Set LastMCTime to now, to inhibit multicast responses | |
2378 | // (no need to send additional multicast responses when we're announcing anyway) | |
2379 | rr->LastMCTime = m->timenow; | |
2380 | rr->LastMCInterface = mDNSInterfaceMark; | |
2381 | ||
2382 | // If this is a record type that's not going to probe, then delay its first announcement so that | |
2383 | // it will go out synchronized with the first announcement for the other records that *are* probing. | |
2384 | // This is a minor performance tweak that helps keep groups of related records synchronized together. | |
2385 | // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are | |
2386 | // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. | |
2387 | // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, | |
2388 | // because they will meet the criterion of being at least half-way to their scheduled announcement time. | |
2389 | if (rr->resrec.RecordType != kDNSRecordTypeUnique) | |
2390 | rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; | |
2391 | ||
2392 | SetNextAnnounceProbeTime(m, rr); | |
6528fe3e A |
2393 | } |
2394 | ||
8e92c31c | 2395 | #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) |
c9b9ae52 A |
2396 | |
2397 | mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) | |
2398 | { | |
2399 | domainname *target = GetRRDomainNameTarget(&rr->resrec); | |
2400 | ||
2401 | if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); | |
2402 | ||
7f0064bd | 2403 | if (target && SameDomainName(target, &m->MulticastHostname)) |
283ee3ff | 2404 | debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c); |
c9b9ae52 | 2405 | |
7f0064bd | 2406 | if (target && !SameDomainName(target, &m->MulticastHostname)) |
c9b9ae52 | 2407 | { |
283ee3ff | 2408 | AssignDomainName(target, &m->MulticastHostname); |
c9b9ae52 A |
2409 | SetNewRData(&rr->resrec, mDNSNULL, 0); |
2410 | ||
2411 | // If we're in the middle of probing this record, we need to start again, | |
2412 | // because changing its rdata may change the outcome of the tie-breaker. | |
2413 | // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) | |
2414 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); | |
2415 | ||
2416 | // If we've announced this record, we really should send a goodbye packet for the old rdata before | |
2417 | // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, | |
2418 | // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. | |
7f0064bd | 2419 | if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared) |
c9d2d929 | 2420 | 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 | 2421 | |
7f0064bd A |
2422 | rr->AnnounceCount = InitialAnnounceCount; |
2423 | rr->RequireGoodbye = mDNSfalse; | |
c9b9ae52 A |
2424 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); |
2425 | InitializeLastAPTime(m,rr); | |
2426 | } | |
2427 | } | |
2428 | ||
7f0064bd | 2429 | mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) |
c9b9ae52 | 2430 | { |
c9b9ae52 A |
2431 | if (!rr->Acknowledged && rr->RecordCallback) |
2432 | { | |
2433 | // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function | |
2434 | // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. | |
2435 | rr->Acknowledged = mDNStrue; | |
2436 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
2437 | rr->RecordCallback(m, rr, mStatus_NoError); | |
2438 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
2439 | } | |
2440 | } | |
2441 | ||
c9b9ae52 | 2442 | // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified |
c9d2d929 A |
2443 | #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified)) |
2444 | #define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec)) | |
c9b9ae52 A |
2445 | |
2446 | mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) | |
6528fe3e | 2447 | { |
c9b9ae52 A |
2448 | domainname *target = GetRRDomainNameTarget(&rr->resrec); |
2449 | AuthRecord *r; | |
2450 | AuthRecord **p = &m->ResourceRecords; | |
2451 | AuthRecord **d = &m->DuplicateRecords; | |
7f0064bd A |
2452 | |
2453 | mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); | |
2454 | ||
2455 | if ((mDNSs32)rr->resrec.rroriginalttl <= 0) | |
2456 | { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } | |
c9b9ae52 | 2457 | |
7f0064bd | 2458 | #ifndef UNICAST_DISABLED |
283ee3ff | 2459 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)) |
8e92c31c A |
2460 | rr->uDNS_info.id = zeroID; |
2461 | else return uDNS_RegisterRecord(m, rr); | |
7f0064bd A |
2462 | #endif |
2463 | ||
6528fe3e | 2464 | while (*p && *p != rr) p=&(*p)->next; |
c9b9ae52 | 2465 | while (*d && *d != rr) d=&(*d)->next; |
283ee3ff | 2466 | if (*d || *p) |
6528fe3e | 2467 | { |
c9d2d929 | 2468 | 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 |
2469 | return(mStatus_AlreadyRegistered); |
2470 | } | |
2471 | ||
2472 | if (rr->DependentOn) | |
2473 | { | |
c9b9ae52 A |
2474 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) |
2475 | rr->resrec.RecordType = kDNSRecordTypeVerified; | |
6528fe3e A |
2476 | else |
2477 | { | |
c9b9ae52 | 2478 | LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", |
283ee3ff | 2479 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e A |
2480 | return(mStatus_Invalid); |
2481 | } | |
c9b9ae52 | 2482 | if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified))) |
6528fe3e | 2483 | { |
c9b9ae52 | 2484 | LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", |
283ee3ff | 2485 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); |
6528fe3e A |
2486 | return(mStatus_Invalid); |
2487 | } | |
2488 | } | |
2489 | ||
c9b9ae52 | 2490 | // If this resource record is referencing a specific interface, make sure it exists |
8e92c31c | 2491 | if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly) |
c9b9ae52 A |
2492 | { |
2493 | NetworkInterfaceInfo *intf; | |
2494 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
2495 | if (intf->InterfaceID == rr->resrec.InterfaceID) break; | |
2496 | if (!intf) | |
2497 | { | |
2498 | debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID); | |
2499 | return(mStatus_BadReferenceErr); | |
2500 | } | |
2501 | } | |
2502 | ||
6528fe3e A |
2503 | rr->next = mDNSNULL; |
2504 | ||
2505 | // Field Group 1: Persistent metadata for Authoritative Records | |
2506 | // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2507 | // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2508 | // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2509 | // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client | |
2510 | // rr->Callback = already set in mDNS_SetupResourceRecord | |
2511 | // rr->Context = already set in mDNS_SetupResourceRecord | |
2512 | // rr->RecordType = already set in mDNS_SetupResourceRecord | |
c9b9ae52 | 2513 | // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client |
7f0064bd | 2514 | // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client |
8e92c31c A |
2515 | // Make sure target is not uninitialized data, or we may crash writing debugging log messages |
2516 | if (rr->HostTarget && target) target->c[0] = 0; | |
6528fe3e A |
2517 | |
2518 | // Field Group 2: Transient state for Authoritative Records | |
2519 | rr->Acknowledged = mDNSfalse; | |
c9b9ae52 A |
2520 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); |
2521 | rr->AnnounceCount = InitialAnnounceCount; | |
7f0064bd | 2522 | rr->RequireGoodbye = mDNSfalse; |
283ee3ff | 2523 | rr->LocalAnswer = mDNSfalse; |
6528fe3e | 2524 | rr->IncludeInProbe = mDNSfalse; |
c9b9ae52 | 2525 | rr->ImmedAnswer = mDNSNULL; |
7f0064bd | 2526 | rr->ImmedUnicast = mDNSfalse; |
c9b9ae52 A |
2527 | rr->ImmedAdditional = mDNSNULL; |
2528 | rr->SendRNow = mDNSNULL; | |
7f0064bd | 2529 | rr->v4Requester = zerov4Addr; |
c9b9ae52 | 2530 | rr->v6Requester = zerov6Addr; |
6528fe3e A |
2531 | rr->NextResponse = mDNSNULL; |
2532 | rr->NR_AnswerTo = mDNSNULL; | |
2533 | rr->NR_AdditionalTo = mDNSNULL; | |
c9b9ae52 | 2534 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); |
8e92c31c | 2535 | if (!rr->HostTarget) InitializeLastAPTime(m, rr); |
c9b9ae52 A |
2536 | // rr->AnnounceUntil = Set for us in InitializeLastAPTime() |
2537 | // rr->LastAPTime = Set for us in InitializeLastAPTime() | |
2538 | // rr->LastMCTime = Set for us in InitializeLastAPTime() | |
2539 | // rr->LastMCInterface = Set for us in InitializeLastAPTime() | |
6528fe3e | 2540 | rr->NewRData = mDNSNULL; |
c9b9ae52 | 2541 | rr->newrdlength = 0; |
6528fe3e | 2542 | rr->UpdateCallback = mDNSNULL; |
c9b9ae52 A |
2543 | rr->UpdateCredits = kMaxUpdateCredits; |
2544 | rr->NextUpdateCredit = 0; | |
2545 | rr->UpdateBlocked = 0; | |
6528fe3e | 2546 | |
c9b9ae52 | 2547 | // rr->resrec.interface = already set in mDNS_SetupResourceRecord |
283ee3ff | 2548 | // rr->resrec.name->c = MUST be set by client |
c9b9ae52 A |
2549 | // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord |
2550 | // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord | |
2551 | // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord | |
2552 | // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set | |
6528fe3e A |
2553 | |
2554 | if (rr->HostTarget) | |
8e92c31c | 2555 | SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); |
6528fe3e A |
2556 | else |
2557 | { | |
c9b9ae52 A |
2558 | rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); |
2559 | rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); | |
6528fe3e | 2560 | } |
6528fe3e | 2561 | |
283ee3ff | 2562 | if (!ValidateDomainName(rr->resrec.name)) |
7f0064bd A |
2563 | { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } |
2564 | ||
7cb34e5c A |
2565 | // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, |
2566 | // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". | |
2567 | // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. | |
7f0064bd | 2568 | if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } |
c9b9ae52 A |
2569 | |
2570 | // Don't do this until *after* we've set rr->resrec.rdlength | |
2571 | if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) | |
7f0064bd | 2572 | { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } |
c9b9ae52 | 2573 | |
283ee3ff | 2574 | rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); |
7cb34e5c | 2575 | rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); |
c9b9ae52 | 2576 | |
8e92c31c | 2577 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) |
c9b9ae52 | 2578 | { |
c9b9ae52 A |
2579 | // If this is supposed to be unique, make sure we don't have any name conflicts |
2580 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
2581 | { | |
2582 | const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; | |
283ee3ff | 2583 | for (r = m->ResourceRecords; r; r=r->next) |
c9b9ae52 A |
2584 | { |
2585 | const AuthRecord *s2 = r->RRSet ? r->RRSet : r; | |
2586 | if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec)) | |
2587 | break; | |
2588 | } | |
283ee3ff | 2589 | if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback |
c9b9ae52 | 2590 | { |
283ee3ff A |
2591 | debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
2592 | rr->resrec.RecordType = kDNSRecordTypeDeregistering; | |
2593 | rr->resrec.rroriginalttl = 0; | |
2594 | rr->ImmedAnswer = mDNSInterfaceMark; | |
2595 | m->NextScheduledResponse = m->timenow; | |
c9b9ae52 A |
2596 | } |
2597 | } | |
2598 | } | |
283ee3ff A |
2599 | |
2600 | // Now that we've finished building our new record, make sure it's not identical to one we already have | |
2601 | for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break; | |
2602 | ||
2603 | if (r) | |
2604 | { | |
7cb34e5c | 2605 | debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr)); |
283ee3ff A |
2606 | *d = rr; |
2607 | // If the previous copy of this record is already verified unique, | |
2608 | // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. | |
2609 | // Setting ProbeCount to zero will cause SendQueries() to advance this record to | |
2610 | // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. | |
2611 | if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) | |
2612 | rr->ProbeCount = 0; | |
2613 | } | |
c9b9ae52 A |
2614 | else |
2615 | { | |
7cb34e5c | 2616 | debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr)); |
283ee3ff A |
2617 | if (!m->NewLocalRecords) m->NewLocalRecords = rr; |
2618 | *p = rr; | |
c9b9ae52 | 2619 | } |
7f0064bd A |
2620 | |
2621 | // For records that are not going to probe, acknowledge them right away | |
283ee3ff A |
2622 | if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) |
2623 | AcknowledgeRecord(m, rr); | |
7f0064bd | 2624 | |
6528fe3e A |
2625 | return(mStatus_NoError); |
2626 | } | |
2627 | ||
c9b9ae52 A |
2628 | mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) |
2629 | { | |
2630 | m->ProbeFailTime = m->timenow; | |
2631 | m->NumFailedProbes++; | |
7f0064bd A |
2632 | // If we've had fifteen or more probe failures, rate-limit to one every five seconds. |
2633 | // If a bunch of hosts have all been configured with the same name, then they'll all | |
2634 | // conflict and run through the same series of names: name-2, name-3, name-4, etc., | |
2635 | // up to name-10. After that they'll start adding random increments in the range 1-100, | |
2636 | // so they're more likely to branch out in the available namespace and settle on a set of | |
2637 | // unique names quickly. If after five more tries the host is still conflicting, then we | |
2638 | // may have a serious problem, so we start rate-limiting so we don't melt down the network. | |
2639 | if (m->NumFailedProbes >= 15) | |
8e92c31c | 2640 | { |
7f0064bd A |
2641 | m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); |
2642 | LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect", | |
283ee3ff | 2643 | m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2644 | } |
2645 | } | |
2646 | ||
2647 | mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr) | |
2648 | { | |
2649 | RData *OldRData = rr->resrec.rdata; | |
2650 | SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata | |
2651 | rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... | |
2652 | if (rr->UpdateCallback) | |
2653 | rr->UpdateCallback(m, rr, OldRData); // ... and let the client know | |
c9b9ae52 A |
2654 | } |
2655 | ||
6528fe3e A |
2656 | // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal |
2657 | // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict | |
2658 | // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered | |
2659 | typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type; | |
2660 | ||
2661 | // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. | |
2662 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 2663 | mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt) |
6528fe3e | 2664 | { |
283ee3ff | 2665 | AuthRecord *r2; |
c9b9ae52 A |
2666 | mDNSu8 RecordType = rr->resrec.RecordType; |
2667 | AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records | |
8e92c31c | 2668 | |
7f0064bd | 2669 | #ifndef UNICAST_DISABLED |
283ee3ff | 2670 | if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))) |
8e92c31c | 2671 | return uDNS_DeregisterRecord(m, rr); |
7f0064bd | 2672 | #endif |
8e92c31c | 2673 | |
c9b9ae52 A |
2674 | while (*p && *p != rr) p=&(*p)->next; |
2675 | ||
2676 | if (*p) | |
6528fe3e | 2677 | { |
c9b9ae52 A |
2678 | // We found our record on the main list. See if there are any duplicates that need special handling. |
2679 | if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment | |
2680 | { | |
283ee3ff A |
2681 | // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished |
2682 | // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory. | |
2683 | for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF; | |
c9b9ae52 A |
2684 | } |
2685 | else | |
2686 | { | |
2687 | // Before we delete the record (and potentially send a goodbye packet) | |
2688 | // first see if we have a record on the duplicate list ready to take over from it. | |
2689 | AuthRecord **d = &m->DuplicateRecords; | |
2690 | while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; | |
2691 | if (*d) | |
2692 | { | |
2693 | AuthRecord *dup = *d; | |
c9d2d929 | 2694 | debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2695 | *d = dup->next; // Cut replacement record from DuplicateRecords list |
2696 | dup->next = rr->next; // And then... | |
2697 | rr->next = dup; // ... splice it in right after the record we're about to delete | |
2698 | dup->resrec.RecordType = rr->resrec.RecordType; | |
7f0064bd A |
2699 | dup->ProbeCount = rr->ProbeCount; |
2700 | dup->AnnounceCount = rr->AnnounceCount; | |
2701 | dup->RequireGoodbye = rr->RequireGoodbye; | |
2702 | dup->ImmedAnswer = rr->ImmedAnswer; | |
2703 | dup->ImmedUnicast = rr->ImmedUnicast; | |
2704 | dup->ImmedAdditional = rr->ImmedAdditional; | |
2705 | dup->v4Requester = rr->v4Requester; | |
2706 | dup->v6Requester = rr->v6Requester; | |
2707 | dup->ThisAPInterval = rr->ThisAPInterval; | |
2708 | dup->AnnounceUntil = rr->AnnounceUntil; | |
2709 | dup->LastAPTime = rr->LastAPTime; | |
2710 | dup->LastMCTime = rr->LastMCTime; | |
2711 | dup->LastMCInterface = rr->LastMCInterface; | |
2712 | rr->RequireGoodbye = mDNSfalse; | |
c9b9ae52 A |
2713 | } |
2714 | } | |
6528fe3e A |
2715 | } |
2716 | else | |
2717 | { | |
c9b9ae52 A |
2718 | // We didn't find our record on the main list; try the DuplicateRecords list instead. |
2719 | p = &m->DuplicateRecords; | |
6528fe3e | 2720 | while (*p && *p != rr) p=&(*p)->next; |
c9b9ae52 | 2721 | // If we found our record on the duplicate list, then make sure we don't send a goodbye for it |
7f0064bd | 2722 | if (*p) rr->RequireGoodbye = mDNSfalse; |
c9d2d929 | 2723 | if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2724 | } |
2725 | ||
2726 | if (!*p) | |
2727 | { | |
2728 | // No need to log an error message if we already know this is a potentially repeated deregistration | |
2729 | if (drt != mDNS_Dereg_repeat) | |
c9d2d929 | 2730 | LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2731 | return(mStatus_BadReferenceErr); |
2732 | } | |
6528fe3e | 2733 | |
c9b9ae52 A |
2734 | // If this is a shared record and we've announced it at least once, |
2735 | // we need to retract that announcement before we delete the record | |
7f0064bd | 2736 | if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) |
c9b9ae52 | 2737 | { |
c9d2d929 | 2738 | verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
2739 | rr->resrec.RecordType = kDNSRecordTypeDeregistering; |
2740 | rr->resrec.rroriginalttl = 0; | |
2741 | rr->ImmedAnswer = mDNSInterfaceMark; | |
283ee3ff A |
2742 | if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) |
2743 | m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); | |
8e92c31c A |
2744 | } |
2745 | else | |
2746 | { | |
2747 | *p = rr->next; // Cut this record from the list | |
2748 | // If someone is about to look at this, bump the pointer forward | |
283ee3ff A |
2749 | if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; |
2750 | if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; | |
8e92c31c | 2751 | rr->next = mDNSNULL; |
6528fe3e | 2752 | |
8e92c31c A |
2753 | if (RecordType == kDNSRecordTypeUnregistered) |
2754 | debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", | |
283ee3ff | 2755 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2756 | else if (RecordType == kDNSRecordTypeDeregistering) |
2757 | debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", | |
283ee3ff | 2758 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2759 | else |
2760 | { | |
c9d2d929 | 2761 | verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
8e92c31c A |
2762 | rr->resrec.RecordType = kDNSRecordTypeUnregistered; |
2763 | } | |
6528fe3e | 2764 | |
8e92c31c A |
2765 | if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) |
2766 | debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", | |
283ee3ff | 2767 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e | 2768 | |
8e92c31c A |
2769 | // If we have an update queued up which never executed, give the client a chance to free that memory |
2770 | if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client | |
2771 | ||
283ee3ff A |
2772 | if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse); |
2773 | ||
8e92c31c A |
2774 | // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function |
2775 | // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. | |
2776 | // In this case the likely client action to the mStatus_MemFree message is to free the memory, | |
2777 | // so any attempt to touch rr after this is likely to lead to a crash. | |
2778 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
283ee3ff A |
2779 | if (drt != mDNS_Dereg_conflict) |
2780 | { | |
2781 | if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this | |
2782 | } | |
2783 | else | |
8e92c31c A |
2784 | { |
2785 | RecordProbeFailure(m, rr); | |
283ee3ff A |
2786 | if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this |
2787 | // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously. | |
2788 | // Note that with all the client callbacks going on, by the time we get here all the | |
2789 | // records we marked may have been explicitly deregistered by the client anyway. | |
2790 | r2 = m->DuplicateRecords; | |
2791 | while (r2) | |
2792 | { | |
2793 | if (r2->ProbeCount != 0xFF) r2 = r2->next; | |
2794 | else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; } | |
2795 | } | |
8e92c31c A |
2796 | } |
2797 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
2798 | } | |
2799 | return(mStatus_NoError); | |
6528fe3e A |
2800 | } |
2801 | ||
2802 | // *************************************************************************** | |
c9b9ae52 | 2803 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
2804 | #pragma mark - |
2805 | #pragma mark - | |
2806 | #pragma mark - Packet Sending Functions | |
2807 | #endif | |
2808 | ||
7f0064bd A |
2809 | mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) |
2810 | { | |
2811 | if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) | |
2812 | { | |
2813 | **nrpp = rr; | |
2814 | // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) | |
2815 | // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does | |
2816 | // The referenced record will definitely be acceptable (by recursive application of this rule) | |
2817 | if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; | |
2818 | rr->NR_AdditionalTo = add; | |
2819 | *nrpp = &rr->NextResponse; | |
2820 | } | |
283ee3ff | 2821 | debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd A |
2822 | } |
2823 | ||
2824 | mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID) | |
2825 | { | |
2826 | AuthRecord *rr, *rr2; | |
2827 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put | |
2828 | { | |
2829 | // (Note: This is an "if", not a "while". If we add a record, we'll find it again | |
2830 | // later in the "for" loop, and we will follow further "additional" links then.) | |
2831 | if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) | |
2832 | AddRecordToResponseList(nrpp, rr->Additional1, rr); | |
2833 | ||
2834 | if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) | |
2835 | AddRecordToResponseList(nrpp, rr->Additional2, rr); | |
2836 | ||
2837 | // For SRV records, automatically add the Address record(s) for the target host | |
2838 | if (rr->resrec.rrtype == kDNSType_SRV) | |
2839 | for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records | |
2840 | if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... | |
2841 | ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... | |
7cb34e5c | 2842 | rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target |
283ee3ff | 2843 | SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name)) |
7f0064bd A |
2844 | AddRecordToResponseList(nrpp, rr2, rr); |
2845 | } | |
2846 | } | |
2847 | ||
2848 | mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) | |
2849 | { | |
2850 | AuthRecord *rr; | |
2851 | AuthRecord *ResponseRecords = mDNSNULL; | |
2852 | AuthRecord **nrp = &ResponseRecords; | |
2853 | ||
2854 | // Make a list of all our records that need to be unicast to this destination | |
2855 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
2856 | { | |
2857 | // If we find we can no longer unicast this answer, clear ImmedUnicast | |
2858 | if (rr->ImmedAnswer == mDNSInterfaceMark || | |
2859 | mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) || | |
2860 | mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) ) | |
2861 | rr->ImmedUnicast = mDNSfalse; | |
2862 | ||
2863 | if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) | |
2864 | if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || | |
2865 | (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) | |
2866 | { | |
2867 | rr->ImmedAnswer = mDNSNULL; // Clear the state fields | |
2868 | rr->ImmedUnicast = mDNSfalse; | |
2869 | rr->v4Requester = zerov4Addr; | |
2870 | rr->v6Requester = zerov6Addr; | |
2871 | if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo | |
2872 | { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } | |
2873 | } | |
2874 | } | |
2875 | ||
2876 | AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); | |
2877 | ||
2878 | while (ResponseRecords) | |
2879 | { | |
2880 | mDNSu8 *responseptr = m->omsg.data; | |
2881 | mDNSu8 *newptr; | |
2882 | InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); | |
2883 | ||
2884 | // Put answers in the packet | |
2885 | while (ResponseRecords && ResponseRecords->NR_AnswerTo) | |
2886 | { | |
2887 | rr = ResponseRecords; | |
2888 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
2889 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it | |
2890 | newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); | |
2891 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state | |
2892 | if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now | |
2893 | if (newptr) responseptr = newptr; | |
2894 | ResponseRecords = rr->NextResponse; | |
2895 | rr->NextResponse = mDNSNULL; | |
2896 | rr->NR_AnswerTo = mDNSNULL; | |
2897 | rr->NR_AdditionalTo = mDNSNULL; | |
2898 | rr->RequireGoodbye = mDNStrue; | |
2899 | } | |
2900 | ||
2901 | // Add additionals, if there's space | |
2902 | while (ResponseRecords && !ResponseRecords->NR_AnswerTo) | |
2903 | { | |
2904 | rr = ResponseRecords; | |
2905 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
2906 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it | |
2907 | newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec); | |
2908 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state | |
2909 | ||
2910 | if (newptr) responseptr = newptr; | |
2911 | if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue; | |
2912 | else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark; | |
2913 | ResponseRecords = rr->NextResponse; | |
2914 | rr->NextResponse = mDNSNULL; | |
2915 | rr->NR_AnswerTo = mDNSNULL; | |
2916 | rr->NR_AdditionalTo = mDNSNULL; | |
2917 | } | |
2918 | ||
2919 | if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL); | |
2920 | } | |
2921 | } | |
2922 | ||
c9b9ae52 | 2923 | mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) |
6528fe3e | 2924 | { |
7f0064bd | 2925 | // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() |
c9b9ae52 | 2926 | // that it should go ahead and immediately dispose of this registration |
7f0064bd A |
2927 | rr->resrec.RecordType = kDNSRecordTypeShared; |
2928 | rr->RequireGoodbye = mDNSfalse; | |
2929 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this | |
6528fe3e A |
2930 | } |
2931 | ||
2932 | // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change | |
2933 | // the record list and/or question list. | |
2934 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 2935 | mDNSlocal void DiscardDeregistrations(mDNS *const m) |
6528fe3e | 2936 | { |
c9b9ae52 | 2937 | if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set"); |
6528fe3e A |
2938 | m->CurrentRecord = m->ResourceRecords; |
2939 | ||
2940 | while (m->CurrentRecord) | |
2941 | { | |
c9b9ae52 | 2942 | AuthRecord *rr = m->CurrentRecord; |
c9b9ae52 | 2943 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) |
7f0064bd | 2944 | CompleteDeregistration(m, rr); // Don't touch rr after this |
283ee3ff A |
2945 | else |
2946 | m->CurrentRecord = rr->next; | |
6528fe3e A |
2947 | } |
2948 | } | |
2949 | ||
8e92c31c A |
2950 | mDNSlocal void GrantUpdateCredit(AuthRecord *rr) |
2951 | { | |
2952 | if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; | |
7f0064bd | 2953 | else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); |
716635cc A |
2954 | } |
2955 | ||
c9b9ae52 A |
2956 | // Note about acceleration of announcements to facilitate automatic coalescing of |
2957 | // multiple independent threads of announcements into a single synchronized thread: | |
2958 | // The announcements in the packet may be at different stages of maturity; | |
2959 | // One-second interval, two-second interval, four-second interval, and so on. | |
2960 | // After we've put in all the announcements that are due, we then consider | |
2961 | // whether there are other nearly-due announcements that are worth accelerating. | |
2962 | // To be eligible for acceleration, a record MUST NOT be older (further along | |
2963 | // its timeline) than the most mature record we've already put in the packet. | |
2964 | // In other words, younger records can have their timelines accelerated to catch up | |
2965 | // with their elder bretheren; this narrows the age gap and helps them eventually get in sync. | |
2966 | // Older records cannot have their timelines accelerated; this would just widen | |
2967 | // the gap between them and their younger bretheren and get them even more out of sync. | |
2968 | ||
2969 | // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change | |
6528fe3e A |
2970 | // the record list and/or question list. |
2971 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 2972 | mDNSlocal void SendResponses(mDNS *const m) |
6528fe3e | 2973 | { |
c9b9ae52 A |
2974 | int pktcount = 0; |
2975 | AuthRecord *rr, *r2; | |
2976 | mDNSs32 maxExistingAnnounceInterval = 0; | |
2977 | const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); | |
6528fe3e | 2978 | |
c9b9ae52 A |
2979 | m->NextScheduledResponse = m->timenow + 0x78000000; |
2980 | ||
7f0064bd A |
2981 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
2982 | if (rr->ImmedUnicast) | |
2983 | { | |
2984 | mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} }; | |
2985 | mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} }; | |
2986 | v4.ip.v4 = rr->v4Requester; | |
2987 | v6.ip.v6 = rr->v6Requester; | |
2988 | if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer); | |
2989 | if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer); | |
2990 | if (rr->ImmedUnicast) | |
2991 | { | |
2992 | LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr)); | |
2993 | rr->ImmedUnicast = mDNSfalse; | |
2994 | } | |
2995 | } | |
2996 | ||
c9b9ae52 A |
2997 | // *** |
2998 | // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on | |
2999 | // *** | |
4e28aad3 | 3000 | |
c9b9ae52 A |
3001 | // Run through our list of records, and decide which ones we're going to announce on all interfaces |
3002 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6528fe3e | 3003 | { |
8e92c31c | 3004 | while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); |
c9b9ae52 A |
3005 | if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr)) |
3006 | { | |
3007 | rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces | |
3008 | if (maxExistingAnnounceInterval < rr->ThisAPInterval) | |
3009 | maxExistingAnnounceInterval = rr->ThisAPInterval; | |
3010 | if (rr->UpdateBlocked) rr->UpdateBlocked = 0; | |
3011 | } | |
3012 | } | |
3013 | ||
3014 | // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) | |
3015 | // Eligible records that are more than half-way to their announcement time are accelerated | |
3016 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3017 | if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || | |
3018 | (rr->ThisAPInterval <= maxExistingAnnounceInterval && | |
3019 | TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && | |
3020 | ResourceRecordIsValidAnswer(rr))) | |
3021 | rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces | |
3022 | ||
8e92c31c A |
3023 | // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals |
3024 | // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, | |
3025 | // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) | |
3026 | // then all that means is that it won't get sent -- which would not be the end of the world. | |
c9b9ae52 A |
3027 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
3028 | if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) | |
3029 | for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records | |
8e92c31c | 3030 | if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ... |
c9b9ae52 A |
3031 | ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... |
3032 | rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... | |
7cb34e5c | 3033 | rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target |
283ee3ff | 3034 | SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) && |
c9b9ae52 | 3035 | (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) |
8e92c31c | 3036 | r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too |
c9b9ae52 A |
3037 | |
3038 | // If there's a record which is supposed to be unique that we're going to send, then make sure that we give | |
3039 | // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class | |
3040 | // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a | |
3041 | // record, then other RRSet members that have not been sent recently will get flushed out of client caches. | |
3042 | // -- 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 | |
3043 | // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces | |
3044 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3045 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) | |
3046 | { | |
3047 | if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked | |
3048 | { | |
3049 | for (r2 = m->ResourceRecords; r2; r2=r2->next) | |
3050 | if (ResourceRecordIsValidAnswer(r2)) | |
c9d2d929 | 3051 | if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) |
c9b9ae52 A |
3052 | r2->ImmedAnswer = rr->ImmedAnswer; |
3053 | } | |
3054 | else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked | |
6528fe3e | 3055 | { |
c9b9ae52 A |
3056 | for (r2 = m->ResourceRecords; r2; r2=r2->next) |
3057 | if (ResourceRecordIsValidAnswer(r2)) | |
3058 | if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) | |
3059 | r2->ImmedAdditional = rr->ImmedAdditional; | |
6528fe3e A |
3060 | } |
3061 | } | |
c9b9ae52 A |
3062 | |
3063 | // Now set SendRNow state appropriately | |
3064 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6528fe3e | 3065 | { |
c9b9ae52 | 3066 | if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces |
6528fe3e | 3067 | { |
c9b9ae52 A |
3068 | rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; |
3069 | rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer | |
3070 | rr->LastMCTime = m->timenow; | |
3071 | rr->LastMCInterface = rr->ImmedAnswer; | |
3072 | // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done | |
3073 | if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) | |
6528fe3e | 3074 | { |
c9b9ae52 A |
3075 | rr->AnnounceCount--; |
3076 | rr->ThisAPInterval *= 2; | |
3077 | rr->LastAPTime = m->timenow; | |
3078 | if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0; | |
283ee3ff | 3079 | debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); |
6528fe3e A |
3080 | } |
3081 | } | |
c9b9ae52 | 3082 | else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: |
6528fe3e | 3083 | { |
8e92c31c | 3084 | rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface |
c9b9ae52 A |
3085 | rr->ImmedAdditional = mDNSNULL; // No need to send as additional too |
3086 | rr->LastMCTime = m->timenow; | |
3087 | rr->LastMCInterface = rr->ImmedAnswer; | |
6528fe3e | 3088 | } |
c9b9ae52 | 3089 | SetNextAnnounceProbeTime(m, rr); |
7f0064bd | 3090 | //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); |
c9b9ae52 A |
3091 | } |
3092 | ||
3093 | // *** | |
3094 | // *** 2. Loop through interface list, sending records as appropriate | |
3095 | // *** | |
3096 | ||
3097 | while (intf) | |
3098 | { | |
3099 | int numDereg = 0; | |
3100 | int numAnnounce = 0; | |
3101 | int numAnswer = 0; | |
7f0064bd | 3102 | mDNSu8 *responseptr = m->omsg.data; |
c9b9ae52 | 3103 | mDNSu8 *newptr; |
7f0064bd | 3104 | InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); |
6528fe3e | 3105 | |
c9b9ae52 A |
3106 | // First Pass. Look for: |
3107 | // 1. Deregistering records that need to send their goodbye packet | |
3108 | // 2. Updated records that need to retract their old data | |
3109 | // 3. Answers and announcements we need to send | |
3110 | // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can | |
3111 | // send this packet and then try again. | |
3112 | // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway, | |
3113 | // because otherwise we'll end up in an infinite loop trying to send a record that will never fit. | |
377735b0 | 3114 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
c9b9ae52 A |
3115 | if (rr->SendRNow == intf->InterfaceID) |
3116 | { | |
3117 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) | |
3118 | { | |
7f0064bd A |
3119 | newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); |
3120 | if (!newptr && m->omsg.h.numAnswers) break; | |
c9b9ae52 A |
3121 | numDereg++; |
3122 | responseptr = newptr; | |
3123 | } | |
c9d2d929 | 3124 | else if (rr->NewRData) // If we have new data for this record |
377735b0 | 3125 | { |
c9b9ae52 A |
3126 | RData *OldRData = rr->resrec.rdata; |
3127 | mDNSu16 oldrdlength = rr->resrec.rdlength; | |
8e92c31c | 3128 | // See if we should send a courtesy "goodbye" for the old data before we replace it. |
7f0064bd | 3129 | if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) |
377735b0 | 3130 | { |
7f0064bd A |
3131 | newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); |
3132 | if (!newptr && m->omsg.h.numAnswers) break; | |
c9b9ae52 | 3133 | numDereg++; |
377735b0 A |
3134 | responseptr = newptr; |
3135 | } | |
c9b9ae52 A |
3136 | // Now try to see if we can fit the update in the same packet (not fatal if we can't) |
3137 | SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); | |
7f0064bd | 3138 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) |
716635cc | 3139 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it |
7f0064bd | 3140 | newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); |
716635cc | 3141 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state |
c9d2d929 | 3142 | if (newptr) responseptr = newptr; |
c9b9ae52 A |
3143 | SetNewRData(&rr->resrec, OldRData, oldrdlength); |
3144 | } | |
3145 | else | |
3146 | { | |
7f0064bd | 3147 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) |
716635cc | 3148 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it |
7f0064bd | 3149 | newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); |
c9b9ae52 | 3150 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state |
7f0064bd | 3151 | if (!newptr && m->omsg.h.numAnswers) break; |
28f7d060 | 3152 | rr->RequireGoodbye = (mDNSu8) (!m->SleepState); |
c9b9ae52 A |
3153 | if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; |
3154 | responseptr = newptr; | |
377735b0 | 3155 | } |
c9b9ae52 A |
3156 | // If sending on all interfaces, go to next interface; else we're finished now |
3157 | if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) | |
3158 | rr->SendRNow = GetNextActiveInterfaceID(intf); | |
3159 | else | |
3160 | rr->SendRNow = mDNSNULL; | |
3161 | } | |
377735b0 | 3162 | |
c9b9ae52 A |
3163 | // Second Pass. Add additional records, if there's space. |
3164 | newptr = responseptr; | |
6528fe3e | 3165 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
c9b9ae52 | 3166 | if (rr->ImmedAdditional == intf->InterfaceID) |
7f0064bd | 3167 | if (ResourceRecordIsValidAnswer(rr)) |
6528fe3e | 3168 | { |
7f0064bd A |
3169 | // If we have at least one answer already in the packet, then plan to add additionals too |
3170 | mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0); | |
3171 | ||
3172 | // If we're not planning to send any additionals, but this record is a unique one, then | |
3173 | // make sure we haven't already sent any other members of its RRSet -- if we have, then they | |
3174 | // will have had the cache flush bit set, so now we need to finish the job and send the rest. | |
3175 | if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)) | |
6528fe3e | 3176 | { |
c9b9ae52 A |
3177 | const AuthRecord *a; |
3178 | for (a = m->ResourceRecords; a; a=a->next) | |
7f0064bd A |
3179 | if (a->LastMCTime == m->timenow && |
3180 | a->LastMCInterface == intf->InterfaceID && | |
3181 | SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; } | |
6528fe3e | 3182 | } |
7f0064bd A |
3183 | if (!SendAdditional) // If we don't want to send this after all, |
3184 | rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field | |
3185 | else if (newptr) // Else, try to add it if we can | |
8e92c31c | 3186 | { |
7f0064bd A |
3187 | if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) |
3188 | rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it | |
3189 | newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); | |
3190 | rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state | |
3191 | if (newptr) | |
3192 | { | |
3193 | responseptr = newptr; | |
3194 | rr->ImmedAdditional = mDNSNULL; | |
3195 | rr->RequireGoodbye = mDNStrue; | |
3196 | // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. | |
3197 | // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, | |
3198 | // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get | |
3199 | // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. | |
3200 | rr->LastMCTime = m->timenow; | |
3201 | rr->LastMCInterface = intf->InterfaceID; | |
3202 | } | |
8e92c31c | 3203 | } |
6528fe3e | 3204 | } |
c9b9ae52 | 3205 | |
7f0064bd | 3206 | if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) |
c9b9ae52 A |
3207 | { |
3208 | debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", | |
3209 | numDereg, numDereg == 1 ? "" : "s", | |
3210 | numAnnounce, numAnnounce == 1 ? "" : "s", | |
3211 | numAnswer, numAnswer == 1 ? "" : "s", | |
7f0064bd A |
3212 | m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); |
3213 | if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); | |
3214 | if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); | |
3215 | if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); | |
3216 | if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } | |
c9b9ae52 A |
3217 | // There might be more things to send on this interface, so go around one more time and try again. |
3218 | } | |
3219 | else // Nothing more to send on this interface; go to next | |
3220 | { | |
3221 | const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); | |
3222 | #if MDNS_DEBUGMSGS && 0 | |
3223 | const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; | |
3224 | debugf(msg, intf, next); | |
3225 | #endif | |
3226 | intf = next; | |
3227 | } | |
6528fe3e A |
3228 | } |
3229 | ||
c9b9ae52 A |
3230 | // *** |
3231 | // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables | |
3232 | // *** | |
6528fe3e | 3233 | |
c9b9ae52 A |
3234 | if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set"); |
3235 | m->CurrentRecord = m->ResourceRecords; | |
3236 | while (m->CurrentRecord) | |
3237 | { | |
3238 | rr = m->CurrentRecord; | |
3239 | m->CurrentRecord = rr->next; | |
3240 | ||
8e92c31c | 3241 | if (rr->SendRNow) |
283ee3ff A |
3242 | { |
3243 | if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) | |
3244 | LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr)); | |
3245 | rr->SendRNow = mDNSNULL; | |
3246 | } | |
c9b9ae52 | 3247 | |
7f0064bd | 3248 | if (rr->ImmedAnswer) |
c9b9ae52 | 3249 | { |
7f0064bd A |
3250 | if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client |
3251 | ||
3252 | if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) | |
3253 | CompleteDeregistration(m, rr); // Don't touch rr after this | |
3254 | else | |
3255 | { | |
3256 | rr->ImmedAnswer = mDNSNULL; | |
3257 | rr->ImmedUnicast = mDNSfalse; | |
3258 | rr->v4Requester = zerov4Addr; | |
3259 | rr->v6Requester = zerov6Addr; | |
3260 | } | |
c9b9ae52 A |
3261 | } |
3262 | } | |
7f0064bd | 3263 | verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow); |
6528fe3e A |
3264 | } |
3265 | ||
c9b9ae52 A |
3266 | // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, |
3267 | // so we want to be lazy about how frequently we do it. | |
3268 | // 1. If a cache record is currently referenced by *no* active questions, | |
3269 | // then we don't mind expiring it up to a minute late (who will know?) | |
3270 | // 2. Else, if a cache record is due for some of its final expiration queries, | |
3271 | // we'll allow them to be late by up to 2% of the TTL | |
3272 | // 3. Else, if a cache record has completed all its final expiration queries without success, | |
3273 | // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late | |
3274 | // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), | |
3275 | // so allow at most 1/10 second lateness | |
3276 | #define CacheCheckGracePeriod(RR) ( \ | |
7f0064bd | 3277 | ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \ |
c9b9ae52 A |
3278 | ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ |
3279 | ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ | |
3280 | ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10)) | |
3281 | ||
3282 | // Note: MUST call SetNextCacheCheckTime any time we change: | |
3283 | // rr->TimeRcvd | |
c9d2d929 | 3284 | // rr->DelayDelivery |
c9b9ae52 A |
3285 | // rr->resrec.rroriginalttl |
3286 | // rr->UnansweredQueries | |
3287 | // rr->CRActiveQuestion | |
3288 | mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) | |
6528fe3e | 3289 | { |
c9b9ae52 | 3290 | rr->NextRequiredQuery = RRExpireTime(rr); |
6528fe3e | 3291 | |
c9b9ae52 A |
3292 | // If we have an active question, then see if we want to schedule a refresher query for this record. |
3293 | // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. | |
3294 | if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) | |
3295 | { | |
3296 | rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); | |
3297 | rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); | |
c9d2d929 A |
3298 | verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec", |
3299 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond); | |
c9b9ae52 A |
3300 | } |
3301 | ||
3302 | if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) | |
3303 | m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); | |
7f0064bd A |
3304 | |
3305 | if (rr->DelayDelivery) | |
3306 | if (m->NextCacheCheck - rr->DelayDelivery > 0) | |
3307 | m->NextCacheCheck = rr->DelayDelivery; | |
c9b9ae52 | 3308 | } |
6528fe3e | 3309 | |
c9d2d929 A |
3310 | #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15) |
3311 | #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5) | |
3312 | #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) | |
6528fe3e | 3313 | |
c9b9ae52 A |
3314 | mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) |
3315 | { | |
3316 | if (interval < kMinimumReconfirmTime) | |
3317 | interval = kMinimumReconfirmTime; | |
3318 | if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below | |
3319 | interval = 0x10000000; | |
3320 | ||
3321 | // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration | |
3322 | if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) | |
6528fe3e | 3323 | { |
c9b9ae52 | 3324 | // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts |
c9d2d929 | 3325 | interval += mDNSRandom(interval/3); |
c9b9ae52 | 3326 | rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; |
c9d2d929 | 3327 | rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond; |
c9b9ae52 | 3328 | SetNextCacheCheckTime(m, rr); |
6528fe3e | 3329 | } |
c9d2d929 | 3330 | debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr)); |
c9b9ae52 | 3331 | return(mStatus_NoError); |
6528fe3e A |
3332 | } |
3333 | ||
c9b9ae52 | 3334 | #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) |
6528fe3e | 3335 | |
c9b9ae52 A |
3336 | // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. |
3337 | // It also appends to the list of known answer records that need to be included, | |
3338 | // and updates the forcast for the size of the known answer section. | |
3339 | mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, | |
3340 | CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) | |
6528fe3e | 3341 | { |
283ee3ff | 3342 | mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; |
c9b9ae52 A |
3343 | mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); |
3344 | const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; | |
3345 | mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); | |
3346 | if (!newptr) | |
3347 | { | |
3348 | debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c); | |
3349 | return(mDNSfalse); | |
3350 | } | |
3351 | else if (newptr + *answerforecast >= limit) | |
3352 | { | |
c9d2d929 | 3353 | verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data); |
c9b9ae52 A |
3354 | query->h.numQuestions--; |
3355 | return(mDNSfalse); | |
3356 | } | |
3357 | else | |
3358 | { | |
3359 | mDNSu32 forecast = *answerforecast; | |
283ee3ff A |
3360 | const mDNSu32 slot = HashSlot(&q->qname); |
3361 | CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); | |
c9b9ae52 A |
3362 | CacheRecord *rr; |
3363 | CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update | |
3364 | ||
283ee3ff | 3365 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, |
c9b9ae52 A |
3366 | if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface |
3367 | rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list | |
3368 | rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet | |
3369 | ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question | |
c9d2d929 | 3370 | rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0) // and it is less than half-way to expiry |
c9b9ae52 A |
3371 | { |
3372 | *ka = rr; // Link this record into our known answer chain | |
3373 | ka = &rr->NextInKAList; | |
3374 | // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) | |
3375 | forecast += 12 + rr->resrec.rdestimate; | |
3376 | // If we're trying to put more than one question in this packet, and it doesn't fit | |
3377 | // then undo that last question and try again next time | |
3378 | if (query->h.numQuestions > 1 && newptr + forecast >= limit) | |
3379 | { | |
3380 | debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", | |
3381 | q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); | |
3382 | query->h.numQuestions--; | |
3383 | ka = *kalistptrptr; // Go back to where we started and retract these answer records | |
3384 | while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } | |
3385 | return(mDNSfalse); // Return false, so we'll try again in the next packet | |
3386 | } | |
3387 | } | |
6528fe3e | 3388 | |
c9b9ae52 A |
3389 | // Traffic reduction: |
3390 | // If we already have at least one unique answer in the cache, | |
3391 | // OR we have so many shared answers that the KA list is too big to fit in one packet | |
3392 | // The we suppress queries number 3 and 5: | |
3393 | // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies) | |
3394 | // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally) | |
3395 | // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress) | |
3396 | // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally) | |
3397 | // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress) | |
3398 | // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally) | |
3399 | if (q->UniqueAnswers || newptr + forecast >= limit) | |
3400 | if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32) | |
6528fe3e | 3401 | { |
c9b9ae52 A |
3402 | query->h.numQuestions--; |
3403 | ka = *kalistptrptr; // Go back to where we started and retract these answer records | |
3404 | while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } | |
3405 | return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question | |
6528fe3e | 3406 | } |
6528fe3e | 3407 | |
c9b9ae52 A |
3408 | // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return |
3409 | *queryptr = newptr; // Update the packet pointer | |
3410 | *answerforecast = forecast; // Update the forecast | |
3411 | *kalistptrptr = ka; // Update the known answer list pointer | |
3412 | if (ucast) m->ExpectUnicastResponse = m->timenow; | |
6528fe3e | 3413 | |
283ee3ff | 3414 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every 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 in the known answer list | |
3417 | ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question | |
3418 | { | |
3419 | rr->UnansweredQueries++; // indicate that we're expecting a response | |
3420 | rr->LastUnansweredTime = m->timenow; | |
3421 | SetNextCacheCheckTime(m, rr); | |
3422 | } | |
3423 | ||
3424 | return(mDNStrue); | |
3425 | } | |
6528fe3e A |
3426 | } |
3427 | ||
c9b9ae52 | 3428 | mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q) |
6528fe3e | 3429 | { |
c9b9ae52 | 3430 | mDNSu32 slot; |
283ee3ff | 3431 | CacheGroup *cg; |
c9b9ae52 A |
3432 | CacheRecord *rr; |
3433 | domainname *target; | |
283ee3ff | 3434 | FORALL_CACHERECORDS(slot, cg, rr) |
7cb34e5c | 3435 | if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname)) |
283ee3ff | 3436 | mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); |
c9b9ae52 A |
3437 | } |
3438 | ||
3439 | // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active | |
3440 | mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time) | |
3441 | { | |
3442 | int i; | |
3443 | for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL; | |
3444 | } | |
3445 | ||
3446 | mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID) | |
3447 | { | |
3448 | int i; | |
3449 | for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL; | |
3450 | } | |
3451 | ||
3452 | mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf) | |
3453 | { | |
3454 | int i; | |
3455 | mDNSBool v4 = !intf->IPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query | |
3456 | mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query | |
3457 | for (i=0; i<DupSuppressInfoSize; i++) | |
3458 | if (ds[i].InterfaceID == intf->InterfaceID) | |
6528fe3e | 3459 | { |
c9b9ae52 A |
3460 | if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue; |
3461 | else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue; | |
3462 | if (v4 && v6) return(mDNStrue); | |
6528fe3e | 3463 | } |
c9b9ae52 A |
3464 | return(mDNSfalse); |
3465 | } | |
3466 | ||
3467 | mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) | |
3468 | { | |
3469 | int i, j; | |
3470 | ||
3471 | // See if we have this one in our list somewhere already | |
3472 | for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break; | |
3473 | ||
3474 | // If not, find a slot we can re-use | |
3475 | if (i >= DupSuppressInfoSize) | |
3476 | { | |
3477 | i = 0; | |
3478 | for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++) | |
3479 | if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0) | |
3480 | i = j; | |
6528fe3e | 3481 | } |
c9b9ae52 A |
3482 | |
3483 | // Record the info about this query we saw | |
3484 | ds[i].Time = Time; | |
3485 | ds[i].InterfaceID = InterfaceID; | |
3486 | ds[i].Type = Type; | |
3487 | ||
3488 | return(i); | |
6528fe3e A |
3489 | } |
3490 | ||
c9b9ae52 | 3491 | mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) |
6528fe3e | 3492 | { |
c9b9ae52 A |
3493 | // If more than 90% of the way to the query time, we should unconditionally accelerate it |
3494 | if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) | |
3495 | return(mDNStrue); | |
3496 | ||
3497 | // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet | |
3498 | if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) | |
6528fe3e | 3499 | { |
c9b9ae52 | 3500 | // We forecast: qname (n) type (2) class (2) |
73792575 | 3501 | mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; |
283ee3ff A |
3502 | const mDNSu32 slot = HashSlot(&q->qname); |
3503 | CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); | |
c9b9ae52 | 3504 | CacheRecord *rr; |
283ee3ff | 3505 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, |
c9b9ae52 A |
3506 | if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet |
3507 | ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question | |
3508 | rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry | |
3509 | rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery | |
6528fe3e | 3510 | { |
c9b9ae52 A |
3511 | // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) |
3512 | forecast += 12 + rr->resrec.rdestimate; | |
3513 | if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate | |
6528fe3e | 3514 | } |
c9b9ae52 | 3515 | return(mDNStrue); |
6528fe3e | 3516 | } |
c9b9ae52 A |
3517 | |
3518 | return(mDNSfalse); | |
6528fe3e A |
3519 | } |
3520 | ||
3521 | // How Standard Queries are generated: | |
3522 | // 1. The Question Section contains the question | |
c9b9ae52 | 3523 | // 2. The Additional Section contains answers we already know, to suppress duplicate responses |
6528fe3e A |
3524 | |
3525 | // How Probe Queries are generated: | |
3526 | // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because | |
3527 | // if some other host is already using *any* records with this name, we want to know about it. | |
3528 | // 2. The Authority Section contains the proposed values we intend to use for one or more | |
3529 | // of our records with that name (analogous to the Update section of DNS Update packets) | |
3530 | // because if some other host is probing at the same time, we each want to know what the other is | |
3531 | // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't. | |
3532 | ||
c9b9ae52 | 3533 | mDNSlocal void SendQueries(mDNS *const m) |
6528fe3e | 3534 | { |
c9b9ae52 | 3535 | int pktcount = 0; |
6528fe3e | 3536 | DNSQuestion *q; |
c9b9ae52 A |
3537 | // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval |
3538 | mDNSs32 maxExistingQuestionInterval = 0; | |
3539 | const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); | |
3540 | CacheRecord *KnownAnswerList = mDNSNULL; | |
3541 | ||
3542 | // 1. If time for a query, work out what we need to do | |
3543 | if (m->timenow - m->NextScheduledQuery >= 0) | |
3544 | { | |
c9d2d929 A |
3545 | mDNSu32 slot; |
3546 | CacheGroup *cg; | |
c9b9ae52 A |
3547 | CacheRecord *rr; |
3548 | m->NextScheduledQuery = m->timenow + 0x78000000; | |
3549 | ||
3550 | // We're expecting to send a query anyway, so see if any expiring cache records are close enough | |
3551 | // to their NextRequiredQuery to be worth batching them together with this one | |
283ee3ff A |
3552 | FORALL_CACHERECORDS(slot, cg, rr) |
3553 | if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) | |
3554 | if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0) | |
3555 | { | |
3556 | q = rr->CRActiveQuestion; | |
3557 | ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID); | |
3558 | if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If unicast query, mark it | |
3559 | else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID; | |
3560 | else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; | |
3561 | } | |
c9b9ae52 | 3562 | |
283ee3ff | 3563 | // Scan our list of questions to see which *unicast* queries need to be sent |
8e92c31c A |
3564 | for (q = m->Questions; q; q=q->next) |
3565 | if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) | |
3566 | { | |
7f0064bd A |
3567 | mDNSu8 *qptr = m->omsg.data; |
3568 | const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); | |
3569 | InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); | |
3570 | qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); | |
3571 | mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL); | |
3572 | q->ThisQInterval *= 2; | |
283ee3ff A |
3573 | if (q->ThisQInterval > MaxQuestionInterval) |
3574 | q->ThisQInterval = MaxQuestionInterval; | |
7f0064bd A |
3575 | q->LastQTime = m->timenow; |
3576 | q->LastQTxTime = m->timenow; | |
3577 | q->RecentAnswerPkts = 0; | |
3578 | q->SendQNow = mDNSNULL; | |
8e92c31c A |
3579 | m->ExpectUnicastResponse = m->timenow; |
3580 | } | |
3581 | ||
283ee3ff | 3582 | // Scan our list of questions to see which *multicast* queries we're definitely going to send |
c9b9ae52 | 3583 | for (q = m->Questions; q; q=q->next) |
8e92c31c | 3584 | if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow)) |
c9b9ae52 A |
3585 | { |
3586 | q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces | |
3587 | if (maxExistingQuestionInterval < q->ThisQInterval) | |
3588 | maxExistingQuestionInterval = q->ThisQInterval; | |
3589 | } | |
6528fe3e | 3590 | |
c9b9ae52 A |
3591 | // Scan our list of questions |
3592 | // (a) to see if there are any more that are worth accelerating, and | |
283ee3ff | 3593 | // (b) to update the state variables for *all* the questions we're going to send |
c9b9ae52 A |
3594 | for (q = m->Questions; q; q=q->next) |
3595 | { | |
8e92c31c A |
3596 | if (q->SendQNow || |
3597 | (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))) | |
c9b9ae52 A |
3598 | { |
3599 | // If at least halfway to next query time, advance to next interval | |
c9d2d929 | 3600 | // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval |
c9b9ae52 A |
3601 | if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0) |
3602 | { | |
3603 | q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces | |
3604 | q->ThisQInterval *= 2; | |
3605 | if (q->ThisQInterval > MaxQuestionInterval) | |
3606 | q->ThisQInterval = MaxQuestionInterval; | |
3607 | else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8) | |
3608 | { | |
c9d2d929 A |
3609 | debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); |
3610 | ReconfirmAntecedents(m, q); // If sending third query, and no answers yet, time to begin doubting the source | |
c9b9ae52 A |
3611 | } |
3612 | } | |
6528fe3e | 3613 | |
c9b9ae52 A |
3614 | // Mark for sending. (If no active interfaces, then don't even try.) |
3615 | q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); | |
3616 | if (q->SendOnAll) | |
3617 | { | |
3618 | q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; | |
3619 | q->LastQTime = m->timenow; | |
3620 | } | |
6528fe3e | 3621 | |
c9b9ae52 A |
3622 | // If we recorded a duplicate suppression for this question less than half an interval ago, |
3623 | // then we consider it recent enough that we don't need to do an identical query ourselves. | |
3624 | ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); | |
6528fe3e | 3625 | |
7f0064bd A |
3626 | q->LastQTxTime = m->timenow; |
3627 | q->RecentAnswerPkts = 0; | |
283ee3ff | 3628 | if (q->RequestUnicast) q->RequestUnicast--; |
c9b9ae52 A |
3629 | } |
3630 | // For all questions (not just the ones we're sending) check what the next scheduled event will be | |
3631 | SetNextQueryTime(m,q); | |
3632 | } | |
3633 | } | |
3634 | ||
3635 | // 2. Scan our authoritative RR list to see what probes we might need to send | |
3636 | if (m->timenow - m->NextScheduledProbe >= 0) | |
6528fe3e | 3637 | { |
c9b9ae52 A |
3638 | m->NextScheduledProbe = m->timenow + 0x78000000; |
3639 | ||
3640 | if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set"); | |
3641 | m->CurrentRecord = m->ResourceRecords; | |
3642 | while (m->CurrentRecord) | |
6528fe3e | 3643 | { |
c9b9ae52 A |
3644 | AuthRecord *rr = m->CurrentRecord; |
3645 | m->CurrentRecord = rr->next; | |
3646 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... | |
3647 | { | |
3648 | // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly | |
3649 | if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) | |
3650 | { | |
3651 | SetNextAnnounceProbeTime(m, rr); | |
3652 | } | |
3653 | // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly | |
3654 | else if (rr->ProbeCount) | |
3655 | { | |
3656 | // Mark for sending. (If no active interfaces, then don't even try.) | |
3657 | rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; | |
3658 | rr->LastAPTime = m->timenow; | |
3659 | rr->ProbeCount--; | |
3660 | SetNextAnnounceProbeTime(m, rr); | |
3661 | } | |
c9d2d929 | 3662 | // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced |
c9b9ae52 A |
3663 | else |
3664 | { | |
3665 | AuthRecord *r2; | |
3666 | rr->resrec.RecordType = kDNSRecordTypeVerified; | |
3667 | rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; | |
3668 | rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; | |
3669 | SetNextAnnounceProbeTime(m, rr); | |
3670 | // If we have any records on our duplicate list that match this one, they have now also completed probing | |
3671 | for (r2 = m->DuplicateRecords; r2; r2=r2->next) | |
3672 | if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) | |
3673 | r2->ProbeCount = 0; | |
7f0064bd | 3674 | AcknowledgeRecord(m, rr); |
c9b9ae52 A |
3675 | } |
3676 | } | |
6528fe3e | 3677 | } |
c9b9ae52 A |
3678 | m->CurrentRecord = m->DuplicateRecords; |
3679 | while (m->CurrentRecord) | |
6528fe3e | 3680 | { |
c9b9ae52 A |
3681 | AuthRecord *rr = m->CurrentRecord; |
3682 | m->CurrentRecord = rr->next; | |
3683 | if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0) | |
7f0064bd | 3684 | AcknowledgeRecord(m, rr); |
6528fe3e A |
3685 | } |
3686 | } | |
6528fe3e | 3687 | |
c9d2d929 | 3688 | // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface |
c9b9ae52 | 3689 | while (intf) |
6528fe3e | 3690 | { |
c9b9ae52 | 3691 | AuthRecord *rr; |
7f0064bd A |
3692 | mDNSu8 *queryptr = m->omsg.data; |
3693 | InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); | |
c9b9ae52 A |
3694 | if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); |
3695 | if (!KnownAnswerList) | |
3696 | { | |
3697 | // Start a new known-answer list | |
3698 | CacheRecord **kalistptr = &KnownAnswerList; | |
3699 | mDNSu32 answerforecast = 0; | |
3700 | ||
3701 | // Put query questions in this packet | |
3702 | for (q = m->Questions; q; q=q->next) | |
3703 | if (q->SendQNow == intf->InterfaceID) | |
3704 | { | |
7f0064bd | 3705 | debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d", |
c9b9ae52 | 3706 | SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", |
7f0064bd | 3707 | q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data); |
c9b9ae52 A |
3708 | // If we're suppressing this question, or we successfully put it, update its SendQNow state |
3709 | if (SuppressOnThisInterface(q->DupSuppress, intf) || | |
7f0064bd | 3710 | BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) |
c9b9ae52 A |
3711 | q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); |
3712 | } | |
6528fe3e | 3713 | |
c9b9ae52 A |
3714 | // Put probe questions in this packet |
3715 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3716 | if (rr->SendRNow == intf->InterfaceID) | |
3717 | { | |
7f0064bd | 3718 | mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; |
c9b9ae52 | 3719 | mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); |
7f0064bd | 3720 | const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); |
283ee3ff | 3721 | mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); |
c9b9ae52 A |
3722 | // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) |
3723 | mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; | |
3724 | if (newptr && newptr + forecast < limit) | |
3725 | { | |
3726 | queryptr = newptr; | |
3727 | answerforecast = forecast; | |
3728 | rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); | |
3729 | rr->IncludeInProbe = mDNStrue; | |
c9d2d929 | 3730 | verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount); |
c9b9ae52 A |
3731 | } |
3732 | else | |
3733 | { | |
c9d2d929 | 3734 | verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd | 3735 | m->omsg.h.numQuestions--; |
c9b9ae52 A |
3736 | } |
3737 | } | |
3738 | } | |
3739 | ||
3740 | // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) | |
3741 | while (KnownAnswerList) | |
6528fe3e | 3742 | { |
c9b9ae52 A |
3743 | CacheRecord *rr = KnownAnswerList; |
3744 | mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; | |
7f0064bd | 3745 | mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); |
6528fe3e | 3746 | if (newptr) |
c9b9ae52 | 3747 | { |
c9d2d929 | 3748 | 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 | 3749 | queryptr = newptr; |
c9b9ae52 A |
3750 | KnownAnswerList = rr->NextInKAList; |
3751 | rr->NextInKAList = mDNSNULL; | |
3752 | } | |
6528fe3e A |
3753 | else |
3754 | { | |
c9b9ae52 A |
3755 | // If we ran out of space and we have more than one question in the packet, that's an error -- |
3756 | // we shouldn't have put more than one question if there was a risk of us running out of space. | |
7f0064bd A |
3757 | if (m->omsg.h.numQuestions > 1) |
3758 | LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers); | |
3759 | m->omsg.h.flags.b[0] |= kDNSFlag0_TC; | |
6528fe3e A |
3760 | break; |
3761 | } | |
3762 | } | |
6528fe3e | 3763 | |
c9b9ae52 A |
3764 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
3765 | if (rr->IncludeInProbe) | |
3766 | { | |
7f0064bd | 3767 | mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec); |
c9b9ae52 A |
3768 | rr->IncludeInProbe = mDNSfalse; |
3769 | if (newptr) queryptr = newptr; | |
3770 | else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?", | |
283ee3ff | 3771 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
3772 | } |
3773 | ||
7f0064bd | 3774 | if (queryptr > m->omsg.data) |
6528fe3e | 3775 | { |
7f0064bd A |
3776 | if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) |
3777 | LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); | |
c9b9ae52 | 3778 | debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", |
7f0064bd A |
3779 | m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", |
3780 | m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", | |
3781 | m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); | |
3782 | if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); | |
3783 | if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); | |
7cb34e5c | 3784 | if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); |
c9b9ae52 A |
3785 | if (++pktcount >= 1000) |
3786 | { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } | |
3787 | // There might be more records left in the known answer list, or more questions to send | |
3788 | // on this interface, so go around one more time and try again. | |
6528fe3e | 3789 | } |
c9b9ae52 | 3790 | else // Nothing more to send on this interface; go to next |
6528fe3e | 3791 | { |
c9b9ae52 A |
3792 | const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); |
3793 | #if MDNS_DEBUGMSGS && 0 | |
3794 | const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; | |
3795 | debugf(msg, intf, next); | |
3796 | #endif | |
3797 | intf = next; | |
6528fe3e | 3798 | } |
c9b9ae52 | 3799 | } |
8e92c31c | 3800 | |
c9d2d929 A |
3801 | // Final sanity check for debugging purposes |
3802 | { | |
3803 | AuthRecord *rr; | |
3804 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
3805 | if (rr->SendRNow) | |
3806 | { | |
3807 | if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) | |
3808 | LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, rr)); | |
3809 | rr->SendRNow = mDNSNULL; | |
3810 | } | |
3811 | } | |
6528fe3e A |
3812 | } |
3813 | ||
3814 | // *************************************************************************** | |
c9b9ae52 | 3815 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
3816 | #pragma mark - |
3817 | #pragma mark - RR List Management & Task Management | |
3818 | #endif | |
3819 | ||
6528fe3e A |
3820 | // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. |
3821 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 3822 | mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord) |
6528fe3e | 3823 | { |
c9b9ae52 | 3824 | verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)", |
283ee3ff | 3825 | q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e | 3826 | |
7f0064bd A |
3827 | // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue) |
3828 | // may be called twice, once when the record is received, and again when it's time to notify local clients. | |
3829 | // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. | |
3830 | ||
c9b9ae52 | 3831 | rr->LastUsed = m->timenow; |
c9b9ae52 | 3832 | if (ActiveQuestion(q) && rr->CRActiveQuestion != q) |
6528fe3e | 3833 | { |
c9b9ae52 A |
3834 | if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count |
3835 | rr->CRActiveQuestion = q; // We know q is non-null | |
3836 | SetNextCacheCheckTime(m, rr); | |
6528fe3e | 3837 | } |
6528fe3e | 3838 | |
283ee3ff A |
3839 | // If this is: |
3840 | // (a) a no-cache add, where we've already done at least one 'QM' query, or | |
3841 | // (b) a normal add, where we have at least one unique-type answer, | |
3842 | // then there's no need to keep polling the network. | |
3843 | // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.) | |
3844 | if ((AddRecord == 2 && !q->RequestUnicast) || | |
3845 | (AddRecord == 1 && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)))) | |
3846 | if (ActiveQuestion(q)) | |
3847 | { | |
3848 | q->LastQTime = m->timenow; | |
3849 | q->LastQTxTime = m->timenow; | |
7cb34e5c | 3850 | q->RecentAnswerPkts = 0; |
283ee3ff A |
3851 | q->ThisQInterval = MaxQuestionInterval; |
3852 | q->RequestUnicast = mDNSfalse; | |
3853 | } | |
3854 | ||
7f0064bd A |
3855 | if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us |
3856 | ||
c9b9ae52 A |
3857 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback |
3858 | if (q->QuestionCallback) | |
3859 | q->QuestionCallback(m, q, &rr->resrec, AddRecord); | |
3860 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again | |
7f0064bd A |
3861 | // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function |
3862 | // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. | |
3863 | // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() | |
3864 | // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions | |
3865 | // being deleted out from under them. | |
3866 | } | |
3867 | ||
3868 | mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) | |
3869 | { | |
c9d2d929 | 3870 | rr->DelayDelivery = 0; |
7f0064bd A |
3871 | if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set"); |
3872 | m->CurrentQuestion = m->Questions; | |
3873 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) | |
3874 | { | |
3875 | DNSQuestion *q = m->CurrentQuestion; | |
3876 | m->CurrentQuestion = q->next; | |
3877 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
3878 | AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
3879 | } | |
3880 | m->CurrentQuestion = mDNSNULL; | |
3881 | } | |
3882 | ||
3883 | mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot) | |
3884 | { | |
3885 | const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second | |
3886 | const mDNSs32 start = m->timenow - 0x10000000; | |
3887 | mDNSs32 delay = start; | |
283ee3ff | 3888 | CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); |
7f0064bd | 3889 | CacheRecord *rr; |
283ee3ff A |
3890 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
3891 | if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name)) | |
7f0064bd A |
3892 | if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second |
3893 | if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted | |
3894 | delay = RRExpireTime(rr); | |
c9d2d929 | 3895 | if (delay - start > 0) return(delay ? delay : 1); // Make sure we return non-zero if we want to delay |
7f0064bd | 3896 | else return(0); |
6528fe3e A |
3897 | } |
3898 | ||
c9b9ae52 | 3899 | // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. |
6528fe3e A |
3900 | // If new questions are created as a result of invoking client callbacks, they will be added to |
3901 | // the end of the question list, and m->NewQuestions will be set to indicate the first new question. | |
c9b9ae52 A |
3902 | // rr is a new CacheRecord just received into our cache |
3903 | // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). | |
3904 | // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback, | |
3905 | // which may change the record list and/or question list. | |
6528fe3e | 3906 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. |
c9b9ae52 | 3907 | mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) |
6528fe3e | 3908 | { |
c9b9ae52 A |
3909 | if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set"); |
3910 | m->CurrentQuestion = m->Questions; | |
6528fe3e A |
3911 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) |
3912 | { | |
3913 | DNSQuestion *q = m->CurrentQuestion; | |
3914 | m->CurrentQuestion = q->next; | |
c9b9ae52 A |
3915 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) |
3916 | { | |
c9d2d929 A |
3917 | // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last |
3918 | // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start. | |
3919 | // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less, | |
3920 | // there's not much benefit accelerating because we will anyway send another query within a few seconds. | |
3921 | // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines. | |
7f0064bd | 3922 | if (q->LastAnswerPktNum != m->PktNum) |
c9b9ae52 | 3923 | { |
7f0064bd A |
3924 | q->LastAnswerPktNum = m->PktNum; |
3925 | if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && | |
283ee3ff | 3926 | q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) |
7f0064bd A |
3927 | { |
3928 | LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", | |
3929 | q->qname.c, DNSTypeName(q->qtype)); | |
283ee3ff A |
3930 | q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); |
3931 | q->ThisQInterval = InitialQuestionInterval; | |
7f0064bd A |
3932 | SetNextQueryTime(m,q); |
3933 | } | |
c9b9ae52 | 3934 | } |
c9d2d929 | 3935 | verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); |
c9b9ae52 A |
3936 | q->CurrentAnswers++; |
3937 | if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; | |
3938 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; | |
7f0064bd A |
3939 | if (q->CurrentAnswers > 4000) |
3940 | { | |
3941 | static int msgcount = 0; | |
3942 | if (msgcount++ < 10) | |
3943 | LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", | |
3944 | q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); | |
3945 | rr->resrec.rroriginalttl = 1; | |
3946 | rr->UnansweredQueries = MaxUnansweredQueries; | |
3947 | } | |
c9b9ae52 A |
3948 | AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); |
3949 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
3950 | } | |
6528fe3e A |
3951 | } |
3952 | m->CurrentQuestion = mDNSNULL; | |
3953 | } | |
3954 | ||
283ee3ff A |
3955 | // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. |
3956 | // If new questions are created as a result of invoking client callbacks, they will be added to | |
3957 | // the end of the question list, and m->NewQuestions will be set to indicate the first new question. | |
3958 | // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique) | |
3959 | // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any | |
3960 | // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists, | |
3961 | // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network. | |
3962 | // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback, | |
3963 | // which may change the record list and/or question list. | |
3964 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
3965 | mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) | |
3966 | { | |
3967 | LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); | |
c9d2d929 | 3968 | if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set"); |
283ee3ff A |
3969 | m->CurrentQuestion = m->Questions; |
3970 | while (m->CurrentQuestion) | |
3971 | { | |
3972 | DNSQuestion *q = m->CurrentQuestion; | |
3973 | m->CurrentQuestion = q->next; | |
3974 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
3975 | AnswerQuestionWithResourceRecord(m, q, rr, 2); // Value '2' indicates "don't expect 'remove' events for this" | |
3976 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
3977 | } | |
3978 | m->CurrentQuestion = mDNSNULL; | |
3979 | } | |
3980 | ||
c9b9ae52 A |
3981 | // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute |
3982 | // If new questions are created as a result of invoking client callbacks, they will be added to | |
3983 | // the end of the question list, and m->NewQuestions will be set to indicate the first new question. | |
3984 | // rr is an existing cache CacheRecord that just expired and is being deleted | |
3985 | // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). | |
3986 | // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback, | |
3987 | // which may change the record list and/or question list. | |
3988 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
3989 | mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) | |
6528fe3e | 3990 | { |
c9b9ae52 A |
3991 | if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set"); |
3992 | m->CurrentQuestion = m->Questions; | |
3993 | while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) | |
3994 | { | |
3995 | DNSQuestion *q = m->CurrentQuestion; | |
3996 | m->CurrentQuestion = q->next; | |
3997 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4e28aad3 | 3998 | { |
c9d2d929 | 3999 | verbosedebugf("CacheRecordRmv %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 | 4000 | if (q->CurrentAnswers == 0) |
c9d2d929 | 4001 | LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype)); |
c9b9ae52 A |
4002 | else |
4003 | { | |
4004 | q->CurrentAnswers--; | |
4005 | if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; | |
4006 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; | |
4007 | } | |
4008 | if (q->CurrentAnswers == 0) | |
4009 | { | |
c9d2d929 | 4010 | debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); |
c9b9ae52 A |
4011 | ReconfirmAntecedents(m, q); |
4012 | } | |
4013 | AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse); | |
4014 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
4e28aad3 | 4015 | } |
c9b9ae52 | 4016 | } |
6528fe3e | 4017 | m->CurrentQuestion = mDNSNULL; |
6528fe3e A |
4018 | } |
4019 | ||
283ee3ff | 4020 | mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e) |
6528fe3e | 4021 | { |
283ee3ff A |
4022 | #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 |
4023 | unsigned int i; | |
4024 | for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; | |
4025 | #endif | |
4026 | e->next = m->rrcache_free; | |
4027 | m->rrcache_free = e; | |
c9b9ae52 | 4028 | m->rrcache_totalused--; |
6528fe3e A |
4029 | } |
4030 | ||
283ee3ff A |
4031 | mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) |
4032 | { | |
4033 | CacheEntity *e = (CacheEntity *)(*cp); | |
4034 | //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c); | |
c9d2d929 A |
4035 | if ((*cp)->rrcache_tail != &(*cp)->members) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); |
4036 | //if ((*cp)->name != (domainname*)((*cp)->namestorage)) LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); | |
283ee3ff A |
4037 | if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); |
4038 | (*cp)->name = mDNSNULL; | |
4039 | *cp = (*cp)->next; // Cut record from list | |
4040 | ReleaseCacheEntity(m, e); | |
4041 | } | |
4042 | ||
4043 | mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) | |
4044 | { | |
4045 | if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) mDNSPlatformMemFree(r->resrec.rdata); | |
4046 | r->resrec.rdata = mDNSNULL; | |
4047 | ReleaseCacheEntity(m, (CacheEntity *)r); | |
4048 | } | |
4049 | ||
c9d2d929 A |
4050 | // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls |
4051 | // The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records | |
283ee3ff | 4052 | mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg) |
6528fe3e | 4053 | { |
283ee3ff | 4054 | CacheRecord **rp = &cg->members; |
c9b9ae52 A |
4055 | |
4056 | if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } | |
6528fe3e | 4057 | m->lock_rrcache = 1; |
c9b9ae52 A |
4058 | |
4059 | while (*rp) | |
6528fe3e | 4060 | { |
c9b9ae52 A |
4061 | CacheRecord *const rr = *rp; |
4062 | mDNSs32 event = RRExpireTime(rr); | |
4063 | if (m->timenow - event >= 0) // If expired, delete it | |
4064 | { | |
4065 | *rp = rr->next; // Cut it from the list | |
7f0064bd | 4066 | verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr)); |
c9b9ae52 A |
4067 | if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away |
4068 | { | |
4069 | CacheRecordRmv(m, rr); | |
4070 | m->rrcache_active--; | |
4071 | } | |
283ee3ff | 4072 | ReleaseCacheRecord(m, rr); |
c9b9ae52 A |
4073 | } |
4074 | else // else, not expired; see if we need to query | |
6528fe3e | 4075 | { |
7f0064bd A |
4076 | if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) |
4077 | event = rr->DelayDelivery; | |
4078 | else | |
c9b9ae52 | 4079 | { |
7f0064bd A |
4080 | if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); |
4081 | if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) | |
c9b9ae52 | 4082 | { |
7f0064bd A |
4083 | if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query |
4084 | event = rr->NextRequiredQuery; // then just record when we want the next query | |
4085 | else // else trigger our question to go out now | |
4086 | { | |
4087 | // Set NextScheduledQuery to timenow so that SendQueries() will run. | |
4088 | // SendQueries() will see that we have records close to expiration, and send FEQs for them. | |
4089 | m->NextScheduledQuery = m->timenow; | |
4090 | // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), | |
c9d2d929 | 4091 | // which will correctly update m->NextCacheCheck for us |
7f0064bd A |
4092 | event = m->timenow + 0x3FFFFFFF; |
4093 | } | |
c9b9ae52 A |
4094 | } |
4095 | } | |
4096 | if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) | |
4097 | m->NextCacheCheck = (event + CacheCheckGracePeriod(rr)); | |
4098 | rp = &rr->next; | |
6528fe3e A |
4099 | } |
4100 | } | |
283ee3ff A |
4101 | if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp); |
4102 | cg->rrcache_tail = rp; | |
6528fe3e | 4103 | m->lock_rrcache = 0; |
6528fe3e A |
4104 | } |
4105 | ||
c9b9ae52 | 4106 | mDNSlocal void AnswerNewQuestion(mDNS *const m) |
6528fe3e | 4107 | { |
c9b9ae52 A |
4108 | mDNSBool ShouldQueryImmediately = mDNStrue; |
4109 | CacheRecord *rr; | |
4110 | DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer | |
7f0064bd | 4111 | const mDNSu32 slot = HashSlot(&q->qname); |
283ee3ff | 4112 | CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); |
c9b9ae52 A |
4113 | |
4114 | verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); | |
6528fe3e | 4115 | |
283ee3ff | 4116 | if (cg) CheckCacheExpiration(m, cg); |
c9b9ae52 A |
4117 | m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration(); |
4118 | ||
4119 | if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); | |
4120 | // This should be safe, because calling the client's question callback may cause the | |
4121 | // question list to be modified, but should not ever cause the rrcache list to be modified. | |
8e92c31c | 4122 | // If the client's question callback deletes the question, then m->CurrentQuestion will |
c9b9ae52 | 4123 | // be advanced, and we'll exit out of the loop |
6528fe3e | 4124 | m->lock_rrcache = 1; |
c9b9ae52 A |
4125 | if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); |
4126 | m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted | |
283ee3ff A |
4127 | |
4128 | if (q->InterfaceID == mDNSInterface_Any) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records | |
4129 | { | |
4130 | if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); | |
4131 | m->CurrentRecord = m->ResourceRecords; | |
4132 | while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) | |
4133 | { | |
4134 | AuthRecord *rr = m->CurrentRecord; | |
4135 | m->CurrentRecord = rr->next; | |
4136 | if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) | |
4137 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4138 | { | |
4139 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
4140 | // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() | |
4141 | if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here | |
4142 | } | |
4143 | } | |
4144 | m->CurrentRecord = mDNSNULL; | |
4145 | } | |
4146 | ||
4147 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) | |
c9b9ae52 | 4148 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) |
6528fe3e | 4149 | { |
c9b9ae52 A |
4150 | // SecsSinceRcvd is whole number of elapsed seconds, rounded down |
4151 | mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; | |
4152 | if (rr->resrec.rroriginalttl <= SecsSinceRcvd) | |
4153 | { | |
4154 | LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)", | |
283ee3ff | 4155 | rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
c9b9ae52 A |
4156 | continue; // Go to next one in loop |
4157 | } | |
4158 | ||
4159 | // If this record set is marked unique, then that means we can reasonably assume we have the whole set | |
4160 | // -- we don't need to rush out on the network and query immediately to see if there are more answers out there | |
7f0064bd A |
4161 | if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) |
4162 | ShouldQueryImmediately = mDNSfalse; | |
c9b9ae52 A |
4163 | q->CurrentAnswers++; |
4164 | if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; | |
4165 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; | |
4166 | AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
4167 | // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() | |
4168 | if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here | |
6528fe3e | 4169 | } |
716635cc | 4170 | else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) |
283ee3ff | 4171 | if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname)) |
716635cc | 4172 | ShouldQueryImmediately = mDNSfalse; |
c9b9ae52 A |
4173 | |
4174 | if (ShouldQueryImmediately && m->CurrentQuestion == q) | |
6528fe3e | 4175 | { |
283ee3ff A |
4176 | q->ThisQInterval = InitialQuestionInterval; |
4177 | q->LastQTime = m->timenow - q->ThisQInterval; | |
c9b9ae52 A |
4178 | m->NextScheduledQuery = m->timenow; |
4179 | } | |
4180 | m->CurrentQuestion = mDNSNULL; | |
4181 | m->lock_rrcache = 0; | |
4182 | } | |
6528fe3e | 4183 | |
c9d2d929 A |
4184 | // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any appropriate answers, |
4185 | // stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions | |
c9b9ae52 A |
4186 | mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) |
4187 | { | |
4188 | DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer | |
4189 | m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions to the next (if any) | |
6528fe3e | 4190 | |
c9b9ae52 A |
4191 | debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); |
4192 | ||
7f0064bd | 4193 | if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set"); |
c9b9ae52 | 4194 | m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted |
6528fe3e | 4195 | |
7f0064bd | 4196 | if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); |
283ee3ff A |
4197 | m->CurrentRecord = m->ResourceRecords; |
4198 | while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) | |
c9b9ae52 A |
4199 | { |
4200 | AuthRecord *rr = m->CurrentRecord; | |
4201 | m->CurrentRecord = rr->next; | |
4202 | if (ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
4203 | { | |
4204 | AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); | |
4205 | // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() | |
4206 | if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here | |
6528fe3e | 4207 | } |
c9b9ae52 A |
4208 | } |
4209 | ||
4210 | m->CurrentQuestion = mDNSNULL; | |
7f0064bd | 4211 | m->CurrentRecord = mDNSNULL; |
c9b9ae52 | 4212 | } |
6528fe3e | 4213 | |
283ee3ff | 4214 | mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG) |
6528fe3e | 4215 | { |
283ee3ff | 4216 | CacheEntity *e = mDNSNULL; |
6528fe3e | 4217 | |
c9b9ae52 A |
4218 | if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } |
4219 | m->lock_rrcache = 1; | |
6528fe3e | 4220 | |
c9b9ae52 A |
4221 | // If we have no free records, ask the client layer to give us some more memory |
4222 | if (!m->rrcache_free && m->MainCallback) | |
6528fe3e | 4223 | { |
c9b9ae52 A |
4224 | if (m->rrcache_totalused != m->rrcache_size) |
4225 | LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", | |
4226 | m->rrcache_totalused, m->rrcache_size); | |
4227 | ||
4228 | // We don't want to be vulnerable to a malicious attacker flooding us with an infinite | |
4229 | // number of bogus records so that we keep growing our cache until the machine runs out of memory. | |
4230 | // To guard against this, if we're actively using less than 1/32 of our cache, then we | |
4231 | // purge all the unused records and recycle them, instead of allocating more memory. | |
4232 | if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active) | |
4233 | debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", | |
4234 | m->rrcache_size, m->rrcache_active); | |
4235 | else | |
8e92c31c A |
4236 | { |
4237 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 4238 | m->MainCallback(m, mStatus_GrowCache); |
8e92c31c A |
4239 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
4240 | } | |
6528fe3e A |
4241 | } |
4242 | ||
c9b9ae52 A |
4243 | // If we still have no free records, recycle all the records we can. |
4244 | // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. | |
4245 | if (!m->rrcache_free) | |
6528fe3e | 4246 | { |
c9b9ae52 A |
4247 | #if MDNS_DEBUGMSGS |
4248 | mDNSu32 oldtotalused = m->rrcache_totalused; | |
4249 | #endif | |
4250 | mDNSu32 slot; | |
c9b9ae52 | 4251 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) |
6528fe3e | 4252 | { |
283ee3ff A |
4253 | CacheGroup **cp = &m->rrcache_hash[slot]; |
4254 | while (*cp) | |
6528fe3e | 4255 | { |
283ee3ff A |
4256 | CacheRecord **rp = &(*cp)->members; |
4257 | while (*rp) | |
6528fe3e | 4258 | { |
283ee3ff A |
4259 | // Records that answer still-active questions are not candidates for recycling |
4260 | // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash | |
4261 | if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList) | |
4262 | rp=&(*rp)->next; | |
4263 | else | |
4264 | { | |
4265 | CacheRecord *rr = *rp; | |
4266 | *rp = (*rp)->next; // Cut record from list | |
4267 | ReleaseCacheRecord(m, rr); | |
4268 | } | |
6528fe3e | 4269 | } |
c9d2d929 | 4270 | if ((*cp)->rrcache_tail != rp) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp); |
283ee3ff A |
4271 | (*cp)->rrcache_tail = rp; |
4272 | if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next; | |
4273 | else ReleaseCacheGroup(m, cp); | |
6528fe3e A |
4274 | } |
4275 | } | |
c9b9ae52 A |
4276 | #if MDNS_DEBUGMSGS |
4277 | debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused); | |
4278 | #endif | |
4279 | } | |
c9b9ae52 | 4280 | |
283ee3ff | 4281 | if (m->rrcache_free) // If there are records in the free list, take one |
c9b9ae52 | 4282 | { |
283ee3ff A |
4283 | e = m->rrcache_free; |
4284 | m->rrcache_free = e->next; | |
c9b9ae52 A |
4285 | if (++m->rrcache_totalused >= m->rrcache_report) |
4286 | { | |
7cb34e5c | 4287 | debugf("RR Cache now using %ld objects", m->rrcache_totalused); |
c9b9ae52 A |
4288 | if (m->rrcache_report < 100) m->rrcache_report += 10; |
4289 | else m->rrcache_report += 100; | |
4290 | } | |
283ee3ff A |
4291 | mDNSPlatformMemZero(e, sizeof(*e)); |
4292 | } | |
4293 | ||
4294 | m->lock_rrcache = 0; | |
4295 | ||
4296 | return(e); | |
4297 | } | |
4298 | ||
4299 | mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength) | |
4300 | { | |
4301 | CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); | |
4302 | if (r) | |
4303 | { | |
7f0064bd | 4304 | r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage |
7f0064bd | 4305 | if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage |
c9b9ae52 A |
4306 | { |
4307 | r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); | |
4308 | if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; | |
283ee3ff | 4309 | else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } |
c9b9ae52 | 4310 | } |
6528fe3e | 4311 | } |
283ee3ff A |
4312 | return(r); |
4313 | } | |
6528fe3e | 4314 | |
283ee3ff A |
4315 | mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) |
4316 | { | |
4317 | mDNSu16 namelen = DomainNameLength(rr->name); | |
4318 | CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL); | |
4319 | if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } | |
4320 | cg->next = m->rrcache_hash[slot]; | |
4321 | cg->namehash = rr->namehash; | |
4322 | cg->members = mDNSNULL; | |
4323 | cg->rrcache_tail = &cg->members; | |
4324 | cg->name = (domainname*)cg->namestorage; | |
c9d2d929 | 4325 | //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c); |
283ee3ff A |
4326 | if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen); |
4327 | if (!cg->name) | |
4328 | { | |
4329 | LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); | |
4330 | ReleaseCacheEntity(m, (CacheEntity*)cg); | |
4331 | return(mDNSNULL); | |
4332 | } | |
4333 | AssignDomainName(cg->name, rr->name); | |
c9b9ae52 | 4334 | |
283ee3ff A |
4335 | if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); |
4336 | m->rrcache_hash[slot] = cg; | |
4337 | if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); | |
4338 | ||
4339 | return(cg); | |
c9b9ae52 | 4340 | } |
6528fe3e | 4341 | |
c9b9ae52 A |
4342 | mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) |
4343 | { | |
4344 | // Make sure we mark this record as thoroughly expired -- we don't ever want to give | |
4345 | // a positive answer using an expired record (e.g. from an interface that has gone away). | |
4346 | // We don't want to clear CRActiveQuestion here, because that would leave the record subject to | |
4347 | // summary deletion without giving the proper callback to any questions that are monitoring it. | |
4348 | // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. | |
4349 | rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; | |
4350 | rr->UnansweredQueries = MaxUnansweredQueries; | |
4351 | rr->resrec.rroriginalttl = 0; | |
4352 | SetNextCacheCheckTime(m, rr); | |
6528fe3e A |
4353 | } |
4354 | ||
7f0064bd | 4355 | mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) |
6528fe3e | 4356 | { |
7f0064bd | 4357 | mDNSs32 time; |
6528fe3e | 4358 | mDNSPlatformLock(m); |
7f0064bd | 4359 | if (m->mDNS_busy) |
c9b9ae52 | 4360 | { |
7f0064bd A |
4361 | LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); |
4362 | if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); | |
c9b9ae52 | 4363 | } |
c9b9ae52 | 4364 | |
7f0064bd | 4365 | if (m->timenow) time = m->timenow; |
28f7d060 | 4366 | else time = mDNS_TimeNow_NoLock(m); |
6528fe3e | 4367 | mDNSPlatformUnlock(m); |
7f0064bd | 4368 | return(time); |
6528fe3e A |
4369 | } |
4370 | ||
c9b9ae52 | 4371 | mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) |
6528fe3e | 4372 | { |
c9b9ae52 A |
4373 | mDNS_Lock(m); // Must grab lock before trying to read m->timenow |
4374 | ||
4375 | if (m->timenow - m->NextScheduledEvent >= 0) | |
4376 | { | |
4377 | int i; | |
6528fe3e | 4378 | |
c9b9ae52 A |
4379 | verbosedebugf("mDNS_Execute"); |
4380 | if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set"); | |
4381 | ||
4382 | // 1. If we're past the probe suppression time, we can clear it | |
4383 | if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; | |
4384 | ||
4385 | // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter | |
4386 | if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; | |
4387 | ||
4388 | // 3. Purge our cache of stale old records | |
4389 | if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) | |
4390 | { | |
4391 | mDNSu32 slot; | |
4392 | m->NextCacheCheck = m->timenow + 0x3FFFFFFF; | |
283ee3ff A |
4393 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) |
4394 | { | |
4395 | CacheGroup **cp = &m->rrcache_hash[slot]; | |
4396 | while (*cp) | |
4397 | { | |
4398 | CheckCacheExpiration(m, *cp); | |
4399 | if ((*cp)->members) cp=&(*cp)->next; | |
4400 | else ReleaseCacheGroup(m, cp); | |
4401 | } | |
4402 | } | |
c9b9ae52 | 4403 | } |
6528fe3e | 4404 | |
c9b9ae52 | 4405 | // 4. See if we can answer any of our new local questions from the cache |
7f0064bd A |
4406 | for (i=0; m->NewQuestions && i<1000; i++) |
4407 | { | |
4408 | if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; | |
4409 | AnswerNewQuestion(m); | |
4410 | } | |
283ee3ff | 4411 | if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); |
c9b9ae52 | 4412 | |
c9b9ae52 | 4413 | for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); |
283ee3ff | 4414 | if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); |
4e28aad3 | 4415 | |
283ee3ff A |
4416 | for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++) |
4417 | { | |
4418 | AuthRecord *rr = m->NewLocalRecords; | |
4419 | m->NewLocalRecords = m->NewLocalRecords->next; | |
4420 | AnswerLocalQuestions(m, rr, mDNStrue); | |
4421 | } | |
4422 | if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit"); | |
6528fe3e | 4423 | |
c9b9ae52 A |
4424 | // 5. See what packets we need to send |
4425 | if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m); | |
4426 | else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0) | |
4427 | { | |
4428 | // If the platform code is ready, and we're not suppressing packet generation right now | |
4429 | // then send our responses, probes, and questions. | |
c9d2d929 | 4430 | // We check the cache first, because there might be records close to expiring that trigger questions to refresh them |
c9b9ae52 A |
4431 | // We send queries next, because there might be final-stage probes that complete their probing here, causing |
4432 | // them to advance to announcing state, and we want those to be included in any announcements we send out. | |
c9d2d929 | 4433 | // Finally, we send responses, including the previously mentioned records that just completed probing |
c9b9ae52 A |
4434 | m->SuppressSending = 0; |
4435 | ||
4436 | // 6. Send Query packets. This may cause some probing records to advance to announcing state | |
4437 | if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); | |
4438 | if (m->timenow - m->NextScheduledQuery >= 0) | |
4439 | { | |
4440 | LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second"); | |
4441 | m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; | |
4442 | } | |
4443 | if (m->timenow - m->NextScheduledProbe >= 0) | |
4444 | { | |
4445 | LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second"); | |
4446 | m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; | |
4447 | } | |
4448 | ||
4449 | // 7. Send Response packets, including probing records just advanced to announcing state | |
4450 | if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); | |
4451 | if (m->timenow - m->NextScheduledResponse >= 0) | |
4452 | { | |
4453 | LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second"); | |
4454 | m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond; | |
4455 | } | |
4456 | } | |
6528fe3e | 4457 | |
c9d2d929 | 4458 | m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary |
c9b9ae52 | 4459 | } |
6528fe3e | 4460 | |
c9b9ae52 A |
4461 | // Note about multi-threaded systems: |
4462 | // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), | |
4463 | // performing mDNS API operations that change our next scheduled event time. | |
4464 | // | |
4465 | // On multi-threaded systems (like the current Windows implementation) that have a single main thread | |
4466 | // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility | |
4467 | // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will | |
4468 | // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one | |
4469 | // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful | |
4470 | // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it | |
4471 | // does, the state of the signal will be noticed, causing the blocking primitive to return immediately | |
4472 | // without blocking. This avoids the race condition between the signal from the other thread arriving | |
4473 | // just *before* or just *after* the main thread enters the blocking primitive. | |
4474 | // | |
4475 | // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, | |
4476 | // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to | |
4477 | // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer | |
4478 | // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale | |
4479 | // by the time it gets to the timer callback function). | |
4480 | ||
7f0064bd | 4481 | #ifndef UNICAST_DISABLED |
8e92c31c | 4482 | uDNS_Execute(m); |
7f0064bd | 4483 | #endif |
c9b9ae52 A |
4484 | mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value |
4485 | return(m->NextScheduledEvent); | |
6528fe3e A |
4486 | } |
4487 | ||
c9b9ae52 A |
4488 | // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep. |
4489 | // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up. | |
4490 | // Normally, the platform support layer below mDNSCore should call this, not the client layer above. | |
4491 | // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call | |
4492 | // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just | |
4493 | // found itself in a new network environment. For example, if the Ethernet hardware indicates that the | |
4494 | // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse) | |
4495 | // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc. | |
4496 | // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network | |
4497 | // traffic, so it should only be called when there is legitimate reason to believe the machine | |
4498 | // may have become attached to a new network. | |
4499 | mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) | |
6528fe3e | 4500 | { |
c9b9ae52 A |
4501 | AuthRecord *rr; |
4502 | ||
4503 | mDNS_Lock(m); | |
6528fe3e A |
4504 | |
4505 | m->SleepState = sleepstate; | |
283ee3ff | 4506 | LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); |
6528fe3e A |
4507 | |
4508 | if (sleepstate) | |
4509 | { | |
7f0064bd A |
4510 | #ifndef UNICAST_DISABLED |
4511 | uDNS_Sleep(m); | |
4512 | #endif | |
c9b9ae52 | 4513 | // Mark all the records we need to deregister and send them |
6528fe3e | 4514 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
7f0064bd | 4515 | if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) |
c9b9ae52 A |
4516 | rr->ImmedAnswer = mDNSInterfaceMark; |
4517 | SendResponses(m); | |
6528fe3e A |
4518 | } |
4519 | else | |
4520 | { | |
4521 | DNSQuestion *q; | |
c9b9ae52 | 4522 | mDNSu32 slot; |
283ee3ff | 4523 | CacheGroup *cg; |
c9b9ae52 A |
4524 | CacheRecord *cr; |
4525 | ||
7f0064bd A |
4526 | #ifndef UNICAST_DISABLED |
4527 | uDNS_Wake(m); | |
4528 | #endif | |
8e92c31c | 4529 | // 1. Retrigger all our questions |
c9b9ae52 A |
4530 | for (q = m->Questions; q; q=q->next) // Scan our list of questions |
4531 | if (ActiveQuestion(q)) | |
4532 | { | |
7f0064bd | 4533 | q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question |
283ee3ff | 4534 | q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it |
7f0064bd A |
4535 | q->LastQTime = m->timenow - q->ThisQInterval; |
4536 | q->RecentAnswerPkts = 0; | |
c9b9ae52 A |
4537 | ExpireDupSuppressInfo(q->DupSuppress, m->timenow); |
4538 | m->NextScheduledQuery = m->timenow; | |
4539 | } | |
6528fe3e | 4540 | |
c9b9ae52 A |
4541 | // 2. Re-validate our cache records |
4542 | m->NextCacheCheck = m->timenow; | |
283ee3ff | 4543 | FORALL_CACHERECORDS(slot, cg, cr) |
c9d2d929 | 4544 | mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect); |
c9b9ae52 A |
4545 | |
4546 | // 3. Retrigger probing and announcing for all our authoritative records | |
6528fe3e A |
4547 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
4548 | { | |
c9b9ae52 | 4549 | if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; |
7f0064bd A |
4550 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); |
4551 | rr->AnnounceCount = InitialAnnounceCount; | |
4552 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); | |
c9b9ae52 | 4553 | InitializeLastAPTime(m, rr); |
6528fe3e | 4554 | } |
6528fe3e A |
4555 | } |
4556 | ||
4557 | mDNS_Unlock(m); | |
4558 | } | |
4559 | ||
4560 | // *************************************************************************** | |
c9b9ae52 | 4561 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
4562 | #pragma mark - |
4563 | #pragma mark - Packet Reception Functions | |
4564 | #endif | |
4565 | ||
6528fe3e A |
4566 | #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) |
4567 | ||
4568 | mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, | |
c9b9ae52 | 4569 | const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) |
6528fe3e | 4570 | { |
c9b9ae52 A |
4571 | mDNSu8 *responseptr = response->data; |
4572 | const mDNSu8 *const limit = response->data + sizeof(response->data); | |
6528fe3e | 4573 | const mDNSu8 *ptr = query->data; |
c9b9ae52 A |
4574 | AuthRecord *rr; |
4575 | mDNSu32 maxttl = 0x70000000; | |
6528fe3e A |
4576 | int i; |
4577 | ||
4578 | // Initialize the response fields so we can answer the questions | |
c9b9ae52 | 4579 | InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); |
6528fe3e A |
4580 | |
4581 | // *** | |
4582 | // *** 1. Write out the list of questions we are actually going to answer with this packet | |
4583 | // *** | |
c9b9ae52 | 4584 | if (LegacyQuery) |
6528fe3e | 4585 | { |
c9b9ae52 A |
4586 | maxttl = 10; |
4587 | for (i=0; i<query->h.numQuestions; i++) // For each question... | |
6528fe3e | 4588 | { |
c9b9ae52 A |
4589 | DNSQuestion q; |
4590 | ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... | |
4591 | if (!ptr) return(mDNSNULL); | |
4592 | ||
4593 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers | |
4594 | { | |
4595 | if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question | |
4596 | { // then put the question in the question section | |
4597 | responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); | |
4598 | if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } | |
4599 | break; // break out of the ResponseRecords loop, and go on to the next question | |
4600 | } | |
6528fe3e A |
4601 | } |
4602 | } | |
c9b9ae52 A |
4603 | |
4604 | if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } | |
6528fe3e A |
4605 | } |
4606 | ||
c9b9ae52 A |
4607 | // *** |
4608 | // *** 2. Write Answers | |
4609 | // *** | |
4610 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
4611 | if (rr->NR_AnswerTo) | |
4612 | { | |
4613 | mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl); | |
4614 | if (p) responseptr = p; | |
4615 | else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } | |
4616 | } | |
6528fe3e A |
4617 | |
4618 | // *** | |
c9b9ae52 | 4619 | // *** 3. Write Additionals |
6528fe3e A |
4620 | // *** |
4621 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
c9b9ae52 | 4622 | if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) |
6528fe3e | 4623 | { |
c9b9ae52 A |
4624 | mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl); |
4625 | if (p) responseptr = p; | |
4626 | else debugf("GenerateUnicastResponse: No more space for additionals"); | |
6528fe3e | 4627 | } |
c9b9ae52 | 4628 | |
6528fe3e A |
4629 | return(responseptr); |
4630 | } | |
4631 | ||
c9b9ae52 A |
4632 | // AuthRecord *our is our Resource Record |
4633 | // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network | |
6528fe3e A |
4634 | // Returns 0 if there is no conflict |
4635 | // Returns +1 if there was a conflict and we won | |
4636 | // Returns -1 if there was a conflict and we lost and have to rename | |
c9b9ae52 | 4637 | mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt) |
6528fe3e | 4638 | { |
6528fe3e | 4639 | mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; |
c9b9ae52 A |
4640 | mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; |
4641 | if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } | |
4642 | if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } | |
4643 | ||
4644 | ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); | |
4645 | pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); | |
4646 | while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } | |
4647 | if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict | |
4648 | ||
4649 | if (ourptr >= ourend) return(-1); // Our data ran out first; We lost | |
4650 | if (pktptr >= pktend) return(+1); // Packet data ran out first; We won | |
4651 | if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost | |
4652 | if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won | |
6528fe3e | 4653 | |
c9d2d929 | 4654 | debugf("CompareRData: How did we get here?"); |
6528fe3e A |
4655 | return(-1); |
4656 | } | |
4657 | ||
c9b9ae52 A |
4658 | // See if we have an authoritative record that's identical to this packet record, |
4659 | // whose canonical DependentOn record is the specified master record. | |
6528fe3e A |
4660 | // The DependentOn pointer is typically used for the TXT record of service registrations |
4661 | // It indicates that there is no inherent conflict detection for the TXT record | |
4662 | // -- it depends on the SRV record to resolve name conflicts | |
c9b9ae52 A |
4663 | // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn |
4664 | // pointer chain (if any) to make sure we reach the canonical DependentOn record | |
6528fe3e A |
4665 | // If the record has no DependentOn, then just return that record's pointer |
4666 | // Returns NULL if we don't have any local RRs that are identical to the one from the packet | |
c9b9ae52 | 4667 | mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master) |
6528fe3e | 4668 | { |
c9b9ae52 A |
4669 | const AuthRecord *r1; |
4670 | for (r1 = m->ResourceRecords; r1; r1=r1->next) | |
6528fe3e | 4671 | { |
c9b9ae52 | 4672 | if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) |
6528fe3e | 4673 | { |
c9b9ae52 A |
4674 | const AuthRecord *r2 = r1; |
4675 | while (r2->DependentOn) r2 = r2->DependentOn; | |
4676 | if (r2 == master) return(mDNStrue); | |
6528fe3e A |
4677 | } |
4678 | } | |
c9b9ae52 A |
4679 | for (r1 = m->DuplicateRecords; r1; r1=r1->next) |
4680 | { | |
4681 | if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) | |
4682 | { | |
4683 | const AuthRecord *r2 = r1; | |
4684 | while (r2->DependentOn) r2 = r2->DependentOn; | |
4685 | if (r2 == master) return(mDNStrue); | |
4686 | } | |
4687 | } | |
4688 | return(mDNSfalse); | |
6528fe3e A |
4689 | } |
4690 | ||
4691 | // Find the canonical RRSet pointer for this RR received in a packet. | |
c9b9ae52 | 4692 | // If we find any identical AuthRecord in our authoritative list, then follow its RRSet |
6528fe3e A |
4693 | // pointers (if any) to make sure we return the canonical member of this name/type/class |
4694 | // Returns NULL if we don't have any local RRs that are identical to the one from the packet | |
c9b9ae52 | 4695 | mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr) |
6528fe3e | 4696 | { |
c9b9ae52 | 4697 | const AuthRecord *rr; |
6528fe3e A |
4698 | for (rr = m->ResourceRecords; rr; rr=rr->next) |
4699 | { | |
c9b9ae52 | 4700 | if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) |
6528fe3e A |
4701 | { |
4702 | while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; | |
4703 | return(rr); | |
4704 | } | |
4705 | } | |
4706 | return(mDNSNULL); | |
4707 | } | |
4708 | ||
4709 | // PacketRRConflict is called when we've received an RR (pktrr) which has the same name | |
4710 | // as one of our records (our) but different rdata. | |
4711 | // 1. If our record is not a type that's supposed to be unique, we don't care. | |
4712 | // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one. | |
4713 | // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer | |
4714 | // points to our record, ignore this conflict (e.g. the packet record matches one of our | |
4715 | // TXT records, and that record is marked as dependent on 'our', its SRV record). | |
4716 | // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record | |
4717 | // are members of the same RRSet, then this is not a conflict. | |
c9b9ae52 | 4718 | mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr) |
6528fe3e | 4719 | { |
c9b9ae52 | 4720 | const AuthRecord *ourset = our->RRSet ? our->RRSet : our; |
6528fe3e A |
4721 | |
4722 | // If not supposed to be unique, not a conflict | |
c9b9ae52 | 4723 | if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); |
6528fe3e A |
4724 | |
4725 | // If a dependent record, not a conflict | |
c9b9ae52 | 4726 | if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); |
6528fe3e A |
4727 | |
4728 | // If the pktrr matches a member of ourset, not a conflict | |
4729 | if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse); | |
4730 | ||
4731 | // Okay, this is a conflict | |
4732 | return(mDNStrue); | |
4733 | } | |
4734 | ||
4735 | // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change | |
4736 | // the record list and/or question list. | |
4737 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
4738 | mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, | |
c9b9ae52 | 4739 | DNSQuestion *q, AuthRecord *our) |
6528fe3e A |
4740 | { |
4741 | int i; | |
4742 | const mDNSu8 *ptr = LocateAuthorities(query, end); | |
4743 | mDNSBool FoundUpdate = mDNSfalse; | |
4744 | ||
4745 | for (i = 0; i < query->h.numAuthorities; i++) | |
4746 | { | |
7f0064bd | 4747 | ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); |
6528fe3e | 4748 | if (!ptr) break; |
7f0064bd | 4749 | if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) |
6528fe3e A |
4750 | { |
4751 | FoundUpdate = mDNStrue; | |
7f0064bd | 4752 | if (PacketRRConflict(m, our, &m->rec.r)) |
6528fe3e | 4753 | { |
7f0064bd A |
4754 | int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; |
4755 | if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; | |
4756 | if (!result) result = CompareRData(our, &m->rec.r); | |
c9d2d929 | 4757 | switch (result) |
6528fe3e | 4758 | { |
c9d2d929 A |
4759 | case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); |
4760 | break; | |
4761 | case 0: break; | |
4762 | case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); | |
4763 | mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); | |
4764 | goto exit; | |
6528fe3e A |
4765 | } |
4766 | } | |
4767 | } | |
7f0064bd | 4768 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
6528fe3e A |
4769 | } |
4770 | if (!FoundUpdate) | |
283ee3ff | 4771 | debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); |
7f0064bd A |
4772 | exit: |
4773 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it | |
c9b9ae52 A |
4774 | } |
4775 | ||
4776 | mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) | |
4777 | { | |
283ee3ff A |
4778 | mDNSu32 slot = HashSlot(pktrr->name); |
4779 | CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); | |
c9b9ae52 | 4780 | CacheRecord *rr; |
283ee3ff | 4781 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
c9b9ae52 A |
4782 | if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break; |
4783 | return(rr); | |
6528fe3e A |
4784 | } |
4785 | ||
4786 | // ProcessQuery examines a received query to see if we have any answers to give | |
4787 | mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, | |
c9d2d929 A |
4788 | const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, |
4789 | DNSMessage *const response) | |
6528fe3e | 4790 | { |
283ee3ff | 4791 | mDNSBool FromLocalSubnet = AddressIsLocalSubnet(m, InterfaceID, srcaddr); |
7f0064bd A |
4792 | AuthRecord *ResponseRecords = mDNSNULL; |
4793 | AuthRecord **nrp = &ResponseRecords; | |
4794 | CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated | |
4795 | CacheRecord **eap = &ExpectedAnswers; | |
4796 | DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet | |
4797 | DNSQuestion **dqp = &DupQuestions; | |
4798 | mDNSs32 delayresponse = 0; | |
4799 | mDNSBool SendLegacyResponse = mDNSfalse; | |
4800 | const mDNSu8 *ptr = query->data; | |
4801 | mDNSu8 *responseptr = mDNSNULL; | |
4802 | AuthRecord *rr; | |
6528fe3e A |
4803 | int i; |
4804 | ||
6528fe3e A |
4805 | // *** |
4806 | // *** 1. Parse Question Section and mark potential answers | |
4807 | // *** | |
4808 | for (i=0; i<query->h.numQuestions; i++) // For each question... | |
4809 | { | |
c9b9ae52 | 4810 | mDNSBool QuestionNeedsMulticastResponse; |
6528fe3e | 4811 | int NumAnswersForThisQuestion = 0; |
c9b9ae52 A |
4812 | DNSQuestion pktq, *q; |
4813 | ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... | |
6528fe3e | 4814 | if (!ptr) goto exit; |
c9b9ae52 A |
4815 | |
4816 | // The only queries that *need* a multicast response are: | |
4817 | // * Queries sent via multicast | |
4818 | // * from port 5353 | |
4819 | // * that don't have the kDNSQClass_UnicastResponse bit set | |
4820 | // These queries need multicast responses because other clients will: | |
4821 | // * suppress their own identical questions when they see these questions, and | |
4822 | // * expire their cache records if they don't see the expected responses | |
4823 | // For other queries, we may still choose to send the occasional multicast response anyway, | |
4824 | // to keep our neighbours caches warm, and for ongoing conflict detection. | |
4825 | QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); | |
4826 | // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later | |
4827 | pktq.qclass &= ~kDNSQClass_UnicastResponse; | |
6528fe3e A |
4828 | |
4829 | // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe | |
4830 | // can result in user callbacks which may change the record list and/or question list. | |
4831 | // Also note: we just mark potential answer records here, without trying to build the | |
4832 | // "ResponseRecords" list, because we don't want to risk user callbacks deleting records | |
c9b9ae52 A |
4833 | // from that list while we're in the middle of trying to build it. |
4834 | if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set"); | |
6528fe3e A |
4835 | m->CurrentRecord = m->ResourceRecords; |
4836 | while (m->CurrentRecord) | |
4837 | { | |
4838 | rr = m->CurrentRecord; | |
4839 | m->CurrentRecord = rr->next; | |
7f0064bd | 4840 | if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) |
6528fe3e | 4841 | { |
c9b9ae52 A |
4842 | if (rr->resrec.RecordType == kDNSRecordTypeUnique) |
4843 | ResolveSimultaneousProbe(m, query, end, &pktq, rr); | |
6528fe3e A |
4844 | else if (ResourceRecordIsValidAnswer(rr)) |
4845 | { | |
4846 | NumAnswersForThisQuestion++; | |
c9b9ae52 | 4847 | // Notes: |
283ee3ff A |
4848 | // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) |
4849 | // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead) | |
7f0064bd | 4850 | // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) |
283ee3ff A |
4851 | // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, |
4852 | // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link) | |
4853 | // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) | |
4854 | if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery)) | |
c9b9ae52 A |
4855 | { |
4856 | // We only mark this question for sending if it is at least one second since the last time we multicast it | |
283ee3ff | 4857 | // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it. |
c9b9ae52 A |
4858 | // This is to guard against the case where someone blasts us with queries as fast as they can. |
4859 | if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || | |
4860 | (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) | |
4861 | rr->NR_AnswerTo = (mDNSu8*)~0; | |
4862 | } | |
7f0064bd | 4863 | else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; |
6528fe3e A |
4864 | } |
4865 | } | |
4866 | } | |
c9b9ae52 | 4867 | |
7f0064bd A |
4868 | // If we couldn't answer this question, someone else might be able to, |
4869 | // so use random delay on response to reduce collisions | |
4870 | if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms | |
4871 | ||
c9b9ae52 A |
4872 | // We only do the following accelerated cache expiration processing and duplicate question suppression processing |
4873 | // for multicast queries with multicast responses. | |
4874 | // For any query generating a unicast response we don't do this because we can't assume we will see the response | |
4875 | if (QuestionNeedsMulticastResponse) | |
4876 | { | |
283ee3ff A |
4877 | const mDNSu32 slot = HashSlot(&pktq.qname); |
4878 | CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname); | |
c9b9ae52 | 4879 | CacheRecord *rr; |
c9b9ae52 A |
4880 | |
4881 | // Make a list indicating which of our own cache records we expect to see updated as a result of this query | |
4882 | // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated | |
283ee3ff | 4883 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
c9b9ae52 A |
4884 | if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit) |
4885 | if (!rr->NextInKAList && eap != &rr->NextInKAList) | |
4886 | { | |
4887 | *eap = rr; | |
4888 | eap = &rr->NextInKAList; | |
4889 | if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond) | |
4890 | { | |
4891 | // Although MPUnansweredQ is only really used for multi-packet query processing, | |
4892 | // we increment it for both single-packet and multi-packet queries, so that it stays in sync | |
4893 | // with the MPUnansweredKA value, which by necessity is incremented for both query types. | |
4894 | rr->MPUnansweredQ++; | |
4895 | rr->MPLastUnansweredQT = m->timenow; | |
4896 | rr->MPExpectingKA = mDNStrue; | |
4897 | } | |
4898 | } | |
4899 | ||
4900 | // Check if this question is the same as any of mine. | |
4901 | // We only do this for non-truncated queries. Right now it would be too complicated to try | |
4902 | // to keep track of duplicate suppression state between multiple packets, especially when we | |
4903 | // can't guarantee to receive all of the Known Answer packets that go with a particular query. | |
4904 | if (!(query->h.flags.b[0] & kDNSFlag0_TC)) | |
4905 | for (q = m->Questions; q; q=q->next) | |
8e92c31c | 4906 | if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) |
c9b9ae52 A |
4907 | if (!q->InterfaceID || q->InterfaceID == InterfaceID) |
4908 | if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) | |
c9d2d929 | 4909 | if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) |
c9b9ae52 A |
4910 | { *dqp = q; dqp = &q->NextInDQList; } |
4911 | } | |
6528fe3e A |
4912 | } |
4913 | ||
4914 | // *** | |
4915 | // *** 2. Now we can safely build the list of marked answers | |
4916 | // *** | |
4917 | for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers | |
4918 | if (rr->NR_AnswerTo) // If we marked the record... | |
c9b9ae52 | 4919 | AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list |
6528fe3e A |
4920 | |
4921 | // *** | |
4922 | // *** 3. Add additional records | |
4923 | // *** | |
7f0064bd | 4924 | AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); |
6528fe3e A |
4925 | |
4926 | // *** | |
c9b9ae52 | 4927 | // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list |
6528fe3e A |
4928 | // *** |
4929 | for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section... | |
4930 | { | |
4931 | // Get the record... | |
c9b9ae52 A |
4932 | AuthRecord *rr; |
4933 | CacheRecord *ourcacherr; | |
7f0064bd | 4934 | ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); |
6528fe3e A |
4935 | if (!ptr) goto exit; |
4936 | ||
c9b9ae52 | 4937 | // See if this Known-Answer suppresses any of our currently planned answers |
6528fe3e | 4938 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) |
7f0064bd | 4939 | if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) |
6528fe3e A |
4940 | { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } |
4941 | ||
c9b9ae52 | 4942 | // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) |
6528fe3e A |
4943 | for (rr=m->ResourceRecords; rr; rr=rr->next) |
4944 | { | |
c9b9ae52 | 4945 | // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression |
7f0064bd | 4946 | if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) |
c9b9ae52 A |
4947 | { |
4948 | if (srcaddr->type == mDNSAddrType_IPv4) | |
6528fe3e | 4949 | { |
7f0064bd | 4950 | if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; |
6528fe3e | 4951 | } |
c9b9ae52 A |
4952 | else if (srcaddr->type == mDNSAddrType_IPv6) |
4953 | { | |
4954 | if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; | |
4955 | } | |
8e92c31c A |
4956 | if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) |
4957 | { | |
7f0064bd A |
4958 | rr->ImmedAnswer = mDNSNULL; |
4959 | rr->ImmedUnicast = mDNSfalse; | |
8e92c31c | 4960 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
7f0064bd | 4961 | LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); |
8e92c31c A |
4962 | #endif |
4963 | } | |
c9b9ae52 A |
4964 | } |
4965 | } | |
4966 | ||
4967 | // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, | |
4968 | // 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 | 4969 | ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); |
c9b9ae52 A |
4970 | if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) |
4971 | { | |
4972 | ourcacherr->MPUnansweredKA++; | |
4973 | ourcacherr->MPExpectingKA = mDNSfalse; | |
4974 | } | |
4975 | ||
4976 | // Having built our ExpectedAnswers list from the questions in this packet, we can definitively | |
4977 | // remove from our ExpectedAnswers list any records that are suppressed in the very same packet. | |
4978 | // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them. | |
4979 | eap = &ExpectedAnswers; | |
4980 | while (*eap) | |
4981 | { | |
4982 | CacheRecord *rr = *eap; | |
7f0064bd | 4983 | if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) |
c9b9ae52 A |
4984 | { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; } |
4985 | else eap = &rr->NextInKAList; | |
4986 | } | |
4987 | ||
4988 | // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. | |
4989 | if (!ourcacherr) | |
4990 | { | |
4991 | dqp = &DupQuestions; | |
4992 | while (*dqp) | |
4993 | { | |
4994 | DNSQuestion *q = *dqp; | |
7f0064bd | 4995 | if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) |
c9b9ae52 A |
4996 | { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } |
4997 | else dqp = &q->NextInDQList; | |
4998 | } | |
6528fe3e | 4999 | } |
7f0064bd | 5000 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
6528fe3e A |
5001 | } |
5002 | ||
5003 | // *** | |
5004 | // *** 5. Cancel any additionals that were added because of now-deleted records | |
5005 | // *** | |
5006 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
5007 | if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo)) | |
5008 | { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } | |
5009 | ||
5010 | // *** | |
5011 | // *** 6. Mark the send flags on the records we plan to send | |
5012 | // *** | |
5013 | for (rr=ResponseRecords; rr; rr=rr->NextResponse) | |
5014 | { | |
c9b9ae52 | 5015 | if (rr->NR_AnswerTo) |
6528fe3e | 5016 | { |
7f0064bd A |
5017 | mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response |
5018 | mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) | |
c9b9ae52 A |
5019 | |
5020 | // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. | |
283ee3ff A |
5021 | if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) |
5022 | { | |
5023 | SendMulticastResponse = mDNStrue; | |
5024 | // If this record was marked for modern (delayed) unicast response, then mark it as promoted to | |
5025 | // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below). | |
5026 | // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value. | |
5027 | if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0; | |
5028 | } | |
c9b9ae52 A |
5029 | |
5030 | // If the client insists on a multicast response, then we'd better send one | |
7f0064bd A |
5031 | if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; |
5032 | else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; | |
5033 | else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; | |
c9b9ae52 | 5034 | |
7f0064bd | 5035 | if (SendMulticastResponse || SendUnicastResponse) |
6528fe3e | 5036 | { |
8e92c31c A |
5037 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
5038 | rr->ImmedAnswerMarkTime = m->timenow; | |
5039 | #endif | |
5040 | m->NextScheduledResponse = m->timenow; | |
c9b9ae52 A |
5041 | // If we're already planning to send this on another interface, just send it on all interfaces |
5042 | if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) | |
c9b9ae52 | 5043 | rr->ImmedAnswer = mDNSInterfaceMark; |
6528fe3e A |
5044 | else |
5045 | { | |
c9b9ae52 | 5046 | rr->ImmedAnswer = InterfaceID; // Record interface to send it on |
7f0064bd | 5047 | if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue; |
c9b9ae52 A |
5048 | if (srcaddr->type == mDNSAddrType_IPv4) |
5049 | { | |
5050 | if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4; | |
5051 | else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr; | |
5052 | } | |
5053 | else if (srcaddr->type == mDNSAddrType_IPv6) | |
5054 | { | |
5055 | if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6; | |
5056 | else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr; | |
5057 | } | |
6528fe3e A |
5058 | } |
5059 | } | |
7f0064bd A |
5060 | // If TC flag is set, it means we should expect that additional known answers may be coming in another packet, |
5061 | // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11) | |
5062 | // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses | |
5063 | // else, for a simple unique record reply, we can reply immediately; no need for delay | |
5064 | if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms | |
5065 | else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms | |
6528fe3e | 5066 | } |
c9b9ae52 A |
5067 | else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) |
5068 | { | |
5069 | // Since additional records are an optimization anyway, we only ever send them on one interface at a time | |
5070 | // If two clients on different interfaces do queries that invoke the same optional additional answer, | |
5071 | // then the earlier client is out of luck | |
5072 | rr->ImmedAdditional = InterfaceID; | |
5073 | // No need to set m->NextScheduledResponse here | |
5074 | // We'll send these additional records when we send them, or not, as the case may be | |
5075 | } | |
6528fe3e A |
5076 | } |
5077 | ||
5078 | // *** | |
5079 | // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer | |
5080 | // *** | |
8d1ca615 A |
5081 | if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) |
5082 | { | |
8e92c31c A |
5083 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
5084 | mDNSs32 oldss = m->SuppressSending; | |
5085 | if (oldss && delayresponse) | |
5086 | LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); | |
5087 | #endif | |
8d1ca615 A |
5088 | // Pick a random delay: |
5089 | // We start with the base delay chosen above (typically either 1 second or 20 seconds), | |
5090 | // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds). | |
5091 | // This is an integer value, with resolution determined by the platform clock rate. | |
5092 | // We then divide that by 50 to get the delay value in ticks. We defer the division until last | |
5093 | // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second). | |
5094 | // The +49 before dividing is to ensure we round up, not down, to ensure that even | |
5095 | // on platforms where the native clock rate is less than fifty ticks per second, | |
5096 | // we still guarantee that the final calculated delay is at least one platform tick. | |
5097 | // We want to make sure we don't ever allow the delay to be zero ticks, | |
7cb34e5c | 5098 | // because if that happens we'll fail the Bonjour Conformance Test. |
8d1ca615 A |
5099 | // Our final computed delay is 20-120ms for normal delayed replies, |
5100 | // or 400-500ms in the case of multi-packet known-answer lists. | |
5101 | m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50; | |
6528fe3e | 5102 | if (m->SuppressSending == 0) m->SuppressSending = 1; |
8e92c31c A |
5103 | #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES |
5104 | if (oldss && delayresponse) | |
5105 | LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow); | |
5106 | #endif | |
6528fe3e A |
5107 | } |
5108 | ||
5109 | // *** | |
8e92c31c | 5110 | // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too |
6528fe3e | 5111 | // *** |
7f0064bd | 5112 | if (SendLegacyResponse) |
c9b9ae52 | 5113 | responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); |
6528fe3e A |
5114 | |
5115 | exit: | |
7f0064bd A |
5116 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
5117 | ||
6528fe3e | 5118 | // *** |
c9b9ae52 | 5119 | // *** 9. Finally, clear our link chains ready for use next time |
6528fe3e A |
5120 | // *** |
5121 | while (ResponseRecords) | |
5122 | { | |
5123 | rr = ResponseRecords; | |
5124 | ResponseRecords = rr->NextResponse; | |
5125 | rr->NextResponse = mDNSNULL; | |
5126 | rr->NR_AnswerTo = mDNSNULL; | |
5127 | rr->NR_AdditionalTo = mDNSNULL; | |
5128 | } | |
5129 | ||
c9b9ae52 A |
5130 | while (ExpectedAnswers) |
5131 | { | |
5132 | CacheRecord *rr; | |
5133 | rr = ExpectedAnswers; | |
5134 | ExpectedAnswers = rr->NextInKAList; | |
5135 | rr->NextInKAList = mDNSNULL; | |
5136 | ||
5137 | // For non-truncated queries, we can definitively say that we should expect | |
5138 | // to be seeing a response for any records still left in the ExpectedAnswers list | |
5139 | if (!(query->h.flags.b[0] & kDNSFlag0_TC)) | |
5140 | if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond) | |
5141 | { | |
5142 | rr->UnansweredQueries++; | |
5143 | rr->LastUnansweredTime = m->timenow; | |
5144 | if (rr->UnansweredQueries > 1) | |
5145 | debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", | |
7f0064bd | 5146 | rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); |
c9b9ae52 A |
5147 | SetNextCacheCheckTime(m, rr); |
5148 | } | |
5149 | ||
5150 | // If we've seen multiple unanswered queries for this record, | |
5151 | // then mark it to expire in five seconds if we don't get a response by then. | |
5152 | if (rr->UnansweredQueries >= MaxUnansweredQueries) | |
5153 | { | |
5154 | // Only show debugging message if this record was not about to expire anyway | |
5155 | if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) | |
5156 | debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", | |
7f0064bd | 5157 | rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); |
c9b9ae52 A |
5158 | mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); |
5159 | } | |
5160 | // Make a guess, based on the multi-packet query / known answer counts, whether we think we | |
5161 | // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for | |
5162 | // possible packet loss of up to 20% of the additional KA packets.) | |
5163 | else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8) | |
5164 | { | |
5165 | // We want to do this conservatively. | |
5166 | // If there are so many machines on the network that they have to use multi-packet known-answer lists, | |
5167 | // then we don't want them to all hit the network simultaneously with their final expiration queries. | |
5168 | // By setting the record to expire in four minutes, we achieve two things: | |
5169 | // (a) the 90-95% final expiration queries will be less bunched together | |
5170 | // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own | |
5171 | mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4; | |
5172 | if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) | |
5173 | remain = 240 * (mDNSu32)mDNSPlatformOneSecond; | |
5174 | ||
5175 | // Only show debugging message if this record was not about to expire anyway | |
5176 | if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) | |
5177 | debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", | |
7f0064bd | 5178 | rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); |
c9b9ae52 A |
5179 | |
5180 | if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) | |
5181 | rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query | |
5182 | rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics | |
5183 | rr->MPUnansweredKA = 0; | |
5184 | rr->MPExpectingKA = mDNSfalse; | |
5185 | ||
5186 | if (remain < kDefaultReconfirmTimeForNoAnswer) | |
5187 | remain = kDefaultReconfirmTimeForNoAnswer; | |
5188 | mDNS_Reconfirm_internal(m, rr, remain); | |
5189 | } | |
5190 | } | |
5191 | ||
5192 | while (DupQuestions) | |
5193 | { | |
5194 | int i; | |
5195 | DNSQuestion *q = DupQuestions; | |
5196 | DupQuestions = q->NextInDQList; | |
5197 | q->NextInDQList = mDNSNULL; | |
5198 | i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); | |
5199 | debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, | |
5200 | srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); | |
5201 | } | |
5202 | ||
6528fe3e A |
5203 | return(responseptr); |
5204 | } | |
5205 | ||
5206 | mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, | |
c9b9ae52 A |
5207 | const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, |
5208 | const mDNSInterfaceID InterfaceID) | |
6528fe3e | 5209 | { |
7f0064bd A |
5210 | mDNSu8 *responseend = mDNSNULL; |
5211 | mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr); | |
8e92c31c | 5212 | |
7f0064bd | 5213 | if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr)) |
6528fe3e | 5214 | { |
c9d2d929 | 5215 | 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 | 5216 | srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, |
b7388343 A |
5217 | msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", |
5218 | msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", | |
5219 | msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", | |
5220 | msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); | |
8e92c31c A |
5221 | return; |
5222 | } | |
b7388343 | 5223 | |
c9d2d929 | 5224 | 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 |
5225 | srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, |
5226 | msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", | |
5227 | msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", | |
5228 | msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", | |
5229 | msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); | |
5230 | ||
5231 | responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, | |
7f0064bd | 5232 | (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); |
8e92c31c A |
5233 | |
5234 | if (responseend) // If responseend is non-null, that means we built a unicast response packet | |
5235 | { | |
5236 | debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld", | |
7f0064bd A |
5237 | m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", |
5238 | m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", | |
5239 | m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", | |
8e92c31c | 5240 | srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); |
7f0064bd | 5241 | mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL); |
6528fe3e A |
5242 | } |
5243 | } | |
5244 | ||
5245 | // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change | |
5246 | // the record list and/or question list. | |
5247 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
5248 | mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, | |
8e92c31c A |
5249 | const DNSMessage *const response, const mDNSu8 *end, |
5250 | const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, | |
7f0064bd | 5251 | const mDNSInterfaceID InterfaceID) |
6528fe3e A |
5252 | { |
5253 | int i; | |
c9d2d929 A |
5254 | const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet |
5255 | CacheRecord *CacheFlushRecords = (CacheRecord*)1; // "(CacheRecord*)1" is special (non-zero) end-of-list marker | |
c9b9ae52 | 5256 | CacheRecord **cfp = &CacheFlushRecords; |
7f0064bd | 5257 | |
6528fe3e | 5258 | // All records in a DNS response packet are treated as equally valid statements of truth. If we want |
c9b9ae52 | 5259 | // to guard against spoof responses, then the only credible protection against that is cryptographic |
6528fe3e A |
5260 | // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record |
5261 | int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals; | |
5262 | ||
c9b9ae52 | 5263 | (void)srcaddr; // Currently used only for display in debugging message |
7f0064bd A |
5264 | (void)srcport; |
5265 | (void)dstport; | |
c9b9ae52 | 5266 | |
c9d2d929 | 5267 | verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", |
7f0064bd | 5268 | srcaddr, dstaddr, InterfaceID, |
c9b9ae52 A |
5269 | response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", |
5270 | response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", | |
5271 | response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", | |
6528fe3e A |
5272 | response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); |
5273 | ||
7f0064bd A |
5274 | // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us |
5275 | if (!mDNSAddrIsDNSMulticast(dstaddr)) | |
283ee3ff | 5276 | { |
7f0064bd A |
5277 | if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2)) |
5278 | return; | |
283ee3ff A |
5279 | // For now we don't put standard wide-area unicast responses in our main cache |
5280 | // (Later we should fix this and cache all known results in a unified manner.) | |
5281 | if (response->h.id.NotAnInteger != 0 || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger) | |
5282 | return; | |
5283 | } | |
6528fe3e A |
5284 | |
5285 | for (i = 0; i < totalrecords && ptr && ptr < end; i++) | |
5286 | { | |
c9b9ae52 | 5287 | const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); |
7f0064bd A |
5288 | ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); |
5289 | if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting | |
6528fe3e A |
5290 | |
5291 | // 1. Check that this packet resource record does not conflict with any of ours | |
c9b9ae52 | 5292 | if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); |
6528fe3e A |
5293 | m->CurrentRecord = m->ResourceRecords; |
5294 | while (m->CurrentRecord) | |
5295 | { | |
c9b9ae52 | 5296 | AuthRecord *rr = m->CurrentRecord; |
6528fe3e | 5297 | m->CurrentRecord = rr->next; |
7f0064bd | 5298 | if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... |
c9b9ae52 | 5299 | { |
7f0064bd A |
5300 | // ... check to see if type and rdata are identical |
5301 | if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec)) | |
6528fe3e A |
5302 | { |
5303 | // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us | |
7f0064bd | 5304 | if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) |
c9b9ae52 A |
5305 | { |
5306 | // If we were planning to send on this -- and only this -- interface, then we don't need to any more | |
7f0064bd | 5307 | if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; } |
c9b9ae52 | 5308 | } |
6528fe3e | 5309 | else |
c9b9ae52 A |
5310 | { |
5311 | if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } | |
5312 | else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } | |
5313 | } | |
6528fe3e | 5314 | } |
7f0064bd A |
5315 | // else, the packet RR has different type or different rdata -- check to see if this is a conflict |
5316 | else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) | |
6528fe3e | 5317 | { |
7cb34e5c A |
5318 | debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr)); |
5319 | debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); | |
c9b9ae52 | 5320 | |
7f0064bd A |
5321 | // If this record is marked DependentOn another record for conflict detection purposes, |
5322 | // then *that* record has to be bumped back to probing state to resolve the conflict | |
5323 | while (rr->DependentOn) rr = rr->DependentOn; | |
c9b9ae52 | 5324 | |
7f0064bd A |
5325 | // If we've just whacked this record's ProbeCount, don't need to do it again |
5326 | if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) | |
5327 | { | |
5328 | // If we'd previously verified this record, put it back to probing state and try again | |
5329 | if (rr->resrec.RecordType == kDNSRecordTypeVerified) | |
6528fe3e | 5330 | { |
283ee3ff | 5331 | debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd A |
5332 | rr->resrec.RecordType = kDNSRecordTypeUnique; |
5333 | rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; | |
5334 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); | |
5335 | InitializeLastAPTime(m, rr); | |
5336 | RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate | |
5337 | } | |
5338 | // If we're probing for this record, we just failed | |
5339 | else if (rr->resrec.RecordType == kDNSRecordTypeUnique) | |
5340 | { | |
283ee3ff | 5341 | debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd A |
5342 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); |
5343 | } | |
5344 | // We assumed this record must be unique, but we were wrong. | |
5345 | // (e.g. There are two mDNSResponders on the same machine giving | |
5346 | // different answers for the reverse mapping record.) | |
5347 | // This is simply a misconfiguration, and we don't try to recover from it. | |
5348 | else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) | |
5349 | { | |
5350 | debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", | |
283ee3ff | 5351 | rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
7f0064bd | 5352 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); |
6528fe3e | 5353 | } |
7f0064bd A |
5354 | else |
5355 | debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", | |
283ee3ff | 5356 | rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); |
6528fe3e A |
5357 | } |
5358 | } | |
7f0064bd A |
5359 | // Else, matching signature, different type or rdata, but not a considered a conflict. |
5360 | // If the packet record has the cache-flush bit set, then we check to see if we | |
5361 | // have any record(s) of the same type that we should re-assert to rescue them | |
5362 | // (see note about "multi-homing and bridged networks" at the end of this function). | |
5363 | else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) | |
5364 | if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) | |
5365 | { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } | |
6528fe3e A |
5366 | } |
5367 | } | |
5368 | ||
5369 | // 2. See if we want to add this packet resource record to our cache | |
5370 | if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in | |
5371 | { | |
283ee3ff A |
5372 | const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); |
5373 | CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); | |
c9b9ae52 | 5374 | CacheRecord *rr; |
6528fe3e | 5375 | // 2a. Check if this packet resource record is already in our cache |
283ee3ff | 5376 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
6528fe3e A |
5377 | { |
5378 | // If we found this exact resource record, refresh its TTL | |
7f0064bd | 5379 | if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) |
6528fe3e | 5380 | { |
7f0064bd | 5381 | if (m->rec.r.resrec.rdlength > InlineCacheRDSize) |
c9b9ae52 | 5382 | verbosedebugf("Found record size %5d interface %p already in cache: %s", |
7f0064bd | 5383 | m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); |
c9b9ae52 A |
5384 | rr->TimeRcvd = m->timenow; |
5385 | ||
7f0064bd | 5386 | if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) |
c9b9ae52 A |
5387 | { |
5388 | // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list | |
5389 | if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList) | |
283ee3ff | 5390 | { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } |
c9b9ae52 A |
5391 | |
5392 | // If this packet record is marked unique, and our previous cached copy was not, then fix it | |
5393 | if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) | |
5394 | { | |
5395 | DNSQuestion *q; | |
5396 | for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; | |
7f0064bd | 5397 | rr->resrec.RecordType = m->rec.r.resrec.RecordType; |
c9b9ae52 A |
5398 | } |
5399 | } | |
5400 | ||
7cb34e5c A |
5401 | if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength)) |
5402 | { | |
5403 | // If the rdata of the packet record differs in name capitalization from the record in our cache | |
5404 | // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get | |
5405 | // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. | |
5406 | rr->resrec.rroriginalttl = 0; | |
5407 | rr->UnansweredQueries = MaxUnansweredQueries; | |
5408 | SetNextCacheCheckTime(m, rr); | |
5409 | // DO NOT break out here -- we want to continue as if we never found it | |
5410 | } | |
5411 | else if (m->rec.r.resrec.rroriginalttl > 0) | |
c9b9ae52 | 5412 | { |
7f0064bd | 5413 | rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl; |
c9b9ae52 A |
5414 | rr->UnansweredQueries = 0; |
5415 | rr->MPUnansweredQ = 0; | |
5416 | rr->MPUnansweredKA = 0; | |
5417 | rr->MPExpectingKA = mDNSfalse; | |
7cb34e5c A |
5418 | SetNextCacheCheckTime(m, rr); |
5419 | break; | |
c9b9ae52 A |
5420 | } |
5421 | else | |
5422 | { | |
5423 | // If the packet TTL is zero, that means we're deleting this record. | |
5424 | // To give other hosts on the network a chance to protest, we push the deletion | |
5425 | // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. | |
5426 | // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent | |
5427 | // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. | |
5428 | rr->resrec.rroriginalttl = 1; | |
5429 | rr->UnansweredQueries = MaxUnansweredQueries; | |
7cb34e5c A |
5430 | SetNextCacheCheckTime(m, rr); |
5431 | break; | |
c9b9ae52 | 5432 | } |
6528fe3e A |
5433 | } |
5434 | } | |
5435 | ||
5436 | // If packet resource record not in our cache, add it now | |
5437 | // (unless it is just a deletion of a record we never had, in which case we don't care) | |
7f0064bd | 5438 | if (!rr && m->rec.r.resrec.rroriginalttl > 0) |
6528fe3e | 5439 | { |
283ee3ff A |
5440 | // If we don't have a CacheGroup for this name, make one now |
5441 | if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); | |
5442 | if (cg) rr = GetCacheRecord(m, cg, m->rec.r.resrec.rdlength); // Make a cache record, being careful not to recycle cg | |
5443 | if (!rr) NoCacheAnswer(m, &m->rec.r); | |
6528fe3e A |
5444 | else |
5445 | { | |
c9b9ae52 | 5446 | RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer |
283ee3ff A |
5447 | *rr = m->rec.r; // Block copy the CacheRecord object |
5448 | rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment | |
5449 | rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header | |
c9b9ae52 | 5450 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) |
283ee3ff | 5451 | { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } |
c9b9ae52 | 5452 | // If this is an oversized record with external storage allocated, copy rdata to external storage |
283ee3ff A |
5453 | if (rr->resrec.rdata != (RData*)&rr->rdatastorage && !(m->rec.r.resrec.rdlength > InlineCacheRDSize)) |
5454 | LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c); | |
7f0064bd A |
5455 | if (m->rec.r.resrec.rdlength > InlineCacheRDSize) |
5456 | mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength); | |
716635cc | 5457 | rr->next = mDNSNULL; // Clear 'next' pointer |
283ee3ff A |
5458 | *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list |
5459 | cg->rrcache_tail = &(rr->next); // Advance tail pointer | |
7f0064bd A |
5460 | if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have |
5461 | rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event | |
5462 | else | |
283ee3ff | 5463 | rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot); |
c9d2d929 A |
5464 | CacheRecordAdd(m, rr); |
5465 | // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us | |
5466 | SetNextCacheCheckTime(m, rr); | |
6528fe3e A |
5467 | } |
5468 | } | |
5469 | } | |
7f0064bd | 5470 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
6528fe3e A |
5471 | } |
5472 | ||
7f0064bd A |
5473 | exit: |
5474 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it | |
5475 | ||
c9b9ae52 A |
5476 | // If we've just received one or more records with their cache flush bits set, |
5477 | // then scan that cache slot to see if there are any old stale records we need to flush | |
283ee3ff | 5478 | while (CacheFlushRecords != (CacheRecord*)1) |
6528fe3e | 5479 | { |
c9b9ae52 | 5480 | CacheRecord *r1 = CacheFlushRecords, *r2; |
283ee3ff A |
5481 | const mDNSu32 slot = HashSlot(r1->resrec.name); |
5482 | CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec); | |
c9b9ae52 A |
5483 | CacheFlushRecords = CacheFlushRecords->NextInCFList; |
5484 | r1->NextInCFList = mDNSNULL; | |
283ee3ff A |
5485 | for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) |
5486 | if (SameResourceRecordSignature(&r1->resrec, &r2->resrec)) | |
6528fe3e | 5487 | { |
c9d2d929 A |
5488 | // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) |
5489 | // else, if record is old, mark it to be flushed | |
5490 | if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond) | |
283ee3ff | 5491 | r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; |
c9d2d929 | 5492 | else |
283ee3ff | 5493 | { |
c9d2d929 | 5494 | verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name->c, DNSTypeName(r2->resrec.rrtype)); |
283ee3ff A |
5495 | // We set stale records to expire in one second. |
5496 | // This gives the owner a chance to rescue it if necessary. | |
5497 | // This is important in the case of multi-homing and bridged networks: | |
5498 | // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be | |
5499 | // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit | |
5500 | // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet | |
5501 | // will promptly delete their cached copies of the (still valid) Ethernet IP address record. | |
5502 | // By delaying the deletion by one second, we give X a change to notice that this bridging has | |
5503 | // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. | |
5504 | // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary | |
5505 | // final expiration queries for this record. | |
5506 | r2->resrec.rroriginalttl = 1; | |
5507 | r2->TimeRcvd = m->timenow; | |
5508 | r2->UnansweredQueries = MaxUnansweredQueries; | |
c9d2d929 | 5509 | SetNextCacheCheckTime(m, r2); |
283ee3ff | 5510 | } |
6528fe3e | 5511 | } |
7f0064bd A |
5512 | if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to |
5513 | { | |
283ee3ff | 5514 | r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot); |
7f0064bd A |
5515 | if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); |
5516 | } | |
6528fe3e A |
5517 | } |
5518 | } | |
5519 | ||
7f0064bd | 5520 | mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, |
c9b9ae52 | 5521 | const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, |
7f0064bd | 5522 | const mDNSInterfaceID InterfaceID) |
6528fe3e | 5523 | { |
7f0064bd A |
5524 | DNSMessage *msg = (DNSMessage *)pkt; |
5525 | const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; | |
5526 | const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; | |
5527 | mDNSu8 QR_OP; | |
5528 | mDNSu8 *ptr = mDNSNULL; | |
283ee3ff | 5529 | const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; |
7f0064bd A |
5530 | |
5531 | #ifndef UNICAST_DISABLED | |
c9d2d929 A |
5532 | mDNSIPPort NATPort = mDNSOpaque16fromIntVal(NATMAP_PORT); |
5533 | ||
5534 | if (srcport.NotAnInteger == NATPort.NotAnInteger) | |
7f0064bd A |
5535 | { |
5536 | mDNS_Lock(m); | |
5537 | uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); | |
5538 | mDNS_Unlock(m); | |
5539 | return; | |
5540 | } | |
5541 | #endif | |
5542 | if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } | |
5543 | QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); | |
6528fe3e | 5544 | // Read the integer parts which are in IETF byte-order (MSB first, LSB second) |
7f0064bd | 5545 | ptr = (mDNSu8 *)&msg->h.numQuestions; |
6528fe3e A |
5546 | msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); |
5547 | msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); | |
5548 | msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); | |
5549 | msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); | |
8e92c31c | 5550 | |
c9b9ae52 A |
5551 | if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } |
5552 | ||
5553 | // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" | |
5554 | // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up | |
5555 | if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } | |
8e92c31c | 5556 | |
6528fe3e | 5557 | mDNS_Lock(m); |
7f0064bd A |
5558 | m->PktNum++; |
5559 | #ifndef UNICAST_DISABLED | |
5560 | if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR)) | |
5561 | uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); | |
5562 | // Note: mDNSCore also needs to get access to received unicast responses | |
5563 | #endif | |
c9b9ae52 | 5564 | if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); |
7f0064bd A |
5565 | else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); |
5566 | else if (QR_OP != UpdateR) | |
5567 | LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)", | |
5568 | msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID); | |
6528fe3e A |
5569 | |
5570 | // Packet reception often causes a change to the task list: | |
5571 | // 1. Inbound queries can cause us to need to send responses | |
5572 | // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses | |
5573 | // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records | |
5574 | // 4. Response packets that answer questions may cause our client to issue new questions | |
5575 | mDNS_Unlock(m); | |
5576 | } | |
5577 | ||
5578 | // *************************************************************************** | |
c9b9ae52 | 5579 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
5580 | #pragma mark - |
5581 | #pragma mark - | |
5582 | #pragma mark - Searcher Functions | |
5583 | #endif | |
5584 | ||
8e92c31c A |
5585 | #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger) |
5586 | ||
6528fe3e A |
5587 | mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question) |
5588 | { | |
5589 | DNSQuestion *q; | |
4e28aad3 A |
5590 | // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list. |
5591 | // This prevents circular references, where two questions are each marked as a duplicate of the other. | |
5592 | // Accordingly, we break out of the loop when we get to 'question', because there's no point searching | |
5593 | // further in the list. | |
c9b9ae52 A |
5594 | for (q = m->Questions; q && q != question; q=q->next) // Scan our list of questions |
5595 | if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID, | |
8e92c31c | 5596 | SameQTarget(q, question) && // and same unicast/multicast target settings |
c9b9ae52 A |
5597 | q->qtype == question->qtype && // type, |
5598 | q->qclass == question->qclass && // class, | |
5599 | q->qnamehash == question->qnamehash && | |
5600 | SameDomainName(&q->qname, &question->qname)) // and name | |
4e28aad3 | 5601 | return(q); |
6528fe3e A |
5602 | return(mDNSNULL); |
5603 | } | |
5604 | ||
5605 | // This is called after a question is deleted, in case other identical questions were being | |
5606 | // suppressed as duplicates | |
c9b9ae52 | 5607 | mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question) |
6528fe3e A |
5608 | { |
5609 | DNSQuestion *q; | |
c9b9ae52 | 5610 | for (q = m->Questions; q; q=q->next) // Scan our list of questions |
6528fe3e A |
5611 | if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate |
5612 | { | |
7f0064bd | 5613 | q->ThisQInterval = question->ThisQInterval; |
283ee3ff | 5614 | q->RequestUnicast = question->RequestUnicast; |
7f0064bd A |
5615 | q->LastQTime = question->LastQTime; |
5616 | q->RecentAnswerPkts = 0; | |
5617 | q->DuplicateOf = FindDuplicateQuestion(m, q); | |
5618 | q->LastQTxTime = question->LastQTxTime; | |
c9b9ae52 | 5619 | SetNextQueryTime(m,q); |
6528fe3e A |
5620 | } |
5621 | } | |
5622 | ||
8e92c31c A |
5623 | #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ |
5624 | ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger)) | |
5625 | ||
c9b9ae52 | 5626 | mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) |
6528fe3e | 5627 | { |
8e92c31c A |
5628 | if (question->Target.type && !ValidQuestionTarget(question)) |
5629 | { | |
7f0064bd | 5630 | LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", |
8e92c31c A |
5631 | question->Target.type, mDNSVal16(question->TargetPort)); |
5632 | question->Target.type = mDNSAddrType_None; | |
5633 | } | |
5634 | ||
7f0064bd A |
5635 | if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID |
5636 | { | |
5637 | question->TargetPort = zeroIPPort; | |
5638 | question->TargetQID = zeroID; | |
5639 | } | |
5640 | ||
5641 | #ifndef UNICAST_DISABLED | |
5642 | // If the client has specified 'kDNSServiceFlagsForceMulticast' | |
8e92c31c | 5643 | // then we do a multicast query on that interface, even for unicast domains. |
7f0064bd | 5644 | if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) |
8e92c31c A |
5645 | question->uDNS_info.id = zeroID; |
5646 | else return uDNS_StartQuery(m, question); | |
7f0064bd A |
5647 | #else |
5648 | question->uDNS_info.id = zeroID; | |
5649 | #endif // UNICAST_DISABLED | |
5650 | ||
5651 | //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); | |
5652 | ||
6528fe3e A |
5653 | if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated |
5654 | return(mStatus_NoCache); | |
5655 | else | |
5656 | { | |
c9b9ae52 A |
5657 | int i; |
5658 | // Note: It important that new questions are appended at the *end* of the list, not prepended at the start | |
5659 | DNSQuestion **q = &m->Questions; | |
8e92c31c | 5660 | if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; |
6528fe3e A |
5661 | while (*q && *q != question) q=&(*q)->next; |
5662 | ||
5663 | if (*q) | |
5664 | { | |
c9b9ae52 A |
5665 | LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list", |
5666 | question->qname.c, DNSTypeName(question->qtype)); | |
6528fe3e A |
5667 | return(mStatus_AlreadyRegistered); |
5668 | } | |
5669 | ||
c9d2d929 | 5670 | // If this question is referencing a specific interface, make sure it exists |
8e92c31c | 5671 | if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly) |
4e28aad3 | 5672 | { |
c9b9ae52 A |
5673 | NetworkInterfaceInfo *intf; |
5674 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
5675 | if (intf->InterfaceID == question->InterfaceID) break; | |
5676 | if (!intf) | |
c9d2d929 A |
5677 | { |
5678 | debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID); | |
5679 | return(mStatus_BadInterfaceErr); | |
5680 | } | |
4e28aad3 A |
5681 | } |
5682 | ||
c9b9ae52 A |
5683 | if (!ValidateDomainName(&question->qname)) |
5684 | { | |
28f7d060 | 5685 | LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); |
c9b9ae52 A |
5686 | return(mStatus_Invalid); |
5687 | } | |
5688 | ||
7f0064bd A |
5689 | // Note: In the case where we already have the answer to this question in our cache, that may be all the client |
5690 | // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would | |
5691 | // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds | |
5692 | // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. | |
c9b9ae52 A |
5693 | if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); |
5694 | ||
c9d2d929 A |
5695 | question->next = mDNSNULL; |
5696 | question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() | |
5697 | question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname)); | |
5698 | question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question | |
5699 | question->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it | |
5700 | question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization | |
5701 | question->LastAnswerPktNum = m->PktNum; | |
5702 | question->RecentAnswerPkts = 0; | |
5703 | question->CurrentAnswers = 0; | |
5704 | question->LargeAnswers = 0; | |
5705 | question->UniqueAnswers = 0; | |
5706 | question->DuplicateOf = FindDuplicateQuestion(m, question); | |
5707 | question->NextInDQList = mDNSNULL; | |
c9b9ae52 A |
5708 | for (i=0; i<DupSuppressInfoSize; i++) |
5709 | question->DupSuppress[i].InterfaceID = mDNSNULL; | |
5710 | // question->InterfaceID must be already set by caller | |
c9d2d929 A |
5711 | question->SendQNow = mDNSNULL; |
5712 | question->SendOnAll = mDNSfalse; | |
5713 | question->LastQTxTime = m->timenow; | |
c9b9ae52 A |
5714 | |
5715 | if (!question->DuplicateOf) | |
28f7d060 | 5716 | verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started", |
c9d2d929 | 5717 | question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question); |
c9b9ae52 | 5718 | else |
28f7d060 | 5719 | verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)", |
c9d2d929 | 5720 | question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf); |
c9b9ae52 | 5721 | |
6528fe3e | 5722 | *q = question; |
8e92c31c | 5723 | if (question->InterfaceID == mDNSInterface_LocalOnly) |
c9b9ae52 A |
5724 | { |
5725 | if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; | |
5726 | } | |
5727 | else | |
5728 | { | |
5729 | if (!m->NewQuestions) m->NewQuestions = question; | |
5730 | SetNextQueryTime(m,question); | |
5731 | } | |
6528fe3e A |
5732 | |
5733 | return(mStatus_NoError); | |
5734 | } | |
5735 | } | |
5736 | ||
c9b9ae52 | 5737 | mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) |
6528fe3e | 5738 | { |
283ee3ff A |
5739 | const mDNSu32 slot = HashSlot(&question->qname); |
5740 | CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); | |
c9b9ae52 A |
5741 | CacheRecord *rr; |
5742 | DNSQuestion **q = &m->Questions; | |
8e92c31c | 5743 | |
7f0064bd A |
5744 | if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); |
5745 | ||
8e92c31c | 5746 | if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; |
6528fe3e A |
5747 | while (*q && *q != question) q=&(*q)->next; |
5748 | if (*q) *q = (*q)->next; | |
c9b9ae52 A |
5749 | else |
5750 | { | |
5751 | if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active | |
5752 | LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", | |
5753 | question->qname.c, DNSTypeName(question->qtype)); | |
5754 | return(mStatus_BadReferenceErr); | |
5755 | } | |
6528fe3e | 5756 | |
c9b9ae52 | 5757 | // Take care to cut question from list *before* calling UpdateQuestionDuplicates |
6528fe3e | 5758 | UpdateQuestionDuplicates(m, question); |
c9b9ae52 | 5759 | // But don't trash ThisQInterval until afterwards. |
4e28aad3 | 5760 | question->ThisQInterval = -1; |
c9b9ae52 A |
5761 | |
5762 | // If there are any cache records referencing this as their active question, then see if any other | |
5763 | // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. | |
283ee3ff | 5764 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) |
c9b9ae52 A |
5765 | { |
5766 | if (rr->CRActiveQuestion == question) | |
5767 | { | |
5768 | DNSQuestion *q; | |
5769 | for (q = m->Questions; q; q=q->next) // Scan our list of questions | |
5770 | if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) | |
5771 | break; | |
c9d2d929 | 5772 | verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q); |
c9b9ae52 A |
5773 | rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null |
5774 | if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count | |
5775 | } | |
5776 | } | |
5777 | ||
5778 | // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at, | |
6528fe3e A |
5779 | // bump its pointer forward one question. |
5780 | if (m->CurrentQuestion == question) | |
5781 | { | |
c9b9ae52 A |
5782 | debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", |
5783 | question->qname.c, DNSTypeName(question->qtype)); | |
5784 | m->CurrentQuestion = question->next; | |
6528fe3e A |
5785 | } |
5786 | ||
c9b9ae52 | 5787 | if (m->NewQuestions == question) |
6528fe3e | 5788 | { |
c9b9ae52 A |
5789 | debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", |
5790 | question->qname.c, DNSTypeName(question->qtype)); | |
5791 | m->NewQuestions = question->next; | |
6528fe3e | 5792 | } |
c9b9ae52 A |
5793 | |
5794 | if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; | |
5795 | ||
4e28aad3 A |
5796 | // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions |
5797 | question->next = mDNSNULL; | |
c9b9ae52 | 5798 | return(mStatus_NoError); |
6528fe3e A |
5799 | } |
5800 | ||
5801 | mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question) | |
5802 | { | |
c9b9ae52 A |
5803 | mStatus status; |
5804 | mDNS_Lock(m); | |
5805 | status = mDNS_StartQuery_internal(m, question); | |
5806 | mDNS_Unlock(m); | |
5807 | return(status); | |
5808 | } | |
5809 | ||
5810 | mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) | |
5811 | { | |
5812 | mStatus status; | |
5813 | mDNS_Lock(m); | |
5814 | status = mDNS_StopQuery_internal(m, question); | |
5815 | mDNS_Unlock(m); | |
5816 | return(status); | |
5817 | } | |
5818 | ||
5819 | mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr) | |
5820 | { | |
5821 | mStatus status; | |
5822 | mDNS_Lock(m); | |
5823 | status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); | |
6528fe3e A |
5824 | mDNS_Unlock(m); |
5825 | return(status); | |
5826 | } | |
5827 | ||
c9b9ae52 | 5828 | mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr) |
6528fe3e | 5829 | { |
c9b9ae52 A |
5830 | mStatus status = mStatus_BadReferenceErr; |
5831 | CacheRecord *cr; | |
6528fe3e | 5832 | mDNS_Lock(m); |
c9b9ae52 A |
5833 | cr = FindIdenticalRecordInCache(m, rr); |
5834 | if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); | |
6528fe3e | 5835 | mDNS_Unlock(m); |
c9b9ae52 | 5836 | return(status); |
6528fe3e A |
5837 | } |
5838 | ||
5839 | mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, | |
5840 | const domainname *const srv, const domainname *const domain, | |
7f0064bd | 5841 | const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) |
c9b9ae52 | 5842 | { |
8e92c31c A |
5843 | question->InterfaceID = InterfaceID; |
5844 | question->Target = zeroAddr; | |
5845 | question->qtype = kDNSType_PTR; | |
5846 | question->qclass = kDNSClass_IN; | |
7f0064bd A |
5847 | question->LongLived = mDNSfalse; |
5848 | question->ExpectUnique = mDNSfalse; | |
5849 | question->ForceMCast = ForceMCast; | |
8e92c31c A |
5850 | question->QuestionCallback = Callback; |
5851 | question->QuestionContext = Context; | |
c9b9ae52 | 5852 | if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); |
8e92c31c | 5853 | |
7f0064bd A |
5854 | #ifndef UNICAST_DISABLED |
5855 | if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) | |
8e92c31c A |
5856 | { |
5857 | question->LongLived = mDNSfalse; | |
5858 | question->uDNS_info.id = zeroID; | |
5859 | return(mDNS_StartQuery(m, question)); | |
5860 | } | |
5861 | else | |
5862 | { | |
7f0064bd A |
5863 | mStatus status; |
5864 | // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not | |
5865 | mDNS_Lock(m); | |
8e92c31c | 5866 | question->LongLived = mDNStrue; |
7f0064bd A |
5867 | status = uDNS_StartQuery(m, question); |
5868 | mDNS_Unlock(m); | |
5869 | return(status); | |
8e92c31c | 5870 | } |
7f0064bd A |
5871 | #else |
5872 | return(mDNS_StartQuery(m, question)); | |
5873 | #endif // UNICAST_DISABLED | |
5874 | } | |
5875 | ||
5876 | mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) | |
5877 | { | |
5878 | NetworkInterfaceInfo *intf; | |
5879 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
5880 | if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); | |
5881 | return(mDNSfalse); | |
6528fe3e A |
5882 | } |
5883 | ||
c9b9ae52 | 5884 | mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) |
6528fe3e | 5885 | { |
c9b9ae52 A |
5886 | ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; |
5887 | mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger); | |
5888 | if (!AddRecord) return; | |
6528fe3e A |
5889 | if (answer->rrtype != kDNSType_SRV) return; |
5890 | ||
5891 | query->info->port = answer->rdata->u.srv.port; | |
5892 | ||
5893 | // If this is our first answer, then set the GotSRV flag and start the address query | |
5894 | if (!query->GotSRV) | |
5895 | { | |
5896 | query->GotSRV = mDNStrue; | |
c9b9ae52 | 5897 | query->qAv4.InterfaceID = answer->InterfaceID; |
283ee3ff | 5898 | AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); |
c9b9ae52 | 5899 | query->qAv6.InterfaceID = answer->InterfaceID; |
283ee3ff | 5900 | AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); |
7f0064bd A |
5901 | mDNS_StartQuery(m, &query->qAv4); |
5902 | // Only do the AAAA query if this machine actually has IPv6 active | |
5903 | if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); | |
6528fe3e A |
5904 | } |
5905 | // If this is not our first answer, only re-issue the address query if the target host name has changed | |
c9b9ae52 A |
5906 | else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || |
5907 | !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) | |
5908 | { | |
7f0064bd A |
5909 | mDNS_StopQuery(m, &query->qAv4); |
5910 | if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); | |
c9b9ae52 A |
5911 | if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) |
5912 | { | |
5913 | // If we get here, it means: | |
5914 | // 1. This is not our first SRV answer | |
5915 | // 2. The interface ID is different, but the target host and port are the same | |
5916 | // This implies that we're seeing the exact same SRV record on more than one interface, so we should | |
5917 | // make our address queries at least as broad as the original SRV query so that we catch all the answers. | |
5918 | query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface | |
5919 | query->qAv6.InterfaceID = query->qSRV.InterfaceID; | |
5920 | } | |
5921 | else | |
5922 | { | |
5923 | query->qAv4.InterfaceID = answer->InterfaceID; | |
283ee3ff | 5924 | AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); |
c9b9ae52 | 5925 | query->qAv6.InterfaceID = answer->InterfaceID; |
283ee3ff | 5926 | AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); |
c9b9ae52 A |
5927 | } |
5928 | debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c); | |
7f0064bd A |
5929 | mDNS_StartQuery(m, &query->qAv4); |
5930 | // Only do the AAAA query if this machine actually has IPv6 active | |
5931 | if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); | |
c9b9ae52 A |
5932 | } |
5933 | else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) | |
6528fe3e | 5934 | { |
c9b9ae52 A |
5935 | if (++query->Answers >= 100) |
5936 | debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", | |
5937 | query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, | |
8e92c31c | 5938 | mDNSVal16(answer->rdata->u.srv.port)); |
c9b9ae52 | 5939 | query->ServiceInfoQueryCallback(m, query); |
6528fe3e | 5940 | } |
c9b9ae52 A |
5941 | // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's |
5942 | // callback function is allowed to do anything, including deleting this query and freeing its memory. | |
6528fe3e A |
5943 | } |
5944 | ||
c9b9ae52 | 5945 | mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) |
6528fe3e | 5946 | { |
c9b9ae52 A |
5947 | ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; |
5948 | if (!AddRecord) return; | |
6528fe3e | 5949 | if (answer->rrtype != kDNSType_TXT) return; |
c9b9ae52 | 5950 | if (answer->rdlength > sizeof(query->info->TXTinfo)) return; |
6528fe3e | 5951 | |
c9b9ae52 A |
5952 | query->GotTXT = mDNStrue; |
5953 | query->info->TXTlen = answer->rdlength; | |
8e92c31c | 5954 | query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero |
c9b9ae52 | 5955 | mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength); |
6528fe3e | 5956 | |
c9b9ae52 | 5957 | verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); |
6528fe3e | 5958 | |
c9b9ae52 A |
5959 | // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's |
5960 | // callback function is allowed to do anything, including deleting this query and freeing its memory. | |
5961 | if (query->ServiceInfoQueryCallback && query->GotADD) | |
5962 | { | |
5963 | if (++query->Answers >= 100) | |
5964 | debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", | |
5965 | query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); | |
5966 | query->ServiceInfoQueryCallback(m, query); | |
5967 | } | |
6528fe3e A |
5968 | } |
5969 | ||
c9b9ae52 | 5970 | mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) |
6528fe3e | 5971 | { |
c9b9ae52 | 5972 | ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; |
7f0064bd | 5973 | //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); |
c9b9ae52 A |
5974 | if (!AddRecord) return; |
5975 | ||
5976 | if (answer->rrtype == kDNSType_A) | |
5977 | { | |
5978 | query->info->ip.type = mDNSAddrType_IPv4; | |
7f0064bd | 5979 | query->info->ip.ip.v4 = answer->rdata->u.ipv4; |
c9b9ae52 A |
5980 | } |
5981 | else if (answer->rrtype == kDNSType_AAAA) | |
5982 | { | |
5983 | query->info->ip.type = mDNSAddrType_IPv6; | |
5984 | query->info->ip.ip.v6 = answer->rdata->u.ipv6; | |
5985 | } | |
5986 | else | |
5987 | { | |
283ee3ff | 5988 | debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); |
c9b9ae52 A |
5989 | return; |
5990 | } | |
5991 | ||
6528fe3e | 5992 | query->GotADD = mDNStrue; |
c9b9ae52 | 5993 | query->info->InterfaceID = answer->InterfaceID; |
6528fe3e | 5994 | |
7f0064bd | 5995 | verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); |
6528fe3e | 5996 | |
c9b9ae52 A |
5997 | // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's |
5998 | // callback function is allowed to do anything, including deleting this query and freeing its memory. | |
5999 | if (query->ServiceInfoQueryCallback && query->GotTXT) | |
6528fe3e | 6000 | { |
c9b9ae52 | 6001 | if (++query->Answers >= 100) |
c9d2d929 A |
6002 | { |
6003 | if (answer->rrtype == kDNSType_A) | |
6004 | debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv4); | |
6005 | else | |
6006 | debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6); | |
6007 | } | |
c9b9ae52 | 6008 | query->ServiceInfoQueryCallback(m, query); |
6528fe3e | 6009 | } |
6528fe3e A |
6010 | } |
6011 | ||
c9b9ae52 A |
6012 | // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure |
6013 | // If the query is not interface-specific, then InterfaceID may be zero | |
6528fe3e | 6014 | // Each time the Callback is invoked, the remainder of the fields will have been filled in |
c9b9ae52 | 6015 | // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response |
6528fe3e | 6016 | mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, |
c9b9ae52 | 6017 | ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) |
6528fe3e A |
6018 | { |
6019 | mStatus status; | |
c9b9ae52 A |
6020 | mDNS_Lock(m); |
6021 | ||
8e92c31c | 6022 | query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6023 | query->qSRV.InterfaceID = info->InterfaceID; |
8e92c31c | 6024 | query->qSRV.Target = zeroAddr; |
283ee3ff | 6025 | AssignDomainName(&query->qSRV.qname, &info->name); |
c9b9ae52 A |
6026 | query->qSRV.qtype = kDNSType_SRV; |
6027 | query->qSRV.qclass = kDNSClass_IN; | |
7f0064bd A |
6028 | query->qSRV.LongLived = mDNSfalse; |
6029 | query->qSRV.ExpectUnique = mDNStrue; | |
6030 | query->qSRV.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6031 | query->qSRV.QuestionCallback = FoundServiceInfoSRV; |
6032 | query->qSRV.QuestionContext = query; | |
6033 | ||
8e92c31c | 6034 | query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6035 | query->qTXT.InterfaceID = info->InterfaceID; |
8e92c31c | 6036 | query->qTXT.Target = zeroAddr; |
283ee3ff | 6037 | AssignDomainName(&query->qTXT.qname, &info->name); |
c9b9ae52 A |
6038 | query->qTXT.qtype = kDNSType_TXT; |
6039 | query->qTXT.qclass = kDNSClass_IN; | |
7f0064bd A |
6040 | query->qTXT.LongLived = mDNSfalse; |
6041 | query->qTXT.ExpectUnique = mDNStrue; | |
6042 | query->qTXT.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6043 | query->qTXT.QuestionCallback = FoundServiceInfoTXT; |
6044 | query->qTXT.QuestionContext = query; | |
6045 | ||
8e92c31c | 6046 | query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6047 | query->qAv4.InterfaceID = info->InterfaceID; |
8e92c31c | 6048 | query->qAv4.Target = zeroAddr; |
c9b9ae52 A |
6049 | query->qAv4.qname.c[0] = 0; |
6050 | query->qAv4.qtype = kDNSType_A; | |
6051 | query->qAv4.qclass = kDNSClass_IN; | |
7f0064bd A |
6052 | query->qAv4.LongLived = mDNSfalse; |
6053 | query->qAv4.ExpectUnique = mDNStrue; | |
6054 | query->qAv4.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6055 | query->qAv4.QuestionCallback = FoundServiceInfo; |
6056 | query->qAv4.QuestionContext = query; | |
6057 | ||
8e92c31c | 6058 | query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question |
c9b9ae52 | 6059 | query->qAv6.InterfaceID = info->InterfaceID; |
8e92c31c | 6060 | query->qAv6.Target = zeroAddr; |
c9b9ae52 A |
6061 | query->qAv6.qname.c[0] = 0; |
6062 | query->qAv6.qtype = kDNSType_AAAA; | |
6063 | query->qAv6.qclass = kDNSClass_IN; | |
7f0064bd A |
6064 | query->qAv6.LongLived = mDNSfalse; |
6065 | query->qAv6.ExpectUnique = mDNStrue; | |
6066 | query->qAv6.ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6067 | query->qAv6.QuestionCallback = FoundServiceInfo; |
6068 | query->qAv6.QuestionContext = query; | |
6069 | ||
6070 | query->GotSRV = mDNSfalse; | |
6071 | query->GotTXT = mDNSfalse; | |
6072 | query->GotADD = mDNSfalse; | |
6073 | query->Answers = 0; | |
6074 | ||
6075 | query->info = info; | |
6076 | query->ServiceInfoQueryCallback = Callback; | |
6077 | query->ServiceInfoQueryContext = Context; | |
6528fe3e A |
6078 | |
6079 | // info->name = Must already be set up by client | |
6080 | // info->interface = Must already be set up by client | |
c9b9ae52 | 6081 | info->ip = zeroAddr; |
6528fe3e A |
6082 | info->port = zeroIPPort; |
6083 | info->TXTlen = 0; | |
6084 | ||
7f0064bd | 6085 | // We use mDNS_StartQuery_internal here because we're already holding the lock |
c9b9ae52 A |
6086 | status = mDNS_StartQuery_internal(m, &query->qSRV); |
6087 | if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); | |
6528fe3e A |
6088 | if (status != mStatus_NoError) mDNS_StopResolveService(m, query); |
6089 | ||
6090 | mDNS_Unlock(m); | |
6091 | return(status); | |
6092 | } | |
6093 | ||
283ee3ff | 6094 | mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q) |
6528fe3e A |
6095 | { |
6096 | mDNS_Lock(m); | |
7f0064bd | 6097 | // We use mDNS_StopQuery_internal here because we're already holding the lock |
283ee3ff A |
6098 | if (q->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qSRV); |
6099 | if (q->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qTXT); | |
6100 | if (q->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv4); | |
6101 | if (q->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv6); | |
6528fe3e A |
6102 | mDNS_Unlock(m); |
6103 | } | |
6104 | ||
8e92c31c | 6105 | mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, |
c9b9ae52 | 6106 | const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) |
6528fe3e | 6107 | { |
c9b9ae52 | 6108 | question->InterfaceID = InterfaceID; |
8e92c31c | 6109 | question->Target = zeroAddr; |
c9b9ae52 A |
6110 | question->qtype = kDNSType_PTR; |
6111 | question->qclass = kDNSClass_IN; | |
7f0064bd A |
6112 | question->LongLived = mDNSfalse; |
6113 | question->ExpectUnique = mDNSfalse; | |
6114 | question->ForceMCast = mDNSfalse; | |
c9b9ae52 A |
6115 | question->QuestionCallback = Callback; |
6116 | question->QuestionContext = Context; | |
283ee3ff | 6117 | if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); |
8e92c31c | 6118 | if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); |
7f0064bd | 6119 | if (!dom) dom = &localdomain; |
8e92c31c A |
6120 | if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); |
6121 | return(mDNS_StartQuery(m, question)); | |
6528fe3e A |
6122 | } |
6123 | ||
6124 | // *************************************************************************** | |
c9b9ae52 | 6125 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
6126 | #pragma mark - |
6127 | #pragma mark - Responder Functions | |
6128 | #endif | |
6129 | ||
c9b9ae52 | 6130 | mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) |
6528fe3e | 6131 | { |
c9b9ae52 A |
6132 | mStatus status; |
6133 | mDNS_Lock(m); | |
6134 | status = mDNS_Register_internal(m, rr); | |
6528fe3e A |
6135 | mDNS_Unlock(m); |
6136 | return(status); | |
6137 | } | |
6138 | ||
c9b9ae52 | 6139 | mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, |
7f0064bd | 6140 | const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) |
6528fe3e | 6141 | { |
7f0064bd | 6142 | #ifndef UNICAST_DISABLED |
283ee3ff | 6143 | mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name)); |
7f0064bd A |
6144 | #else |
6145 | mDNSBool unicast = mDNSfalse; | |
6146 | #endif | |
8e92c31c | 6147 | |
c9b9ae52 | 6148 | if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) |
c9d2d929 | 6149 | { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); } |
7f0064bd | 6150 | |
73792575 A |
6151 | mDNS_Lock(m); |
6152 | ||
c9b9ae52 A |
6153 | // If TTL is unspecified, leave TTL unchanged |
6154 | if (newttl == 0) newttl = rr->resrec.rroriginalttl; | |
6528fe3e A |
6155 | |
6156 | // If we already have an update queued up which has not gone through yet, | |
6157 | // give the client a chance to free that memory | |
7f0064bd | 6158 | if (!unicast && rr->NewRData) |
6528fe3e A |
6159 | { |
6160 | RData *n = rr->NewRData; | |
c9b9ae52 A |
6161 | rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... |
6162 | if (rr->UpdateCallback) | |
6163 | rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary | |
6164 | } | |
8e92c31c | 6165 | |
c9b9ae52 A |
6166 | rr->NewRData = newrdata; |
6167 | rr->newrdlength = newrdlength; | |
6168 | rr->UpdateCallback = Callback; | |
7f0064bd A |
6169 | |
6170 | if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); } | |
6171 | ||
c9d2d929 | 6172 | if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) |
8e92c31c A |
6173 | CompleteRDataUpdate(m, rr); |
6174 | else | |
c9b9ae52 | 6175 | { |
8e92c31c A |
6176 | domainlabel name; |
6177 | domainname type, domain; | |
283ee3ff | 6178 | DeconstructServiceName(rr->resrec.name, &name, &type, &domain); |
7f0064bd | 6179 | rr->AnnounceCount = InitialAnnounceCount; |
8e92c31c A |
6180 | // iChat often does suprious record updates where no data has changed. For the _presence service type, using |
6181 | // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful | |
6182 | // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data | |
6183 | // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us. | |
6184 | // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement. | |
6185 | if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1; | |
6186 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); | |
6187 | InitializeLastAPTime(m, rr); | |
6188 | while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); | |
6189 | if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; | |
7f0064bd | 6190 | if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); |
8e92c31c A |
6191 | if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); |
6192 | if (rr->UpdateCredits <= 5) | |
6193 | { | |
283ee3ff A |
6194 | mDNSu32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum |
6195 | if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond); | |
8e92c31c | 6196 | rr->ThisAPInterval *= 4; |
7f0064bd | 6197 | rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval; |
c9d2d929 | 6198 | LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name->c, delay, delay > 1 ? "s" : ""); |
8e92c31c A |
6199 | } |
6200 | rr->resrec.rroriginalttl = newttl; | |
c9b9ae52 | 6201 | } |
8e92c31c | 6202 | |
6528fe3e A |
6203 | mDNS_Unlock(m); |
6204 | return(mStatus_NoError); | |
6205 | } | |
6206 | ||
6207 | // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change | |
6208 | // the record list and/or question list. | |
6209 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 6210 | mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) |
6528fe3e | 6211 | { |
c9b9ae52 A |
6212 | mStatus status; |
6213 | mDNS_Lock(m); | |
6214 | status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); | |
6528fe3e | 6215 | mDNS_Unlock(m); |
c9b9ae52 A |
6216 | return(status); |
6217 | } | |
6218 | ||
8e92c31c | 6219 | mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); |
c9b9ae52 A |
6220 | |
6221 | mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) | |
6222 | { | |
6223 | NetworkInterfaceInfo *intf; | |
6224 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6225 | if (intf->Advertise) break; | |
6226 | return(intf); | |
6227 | } | |
6228 | ||
8e92c31c | 6229 | mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) |
c9b9ae52 A |
6230 | { |
6231 | char buffer[256]; | |
6232 | NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); | |
6233 | if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary | |
8e92c31c A |
6234 | |
6235 | // Send dynamic update for non-linklocal IPv4 Addresses | |
7f0064bd A |
6236 | mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set); |
6237 | mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); | |
6238 | mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); | |
6239 | ||
6240 | #if ANSWER_REMOTE_HOSTNAME_QUERIES | |
6241 | set->RR_A .AllowRemoteQuery = mDNStrue; | |
6242 | set->RR_PTR .AllowRemoteQuery = mDNStrue; | |
6243 | set->RR_HINFO.AllowRemoteQuery = mDNStrue; | |
6244 | #endif | |
c9b9ae52 A |
6245 | // 1. Set up Address record to map from host name ("foo.local.") to IP address |
6246 | // 2. Set up reverse-lookup PTR record to map from our address back to our host name | |
283ee3ff | 6247 | AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname); |
c9b9ae52 A |
6248 | if (set->ip.type == mDNSAddrType_IPv4) |
6249 | { | |
6250 | set->RR_A.resrec.rrtype = kDNSType_A; | |
7f0064bd | 6251 | set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; |
c9b9ae52 A |
6252 | // Note: This is reverse order compared to a normal dotted-decimal IP address |
6253 | mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", | |
6254 | set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); | |
6255 | } | |
6256 | else if (set->ip.type == mDNSAddrType_IPv6) | |
6257 | { | |
6258 | int i; | |
6259 | set->RR_A.resrec.rrtype = kDNSType_AAAA; | |
6260 | set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; | |
6261 | for (i = 0; i < 16; i++) | |
6262 | { | |
6263 | static const char hexValues[] = "0123456789ABCDEF"; | |
6264 | buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; | |
6265 | buffer[i * 4 + 1] = '.'; | |
6266 | buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; | |
6267 | buffer[i * 4 + 3] = '.'; | |
6268 | } | |
6269 | mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); | |
6270 | } | |
6271 | ||
283ee3ff | 6272 | MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer); |
c9b9ae52 | 6273 | set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name |
7f0064bd | 6274 | 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 |
6275 | |
6276 | set->RR_A.RRSet = &primary->RR_A; // May refer to self | |
6277 | ||
6278 | mDNS_Register_internal(m, &set->RR_A); | |
6279 | mDNS_Register_internal(m, &set->RR_PTR); | |
6280 | ||
6281 | if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) | |
6282 | { | |
6283 | mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; | |
283ee3ff | 6284 | AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname); |
c9b9ae52 A |
6285 | set->RR_HINFO.DependentOn = &set->RR_A; |
6286 | mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]); | |
6287 | p += 1 + (int)p[0]; | |
6288 | mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]); | |
6289 | mDNS_Register_internal(m, &set->RR_HINFO); | |
6290 | } | |
6291 | else | |
6292 | { | |
6293 | debugf("Not creating HINFO record: platform support layer provided no information"); | |
6294 | set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; | |
6295 | } | |
6296 | } | |
6297 | ||
8e92c31c | 6298 | mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) |
c9b9ae52 A |
6299 | { |
6300 | NetworkInterfaceInfo *intf; | |
8e92c31c A |
6301 | |
6302 | // If we still have address records referring to this one, update them | |
c9b9ae52 A |
6303 | NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); |
6304 | AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; | |
6305 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6306 | if (intf->RR_A.RRSet == &set->RR_A) | |
6307 | intf->RR_A.RRSet = A; | |
6308 | ||
6309 | // Unregister these records. | |
8e92c31c | 6310 | // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform |
c9b9ae52 A |
6311 | // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. |
6312 | // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. | |
6313 | // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). | |
6314 | if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); | |
6315 | if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); | |
6316 | if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); | |
6528fe3e A |
6317 | } |
6318 | ||
7f0064bd | 6319 | mDNSexport void mDNS_SetFQDN(mDNS *const m) |
6528fe3e | 6320 | { |
7f0064bd A |
6321 | domainname newmname; |
6322 | NetworkInterfaceInfo *intf; | |
6323 | AuthRecord *rr; | |
6324 | newmname.c[0] = 0; | |
c9b9ae52 | 6325 | |
7f0064bd A |
6326 | if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } |
6327 | if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } | |
6328 | if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; } | |
c9b9ae52 | 6329 | |
7f0064bd | 6330 | mDNS_Lock(m); |
283ee3ff | 6331 | AssignDomainName(&m->MulticastHostname, &newmname); |
c9b9ae52 | 6332 | |
7f0064bd A |
6333 | // 1. Stop advertising our address records on all interfaces |
6334 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6335 | if (intf->Advertise) DeadvertiseInterface(m, intf); | |
6528fe3e | 6336 | |
7f0064bd A |
6337 | // 2. Start advertising our address records using the new name |
6338 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6339 | if (intf->Advertise) AdvertiseInterface(m, intf); | |
6528fe3e | 6340 | |
7f0064bd A |
6341 | // 3. Make sure that any SRV records (and the like) that reference our |
6342 | // host name in their rdata get updated to reference this new host name | |
6343 | for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); | |
6344 | for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); | |
6345 | ||
6346 | mDNS_Unlock(m); | |
8e92c31c | 6347 | } |
c9b9ae52 | 6348 | |
8e92c31c A |
6349 | mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
6350 | { | |
6351 | (void)rr; // Unused parameter | |
6352 | ||
c9b9ae52 | 6353 | #if MDNS_DEBUGMSGS |
6528fe3e | 6354 | { |
c9b9ae52 A |
6355 | char *msg = "Unknown result"; |
6356 | if (result == mStatus_NoError) msg = "Name registered"; | |
6357 | else if (result == mStatus_NameConflict) msg = "Name conflict"; | |
283ee3ff | 6358 | debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); |
6528fe3e | 6359 | } |
c9b9ae52 | 6360 | #endif |
6528fe3e | 6361 | |
c9b9ae52 A |
6362 | if (result == mStatus_NoError) |
6363 | { | |
6364 | // Notify the client that the host name is successfully registered | |
6365 | if (m->MainCallback) | |
8e92c31c A |
6366 | { |
6367 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 6368 | m->MainCallback(m, result); |
8e92c31c A |
6369 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
6370 | } | |
c9b9ae52 A |
6371 | } |
6372 | else if (result == mStatus_NameConflict) | |
6528fe3e | 6373 | { |
6528fe3e | 6374 | domainlabel oldlabel = m->hostlabel; |
7f0064bd | 6375 | |
c9b9ae52 A |
6376 | // 1. First give the client callback a chance to pick a new name |
6377 | if (m->MainCallback) | |
8e92c31c A |
6378 | { |
6379 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 6380 | m->MainCallback(m, mStatus_NameConflict); |
8e92c31c A |
6381 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
6382 | } | |
c9b9ae52 A |
6383 | |
6384 | // 2. If the client callback didn't do it, add (or increment) an index ourselves | |
6528fe3e A |
6385 | if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) |
6386 | IncrementLabelSuffix(&m->hostlabel, mDNSfalse); | |
8e92c31c | 6387 | |
c9b9ae52 A |
6388 | // 3. Generate the FQDNs from the hostlabel, |
6389 | // and make sure all SRV records, etc., are updated to reference our new hostname | |
7f0064bd A |
6390 | mDNS_SetFQDN(m); |
6391 | LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); | |
6392 | } | |
6393 | else if (result == mStatus_MemFree) | |
6394 | { | |
6395 | // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by | |
6396 | // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface | |
6397 | debugf("mDNS_HostNameCallback: MemFree (ignored)"); | |
6528fe3e | 6398 | } |
7f0064bd | 6399 | else |
283ee3ff | 6400 | LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c); |
6528fe3e A |
6401 | } |
6402 | ||
c9b9ae52 | 6403 | mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) |
6528fe3e | 6404 | { |
c9b9ae52 A |
6405 | NetworkInterfaceInfo *intf; |
6406 | active->IPv4Available = mDNSfalse; | |
6407 | active->IPv6Available = mDNSfalse; | |
6408 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6409 | if (intf->InterfaceID == active->InterfaceID) | |
6410 | { | |
8e92c31c A |
6411 | if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; |
6412 | if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; | |
c9b9ae52 | 6413 | } |
6528fe3e A |
6414 | } |
6415 | ||
c9d2d929 | 6416 | mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSs32 delay) |
6528fe3e | 6417 | { |
c9b9ae52 | 6418 | mDNSBool FirstOfType = mDNStrue; |
6528fe3e | 6419 | NetworkInterfaceInfo **p = &m->HostInterfaces; |
7f0064bd A |
6420 | |
6421 | if (!set->InterfaceID) | |
6422 | { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } | |
6423 | ||
6424 | if (!mDNSAddressIsValidNonZero(&set->mask)) | |
6425 | { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } | |
6426 | ||
c9b9ae52 | 6427 | mDNS_Lock(m); |
6528fe3e | 6428 | |
c9d2d929 | 6429 | // Assume this interface will be active |
c9b9ae52 | 6430 | set->InterfaceActive = mDNStrue; |
8e92c31c A |
6431 | set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); |
6432 | set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); | |
6528fe3e | 6433 | |
c9b9ae52 | 6434 | while (*p) |
6528fe3e | 6435 | { |
c9b9ae52 A |
6436 | if (*p == set) |
6437 | { | |
6438 | LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list"); | |
6439 | mDNS_Unlock(m); | |
6440 | return(mStatus_AlreadyRegistered); | |
6441 | } | |
6528fe3e | 6442 | |
c9d2d929 | 6443 | // This InterfaceID is already in the list, so mark this interface inactive for now |
c9b9ae52 | 6444 | if ((*p)->InterfaceID == set->InterfaceID) |
4e28aad3 | 6445 | { |
c9b9ae52 A |
6446 | set->InterfaceActive = mDNSfalse; |
6447 | if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; | |
8e92c31c A |
6448 | if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; |
6449 | if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; | |
4e28aad3 | 6450 | } |
c9b9ae52 A |
6451 | |
6452 | p=&(*p)->next; | |
6453 | } | |
4e28aad3 | 6454 | |
6528fe3e A |
6455 | set->next = mDNSNULL; |
6456 | *p = set; | |
c9b9ae52 | 6457 | |
283ee3ff A |
6458 | if (set->Advertise) |
6459 | AdvertiseInterface(m, set); | |
6460 | ||
c9d2d929 | 6461 | debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip, |
c9b9ae52 A |
6462 | set->InterfaceActive ? |
6463 | "not represented in list; marking active and retriggering queries" : | |
6464 | "already represented in list; marking inactive for now"); | |
8e92c31c | 6465 | |
c9d2d929 | 6466 | // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, |
c9b9ae52 A |
6467 | // giving the false impression that there's an active representative of this interface when there really isn't. |
6468 | // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, | |
6469 | // even if we believe that we previously had an active representative of this interface. | |
283ee3ff | 6470 | if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive)) |
6528fe3e | 6471 | { |
716635cc A |
6472 | DNSQuestion *q; |
6473 | AuthRecord *rr; | |
c9d2d929 | 6474 | mDNSs32 initial = InitialQuestionInterval; |
283ee3ff | 6475 | |
c9b9ae52 | 6476 | // Use a small amount of randomness: |
c9d2d929 A |
6477 | // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at |
6478 | // 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 | 6479 | if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); |
283ee3ff | 6480 | |
c9d2d929 | 6481 | if (delay) |
283ee3ff | 6482 | { |
c9d2d929 A |
6483 | LogMsg("Repeated transitions for interface %s (%#a); delaying packets by %d seconds", |
6484 | set->ifname, &set->ip, delay/mDNSPlatformOneSecond); | |
6485 | initial = InitialQuestionInterval * 8; // Delay between first and second queries is eight seconds | |
283ee3ff A |
6486 | if (!m->SuppressProbes || |
6487 | m->SuppressProbes - (m->timenow + delay) < 0) | |
6488 | m->SuppressProbes = (m->timenow + delay); | |
6489 | } | |
c9b9ae52 A |
6490 | for (q = m->Questions; q; q=q->next) // Scan our list of questions |
6491 | if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, | |
6492 | { // then reactivate this question | |
c9d2d929 A |
6493 | q->ThisQInterval = initial; // MUST be > zero for an active question |
6494 | q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it | |
6495 | q->LastQTime = m->timenow - q->ThisQInterval + delay; | |
7f0064bd | 6496 | q->RecentAnswerPkts = 0; |
283ee3ff | 6497 | SetNextQueryTime(m,q); |
c9b9ae52 A |
6498 | } |
6499 | ||
6500 | // For all our non-specific authoritative resource records (and any dormant records specific to this interface) | |
6501 | // we now need them to re-probe if necessary, and then re-announce. | |
6502 | for (rr = m->ResourceRecords; rr; rr=rr->next) | |
6503 | if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) | |
6504 | { | |
6505 | if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; | |
7f0064bd | 6506 | rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); |
c9d2d929 | 6507 | rr->AnnounceCount = delay ? (mDNSu8)1 : InitialAnnounceCount; |
7f0064bd | 6508 | rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); |
c9b9ae52 A |
6509 | InitializeLastAPTime(m, rr); |
6510 | } | |
6528fe3e A |
6511 | } |
6512 | ||
c9b9ae52 A |
6513 | mDNS_Unlock(m); |
6514 | return(mStatus_NoError); | |
6528fe3e A |
6515 | } |
6516 | ||
6517 | // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change | |
6518 | // the record list and/or question list. | |
6519 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9d2d929 | 6520 | mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set) |
6528fe3e A |
6521 | { |
6522 | NetworkInterfaceInfo **p = &m->HostInterfaces; | |
c9b9ae52 A |
6523 | |
6524 | mDNSBool revalidate = mDNSfalse; | |
6525 | // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every | |
6526 | // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it | |
6527 | // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion. | |
6528 | if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue; | |
6529 | ||
6530 | mDNS_Lock(m); | |
6528fe3e A |
6531 | |
6532 | // Find this record in our list | |
6533 | while (*p && *p != set) p=&(*p)->next; | |
c9b9ae52 | 6534 | if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } |
6528fe3e A |
6535 | |
6536 | // Unlink this record from our list | |
6537 | *p = (*p)->next; | |
6538 | set->next = mDNSNULL; | |
6539 | ||
c9b9ae52 A |
6540 | if (!set->InterfaceActive) |
6541 | { | |
6542 | // If this interface not the active member of its set, update the v4/v6Available flags for the active member | |
6543 | NetworkInterfaceInfo *intf; | |
6544 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6545 | if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) | |
6546 | UpdateInterfaceProtocols(m, intf); | |
6547 | } | |
6548 | else | |
6549 | { | |
6550 | NetworkInterfaceInfo *intf; | |
6551 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6552 | if (intf->InterfaceID == set->InterfaceID) | |
6553 | break; | |
6554 | if (intf) | |
6555 | { | |
c9d2d929 A |
6556 | debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active", |
6557 | set->InterfaceID); | |
c9b9ae52 A |
6558 | intf->InterfaceActive = mDNStrue; |
6559 | UpdateInterfaceProtocols(m, intf); | |
6560 | ||
6561 | // See if another representative *of the same type* exists. If not, we mave have gone from | |
6562 | // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. | |
6563 | for (intf = m->HostInterfaces; intf; intf = intf->next) | |
6564 | if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) | |
6565 | break; | |
6566 | if (!intf) revalidate = mDNStrue; | |
6567 | } | |
6568 | else | |
6569 | { | |
283ee3ff A |
6570 | mDNSu32 slot; |
6571 | CacheGroup *cg; | |
c9b9ae52 A |
6572 | CacheRecord *rr; |
6573 | DNSQuestion *q; | |
c9d2d929 A |
6574 | debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant", |
6575 | set->InterfaceID); | |
619ee211 | 6576 | |
c9d2d929 | 6577 | // 1. Deactivate any questions specific to this interface |
c9b9ae52 | 6578 | for (q = m->Questions; q; q=q->next) |
c9d2d929 A |
6579 | if (q->InterfaceID == set->InterfaceID) |
6580 | q->ThisQInterval = 0; | |
c9b9ae52 A |
6581 | |
6582 | // 2. Flush any cache records received on this interface | |
6583 | revalidate = mDNSfalse; // Don't revalidate if we're flushing the records | |
283ee3ff A |
6584 | FORALL_CACHERECORDS(slot, cg, rr) |
6585 | if (rr->resrec.InterfaceID == set->InterfaceID) | |
c9d2d929 | 6586 | PurgeCacheResourceRecord(m, rr); |
c9b9ae52 A |
6587 | } |
6588 | } | |
6528fe3e | 6589 | |
c9b9ae52 | 6590 | // If we were advertising on this interface, deregister those address and reverse-lookup records now |
7f0064bd A |
6591 | if (set->Advertise) DeadvertiseInterface(m, set); |
6592 | ||
c9b9ae52 A |
6593 | // If we have any cache records received on this interface that went away, then re-verify them. |
6594 | // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, | |
6595 | // giving the false impression that there's an active representative of this interface when there really isn't. | |
6596 | // Don't need to do this when shutting down, because *all* interfaces are about to go away | |
6597 | if (revalidate && !m->mDNS_shutdown) | |
6598 | { | |
6599 | mDNSu32 slot; | |
283ee3ff | 6600 | CacheGroup *cg; |
c9b9ae52 A |
6601 | CacheRecord *rr; |
6602 | m->NextCacheCheck = m->timenow; | |
283ee3ff A |
6603 | FORALL_CACHERECORDS(slot, cg, rr) |
6604 | if (rr->resrec.InterfaceID == set->InterfaceID) | |
c9d2d929 | 6605 | mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect); |
c9b9ae52 | 6606 | } |
6528fe3e A |
6607 | |
6608 | mDNS_Unlock(m); | |
6609 | } | |
6610 | ||
c9b9ae52 | 6611 | mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
6528fe3e | 6612 | { |
c9b9ae52 A |
6613 | ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; |
6614 | (void)m; // Unused parameter | |
6528fe3e | 6615 | |
c9b9ae52 A |
6616 | #if MDNS_DEBUGMSGS |
6617 | { | |
6618 | char *msg = "Unknown result"; | |
6619 | if (result == mStatus_NoError) msg = "Name Registered"; | |
6620 | else if (result == mStatus_NameConflict) msg = "Name Conflict"; | |
6621 | else if (result == mStatus_MemFree) msg = "Memory Free"; | |
283ee3ff | 6622 | debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); |
6528fe3e | 6623 | } |
c9b9ae52 | 6624 | #endif |
6528fe3e | 6625 | |
7f0064bd A |
6626 | // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing) |
6627 | if (result == mStatus_NoError && rr != &sr->RR_SRV) return; | |
6628 | ||
6528fe3e | 6629 | // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that |
c9b9ae52 A |
6630 | if (result == mStatus_NameConflict) |
6631 | { | |
7f0064bd A |
6632 | sr->Conflict = mDNStrue; // Record that this service set had a conflict |
6633 | mDNS_DeregisterService(m, sr); // Unlink the records from our list | |
c9b9ae52 A |
6634 | return; |
6635 | } | |
6528fe3e | 6636 | |
c9b9ae52 A |
6637 | if (result == mStatus_MemFree) |
6638 | { | |
283ee3ff | 6639 | // If the PTR record or any of the subtype PTR records are still in the process of deregistering, |
c9b9ae52 A |
6640 | // don't pass on the NameConflict/MemFree message until every record is finished cleaning up. |
6641 | mDNSu32 i; | |
6642 | if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; | |
6643 | for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; | |
6644 | ||
6645 | // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, | |
6646 | // then we can now report the NameConflict to the client | |
6647 | if (sr->Conflict) result = mStatus_NameConflict; | |
6648 | } | |
6528fe3e | 6649 | |
c9b9ae52 A |
6650 | // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback |
6651 | // function is allowed to do anything, including deregistering this service and freeing its memory. | |
6652 | if (sr->ServiceCallback) | |
6653 | sr->ServiceCallback(m, sr, result); | |
6528fe3e A |
6654 | } |
6655 | ||
7f0064bd A |
6656 | mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) |
6657 | { | |
6658 | ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; | |
6659 | if (sr->ServiceCallback) | |
6660 | sr->ServiceCallback(m, sr, result); | |
6661 | } | |
6662 | ||
6528fe3e A |
6663 | // Note: |
6664 | // Name is first label of domain name (any dots in the name are actual dots, not label separators) | |
8e92c31c | 6665 | // Type is service type (e.g. "_ipp._tcp.") |
6528fe3e A |
6666 | // Domain is fully qualified domain name (i.e. ending with a null label) |
6667 | // We always register a TXT, even if it is empty (so that clients are not | |
6668 | // left waiting forever looking for a nonexistent record.) | |
c9b9ae52 | 6669 | // If the host parameter is mDNSNULL or the root domain (ASCII NUL), |
7f0064bd | 6670 | // then the default host name (m->MulticastHostname) is automatically used |
6528fe3e A |
6671 | mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, |
6672 | const domainlabel *const name, const domainname *const type, const domainname *const domain, | |
6673 | const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, | |
c9b9ae52 A |
6674 | AuthRecord *SubTypes, mDNSu32 NumSubTypes, |
6675 | const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context) | |
6528fe3e | 6676 | { |
c9b9ae52 A |
6677 | mStatus err; |
6678 | mDNSu32 i; | |
6528fe3e | 6679 | |
c9b9ae52 A |
6680 | sr->ServiceCallback = Callback; |
6681 | sr->ServiceContext = Context; | |
6682 | sr->Extras = mDNSNULL; | |
6683 | sr->NumSubTypes = NumSubTypes; | |
6684 | sr->SubTypes = SubTypes; | |
6685 | sr->Conflict = mDNSfalse; | |
6528fe3e A |
6686 | if (host && host->c[0]) sr->Host = *host; |
6687 | else sr->Host.c[0] = 0; | |
6688 | ||
7f0064bd | 6689 | // If port number is zero, that means the client is really trying to do a RegisterNoSuchService |
c9d2d929 | 6690 | if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr)); |
7f0064bd | 6691 | |
c9b9ae52 | 6692 | // Initialize the AuthRecord objects to sane values |
7f0064bd A |
6693 | mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr); |
6694 | mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); | |
6695 | mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr); | |
6696 | mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr); | |
8e92c31c | 6697 | |
6528fe3e A |
6698 | // If the client is registering an oversized TXT record, |
6699 | // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it | |
c9b9ae52 A |
6700 | if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) |
6701 | sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; | |
6702 | ||
6703 | // Set up the record names | |
6704 | // For now we only create an advisory record for the main type, not for subtypes | |
6705 | // We need to gain some operational experience before we decide if there's a need to create them for subtypes too | |
283ee3ff | 6706 | if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL) |
c9b9ae52 | 6707 | return(mStatus_BadParamErr); |
283ee3ff A |
6708 | if (ConstructServiceName(sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); |
6709 | if (ConstructServiceName(sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); | |
c9b9ae52 | 6710 | AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name); |
6528fe3e | 6711 | |
c9b9ae52 | 6712 | // 1. Set up the ADV record rdata to advertise our service type |
283ee3ff | 6713 | AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); |
c9b9ae52 A |
6714 | |
6715 | // 2. Set up the PTR record rdata to point to our service name | |
6528fe3e | 6716 | // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too |
283ee3ff | 6717 | AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); |
6528fe3e A |
6718 | sr->RR_PTR.Additional1 = &sr->RR_SRV; |
6719 | sr->RR_PTR.Additional2 = &sr->RR_TXT; | |
6720 | ||
c9b9ae52 A |
6721 | // 2a. Set up any subtype PTRs to point to our service name |
6722 | // If the client is using subtypes, it is the client's responsibility to have | |
6723 | // already set the first label of the record name to the subtype being registered | |
6724 | for (i=0; i<NumSubTypes; i++) | |
6725 | { | |
7f0064bd | 6726 | domainname st; |
283ee3ff A |
6727 | AssignDomainName(&st, sr->SubTypes[i].resrec.name); |
6728 | st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) | |
7f0064bd A |
6729 | AppendDomainName(&st, type); |
6730 | mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); | |
283ee3ff A |
6731 | if (ConstructServiceName(sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); |
6732 | AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name); | |
c9b9ae52 A |
6733 | sr->SubTypes[i].Additional1 = &sr->RR_SRV; |
6734 | sr->SubTypes[i].Additional2 = &sr->RR_TXT; | |
6735 | } | |
6736 | ||
6737 | // 3. Set up the SRV record rdata. | |
6738 | sr->RR_SRV.resrec.rdata->u.srv.priority = 0; | |
6739 | sr->RR_SRV.resrec.rdata->u.srv.weight = 0; | |
6740 | sr->RR_SRV.resrec.rdata->u.srv.port = port; | |
6528fe3e A |
6741 | |
6742 | // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name | |
283ee3ff | 6743 | if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host); |
8e92c31c | 6744 | else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } |
6528fe3e | 6745 | |
c9b9ae52 | 6746 | // 4. Set up the TXT record rdata, |
6528fe3e | 6747 | // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us |
c9b9ae52 A |
6748 | if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; |
6749 | else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) | |
6528fe3e | 6750 | { |
c9b9ae52 A |
6751 | sr->RR_TXT.resrec.rdlength = txtlen; |
6752 | if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); | |
6753 | mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen); | |
6528fe3e A |
6754 | } |
6755 | sr->RR_TXT.DependentOn = &sr->RR_SRV; | |
6756 | ||
7f0064bd | 6757 | #ifndef UNICAST_DISABLED |
8e92c31c | 6758 | // If the client has specified an explicit InterfaceID, |
28f7d060 | 6759 | // then we do a multicast registration on that interface, even for unicast domains. |
283ee3ff | 6760 | if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
7f0064bd A |
6761 | { |
6762 | mStatus status; | |
6763 | mDNS_Lock(m); | |
7cb34e5c A |
6764 | // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, |
6765 | // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". | |
6766 | // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. | |
7f0064bd A |
6767 | // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck) |
6768 | if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; } | |
6769 | status = uDNS_RegisterService(m, sr); | |
6770 | mDNS_Unlock(m); | |
6771 | return(status); | |
6772 | } | |
6773 | #endif | |
c9b9ae52 A |
6774 | mDNS_Lock(m); |
6775 | err = mDNS_Register_internal(m, &sr->RR_SRV); | |
6776 | if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); | |
6528fe3e A |
6777 | // We register the RR_PTR last, because we want to be sure that in the event of a forced call to |
6778 | // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers | |
6779 | // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to | |
6780 | // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to | |
6781 | // make sure we've deregistered all our records and done any other necessary cleanup before that happens. | |
c9b9ae52 A |
6782 | if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); |
6783 | for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]); | |
6784 | if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); | |
6528fe3e | 6785 | |
8e92c31c A |
6786 | mDNS_Unlock(m); |
6787 | ||
c9b9ae52 A |
6788 | if (err) mDNS_DeregisterService(m, sr); |
6789 | return(err); | |
6528fe3e A |
6790 | } |
6791 | ||
c9b9ae52 A |
6792 | mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, |
6793 | ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) | |
6528fe3e | 6794 | { |
8e92c31c A |
6795 | ExtraResourceRecord **e; |
6796 | mStatus status; | |
6797 | ||
7f0064bd | 6798 | extra->next = mDNSNULL; |
c9d2d929 | 6799 | mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); |
7f0064bd A |
6800 | AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); |
6801 | ||
6802 | #ifndef UNICAST_DISABLED | |
283ee3ff | 6803 | if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
8e92c31c | 6804 | { |
7f0064bd | 6805 | mDNS_Lock(m); |
7cb34e5c A |
6806 | // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, |
6807 | // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". | |
6808 | // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. | |
7f0064bd A |
6809 | // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck) |
6810 | if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0) | |
6811 | { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; } | |
6812 | status = uDNS_AddRecordToService(m, sr, extra); | |
6813 | mDNS_Unlock(m); | |
6814 | return status; | |
8e92c31c | 6815 | } |
7f0064bd A |
6816 | #endif |
6817 | ||
8e92c31c A |
6818 | mDNS_Lock(m); |
6819 | e = &sr->Extras; | |
6528fe3e A |
6820 | while (*e) e = &(*e)->next; |
6821 | ||
7f0064bd | 6822 | if (ttl == 0) ttl = kStandardTTL; |
6528fe3e | 6823 | |
6528fe3e A |
6824 | extra->r.DependentOn = &sr->RR_SRV; |
6825 | ||
283ee3ff | 6826 | debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c); |
6528fe3e | 6827 | |
8e92c31c A |
6828 | status = mDNS_Register_internal(m, &extra->r); |
6829 | if (status == mStatus_NoError) *e = extra; | |
6830 | mDNS_Unlock(m); | |
6831 | return(status); | |
6528fe3e A |
6832 | } |
6833 | ||
c9d2d929 | 6834 | mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context) |
6528fe3e | 6835 | { |
8e92c31c A |
6836 | ExtraResourceRecord **e; |
6837 | mStatus status; | |
6838 | ||
8e92c31c A |
6839 | mDNS_Lock(m); |
6840 | e = &sr->Extras; | |
6528fe3e A |
6841 | while (*e && *e != extra) e = &(*e)->next; |
6842 | if (!*e) | |
6843 | { | |
283ee3ff | 6844 | debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c); |
8e92c31c | 6845 | status = mStatus_BadReferenceErr; |
6528fe3e | 6846 | } |
8e92c31c A |
6847 | else |
6848 | { | |
283ee3ff | 6849 | debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c); |
7f0064bd A |
6850 | extra->r.RecordCallback = MemFreeCallback; |
6851 | extra->r.RecordContext = Context; | |
8e92c31c | 6852 | *e = (*e)->next; |
7f0064bd | 6853 | #ifndef UNICAST_DISABLED |
283ee3ff | 6854 | if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
7f0064bd A |
6855 | status = uDNS_DeregisterRecord(m, &extra->r); |
6856 | else | |
6857 | #endif | |
8e92c31c A |
6858 | status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); |
6859 | } | |
6860 | mDNS_Unlock(m); | |
6861 | return(status); | |
6528fe3e A |
6862 | } |
6863 | ||
377735b0 | 6864 | mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) |
6528fe3e | 6865 | { |
8e92c31c A |
6866 | // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines |
6867 | // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. | |
6868 | domainlabel name1, name2; | |
6528fe3e A |
6869 | domainname type, domain; |
6870 | domainname *host = mDNSNULL; | |
6871 | ExtraResourceRecord *extras = sr->Extras; | |
6872 | mStatus err; | |
6873 | ||
283ee3ff | 6874 | DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain); |
377735b0 A |
6875 | if (!newname) |
6876 | { | |
8e92c31c A |
6877 | name2 = name1; |
6878 | IncrementLabelSuffix(&name2, mDNStrue); | |
6879 | newname = &name2; | |
377735b0 | 6880 | } |
c9d2d929 | 6881 | LogMsg("Service \"%##s\" renamed to \"%#s\"", sr->RR_SRV.resrec.name->c, newname->c); |
6528fe3e A |
6882 | if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host; |
6883 | ||
377735b0 | 6884 | err = mDNS_RegisterService(m, sr, newname, &type, &domain, |
c9b9ae52 A |
6885 | host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, |
6886 | sr->SubTypes, sr->NumSubTypes, | |
6887 | sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext); | |
6528fe3e | 6888 | |
c9b9ae52 A |
6889 | // mDNS_RegisterService() just reset sr->Extras to NULL. |
6890 | // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run | |
6891 | // through the old list of extra records, and re-add them to our freshly created service registration | |
6528fe3e A |
6892 | while (!err && extras) |
6893 | { | |
6894 | ExtraResourceRecord *e = extras; | |
6895 | extras = extras->next; | |
c9b9ae52 | 6896 | err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl); |
6528fe3e A |
6897 | } |
6898 | ||
6899 | return(err); | |
6900 | } | |
6901 | ||
6902 | // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, | |
6903 | // which may change the record list and/or question list. | |
6904 | // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. | |
c9b9ae52 | 6905 | mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) |
6528fe3e | 6906 | { |
7f0064bd A |
6907 | // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() |
6908 | if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); | |
8e92c31c | 6909 | |
7f0064bd | 6910 | #ifndef UNICAST_DISABLED |
283ee3ff | 6911 | if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) |
7f0064bd A |
6912 | { |
6913 | mStatus status; | |
6914 | mDNS_Lock(m); | |
6915 | status = uDNS_DeregisterService(m, sr); | |
6916 | mDNS_Unlock(m); | |
6917 | return(status); | |
6918 | } | |
6919 | #endif | |
c9b9ae52 A |
6920 | if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) |
6921 | { | |
283ee3ff | 6922 | debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); |
c9b9ae52 A |
6923 | return(mStatus_BadReferenceErr); |
6924 | } | |
6925 | else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) | |
6528fe3e | 6926 | { |
283ee3ff | 6927 | debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); |
c9b9ae52 | 6928 | return(mStatus_NoError); |
6528fe3e | 6929 | } |
c9b9ae52 A |
6930 | else |
6931 | { | |
6932 | mDNSu32 i; | |
6933 | mStatus status; | |
6934 | ExtraResourceRecord *e; | |
6935 | mDNS_Lock(m); | |
6936 | e = sr->Extras; | |
6937 | ||
6938 | // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the | |
6939 | // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay | |
6940 | mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); | |
6941 | mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); | |
6942 | ||
6943 | mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal); | |
6944 | ||
6945 | // We deregister all of the extra records, but we leave the sr->Extras list intact | |
6946 | // in case the client wants to do a RenameAndReregister and reinstate the registration | |
6947 | while (e) | |
6948 | { | |
6949 | mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); | |
6950 | e = e->next; | |
6951 | } | |
6528fe3e | 6952 | |
c9b9ae52 A |
6953 | for (i=0; i<sr->NumSubTypes; i++) |
6954 | mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal); | |
6528fe3e | 6955 | |
c9b9ae52 A |
6956 | // Be sure to deregister the PTR last! |
6957 | // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback, | |
6958 | // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback, | |
6959 | // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure | |
6960 | // we've deregistered all our records and done any other necessary cleanup before that happens. | |
6961 | status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal); | |
6962 | mDNS_Unlock(m); | |
6963 | return(status); | |
6964 | } | |
6965 | } | |
6966 | ||
6967 | // Create a registration that asserts that no such service exists with this name. | |
6968 | // This can be useful where there is a given function is available through several protocols. | |
6969 | // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP" | |
6970 | // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an | |
6971 | // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing | |
6972 | // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users. | |
6973 | mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, | |
6974 | const domainlabel *const name, const domainname *const type, const domainname *const domain, | |
6975 | const domainname *const host, | |
6976 | const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context) | |
6977 | { | |
7f0064bd | 6978 | mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context); |
283ee3ff | 6979 | if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); |
c9b9ae52 A |
6980 | rr->resrec.rdata->u.srv.priority = 0; |
6981 | rr->resrec.rdata->u.srv.weight = 0; | |
6982 | rr->resrec.rdata->u.srv.port = zeroIPPort; | |
283ee3ff | 6983 | if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host); |
c9b9ae52 A |
6984 | else rr->HostTarget = mDNStrue; |
6985 | return(mDNS_Register(m, rr)); | |
6528fe3e A |
6986 | } |
6987 | ||
c9b9ae52 A |
6988 | mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, |
6989 | mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) | |
6528fe3e | 6990 | { |
7f0064bd | 6991 | mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); |
283ee3ff | 6992 | if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); |
c9b9ae52 | 6993 | if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); |
6528fe3e A |
6994 | return(mDNS_Register(m, rr)); |
6995 | } | |
6996 | ||
6997 | // *************************************************************************** | |
c9b9ae52 | 6998 | #if COMPILER_LIKES_PRAGMA_MARK |
6528fe3e A |
6999 | #pragma mark - |
7000 | #pragma mark - | |
7001 | #pragma mark - Startup and Shutdown | |
7002 | #endif | |
7003 | ||
283ee3ff | 7004 | mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) |
c9b9ae52 A |
7005 | { |
7006 | if (storage && numrecords) | |
7007 | { | |
7008 | mDNSu32 i; | |
7cb34e5c | 7009 | debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity)); |
c9b9ae52 A |
7010 | for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1]; |
7011 | storage[numrecords-1].next = m->rrcache_free; | |
7012 | m->rrcache_free = storage; | |
7013 | m->rrcache_size += numrecords; | |
7014 | } | |
7015 | } | |
7016 | ||
283ee3ff | 7017 | mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) |
8e92c31c A |
7018 | { |
7019 | mDNS_Lock(m); | |
7020 | mDNS_GrowCache_internal(m, storage, numrecords); | |
7021 | mDNS_Unlock(m); | |
7022 | } | |
7023 | ||
6528fe3e | 7024 | mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, |
283ee3ff | 7025 | CacheEntity *rrcachestorage, mDNSu32 rrcachesize, |
c9b9ae52 | 7026 | mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) |
6528fe3e | 7027 | { |
716635cc | 7028 | mDNSu32 slot; |
28f7d060 | 7029 | mDNSs32 timenow; |
7f0064bd | 7030 | mStatus result; |
6528fe3e A |
7031 | |
7032 | if (!rrcachestorage) rrcachesize = 0; | |
7033 | ||
c9b9ae52 A |
7034 | m->p = p; |
7035 | m->KnownBugs = 0; | |
c9d2d929 | 7036 | m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise |
c9b9ae52 A |
7037 | m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; |
7038 | m->mDNSPlatformStatus = mStatus_Waiting; | |
7f0064bd A |
7039 | m->UnicastPort4 = zeroIPPort; |
7040 | m->UnicastPort6 = zeroIPPort; | |
c9b9ae52 A |
7041 | m->MainCallback = Callback; |
7042 | m->MainContext = Context; | |
7f0064bd | 7043 | m->rec.r.resrec.RecordType = 0; |
c9b9ae52 A |
7044 | |
7045 | // For debugging: To catch and report locking failures | |
7046 | m->mDNS_busy = 0; | |
7047 | m->mDNS_reentrancy = 0; | |
7048 | m->mDNS_shutdown = mDNSfalse; | |
7049 | m->lock_rrcache = 0; | |
7050 | m->lock_Questions = 0; | |
7051 | m->lock_Records = 0; | |
7052 | ||
7053 | // Task Scheduling variables | |
7f0064bd A |
7054 | result = mDNSPlatformTimeInit(); |
7055 | if (result != mStatus_NoError) return(result); | |
28f7d060 A |
7056 | m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); |
7057 | timenow = mDNS_TimeNow_NoLock(m); | |
7f0064bd | 7058 | |
c9b9ae52 A |
7059 | m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section |
7060 | m->timenow_last = timenow; | |
c9b9ae52 A |
7061 | m->NextScheduledEvent = timenow; |
7062 | m->SuppressSending = timenow; | |
7063 | m->NextCacheCheck = timenow + 0x78000000; | |
7064 | m->NextScheduledQuery = timenow + 0x78000000; | |
7065 | m->NextScheduledProbe = timenow + 0x78000000; | |
7066 | m->NextScheduledResponse = timenow + 0x78000000; | |
7067 | m->ExpectUnicastResponse = timenow + 0x78000000; | |
7068 | m->RandomQueryDelay = 0; | |
7f0064bd | 7069 | m->PktNum = 0; |
c9b9ae52 A |
7070 | m->SendDeregistrations = mDNSfalse; |
7071 | m->SendImmediateAnswers = mDNSfalse; | |
7072 | m->SleepState = mDNSfalse; | |
7073 | ||
7074 | // These fields only required for mDNS Searcher... | |
7075 | m->Questions = mDNSNULL; | |
7076 | m->NewQuestions = mDNSNULL; | |
7077 | m->CurrentQuestion = mDNSNULL; | |
7078 | m->LocalOnlyQuestions = mDNSNULL; | |
7079 | m->NewLocalOnlyQuestions = mDNSNULL; | |
7080 | m->rrcache_size = 0; | |
7081 | m->rrcache_totalused = 0; | |
7082 | m->rrcache_active = 0; | |
7083 | m->rrcache_report = 10; | |
7084 | m->rrcache_free = mDNSNULL; | |
7085 | ||
283ee3ff | 7086 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL; |
c9b9ae52 | 7087 | |
8e92c31c | 7088 | mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); |
c9b9ae52 A |
7089 | |
7090 | // Fields below only required for mDNS Responder... | |
7091 | m->hostlabel.c[0] = 0; | |
7092 | m->nicelabel.c[0] = 0; | |
7f0064bd | 7093 | m->MulticastHostname.c[0] = 0; |
c9b9ae52 A |
7094 | m->HIHardware.c[0] = 0; |
7095 | m->HISoftware.c[0] = 0; | |
7096 | m->ResourceRecords = mDNSNULL; | |
7097 | m->DuplicateRecords = mDNSNULL; | |
283ee3ff | 7098 | m->NewLocalRecords = mDNSNULL; |
c9b9ae52 A |
7099 | m->CurrentRecord = mDNSNULL; |
7100 | m->HostInterfaces = mDNSNULL; | |
7101 | m->ProbeFailTime = 0; | |
7102 | m->NumFailedProbes = 0; | |
7103 | m->SuppressProbes = 0; | |
6528fe3e | 7104 | |
7f0064bd | 7105 | #ifndef UNICAST_DISABLED |
8e92c31c | 7106 | uDNS_Init(m); |
28f7d060 | 7107 | m->SuppressStdPort53Queries = 0; |
7f0064bd | 7108 | #endif |
6528fe3e | 7109 | result = mDNSPlatformInit(m); |
c9b9ae52 | 7110 | |
6528fe3e A |
7111 | return(result); |
7112 | } | |
7113 | ||
c9b9ae52 | 7114 | mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) |
6528fe3e A |
7115 | { |
7116 | m->mDNSPlatformStatus = result; | |
c9b9ae52 | 7117 | if (m->MainCallback) |
8e92c31c A |
7118 | { |
7119 | mDNS_Lock(m); | |
7120 | m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback | |
c9b9ae52 | 7121 | m->MainCallback(m, mStatus_NoError); |
8e92c31c A |
7122 | m->mDNS_reentrancy--; // Decrement to block mDNS API calls again |
7123 | mDNS_Unlock(m); | |
7124 | } | |
6528fe3e A |
7125 | } |
7126 | ||
c9b9ae52 | 7127 | mDNSexport void mDNS_Close(mDNS *const m) |
6528fe3e | 7128 | { |
c9b9ae52 A |
7129 | mDNSu32 rrcache_active = 0; |
7130 | mDNSu32 rrcache_totalused = 0; | |
7131 | mDNSu32 slot; | |
7132 | NetworkInterfaceInfo *intf; | |
7133 | mDNS_Lock(m); | |
8e92c31c | 7134 | |
c9b9ae52 | 7135 | m->mDNS_shutdown = mDNStrue; |
6528fe3e | 7136 | |
7f0064bd A |
7137 | #ifndef UNICAST_DISABLED |
7138 | uDNS_Close(m); | |
7139 | #endif | |
c9b9ae52 A |
7140 | rrcache_totalused = m->rrcache_totalused; |
7141 | for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) | |
716635cc | 7142 | { |
283ee3ff | 7143 | while(m->rrcache_hash[slot]) |
c9b9ae52 | 7144 | { |
283ee3ff A |
7145 | CacheGroup *cg = m->rrcache_hash[slot]; |
7146 | while (cg->members) | |
7147 | { | |
7148 | CacheRecord *rr = cg->members; | |
7149 | cg->members = cg->members->next; | |
7150 | if (rr->CRActiveQuestion) rrcache_active++; | |
7151 | ReleaseCacheRecord(m, rr); | |
7152 | } | |
7153 | cg->rrcache_tail = &cg->members; | |
7154 | ReleaseCacheGroup(m, &m->rrcache_hash[slot]); | |
c9b9ae52 | 7155 | } |
716635cc | 7156 | } |
7f0064bd | 7157 | debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); |
c9b9ae52 A |
7158 | if (rrcache_active != m->rrcache_active) |
7159 | LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); | |
7160 | ||
c9b9ae52 A |
7161 | for (intf = m->HostInterfaces; intf; intf = intf->next) |
7162 | if (intf->Advertise) | |
8e92c31c | 7163 | DeadvertiseInterface(m, intf); |
6528fe3e A |
7164 | |
7165 | // Make sure there are nothing but deregistering records remaining in the list | |
c9b9ae52 | 7166 | if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set"); |
6528fe3e A |
7167 | m->CurrentRecord = m->ResourceRecords; |
7168 | while (m->CurrentRecord) | |
7169 | { | |
c9b9ae52 | 7170 | AuthRecord *rr = m->CurrentRecord; |
c9b9ae52 | 7171 | if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) |
6528fe3e | 7172 | { |
283ee3ff | 7173 | debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c); |
c9b9ae52 | 7174 | mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); |
6528fe3e | 7175 | } |
283ee3ff A |
7176 | else |
7177 | m->CurrentRecord = rr->next; | |
6528fe3e A |
7178 | } |
7179 | ||
7180 | if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records"); | |
7181 | else debugf("mDNS_Close: No deregistering records remain"); | |
7182 | ||
7183 | // If any deregistering records remain, send their deregistration announcements before we exit | |
283ee3ff A |
7184 | if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); |
7185 | else if (m->ResourceRecords) SendResponses(m); | |
7186 | if (m->ResourceRecords) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m, m->ResourceRecords)); | |
6528fe3e A |
7187 | |
7188 | mDNS_Unlock(m); | |
7189 | debugf("mDNS_Close: mDNSPlatformClose"); | |
7190 | mDNSPlatformClose(m); | |
7191 | debugf("mDNS_Close: done"); | |
7192 | } |