1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
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)
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
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.
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
39 Revision 1.248 2009/04/23 22:11:16 cheshire
40 Minor cleanup in debugging checks in GetLargeResourceRecord
42 Revision 1.247 2009/04/21 23:36:25 cheshire
43 <rdar://problem/6814427> Remove unused kDNSType_MAC
45 Revision 1.246 2009/04/21 01:00:19 cheshire
46 Fixed typo in previous checkin
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
52 Revision 1.244 2009/04/11 00:19:30 jessic2
53 <rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically
55 Revision 1.243 2009/04/01 17:50:10 mcguire
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
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
64 Revision 1.240 2009/03/18 20:41:04 cheshire
65 Added definition of the all-ones mDNSOpaque16 ID
67 Revision 1.239 2009/03/06 23:51:50 mcguire
68 Fix broken build by defining DiscardPort
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>
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)
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
80 Revision 1.235 2009/02/07 05:55:44 cheshire
81 Only pay attention to m->DelaySleep when it's nonzero
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
87 Revision 1.233 2009/01/30 23:50:31 cheshire
88 Added LastLabel() routine to get the last label of a domainname
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
93 Revision 1.231 2008/12/12 01:24:06 cheshire
94 Updated GetNextScheduledEvent() to pay attention to m->SPSProxyListChanged
96 Revision 1.230 2008/12/10 01:55:54 cheshire
97 Renamed "Max" macro to avoid conflict with another "Max" macro on ARMv5
99 Revision 1.229 2008/11/27 01:28:45 cheshire
100 For display purposes, show sleep sequence number as unsigned
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
106 Revision 1.227 2008/11/26 20:28:05 cheshire
107 Added new SSHPort constant
109 Revision 1.226 2008/11/16 16:55:51 cheshire
110 Updated debugging messages
112 Revision 1.225 2008/11/14 21:56:31 cheshire
113 Moved debugging routine ShowTaskSchedulingError() from daemon.c into DNSCommon.c
115 Revision 1.224 2008/11/14 02:20:03 cheshire
116 Include m->NextScheduledSPS in task scheduling calculations
118 Revision 1.223 2008/11/14 01:19:03 cheshire
119 Initialize TimeRcvd and TimeExpire fields in AuthRecord_struct
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
125 Revision 1.221 2008/11/13 19:06:02 cheshire
126 Added code to put, get, and display rdataOPT properly
128 Revision 1.220 2008/11/06 01:08:11 mcguire
129 Fix compiler warning about discarding const
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
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
138 Revision 1.217 2008/11/04 22:13:43 cheshire
139 Made RDataBody parameter to GetRRDisplayString_rdb "const"
141 Revision 1.216 2008/11/04 20:06:19 cheshire
142 <rdar://problem/6186231> Change MAX_DOMAIN_NAME to 256
144 Revision 1.215 2008/10/23 23:54:35 cheshire
145 Added missing "const" in declaration
147 Revision 1.214 2008/10/23 22:25:55 cheshire
148 Renamed field "id" to more descriptive "updateid"
150 Revision 1.213 2008/10/22 01:01:52 cheshire
151 Added onesEthAddr constant, used for sending ARP broadcasts
153 Revision 1.212 2008/10/14 21:52:18 cheshire
154 Added support for putting/getting/printing kDNSType_MAC
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
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
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
167 Revision 1.208 2008/09/23 02:33:56 cheshire
168 <rdar://problem/4738033> uDNS: Should not compress SRV rdata in uDNS packets
170 Revision 1.207 2008/09/23 02:30:07 cheshire
171 Get rid of PutResourceRecordCappedTTL()
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)
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
179 Revision 1.204 2008/08/29 19:03:05 cheshire
180 <rdar://problem/6185645> Off-by-one error in putDomainNameAsLabels()
182 Revision 1.203 2008/08/13 00:47:53 mcguire
183 Handle failures when packet logging
185 Revision 1.202 2008/08/13 00:32:48 mcguire
186 refactor to use SwapDNSHeaderBytes instead of swapping manually
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
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
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
198 Revision 1.198 2008/03/07 23:29:24 cheshire
199 Fixed cosmetic byte order display issue in DumpPacket output
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
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
210 Revision 1.195 2008/03/05 19:06:30 mcguire
211 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
214 Revision 1.194 2008/03/05 00:26:06 cheshire
215 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
217 Revision 1.193 2007/12/17 23:42:36 cheshire
218 Added comments about DNSDigest_SignMessage()
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
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
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.
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).
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
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
245 Revision 1.186 2007/11/15 22:52:29 cheshire
246 <rdar://problem/5589039> ERROR: mDNSPlatformWriteTCP - send Broken pipe
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
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
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)
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
262 Revision 1.181 2007/10/01 18:36:53 cheshire
263 Yet another fix to finally get the DumpPacket RCODE display right
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
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
271 Revision 1.178 2007/09/27 21:11:44 cheshire
272 Fixed spelling mistake: ROCDE -> RCODE
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
277 Revision 1.176 2007/09/27 17:53:37 cheshire
278 Add display of RCODE and flags in DumpPacket output
280 Revision 1.175 2007/09/26 22:26:40 cheshire
281 Also show DNS query/response ID in DumpPacket output
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
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
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
295 Revision 1.171 2007/09/21 21:12:36 cheshire
296 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
298 Revision 1.170 2007/09/07 21:16:58 cheshire
299 Add new symbol "NATPMPAnnouncementPort" (5350)
301 Revision 1.169 2007/08/30 00:31:20 cheshire
302 Improve "locking failure" debugging messages to show function name using __func__ macro
304 Revision 1.168 2007/08/28 23:58:42 cheshire
305 Rename HostTarget -> AutoTarget
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
310 Revision 1.166 2007/08/01 16:09:13 cheshire
311 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
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
317 Revision 1.164 2007/07/27 20:48:43 cheshire
318 In DumpRecords(), include record TTL in output
320 Revision 1.163 2007/07/16 20:10:11 vazquez
321 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
322 Added SSDP port number
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
328 Revision 1.161 2007/07/06 18:56:26 cheshire
329 Check m->NextScheduledNATOp in GetNextScheduledEvent()
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)
334 Revision 1.159 2007/06/28 21:17:17 cheshire
335 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
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
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
344 Revision 1.156 2007/05/15 00:29:00 cheshire
345 Print «ZERO ADDRESS» for %#a with a zero mDNSAddr
347 Revision 1.155 2007/05/07 22:07:47 cheshire
348 <rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types
350 Revision 1.154 2007/05/04 20:19:53 cheshire
351 Improve DumpPacket() output
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
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.)
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
365 Revision 1.150 2007/04/24 00:17:33 cheshire
366 Made LocateLLQOptData guard against packets with bogus numAdditionals value
368 Revision 1.149 2007/04/23 21:43:00 cheshire
369 Remove debugging check
371 Revision 1.148 2007/04/23 04:55:29 cheshire
372 Add some defensive null pointer checks
374 Revision 1.147 2007/04/22 20:18:10 cheshire
375 Add comment about mDNSRandom()
377 Revision 1.146 2007/04/22 06:02:02 cheshire
378 <rdar://problem/4615977> Query should immediately return failure when no server
380 Revision 1.145 2007/04/20 21:17:24 cheshire
381 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
383 Revision 1.144 2007/04/19 18:02:43 cheshire
384 <rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit
386 Revision 1.143 2007/04/16 21:53:49 cheshire
387 Improve display of negative cache entries
389 Revision 1.142 2007/04/05 22:55:35 cheshire
390 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
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
396 Revision 1.140 2007/04/03 19:37:58 cheshire
397 Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918()
399 Revision 1.139 2007/04/03 19:18:39 cheshire
400 Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly
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
405 Revision 1.137 2007/03/28 20:59:26 cheshire
406 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
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
411 Revision 1.135 2007/03/28 01:20:05 cheshire
412 <rdar://problem/4883206> Improve/create logging for secure browse
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)
418 Revision 1.133 2007/03/26 22:55:45 cheshire
419 Add OPT and TSIG to list of types DNSTypeName() knows about
421 Revision 1.132 2007/03/22 18:31:48 cheshire
422 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
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
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
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
436 Revision 1.128 2007/03/21 00:30:02 cheshire
437 <rdar://problem/4789455> Multiple errors in DNameList-related code
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"
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
445 Revision 1.125 2007/03/07 00:08:58 cheshire
446 <rdar://problem/4347550> Don't allow hyphens at start of service type
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
451 Revision 1.123 2007/01/10 22:45:51 cheshire
452 Cast static strings to "(const domainname*)", not "(domainname*)"
454 Revision 1.122 2007/01/06 00:47:35 cheshire
455 Improve GetRRDisplayString to indicate when record has zero-length rdata
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)
461 Revision 1.120 2007/01/05 05:23:00 cheshire
462 Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd)
464 Revision 1.119 2007/01/05 04:30:16 cheshire
465 Change a couple of "(domainname *)" casts to "(const domainname *)"
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
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
475 Revision 1.116 2006/12/21 00:04:07 cheshire
476 To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord()
478 Revision 1.115 2006/12/20 04:07:34 cheshire
479 Remove uDNS_info substructure from AuthRecord_struct
481 Revision 1.114 2006/12/19 22:40:04 cheshire
482 Fix compiler warnings
484 Revision 1.113 2006/12/19 02:21:08 cheshire
485 Delete spurious spaces
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.
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).
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
504 Revision 1.109 2006/11/10 00:54:14 cheshire
505 <rdar://problem/4816598> Changing case of Computer Name doesn't work
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
510 Revision 1.107 2006/09/15 21:20:14 cheshire
511 Remove uDNS_info substructure from mDNS_struct
513 Revision 1.106 2006/08/14 23:24:22 cheshire
514 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
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
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"
524 Revision 1.103 2006/06/29 07:42:14 cheshire
525 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
527 Revision 1.102 2006/06/22 19:49:11 cheshire
528 Added (commented out) definitions for the LLMNR UDP port and multicast addresses
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
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
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)
541 Revision 1.98 2006/03/19 17:00:58 cheshire
542 Define symbol MaxMsg instead of using hard-coded constant value '80'
544 Revision 1.97 2006/03/18 21:47:56 cheshire
545 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
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
551 Revision 1.95 2006/03/08 22:43:11 cheshire
552 Use "localdomain" symbol instead of literal string
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()
558 Revision 1.93 2006/03/02 20:30:47 cheshire
559 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
563 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
564 #define mDNS_InstantiateInlines 1
565 #include "DNSCommon.h"
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)
578 // ***************************************************************************
579 #if COMPILER_LIKES_PRAGMA_MARK
580 #pragma mark - Program Constants
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 } };
592 mDNSexport
const OwnerOptData zeroOwner
= { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
594 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
595 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
596 mDNSexport
const mDNSInterfaceID mDNSInterface_Unicast
= (mDNSInterfaceID
)2;
598 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
599 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
600 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
601 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
602 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
603 // with Microsoft's LLMNR client code.
605 #define DiscardPortAsNumber 9
606 #define SSHPortAsNumber 22
607 #define UnicastDNSPortAsNumber 53
608 #define SSDPPortAsNumber 1900
609 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
610 #define NATPMPAnnouncementPortAsNumber 5350
611 #define NATPMPPortAsNumber 5351
612 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
613 #define MulticastDNSPortAsNumber 5353
614 #define LoopbackIPCPortAsNumber 5354
615 //#define MulticastDNSPortAsNumber 5355 // LLMNR
616 #define PrivateDNSPortAsNumber 5533
618 mDNSexport
const mDNSIPPort DiscardPort
= { { DiscardPortAsNumber
>> 8, DiscardPortAsNumber
& 0xFF } };
619 mDNSexport
const mDNSIPPort SSHPort
= { { SSHPortAsNumber
>> 8, SSHPortAsNumber
& 0xFF } };
620 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
621 mDNSexport
const mDNSIPPort SSDPPort
= { { SSDPPortAsNumber
>> 8, SSDPPortAsNumber
& 0xFF } };
622 mDNSexport
const mDNSIPPort NSIPCPort
= { { NSIPCPortAsNumber
>> 8, NSIPCPortAsNumber
& 0xFF } };
623 mDNSexport
const mDNSIPPort NATPMPAnnouncementPort
= { { NATPMPAnnouncementPortAsNumber
>> 8, NATPMPAnnouncementPortAsNumber
& 0xFF } };
624 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
625 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
626 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
627 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
628 mDNSexport
const mDNSIPPort PrivateDNSPort
= { { PrivateDNSPortAsNumber
>> 8, PrivateDNSPortAsNumber
& 0xFF } };
630 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
631 mDNSexport
const mDNSv4Addr AllSystemsMcast
= { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements
632 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
633 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
634 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
635 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
637 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
638 mDNSexport
const mDNSOpaque16 onesID
= { { 255, 255 } };
639 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
640 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
641 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
642 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
643 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
645 mDNSexport
const mDNSOpaque64 zeroOpaque64
= { { 0 } };
647 // ***************************************************************************
648 #if COMPILER_LIKES_PRAGMA_MARK
650 #pragma mark - General Utility Functions
653 // return true for RFC1918 private addresses
654 mDNSexport mDNSBool
mDNSv4AddrIsRFC1918(mDNSv4Addr
*addr
)
656 return ((addr
->b
[0] == 10) || // 10/8 prefix
657 (addr
->b
[0] == 172 && (addr
->b
[1] & 0xF0) == 16) || // 172.16/12
658 (addr
->b
[0] == 192 && addr
->b
[1] == 168)); // 192.168/16
661 mDNSexport NetworkInterfaceInfo
*GetFirstActiveInterface(NetworkInterfaceInfo
*intf
)
663 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
667 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
669 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
670 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
673 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
675 mDNSu32 slot
, used
= 0;
677 const CacheRecord
*rr
;
678 FORALL_CACHERECORDS(slot
, cg
, rr
)
679 if (rr
->resrec
.InterfaceID
== id
) used
++;
683 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
687 case kDNSType_A
: return("Addr");
688 case kDNSType_NS
: return("NS");
689 case kDNSType_CNAME
:return("CNAME");
690 case kDNSType_SOA
: return("SOA");
691 case kDNSType_NULL
: return("NULL");
692 case kDNSType_PTR
: return("PTR");
693 case kDNSType_HINFO
:return("HINFO");
694 case kDNSType_TXT
: return("TXT");
695 case kDNSType_AAAA
: return("AAAA");
696 case kDNSType_SRV
: return("SRV");
697 case kDNSType_OPT
: return("OPT");
698 case kDNSType_NSEC
: return("NSEC");
699 case kDNSType_TSIG
: return("TSIG");
700 case kDNSQType_ANY
: return("ANY");
702 static char buffer
[16];
703 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
709 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
710 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
711 // long as this routine is only used for debugging messages, it probably isn't a big problem.
712 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*const rr
, const RDataBody
*const rd1
, char *const buffer
)
714 const RDataBody2
*const rd
= (RDataBody2
*)rd1
;
715 #define RemSpc (MaxMsg-1-length)
717 mDNSu32 length
= mDNS_snprintf(buffer
, MaxMsg
-1, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
718 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) return(buffer
);
719 if (!rr
->rdlength
) { mDNS_snprintf(buffer
+length
, RemSpc
, "<< ZERO RDATA LENGTH >>"); return(buffer
); }
723 case kDNSType_A
: mDNS_snprintf(buffer
+length
, RemSpc
, "%.4a", &rd
->ipv4
); break;
725 case kDNSType_NS
: // Same as PTR
726 case kDNSType_CNAME
:// Same as PTR
727 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, RemSpc
, "%##s", rd
->name
.c
); break;
729 case kDNSType_SOA
: mDNS_snprintf(buffer
+length
, RemSpc
, "%##s %##s %d %d %d %d %d",
730 rd
->soa
.mname
.c
, rd
->soa
.rname
.c
,
731 rd
->soa
.serial
, rd
->soa
.refresh
, rd
->soa
.retry
, rd
->soa
.expire
, rd
->soa
.min
);
734 case kDNSType_HINFO
:// Display this the same as TXT (show all constituent strings)
736 const mDNSu8
*t
= rd
->txt
.c
;
737 while (t
< rd
->txt
.c
+ rr
->rdlength
)
739 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "%s%#s", t
> rd
->txt
.c
? "¦" : "", t
);
744 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, RemSpc
, "%.16a", &rd
->ipv6
); break;
745 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, RemSpc
, "%u %u %u %##s",
746 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
750 const rdataOPT
*const end
= (const rdataOPT
*)&rd
->data
[rr
->rdlength
];
751 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "Max %d", rr
->rrclass
);
752 for (opt
= &rd
->opt
[0]; opt
< end
; opt
++)
757 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Vers %d", opt
->u
.llq
.vers
);
758 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Op %d", opt
->u
.llq
.llqOp
);
759 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Err/Port %d", opt
->u
.llq
.err
);
760 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " ID %08X%08X", opt
->u
.llq
.id
.l
[0], opt
->u
.llq
.id
.l
[1]);
761 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Lease %d", opt
->u
.llq
.llqlease
);
764 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Lease %d", opt
->u
.updatelease
);
767 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Vers %d", opt
->u
.owner
.vers
);
768 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Seq %3d", (mDNSu8
)opt
->u
.owner
.seq
); // Display as unsigned
769 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " MAC %.6a", opt
->u
.owner
.HMAC
.b
);
770 if (opt
->optlen
>= DNSOpt_OwnerData_ID_Wake_Space
-4)
772 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " I-MAC %.6a", opt
->u
.owner
.IMAC
.b
);
773 if (opt
->optlen
> DNSOpt_OwnerData_ID_Wake_Space
-4)
774 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Password %.6a", opt
->u
.owner
.password
.b
);
778 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Unknown %d", opt
->opt
);
785 case kDNSType_NSEC
: {
787 for (i
=0; i
<255; i
++)
788 if (rd
->nsec
.bitmap
[i
>>3] & (128 >> (i
&7)))
789 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "%s ", DNSTypeName(i
));
793 default: mDNS_snprintf(buffer
+length
, RemSpc
, "RDLen %d: %s", rr
->rdlength
, rd
->data
);
794 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
795 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
= '.';
801 // See comments in mDNSEmbeddedAPI.h
802 #if _PLATFORM_HAS_STRONG_PRNG_
803 #define mDNSRandomNumber mDNSPlatformRandomNumber
805 mDNSlocal mDNSu32
mDNSRandomFromSeed(mDNSu32 seed
)
807 return seed
* 21 + 1;
810 mDNSlocal mDNSu32
mDNSMixRandomSeed(mDNSu32 seed
, mDNSu8 iteration
)
812 return iteration
? mDNSMixRandomSeed(mDNSRandomFromSeed(seed
), --iteration
) : seed
;
815 mDNSlocal mDNSu32
mDNSRandomNumber()
817 static mDNSBool seeded
= mDNSfalse
;
818 static mDNSu32 seed
= 0;
821 seed
= mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
824 return (seed
= mDNSRandomFromSeed(seed
));
826 #endif // ! _PLATFORM_HAS_STRONG_PRNG_
828 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
) // Returns pseudo-random result from zero to max inclusive
833 while (mask
< max
) mask
= (mask
<< 1) | 1;
835 do ret
= mDNSRandomNumber() & mask
;
841 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
843 if (ip1
->type
== ip2
->type
)
847 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
848 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
849 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
855 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
859 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip
->ip
.v4
, AllDNSLinkGroup_v4
.ip
.v4
));
860 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip
->ip
.v6
, AllDNSLinkGroup_v6
.ip
.v6
));
861 default: return(mDNSfalse
);
865 // ***************************************************************************
866 #if COMPILER_LIKES_PRAGMA_MARK
868 #pragma mark - Domain Name Utility Functions
871 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
874 const int len
= *a
++;
876 if (len
> MAX_DOMAIN_LABEL
)
877 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
879 if (len
!= *b
++) return(mDNSfalse
);
880 for (i
=0; i
<len
; i
++)
884 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
885 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
886 if (ac
!= bc
) return(mDNSfalse
);
891 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
893 const mDNSu8
* a
= d1
->c
;
894 const mDNSu8
* b
= d2
->c
;
895 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
899 if (a
+ 1 + *a
>= max
)
900 { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse
); }
901 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
909 mDNSexport mDNSBool
SameDomainNameCS(const domainname
*const d1
, const domainname
*const d2
)
911 mDNSu16 l1
= DomainNameLength(d1
);
912 mDNSu16 l2
= DomainNameLength(d2
);
913 return(l1
<= MAX_DOMAIN_NAME
&& l1
== l2
&& mDNSPlatformMemSame(d1
, d2
, l1
));
916 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
918 // Domains that are defined to be resolved via link-local multicast are:
919 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
920 static const domainname
*nL
= (const domainname
*)"\x5" "local";
921 static const domainname
*nR
= (const domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
922 static const domainname
*n8
= (const domainname
*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
923 static const domainname
*n9
= (const domainname
*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
924 static const domainname
*nA
= (const domainname
*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
925 static const domainname
*nB
= (const domainname
*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
927 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
; // Top-level domain, second-level domain, etc.
928 d1
= d2
= d3
= d4
= d5
= mDNSNULL
;
931 d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
932 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
935 if (d1
&& SameDomainName(d1
, nL
)) return(mDNStrue
);
936 if (d4
&& SameDomainName(d4
, nR
)) return(mDNStrue
);
937 if (d5
&& SameDomainName(d5
, n8
)) return(mDNStrue
);
938 if (d5
&& SameDomainName(d5
, n9
)) return(mDNStrue
);
939 if (d5
&& SameDomainName(d5
, nA
)) return(mDNStrue
);
940 if (d5
&& SameDomainName(d5
, nB
)) return(mDNStrue
);
944 mDNSexport
const mDNSu8
*LastLabel(const domainname
*d
)
946 const mDNSu8
*p
= d
->c
;
950 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
955 // Returns length of a domain name INCLUDING the byte for the final null label
956 // e.g. for the root label "." it returns one
957 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
958 // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
959 // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
960 mDNSexport mDNSu16
DomainNameLengthLimit(const domainname
*const name
, const mDNSu8
*limit
)
962 const mDNSu8
*src
= name
->c
;
963 while (src
< limit
&& *src
<= MAX_DOMAIN_LABEL
)
965 if (*src
== 0) return((mDNSu16
)(src
- name
->c
+ 1));
968 return(MAX_DOMAIN_NAME
+1);
971 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
972 // for the final null label, e.g. for the root label "." it returns one.
973 // E.g. for the FQDN "foo.com." it returns 9
974 // (length, three data bytes, length, three more data bytes, final zero).
975 // In the case where a parent domain name is provided, and the given name is a child
976 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
977 // of the child name, plus TWO bytes for the compression pointer.
978 // E.g. for the name "foo.com." with parent "com.", it returns 6
979 // (length, three data bytes, two-byte compression pointer).
980 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
982 const mDNSu8
*src
= name
->c
;
983 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
986 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
987 if (parent
&& SameDomainName((const domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
989 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
991 return((mDNSu16
)(src
- name
->c
+ 1));
994 // CountLabels() returns number of labels in name, excluding final root label
995 // (e.g. for "apple.com." CountLabels returns 2.)
996 mDNSexport
int CountLabels(const domainname
*d
)
1000 for (ptr
= d
->c
; *ptr
; ptr
= ptr
+ ptr
[0] + 1) count
++;
1004 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
1005 // returning a pointer to the suffix with 'skip' labels removed.
1006 mDNSexport
const domainname
*SkipLeadingLabels(const domainname
*d
, int skip
)
1008 while (skip
> 0 && d
->c
[0]) { d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]); skip
--; }
1012 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
1013 // The C string contains the label as-is, with no escaping, etc.
1014 // Any dots in the name are literal dots, not label separators
1015 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
1016 // in the domainname bufer (i.e. the next byte after the terminating zero).
1017 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1018 // AppendLiteralLabelString returns mDNSNULL.
1019 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
1021 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1022 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1023 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
1024 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
1025 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
1027 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
1028 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
1029 *ptr
++ = 0; // Put the null root label on the end
1030 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
1031 else return(ptr
); // Success: return new value of ptr
1034 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
1035 // The C string is in conventional DNS syntax:
1036 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1037 // If successful, AppendDNSNameString returns a pointer to the next unused byte
1038 // in the domainname bufer (i.e. the next byte after the terminating zero).
1039 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1040 // AppendDNSNameString returns mDNSNULL.
1041 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
1043 const char *cstr
= cstring
;
1044 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1045 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1046 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
1048 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
1049 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
1050 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
1052 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
1053 if (c
== '\\') // If escape character, check next character
1055 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
1056 if (mDNSIsDigit(cstr
[-1]) && mDNSIsDigit(cstr
[0]) && mDNSIsDigit(cstr
[1]))
1057 { // If three decimal digits,
1058 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
1059 int v1
= cstr
[ 0] - '0';
1060 int v2
= cstr
[ 1] - '0';
1061 int val
= v0
* 100 + v1
* 10 + v2
;
1062 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
1065 *ptr
++ = c
; // Write the character
1067 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
1068 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
1070 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
1073 *ptr
++ = 0; // Put the null root label on the end
1074 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
1075 else return(ptr
); // Success: return new value of ptr
1078 // AppendDomainLabel appends a single label to a name.
1079 // If successful, AppendDomainLabel returns a pointer to the next unused byte
1080 // in the domainname bufer (i.e. the next byte after the terminating zero).
1081 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1082 // AppendDomainLabel returns mDNSNULL.
1083 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
1086 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
1088 // Check label is legal
1089 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
1091 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
1092 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
1094 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
1095 *ptr
++ = 0; // Put the null root label on the end
1099 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
1101 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1102 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1103 const mDNSu8
* src
= append
->c
;
1107 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
1108 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
1109 *ptr
= 0; // Put the null root label on the end
1115 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
1116 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
1117 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
1118 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
1119 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
1120 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
1121 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
1123 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
1124 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
1125 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
1126 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
1127 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
1130 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
1131 // The C string is in conventional DNS syntax:
1132 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1133 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
1134 // in the domainname bufer (i.e. the next byte after the terminating zero).
1135 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1136 // MakeDomainNameFromDNSNameString returns mDNSNULL.
1137 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
1139 name
->c
[0] = 0; // Make an empty domain name
1140 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
1143 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
1145 const mDNSu8
* src
= label
->c
; // Domain label we're reading
1146 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
1147 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
1148 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
1149 while (src
< end
) // While we have characters in the label
1154 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
1155 *ptr
++ = esc
; // Output escape character
1156 else if (c
<= ' ') // If non-printing ascii,
1157 { // Output decimal escape sequence
1159 *ptr
++ = (char) ('0' + (c
/ 100) );
1160 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
1161 c
= (mDNSu8
)('0' + (c
) % 10);
1164 *ptr
++ = (char)c
; // Copy the character
1166 *ptr
= 0; // Null-terminate the string
1167 return(ptr
); // and return
1170 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
1171 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
1173 const mDNSu8
*src
= name
->c
; // Domain name we're reading
1174 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1176 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
1178 while (*src
) // While more characters in the domain name
1180 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
1181 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
1182 if (!ptr
) return(mDNSNULL
);
1184 *ptr
++ = '.'; // Write the dot after the label
1187 *ptr
++ = 0; // Null-terminate the string
1188 return(ptr
); // and return
1192 // Host names must start with a letter, end with a letter or digit,
1193 // and have as interior characters only letters, digits, and hyphen.
1194 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
1196 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
1198 const mDNSu8
* src
= &UTF8Name
[1];
1199 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
1200 mDNSu8
* ptr
= &hostlabel
->c
[1];
1201 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
1204 // Delete apostrophes from source name
1205 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
1206 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
1207 { src
+= 3; continue; } // Unicode curly apostrophe
1210 if (mDNSValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
1211 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
1215 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
1216 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
1219 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
1220 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
1221 ((X)[4] | 0x20) == 'p')
1223 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
1224 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
1227 mDNSu8
*dst
= fqdn
->c
;
1229 const char *errormsg
;
1230 #if APPLE_OSX_mDNSResponder
1231 mDNSBool loggedUnderscore
= mDNSfalse
;
1232 static char typeBuf
[MAX_ESCAPED_DOMAIN_NAME
];
1235 // In the case where there is no name (and ONLY in that case),
1236 // a single-label subtype is allowed as the first label of a three-part "type"
1239 const mDNSu8
*s0
= type
->c
;
1240 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
1242 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
1243 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1245 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
1246 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
1248 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
1249 src
= s0
; // Copy the first label
1251 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
1252 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
1253 type
= (const domainname
*)s1
;
1255 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1256 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1257 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1258 if (SameDomainName((domainname
*)s0
, (const domainname
*)"\x09_services\x07_dns-sd\x04_udp"))
1259 dst
-= sizeof(SubTypeLabel
);
1265 if (name
&& name
->c
[0])
1267 src
= name
->c
; // Put the service name into the domain name
1269 if (len
>= 0x40) { errormsg
= "Service instance name too long"; goto fail
; }
1270 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1273 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1275 src
= type
->c
; // Put the service type into the domain name
1277 if (len
< 2 || len
> 15)
1279 LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-14 characters. "
1280 "See <http://www.dns-sd.org/ServiceTypes.html>", name
->c
, type
->c
, domain
->c
);
1281 #if APPLE_OSX_mDNSResponder
1282 ConvertDomainNameToCString(type
, typeBuf
);
1283 mDNSASLLog(mDNSNULL
, "serviceType.nameTooLong", "noop", typeBuf
, "");
1286 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
))) return(mDNSNULL
);
1287 if (src
[1] != '_') { errormsg
= "Application protocol name must begin with underscore"; goto fail
; }
1288 for (i
=2; i
<=len
; i
++)
1290 // Letters and digits are allowed anywhere
1291 if (mDNSIsLetter(src
[i
]) || mDNSIsDigit(src
[i
])) continue;
1292 // Hyphens are only allowed as interior characters
1293 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1294 // with the same rule as hyphens
1295 if ((src
[i
] == '-' || src
[i
] == '_') && i
> 2 && i
< len
)
1297 #if APPLE_OSX_mDNSResponder
1298 if (src
[i
] == '_' && loggedUnderscore
== mDNSfalse
)
1300 ConvertDomainNameToCString(type
, typeBuf
);
1301 mDNSASLLog(mDNSNULL
, "serviceType.nameWithUnderscore", "noop", typeBuf
, "");
1302 loggedUnderscore
= mDNStrue
;
1307 errormsg
= "Application protocol name must contain only letters, digits, and hyphens";
1308 #if APPLE_OSX_mDNSResponder
1310 ConvertDomainNameToCString(type
, typeBuf
);
1311 mDNSASLLog(mDNSNULL
, "serviceType.nameWithIllegalCharacters", "noop", typeBuf
, "");
1316 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1319 if (!ValidTransportProtocol(src
)) { errormsg
= "Transport protocol name must be _udp or _tcp"; goto fail
; }
1320 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1322 if (*src
) { errormsg
= "Service type must have only two labels"; goto fail
; }
1325 if (!domain
->c
[0]) { errormsg
= "Service domain must be non-empty"; goto fail
; }
1326 if (SameDomainName(domain
, (const domainname
*)"\x05" "local" "\x04" "arpa"))
1327 { errormsg
= "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
1328 dst
= AppendDomainName(fqdn
, domain
);
1329 if (!dst
) { errormsg
= "Service domain too long"; goto fail
; }
1333 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
1337 // A service name has the form: instance.application-protocol.transport-protocol.domain
1338 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1339 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1340 // However, if the given FQDN doesn't contain at least three labels,
1341 // DeconstructServiceName will reject it and return mDNSfalse.
1342 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
1343 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
1346 const mDNSu8
*src
= fqdn
->c
;
1347 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
1350 dst
= name
->c
; // Extract the service name
1352 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
1353 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
1354 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1356 dst
= type
->c
; // Extract the service type
1358 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
1359 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
1360 if (src
[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse
); }
1361 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1364 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
1365 if (!ValidTransportProtocol(src
))
1366 { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse
); }
1367 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1368 *dst
++ = 0; // Put terminator on the end of service type
1370 dst
= domain
->c
; // Extract the service domain
1375 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
1376 if (src
+ 1 + len
+ 1 >= max
)
1377 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
1378 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1380 *dst
++ = 0; // Put the null root label on the end
1386 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1387 // 10xxxxxx is a continuation byte of a multi-byte character
1388 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1389 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1390 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1391 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1392 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1394 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1395 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1396 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1397 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1398 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1400 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
1404 mDNSu8 c1
= string
[max
]; // First byte after cut point
1405 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : (mDNSu8
)0xB0; // Second byte after cut point
1406 length
= max
; // Trim length down
1409 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1410 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1411 // If so, then we continue to chop more bytes until we get to a legal chop point.
1412 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1413 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1414 if (!continuation
&& !secondsurrogate
) break;
1416 c1
= string
[--length
];
1418 // Having truncated characters off the end of our string, also cut off any residual white space
1419 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1424 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1425 // name ends in "-nnn", where n is some decimal number.
1426 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1428 mDNSu16 l
= name
->c
[0];
1432 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1433 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1434 if (!mDNSIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1436 while (l
> 2 && mDNSIsDigit(name
->c
[l
])) l
--; // Strip off digits
1437 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1441 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1442 if (!mDNSIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1444 while (l
> 2 && mDNSIsDigit(name
->c
[l
])) l
--; // Strip off digits
1445 return (name
->c
[l
] == '-');
1449 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1450 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1451 // from the suffix that was removed.
1452 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1454 mDNSu32 val
= 0, multiplier
= 1;
1456 // Chop closing parentheses from RichText suffix
1457 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1459 // Get any existing numerical suffix off the name
1460 while (mDNSIsDigit(name
->c
[name
->c
[0]]))
1461 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1463 // Chop opening parentheses or dash from suffix
1466 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1470 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1476 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1477 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1478 mDNSexport
void AppendLabelSuffix(domainlabel
*const name
, mDNSu32 val
, const mDNSBool RichText
)
1480 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1481 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1483 // Truncate trailing spaces from RichText names
1484 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1486 while (divisor
< 0xFFFFFFFFUL
/10 && val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1488 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1490 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1491 else { name
->c
[++name
->c
[0]] = '-'; }
1495 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1500 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1503 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1507 if (LabelContainsSuffix(name
, RichText
))
1508 val
= RemoveLabelSuffix(name
, RichText
);
1510 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1511 // If existing suffix in the range 2-9, increment it.
1512 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1513 // so add a random increment to improve the chances of finding an available name next time.
1514 if (val
== 0) val
= 2;
1515 else if (val
< 10) val
++;
1516 else val
+= 1 + mDNSRandom(99);
1518 AppendLabelSuffix(name
, val
, RichText
);
1521 // ***************************************************************************
1522 #if COMPILER_LIKES_PRAGMA_MARK
1524 #pragma mark - Resource Record Utility Functions
1527 // Set up a AuthRecord with sensible default values.
1528 // These defaults may be overwritten with new values before mDNS_Register is called
1529 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
1530 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
1532 // Don't try to store a TTL bigger than we can represent in platform time units
1533 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
1534 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
1535 else if (ttl
== 0) // And Zero TTL is illegal
1536 ttl
= DefaultTTLforRRType(rrtype
);
1538 // Field Group 1: The actual information pertaining to this resource record
1539 rr
->resrec
.RecordType
= RecordType
;
1540 rr
->resrec
.InterfaceID
= InterfaceID
;
1541 rr
->resrec
.name
= &rr
->namestorage
;
1542 rr
->resrec
.rrtype
= rrtype
;
1543 rr
->resrec
.rrclass
= kDNSClass_IN
;
1544 rr
->resrec
.rroriginalttl
= ttl
;
1545 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1546 // rr->resrec.rdestimate = set in mDNS_Register_internal
1547 // rr->resrec.rdata = MUST be set by client
1550 rr
->resrec
.rdata
= RDataStorage
;
1553 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1554 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1557 // Field Group 2: Persistent metadata for Authoritative Records
1558 rr
->Additional1
= mDNSNULL
;
1559 rr
->Additional2
= mDNSNULL
;
1560 rr
->DependentOn
= mDNSNULL
;
1561 rr
->RRSet
= mDNSNULL
;
1562 rr
->RecordCallback
= Callback
;
1563 rr
->RecordContext
= Context
;
1565 rr
->AutoTarget
= Target_Manual
;
1566 rr
->AllowRemoteQuery
= mDNSfalse
;
1567 rr
->ForceMCast
= mDNSfalse
;
1569 rr
->WakeUp
= zeroOwner
;
1570 rr
->AddressProxy
= zeroAddr
;
1574 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1575 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1577 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1578 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1579 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1580 rr
->state
= regState_Zero
;
1584 rr
->updateid
= zeroID
;
1585 rr
->zone
= rr
->resrec
.name
;
1586 rr
->UpdateServer
= zeroAddr
;
1587 rr
->UpdatePort
= zeroIPPort
;
1592 rr
->InFlightRData
= 0;
1593 rr
->InFlightRDLen
= 0;
1594 rr
->QueuedRData
= 0;
1595 rr
->QueuedRDLen
= 0;
1597 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
1600 mDNSexport
void mDNS_SetupQuestion(DNSQuestion
*const q
, const mDNSInterfaceID InterfaceID
, const domainname
*const name
,
1601 const mDNSu16 qtype
, mDNSQuestionCallback
*const callback
, void *const context
)
1603 q
->InterfaceID
= InterfaceID
;
1604 q
->Target
= zeroAddr
;
1605 AssignDomainName(&q
->qname
, name
);
1607 q
->qclass
= kDNSClass_IN
;
1608 q
->LongLived
= (qtype
== kDNSType_PTR
);
1609 q
->ExpectUnique
= (qtype
!= kDNSType_PTR
);
1610 q
->ForceMCast
= mDNSfalse
;
1611 q
->ReturnIntermed
= mDNSfalse
;
1612 q
->QuestionCallback
= callback
;
1613 q
->QuestionContext
= context
;
1616 mDNSexport mDNSu32
RDataHashValue(const ResourceRecord
*const rr
)
1618 int len
= rr
->rdlength
;
1619 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
1623 case kDNSType_CNAME
:
1625 case kDNSType_DNAME
: return DomainNameHashValue(&rdb
->name
);
1627 case kDNSType_SOA
: return rdb
->soa
.serial
+
1632 DomainNameHashValue(&rdb
->soa
.mname
) +
1633 DomainNameHashValue(&rdb
->soa
.rname
);
1636 case kDNSType_AFSDB
:
1638 case kDNSType_KX
: return DomainNameHashValue(&rdb
->mx
.exchange
);
1640 case kDNSType_RP
: return DomainNameHashValue(&rdb
->rp
.mbox
) + DomainNameHashValue(&rdb
->rp
.txt
);
1642 case kDNSType_PX
: return DomainNameHashValue(&rdb
->px
.map822
) + DomainNameHashValue(&rdb
->px
.mapx400
);
1644 case kDNSType_SRV
: return DomainNameHashValue(&rdb
->srv
.target
);
1646 case kDNSType_OPT
: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1648 case kDNSType_NSEC
: len
= sizeof(rdataNSEC
); // Use in-memory length of 32, and fall through default checksum computation below
1654 for (i
=0; i
+1 < len
; i
+=2)
1656 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1657 sum
= (sum
<<3) | (sum
>>29);
1661 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1668 // r1 has to be a full ResourceRecord including rrtype and rdlength
1669 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1670 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1672 const RDataBody2
*const b1
= (RDataBody2
*)r1
->rdata
->u
.data
;
1673 const RDataBody2
*const b2
= (RDataBody2
*)r2
;
1677 case kDNSType_CNAME
:
1679 case kDNSType_DNAME
:return(SameDomainName(&b1
->name
, &b2
->name
));
1681 case kDNSType_SOA
: return(mDNSBool
)( b1
->soa
.serial
== b2
->soa
.serial
&&
1682 b1
->soa
.refresh
== b2
->soa
.refresh
&&
1683 b1
->soa
.retry
== b2
->soa
.retry
&&
1684 b1
->soa
.expire
== b2
->soa
.expire
&&
1685 b1
->soa
.min
== b2
->soa
.min
&&
1686 SameDomainName(&b1
->soa
.mname
, &b2
->soa
.mname
) &&
1687 SameDomainName(&b1
->soa
.rname
, &b2
->soa
.rname
));
1690 case kDNSType_AFSDB
:
1692 case kDNSType_KX
: return(mDNSBool
)( b1
->mx
.preference
== b2
->mx
.preference
&&
1693 SameDomainName(&b1
->mx
.exchange
, &b2
->mx
.exchange
));
1695 case kDNSType_RP
: return(mDNSBool
)( SameDomainName(&b1
->rp
.mbox
, &b2
->rp
.mbox
) &&
1696 SameDomainName(&b1
->rp
.txt
, &b2
->rp
.txt
));
1698 case kDNSType_PX
: return(mDNSBool
)( b1
->px
.preference
== b2
->px
.preference
&&
1699 SameDomainName(&b1
->px
.map822
, &b2
->px
.map822
) &&
1700 SameDomainName(&b1
->px
.mapx400
, &b2
->px
.mapx400
));
1702 case kDNSType_SRV
: return(mDNSBool
)( b1
->srv
.priority
== b2
->srv
.priority
&&
1703 b1
->srv
.weight
== b2
->srv
.weight
&&
1704 mDNSSameIPPort(b1
->srv
.port
, b2
->srv
.port
) &&
1705 SameDomainName(&b1
->srv
.target
, &b2
->srv
.target
));
1707 case kDNSType_OPT
: return mDNSfalse
; // OPT is a pseudo-RR container structure; makes no sense to compare
1709 case kDNSType_NSEC
: return(mDNSPlatformMemSame(b1
->data
, b2
->data
, sizeof(rdataNSEC
)));
1711 default: return(mDNSPlatformMemSame(b1
->data
, b2
->data
, r1
->rdlength
));
1715 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1716 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1717 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1718 // because it has to check all the way to the end of the names to be sure.
1719 // In cases where we know in advance that the names match it's especially advantageous to skip the
1720 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1722 mDNSexport mDNSBool
SameNameRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1724 if (rr
->InterfaceID
&&
1725 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1726 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1728 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1729 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1731 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1732 if (!RRTypeAnswersQuestionType(rr
,q
->qtype
)) return(mDNSfalse
);
1733 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1738 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1740 if (rr
->InterfaceID
&&
1741 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1742 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1744 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1745 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1747 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1748 if (!RRTypeAnswersQuestionType(rr
,q
->qtype
)) return(mDNSfalse
);
1749 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1751 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1754 mDNSexport mDNSBool
AnyTypeRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1756 if (rr
->InterfaceID
&&
1757 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1758 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1760 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1761 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1763 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1765 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1768 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1770 const RDataBody2
*const rd
= (RDataBody2
*)rr
->rdata
->u
.data
;
1771 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1772 if (rr
->rrclass
== kDNSQClass_ANY
) return(rr
->rdlength
); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1773 else switch (rr
->rrtype
)
1775 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1778 case kDNSType_CNAME
:
1780 case kDNSType_DNAME
:return(CompressedDomainNameLength(&rd
->name
, name
));
1782 case kDNSType_SOA
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1783 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1784 5 * sizeof(mDNSOpaque32
));
1792 case kDNSType_DHCID
:return(rr
->rdlength
); // Not self-describing, so have to just trust rdlength
1794 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1797 case kDNSType_AFSDB
:
1799 case kDNSType_KX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->mx
.exchange
, name
));
1801 case kDNSType_RP
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->rp
.mbox
, name
) +
1802 CompressedDomainNameLength(&rd
->rp
.txt
, name
));
1804 case kDNSType_PX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->px
.map822
, name
) +
1805 CompressedDomainNameLength(&rd
->px
.mapx400
, name
));
1807 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1809 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1811 case kDNSType_OPT
: return(rr
->rdlength
);
1813 case kDNSType_NSEC
: {
1815 for (i
=sizeof(rdataNSEC
); i
>0; i
--) if (rd
->nsec
.bitmap
[i
-1]) break;
1816 // For our simplified use of NSEC synthetic records:
1817 // nextname is always the record's own name,
1818 // the block number is always 0,
1819 // the count byte is a value in the range 1-32,
1820 // followed by the 1-32 data bytes
1821 return((estimate
? 1 : DomainNameLength(rr
->name
)) + 2 + i
);
1824 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1825 return(rr
->rdlength
);
1829 // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
1830 // to help reduce the risk of bogus malformed data on the network
1831 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1837 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1839 case kDNSType_NS
: // Same as PTR
1840 case kDNSType_MD
: // Same as PTR
1841 case kDNSType_MF
: // Same as PTR
1842 case kDNSType_CNAME
:// Same as PTR
1843 //case kDNSType_SOA not checked
1844 case kDNSType_MB
: // Same as PTR
1845 case kDNSType_MG
: // Same as PTR
1846 case kDNSType_MR
: // Same as PTR
1847 //case kDNSType_NULL not checked (no specified format, so always valid)
1848 //case kDNSType_WKS not checked
1849 case kDNSType_PTR
: len
= DomainNameLengthLimit(&rd
->u
.name
, rd
->u
.data
+ rdlength
);
1850 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1852 case kDNSType_HINFO
:// Same as TXT (roughly)
1853 case kDNSType_MINFO
:// Same as TXT (roughly)
1854 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1856 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1857 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1858 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1859 return (ptr
== end
);
1862 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1864 case kDNSType_MX
: // Must be at least two-byte preference, plus domainname
1865 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1866 len
= DomainNameLengthLimit(&rd
->u
.mx
.exchange
, rd
->u
.data
+ rdlength
);
1867 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1869 case kDNSType_SRV
: // Must be at least priority+weight+port, plus domainname
1870 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1871 len
= DomainNameLengthLimit(&rd
->u
.srv
.target
, rd
->u
.data
+ rdlength
);
1872 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1874 //case kDNSType_NSEC not checked
1876 default: return(mDNStrue
); // Allow all other types without checking
1880 // ***************************************************************************
1881 #if COMPILER_LIKES_PRAGMA_MARK
1883 #pragma mark - DNS Message Creation Functions
1886 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1890 h
->numQuestions
= 0;
1892 h
->numAuthorities
= 0;
1893 h
->numAdditionals
= 0;
1896 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1898 const mDNSu8
*result
= end
- *domname
- 1;
1900 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1902 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1903 while (result
>= base
)
1905 // If the length byte and first character of the label match, then check further to see
1906 // if this location in the packet will yield a useful name compression pointer.
1907 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1909 const mDNSu8
*name
= domname
;
1910 const mDNSu8
*targ
= result
;
1911 while (targ
+ *name
< end
)
1913 // First see if this label matches
1915 const mDNSu8
*pointertarget
;
1916 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1917 if (i
<= *name
) break; // If label did not match, bail out
1918 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1919 name
+= 1 + *name
; // and proceed to check next label
1920 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1921 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1923 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1924 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1925 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1926 if (targ
+1 >= end
) break; // Second byte not present!
1927 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1928 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1929 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1930 targ
= pointertarget
;
1933 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1938 // Put a string of dot-separated labels as length-prefixed labels
1939 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1940 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1941 // end points to the end of the message so far
1942 // ptr points to where we want to put the name
1943 // limit points to one byte past the end of the buffer that we must not overrun
1944 // domainname is the name to put
1945 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1946 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1948 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1949 const mDNSu8
* np
= name
->c
;
1950 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1951 const mDNSu8
* pointer
= mDNSNULL
;
1952 const mDNSu8
*const searchlimit
= ptr
;
1954 if (!ptr
) { LogMsg("putDomainNameAsLabels %##s ptr is null", name
->c
); return(mDNSNULL
); }
1956 if (!*np
) // If just writing one-byte root label, make sure we have space for that
1958 if (ptr
>= limit
) return(mDNSNULL
);
1960 else // else, loop through writing labels and/or a compression offset
1963 if (*np
> MAX_DOMAIN_LABEL
)
1964 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1966 // This check correctly allows for the final trailing root label:
1968 // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
1969 // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
1970 // We know that max will be at name->c[256]
1971 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1972 // six bytes, then exit the loop, write the final terminating root label, and the domain
1973 // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
1974 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1975 if (np
+ 1 + *np
>= max
)
1976 { LogMsg("Malformed domain name %##s (more than 256 bytes)", name
->c
); return(mDNSNULL
); }
1978 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1979 if (pointer
) // Use a compression pointer if we can
1981 const mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1982 if (ptr
+2 > limit
) return(mDNSNULL
); // If we don't have two bytes of space left, give up
1983 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1984 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1987 else // Else copy one label and try again
1991 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
1992 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1994 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1996 } while (*np
); // While we've got characters remaining in the name, continue
1999 *ptr
++ = 0; // Put the final root label
2003 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
2005 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
2006 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
2007 return ptr
+ sizeof(mDNSOpaque16
);
2010 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
2012 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
2013 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
2014 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
2015 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
2016 return ptr
+ sizeof(mDNSu32
);
2019 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
2020 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const ResourceRecord
*const rr
)
2022 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
2025 case kDNSType_A
: if (rr
->rdlength
!= 4)
2026 { debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
); return(mDNSNULL
); }
2027 if (ptr
+ 4 > limit
) return(mDNSNULL
);
2028 *ptr
++ = rdb
->ipv4
.b
[0];
2029 *ptr
++ = rdb
->ipv4
.b
[1];
2030 *ptr
++ = rdb
->ipv4
.b
[2];
2031 *ptr
++ = rdb
->ipv4
.b
[3];
2035 case kDNSType_CNAME
:
2037 case kDNSType_DNAME
:return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->name
));
2039 case kDNSType_SOA
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->soa
.mname
);
2040 if (!ptr
) return(mDNSNULL
);
2041 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->soa
.rname
);
2042 if (!ptr
|| ptr
+ 20 > limit
) return(mDNSNULL
);
2043 ptr
= putVal32(ptr
, rdb
->soa
.serial
);
2044 ptr
= putVal32(ptr
, rdb
->soa
.refresh
);
2045 ptr
= putVal32(ptr
, rdb
->soa
.retry
);
2046 ptr
= putVal32(ptr
, rdb
->soa
.expire
);
2047 ptr
= putVal32(ptr
, rdb
->soa
.min
);
2051 case kDNSType_HINFO
:
2057 case kDNSType_DHCID
:if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
2058 mDNSPlatformMemCopy(ptr
, rdb
->data
, rr
->rdlength
);
2059 return(ptr
+ rr
->rdlength
);
2062 case kDNSType_AFSDB
:
2064 case kDNSType_KX
: if (ptr
+ 3 > limit
) return(mDNSNULL
);
2065 ptr
= putVal16(ptr
, rdb
->mx
.preference
);
2066 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->mx
.exchange
));
2068 case kDNSType_RP
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->rp
.mbox
);
2069 if (!ptr
) return(mDNSNULL
);
2070 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->rp
.txt
);
2073 case kDNSType_PX
: if (ptr
+ 5 > limit
) return(mDNSNULL
);
2074 ptr
= putVal16(ptr
, rdb
->px
.preference
);
2075 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->px
.map822
);
2076 if (!ptr
) return(mDNSNULL
);
2077 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->px
.mapx400
);
2080 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rdb
->ipv6
))
2081 { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
); return(mDNSNULL
); }
2082 if (ptr
+ sizeof(rdb
->ipv6
) > limit
) return(mDNSNULL
);
2083 mDNSPlatformMemCopy(ptr
, &rdb
->ipv6
, sizeof(rdb
->ipv6
));
2084 return(ptr
+ sizeof(rdb
->ipv6
));
2086 case kDNSType_SRV
: if (ptr
+ 7 > limit
) return(mDNSNULL
);
2087 *ptr
++ = (mDNSu8
)(rdb
->srv
.priority
>> 8);
2088 *ptr
++ = (mDNSu8
)(rdb
->srv
.priority
& 0xFF);
2089 *ptr
++ = (mDNSu8
)(rdb
->srv
.weight
>> 8);
2090 *ptr
++ = (mDNSu8
)(rdb
->srv
.weight
& 0xFF);
2091 *ptr
++ = rdb
->srv
.port
.b
[0];
2092 *ptr
++ = rdb
->srv
.port
.b
[1];
2093 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->srv
.target
));
2095 case kDNSType_OPT
: {
2097 const rdataOPT
*opt
;
2098 const rdataOPT
*const end
= (const rdataOPT
*)&rr
->rdata
->u
.data
[rr
->rdlength
];
2099 for (opt
= &rr
->rdata
->u
.opt
[0]; opt
< end
; opt
++) len
+= DNSOpt_Data_Space(opt
);
2100 if (ptr
+ len
> limit
) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL
; }
2102 for (opt
= &rr
->rdata
->u
.opt
[0]; opt
< end
; opt
++)
2104 const int space
= DNSOpt_Data_Space(opt
);
2105 ptr
= putVal16(ptr
, opt
->opt
);
2106 ptr
= putVal16(ptr
, space
- 4);
2110 ptr
= putVal16(ptr
, opt
->u
.llq
.vers
);
2111 ptr
= putVal16(ptr
, opt
->u
.llq
.llqOp
);
2112 ptr
= putVal16(ptr
, opt
->u
.llq
.err
);
2113 mDNSPlatformMemCopy(ptr
, opt
->u
.llq
.id
.b
, 8); // 8-byte id
2115 ptr
= putVal32(ptr
, opt
->u
.llq
.llqlease
);
2118 ptr
= putVal32(ptr
, opt
->u
.updatelease
);
2121 *ptr
++ = opt
->u
.owner
.vers
;
2122 *ptr
++ = opt
->u
.owner
.seq
;
2123 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.HMAC
.b
, 6); // 6-byte Host identifier
2125 if (space
>= DNSOpt_OwnerData_ID_Wake_Space
)
2127 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.IMAC
.b
, 6); // 6-byte interface MAC
2129 if (space
> DNSOpt_OwnerData_ID_Wake_Space
)
2131 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.password
.b
, space
- DNSOpt_OwnerData_ID_Wake_Space
);
2132 ptr
+= space
- DNSOpt_OwnerData_ID_Wake_Space
;
2141 case kDNSType_NSEC
: {
2142 // For our simplified use of NSEC synthetic records:
2143 // nextname is always the record's own name,
2144 // the block number is always 0,
2145 // the count byte is a value in the range 1-32,
2146 // followed by the 1-32 data bytes
2148 for (i
=sizeof(rdataNSEC
); i
>0; i
--) if (rdb
->nsec
.bitmap
[i
-1]) break;
2149 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
2150 if (!ptr
) return(mDNSNULL
);
2151 if (ptr
+ 2 + i
> limit
) return(mDNSNULL
);
2154 for (j
=0; j
<i
; j
++) *ptr
++ = rdb
->nsec
.bitmap
[j
];
2158 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
2159 if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
2160 mDNSPlatformMemCopy(ptr
, rdb
->data
, rr
->rdlength
);
2161 return(ptr
+ rr
->rdlength
);
2165 #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2167 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
2170 mDNSu16 actualLength
;
2171 // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2172 const DNSMessage
*const rdatacompressionbase
= (IsUnicastUpdate(msg
) && rr
->rrtype
== kDNSType_SRV
) ? mDNSNULL
: msg
;
2174 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
2176 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
2180 if (!ptr
) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL
); }
2182 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
2183 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2184 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
2185 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
2186 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
2187 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
2188 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
2189 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
2190 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
2191 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
2192 endofrdata
= putRData(rdatacompressionbase
, ptr
+10, limit
, rr
);
2193 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
2195 // Go back and fill in the actual number of data bytes we wrote
2196 // (actualLength can be less than rdlength when domain name compression is used)
2197 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
2198 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
2199 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
2201 if (count
) (*count
)++;
2202 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
2206 mDNSlocal mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, mDNSu16
*count
, const AuthRecord
*rr
)
2208 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
2209 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2210 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
2211 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
2212 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
2213 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
2214 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
2215 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
2220 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
2222 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2223 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2224 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2225 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2226 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
2227 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
2228 msg
->h
.numQuestions
++;
2232 // for dynamic updates
2233 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
2235 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
2236 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
2237 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
2238 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
2239 *ptr
++ = zoneClass
.b
[0];
2240 *ptr
++ = zoneClass
.b
[1];
2241 msg
->h
.mDNS_numZones
++;
2245 // for dynamic updates
2246 mDNSexport mDNSu8
*putPrereqNameNotInUse(const domainname
*const name
, DNSMessage
*const msg
, mDNSu8
*const ptr
, mDNSu8
*const end
)
2249 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
2250 AssignDomainName(&prereq
.namestorage
, name
);
2251 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
2252 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
2253 return putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
2256 // for dynamic updates
2257 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
2259 // deletion: specify record w/ TTL 0, class NONE
2260 const mDNSu16 origclass
= rr
->rrclass
;
2261 rr
->rrclass
= kDNSClass_NONE
;
2262 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
2263 rr
->rrclass
= origclass
;
2267 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
2269 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2270 mDNSu16
class = kDNSQClass_ANY
;
2272 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2273 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2274 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2275 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2276 ptr
[2] = (mDNSu8
)(class >> 8);
2277 ptr
[3] = (mDNSu8
)(class & 0xFF);
2278 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2279 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2281 msg
->h
.mDNS_numUpdates
++;
2285 // for dynamic updates
2286 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
2288 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2289 mDNSu16
class = kDNSQClass_ANY
;
2290 mDNSu16 rrtype
= kDNSQType_ANY
;
2292 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2293 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2294 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2295 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2296 ptr
[2] = (mDNSu8
)(class >> 8);
2297 ptr
[3] = (mDNSu8
)(class & 0xFF);
2298 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2299 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2301 msg
->h
.mDNS_numUpdates
++;
2305 // for dynamic updates
2306 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
2309 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
2310 rr
.resrec
.rrclass
= NormalMaxDNSMessageData
;
2311 rr
.resrec
.rdlength
= sizeof(rdataOPT
); // One option in this OPT record
2312 rr
.resrec
.rdestimate
= sizeof(rdataOPT
);
2313 rr
.resrec
.rdata
->u
.opt
[0].opt
= kDNSOpt_Lease
;
2314 rr
.resrec
.rdata
->u
.opt
[0].u
.updatelease
= lease
;
2315 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, &rr
.resrec
, 0);
2316 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
2320 mDNSexport mDNSu8
*putHINFO(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
, DomainAuthInfo
*authInfo
)
2322 if (authInfo
&& authInfo
->AutoTunnel
)
2325 mDNSu8
*h
= hinfo
.rdatastorage
.u
.data
;
2326 mDNSu16 len
= 2 + m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0];
2328 mDNS_SetupResourceRecord(&hinfo
, mDNSNULL
, mDNSInterface_Any
, kDNSType_HINFO
, 0, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
2329 AppendDomainLabel(&hinfo
.namestorage
, &m
->hostlabel
);
2330 AppendDomainName (&hinfo
.namestorage
, &authInfo
->domain
);
2331 hinfo
.resrec
.rroriginalttl
= 0;
2332 mDNSPlatformMemCopy(h
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
2334 mDNSPlatformMemCopy(h
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
2335 hinfo
.resrec
.rdlength
= len
;
2336 hinfo
.resrec
.rdestimate
= len
;
2337 newptr
= PutResourceRecord(msg
, end
, &msg
->h
.numAdditionals
, &hinfo
.resrec
);
2344 // ***************************************************************************
2345 #if COMPILER_LIKES_PRAGMA_MARK
2347 #pragma mark - DNS Message Parsing Functions
2350 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
2355 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
2357 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
2358 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
2359 sum
= (sum
<<3) | (sum
>>29);
2361 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
2365 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
2370 rr
->rdata
= NewRData
;
2371 rr
->rdlength
= rdlength
;
2373 // Must not try to get target pointer until after updating rr->rdata
2374 target
= GetRRDomainNameTarget(rr
);
2375 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
2376 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
2377 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
);
2380 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
2384 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2385 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2387 while (1) // Read sequence of labels
2389 const mDNSu8 len
= *ptr
++; // Read length of this label
2390 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
2393 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2394 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2395 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
2396 { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL
); }
2401 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
2402 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
2403 case 0xC0: return(ptr
+1);
2408 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2409 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
2410 domainname
*const name
)
2412 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
2413 mDNSu8
*np
= name
->c
; // Name pointer
2414 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
2416 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2417 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2419 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2421 while (1) // Read sequence of labels
2423 const mDNSu8 len
= *ptr
++; // Read length of this label
2424 if (len
== 0) break; // If length is zero, that means this name is complete
2430 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2431 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2432 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
2433 { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL
); }
2435 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
2436 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2439 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
2442 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
2444 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
2445 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
2446 ptr
= (mDNSu8
*)msg
+ offset
;
2447 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2448 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
2450 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
2455 if (nextbyte
) return(nextbyte
);
2459 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2461 mDNSu16 pktrdlength
;
2463 ptr
= skipDomainName(msg
, ptr
, end
);
2464 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2466 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2467 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2469 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2471 return(ptr
+ pktrdlength
);
2474 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
2476 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
2477 *ptr
+= sizeof(mDNSOpaque16
);
2481 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
,
2482 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*const largecr
)
2484 CacheRecord
*const rr
= &largecr
->r
;
2485 RDataBody2
*const rdb
= (RDataBody2
*)rr
->smallrdatastorage
.data
;
2486 mDNSu16 pktrdlength
;
2488 if (largecr
== &m
->rec
&& m
->rec
.r
.resrec
.RecordType
)
2490 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &m
->rec
.r
));
2496 rr
->next
= mDNSNULL
;
2497 rr
->resrec
.name
= &largecr
->namestorage
;
2499 rr
->NextInKAList
= mDNSNULL
;
2500 rr
->TimeRcvd
= m
? m
->timenow
: 0;
2501 rr
->DelayDelivery
= 0;
2502 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2503 rr
->LastUsed
= m
? m
->timenow
: 0;
2504 rr
->CRActiveQuestion
= mDNSNULL
;
2505 rr
->UnansweredQueries
= 0;
2506 rr
->LastUnansweredTime
= 0;
2507 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
2508 rr
->MPUnansweredQ
= 0;
2509 rr
->MPLastUnansweredQT
= 0;
2510 rr
->MPUnansweredKA
= 0;
2511 rr
->MPExpectingKA
= mDNSfalse
;
2513 rr
->NextInCFList
= mDNSNULL
;
2515 rr
->resrec
.InterfaceID
= InterfaceID
;
2516 ptr
= getDomainName(msg
, ptr
, end
, &largecr
->namestorage
);
2517 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2519 if (ptr
+ 10 > end
) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2521 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2522 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
2523 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
2524 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
2525 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2526 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2527 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2528 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2530 // If mDNS record has cache-flush bit set, we mark it unique
2531 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2532 // authoritative for the entire RRSet), unless this is a truncated response
2533 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8) || (!InterfaceID
&& !(msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)))
2534 RecordType
|= kDNSRecordTypePacketUniqueMask
;
2536 if (ptr
+ pktrdlength
> end
) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2537 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
2539 rr
->resrec
.rdata
= (RData
*)&rr
->smallrdatastorage
;
2540 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
2542 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
2544 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2545 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2546 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2547 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2548 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2549 if (rr
->resrec
.rrclass
== kDNSQClass_ANY
&& pktrdlength
== 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2550 rr
->resrec
.rdlength
= 0;
2551 else switch (rr
->resrec
.rrtype
)
2553 case kDNSType_A
: if (pktrdlength
!= sizeof(mDNSv4Addr
)) return(mDNSNULL
);
2554 rdb
->ipv4
.b
[0] = ptr
[0];
2555 rdb
->ipv4
.b
[1] = ptr
[1];
2556 rdb
->ipv4
.b
[2] = ptr
[2];
2557 rdb
->ipv4
.b
[3] = ptr
[3];
2561 case kDNSType_CNAME
:
2563 case kDNSType_DNAME
:ptr
= getDomainName(msg
, ptr
, end
, &rdb
->name
);
2564 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
2565 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength);
2568 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rdb
->soa
.mname
);
2569 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
2570 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->soa
.rname
);
2571 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
2572 if (ptr
+ 0x14 != end
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
2573 rdb
->soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
2574 rdb
->soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
2575 rdb
->soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
2576 rdb
->soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
2577 rdb
->soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
2581 case kDNSType_HINFO
:
2587 case kDNSType_DHCID
:if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2589 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2590 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2593 rr
->resrec
.rdlength
= pktrdlength
;
2594 mDNSPlatformMemCopy(rdb
->data
, ptr
, pktrdlength
);
2598 case kDNSType_AFSDB
:
2600 case kDNSType_KX
: if (pktrdlength
< 3) return(mDNSNULL
); // Preference + domainname
2601 rdb
->mx
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2602 ptr
= getDomainName(msg
, ptr
+2, end
, &rdb
->mx
.exchange
);
2603 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL
); }
2604 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2607 case kDNSType_RP
: ptr
= getDomainName(msg
, ptr
, end
, &rdb
->rp
.mbox
); // Domainname + domainname
2608 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL
; }
2609 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->rp
.txt
);
2610 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL
; }
2613 case kDNSType_PX
: if (pktrdlength
< 4) return(mDNSNULL
); // Preference + domainname + domainname
2614 rdb
->px
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2615 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->px
.map822
);
2616 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL
; }
2617 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->px
.mapx400
);
2618 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL
; }
2621 case kDNSType_AAAA
: if (pktrdlength
!= sizeof(mDNSv6Addr
)) return(mDNSNULL
);
2622 mDNSPlatformMemCopy(&rdb
->ipv6
, ptr
, sizeof(rdb
->ipv6
));
2625 case kDNSType_SRV
: if (pktrdlength
< 7) return(mDNSNULL
); // Priority + weight + port + domainname
2626 rdb
->srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2627 rdb
->srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
2628 rdb
->srv
.port
.b
[0] = ptr
[4];
2629 rdb
->srv
.port
.b
[1] = ptr
[5];
2630 ptr
= getDomainName(msg
, ptr
+6, end
, &rdb
->srv
.target
);
2631 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
2632 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2635 case kDNSType_OPT
: {
2636 rdataOPT
*opt
= rr
->resrec
.rdata
->u
.opt
;
2637 rr
->resrec
.rdlength
= 0;
2638 while (ptr
< end
&& (mDNSu8
*)(opt
+1) < &rr
->resrec
.rdata
->u
.data
[MaximumRDSize
])
2640 if (ptr
+ 4 > end
) { LogMsg("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); return(mDNSNULL
); }
2641 opt
->opt
= getVal16(&ptr
);
2642 opt
->optlen
= getVal16(&ptr
);
2643 if (!ValidDNSOpt(opt
)) { LogMsg("GetLargeResourceRecord: opt %d optlen %d wrong", opt
->opt
, opt
->optlen
); return(mDNSNULL
); }
2644 if (ptr
+ opt
->optlen
> end
) { LogMsg("GetLargeResourceRecord: ptr + opt->optlen > end"); return(mDNSNULL
); }
2648 opt
->u
.llq
.vers
= getVal16(&ptr
);
2649 opt
->u
.llq
.llqOp
= getVal16(&ptr
);
2650 opt
->u
.llq
.err
= getVal16(&ptr
);
2651 mDNSPlatformMemCopy(opt
->u
.llq
.id
.b
, ptr
, 8);
2653 opt
->u
.llq
.llqlease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
2654 if (opt
->u
.llq
.llqlease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
2655 opt
->u
.llq
.llqlease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2656 ptr
+= sizeof(mDNSOpaque32
);
2659 opt
->u
.updatelease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
2660 if (opt
->u
.updatelease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
2661 opt
->u
.updatelease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2662 ptr
+= sizeof(mDNSs32
);
2665 opt
->u
.owner
.vers
= ptr
[0];
2666 opt
->u
.owner
.seq
= ptr
[1];
2667 mDNSPlatformMemCopy(opt
->u
.owner
.HMAC
.b
, ptr
+2, 6); // 6-byte MAC address
2668 mDNSPlatformMemCopy(opt
->u
.owner
.IMAC
.b
, ptr
+2, 6); // 6-byte MAC address
2669 opt
->u
.owner
.password
= zeroEthAddr
;
2670 if (opt
->optlen
>= DNSOpt_OwnerData_ID_Wake_Space
-4)
2672 mDNSPlatformMemCopy(opt
->u
.owner
.IMAC
.b
, ptr
+8, 6); // 6-byte MAC address
2673 if (opt
->optlen
> DNSOpt_OwnerData_ID_Wake_Space
-4)
2674 mDNSPlatformMemCopy(opt
->u
.owner
.password
.b
, ptr
+14, opt
->optlen
- (DNSOpt_OwnerData_ID_Wake_Space
-4));
2679 opt
++; // increment pointer into rdatabody
2681 rr
->resrec
.rdlength
= (mDNSu8
*)opt
- rr
->resrec
.rdata
->u
.data
;
2682 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL
); }
2686 case kDNSType_NSEC
: {
2689 ptr
= getDomainName(msg
, ptr
, end
, &d
); // Ignored for our simplified use of NSEC synthetic records
2690 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed NSEC nextname"); return mDNSNULL
; }
2691 if (*ptr
++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); return mDNSNULL
; }
2693 if (i
< 1 || i
> sizeof(rdataNSEC
)) { debugf("GetLargeResourceRecord: invalid block length %d", i
); return mDNSNULL
; }
2694 mDNSPlatformMemZero(rdb
->nsec
.bitmap
, sizeof(rdb
->nsec
.bitmap
));
2695 for (j
=0; j
<i
; j
++) rdb
->nsec
.bitmap
[j
] = *ptr
++;
2696 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed NSEC"); return(mDNSNULL
); }
2700 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2702 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2703 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2706 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2707 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
2708 // Note: Just because we don't understand the record type, that doesn't
2709 // mean we fail. The DNS protocol specifies rdlength, so we can
2710 // safely skip over unknown records and ignore them.
2711 // We also grab a binary copy of the rdata anyway, since the caller
2712 // might know how to interpret it even if we don't.
2713 rr
->resrec
.rdlength
= pktrdlength
;
2714 mDNSPlatformMemCopy(rdb
->data
, ptr
, pktrdlength
);
2718 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2719 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rdlength, rdestimate, rdatahash for us
2721 // Success! Now fill in RecordType to show this record contains valid data
2722 rr
->resrec
.RecordType
= RecordType
;
2726 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2728 ptr
= skipDomainName(msg
, ptr
, end
);
2729 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
2730 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2734 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
2735 DNSQuestion
*question
)
2737 mDNSPlatformMemZero(question
, sizeof(*question
));
2738 question
->InterfaceID
= InterfaceID
;
2739 if (!InterfaceID
) question
->TargetQID
= onesID
; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
2740 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
2741 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
2742 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2744 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
2745 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
2746 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
2750 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2753 const mDNSu8
*ptr
= msg
->data
;
2754 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
2758 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2761 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
2762 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2766 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2769 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
2770 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2774 mDNSexport
const mDNSu8
*LocateOptRR(const DNSMessage
*const msg
, const mDNSu8
*const end
, int minsize
)
2777 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2779 // Locate the OPT record.
2780 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2781 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2782 // but not necessarily the *last* entry in the Additional Section.
2783 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2785 if (ptr
+ DNSOpt_Header_Space
+ minsize
<= end
&& // Make sure we have 11+minsize bytes of data
2786 ptr
[0] == 0 && // Name must be root label
2787 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2788 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2789 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)minsize
)
2792 ptr
= skipResourceRecord(msg
, ptr
, end
);
2797 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2798 // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
2799 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2800 // The code that currently calls this assumes there's only one, instead of iterating through the set
2801 mDNSexport
const rdataOPT
*GetLLQOptData(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2803 const mDNSu8
*ptr
= LocateOptRR(msg
, end
, DNSOpt_LLQData_Space
);
2806 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2807 if (ptr
) return(&m
->rec
.r
.resrec
.rdata
->u
.opt
[0]);
2812 // Get the lease life of records in a dynamic update
2813 // returns 0 on error or if no lease present
2814 mDNSexport mDNSu32
GetPktLease(mDNS
*m
, DNSMessage
*msg
, const mDNSu8
*end
)
2817 const mDNSu8
*ptr
= LocateOptRR(msg
, end
, DNSOpt_LeaseData_Space
);
2818 if (ptr
) ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2819 if (ptr
&& m
->rec
.r
.resrec
.rdlength
>= DNSOpt_LeaseData_Space
&& m
->rec
.r
.resrec
.rdata
->u
.opt
[0].opt
== kDNSOpt_Lease
)
2820 result
= m
->rec
.r
.resrec
.rdata
->u
.opt
[0].u
.updatelease
;
2821 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
2825 mDNSlocal
const mDNSu8
*DumpRecords(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
, int count
, char *label
)
2828 LogMsg("%2d %s", count
, label
);
2829 for (i
= 0; i
< count
&& ptr
; i
++)
2831 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2832 // but since it's only used for debugging (and probably only on OS X, not on
2833 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2834 LargeCacheRecord largecr
;
2835 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, mDNSInterface_Any
, kDNSRecordTypePacketAns
, &largecr
);
2836 if (ptr
) LogMsg("%2d TTL%7d %s", i
, largecr
.r
.resrec
.rroriginalttl
, CRDisplayString(m
, &largecr
.r
));
2838 if (!ptr
) LogMsg("ERROR: Premature end of packet data");
2842 #define DNS_OP_Name(X) ( \
2843 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2844 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2845 (X) == kDNSFlag0_OP_Status ? "Status " : \
2846 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2847 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2848 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2850 #define DNS_RC_Name(X) ( \
2851 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2852 (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
2853 (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
2854 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2855 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2856 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2857 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2858 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2859 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2860 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2861 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2863 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2864 mDNSexport
void DumpPacket(mDNS
*const m
, mStatus status
, mDNSBool sent
, char *transport
,
2865 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
,
2866 const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2868 mDNSBool IsUpdate
= ((msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
) == kDNSFlag0_OP_Update
);
2869 const mDNSu8
*ptr
= msg
->data
;
2872 char tbuffer
[64], sbuffer
[64], dbuffer
[64] = "";
2873 if (!status
) tbuffer
[mDNS_snprintf(tbuffer
, sizeof(tbuffer
), sent
? "Sent" : "Received" )] = 0;
2874 else tbuffer
[mDNS_snprintf(tbuffer
, sizeof(tbuffer
), "ERROR %d %sing", status
, sent
? "Send" : "Receiv")] = 0;
2875 if (sent
) sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "port " )] = 0;
2876 else sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "%#a:", srcaddr
)] = 0;
2877 if (dstaddr
|| !mDNSIPPortIsZero(dstport
))
2878 dbuffer
[mDNS_snprintf(dbuffer
, sizeof(dbuffer
), " to %#a:%d", dstaddr
, mDNSVal16(dstport
))] = 0;
2880 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 --",
2882 DNS_OP_Name(msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
),
2883 msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
? "Response" : "Query",
2884 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1],
2885 DNS_RC_Name(msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
),
2886 msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
,
2887 msg
->h
.flags
.b
[0] & kDNSFlag0_AA
? "AA " : "",
2888 msg
->h
.flags
.b
[0] & kDNSFlag0_TC
? "TC " : "",
2889 msg
->h
.flags
.b
[0] & kDNSFlag0_RD
? "RD " : "",
2890 msg
->h
.flags
.b
[1] & kDNSFlag1_RA
? "RA " : "",
2891 msg
->h
.flags
.b
[1] & kDNSFlag1_AD
? "AD " : "",
2892 msg
->h
.flags
.b
[1] & kDNSFlag1_CD
? "CD " : "",
2893 mDNSVal16(msg
->h
.id
),
2895 sbuffer
, mDNSVal16(srcport
), dbuffer
,
2896 (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
) ? " (truncated)" : ""
2899 LogMsg("%2d %s", msg
->h
.numQuestions
, IsUpdate
? "Zone" : "Questions");
2900 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++)
2902 ptr
= getQuestion(msg
, ptr
, end
, mDNSInterface_Any
, &q
);
2903 if (ptr
) LogMsg("%2d %##s %s", i
, q
.qname
.c
, DNSTypeName(q
.qtype
));
2905 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAnswers
, IsUpdate
? "Prerequisites" : "Answers");
2906 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAuthorities
, IsUpdate
? "Updates" : "Authorities");
2907 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAdditionals
, "Additionals");
2908 LogMsg("--------------");
2911 // ***************************************************************************
2912 #if COMPILER_LIKES_PRAGMA_MARK
2914 #pragma mark - Packet Sending Functions
2917 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2918 struct TCPSocket_struct
{ TCPSocketFlags flags
; /* ... */ };
2920 struct UDPSocket_struct
2922 mDNSIPPort port
; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
2925 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2926 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2927 mDNSexport mStatus
mDNSSendDNSMessage(mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2928 mDNSInterfaceID InterfaceID
, UDPSocket
*src
, const mDNSAddr
*dst
, mDNSIPPort dstport
, TCPSocket
*sock
, DomainAuthInfo
*authInfo
)
2930 mStatus status
= mStatus_NoError
;
2931 const mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2934 // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
2935 if (end
< msg
->data
|| end
- msg
->data
> AbsoluteMaxDNSMessageData
)
2937 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg
->data
, end
, end
- msg
->data
);
2938 return mStatus_BadParamErr
;
2941 newend
= putHINFO(m
, msg
, end
, authInfo
);
2942 if (!newend
) LogMsg("mDNSSendDNSMessage: putHINFO failed"); // Not fatal
2945 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2946 SwapDNSHeaderBytes(msg
);
2948 if (authInfo
) DNSDigest_SignMessage(msg
, &end
, authInfo
, 0); // DNSDigest_SignMessage operates on message in network byte order
2949 if (!end
) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status
= mStatus_NoMemoryErr
; }
2952 // Send the packet on the wire
2954 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, src
, dst
, dstport
);
2957 mDNSu16 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2958 mDNSu8 lenbuf
[2] = { (mDNSu8
)(msglen
>> 8), (mDNSu8
)(msglen
& 0xFF) };
2959 long nsent
= mDNSPlatformWriteTCP(sock
, (char*)lenbuf
, 2); // Should do scatter/gather here -- this is probably going out as two packets
2960 if (nsent
!= 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent
, 2); status
= mStatus_ConnFailed
; }
2963 nsent
= mDNSPlatformWriteTCP(sock
, (char *)msg
, msglen
);
2964 if (nsent
!= msglen
) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent
, msglen
); status
= mStatus_ConnFailed
; }
2969 // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
2970 SwapDNSHeaderBytes(msg
);
2972 // Dump the packet with the HINFO and TSIG
2973 if (mDNS_PacketLoggingEnabled
&& !mDNSOpaque16IsZero(msg
->h
.id
))
2974 DumpPacket(m
, status
, mDNStrue
, sock
&& (sock
->flags
& kTCPSocketFlags_UseTLS
) ? "TLS" : sock
? "TCP" : "UDP", mDNSNULL
, src
? src
->port
: MulticastDNSPort
, dst
, dstport
, msg
, end
);
2976 // put the number of additionals back the way it was
2977 msg
->h
.numAdditionals
= numAdditionals
;
2982 // ***************************************************************************
2983 #if COMPILER_LIKES_PRAGMA_MARK
2985 #pragma mark - RR List Management & Task Management
2988 mDNSexport
void mDNS_Lock_(mDNS
*const m
)
2990 // MUST grab the platform lock FIRST!
2991 mDNSPlatformLock(m
);
2993 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2994 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2995 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2996 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2998 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
3000 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
3005 // If this is an initial entry into the mDNSCore code, set m->timenow
3006 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3007 if (m
->mDNS_busy
== 0)
3010 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
3011 m
->timenow
= mDNS_TimeNow_NoLock(m
);
3012 if (m
->timenow
== 0) m
->timenow
= 1;
3014 else if (m
->timenow
== 0)
3016 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
3017 m
->timenow
= mDNS_TimeNow_NoLock(m
);
3018 if (m
->timenow
== 0) m
->timenow
= 1;
3021 if (m
->timenow_last
- m
->timenow
> 0)
3023 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
3024 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
3025 m
->timenow
= m
->timenow_last
;
3027 m
->timenow_last
= m
->timenow
;
3029 // Increment mDNS_busy so we'll recognise re-entrant calls
3033 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
3035 mDNSs32 e
= m
->timenow
+ 0x78000000;
3036 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) return(e
);
3037 if (m
->NewQuestions
)
3039 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
3040 else return(m
->timenow
);
3042 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
3043 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
3044 if (m
->SPSProxyListChanged
) return(m
->timenow
);
3045 #ifndef UNICAST_DISABLED
3046 if (e
- m
->NextuDNSEvent
> 0) e
= m
->NextuDNSEvent
;
3047 if (e
- m
->NextScheduledNATOp
> 0) e
= m
->NextScheduledNATOp
;
3049 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
3050 if (e
- m
->NextScheduledSPS
> 0) e
= m
->NextScheduledSPS
;
3051 if (m
->SleepLimit
&& e
- m
->NextScheduledSPRetry
> 0) e
= m
->NextScheduledSPRetry
;
3052 if (m
->DelaySleep
&& e
- m
->DelaySleep
> 0) e
= m
->DelaySleep
;
3054 if (m
->SuppressSending
)
3056 if (e
- m
->SuppressSending
> 0) e
= m
->SuppressSending
;
3060 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
3061 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
3062 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
3068 mDNSexport
void ShowTaskSchedulingError(mDNS
*const m
)
3072 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
3074 // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3076 if (m
->NewQuestions
&& (!m
->NewQuestions
->DelayAnswering
|| m
->timenow
- m
->NewQuestions
->DelayAnswering
>= 0))
3077 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
3078 m
->NewQuestions
->qname
.c
, DNSTypeName(m
->NewQuestions
->qtype
));
3080 if (m
->NewLocalOnlyQuestions
)
3081 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3082 m
->NewLocalOnlyQuestions
->qname
.c
, DNSTypeName(m
->NewLocalOnlyQuestions
->qtype
));
3084 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
))
3085 LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m
, m
->NewLocalRecords
));
3087 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
3088 LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m
->timenow
- m
->NextScheduledEvent
);
3089 if (m
->SuppressSending
&& m
->timenow
- m
->SuppressSending
>= 0)
3090 LogMsg("Task Scheduling Error: m->SuppressSending %d", m
->timenow
- m
->SuppressSending
);
3091 if (m
->timenow
- m
->NextCacheCheck
>= 0)
3092 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m
->timenow
- m
->NextCacheCheck
);
3093 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3094 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m
->timenow
- m
->NextScheduledQuery
);
3095 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3096 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m
->timenow
- m
->NextScheduledProbe
);
3097 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
3098 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m
->timenow
- m
->NextScheduledResponse
);
3099 if (m
->timenow
- m
->NextScheduledNATOp
>= 0)
3100 LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m
->timenow
- m
->NextScheduledNATOp
);
3101 if (m
->timenow
- m
->NextScheduledSPS
>= 0)
3102 LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m
->timenow
- m
->NextScheduledSPS
);
3103 if (m
->SleepLimit
&& m
->timenow
- m
->NextScheduledSPRetry
>= 0)
3104 LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m
->timenow
- m
->NextScheduledSPRetry
);
3105 if (m
->DelaySleep
&& m
->timenow
- m
->DelaySleep
>= 0)
3106 LogMsg("Task Scheduling Error: m->DelaySleep %d", m
->timenow
- m
->DelaySleep
);
3107 #ifndef UNICAST_DISABLED
3108 if (m
->timenow
- m
->NextuDNSEvent
>= 0)
3109 LogMsg("Task Scheduling Error: NextuDNSEvent %d", m
->timenow
- m
->NextuDNSEvent
);
3115 mDNSexport
void mDNS_Unlock_(mDNS
*const m
)
3117 // Decrement mDNS_busy
3120 // Check for locking failures
3122 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
3124 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
3129 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
3130 if (m
->mDNS_busy
== 0)
3132 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
3133 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
3137 // MUST release the platform lock LAST!
3138 mDNSPlatformUnlock(m
);
3141 // ***************************************************************************
3142 #if COMPILER_LIKES_PRAGMA_MARK
3144 #pragma mark - Specialized mDNS version of vsnprintf
3147 static const struct mDNSprintf_format
3149 unsigned leftJustify
: 1;
3150 unsigned forceSign
: 1;
3151 unsigned zeroPad
: 1;
3152 unsigned havePrecision
: 1;
3156 char sign
; // +, - or space
3157 unsigned int fieldWidth
;
3158 unsigned int precision
;
3159 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3161 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
3163 mDNSu32 nwritten
= 0;
3165 if (buflen
== 0) return(0);
3166 buflen
--; // Pre-reserve one space in the buffer for the terminating null
3167 if (buflen
== 0) goto exit
;
3169 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
3173 *sbuffer
++ = (char)c
;
3174 if (++nwritten
>= buflen
) goto exit
;
3178 unsigned int i
=0, j
;
3179 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
3180 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
3181 // The size needs to be enough for a 256-byte domain name plus some error text.
3182 #define mDNS_VACB_Size 300
3183 char mDNS_VACB
[mDNS_VACB_Size
];
3184 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
3185 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
3186 char *s
= mDNS_VACB_Lim
, *digits
;
3187 struct mDNSprintf_format F
= mDNSprintf_format_default
;
3189 while (1) // decode flags
3192 if (c
== '-') F
.leftJustify
= 1;
3193 else if (c
== '+') F
.forceSign
= 1;
3194 else if (c
== ' ') F
.sign
= ' ';
3195 else if (c
== '#') F
.altForm
++;
3196 else if (c
== '0') F
.zeroPad
= 1;
3200 if (c
== '*') // decode field width
3202 int f
= va_arg(arg
, int);
3203 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
3204 F
.fieldWidth
= (unsigned int)f
;
3209 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
3210 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
3213 if (c
== '.') // decode precision
3215 if ((c
= *++fmt
) == '*')
3216 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
3217 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
3218 F
.precision
= (10 * F
.precision
) + (c
- '0');
3219 F
.havePrecision
= 1;
3222 if (F
.leftJustify
) F
.zeroPad
= 0;
3225 switch (c
) // perform appropriate conversion
3228 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
3229 case 'l' : // fall through
3230 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
3232 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
3233 else n
= (unsigned long)va_arg(arg
, int);
3234 if (F
.hSize
) n
= (short) n
;
3235 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
3236 else if (F
.forceSign
) F
.sign
= '+';
3238 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3239 else n
= va_arg(arg
, unsigned int);
3240 if (F
.hSize
) n
= (unsigned short) n
;
3243 decimal
: if (!F
.havePrecision
)
3247 F
.precision
= F
.fieldWidth
;
3248 if (F
.sign
) --F
.precision
;
3250 if (F
.precision
< 1) F
.precision
= 1;
3252 if (F
.precision
> mDNS_VACB_Size
- 1)
3253 F
.precision
= mDNS_VACB_Size
- 1;
3254 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
3255 for (; i
< F
.precision
; i
++) *--s
= '0';
3256 if (F
.sign
) { *--s
= F
.sign
; i
++; }
3259 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3260 else n
= va_arg(arg
, unsigned int);
3261 if (F
.hSize
) n
= (unsigned short) n
;
3262 if (!F
.havePrecision
)
3264 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
3265 if (F
.precision
< 1) F
.precision
= 1;
3267 if (F
.precision
> mDNS_VACB_Size
- 1)
3268 F
.precision
= mDNS_VACB_Size
- 1;
3269 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
3270 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
3271 for (; i
< F
.precision
; i
++) *--s
= '0';
3275 unsigned char *a
= va_arg(arg
, unsigned char *);
3276 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
3279 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3282 mDNSAddr
*ip
= (mDNSAddr
*)a
;
3285 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
3286 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
3287 default: F
.precision
= 0; break;
3290 if (F
.altForm
&& !F
.precision
)
3291 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "«ZERO ADDRESS»");
3292 else switch (F
.precision
)
3294 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
3295 a
[0], a
[1], a
[2], a
[3]); break;
3296 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
3297 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
3298 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
3299 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
3300 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
3301 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
3302 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify"
3303 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
3309 case 'p' : F
.havePrecision
= F
.lSize
= 1;
3311 case 'X' : digits
= "0123456789ABCDEF";
3313 case 'x' : digits
= "0123456789abcdef";
3314 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3315 else n
= va_arg(arg
, unsigned int);
3316 if (F
.hSize
) n
= (unsigned short) n
;
3317 if (!F
.havePrecision
)
3321 F
.precision
= F
.fieldWidth
;
3322 if (F
.altForm
) F
.precision
-= 2;
3324 if (F
.precision
< 1) F
.precision
= 1;
3326 if (F
.precision
> mDNS_VACB_Size
- 1)
3327 F
.precision
= mDNS_VACB_Size
- 1;
3328 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
3329 for (; i
< F
.precision
; i
++) *--s
= '0';
3330 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
3333 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
3335 case 's' : s
= va_arg(arg
, char *);
3336 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
3337 else switch (F
.altForm
)
3340 if (!F
.havePrecision
) // C string
3344 while ((i
< F
.precision
) && s
[i
]) i
++;
3345 // Make sure we don't truncate in the middle of a UTF-8 character
3346 // If last character we got was any kind of UTF-8 multi-byte character,
3347 // then see if we have to back up.
3348 // This is not as easy as the similar checks below, because
3349 // here we can't assume it's safe to examine the *next* byte, so we
3350 // have to confine ourselves to working only backwards in the string.
3351 j
= i
; // Record where we got to
3352 // Now, back up until we find first non-continuation-char
3353 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
3354 // Now s[i-1] is the first non-continuation-char
3355 // and (j-i) is the number of continuation-chars we found
3356 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
3358 i
--; // Tentatively eliminate this start-char as well
3359 // Now (j-i) is the number of characters we're considering eliminating.
3360 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
3361 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
3362 // (with sign extension) then the result has to be 0xFE.
3363 // If this is right, then we reinstate the tentatively eliminated bytes.
3364 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
3368 case 1: i
= (unsigned char) *s
++; break; // Pascal string
3369 case 2: { // DNS label-sequence name
3370 unsigned char *a
= (unsigned char *)s
;
3371 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3372 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
3377 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
3378 if (s
+ *a
>= &mDNS_VACB
[254])
3379 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
3380 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3381 // so it's clear what's a literal dot and what's a label separator
3382 ConvertDomainLabelToCString((domainlabel
*)a
, buf
);
3383 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%s.", buf
);
3386 i
= (mDNSu32
)(s
- mDNS_VACB
);
3387 s
= mDNS_VACB
; // Reset s back to the start of the buffer
3391 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3392 if (F
.havePrecision
&& i
> F
.precision
)
3393 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3396 case 'n' : s
= va_arg(arg
, char *);
3397 if (F
.hSize
) * (short *) s
= (short)nwritten
;
3398 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
3399 else * (int *) s
= (int)nwritten
;
3402 default: s
= mDNS_VACB
;
3403 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
3405 case '%' : *sbuffer
++ = (char)c
;
3406 if (++nwritten
>= buflen
) goto exit
;
3410 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
3413 if (++nwritten
>= buflen
) goto exit
;
3414 } while (i
< --F
.fieldWidth
);
3416 // Make sure we don't truncate in the middle of a UTF-8 character.
3417 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3418 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3419 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3420 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3421 if (i
> buflen
- nwritten
)
3422 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3423 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
3425 if (nwritten
>= buflen
) goto exit
;
3427 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
3430 if (++nwritten
>= buflen
) goto exit
;
3439 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
3445 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);