]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/DNSCommon.c
mDNSResponder-214.3.tar.gz
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16
17 Change History (most recent first):
18
19 $Log: DNSCommon.c,v $
20 Revision 1.252 2009/06/27 00:27:03 cheshire
21 <rdar://problem/6959273> mDNSResponder taking up 13% CPU with 400 KBps incoming bonjour requests
22 Removed overly-complicate and ineffective multi-packet known-answer snooping code
23 (Bracketed it with "#if ENABLE_MULTI_PACKET_QUERY_SNOOPING" for now; will delete actual code later)
24
25 Revision 1.251 2009/05/19 23:40:37 cheshire
26 <rdar://problem/6903507> Sleep Proxy: Retransmission logic not working reliably on quiet networks
27 Added m->NextScheduledSPRetry timer for scheduling Sleep Proxy registration retries
28
29 Revision 1.250 2009/05/01 21:28:33 cheshire
30 <rdar://problem/6721680> AppleConnectAgent's reachability checks delay sleep by 30 seconds
31 No longer suspend network operations after we've acknowledged that the machine is going to sleep,
32 because other software may not have yet acknowledged the sleep event, and may be still trying
33 to do unicast DNS queries or other Bonjour operations.
34
35 Revision 1.249 2009/04/24 00:29:20 cheshire
36 <rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists
37 Added support for generating/parsing/displaying NSEC records
38
39 Revision 1.248 2009/04/23 22:11:16 cheshire
40 Minor cleanup in debugging checks in GetLargeResourceRecord
41
42 Revision 1.247 2009/04/21 23:36:25 cheshire
43 <rdar://problem/6814427> Remove unused kDNSType_MAC
44
45 Revision 1.246 2009/04/21 01:00:19 cheshire
46 Fixed typo in previous checkin
47
48 Revision 1.245 2009/04/21 00:57:23 cheshire
49 <rdar://problem/6810410> Off-by-one error in putDomainNameAsLabels()
50 If just writing one-byte root label, make sure we have space for that
51
52 Revision 1.244 2009/04/11 00:19:30 jessic2
53 <rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically
54
55 Revision 1.243 2009/04/01 17:50:10 mcguire
56 cleanup mDNSRandom
57
58 Revision 1.242 2009/03/26 04:01:55 jessic2
59 <rdar://problem/6613786> MessageTracer: Log service types longer than 14 characters and service types with underscores
60
61 Revision 1.241 2009/03/18 20:50:08 cheshire
62 <rdar://problem/6650064> uDNS: Reverse lookup of own IP address takes way too long, sometimes forever
63
64 Revision 1.240 2009/03/18 20:41:04 cheshire
65 Added definition of the all-ones mDNSOpaque16 ID
66
67 Revision 1.239 2009/03/06 23:51:50 mcguire
68 Fix broken build by defining DiscardPort
69
70 Revision 1.238 2009/03/04 00:40:13 cheshire
71 Updated DNS server error codes to be more consistent with definitions at
72 <http://www.iana.org/assignments/dns-parameters>
73
74 Revision 1.237 2009/03/03 23:04:43 cheshire
75 For clarity, renamed "MAC" field to "HMAC" (Host MAC, as opposed to Interface MAC)
76
77 Revision 1.236 2009/03/03 22:51:53 cheshire
78 <rdar://problem/6504236> Sleep Proxy: Waking on same network but different interface will cause conflicts
79
80 Revision 1.235 2009/02/07 05:55:44 cheshire
81 Only pay attention to m->DelaySleep when it's nonzero
82
83 Revision 1.234 2009/02/07 02:52:52 cheshire
84 <rdar://problem/6084043> Sleep Proxy: Need to adopt IOPMConnection
85 Pay attention to m->DelaySleep when computing next task time
86
87 Revision 1.233 2009/01/30 23:50:31 cheshire
88 Added LastLabel() routine to get the last label of a domainname
89
90 Revision 1.232 2009/01/15 00:22:48 mcguire
91 <rdar://problem/6437092> NAT-PMP: mDNSResponder needs to listen on 224.0.0.1:5350/UDP with REUSEPORT
92
93 Revision 1.231 2008/12/12 01:24:06 cheshire
94 Updated GetNextScheduledEvent() to pay attention to m->SPSProxyListChanged
95
96 Revision 1.230 2008/12/10 01:55:54 cheshire
97 Renamed "Max" macro to avoid conflict with another "Max" macro on ARMv5
98
99 Revision 1.229 2008/11/27 01:28:45 cheshire
100 For display purposes, show sleep sequence number as unsigned
101
102 Revision 1.228 2008/11/26 20:57:37 cheshire
103 For consistency with other similar macros, renamed mdnsIsDigit/mdnsIsLetter/mdnsValidHostChar
104 to mDNSIsDigit/mDNSIsLetter/mDNSValidHostChar
105
106 Revision 1.227 2008/11/26 20:28:05 cheshire
107 Added new SSHPort constant
108
109 Revision 1.226 2008/11/16 16:55:51 cheshire
110 Updated debugging messages
111
112 Revision 1.225 2008/11/14 21:56:31 cheshire
113 Moved debugging routine ShowTaskSchedulingError() from daemon.c into DNSCommon.c
114
115 Revision 1.224 2008/11/14 02:20:03 cheshire
116 Include m->NextScheduledSPS in task scheduling calculations
117
118 Revision 1.223 2008/11/14 01:19:03 cheshire
119 Initialize TimeRcvd and TimeExpire fields in AuthRecord_struct
120
121 Revision 1.222 2008/11/14 00:00:53 cheshire
122 After client machine wakes up, Sleep Proxy machine need to remove any records
123 it was temporarily holding as proxy for that client
124
125 Revision 1.221 2008/11/13 19:06:02 cheshire
126 Added code to put, get, and display rdataOPT properly
127
128 Revision 1.220 2008/11/06 01:08:11 mcguire
129 Fix compiler warning about discarding const
130
131 Revision 1.219 2008/11/04 23:06:50 cheshire
132 Split RDataBody union definition into RDataBody and RDataBody2, and removed
133 SOA from the normal RDataBody union definition, saving 270 bytes per AuthRecord
134
135 Revision 1.218 2008/11/04 22:21:44 cheshire
136 Changed zone field of AuthRecord_struct from domainname to pointer, saving 252 bytes per AuthRecord
137
138 Revision 1.217 2008/11/04 22:13:43 cheshire
139 Made RDataBody parameter to GetRRDisplayString_rdb "const"
140
141 Revision 1.216 2008/11/04 20:06:19 cheshire
142 <rdar://problem/6186231> Change MAX_DOMAIN_NAME to 256
143
144 Revision 1.215 2008/10/23 23:54:35 cheshire
145 Added missing "const" in declaration
146
147 Revision 1.214 2008/10/23 22:25:55 cheshire
148 Renamed field "id" to more descriptive "updateid"
149
150 Revision 1.213 2008/10/22 01:01:52 cheshire
151 Added onesEthAddr constant, used for sending ARP broadcasts
152
153 Revision 1.212 2008/10/14 21:52:18 cheshire
154 Added support for putting/getting/printing kDNSType_MAC
155
156 Revision 1.211 2008/10/09 22:36:08 cheshire
157 Now that we have Sleep Proxy Server, can't suppress normal scheduling logic while going to sleep
158
159 Revision 1.210 2008/10/08 01:03:52 cheshire
160 Change GetFirstActiveInterface() so the NetworkInterfaceInfo it returns is not "const"
161 Added mDNS_SetupQuestion() convenience function
162
163 Revision 1.209 2008/09/23 04:13:30 cheshire
164 <rdar://problem/6238774> Remove "local" from the end of _services._dns-sd._udp PTR records
165 Removed old special-case Bonjour Browser hack that is no longer needed
166
167 Revision 1.208 2008/09/23 02:33:56 cheshire
168 <rdar://problem/4738033> uDNS: Should not compress SRV rdata in uDNS packets
169
170 Revision 1.207 2008/09/23 02:30:07 cheshire
171 Get rid of PutResourceRecordCappedTTL()
172
173 Revision 1.206 2008/09/23 02:26:09 cheshire
174 Don't need to export putEmptyResourceRecord (it's only used from DNSCommon.c)
175
176 Revision 1.205 2008/09/23 02:21:00 cheshire
177 Don't need to force setting of rrclass in PutResourceRecordTTLWithLimit() now that putLLQ() sets it correctly
178
179 Revision 1.204 2008/08/29 19:03:05 cheshire
180 <rdar://problem/6185645> Off-by-one error in putDomainNameAsLabels()
181
182 Revision 1.203 2008/08/13 00:47:53 mcguire
183 Handle failures when packet logging
184
185 Revision 1.202 2008/08/13 00:32:48 mcguire
186 refactor to use SwapDNSHeaderBytes instead of swapping manually
187
188 Revision 1.201 2008/07/24 20:23:03 cheshire
189 <rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning
190
191 Revision 1.200 2008/07/18 00:07:50 cheshire
192 <rdar://problem/5904999> Log a message for applications that register service types longer than 14 characters
193
194 Revision 1.199 2008/03/14 19:58:38 mcguire
195 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
196 Make sure we add the record when sending LLQ refreshes
197
198 Revision 1.198 2008/03/07 23:29:24 cheshire
199 Fixed cosmetic byte order display issue in DumpPacket output
200
201 Revision 1.197 2008/03/05 22:51:29 mcguire
202 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
203 Even further refinements
204
205 Revision 1.196 2008/03/05 22:01:53 cheshire
206 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
207 Now that we optionally add the HINFO record, when rewriting the header fields into network byte
208 order, we need to use our updated msg->h.numAdditionals, not the stack variable numAdditionals
209
210 Revision 1.195 2008/03/05 19:06:30 mcguire
211 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
212 further refinements
213
214 Revision 1.194 2008/03/05 00:26:06 cheshire
215 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
216
217 Revision 1.193 2007/12/17 23:42:36 cheshire
218 Added comments about DNSDigest_SignMessage()
219
220 Revision 1.192 2007/12/17 21:24:09 cheshire
221 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
222 We suspend sending of mDNS queries responses when going to sleep, so calculate GetNextScheduledEvent() time accordingly
223
224 Revision 1.191 2007/12/14 00:59:36 cheshire
225 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
226 While going to sleep, don't block event scheduling
227
228 Revision 1.190 2007/12/13 20:20:17 cheshire
229 Minor efficiency tweaks -- converted IdenticalResourceRecord, IdenticalSameNameRecord, and
230 SameRData from functions to macros, which allows the code to be inlined (the compiler can't
231 inline a function defined in a different compilation unit) and therefore optimized better.
232
233 Revision 1.189 2007/12/13 00:17:32 cheshire
234 RDataHashValue was not calculating hash value reliably for RDATA types that have 'holes' in the
235 in-memory representation (particularly SOA was affected by this, resulting in multiple duplicate
236 cache entities for the same SOA record, because they had erroneously different rdatahash values).
237
238 Revision 1.188 2007/12/13 00:13:03 cheshire
239 Simplified RDataHashValue to take a single ResourceRecord pointer, instead of separate rdlength and RDataBody
240
241 Revision 1.187 2007/12/08 00:35:20 cheshire
242 <rdar://problem/5636422> Updating TXT records is too slow
243 m->SuppressSending should not suppress all activity, just mDNS Query/Probe/Response
244
245 Revision 1.186 2007/11/15 22:52:29 cheshire
246 <rdar://problem/5589039> ERROR: mDNSPlatformWriteTCP - send Broken pipe
247
248 Revision 1.185 2007/10/10 20:22:03 cheshire
249 Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
250 apparently caused by trying to sign zero-length messages
251
252 Revision 1.184 2007/10/05 17:56:07 cheshire
253 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
254
255 Revision 1.183 2007/10/02 18:33:46 cheshire
256 Improved GetRRDisplayString to show all constituent strings within a text record
257 (up to the usual MaxMsg 120-character limit)
258
259 Revision 1.182 2007/10/01 19:45:01 cheshire
260 <rdar://problem/5514859> BTMM: Sometimes Back to My Mac autotunnel registrations are malformed
261
262 Revision 1.181 2007/10/01 18:36:53 cheshire
263 Yet another fix to finally get the DumpPacket RCODE display right
264
265 Revision 1.180 2007/09/29 21:30:38 cheshire
266 In DumpPacket/DumpRecords, show an error line if we run out of packet data
267
268 Revision 1.179 2007/09/29 20:44:56 cheshire
269 Fix error in DumpPacket where it was not displaying the RCODE field properly
270
271 Revision 1.178 2007/09/27 21:11:44 cheshire
272 Fixed spelling mistake: ROCDE -> RCODE
273
274 Revision 1.177 2007/09/27 18:51:26 cheshire
275 Improved DumpPacket to use "Zone/Prerequisites/Updates" nomenclature when displaying a DNS Update packet
276
277 Revision 1.176 2007/09/27 17:53:37 cheshire
278 Add display of RCODE and flags in DumpPacket output
279
280 Revision 1.175 2007/09/26 22:26:40 cheshire
281 Also show DNS query/response ID in DumpPacket output
282
283 Revision 1.174 2007/09/26 16:36:02 cheshire
284 In DumpPacket output, begin header line with "-- " to make it visually stand out better
285
286 Revision 1.173 2007/09/26 00:49:46 cheshire
287 Improve packet logging to show sent and received packets,
288 transport protocol (UDP/TCP/TLS) and source/destination address:port
289
290 Revision 1.172 2007/09/21 23:14:39 cheshire
291 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents in verbose debug mode
292 Changed DumpRecords to use LargeCacheRecord on the stack instead of the shared m->rec storage,
293 to eliminate "GetLargeResourceRecord: m->rec appears to be already in use" warnings
294
295 Revision 1.171 2007/09/21 21:12:36 cheshire
296 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
297
298 Revision 1.170 2007/09/07 21:16:58 cheshire
299 Add new symbol "NATPMPAnnouncementPort" (5350)
300
301 Revision 1.169 2007/08/30 00:31:20 cheshire
302 Improve "locking failure" debugging messages to show function name using __func__ macro
303
304 Revision 1.168 2007/08/28 23:58:42 cheshire
305 Rename HostTarget -> AutoTarget
306
307 Revision 1.167 2007/08/10 23:10:05 vazquez
308 <rdar://problem/5389850> mDNS: Reverse lookups of IPv6 link-local addresses always fail
309
310 Revision 1.166 2007/08/01 16:09:13 cheshire
311 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
312
313 Revision 1.165 2007/08/01 00:04:13 cheshire
314 <rdar://problem/5261696> Crash in tcpKQSocketCallback
315 Half-open TCP connections were not being cancelled properly
316
317 Revision 1.164 2007/07/27 20:48:43 cheshire
318 In DumpRecords(), include record TTL in output
319
320 Revision 1.163 2007/07/16 20:10:11 vazquez
321 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
322 Added SSDP port number
323
324 Revision 1.162 2007/07/10 01:59:33 cheshire
325 <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
326 Fixed GetPktLease to use shared m->rec instead of putting LargeCacheRecord on the stack
327
328 Revision 1.161 2007/07/06 18:56:26 cheshire
329 Check m->NextScheduledNATOp in GetNextScheduledEvent()
330
331 Revision 1.160 2007/06/29 00:06:42 vazquez
332 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
333
334 Revision 1.159 2007/06/28 21:17:17 cheshire
335 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
336
337 Revision 1.158 2007/05/25 00:25:43 cheshire
338 <rdar://problem/5227737> Need to enhance putRData to output all current known types
339
340 Revision 1.157 2007/05/23 00:32:15 cheshire
341 Don't treat uDNS responses as an entire RRSet (kDNSRecordTypePacketUniqueMask)
342 when received in a truncated UDP response
343
344 Revision 1.156 2007/05/15 00:29:00 cheshire
345 Print «ZERO ADDRESS» for %#a with a zero mDNSAddr
346
347 Revision 1.155 2007/05/07 22:07:47 cheshire
348 <rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types
349
350 Revision 1.154 2007/05/04 20:19:53 cheshire
351 Improve DumpPacket() output
352
353 Revision 1.153 2007/05/01 21:46:31 cheshire
354 Move GetLLQOptData/GetPktLease from uDNS.c into DNSCommon.c so that dnsextd can use them
355
356 Revision 1.152 2007/04/27 19:28:01 cheshire
357 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
358 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
359 -- it would start a query and then quickly cancel it, and then when
360 StartGetZoneData completed, it had a dangling pointer and crashed.)
361
362 Revision 1.151 2007/04/26 13:35:25 cheshire
363 Add kDNSType_SOA case in SameRDataBody, and a comment in GetLargeResourceRecord about why this is important
364
365 Revision 1.150 2007/04/24 00:17:33 cheshire
366 Made LocateLLQOptData guard against packets with bogus numAdditionals value
367
368 Revision 1.149 2007/04/23 21:43:00 cheshire
369 Remove debugging check
370
371 Revision 1.148 2007/04/23 04:55:29 cheshire
372 Add some defensive null pointer checks
373
374 Revision 1.147 2007/04/22 20:18:10 cheshire
375 Add comment about mDNSRandom()
376
377 Revision 1.146 2007/04/22 06:02:02 cheshire
378 <rdar://problem/4615977> Query should immediately return failure when no server
379
380 Revision 1.145 2007/04/20 21:17:24 cheshire
381 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
382
383 Revision 1.144 2007/04/19 18:02:43 cheshire
384 <rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit
385
386 Revision 1.143 2007/04/16 21:53:49 cheshire
387 Improve display of negative cache entries
388
389 Revision 1.142 2007/04/05 22:55:35 cheshire
390 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
391
392 Revision 1.141 2007/04/04 01:33:11 cheshire
393 <rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record
394 Overly defensive code was zeroing too much of the AuthRecord structure
395
396 Revision 1.140 2007/04/03 19:37:58 cheshire
397 Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918()
398
399 Revision 1.139 2007/04/03 19:18:39 cheshire
400 Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly
401
402 Revision 1.138 2007/03/28 21:14:08 cheshire
403 The rrclass field of an OPT pseudo-RR holds the sender's UDP payload size
404
405 Revision 1.137 2007/03/28 20:59:26 cheshire
406 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
407
408 Revision 1.136 2007/03/28 15:56:37 cheshire
409 <rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output
410
411 Revision 1.135 2007/03/28 01:20:05 cheshire
412 <rdar://problem/4883206> Improve/create logging for secure browse
413
414 Revision 1.134 2007/03/27 23:25:35 cheshire
415 Fix error caching SOA records
416 (cache entry was size of wire-format packed data, not size of in-memory structure)
417
418 Revision 1.133 2007/03/26 22:55:45 cheshire
419 Add OPT and TSIG to list of types DNSTypeName() knows about
420
421 Revision 1.132 2007/03/22 18:31:48 cheshire
422 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
423
424 Revision 1.131 2007/03/21 21:55:20 cheshire
425 <rdar://problem/5069688> Hostname gets ; or : which are illegal characters
426 Error in AppendLabelSuffix() for numbers close to the 32-bit limit
427
428 Revision 1.130 2007/03/21 19:23:37 cheshire
429 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
430 Make check less strict so we don't break Bonjour Browser
431
432 Revision 1.129 2007/03/21 01:00:45 cheshire
433 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
434 DeconstructServiceName() needs to be more defensive about what it considers legal
435
436 Revision 1.128 2007/03/21 00:30:02 cheshire
437 <rdar://problem/4789455> Multiple errors in DNameList-related code
438
439 Revision 1.127 2007/03/20 17:07:15 cheshire
440 Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket"
441
442 Revision 1.126 2007/03/10 03:26:44 cheshire
443 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
444
445 Revision 1.125 2007/03/07 00:08:58 cheshire
446 <rdar://problem/4347550> Don't allow hyphens at start of service type
447
448 Revision 1.124 2007/01/19 18:04:05 cheshire
449 For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT
450
451 Revision 1.123 2007/01/10 22:45:51 cheshire
452 Cast static strings to "(const domainname*)", not "(domainname*)"
453
454 Revision 1.122 2007/01/06 00:47:35 cheshire
455 Improve GetRRDisplayString to indicate when record has zero-length rdata
456
457 Revision 1.121 2007/01/05 08:30:39 cheshire
458 Trim excessive "$Log" checkin history from before 2006
459 (checkin history still available via "cvs log ..." of course)
460
461 Revision 1.120 2007/01/05 05:23:00 cheshire
462 Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd)
463
464 Revision 1.119 2007/01/05 04:30:16 cheshire
465 Change a couple of "(domainname *)" casts to "(const domainname *)"
466
467 Revision 1.118 2007/01/04 20:21:59 cheshire
468 <rdar://problem/4720673> uDNS: Need to start caching unicast records
469 Don't return multicast answers in response to unicast questions
470
471 Revision 1.117 2006/12/22 20:59:49 cheshire
472 <rdar://problem/4742742> Read *all* DNS keys from keychain,
473 not just key for the system-wide default registration domain
474
475 Revision 1.116 2006/12/21 00:04:07 cheshire
476 To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord()
477
478 Revision 1.115 2006/12/20 04:07:34 cheshire
479 Remove uDNS_info substructure from AuthRecord_struct
480
481 Revision 1.114 2006/12/19 22:40:04 cheshire
482 Fix compiler warnings
483
484 Revision 1.113 2006/12/19 02:21:08 cheshire
485 Delete spurious spaces
486
487 Revision 1.112 2006/12/15 20:42:10 cheshire
488 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
489 Additional defensive coding in GetLargeResourceRecord() to reject apparently-valid
490 rdata that actually runs past the end of the received packet data.
491
492 Revision 1.111 2006/12/15 19:09:57 cheshire
493 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
494 Made DomainNameLength() more defensive by adding a limit parameter, so it can be
495 safely used to inspect potentially malformed data received from external sources.
496 Without this, a domain name that starts off apparently valid, but extends beyond the end of
497 the received packet data, could have appeared valid if the random bytes are already in memory
498 beyond the end of the packet just happened to have reasonable values (e.g. all zeroes).
499
500 Revision 1.110 2006/11/18 05:01:30 cheshire
501 Preliminary support for unifying the uDNS and mDNS code,
502 including caching of uDNS answers
503
504 Revision 1.109 2006/11/10 00:54:14 cheshire
505 <rdar://problem/4816598> Changing case of Computer Name doesn't work
506
507 Revision 1.108 2006/10/05 23:11:18 cheshire
508 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
509
510 Revision 1.107 2006/09/15 21:20:14 cheshire
511 Remove uDNS_info substructure from mDNS_struct
512
513 Revision 1.106 2006/08/14 23:24:22 cheshire
514 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
515
516 Revision 1.105 2006/07/15 02:01:28 cheshire
517 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
518 Fix broken "empty string" browsing
519
520 Revision 1.104 2006/07/05 23:09:13 cheshire
521 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
522 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
523
524 Revision 1.103 2006/06/29 07:42:14 cheshire
525 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
526
527 Revision 1.102 2006/06/22 19:49:11 cheshire
528 Added (commented out) definitions for the LLMNR UDP port and multicast addresses
529
530 Revision 1.101 2006/06/15 21:35:15 cheshire
531 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
532 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
533
534 Revision 1.100 2006/06/08 22:58:46 cheshire
535 <rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
536
537 Revision 1.99 2006/05/18 01:32:33 cheshire
538 <rdar://problem/4472706> iChat: Lost connection with Bonjour
539 (mDNSResponder insufficiently defensive against malformed browsing PTR responses)
540
541 Revision 1.98 2006/03/19 17:00:58 cheshire
542 Define symbol MaxMsg instead of using hard-coded constant value '80'
543
544 Revision 1.97 2006/03/18 21:47:56 cheshire
545 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
546
547 Revision 1.96 2006/03/10 21:51:42 cheshire
548 <rdar://problem/4111464> After record update, old record sometimes remains in cache
549 Split out SameRDataBody() into a separate routine so it can be called from other code
550
551 Revision 1.95 2006/03/08 22:43:11 cheshire
552 Use "localdomain" symbol instead of literal string
553
554 Revision 1.94 2006/03/02 21:59:55 cheshire
555 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
556 Improve sanity checks & debugging support in GetLargeResourceRecord()
557
558 Revision 1.93 2006/03/02 20:30:47 cheshire
559 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
560
561 */
562
563 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
564 #define mDNS_InstantiateInlines 1
565 #include "DNSCommon.h"
566
567 // Disable certain benign warnings with Microsoft compilers
568 #if (defined(_MSC_VER))
569 // Disable "conditional expression is constant" warning for debug macros.
570 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
571 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
572 #pragma warning(disable:4127)
573 // Disable "array is too small to include a terminating null character" warning
574 // -- domain labels have an initial length byte, not a terminating null character
575 #pragma warning(disable:4295)
576 #endif
577
578 // ***************************************************************************
579 #if COMPILER_LIKES_PRAGMA_MARK
580 #pragma mark - Program Constants
581 #endif
582
583 mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
584 mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
585 mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
586 mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
587 mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
588 mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
589 mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
590 mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } };
591
592 mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
593
594 mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
595 mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1;
596 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
597 mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3;
598
599 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
600 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
601 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
602 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
603 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
604 // with Microsoft's LLMNR client code.
605
606 #define DiscardPortAsNumber 9
607 #define SSHPortAsNumber 22
608 #define UnicastDNSPortAsNumber 53
609 #define SSDPPortAsNumber 1900
610 #define IPSECPortAsNumber 4500
611 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
612 #define NATPMPAnnouncementPortAsNumber 5350
613 #define NATPMPPortAsNumber 5351
614 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
615 #define MulticastDNSPortAsNumber 5353
616 #define LoopbackIPCPortAsNumber 5354
617 //#define MulticastDNSPortAsNumber 5355 // LLMNR
618 #define PrivateDNSPortAsNumber 5533
619
620 mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber >> 8, DiscardPortAsNumber & 0xFF } };
621 mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } };
622 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
623 mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } };
624 mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } };
625 mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } };
626 mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
627 mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } };
628 mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } };
629 mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
630 mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } };
631 mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } };
632
633 mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
634 mDNSexport const mDNSv4Addr AllSystemsMcast = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements
635 mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
636 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
637 mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
638 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
639
640 mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
641 mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } };
642 mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
643 mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
644 mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
645 mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
646 mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
647
648 mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } };
649
650 // ***************************************************************************
651 #if COMPILER_LIKES_PRAGMA_MARK
652 #pragma mark -
653 #pragma mark - General Utility Functions
654 #endif
655
656 // return true for RFC1918 private addresses
657 mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr)
658 {
659 return ((addr->b[0] == 10) || // 10/8 prefix
660 (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12
661 (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16
662 }
663
664 mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
665 {
666 while (intf && !intf->InterfaceActive) intf = intf->next;
667 return(intf);
668 }
669
670 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
671 {
672 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
673 if (next) return(next->InterfaceID); else return(mDNSNULL);
674 }
675
676 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
677 {
678 mDNSu32 slot, used = 0;
679 CacheGroup *cg;
680 const CacheRecord *rr;
681 FORALL_CACHERECORDS(slot, cg, rr)
682 if (rr->resrec.InterfaceID == id) used++;
683 return(used);
684 }
685
686 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
687 {
688 switch (rrtype)
689 {
690 case kDNSType_A: return("Addr");
691 case kDNSType_NS: return("NS");
692 case kDNSType_CNAME:return("CNAME");
693 case kDNSType_SOA: return("SOA");
694 case kDNSType_NULL: return("NULL");
695 case kDNSType_PTR: return("PTR");
696 case kDNSType_HINFO:return("HINFO");
697 case kDNSType_TXT: return("TXT");
698 case kDNSType_AAAA: return("AAAA");
699 case kDNSType_SRV: return("SRV");
700 case kDNSType_OPT: return("OPT");
701 case kDNSType_NSEC: return("NSEC");
702 case kDNSType_TSIG: return("TSIG");
703 case kDNSQType_ANY: return("ANY");
704 default: {
705 static char buffer[16];
706 mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
707 return(buffer);
708 }
709 }
710 }
711
712 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
713 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
714 // long as this routine is only used for debugging messages, it probably isn't a big problem.
715 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer)
716 {
717 const RDataBody2 *const rd = (RDataBody2 *)rd1;
718 #define RemSpc (MaxMsg-1-length)
719 char *ptr = buffer;
720 mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
721 if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
722 if (!rr->rdlength) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); }
723
724 switch (rr->rrtype)
725 {
726 case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break;
727
728 case kDNSType_NS: // Same as PTR
729 case kDNSType_CNAME:// Same as PTR
730 case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break;
731
732 case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d",
733 rd->soa.mname.c, rd->soa.rname.c,
734 rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
735 break;
736
737 case kDNSType_HINFO:// Display this the same as TXT (show all constituent strings)
738 case kDNSType_TXT: {
739 const mDNSu8 *t = rd->txt.c;
740 while (t < rd->txt.c + rr->rdlength)
741 {
742 length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t);
743 t += 1 + t[0];
744 }
745 } break;
746
747 case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break;
748 case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s",
749 rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
750
751 case kDNSType_OPT: {
752 const rdataOPT *opt;
753 const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength];
754 length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass);
755 for (opt = &rd->opt[0]; opt < end; opt++)
756 {
757 switch(opt->opt)
758 {
759 case kDNSOpt_LLQ:
760 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers);
761 length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp);
762 length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err);
763 length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]);
764 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease);
765 break;
766 case kDNSOpt_Lease:
767 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease);
768 break;
769 case kDNSOpt_Owner:
770 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers);
771 length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned
772 length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b);
773 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
774 {
775 length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b);
776 if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
777 length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
778 }
779 break;
780 default:
781 length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt);
782 break;
783 }
784 }
785 }
786 break;
787
788 case kDNSType_NSEC: {
789 int i;
790 for (i=0; i<255; i++)
791 if (rd->nsec.bitmap[i>>3] & (128 >> (i&7)))
792 length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(i));
793 }
794 break;
795
796 default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data);
797 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
798 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
799 break;
800 }
801 return(buffer);
802 }
803
804 // See comments in mDNSEmbeddedAPI.h
805 #if _PLATFORM_HAS_STRONG_PRNG_
806 #define mDNSRandomNumber mDNSPlatformRandomNumber
807 #else
808 mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed)
809 {
810 return seed * 21 + 1;
811 }
812
813 mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration)
814 {
815 return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed;
816 }
817
818 mDNSlocal mDNSu32 mDNSRandomNumber()
819 {
820 static mDNSBool seeded = mDNSfalse;
821 static mDNSu32 seed = 0;
822 if (!seeded)
823 {
824 seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
825 seeded = mDNStrue;
826 }
827 return (seed = mDNSRandomFromSeed(seed));
828 }
829 #endif // ! _PLATFORM_HAS_STRONG_PRNG_
830
831 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive
832 {
833 mDNSu32 ret = 0;
834 mDNSu32 mask = 1;
835
836 while (mask < max) mask = (mask << 1) | 1;
837
838 do ret = mDNSRandomNumber() & mask;
839 while (ret > max);
840
841 return ret;
842 }
843
844 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
845 {
846 if (ip1->type == ip2->type)
847 {
848 switch (ip1->type)
849 {
850 case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal
851 case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
852 case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
853 }
854 }
855 return(mDNSfalse);
856 }
857
858 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
859 {
860 switch(ip->type)
861 {
862 case mDNSAddrType_IPv4: return(mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
863 case mDNSAddrType_IPv6: return(mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
864 default: return(mDNSfalse);
865 }
866 }
867
868 // ***************************************************************************
869 #if COMPILER_LIKES_PRAGMA_MARK
870 #pragma mark -
871 #pragma mark - Domain Name Utility Functions
872 #endif
873
874 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
875 {
876 int i;
877 const int len = *a++;
878
879 if (len > MAX_DOMAIN_LABEL)
880 { debugf("Malformed label (too long)"); return(mDNSfalse); }
881
882 if (len != *b++) return(mDNSfalse);
883 for (i=0; i<len; i++)
884 {
885 mDNSu8 ac = *a++;
886 mDNSu8 bc = *b++;
887 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
888 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
889 if (ac != bc) return(mDNSfalse);
890 }
891 return(mDNStrue);
892 }
893
894 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
895 {
896 const mDNSu8 * a = d1->c;
897 const mDNSu8 * b = d2->c;
898 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
899
900 while (*a || *b)
901 {
902 if (a + 1 + *a >= max)
903 { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); }
904 if (!SameDomainLabel(a, b)) return(mDNSfalse);
905 a += 1 + *a;
906 b += 1 + *b;
907 }
908
909 return(mDNStrue);
910 }
911
912 mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
913 {
914 mDNSu16 l1 = DomainNameLength(d1);
915 mDNSu16 l2 = DomainNameLength(d2);
916 return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
917 }
918
919 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
920 {
921 // Domains that are defined to be resolved via link-local multicast are:
922 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
923 static const domainname *nL = (const domainname*)"\x5" "local";
924 static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
925 static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
926 static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
927 static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
928 static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
929
930 const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc.
931 d1 = d2 = d3 = d4 = d5 = mDNSNULL;
932 while (d->c[0])
933 {
934 d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
935 d = (const domainname*)(d->c + 1 + d->c[0]);
936 }
937
938 if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
939 if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
940 if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
941 if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
942 if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
943 if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
944 return(mDNSfalse);
945 }
946
947 mDNSexport const mDNSu8 *LastLabel(const domainname *d)
948 {
949 const mDNSu8 *p = d->c;
950 while (d->c[0])
951 {
952 p = d->c;
953 d = (const domainname*)(d->c + 1 + d->c[0]);
954 }
955 return(p);
956 }
957
958 // Returns length of a domain name INCLUDING the byte for the final null label
959 // e.g. for the root label "." it returns one
960 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
961 // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
962 // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
963 mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
964 {
965 const mDNSu8 *src = name->c;
966 while (src < limit && *src <= MAX_DOMAIN_LABEL)
967 {
968 if (*src == 0) return((mDNSu16)(src - name->c + 1));
969 src += 1 + *src;
970 }
971 return(MAX_DOMAIN_NAME+1);
972 }
973
974 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
975 // for the final null label, e.g. for the root label "." it returns one.
976 // E.g. for the FQDN "foo.com." it returns 9
977 // (length, three data bytes, length, three more data bytes, final zero).
978 // In the case where a parent domain name is provided, and the given name is a child
979 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
980 // of the child name, plus TWO bytes for the compression pointer.
981 // E.g. for the name "foo.com." with parent "com.", it returns 6
982 // (length, three data bytes, two-byte compression pointer).
983 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
984 {
985 const mDNSu8 *src = name->c;
986 if (parent && parent->c[0] == 0) parent = mDNSNULL;
987 while (*src)
988 {
989 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
990 if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
991 src += 1 + *src;
992 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
993 }
994 return((mDNSu16)(src - name->c + 1));
995 }
996
997 // CountLabels() returns number of labels in name, excluding final root label
998 // (e.g. for "apple.com." CountLabels returns 2.)
999 mDNSexport int CountLabels(const domainname *d)
1000 {
1001 int count = 0;
1002 const mDNSu8 *ptr;
1003 for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
1004 return count;
1005 }
1006
1007 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
1008 // returning a pointer to the suffix with 'skip' labels removed.
1009 mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
1010 {
1011 while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
1012 return(d);
1013 }
1014
1015 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
1016 // The C string contains the label as-is, with no escaping, etc.
1017 // Any dots in the name are literal dots, not label separators
1018 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
1019 // in the domainname bufer (i.e. the next byte after the terminating zero).
1020 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1021 // AppendLiteralLabelString returns mDNSNULL.
1022 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
1023 {
1024 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
1025 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
1026 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
1027 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
1028 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
1029
1030 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
1031 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
1032 *ptr++ = 0; // Put the null root label on the end
1033 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
1034 else return(ptr); // Success: return new value of ptr
1035 }
1036
1037 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
1038 // The C string is in conventional DNS syntax:
1039 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1040 // If successful, AppendDNSNameString returns a pointer to the next unused byte
1041 // in the domainname bufer (i.e. the next byte after the terminating zero).
1042 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1043 // AppendDNSNameString returns mDNSNULL.
1044 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
1045 {
1046 const char *cstr = cstring;
1047 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
1048 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
1049 while (*cstr && ptr < lim) // While more characters, and space to put them...
1050 {
1051 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
1052 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
1053 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
1054 {
1055 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
1056 if (c == '\\') // If escape character, check next character
1057 {
1058 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
1059 if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1]))
1060 { // If three decimal digits,
1061 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
1062 int v1 = cstr[ 0] - '0';
1063 int v2 = cstr[ 1] - '0';
1064 int val = v0 * 100 + v1 * 10 + v2;
1065 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
1066 }
1067 }
1068 *ptr++ = c; // Write the character
1069 }
1070 if (*cstr) cstr++; // Skip over the trailing dot (if present)
1071 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
1072 return(mDNSNULL);
1073 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
1074 }
1075
1076 *ptr++ = 0; // Put the null root label on the end
1077 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
1078 else return(ptr); // Success: return new value of ptr
1079 }
1080
1081 // AppendDomainLabel appends a single label to a name.
1082 // If successful, AppendDomainLabel returns a pointer to the next unused byte
1083 // in the domainname bufer (i.e. the next byte after the terminating zero).
1084 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1085 // AppendDomainLabel returns mDNSNULL.
1086 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
1087 {
1088 int i;
1089 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
1090
1091 // Check label is legal
1092 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
1093
1094 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
1095 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
1096
1097 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
1098 *ptr++ = 0; // Put the null root label on the end
1099 return(ptr);
1100 }
1101
1102 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
1103 {
1104 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
1105 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
1106 const mDNSu8 * src = append->c;
1107 while (src[0])
1108 {
1109 int i;
1110 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
1111 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
1112 *ptr = 0; // Put the null root label on the end
1113 src += i;
1114 }
1115 return(ptr);
1116 }
1117
1118 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
1119 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
1120 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
1121 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
1122 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
1123 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
1124 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
1125 {
1126 mDNSu8 * ptr = label->c + 1; // Where we're putting it
1127 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
1128 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
1129 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
1130 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
1131 }
1132
1133 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
1134 // The C string is in conventional DNS syntax:
1135 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1136 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
1137 // in the domainname bufer (i.e. the next byte after the terminating zero).
1138 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1139 // MakeDomainNameFromDNSNameString returns mDNSNULL.
1140 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
1141 {
1142 name->c[0] = 0; // Make an empty domain name
1143 return(AppendDNSNameString(name, cstr)); // And then add this string to it
1144 }
1145
1146 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
1147 {
1148 const mDNSu8 * src = label->c; // Domain label we're reading
1149 const mDNSu8 len = *src++; // Read length of this (non-null) label
1150 const mDNSu8 *const end = src + len; // Work out where the label ends
1151 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
1152 while (src < end) // While we have characters in the label
1153 {
1154 mDNSu8 c = *src++;
1155 if (esc)
1156 {
1157 if (c == '.' || c == esc) // If character is a dot or the escape character
1158 *ptr++ = esc; // Output escape character
1159 else if (c <= ' ') // If non-printing ascii,
1160 { // Output decimal escape sequence
1161 *ptr++ = esc;
1162 *ptr++ = (char) ('0' + (c / 100) );
1163 *ptr++ = (char) ('0' + (c / 10) % 10);
1164 c = (mDNSu8)('0' + (c ) % 10);
1165 }
1166 }
1167 *ptr++ = (char)c; // Copy the character
1168 }
1169 *ptr = 0; // Null-terminate the string
1170 return(ptr); // and return
1171 }
1172
1173 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
1174 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
1175 {
1176 const mDNSu8 *src = name->c; // Domain name we're reading
1177 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1178
1179 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
1180
1181 while (*src) // While more characters in the domain name
1182 {
1183 if (src + 1 + *src >= max) return(mDNSNULL);
1184 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
1185 if (!ptr) return(mDNSNULL);
1186 src += 1 + *src;
1187 *ptr++ = '.'; // Write the dot after the label
1188 }
1189
1190 *ptr++ = 0; // Null-terminate the string
1191 return(ptr); // and return
1192 }
1193
1194 // RFC 1034 rules:
1195 // Host names must start with a letter, end with a letter or digit,
1196 // and have as interior characters only letters, digits, and hyphen.
1197 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
1198
1199 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
1200 {
1201 const mDNSu8 * src = &UTF8Name[1];
1202 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
1203 mDNSu8 * ptr = &hostlabel->c[1];
1204 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
1205 while (src < end)
1206 {
1207 // Delete apostrophes from source name
1208 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
1209 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
1210 { src += 3; continue; } // Unicode curly apostrophe
1211 if (ptr < lim)
1212 {
1213 if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
1214 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
1215 }
1216 src++;
1217 }
1218 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
1219 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
1220 }
1221
1222 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
1223 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
1224 ((X)[4] | 0x20) == 'p')
1225
1226 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
1227 const domainlabel *name, const domainname *type, const domainname *const domain)
1228 {
1229 int i, len;
1230 mDNSu8 *dst = fqdn->c;
1231 const mDNSu8 *src;
1232 const char *errormsg;
1233 #if APPLE_OSX_mDNSResponder
1234 mDNSBool loggedUnderscore = mDNSfalse;
1235 static char typeBuf[MAX_ESCAPED_DOMAIN_NAME];
1236 #endif
1237
1238 // In the case where there is no name (and ONLY in that case),
1239 // a single-label subtype is allowed as the first label of a three-part "type"
1240 if (!name && type)
1241 {
1242 const mDNSu8 *s0 = type->c;
1243 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
1244 {
1245 const mDNSu8 * s1 = s0 + 1 + s0[0];
1246 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1247 {
1248 const mDNSu8 *s2 = s1 + 1 + s1[0];
1249 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
1250 {
1251 static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
1252 src = s0; // Copy the first label
1253 len = *src;
1254 for (i=0; i <= len; i++) *dst++ = *src++;
1255 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
1256 type = (const domainname *)s1;
1257
1258 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1259 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1260 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1261 if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
1262 dst -= sizeof(SubTypeLabel);
1263 }
1264 }
1265 }
1266 }
1267
1268 if (name && name->c[0])
1269 {
1270 src = name->c; // Put the service name into the domain name
1271 len = *src;
1272 if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
1273 for (i=0; i<=len; i++) *dst++ = *src++;
1274 }
1275 else
1276 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1277
1278 src = type->c; // Put the service type into the domain name
1279 len = *src;
1280 if (len < 2 || len > 15)
1281 {
1282 LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-14 characters. "
1283 "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c);
1284 #if APPLE_OSX_mDNSResponder
1285 ConvertDomainNameToCString(type, typeBuf);
1286 mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, "");
1287 #endif
1288 }
1289 if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain))) return(mDNSNULL);
1290 if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
1291 for (i=2; i<=len; i++)
1292 {
1293 // Letters and digits are allowed anywhere
1294 if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue;
1295 // Hyphens are only allowed as interior characters
1296 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1297 // with the same rule as hyphens
1298 if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len)
1299 {
1300 #if APPLE_OSX_mDNSResponder
1301 if (src[i] == '_' && loggedUnderscore == mDNSfalse)
1302 {
1303 ConvertDomainNameToCString(type, typeBuf);
1304 mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, "");
1305 loggedUnderscore = mDNStrue;
1306 }
1307 #endif
1308 continue;
1309 }
1310 errormsg = "Application protocol name must contain only letters, digits, and hyphens";
1311 #if APPLE_OSX_mDNSResponder
1312 {
1313 ConvertDomainNameToCString(type, typeBuf);
1314 mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, "");
1315 }
1316 #endif
1317 goto fail;
1318 }
1319 for (i=0; i<=len; i++) *dst++ = *src++;
1320
1321 len = *src;
1322 if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
1323 for (i=0; i<=len; i++) *dst++ = *src++;
1324
1325 if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
1326
1327 *dst = 0;
1328 if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
1329 if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
1330 { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
1331 dst = AppendDomainName(fqdn, domain);
1332 if (!dst) { errormsg = "Service domain too long"; goto fail; }
1333 return(dst);
1334
1335 fail:
1336 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
1337 return(mDNSNULL);
1338 }
1339
1340 // A service name has the form: instance.application-protocol.transport-protocol.domain
1341 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1342 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1343 // However, if the given FQDN doesn't contain at least three labels,
1344 // DeconstructServiceName will reject it and return mDNSfalse.
1345 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
1346 domainlabel *const name, domainname *const type, domainname *const domain)
1347 {
1348 int i, len;
1349 const mDNSu8 *src = fqdn->c;
1350 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
1351 mDNSu8 *dst;
1352
1353 dst = name->c; // Extract the service name
1354 len = *src;
1355 if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); }
1356 if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
1357 for (i=0; i<=len; i++) *dst++ = *src++;
1358
1359 dst = type->c; // Extract the service type
1360 len = *src;
1361 if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); }
1362 if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
1363 if (src[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); }
1364 for (i=0; i<=len; i++) *dst++ = *src++;
1365
1366 len = *src;
1367 if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
1368 if (!ValidTransportProtocol(src))
1369 { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
1370 for (i=0; i<=len; i++) *dst++ = *src++;
1371 *dst++ = 0; // Put terminator on the end of service type
1372
1373 dst = domain->c; // Extract the service domain
1374 while (*src)
1375 {
1376 len = *src;
1377 if (len >= 0x40)
1378 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
1379 if (src + 1 + len + 1 >= max)
1380 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
1381 for (i=0; i<=len; i++) *dst++ = *src++;
1382 }
1383 *dst++ = 0; // Put the null root label on the end
1384
1385 return(mDNStrue);
1386 }
1387
1388 // Notes on UTF-8:
1389 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1390 // 10xxxxxx is a continuation byte of a multi-byte character
1391 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1392 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1393 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1394 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1395 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1396 //
1397 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1398 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1399 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1400 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1401 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1402
1403 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
1404 {
1405 if (length > max)
1406 {
1407 mDNSu8 c1 = string[max]; // First byte after cut point
1408 mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point
1409 length = max; // Trim length down
1410 while (length > 0)
1411 {
1412 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1413 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1414 // If so, then we continue to chop more bytes until we get to a legal chop point.
1415 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
1416 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
1417 if (!continuation && !secondsurrogate) break;
1418 c2 = c1;
1419 c1 = string[--length];
1420 }
1421 // Having truncated characters off the end of our string, also cut off any residual white space
1422 while (length > 0 && string[length-1] <= ' ') length--;
1423 }
1424 return(length);
1425 }
1426
1427 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1428 // name ends in "-nnn", where n is some decimal number.
1429 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
1430 {
1431 mDNSu16 l = name->c[0];
1432
1433 if (RichText)
1434 {
1435 if (l < 4) return mDNSfalse; // Need at least " (2)"
1436 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
1437 if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
1438 l--;
1439 while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
1440 return (name->c[l] == '(' && name->c[l - 1] == ' ');
1441 }
1442 else
1443 {
1444 if (l < 2) return mDNSfalse; // Need at least "-2"
1445 if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
1446 l--;
1447 while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
1448 return (name->c[l] == '-');
1449 }
1450 }
1451
1452 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1453 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1454 // from the suffix that was removed.
1455 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
1456 {
1457 mDNSu32 val = 0, multiplier = 1;
1458
1459 // Chop closing parentheses from RichText suffix
1460 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1461
1462 // Get any existing numerical suffix off the name
1463 while (mDNSIsDigit(name->c[name->c[0]]))
1464 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
1465
1466 // Chop opening parentheses or dash from suffix
1467 if (RichText)
1468 {
1469 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1470 }
1471 else
1472 {
1473 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1474 }
1475
1476 return(val);
1477 }
1478
1479 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1480 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1481 mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
1482 {
1483 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1484 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1485
1486 // Truncate trailing spaces from RichText names
1487 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1488
1489 while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
1490
1491 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1492
1493 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1494 else { name->c[++name->c[0]] = '-'; }
1495
1496 while (divisor)
1497 {
1498 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1499 val %= divisor;
1500 divisor /= 10;
1501 }
1502
1503 if (RichText) name->c[++name->c[0]] = ')';
1504 }
1505
1506 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1507 {
1508 mDNSu32 val = 0;
1509
1510 if (LabelContainsSuffix(name, RichText))
1511 val = RemoveLabelSuffix(name, RichText);
1512
1513 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1514 // If existing suffix in the range 2-9, increment it.
1515 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1516 // so add a random increment to improve the chances of finding an available name next time.
1517 if (val == 0) val = 2;
1518 else if (val < 10) val++;
1519 else val += 1 + mDNSRandom(99);
1520
1521 AppendLabelSuffix(name, val, RichText);
1522 }
1523
1524 // ***************************************************************************
1525 #if COMPILER_LIKES_PRAGMA_MARK
1526 #pragma mark -
1527 #pragma mark - Resource Record Utility Functions
1528 #endif
1529
1530 // Set up a AuthRecord with sensible default values.
1531 // These defaults may be overwritten with new values before mDNS_Register is called
1532 mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
1533 mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
1534 {
1535 // Don't try to store a TTL bigger than we can represent in platform time units
1536 if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
1537 ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
1538 else if (ttl == 0) // And Zero TTL is illegal
1539 ttl = DefaultTTLforRRType(rrtype);
1540
1541 // Field Group 1: The actual information pertaining to this resource record
1542 rr->resrec.RecordType = RecordType;
1543 rr->resrec.InterfaceID = InterfaceID;
1544 rr->resrec.name = &rr->namestorage;
1545 rr->resrec.rrtype = rrtype;
1546 rr->resrec.rrclass = kDNSClass_IN;
1547 rr->resrec.rroriginalttl = ttl;
1548 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1549 // rr->resrec.rdestimate = set in mDNS_Register_internal
1550 // rr->resrec.rdata = MUST be set by client
1551
1552 if (RDataStorage)
1553 rr->resrec.rdata = RDataStorage;
1554 else
1555 {
1556 rr->resrec.rdata = &rr->rdatastorage;
1557 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
1558 }
1559
1560 // Field Group 2: Persistent metadata for Authoritative Records
1561 rr->Additional1 = mDNSNULL;
1562 rr->Additional2 = mDNSNULL;
1563 rr->DependentOn = mDNSNULL;
1564 rr->RRSet = mDNSNULL;
1565 rr->RecordCallback = Callback;
1566 rr->RecordContext = Context;
1567
1568 rr->AutoTarget = Target_Manual;
1569 rr->AllowRemoteQuery = mDNSfalse;
1570 rr->ForceMCast = mDNSfalse;
1571
1572 rr->WakeUp = zeroOwner;
1573 rr->AddressProxy = zeroAddr;
1574 rr->TimeRcvd = 0;
1575 rr->TimeExpire = 0;
1576
1577 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1578 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1579
1580 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1581 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1582 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1583 rr->state = regState_Zero;
1584 rr->uselease = 0;
1585 rr->expire = 0;
1586 rr->Private = 0;
1587 rr->updateid = zeroID;
1588 rr->zone = rr->resrec.name;
1589 rr->UpdateServer = zeroAddr;
1590 rr->UpdatePort = zeroIPPort;
1591 rr->nta = mDNSNULL;
1592 rr->tcp = mDNSNULL;
1593 rr->OrigRData = 0;
1594 rr->OrigRDLen = 0;
1595 rr->InFlightRData = 0;
1596 rr->InFlightRDLen = 0;
1597 rr->QueuedRData = 0;
1598 rr->QueuedRDLen = 0;
1599
1600 rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
1601 }
1602
1603 mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
1604 const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context)
1605 {
1606 q->InterfaceID = InterfaceID;
1607 q->Target = zeroAddr;
1608 AssignDomainName(&q->qname, name);
1609 q->qtype = qtype;
1610 q->qclass = kDNSClass_IN;
1611 q->LongLived = (qtype == kDNSType_PTR);
1612 q->ExpectUnique = (qtype != kDNSType_PTR);
1613 q->ForceMCast = mDNSfalse;
1614 q->ReturnIntermed = mDNSfalse;
1615 q->QuestionCallback = callback;
1616 q->QuestionContext = context;
1617 }
1618
1619 mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
1620 {
1621 int len = rr->rdlength;
1622 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1623 switch(rr->rrtype)
1624 {
1625 case kDNSType_NS:
1626 case kDNSType_CNAME:
1627 case kDNSType_PTR:
1628 case kDNSType_DNAME: return DomainNameHashValue(&rdb->name);
1629
1630 case kDNSType_SOA: return rdb->soa.serial +
1631 rdb->soa.refresh +
1632 rdb->soa.retry +
1633 rdb->soa.expire +
1634 rdb->soa.min +
1635 DomainNameHashValue(&rdb->soa.mname) +
1636 DomainNameHashValue(&rdb->soa.rname);
1637
1638 case kDNSType_MX:
1639 case kDNSType_AFSDB:
1640 case kDNSType_RT:
1641 case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange);
1642
1643 case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt);
1644
1645 case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400);
1646
1647 case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target);
1648
1649 case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1650
1651 case kDNSType_NSEC: len = sizeof(rdataNSEC); // Use in-memory length of 32, and fall through default checksum computation below
1652
1653 default:
1654 {
1655 mDNSu32 sum = 0;
1656 int i;
1657 for (i=0; i+1 < len; i+=2)
1658 {
1659 sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
1660 sum = (sum<<3) | (sum>>29);
1661 }
1662 if (i < len)
1663 {
1664 sum += ((mDNSu32)(rdb->data[i])) << 8;
1665 }
1666 return(sum);
1667 }
1668 }
1669 }
1670
1671 // r1 has to be a full ResourceRecord including rrtype and rdlength
1672 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1673 mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2)
1674 {
1675 const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data;
1676 const RDataBody2 *const b2 = (RDataBody2 *)r2;
1677 switch(r1->rrtype)
1678 {
1679 case kDNSType_NS:
1680 case kDNSType_CNAME:
1681 case kDNSType_PTR:
1682 case kDNSType_DNAME:return(SameDomainName(&b1->name, &b2->name));
1683
1684 case kDNSType_SOA: return(mDNSBool)( b1->soa.serial == b2->soa.serial &&
1685 b1->soa.refresh == b2->soa.refresh &&
1686 b1->soa.retry == b2->soa.retry &&
1687 b1->soa.expire == b2->soa.expire &&
1688 b1->soa.min == b2->soa.min &&
1689 SameDomainName(&b1->soa.mname, &b2->soa.mname) &&
1690 SameDomainName(&b1->soa.rname, &b2->soa.rname));
1691
1692 case kDNSType_MX:
1693 case kDNSType_AFSDB:
1694 case kDNSType_RT:
1695 case kDNSType_KX: return(mDNSBool)( b1->mx.preference == b2->mx.preference &&
1696 SameDomainName(&b1->mx.exchange, &b2->mx.exchange));
1697
1698 case kDNSType_RP: return(mDNSBool)( SameDomainName(&b1->rp.mbox, &b2->rp.mbox) &&
1699 SameDomainName(&b1->rp.txt, &b2->rp.txt));
1700
1701 case kDNSType_PX: return(mDNSBool)( b1->px.preference == b2->px.preference &&
1702 SameDomainName(&b1->px.map822, &b2->px.map822) &&
1703 SameDomainName(&b1->px.mapx400, &b2->px.mapx400));
1704
1705 case kDNSType_SRV: return(mDNSBool)( b1->srv.priority == b2->srv.priority &&
1706 b1->srv.weight == b2->srv.weight &&
1707 mDNSSameIPPort(b1->srv.port, b2->srv.port) &&
1708 SameDomainName(&b1->srv.target, &b2->srv.target));
1709
1710 case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare
1711
1712 case kDNSType_NSEC: return(mDNSPlatformMemSame(b1->data, b2->data, sizeof(rdataNSEC)));
1713
1714 default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength));
1715 }
1716 }
1717
1718 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1719 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1720 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1721 // because it has to check all the way to the end of the names to be sure.
1722 // In cases where we know in advance that the names match it's especially advantageous to skip the
1723 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1724
1725 mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1726 {
1727 if (rr->InterfaceID &&
1728 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1729 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1730
1731 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1732 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1733
1734 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1735 if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1736 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1737
1738 return(mDNStrue);
1739 }
1740
1741 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1742 {
1743 if (rr->InterfaceID &&
1744 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1745 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1746
1747 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1748 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1749
1750 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1751 if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1752 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1753
1754 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1755 }
1756
1757 mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1758 {
1759 if (rr->InterfaceID &&
1760 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1761 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1762
1763 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1764 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1765
1766 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1767
1768 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1769 }
1770
1771 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1772 {
1773 const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data;
1774 const domainname *const name = estimate ? rr->name : mDNSNULL;
1775 if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1776 else switch (rr->rrtype)
1777 {
1778 case kDNSType_A: return(sizeof(rd->ipv4));
1779
1780 case kDNSType_NS:
1781 case kDNSType_CNAME:
1782 case kDNSType_PTR:
1783 case kDNSType_DNAME:return(CompressedDomainNameLength(&rd->name, name));
1784
1785 case kDNSType_SOA: return(mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
1786 CompressedDomainNameLength(&rd->soa.rname, name) +
1787 5 * sizeof(mDNSOpaque32));
1788
1789 case kDNSType_NULL:
1790 case kDNSType_TSIG:
1791 case kDNSType_TXT:
1792 case kDNSType_X25:
1793 case kDNSType_ISDN:
1794 case kDNSType_LOC:
1795 case kDNSType_DHCID:return(rr->rdlength); // Not self-describing, so have to just trust rdlength
1796
1797 case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
1798
1799 case kDNSType_MX:
1800 case kDNSType_AFSDB:
1801 case kDNSType_RT:
1802 case kDNSType_KX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
1803
1804 case kDNSType_RP: return(mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
1805 CompressedDomainNameLength(&rd->rp.txt, name));
1806
1807 case kDNSType_PX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
1808 CompressedDomainNameLength(&rd->px.mapx400, name));
1809
1810 case kDNSType_AAAA: return(sizeof(rd->ipv6));
1811
1812 case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
1813
1814 case kDNSType_OPT: return(rr->rdlength);
1815
1816 case kDNSType_NSEC: {
1817 int i;
1818 for (i=sizeof(rdataNSEC); i>0; i--) if (rd->nsec.bitmap[i-1]) break;
1819 // For our simplified use of NSEC synthetic records:
1820 // nextname is always the record's own name,
1821 // the block number is always 0,
1822 // the count byte is a value in the range 1-32,
1823 // followed by the 1-32 data bytes
1824 return((estimate ? 1 : DomainNameLength(rr->name)) + 2 + i);
1825 }
1826
1827 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
1828 return(rr->rdlength);
1829 }
1830 }
1831
1832 // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
1833 // to help reduce the risk of bogus malformed data on the network
1834 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1835 {
1836 mDNSu16 len;
1837
1838 switch(rrtype)
1839 {
1840 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1841
1842 case kDNSType_NS: // Same as PTR
1843 case kDNSType_MD: // Same as PTR
1844 case kDNSType_MF: // Same as PTR
1845 case kDNSType_CNAME:// Same as PTR
1846 //case kDNSType_SOA not checked
1847 case kDNSType_MB: // Same as PTR
1848 case kDNSType_MG: // Same as PTR
1849 case kDNSType_MR: // Same as PTR
1850 //case kDNSType_NULL not checked (no specified format, so always valid)
1851 //case kDNSType_WKS not checked
1852 case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
1853 return(len <= MAX_DOMAIN_NAME && rdlength == len);
1854
1855 case kDNSType_HINFO:// Same as TXT (roughly)
1856 case kDNSType_MINFO:// Same as TXT (roughly)
1857 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
1858 {
1859 const mDNSu8 *ptr = rd->u.txt.c;
1860 const mDNSu8 *end = rd->u.txt.c + rdlength;
1861 while (ptr < end) ptr += 1 + ptr[0];
1862 return (ptr == end);
1863 }
1864
1865 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1866
1867 case kDNSType_MX: // Must be at least two-byte preference, plus domainname
1868 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1869 len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
1870 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1871
1872 case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname
1873 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1874 len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
1875 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1876
1877 //case kDNSType_NSEC not checked
1878
1879 default: return(mDNStrue); // Allow all other types without checking
1880 }
1881 }
1882
1883 // ***************************************************************************
1884 #if COMPILER_LIKES_PRAGMA_MARK
1885 #pragma mark -
1886 #pragma mark - DNS Message Creation Functions
1887 #endif
1888
1889 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1890 {
1891 h->id = id;
1892 h->flags = flags;
1893 h->numQuestions = 0;
1894 h->numAnswers = 0;
1895 h->numAuthorities = 0;
1896 h->numAdditionals = 0;
1897 }
1898
1899 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1900 {
1901 const mDNSu8 *result = end - *domname - 1;
1902
1903 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1904
1905 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1906 while (result >= base)
1907 {
1908 // If the length byte and first character of the label match, then check further to see
1909 // if this location in the packet will yield a useful name compression pointer.
1910 if (result[0] == domname[0] && result[1] == domname[1])
1911 {
1912 const mDNSu8 *name = domname;
1913 const mDNSu8 *targ = result;
1914 while (targ + *name < end)
1915 {
1916 // First see if this label matches
1917 int i;
1918 const mDNSu8 *pointertarget;
1919 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
1920 if (i <= *name) break; // If label did not match, bail out
1921 targ += 1 + *name; // Else, did match, so advance target pointer
1922 name += 1 + *name; // and proceed to check next label
1923 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
1924 if (*name == 0) break; // If no more labels to match, we failed, so bail out
1925
1926 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1927 if (targ[0] < 0x40) continue; // If length value, continue to check next label
1928 if (targ[0] < 0xC0) break; // If 40-BF, not valid
1929 if (targ+1 >= end) break; // Second byte not present!
1930 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
1931 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
1932 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1933 targ = pointertarget;
1934 }
1935 }
1936 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1937 }
1938 return(mDNSNULL);
1939 }
1940
1941 // Put a string of dot-separated labels as length-prefixed labels
1942 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1943 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1944 // end points to the end of the message so far
1945 // ptr points to where we want to put the name
1946 // limit points to one byte past the end of the buffer that we must not overrun
1947 // domainname is the name to put
1948 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1949 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1950 {
1951 const mDNSu8 *const base = (const mDNSu8 *)msg;
1952 const mDNSu8 * np = name->c;
1953 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1954 const mDNSu8 * pointer = mDNSNULL;
1955 const mDNSu8 *const searchlimit = ptr;
1956
1957 if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); }
1958
1959 if (!*np) // If just writing one-byte root label, make sure we have space for that
1960 {
1961 if (ptr >= limit) return(mDNSNULL);
1962 }
1963 else // else, loop through writing labels and/or a compression offset
1964 {
1965 do {
1966 if (*np > MAX_DOMAIN_LABEL)
1967 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1968
1969 // This check correctly allows for the final trailing root label:
1970 // e.g.
1971 // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
1972 // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
1973 // We know that max will be at name->c[256]
1974 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1975 // six bytes, then exit the loop, write the final terminating root label, and the domain
1976 // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
1977 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1978 if (np + 1 + *np >= max)
1979 { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); }
1980
1981 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1982 if (pointer) // Use a compression pointer if we can
1983 {
1984 const mDNSu16 offset = (mDNSu16)(pointer - base);
1985 if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up
1986 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1987 *ptr++ = (mDNSu8)( offset & 0xFF);
1988 return(ptr);
1989 }
1990 else // Else copy one label and try again
1991 {
1992 int i;
1993 mDNSu8 len = *np++;
1994 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
1995 if (ptr + 1 + len >= limit) return(mDNSNULL);
1996 *ptr++ = len;
1997 for (i=0; i<len; i++) *ptr++ = *np++;
1998 }
1999 } while (*np); // While we've got characters remaining in the name, continue
2000 }
2001
2002 *ptr++ = 0; // Put the final root label
2003 return(ptr);
2004 }
2005
2006 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
2007 {
2008 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
2009 ptr[1] = (mDNSu8)((val ) & 0xFF);
2010 return ptr + sizeof(mDNSOpaque16);
2011 }
2012
2013 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
2014 {
2015 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
2016 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
2017 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
2018 ptr[3] = (mDNSu8)((val ) & 0xFF);
2019 return ptr + sizeof(mDNSu32);
2020 }
2021
2022 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
2023 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
2024 {
2025 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
2026 switch (rr->rrtype)
2027 {
2028 case kDNSType_A: if (rr->rdlength != 4)
2029 { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); }
2030 if (ptr + 4 > limit) return(mDNSNULL);
2031 *ptr++ = rdb->ipv4.b[0];
2032 *ptr++ = rdb->ipv4.b[1];
2033 *ptr++ = rdb->ipv4.b[2];
2034 *ptr++ = rdb->ipv4.b[3];
2035 return(ptr);
2036
2037 case kDNSType_NS:
2038 case kDNSType_CNAME:
2039 case kDNSType_PTR:
2040 case kDNSType_DNAME:return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name));
2041
2042 case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname);
2043 if (!ptr) return(mDNSNULL);
2044 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname);
2045 if (!ptr || ptr + 20 > limit) return(mDNSNULL);
2046 ptr = putVal32(ptr, rdb->soa.serial);
2047 ptr = putVal32(ptr, rdb->soa.refresh);
2048 ptr = putVal32(ptr, rdb->soa.retry);
2049 ptr = putVal32(ptr, rdb->soa.expire);
2050 ptr = putVal32(ptr, rdb->soa.min);
2051 return(ptr);
2052
2053 case kDNSType_NULL:
2054 case kDNSType_HINFO:
2055 case kDNSType_TSIG:
2056 case kDNSType_TXT:
2057 case kDNSType_X25:
2058 case kDNSType_ISDN:
2059 case kDNSType_LOC:
2060 case kDNSType_DHCID:if (ptr + rr->rdlength > limit) return(mDNSNULL);
2061 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2062 return(ptr + rr->rdlength);
2063
2064 case kDNSType_MX:
2065 case kDNSType_AFSDB:
2066 case kDNSType_RT:
2067 case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL);
2068 ptr = putVal16(ptr, rdb->mx.preference);
2069 return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange));
2070
2071 case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox);
2072 if (!ptr) return(mDNSNULL);
2073 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt);
2074 return(ptr);
2075
2076 case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL);
2077 ptr = putVal16(ptr, rdb->px.preference);
2078 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822);
2079 if (!ptr) return(mDNSNULL);
2080 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400);
2081 return(ptr);
2082
2083 case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6))
2084 { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); }
2085 if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL);
2086 mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6));
2087 return(ptr + sizeof(rdb->ipv6));
2088
2089 case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL);
2090 *ptr++ = (mDNSu8)(rdb->srv.priority >> 8);
2091 *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF);
2092 *ptr++ = (mDNSu8)(rdb->srv.weight >> 8);
2093 *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF);
2094 *ptr++ = rdb->srv.port.b[0];
2095 *ptr++ = rdb->srv.port.b[1];
2096 return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target));
2097
2098 case kDNSType_OPT: {
2099 int len = 0;
2100 const rdataOPT *opt;
2101 const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
2102 for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt);
2103 if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; }
2104
2105 for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
2106 {
2107 const int space = DNSOpt_Data_Space(opt);
2108 ptr = putVal16(ptr, opt->opt);
2109 ptr = putVal16(ptr, space - 4);
2110 switch (opt->opt)
2111 {
2112 case kDNSOpt_LLQ:
2113 ptr = putVal16(ptr, opt->u.llq.vers);
2114 ptr = putVal16(ptr, opt->u.llq.llqOp);
2115 ptr = putVal16(ptr, opt->u.llq.err);
2116 mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id
2117 ptr += 8;
2118 ptr = putVal32(ptr, opt->u.llq.llqlease);
2119 break;
2120 case kDNSOpt_Lease:
2121 ptr = putVal32(ptr, opt->u.updatelease);
2122 break;
2123 case kDNSOpt_Owner:
2124 *ptr++ = opt->u.owner.vers;
2125 *ptr++ = opt->u.owner.seq;
2126 mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier
2127 ptr += 6;
2128 if (space >= DNSOpt_OwnerData_ID_Wake_Space)
2129 {
2130 mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC
2131 ptr += 6;
2132 if (space > DNSOpt_OwnerData_ID_Wake_Space)
2133 {
2134 mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space);
2135 ptr += space - DNSOpt_OwnerData_ID_Wake_Space;
2136 }
2137 }
2138 break;
2139 }
2140 }
2141 return ptr;
2142 }
2143
2144 case kDNSType_NSEC: {
2145 // For our simplified use of NSEC synthetic records:
2146 // nextname is always the record's own name,
2147 // the block number is always 0,
2148 // the count byte is a value in the range 1-32,
2149 // followed by the 1-32 data bytes
2150 int i, j;
2151 for (i=sizeof(rdataNSEC); i>0; i--) if (rdb->nsec.bitmap[i-1]) break;
2152 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2153 if (!ptr) return(mDNSNULL);
2154 if (ptr + 2 + i > limit) return(mDNSNULL);
2155 *ptr++ = 0;
2156 *ptr++ = i;
2157 for (j=0; j<i; j++) *ptr++ = rdb->nsec.bitmap[j];
2158 return ptr;
2159 }
2160
2161 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
2162 if (ptr + rr->rdlength > limit) return(mDNSNULL);
2163 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2164 return(ptr + rr->rdlength);
2165 }
2166 }
2167
2168 #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2169
2170 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
2171 {
2172 mDNSu8 *endofrdata;
2173 mDNSu16 actualLength;
2174 // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2175 const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg;
2176
2177 if (rr->RecordType == kDNSRecordTypeUnregistered)
2178 {
2179 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
2180 return(ptr);
2181 }
2182
2183 if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); }
2184
2185 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2186 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2187 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
2188 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
2189 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
2190 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
2191 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
2192 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
2193 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
2194 ptr[7] = (mDNSu8)( ttl & 0xFF);
2195 endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
2196 if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
2197
2198 // Go back and fill in the actual number of data bytes we wrote
2199 // (actualLength can be less than rdlength when domain name compression is used)
2200 actualLength = (mDNSu16)(endofrdata - ptr - 10);
2201 ptr[8] = (mDNSu8)(actualLength >> 8);
2202 ptr[9] = (mDNSu8)(actualLength & 0xFF);
2203
2204 if (count) (*count)++;
2205 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
2206 return(endofrdata);
2207 }
2208
2209 mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr)
2210 {
2211 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
2212 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2213 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
2214 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
2215 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
2216 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
2217 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
2218 ptr[8] = ptr[9] = 0; // RDATA length is zero
2219 (*count)++;
2220 return(ptr + 10);
2221 }
2222
2223 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
2224 {
2225 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2226 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2227 ptr[0] = (mDNSu8)(rrtype >> 8);
2228 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2229 ptr[2] = (mDNSu8)(rrclass >> 8);
2230 ptr[3] = (mDNSu8)(rrclass & 0xFF);
2231 msg->h.numQuestions++;
2232 return(ptr+4);
2233 }
2234
2235 // for dynamic updates
2236 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
2237 {
2238 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
2239 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
2240 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
2241 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
2242 *ptr++ = zoneClass.b[0];
2243 *ptr++ = zoneClass.b[1];
2244 msg->h.mDNS_numZones++;
2245 return ptr;
2246 }
2247
2248 // for dynamic updates
2249 mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
2250 {
2251 AuthRecord prereq;
2252 mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
2253 AssignDomainName(&prereq.namestorage, name);
2254 prereq.resrec.rrtype = kDNSQType_ANY;
2255 prereq.resrec.rrclass = kDNSClass_NONE;
2256 return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
2257 }
2258
2259 // for dynamic updates
2260 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
2261 {
2262 // deletion: specify record w/ TTL 0, class NONE
2263 const mDNSu16 origclass = rr->rrclass;
2264 rr->rrclass = kDNSClass_NONE;
2265 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
2266 rr->rrclass = origclass;
2267 return ptr;
2268 }
2269
2270 mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype)
2271 {
2272 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
2273 mDNSu16 class = kDNSQClass_ANY;
2274
2275 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2276 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2277 ptr[0] = (mDNSu8)(rrtype >> 8);
2278 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2279 ptr[2] = (mDNSu8)(class >> 8);
2280 ptr[3] = (mDNSu8)(class & 0xFF);
2281 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2282 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2283
2284 msg->h.mDNS_numUpdates++;
2285 return ptr + 10;
2286 }
2287
2288 // for dynamic updates
2289 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
2290 {
2291 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
2292 mDNSu16 class = kDNSQClass_ANY;
2293 mDNSu16 rrtype = kDNSQType_ANY;
2294
2295 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2296 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2297 ptr[0] = (mDNSu8)(rrtype >> 8);
2298 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2299 ptr[2] = (mDNSu8)(class >> 8);
2300 ptr[3] = (mDNSu8)(class & 0xFF);
2301 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2302 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2303
2304 msg->h.mDNS_numUpdates++;
2305 return ptr + 10;
2306 }
2307
2308 // for dynamic updates
2309 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
2310 {
2311 AuthRecord rr;
2312 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
2313 rr.resrec.rrclass = NormalMaxDNSMessageData;
2314 rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2315 rr.resrec.rdestimate = sizeof(rdataOPT);
2316 rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2317 rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2318 end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0);
2319 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
2320 return end;
2321 }
2322
2323 mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo)
2324 {
2325 if (authInfo && authInfo->AutoTunnel)
2326 {
2327 AuthRecord hinfo;
2328 mDNSu8 *h = hinfo.rdatastorage.u.data;
2329 mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
2330 mDNSu8 *newptr;
2331 mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
2332 AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
2333 AppendDomainName (&hinfo.namestorage, &authInfo->domain);
2334 hinfo.resrec.rroriginalttl = 0;
2335 mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]);
2336 h += 1 + (int)h[0];
2337 mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]);
2338 hinfo.resrec.rdlength = len;
2339 hinfo.resrec.rdestimate = len;
2340 newptr = PutResourceRecord(msg, end, &msg->h.numAdditionals, &hinfo.resrec);
2341 return newptr;
2342 }
2343 else
2344 return end;
2345 }
2346
2347 // ***************************************************************************
2348 #if COMPILER_LIKES_PRAGMA_MARK
2349 #pragma mark -
2350 #pragma mark - DNS Message Parsing Functions
2351 #endif
2352
2353 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
2354 {
2355 mDNSu32 sum = 0;
2356 const mDNSu8 *c;
2357
2358 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
2359 {
2360 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
2361 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
2362 sum = (sum<<3) | (sum>>29);
2363 }
2364 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
2365 return(sum);
2366 }
2367
2368 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
2369 {
2370 domainname *target;
2371 if (NewRData)
2372 {
2373 rr->rdata = NewRData;
2374 rr->rdlength = rdlength;
2375 }
2376 // Must not try to get target pointer until after updating rr->rdata
2377 target = GetRRDomainNameTarget(rr);
2378 rr->rdlength = GetRDLength(rr, mDNSfalse);
2379 rr->rdestimate = GetRDLength(rr, mDNStrue);
2380 rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr);
2381 }
2382
2383 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
2384 {
2385 mDNSu16 total = 0;
2386
2387 if (ptr < (mDNSu8*)msg || ptr >= end)
2388 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2389
2390 while (1) // Read sequence of labels
2391 {
2392 const mDNSu8 len = *ptr++; // Read length of this label
2393 if (len == 0) return(ptr); // If length is zero, that means this name is complete
2394 switch (len & 0xC0)
2395 {
2396 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2397 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2398 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
2399 { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
2400 ptr += len;
2401 total += 1 + len;
2402 break;
2403
2404 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
2405 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
2406 case 0xC0: return(ptr+1);
2407 }
2408 }
2409 }
2410
2411 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2412 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
2413 domainname *const name)
2414 {
2415 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
2416 mDNSu8 *np = name->c; // Name pointer
2417 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
2418
2419 if (ptr < (mDNSu8*)msg || ptr >= end)
2420 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2421
2422 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2423
2424 while (1) // Read sequence of labels
2425 {
2426 const mDNSu8 len = *ptr++; // Read length of this label
2427 if (len == 0) break; // If length is zero, that means this name is complete
2428 switch (len & 0xC0)
2429 {
2430 int i;
2431 mDNSu16 offset;
2432
2433 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2434 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2435 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
2436 { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
2437 *np++ = len;
2438 for (i=0; i<len; i++) *np++ = *ptr++;
2439 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2440 break;
2441
2442 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
2443 return(mDNSNULL);
2444
2445 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
2446
2447 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
2448 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
2449 ptr = (mDNSu8 *)msg + offset;
2450 if (ptr < (mDNSu8*)msg || ptr >= end)
2451 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
2452 if (*ptr & 0xC0)
2453 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
2454 break;
2455 }
2456 }
2457
2458 if (nextbyte) return(nextbyte);
2459 else return(ptr);
2460 }
2461
2462 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2463 {
2464 mDNSu16 pktrdlength;
2465
2466 ptr = skipDomainName(msg, ptr, end);
2467 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
2468
2469 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2470 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
2471 ptr += 10;
2472 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2473
2474 return(ptr + pktrdlength);
2475 }
2476
2477 mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
2478 {
2479 mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
2480 *ptr += sizeof(mDNSOpaque16);
2481 return val;
2482 }
2483
2484 mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
2485 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
2486 {
2487 CacheRecord *const rr = &largecr->r;
2488 RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
2489 mDNSu16 pktrdlength;
2490
2491 if (largecr == &m->rec && m->rec.r.resrec.RecordType)
2492 {
2493 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
2494 #if ForceAlerts
2495 *(long*)0 = 0;
2496 #endif
2497 }
2498
2499 rr->next = mDNSNULL;
2500 rr->resrec.name = &largecr->namestorage;
2501
2502 rr->NextInKAList = mDNSNULL;
2503 rr->TimeRcvd = m ? m->timenow : 0;
2504 rr->DelayDelivery = 0;
2505 rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2506 rr->LastUsed = m ? m->timenow : 0;
2507 rr->CRActiveQuestion = mDNSNULL;
2508 rr->UnansweredQueries = 0;
2509 rr->LastUnansweredTime= 0;
2510 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
2511 rr->MPUnansweredQ = 0;
2512 rr->MPLastUnansweredQT= 0;
2513 rr->MPUnansweredKA = 0;
2514 rr->MPExpectingKA = mDNSfalse;
2515 #endif
2516 rr->NextInCFList = mDNSNULL;
2517
2518 rr->resrec.InterfaceID = InterfaceID;
2519 ptr = getDomainName(msg, ptr, end, &largecr->namestorage);
2520 if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
2521
2522 if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2523
2524 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
2525 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
2526 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
2527 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
2528 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
2529 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2530 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2531 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
2532
2533 // If mDNS record has cache-flush bit set, we mark it unique
2534 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2535 // authoritative for the entire RRSet), unless this is a truncated response
2536 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC)))
2537 RecordType |= kDNSRecordTypePacketUniqueMask;
2538 ptr += 10;
2539 if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2540 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
2541
2542 rr->resrec.rdata = (RData*)&rr->smallrdatastorage;
2543 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
2544
2545 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
2546
2547 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2548 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2549 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2550 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2551 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2552 if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2553 rr->resrec.rdlength = 0;
2554 else switch (rr->resrec.rrtype)
2555 {
2556 case kDNSType_A: if (pktrdlength != sizeof(mDNSv4Addr)) return(mDNSNULL);
2557 rdb->ipv4.b[0] = ptr[0];
2558 rdb->ipv4.b[1] = ptr[1];
2559 rdb->ipv4.b[2] = ptr[2];
2560 rdb->ipv4.b[3] = ptr[3];
2561 break;
2562
2563 case kDNSType_NS:
2564 case kDNSType_CNAME:
2565 case kDNSType_PTR:
2566 case kDNSType_DNAME:ptr = getDomainName(msg, ptr, end, &rdb->name);
2567 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
2568 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength);
2569 break;
2570
2571 case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
2572 if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; }
2573 ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
2574 if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; }
2575 if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL; }
2576 rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
2577 rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
2578 rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
2579 rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
2580 rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
2581 break;
2582
2583 case kDNSType_NULL:
2584 case kDNSType_HINFO:
2585 case kDNSType_TSIG:
2586 case kDNSType_TXT:
2587 case kDNSType_X25:
2588 case kDNSType_ISDN:
2589 case kDNSType_LOC:
2590 case kDNSType_DHCID:if (pktrdlength > rr->resrec.rdata->MaxRDLength)
2591 {
2592 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2593 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
2594 return(mDNSNULL);
2595 }
2596 rr->resrec.rdlength = pktrdlength;
2597 mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength);
2598 break;
2599
2600 case kDNSType_MX:
2601 case kDNSType_AFSDB:
2602 case kDNSType_RT:
2603 case kDNSType_KX: if (pktrdlength < 3) return(mDNSNULL); // Preference + domainname
2604 rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2605 ptr = getDomainName(msg, ptr+2, end, &rdb->mx.exchange);
2606 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL); }
2607 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2608 break;
2609
2610 case kDNSType_RP: ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox); // Domainname + domainname
2611 if (!ptr) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL; }
2612 ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
2613 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL; }
2614 break;
2615
2616 case kDNSType_PX: if (pktrdlength < 4) return(mDNSNULL); // Preference + domainname + domainname
2617 rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2618 ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
2619 if (!ptr) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL; }
2620 ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
2621 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL; }
2622 break;
2623
2624 case kDNSType_AAAA: if (pktrdlength != sizeof(mDNSv6Addr)) return(mDNSNULL);
2625 mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
2626 break;
2627
2628 case kDNSType_SRV: if (pktrdlength < 7) return(mDNSNULL); // Priority + weight + port + domainname
2629 rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2630 rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2631 rdb->srv.port.b[0] = ptr[4];
2632 rdb->srv.port.b[1] = ptr[5];
2633 ptr = getDomainName(msg, ptr+6, end, &rdb->srv.target);
2634 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
2635 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2636 break;
2637
2638 case kDNSType_OPT: {
2639 rdataOPT *opt = rr->resrec.rdata->u.opt;
2640 rr->resrec.rdlength = 0;
2641 while (ptr < end && (mDNSu8 *)(opt+1) < &rr->resrec.rdata->u.data[MaximumRDSize])
2642 {
2643 if (ptr + 4 > end) { LogMsg("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); return(mDNSNULL); }
2644 opt->opt = getVal16(&ptr);
2645 opt->optlen = getVal16(&ptr);
2646 if (!ValidDNSOpt(opt)) { LogMsg("GetLargeResourceRecord: opt %d optlen %d wrong", opt->opt, opt->optlen); return(mDNSNULL); }
2647 if (ptr + opt->optlen > end) { LogMsg("GetLargeResourceRecord: ptr + opt->optlen > end"); return(mDNSNULL); }
2648 switch(opt->opt)
2649 {
2650 case kDNSOpt_LLQ:
2651 opt->u.llq.vers = getVal16(&ptr);
2652 opt->u.llq.llqOp = getVal16(&ptr);
2653 opt->u.llq.err = getVal16(&ptr);
2654 mDNSPlatformMemCopy(opt->u.llq.id.b, ptr, 8);
2655 ptr += 8;
2656 opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
2657 if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
2658 opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
2659 ptr += sizeof(mDNSOpaque32);
2660 break;
2661 case kDNSOpt_Lease:
2662 opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
2663 if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
2664 opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
2665 ptr += sizeof(mDNSs32);
2666 break;
2667 case kDNSOpt_Owner:
2668 opt->u.owner.vers = ptr[0];
2669 opt->u.owner.seq = ptr[1];
2670 mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address
2671 mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address
2672 opt->u.owner.password = zeroEthAddr;
2673 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
2674 {
2675 mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address
2676 if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
2677 mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
2678 }
2679 ptr += opt->optlen;
2680 break;
2681 }
2682 opt++; // increment pointer into rdatabody
2683 }
2684 rr->resrec.rdlength = (mDNSu8*)opt - rr->resrec.rdata->u.data;
2685 if (ptr != end) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL); }
2686 break;
2687 }
2688
2689 case kDNSType_NSEC: {
2690 unsigned int i, j;
2691 domainname d;
2692 ptr = getDomainName(msg, ptr, end, &d); // Ignored for our simplified use of NSEC synthetic records
2693 if (!ptr) { debugf("GetLargeResourceRecord: Malformed NSEC nextname"); return mDNSNULL; }
2694 if (*ptr++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); return mDNSNULL; }
2695 i = *ptr++;
2696 if (i < 1 || i > sizeof(rdataNSEC)) { debugf("GetLargeResourceRecord: invalid block length %d", i); return mDNSNULL; }
2697 mDNSPlatformMemZero(rdb->nsec.bitmap, sizeof(rdb->nsec.bitmap));
2698 for (j=0; j<i; j++) rdb->nsec.bitmap[j] = *ptr++;
2699 if (ptr != end) { LogMsg("GetLargeResourceRecord: Malformed NSEC"); return(mDNSNULL); }
2700 break;
2701 }
2702
2703 default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
2704 {
2705 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2706 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
2707 return(mDNSNULL);
2708 }
2709 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2710 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
2711 // Note: Just because we don't understand the record type, that doesn't
2712 // mean we fail. The DNS protocol specifies rdlength, so we can
2713 // safely skip over unknown records and ignore them.
2714 // We also grab a binary copy of the rdata anyway, since the caller
2715 // might know how to interpret it even if we don't.
2716 rr->resrec.rdlength = pktrdlength;
2717 mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength);
2718 break;
2719 }
2720
2721 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
2722 SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us
2723
2724 // Success! Now fill in RecordType to show this record contains valid data
2725 rr->resrec.RecordType = RecordType;
2726 return(end);
2727 }
2728
2729 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2730 {
2731 ptr = skipDomainName(msg, ptr, end);
2732 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
2733 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
2734 return(ptr+4);
2735 }
2736
2737 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
2738 DNSQuestion *question)
2739 {
2740 mDNSPlatformMemZero(question, sizeof(*question));
2741 question->InterfaceID = InterfaceID;
2742 if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
2743 ptr = getDomainName(msg, ptr, end, &question->qname);
2744 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
2745 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
2746
2747 question->qnamehash = DomainNameHashValue(&question->qname);
2748 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
2749 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
2750 return(ptr+4);
2751 }
2752
2753 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
2754 {
2755 int i;
2756 const mDNSu8 *ptr = msg->data;
2757 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
2758 return(ptr);
2759 }
2760
2761 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
2762 {
2763 int i;
2764 const mDNSu8 *ptr = LocateAnswers(msg, end);
2765 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
2766 return(ptr);
2767 }
2768
2769 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
2770 {
2771 int i;
2772 const mDNSu8 *ptr = LocateAuthorities(msg, end);
2773 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
2774 return (ptr);
2775 }
2776
2777 mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize)
2778 {
2779 int i;
2780 const mDNSu8 *ptr = LocateAdditionals(msg, end);
2781
2782 // Locate the OPT record.
2783 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2784 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2785 // but not necessarily the *last* entry in the Additional Section.
2786 for (i = 0; ptr && i < msg->h.numAdditionals; i++)
2787 {
2788 if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data
2789 ptr[0] == 0 && // Name must be root label
2790 ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT
2791 ptr[2] == (kDNSType_OPT & 0xFF) &&
2792 ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize)
2793 return(ptr);
2794 else
2795 ptr = skipResourceRecord(msg, ptr, end);
2796 }
2797 return(mDNSNULL);
2798 }
2799
2800 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2801 // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
2802 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2803 // The code that currently calls this assumes there's only one, instead of iterating through the set
2804 mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
2805 {
2806 const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space);
2807 if (ptr)
2808 {
2809 ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
2810 if (ptr) return(&m->rec.r.resrec.rdata->u.opt[0]);
2811 }
2812 return(mDNSNULL);
2813 }
2814
2815 // Get the lease life of records in a dynamic update
2816 // returns 0 on error or if no lease present
2817 mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end)
2818 {
2819 mDNSu32 result = 0;
2820 const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
2821 if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
2822 if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease)
2823 result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease;
2824 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
2825 return(result);
2826 }
2827
2828 mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
2829 {
2830 int i;
2831 LogMsg("%2d %s", count, label);
2832 for (i = 0; i < count && ptr; i++)
2833 {
2834 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2835 // but since it's only used for debugging (and probably only on OS X, not on
2836 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2837 LargeCacheRecord largecr;
2838 ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
2839 if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
2840 }
2841 if (!ptr) LogMsg("ERROR: Premature end of packet data");
2842 return(ptr);
2843 }
2844
2845 #define DNS_OP_Name(X) ( \
2846 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2847 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2848 (X) == kDNSFlag0_OP_Status ? "Status " : \
2849 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2850 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2851 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2852
2853 #define DNS_RC_Name(X) ( \
2854 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2855 (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
2856 (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
2857 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2858 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2859 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2860 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2861 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2862 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2863 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2864 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2865
2866 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2867 mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
2868 const mDNSAddr *srcaddr, mDNSIPPort srcport,
2869 const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
2870 {
2871 mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
2872 const mDNSu8 *ptr = msg->data;
2873 int i;
2874 DNSQuestion q;
2875 char tbuffer[64], sbuffer[64], dbuffer[64] = "";
2876 if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0;
2877 else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0;
2878 if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0;
2879 else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
2880 if (dstaddr || !mDNSIPPortIsZero(dstport))
2881 dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0;
2882
2883 LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
2884 tbuffer, transport,
2885 DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
2886 msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
2887 msg->h.flags.b[0], msg->h.flags.b[1],
2888 DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
2889 msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
2890 msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "",
2891 msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "",
2892 msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "",
2893 msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "",
2894 msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "",
2895 msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "",
2896 mDNSVal16(msg->h.id),
2897 end - msg->data,
2898 sbuffer, mDNSVal16(srcport), dbuffer,
2899 (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
2900 );
2901
2902 LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
2903 for (i = 0; i < msg->h.numQuestions && ptr; i++)
2904 {
2905 ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
2906 if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
2907 }
2908 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers");
2909 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities");
2910 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
2911 LogMsg("--------------");
2912 }
2913
2914 // ***************************************************************************
2915 #if COMPILER_LIKES_PRAGMA_MARK
2916 #pragma mark -
2917 #pragma mark - Packet Sending Functions
2918 #endif
2919
2920 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2921 struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
2922
2923 struct UDPSocket_struct
2924 {
2925 mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
2926 };
2927
2928 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2929 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2930 mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
2931 mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo)
2932 {
2933 mStatus status = mStatus_NoError;
2934 const mDNSu16 numAdditionals = msg->h.numAdditionals;
2935 mDNSu8 *newend;
2936
2937 // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
2938 if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
2939 {
2940 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
2941 return mStatus_BadParamErr;
2942 }
2943
2944 newend = putHINFO(m, msg, end, authInfo);
2945 if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed"); // Not fatal
2946 else end = newend;
2947
2948 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2949 SwapDNSHeaderBytes(msg);
2950
2951 if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order
2952 if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
2953 else
2954 {
2955 // Send the packet on the wire
2956 if (!sock)
2957 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport);
2958 else
2959 {
2960 mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
2961 mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
2962 long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets
2963 if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; }
2964 else
2965 {
2966 nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
2967 if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; }
2968 }
2969 }
2970 }
2971
2972 // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
2973 SwapDNSHeaderBytes(msg);
2974
2975 // Dump the packet with the HINFO and TSIG
2976 if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
2977 DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end);
2978
2979 // put the number of additionals back the way it was
2980 msg->h.numAdditionals = numAdditionals;
2981
2982 return(status);
2983 }
2984
2985 // ***************************************************************************
2986 #if COMPILER_LIKES_PRAGMA_MARK
2987 #pragma mark -
2988 #pragma mark - RR List Management & Task Management
2989 #endif
2990
2991 mDNSexport void mDNS_Lock_(mDNS *const m)
2992 {
2993 // MUST grab the platform lock FIRST!
2994 mDNSPlatformLock(m);
2995
2996 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2997 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2998 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2999 // If mDNS_busy != mDNS_reentrancy that's a bad sign
3000 #if ForceAlerts
3001 if (m->mDNS_busy != m->mDNS_reentrancy)
3002 {
3003 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
3004 *(long*)0 = 0;
3005 }
3006 #endif
3007
3008 // If this is an initial entry into the mDNSCore code, set m->timenow
3009 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3010 if (m->mDNS_busy == 0)
3011 {
3012 if (m->timenow)
3013 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m));
3014 m->timenow = mDNS_TimeNow_NoLock(m);
3015 if (m->timenow == 0) m->timenow = 1;
3016 }
3017 else if (m->timenow == 0)
3018 {
3019 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
3020 m->timenow = mDNS_TimeNow_NoLock(m);
3021 if (m->timenow == 0) m->timenow = 1;
3022 }
3023
3024 if (m->timenow_last - m->timenow > 0)
3025 {
3026 m->timenow_adjust += m->timenow_last - m->timenow;
3027 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust);
3028 m->timenow = m->timenow_last;
3029 }
3030 m->timenow_last = m->timenow;
3031
3032 // Increment mDNS_busy so we'll recognise re-entrant calls
3033 m->mDNS_busy++;
3034 }
3035
3036 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
3037 {
3038 mDNSs32 e = m->timenow + 0x78000000;
3039 if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
3040 if (m->NewQuestions)
3041 {
3042 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
3043 else return(m->timenow);
3044 }
3045 if (m->NewLocalOnlyQuestions) return(m->timenow);
3046 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) return(m->timenow);
3047 if (m->SPSProxyListChanged) return(m->timenow);
3048 #ifndef UNICAST_DISABLED
3049 if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent;
3050 if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp;
3051 #endif
3052 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
3053 if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS;
3054 // NextScheduledSPRetry only valid when DelaySleep not set
3055 if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
3056 if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
3057
3058 if (m->SuppressSending)
3059 {
3060 if (e - m->SuppressSending > 0) e = m->SuppressSending;
3061 }
3062 else
3063 {
3064 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
3065 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
3066 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
3067 }
3068
3069 return(e);
3070 }
3071
3072 mDNSexport void ShowTaskSchedulingError(mDNS *const m)
3073 {
3074 mDNS_Lock(m);
3075
3076 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
3077
3078 // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3079
3080 if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
3081 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
3082 m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
3083
3084 if (m->NewLocalOnlyQuestions)
3085 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3086 m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
3087
3088 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords))
3089 LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, m->NewLocalRecords));
3090
3091 if (m->timenow - m->NextScheduledEvent >= 0)
3092 LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent);
3093 if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
3094 LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending);
3095 if (m->timenow - m->NextCacheCheck >= 0)
3096 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck);
3097 if (m->timenow - m->NextScheduledQuery >= 0)
3098 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery);
3099 if (m->timenow - m->NextScheduledProbe >= 0)
3100 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe);
3101 if (m->timenow - m->NextScheduledResponse >= 0)
3102 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
3103 if (m->timenow - m->NextScheduledNATOp >= 0)
3104 LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp);
3105 if (m->timenow - m->NextScheduledSPS >= 0)
3106 LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS);
3107 if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
3108 LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry);
3109 if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
3110 LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep);
3111 #ifndef UNICAST_DISABLED
3112 if (m->timenow - m->NextuDNSEvent >= 0)
3113 LogMsg("Task Scheduling Error: NextuDNSEvent %d", m->timenow - m->NextuDNSEvent);
3114 #endif
3115
3116 mDNS_Unlock(m);
3117 }
3118
3119 mDNSexport void mDNS_Unlock_(mDNS *const m)
3120 {
3121 // Decrement mDNS_busy
3122 m->mDNS_busy--;
3123
3124 // Check for locking failures
3125 #if ForceAlerts
3126 if (m->mDNS_busy != m->mDNS_reentrancy)
3127 {
3128 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
3129 *(long*)0 = 0;
3130 }
3131 #endif
3132
3133 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
3134 if (m->mDNS_busy == 0)
3135 {
3136 m->NextScheduledEvent = GetNextScheduledEvent(m);
3137 if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
3138 m->timenow = 0;
3139 }
3140
3141 // MUST release the platform lock LAST!
3142 mDNSPlatformUnlock(m);
3143 }
3144
3145 // ***************************************************************************
3146 #if COMPILER_LIKES_PRAGMA_MARK
3147 #pragma mark -
3148 #pragma mark - Specialized mDNS version of vsnprintf
3149 #endif
3150
3151 static const struct mDNSprintf_format
3152 {
3153 unsigned leftJustify : 1;
3154 unsigned forceSign : 1;
3155 unsigned zeroPad : 1;
3156 unsigned havePrecision : 1;
3157 unsigned hSize : 1;
3158 unsigned lSize : 1;
3159 char altForm;
3160 char sign; // +, - or space
3161 unsigned int fieldWidth;
3162 unsigned int precision;
3163 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3164
3165 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
3166 {
3167 mDNSu32 nwritten = 0;
3168 int c;
3169 if (buflen == 0) return(0);
3170 buflen--; // Pre-reserve one space in the buffer for the terminating null
3171 if (buflen == 0) goto exit;
3172
3173 for (c = *fmt; c != 0; c = *++fmt)
3174 {
3175 if (c != '%')
3176 {
3177 *sbuffer++ = (char)c;
3178 if (++nwritten >= buflen) goto exit;
3179 }
3180 else
3181 {
3182 unsigned int i=0, j;
3183 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
3184 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
3185 // The size needs to be enough for a 256-byte domain name plus some error text.
3186 #define mDNS_VACB_Size 300
3187 char mDNS_VACB[mDNS_VACB_Size];
3188 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
3189 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
3190 char *s = mDNS_VACB_Lim, *digits;
3191 struct mDNSprintf_format F = mDNSprintf_format_default;
3192
3193 while (1) // decode flags
3194 {
3195 c = *++fmt;
3196 if (c == '-') F.leftJustify = 1;
3197 else if (c == '+') F.forceSign = 1;
3198 else if (c == ' ') F.sign = ' ';
3199 else if (c == '#') F.altForm++;
3200 else if (c == '0') F.zeroPad = 1;
3201 else break;
3202 }
3203
3204 if (c == '*') // decode field width
3205 {
3206 int f = va_arg(arg, int);
3207 if (f < 0) { f = -f; F.leftJustify = 1; }
3208 F.fieldWidth = (unsigned int)f;
3209 c = *++fmt;
3210 }
3211 else
3212 {
3213 for (; c >= '0' && c <= '9'; c = *++fmt)
3214 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
3215 }
3216
3217 if (c == '.') // decode precision
3218 {
3219 if ((c = *++fmt) == '*')
3220 { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
3221 else for (; c >= '0' && c <= '9'; c = *++fmt)
3222 F.precision = (10 * F.precision) + (c - '0');
3223 F.havePrecision = 1;
3224 }
3225
3226 if (F.leftJustify) F.zeroPad = 0;
3227
3228 conv:
3229 switch (c) // perform appropriate conversion
3230 {
3231 unsigned long n;
3232 case 'h' : F.hSize = 1; c = *++fmt; goto conv;
3233 case 'l' : // fall through
3234 case 'L' : F.lSize = 1; c = *++fmt; goto conv;
3235 case 'd' :
3236 case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long);
3237 else n = (unsigned long)va_arg(arg, int);
3238 if (F.hSize) n = (short) n;
3239 if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
3240 else if (F.forceSign) F.sign = '+';
3241 goto decimal;
3242 case 'u' : if (F.lSize) n = va_arg(arg, unsigned long);
3243 else n = va_arg(arg, unsigned int);
3244 if (F.hSize) n = (unsigned short) n;
3245 F.sign = 0;
3246 goto decimal;
3247 decimal: if (!F.havePrecision)
3248 {
3249 if (F.zeroPad)
3250 {
3251 F.precision = F.fieldWidth;
3252 if (F.sign) --F.precision;
3253 }
3254 if (F.precision < 1) F.precision = 1;
3255 }
3256 if (F.precision > mDNS_VACB_Size - 1)
3257 F.precision = mDNS_VACB_Size - 1;
3258 for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
3259 for (; i < F.precision; i++) *--s = '0';
3260 if (F.sign) { *--s = F.sign; i++; }
3261 break;
3262
3263 case 'o' : if (F.lSize) n = va_arg(arg, unsigned long);
3264 else n = va_arg(arg, unsigned int);
3265 if (F.hSize) n = (unsigned short) n;
3266 if (!F.havePrecision)
3267 {
3268 if (F.zeroPad) F.precision = F.fieldWidth;
3269 if (F.precision < 1) F.precision = 1;
3270 }
3271 if (F.precision > mDNS_VACB_Size - 1)
3272 F.precision = mDNS_VACB_Size - 1;
3273 for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
3274 if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
3275 for (; i < F.precision; i++) *--s = '0';
3276 break;
3277
3278 case 'a' : {
3279 unsigned char *a = va_arg(arg, unsigned char *);
3280 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
3281 else
3282 {
3283 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
3284 if (F.altForm)
3285 {
3286 mDNSAddr *ip = (mDNSAddr*)a;
3287 switch (ip->type)
3288 {
3289 case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
3290 case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
3291 default: F.precision = 0; break;
3292 }
3293 }
3294 if (F.altForm && !F.precision)
3295 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
3296 else switch (F.precision)
3297 {
3298 case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
3299 a[0], a[1], a[2], a[3]); break;
3300 case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
3301 a[0], a[1], a[2], a[3], a[4], a[5]); break;
3302 case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
3303 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
3304 a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
3305 a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
3306 default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
3307 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
3308 }
3309 }
3310 }
3311 break;
3312
3313 case 'p' : F.havePrecision = F.lSize = 1;
3314 F.precision = 8;
3315 case 'X' : digits = "0123456789ABCDEF";
3316 goto hexadecimal;
3317 case 'x' : digits = "0123456789abcdef";
3318 hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
3319 else n = va_arg(arg, unsigned int);
3320 if (F.hSize) n = (unsigned short) n;
3321 if (!F.havePrecision)
3322 {
3323 if (F.zeroPad)
3324 {
3325 F.precision = F.fieldWidth;
3326 if (F.altForm) F.precision -= 2;
3327 }
3328 if (F.precision < 1) F.precision = 1;
3329 }
3330 if (F.precision > mDNS_VACB_Size - 1)
3331 F.precision = mDNS_VACB_Size - 1;
3332 for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
3333 for (; i < F.precision; i++) *--s = '0';
3334 if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
3335 break;
3336
3337 case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
3338
3339 case 's' : s = va_arg(arg, char *);
3340 if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
3341 else switch (F.altForm)
3342 {
3343 case 0: i=0;
3344 if (!F.havePrecision) // C string
3345 while (s[i]) i++;
3346 else
3347 {
3348 while ((i < F.precision) && s[i]) i++;
3349 // Make sure we don't truncate in the middle of a UTF-8 character
3350 // If last character we got was any kind of UTF-8 multi-byte character,
3351 // then see if we have to back up.
3352 // This is not as easy as the similar checks below, because
3353 // here we can't assume it's safe to examine the *next* byte, so we
3354 // have to confine ourselves to working only backwards in the string.
3355 j = i; // Record where we got to
3356 // Now, back up until we find first non-continuation-char
3357 while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
3358 // Now s[i-1] is the first non-continuation-char
3359 // and (j-i) is the number of continuation-chars we found
3360 if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char
3361 {
3362 i--; // Tentatively eliminate this start-char as well
3363 // Now (j-i) is the number of characters we're considering eliminating.
3364 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
3365 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
3366 // (with sign extension) then the result has to be 0xFE.
3367 // If this is right, then we reinstate the tentatively eliminated bytes.
3368 if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
3369 }
3370 }
3371 break;
3372 case 1: i = (unsigned char) *s++; break; // Pascal string
3373 case 2: { // DNS label-sequence name
3374 unsigned char *a = (unsigned char *)s;
3375 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
3376 if (*a == 0) *s++ = '.'; // Special case for root DNS name
3377 while (*a)
3378 {
3379 char buf[63*4+1];
3380 if (*a > 63)
3381 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
3382 if (s + *a >= &mDNS_VACB[254])
3383 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
3384 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3385 // so it's clear what's a literal dot and what's a label separator
3386 ConvertDomainLabelToCString((domainlabel*)a, buf);
3387 s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
3388 a += 1 + *a;
3389 }
3390 i = (mDNSu32)(s - mDNS_VACB);
3391 s = mDNS_VACB; // Reset s back to the start of the buffer
3392 break;
3393 }
3394 }
3395 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3396 if (F.havePrecision && i > F.precision)
3397 { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
3398 break;
3399
3400 case 'n' : s = va_arg(arg, char *);
3401 if (F.hSize) * (short *) s = (short)nwritten;
3402 else if (F.lSize) * (long *) s = (long)nwritten;
3403 else * (int *) s = (int)nwritten;
3404 continue;
3405
3406 default: s = mDNS_VACB;
3407 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
3408
3409 case '%' : *sbuffer++ = (char)c;
3410 if (++nwritten >= buflen) goto exit;
3411 break;
3412 }
3413
3414 if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
3415 do {
3416 *sbuffer++ = ' ';
3417 if (++nwritten >= buflen) goto exit;
3418 } while (i < --F.fieldWidth);
3419
3420 // Make sure we don't truncate in the middle of a UTF-8 character.
3421 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3422 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3423 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3424 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3425 if (i > buflen - nwritten)
3426 { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
3427 for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
3428 nwritten += i;
3429 if (nwritten >= buflen) goto exit;
3430
3431 for (; i < F.fieldWidth; i++) // Pad on the right
3432 {
3433 *sbuffer++ = ' ';
3434 if (++nwritten >= buflen) goto exit;
3435 }
3436 }
3437 }
3438 exit:
3439 *sbuffer++ = 0;
3440 return(nwritten);
3441 }
3442
3443 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
3444 {
3445 mDNSu32 length;
3446
3447 va_list ptr;
3448 va_start(ptr,fmt);
3449 length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
3450 va_end(ptr);
3451
3452 return(length);
3453 }