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 IPSECPortAsNumber 4500
610 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
611 #define NATPMPAnnouncementPortAsNumber 5350
612 #define NATPMPPortAsNumber 5351
613 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
614 #define MulticastDNSPortAsNumber 5353
615 #define LoopbackIPCPortAsNumber 5354
616 //#define MulticastDNSPortAsNumber 5355 // LLMNR
617 #define PrivateDNSPortAsNumber 5533
619 mDNSexport
const mDNSIPPort DiscardPort
= { { DiscardPortAsNumber
>> 8, DiscardPortAsNumber
& 0xFF } };
620 mDNSexport
const mDNSIPPort SSHPort
= { { SSHPortAsNumber
>> 8, SSHPortAsNumber
& 0xFF } };
621 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
622 mDNSexport
const mDNSIPPort SSDPPort
= { { SSDPPortAsNumber
>> 8, SSDPPortAsNumber
& 0xFF } };
623 mDNSexport
const mDNSIPPort IPSECPort
= { { IPSECPortAsNumber
>> 8, IPSECPortAsNumber
& 0xFF } };
624 mDNSexport
const mDNSIPPort NSIPCPort
= { { NSIPCPortAsNumber
>> 8, NSIPCPortAsNumber
& 0xFF } };
625 mDNSexport
const mDNSIPPort NATPMPAnnouncementPort
= { { NATPMPAnnouncementPortAsNumber
>> 8, NATPMPAnnouncementPortAsNumber
& 0xFF } };
626 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
627 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
628 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
629 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
630 mDNSexport
const mDNSIPPort PrivateDNSPort
= { { PrivateDNSPortAsNumber
>> 8, PrivateDNSPortAsNumber
& 0xFF } };
632 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
633 mDNSexport
const mDNSv4Addr AllSystemsMcast
= { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements
634 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
635 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
636 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
637 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
639 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
640 mDNSexport
const mDNSOpaque16 onesID
= { { 255, 255 } };
641 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
642 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
643 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
644 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
645 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
647 mDNSexport
const mDNSOpaque64 zeroOpaque64
= { { 0 } };
649 // ***************************************************************************
650 #if COMPILER_LIKES_PRAGMA_MARK
652 #pragma mark - General Utility Functions
655 // return true for RFC1918 private addresses
656 mDNSexport mDNSBool
mDNSv4AddrIsRFC1918(mDNSv4Addr
*addr
)
658 return ((addr
->b
[0] == 10) || // 10/8 prefix
659 (addr
->b
[0] == 172 && (addr
->b
[1] & 0xF0) == 16) || // 172.16/12
660 (addr
->b
[0] == 192 && addr
->b
[1] == 168)); // 192.168/16
663 mDNSexport NetworkInterfaceInfo
*GetFirstActiveInterface(NetworkInterfaceInfo
*intf
)
665 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
669 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
671 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
672 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
675 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
677 mDNSu32 slot
, used
= 0;
679 const CacheRecord
*rr
;
680 FORALL_CACHERECORDS(slot
, cg
, rr
)
681 if (rr
->resrec
.InterfaceID
== id
) used
++;
685 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
689 case kDNSType_A
: return("Addr");
690 case kDNSType_NS
: return("NS");
691 case kDNSType_CNAME
:return("CNAME");
692 case kDNSType_SOA
: return("SOA");
693 case kDNSType_NULL
: return("NULL");
694 case kDNSType_PTR
: return("PTR");
695 case kDNSType_HINFO
:return("HINFO");
696 case kDNSType_TXT
: return("TXT");
697 case kDNSType_AAAA
: return("AAAA");
698 case kDNSType_SRV
: return("SRV");
699 case kDNSType_OPT
: return("OPT");
700 case kDNSType_NSEC
: return("NSEC");
701 case kDNSType_TSIG
: return("TSIG");
702 case kDNSQType_ANY
: return("ANY");
704 static char buffer
[16];
705 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
711 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
712 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
713 // long as this routine is only used for debugging messages, it probably isn't a big problem.
714 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*const rr
, const RDataBody
*const rd1
, char *const buffer
)
716 const RDataBody2
*const rd
= (RDataBody2
*)rd1
;
717 #define RemSpc (MaxMsg-1-length)
719 mDNSu32 length
= mDNS_snprintf(buffer
, MaxMsg
-1, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
720 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) return(buffer
);
721 if (!rr
->rdlength
) { mDNS_snprintf(buffer
+length
, RemSpc
, "<< ZERO RDATA LENGTH >>"); return(buffer
); }
725 case kDNSType_A
: mDNS_snprintf(buffer
+length
, RemSpc
, "%.4a", &rd
->ipv4
); break;
727 case kDNSType_NS
: // Same as PTR
728 case kDNSType_CNAME
:// Same as PTR
729 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, RemSpc
, "%##s", rd
->name
.c
); break;
731 case kDNSType_SOA
: mDNS_snprintf(buffer
+length
, RemSpc
, "%##s %##s %d %d %d %d %d",
732 rd
->soa
.mname
.c
, rd
->soa
.rname
.c
,
733 rd
->soa
.serial
, rd
->soa
.refresh
, rd
->soa
.retry
, rd
->soa
.expire
, rd
->soa
.min
);
736 case kDNSType_HINFO
:// Display this the same as TXT (show all constituent strings)
738 const mDNSu8
*t
= rd
->txt
.c
;
739 while (t
< rd
->txt
.c
+ rr
->rdlength
)
741 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "%s%#s", t
> rd
->txt
.c
? "¦" : "", t
);
746 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, RemSpc
, "%.16a", &rd
->ipv6
); break;
747 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, RemSpc
, "%u %u %u %##s",
748 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
752 const rdataOPT
*const end
= (const rdataOPT
*)&rd
->data
[rr
->rdlength
];
753 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "Max %d", rr
->rrclass
);
754 for (opt
= &rd
->opt
[0]; opt
< end
; opt
++)
759 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Vers %d", opt
->u
.llq
.vers
);
760 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Op %d", opt
->u
.llq
.llqOp
);
761 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Err/Port %d", opt
->u
.llq
.err
);
762 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " ID %08X%08X", opt
->u
.llq
.id
.l
[0], opt
->u
.llq
.id
.l
[1]);
763 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Lease %d", opt
->u
.llq
.llqlease
);
766 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Lease %d", opt
->u
.updatelease
);
769 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Vers %d", opt
->u
.owner
.vers
);
770 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Seq %3d", (mDNSu8
)opt
->u
.owner
.seq
); // Display as unsigned
771 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " MAC %.6a", opt
->u
.owner
.HMAC
.b
);
772 if (opt
->optlen
>= DNSOpt_OwnerData_ID_Wake_Space
-4)
774 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " I-MAC %.6a", opt
->u
.owner
.IMAC
.b
);
775 if (opt
->optlen
> DNSOpt_OwnerData_ID_Wake_Space
-4)
776 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Password %.6a", opt
->u
.owner
.password
.b
);
780 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Unknown %d", opt
->opt
);
787 case kDNSType_NSEC
: {
789 for (i
=0; i
<255; i
++)
790 if (rd
->nsec
.bitmap
[i
>>3] & (128 >> (i
&7)))
791 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "%s ", DNSTypeName(i
));
795 default: mDNS_snprintf(buffer
+length
, RemSpc
, "RDLen %d: %s", rr
->rdlength
, rd
->data
);
796 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
797 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
= '.';
803 // See comments in mDNSEmbeddedAPI.h
804 #if _PLATFORM_HAS_STRONG_PRNG_
805 #define mDNSRandomNumber mDNSPlatformRandomNumber
807 mDNSlocal mDNSu32
mDNSRandomFromSeed(mDNSu32 seed
)
809 return seed
* 21 + 1;
812 mDNSlocal mDNSu32
mDNSMixRandomSeed(mDNSu32 seed
, mDNSu8 iteration
)
814 return iteration
? mDNSMixRandomSeed(mDNSRandomFromSeed(seed
), --iteration
) : seed
;
817 mDNSlocal mDNSu32
mDNSRandomNumber()
819 static mDNSBool seeded
= mDNSfalse
;
820 static mDNSu32 seed
= 0;
823 seed
= mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
826 return (seed
= mDNSRandomFromSeed(seed
));
828 #endif // ! _PLATFORM_HAS_STRONG_PRNG_
830 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
) // Returns pseudo-random result from zero to max inclusive
835 while (mask
< max
) mask
= (mask
<< 1) | 1;
837 do ret
= mDNSRandomNumber() & mask
;
843 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
845 if (ip1
->type
== ip2
->type
)
849 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
850 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
851 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
857 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
861 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip
->ip
.v4
, AllDNSLinkGroup_v4
.ip
.v4
));
862 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip
->ip
.v6
, AllDNSLinkGroup_v6
.ip
.v6
));
863 default: return(mDNSfalse
);
867 // ***************************************************************************
868 #if COMPILER_LIKES_PRAGMA_MARK
870 #pragma mark - Domain Name Utility Functions
873 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
876 const int len
= *a
++;
878 if (len
> MAX_DOMAIN_LABEL
)
879 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
881 if (len
!= *b
++) return(mDNSfalse
);
882 for (i
=0; i
<len
; i
++)
886 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
887 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
888 if (ac
!= bc
) return(mDNSfalse
);
893 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
895 const mDNSu8
* a
= d1
->c
;
896 const mDNSu8
* b
= d2
->c
;
897 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
901 if (a
+ 1 + *a
>= max
)
902 { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse
); }
903 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
911 mDNSexport mDNSBool
SameDomainNameCS(const domainname
*const d1
, const domainname
*const d2
)
913 mDNSu16 l1
= DomainNameLength(d1
);
914 mDNSu16 l2
= DomainNameLength(d2
);
915 return(l1
<= MAX_DOMAIN_NAME
&& l1
== l2
&& mDNSPlatformMemSame(d1
, d2
, l1
));
918 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
920 // Domains that are defined to be resolved via link-local multicast are:
921 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
922 static const domainname
*nL
= (const domainname
*)"\x5" "local";
923 static const domainname
*nR
= (const domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
924 static const domainname
*n8
= (const domainname
*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
925 static const domainname
*n9
= (const domainname
*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
926 static const domainname
*nA
= (const domainname
*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
927 static const domainname
*nB
= (const domainname
*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
929 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
; // Top-level domain, second-level domain, etc.
930 d1
= d2
= d3
= d4
= d5
= mDNSNULL
;
933 d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
934 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
937 if (d1
&& SameDomainName(d1
, nL
)) return(mDNStrue
);
938 if (d4
&& SameDomainName(d4
, nR
)) return(mDNStrue
);
939 if (d5
&& SameDomainName(d5
, n8
)) return(mDNStrue
);
940 if (d5
&& SameDomainName(d5
, n9
)) return(mDNStrue
);
941 if (d5
&& SameDomainName(d5
, nA
)) return(mDNStrue
);
942 if (d5
&& SameDomainName(d5
, nB
)) return(mDNStrue
);
946 mDNSexport
const mDNSu8
*LastLabel(const domainname
*d
)
948 const mDNSu8
*p
= d
->c
;
952 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
957 // Returns length of a domain name INCLUDING the byte for the final null label
958 // e.g. for the root label "." it returns one
959 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
960 // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
961 // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
962 mDNSexport mDNSu16
DomainNameLengthLimit(const domainname
*const name
, const mDNSu8
*limit
)
964 const mDNSu8
*src
= name
->c
;
965 while (src
< limit
&& *src
<= MAX_DOMAIN_LABEL
)
967 if (*src
== 0) return((mDNSu16
)(src
- name
->c
+ 1));
970 return(MAX_DOMAIN_NAME
+1);
973 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
974 // for the final null label, e.g. for the root label "." it returns one.
975 // E.g. for the FQDN "foo.com." it returns 9
976 // (length, three data bytes, length, three more data bytes, final zero).
977 // In the case where a parent domain name is provided, and the given name is a child
978 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
979 // of the child name, plus TWO bytes for the compression pointer.
980 // E.g. for the name "foo.com." with parent "com.", it returns 6
981 // (length, three data bytes, two-byte compression pointer).
982 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
984 const mDNSu8
*src
= name
->c
;
985 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
988 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
989 if (parent
&& SameDomainName((const domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
991 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
993 return((mDNSu16
)(src
- name
->c
+ 1));
996 // CountLabels() returns number of labels in name, excluding final root label
997 // (e.g. for "apple.com." CountLabels returns 2.)
998 mDNSexport
int CountLabels(const domainname
*d
)
1002 for (ptr
= d
->c
; *ptr
; ptr
= ptr
+ ptr
[0] + 1) count
++;
1006 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
1007 // returning a pointer to the suffix with 'skip' labels removed.
1008 mDNSexport
const domainname
*SkipLeadingLabels(const domainname
*d
, int skip
)
1010 while (skip
> 0 && d
->c
[0]) { d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]); skip
--; }
1014 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
1015 // The C string contains the label as-is, with no escaping, etc.
1016 // Any dots in the name are literal dots, not label separators
1017 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
1018 // in the domainname bufer (i.e. the next byte after the terminating zero).
1019 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1020 // AppendLiteralLabelString returns mDNSNULL.
1021 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
1023 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1024 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1025 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
1026 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
1027 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
1029 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
1030 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
1031 *ptr
++ = 0; // Put the null root label on the end
1032 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
1033 else return(ptr
); // Success: return new value of ptr
1036 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
1037 // The C string is in conventional DNS syntax:
1038 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1039 // If successful, AppendDNSNameString returns a pointer to the next unused byte
1040 // in the domainname bufer (i.e. the next byte after the terminating zero).
1041 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1042 // AppendDNSNameString returns mDNSNULL.
1043 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
1045 const char *cstr
= cstring
;
1046 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1047 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1048 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
1050 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
1051 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
1052 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
1054 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
1055 if (c
== '\\') // If escape character, check next character
1057 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
1058 if (mDNSIsDigit(cstr
[-1]) && mDNSIsDigit(cstr
[0]) && mDNSIsDigit(cstr
[1]))
1059 { // If three decimal digits,
1060 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
1061 int v1
= cstr
[ 0] - '0';
1062 int v2
= cstr
[ 1] - '0';
1063 int val
= v0
* 100 + v1
* 10 + v2
;
1064 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
1067 *ptr
++ = c
; // Write the character
1069 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
1070 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
1072 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
1075 *ptr
++ = 0; // Put the null root label on the end
1076 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
1077 else return(ptr
); // Success: return new value of ptr
1080 // AppendDomainLabel appends a single label to a name.
1081 // If successful, AppendDomainLabel returns a pointer to the next unused byte
1082 // in the domainname bufer (i.e. the next byte after the terminating zero).
1083 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1084 // AppendDomainLabel returns mDNSNULL.
1085 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
1088 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
1090 // Check label is legal
1091 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
1093 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
1094 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
1096 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
1097 *ptr
++ = 0; // Put the null root label on the end
1101 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
1103 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1104 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1105 const mDNSu8
* src
= append
->c
;
1109 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
1110 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
1111 *ptr
= 0; // Put the null root label on the end
1117 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
1118 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
1119 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
1120 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
1121 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
1122 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
1123 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
1125 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
1126 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
1127 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
1128 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
1129 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
1132 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
1133 // The C string is in conventional DNS syntax:
1134 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1135 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
1136 // in the domainname bufer (i.e. the next byte after the terminating zero).
1137 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1138 // MakeDomainNameFromDNSNameString returns mDNSNULL.
1139 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
1141 name
->c
[0] = 0; // Make an empty domain name
1142 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
1145 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
1147 const mDNSu8
* src
= label
->c
; // Domain label we're reading
1148 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
1149 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
1150 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
1151 while (src
< end
) // While we have characters in the label
1156 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
1157 *ptr
++ = esc
; // Output escape character
1158 else if (c
<= ' ') // If non-printing ascii,
1159 { // Output decimal escape sequence
1161 *ptr
++ = (char) ('0' + (c
/ 100) );
1162 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
1163 c
= (mDNSu8
)('0' + (c
) % 10);
1166 *ptr
++ = (char)c
; // Copy the character
1168 *ptr
= 0; // Null-terminate the string
1169 return(ptr
); // and return
1172 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
1173 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
1175 const mDNSu8
*src
= name
->c
; // Domain name we're reading
1176 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1178 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
1180 while (*src
) // While more characters in the domain name
1182 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
1183 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
1184 if (!ptr
) return(mDNSNULL
);
1186 *ptr
++ = '.'; // Write the dot after the label
1189 *ptr
++ = 0; // Null-terminate the string
1190 return(ptr
); // and return
1194 // Host names must start with a letter, end with a letter or digit,
1195 // and have as interior characters only letters, digits, and hyphen.
1196 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
1198 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
1200 const mDNSu8
* src
= &UTF8Name
[1];
1201 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
1202 mDNSu8
* ptr
= &hostlabel
->c
[1];
1203 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
1206 // Delete apostrophes from source name
1207 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
1208 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
1209 { src
+= 3; continue; } // Unicode curly apostrophe
1212 if (mDNSValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
1213 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
1217 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
1218 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
1221 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
1222 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
1223 ((X)[4] | 0x20) == 'p')
1225 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
1226 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
1229 mDNSu8
*dst
= fqdn
->c
;
1231 const char *errormsg
;
1232 #if APPLE_OSX_mDNSResponder
1233 mDNSBool loggedUnderscore
= mDNSfalse
;
1234 static char typeBuf
[MAX_ESCAPED_DOMAIN_NAME
];
1237 // In the case where there is no name (and ONLY in that case),
1238 // a single-label subtype is allowed as the first label of a three-part "type"
1241 const mDNSu8
*s0
= type
->c
;
1242 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
1244 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
1245 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1247 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
1248 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
1250 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
1251 src
= s0
; // Copy the first label
1253 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
1254 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
1255 type
= (const domainname
*)s1
;
1257 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1258 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1259 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1260 if (SameDomainName((domainname
*)s0
, (const domainname
*)"\x09_services\x07_dns-sd\x04_udp"))
1261 dst
-= sizeof(SubTypeLabel
);
1267 if (name
&& name
->c
[0])
1269 src
= name
->c
; // Put the service name into the domain name
1271 if (len
>= 0x40) { errormsg
= "Service instance name too long"; goto fail
; }
1272 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1275 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1277 src
= type
->c
; // Put the service type into the domain name
1279 if (len
< 2 || len
> 15)
1281 LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-14 characters. "
1282 "See <http://www.dns-sd.org/ServiceTypes.html>", name
->c
, type
->c
, domain
->c
);
1283 #if APPLE_OSX_mDNSResponder
1284 ConvertDomainNameToCString(type
, typeBuf
);
1285 mDNSASLLog(mDNSNULL
, "serviceType.nameTooLong", "noop", typeBuf
, "");
1288 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
))) return(mDNSNULL
);
1289 if (src
[1] != '_') { errormsg
= "Application protocol name must begin with underscore"; goto fail
; }
1290 for (i
=2; i
<=len
; i
++)
1292 // Letters and digits are allowed anywhere
1293 if (mDNSIsLetter(src
[i
]) || mDNSIsDigit(src
[i
])) continue;
1294 // Hyphens are only allowed as interior characters
1295 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1296 // with the same rule as hyphens
1297 if ((src
[i
] == '-' || src
[i
] == '_') && i
> 2 && i
< len
)
1299 #if APPLE_OSX_mDNSResponder
1300 if (src
[i
] == '_' && loggedUnderscore
== mDNSfalse
)
1302 ConvertDomainNameToCString(type
, typeBuf
);
1303 mDNSASLLog(mDNSNULL
, "serviceType.nameWithUnderscore", "noop", typeBuf
, "");
1304 loggedUnderscore
= mDNStrue
;
1309 errormsg
= "Application protocol name must contain only letters, digits, and hyphens";
1310 #if APPLE_OSX_mDNSResponder
1312 ConvertDomainNameToCString(type
, typeBuf
);
1313 mDNSASLLog(mDNSNULL
, "serviceType.nameWithIllegalCharacters", "noop", typeBuf
, "");
1318 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1321 if (!ValidTransportProtocol(src
)) { errormsg
= "Transport protocol name must be _udp or _tcp"; goto fail
; }
1322 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1324 if (*src
) { errormsg
= "Service type must have only two labels"; goto fail
; }
1327 if (!domain
->c
[0]) { errormsg
= "Service domain must be non-empty"; goto fail
; }
1328 if (SameDomainName(domain
, (const domainname
*)"\x05" "local" "\x04" "arpa"))
1329 { errormsg
= "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
1330 dst
= AppendDomainName(fqdn
, domain
);
1331 if (!dst
) { errormsg
= "Service domain too long"; goto fail
; }
1335 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
1339 // A service name has the form: instance.application-protocol.transport-protocol.domain
1340 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1341 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1342 // However, if the given FQDN doesn't contain at least three labels,
1343 // DeconstructServiceName will reject it and return mDNSfalse.
1344 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
1345 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
1348 const mDNSu8
*src
= fqdn
->c
;
1349 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
1352 dst
= name
->c
; // Extract the service name
1354 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
1355 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
1356 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1358 dst
= type
->c
; // Extract the service type
1360 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
1361 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
1362 if (src
[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse
); }
1363 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1366 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
1367 if (!ValidTransportProtocol(src
))
1368 { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse
); }
1369 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1370 *dst
++ = 0; // Put terminator on the end of service type
1372 dst
= domain
->c
; // Extract the service domain
1377 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
1378 if (src
+ 1 + len
+ 1 >= max
)
1379 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
1380 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1382 *dst
++ = 0; // Put the null root label on the end
1388 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1389 // 10xxxxxx is a continuation byte of a multi-byte character
1390 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1391 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1392 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1393 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1394 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1396 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1397 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1398 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1399 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1400 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1402 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
1406 mDNSu8 c1
= string
[max
]; // First byte after cut point
1407 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : (mDNSu8
)0xB0; // Second byte after cut point
1408 length
= max
; // Trim length down
1411 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1412 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1413 // If so, then we continue to chop more bytes until we get to a legal chop point.
1414 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1415 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1416 if (!continuation
&& !secondsurrogate
) break;
1418 c1
= string
[--length
];
1420 // Having truncated characters off the end of our string, also cut off any residual white space
1421 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1426 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1427 // name ends in "-nnn", where n is some decimal number.
1428 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1430 mDNSu16 l
= name
->c
[0];
1434 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1435 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1436 if (!mDNSIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1438 while (l
> 2 && mDNSIsDigit(name
->c
[l
])) l
--; // Strip off digits
1439 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1443 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1444 if (!mDNSIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1446 while (l
> 2 && mDNSIsDigit(name
->c
[l
])) l
--; // Strip off digits
1447 return (name
->c
[l
] == '-');
1451 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1452 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1453 // from the suffix that was removed.
1454 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1456 mDNSu32 val
= 0, multiplier
= 1;
1458 // Chop closing parentheses from RichText suffix
1459 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1461 // Get any existing numerical suffix off the name
1462 while (mDNSIsDigit(name
->c
[name
->c
[0]]))
1463 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1465 // Chop opening parentheses or dash from suffix
1468 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1472 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1478 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1479 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1480 mDNSexport
void AppendLabelSuffix(domainlabel
*const name
, mDNSu32 val
, const mDNSBool RichText
)
1482 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1483 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1485 // Truncate trailing spaces from RichText names
1486 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1488 while (divisor
< 0xFFFFFFFFUL
/10 && val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1490 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1492 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1493 else { name
->c
[++name
->c
[0]] = '-'; }
1497 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1502 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1505 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1509 if (LabelContainsSuffix(name
, RichText
))
1510 val
= RemoveLabelSuffix(name
, RichText
);
1512 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1513 // If existing suffix in the range 2-9, increment it.
1514 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1515 // so add a random increment to improve the chances of finding an available name next time.
1516 if (val
== 0) val
= 2;
1517 else if (val
< 10) val
++;
1518 else val
+= 1 + mDNSRandom(99);
1520 AppendLabelSuffix(name
, val
, RichText
);
1523 // ***************************************************************************
1524 #if COMPILER_LIKES_PRAGMA_MARK
1526 #pragma mark - Resource Record Utility Functions
1529 // Set up a AuthRecord with sensible default values.
1530 // These defaults may be overwritten with new values before mDNS_Register is called
1531 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
1532 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
1534 // Don't try to store a TTL bigger than we can represent in platform time units
1535 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
1536 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
1537 else if (ttl
== 0) // And Zero TTL is illegal
1538 ttl
= DefaultTTLforRRType(rrtype
);
1540 // Field Group 1: The actual information pertaining to this resource record
1541 rr
->resrec
.RecordType
= RecordType
;
1542 rr
->resrec
.InterfaceID
= InterfaceID
;
1543 rr
->resrec
.name
= &rr
->namestorage
;
1544 rr
->resrec
.rrtype
= rrtype
;
1545 rr
->resrec
.rrclass
= kDNSClass_IN
;
1546 rr
->resrec
.rroriginalttl
= ttl
;
1547 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1548 // rr->resrec.rdestimate = set in mDNS_Register_internal
1549 // rr->resrec.rdata = MUST be set by client
1552 rr
->resrec
.rdata
= RDataStorage
;
1555 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1556 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1559 // Field Group 2: Persistent metadata for Authoritative Records
1560 rr
->Additional1
= mDNSNULL
;
1561 rr
->Additional2
= mDNSNULL
;
1562 rr
->DependentOn
= mDNSNULL
;
1563 rr
->RRSet
= mDNSNULL
;
1564 rr
->RecordCallback
= Callback
;
1565 rr
->RecordContext
= Context
;
1567 rr
->AutoTarget
= Target_Manual
;
1568 rr
->AllowRemoteQuery
= mDNSfalse
;
1569 rr
->ForceMCast
= mDNSfalse
;
1571 rr
->WakeUp
= zeroOwner
;
1572 rr
->AddressProxy
= zeroAddr
;
1576 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1577 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1579 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1580 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1581 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1582 rr
->state
= regState_Zero
;
1586 rr
->updateid
= zeroID
;
1587 rr
->zone
= rr
->resrec
.name
;
1588 rr
->UpdateServer
= zeroAddr
;
1589 rr
->UpdatePort
= zeroIPPort
;
1594 rr
->InFlightRData
= 0;
1595 rr
->InFlightRDLen
= 0;
1596 rr
->QueuedRData
= 0;
1597 rr
->QueuedRDLen
= 0;
1599 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
1602 mDNSexport
void mDNS_SetupQuestion(DNSQuestion
*const q
, const mDNSInterfaceID InterfaceID
, const domainname
*const name
,
1603 const mDNSu16 qtype
, mDNSQuestionCallback
*const callback
, void *const context
)
1605 q
->InterfaceID
= InterfaceID
;
1606 q
->Target
= zeroAddr
;
1607 AssignDomainName(&q
->qname
, name
);
1609 q
->qclass
= kDNSClass_IN
;
1610 q
->LongLived
= (qtype
== kDNSType_PTR
);
1611 q
->ExpectUnique
= (qtype
!= kDNSType_PTR
);
1612 q
->ForceMCast
= mDNSfalse
;
1613 q
->ReturnIntermed
= mDNSfalse
;
1614 q
->QuestionCallback
= callback
;
1615 q
->QuestionContext
= context
;
1618 mDNSexport mDNSu32
RDataHashValue(const ResourceRecord
*const rr
)
1620 int len
= rr
->rdlength
;
1621 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
1625 case kDNSType_CNAME
:
1627 case kDNSType_DNAME
: return DomainNameHashValue(&rdb
->name
);
1629 case kDNSType_SOA
: return rdb
->soa
.serial
+
1634 DomainNameHashValue(&rdb
->soa
.mname
) +
1635 DomainNameHashValue(&rdb
->soa
.rname
);
1638 case kDNSType_AFSDB
:
1640 case kDNSType_KX
: return DomainNameHashValue(&rdb
->mx
.exchange
);
1642 case kDNSType_RP
: return DomainNameHashValue(&rdb
->rp
.mbox
) + DomainNameHashValue(&rdb
->rp
.txt
);
1644 case kDNSType_PX
: return DomainNameHashValue(&rdb
->px
.map822
) + DomainNameHashValue(&rdb
->px
.mapx400
);
1646 case kDNSType_SRV
: return DomainNameHashValue(&rdb
->srv
.target
);
1648 case kDNSType_OPT
: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1650 case kDNSType_NSEC
: len
= sizeof(rdataNSEC
); // Use in-memory length of 32, and fall through default checksum computation below
1656 for (i
=0; i
+1 < len
; i
+=2)
1658 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1659 sum
= (sum
<<3) | (sum
>>29);
1663 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1670 // r1 has to be a full ResourceRecord including rrtype and rdlength
1671 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1672 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1674 const RDataBody2
*const b1
= (RDataBody2
*)r1
->rdata
->u
.data
;
1675 const RDataBody2
*const b2
= (RDataBody2
*)r2
;
1679 case kDNSType_CNAME
:
1681 case kDNSType_DNAME
:return(SameDomainName(&b1
->name
, &b2
->name
));
1683 case kDNSType_SOA
: return(mDNSBool
)( b1
->soa
.serial
== b2
->soa
.serial
&&
1684 b1
->soa
.refresh
== b2
->soa
.refresh
&&
1685 b1
->soa
.retry
== b2
->soa
.retry
&&
1686 b1
->soa
.expire
== b2
->soa
.expire
&&
1687 b1
->soa
.min
== b2
->soa
.min
&&
1688 SameDomainName(&b1
->soa
.mname
, &b2
->soa
.mname
) &&
1689 SameDomainName(&b1
->soa
.rname
, &b2
->soa
.rname
));
1692 case kDNSType_AFSDB
:
1694 case kDNSType_KX
: return(mDNSBool
)( b1
->mx
.preference
== b2
->mx
.preference
&&
1695 SameDomainName(&b1
->mx
.exchange
, &b2
->mx
.exchange
));
1697 case kDNSType_RP
: return(mDNSBool
)( SameDomainName(&b1
->rp
.mbox
, &b2
->rp
.mbox
) &&
1698 SameDomainName(&b1
->rp
.txt
, &b2
->rp
.txt
));
1700 case kDNSType_PX
: return(mDNSBool
)( b1
->px
.preference
== b2
->px
.preference
&&
1701 SameDomainName(&b1
->px
.map822
, &b2
->px
.map822
) &&
1702 SameDomainName(&b1
->px
.mapx400
, &b2
->px
.mapx400
));
1704 case kDNSType_SRV
: return(mDNSBool
)( b1
->srv
.priority
== b2
->srv
.priority
&&
1705 b1
->srv
.weight
== b2
->srv
.weight
&&
1706 mDNSSameIPPort(b1
->srv
.port
, b2
->srv
.port
) &&
1707 SameDomainName(&b1
->srv
.target
, &b2
->srv
.target
));
1709 case kDNSType_OPT
: return mDNSfalse
; // OPT is a pseudo-RR container structure; makes no sense to compare
1711 case kDNSType_NSEC
: return(mDNSPlatformMemSame(b1
->data
, b2
->data
, sizeof(rdataNSEC
)));
1713 default: return(mDNSPlatformMemSame(b1
->data
, b2
->data
, r1
->rdlength
));
1717 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1718 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1719 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1720 // because it has to check all the way to the end of the names to be sure.
1721 // In cases where we know in advance that the names match it's especially advantageous to skip the
1722 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1724 mDNSexport mDNSBool
SameNameRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1726 if (rr
->InterfaceID
&&
1727 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1728 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1730 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1731 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1733 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1734 if (!RRTypeAnswersQuestionType(rr
,q
->qtype
)) return(mDNSfalse
);
1735 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1740 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1742 if (rr
->InterfaceID
&&
1743 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1744 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1746 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1747 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1749 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1750 if (!RRTypeAnswersQuestionType(rr
,q
->qtype
)) return(mDNSfalse
);
1751 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1753 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1756 mDNSexport mDNSBool
AnyTypeRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1758 if (rr
->InterfaceID
&&
1759 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1760 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1762 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1763 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1765 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1767 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1770 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1772 const RDataBody2
*const rd
= (RDataBody2
*)rr
->rdata
->u
.data
;
1773 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1774 if (rr
->rrclass
== kDNSQClass_ANY
) return(rr
->rdlength
); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1775 else switch (rr
->rrtype
)
1777 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1780 case kDNSType_CNAME
:
1782 case kDNSType_DNAME
:return(CompressedDomainNameLength(&rd
->name
, name
));
1784 case kDNSType_SOA
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1785 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1786 5 * sizeof(mDNSOpaque32
));
1794 case kDNSType_DHCID
:return(rr
->rdlength
); // Not self-describing, so have to just trust rdlength
1796 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1799 case kDNSType_AFSDB
:
1801 case kDNSType_KX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->mx
.exchange
, name
));
1803 case kDNSType_RP
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->rp
.mbox
, name
) +
1804 CompressedDomainNameLength(&rd
->rp
.txt
, name
));
1806 case kDNSType_PX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->px
.map822
, name
) +
1807 CompressedDomainNameLength(&rd
->px
.mapx400
, name
));
1809 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1811 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1813 case kDNSType_OPT
: return(rr
->rdlength
);
1815 case kDNSType_NSEC
: {
1817 for (i
=sizeof(rdataNSEC
); i
>0; i
--) if (rd
->nsec
.bitmap
[i
-1]) break;
1818 // For our simplified use of NSEC synthetic records:
1819 // nextname is always the record's own name,
1820 // the block number is always 0,
1821 // the count byte is a value in the range 1-32,
1822 // followed by the 1-32 data bytes
1823 return((estimate
? 1 : DomainNameLength(rr
->name
)) + 2 + i
);
1826 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1827 return(rr
->rdlength
);
1831 // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
1832 // to help reduce the risk of bogus malformed data on the network
1833 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1839 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1841 case kDNSType_NS
: // Same as PTR
1842 case kDNSType_MD
: // Same as PTR
1843 case kDNSType_MF
: // Same as PTR
1844 case kDNSType_CNAME
:// Same as PTR
1845 //case kDNSType_SOA not checked
1846 case kDNSType_MB
: // Same as PTR
1847 case kDNSType_MG
: // Same as PTR
1848 case kDNSType_MR
: // Same as PTR
1849 //case kDNSType_NULL not checked (no specified format, so always valid)
1850 //case kDNSType_WKS not checked
1851 case kDNSType_PTR
: len
= DomainNameLengthLimit(&rd
->u
.name
, rd
->u
.data
+ rdlength
);
1852 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1854 case kDNSType_HINFO
:// Same as TXT (roughly)
1855 case kDNSType_MINFO
:// Same as TXT (roughly)
1856 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1858 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1859 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1860 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1861 return (ptr
== end
);
1864 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1866 case kDNSType_MX
: // Must be at least two-byte preference, plus domainname
1867 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1868 len
= DomainNameLengthLimit(&rd
->u
.mx
.exchange
, rd
->u
.data
+ rdlength
);
1869 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1871 case kDNSType_SRV
: // Must be at least priority+weight+port, plus domainname
1872 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1873 len
= DomainNameLengthLimit(&rd
->u
.srv
.target
, rd
->u
.data
+ rdlength
);
1874 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1876 //case kDNSType_NSEC not checked
1878 default: return(mDNStrue
); // Allow all other types without checking
1882 // ***************************************************************************
1883 #if COMPILER_LIKES_PRAGMA_MARK
1885 #pragma mark - DNS Message Creation Functions
1888 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1892 h
->numQuestions
= 0;
1894 h
->numAuthorities
= 0;
1895 h
->numAdditionals
= 0;
1898 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1900 const mDNSu8
*result
= end
- *domname
- 1;
1902 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1904 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1905 while (result
>= base
)
1907 // If the length byte and first character of the label match, then check further to see
1908 // if this location in the packet will yield a useful name compression pointer.
1909 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1911 const mDNSu8
*name
= domname
;
1912 const mDNSu8
*targ
= result
;
1913 while (targ
+ *name
< end
)
1915 // First see if this label matches
1917 const mDNSu8
*pointertarget
;
1918 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1919 if (i
<= *name
) break; // If label did not match, bail out
1920 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1921 name
+= 1 + *name
; // and proceed to check next label
1922 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1923 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1925 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1926 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1927 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1928 if (targ
+1 >= end
) break; // Second byte not present!
1929 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1930 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1931 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1932 targ
= pointertarget
;
1935 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1940 // Put a string of dot-separated labels as length-prefixed labels
1941 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1942 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1943 // end points to the end of the message so far
1944 // ptr points to where we want to put the name
1945 // limit points to one byte past the end of the buffer that we must not overrun
1946 // domainname is the name to put
1947 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1948 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1950 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1951 const mDNSu8
* np
= name
->c
;
1952 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1953 const mDNSu8
* pointer
= mDNSNULL
;
1954 const mDNSu8
*const searchlimit
= ptr
;
1956 if (!ptr
) { LogMsg("putDomainNameAsLabels %##s ptr is null", name
->c
); return(mDNSNULL
); }
1958 if (!*np
) // If just writing one-byte root label, make sure we have space for that
1960 if (ptr
>= limit
) return(mDNSNULL
);
1962 else // else, loop through writing labels and/or a compression offset
1965 if (*np
> MAX_DOMAIN_LABEL
)
1966 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1968 // This check correctly allows for the final trailing root label:
1970 // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
1971 // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
1972 // We know that max will be at name->c[256]
1973 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1974 // six bytes, then exit the loop, write the final terminating root label, and the domain
1975 // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
1976 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1977 if (np
+ 1 + *np
>= max
)
1978 { LogMsg("Malformed domain name %##s (more than 256 bytes)", name
->c
); return(mDNSNULL
); }
1980 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1981 if (pointer
) // Use a compression pointer if we can
1983 const mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1984 if (ptr
+2 > limit
) return(mDNSNULL
); // If we don't have two bytes of space left, give up
1985 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1986 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1989 else // Else copy one label and try again
1993 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
1994 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1996 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1998 } while (*np
); // While we've got characters remaining in the name, continue
2001 *ptr
++ = 0; // Put the final root label
2005 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
2007 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
2008 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
2009 return ptr
+ sizeof(mDNSOpaque16
);
2012 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
2014 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
2015 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
2016 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
2017 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
2018 return ptr
+ sizeof(mDNSu32
);
2021 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
2022 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const ResourceRecord
*const rr
)
2024 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
2027 case kDNSType_A
: if (rr
->rdlength
!= 4)
2028 { debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
); return(mDNSNULL
); }
2029 if (ptr
+ 4 > limit
) return(mDNSNULL
);
2030 *ptr
++ = rdb
->ipv4
.b
[0];
2031 *ptr
++ = rdb
->ipv4
.b
[1];
2032 *ptr
++ = rdb
->ipv4
.b
[2];
2033 *ptr
++ = rdb
->ipv4
.b
[3];
2037 case kDNSType_CNAME
:
2039 case kDNSType_DNAME
:return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->name
));
2041 case kDNSType_SOA
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->soa
.mname
);
2042 if (!ptr
) return(mDNSNULL
);
2043 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->soa
.rname
);
2044 if (!ptr
|| ptr
+ 20 > limit
) return(mDNSNULL
);
2045 ptr
= putVal32(ptr
, rdb
->soa
.serial
);
2046 ptr
= putVal32(ptr
, rdb
->soa
.refresh
);
2047 ptr
= putVal32(ptr
, rdb
->soa
.retry
);
2048 ptr
= putVal32(ptr
, rdb
->soa
.expire
);
2049 ptr
= putVal32(ptr
, rdb
->soa
.min
);
2053 case kDNSType_HINFO
:
2059 case kDNSType_DHCID
:if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
2060 mDNSPlatformMemCopy(ptr
, rdb
->data
, rr
->rdlength
);
2061 return(ptr
+ rr
->rdlength
);
2064 case kDNSType_AFSDB
:
2066 case kDNSType_KX
: if (ptr
+ 3 > limit
) return(mDNSNULL
);
2067 ptr
= putVal16(ptr
, rdb
->mx
.preference
);
2068 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->mx
.exchange
));
2070 case kDNSType_RP
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->rp
.mbox
);
2071 if (!ptr
) return(mDNSNULL
);
2072 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->rp
.txt
);
2075 case kDNSType_PX
: if (ptr
+ 5 > limit
) return(mDNSNULL
);
2076 ptr
= putVal16(ptr
, rdb
->px
.preference
);
2077 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->px
.map822
);
2078 if (!ptr
) return(mDNSNULL
);
2079 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->px
.mapx400
);
2082 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rdb
->ipv6
))
2083 { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
); return(mDNSNULL
); }
2084 if (ptr
+ sizeof(rdb
->ipv6
) > limit
) return(mDNSNULL
);
2085 mDNSPlatformMemCopy(ptr
, &rdb
->ipv6
, sizeof(rdb
->ipv6
));
2086 return(ptr
+ sizeof(rdb
->ipv6
));
2088 case kDNSType_SRV
: if (ptr
+ 7 > limit
) return(mDNSNULL
);
2089 *ptr
++ = (mDNSu8
)(rdb
->srv
.priority
>> 8);
2090 *ptr
++ = (mDNSu8
)(rdb
->srv
.priority
& 0xFF);
2091 *ptr
++ = (mDNSu8
)(rdb
->srv
.weight
>> 8);
2092 *ptr
++ = (mDNSu8
)(rdb
->srv
.weight
& 0xFF);
2093 *ptr
++ = rdb
->srv
.port
.b
[0];
2094 *ptr
++ = rdb
->srv
.port
.b
[1];
2095 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->srv
.target
));
2097 case kDNSType_OPT
: {
2099 const rdataOPT
*opt
;
2100 const rdataOPT
*const end
= (const rdataOPT
*)&rr
->rdata
->u
.data
[rr
->rdlength
];
2101 for (opt
= &rr
->rdata
->u
.opt
[0]; opt
< end
; opt
++) len
+= DNSOpt_Data_Space(opt
);
2102 if (ptr
+ len
> limit
) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL
; }
2104 for (opt
= &rr
->rdata
->u
.opt
[0]; opt
< end
; opt
++)
2106 const int space
= DNSOpt_Data_Space(opt
);
2107 ptr
= putVal16(ptr
, opt
->opt
);
2108 ptr
= putVal16(ptr
, space
- 4);
2112 ptr
= putVal16(ptr
, opt
->u
.llq
.vers
);
2113 ptr
= putVal16(ptr
, opt
->u
.llq
.llqOp
);
2114 ptr
= putVal16(ptr
, opt
->u
.llq
.err
);
2115 mDNSPlatformMemCopy(ptr
, opt
->u
.llq
.id
.b
, 8); // 8-byte id
2117 ptr
= putVal32(ptr
, opt
->u
.llq
.llqlease
);
2120 ptr
= putVal32(ptr
, opt
->u
.updatelease
);
2123 *ptr
++ = opt
->u
.owner
.vers
;
2124 *ptr
++ = opt
->u
.owner
.seq
;
2125 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.HMAC
.b
, 6); // 6-byte Host identifier
2127 if (space
>= DNSOpt_OwnerData_ID_Wake_Space
)
2129 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.IMAC
.b
, 6); // 6-byte interface MAC
2131 if (space
> DNSOpt_OwnerData_ID_Wake_Space
)
2133 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.password
.b
, space
- DNSOpt_OwnerData_ID_Wake_Space
);
2134 ptr
+= space
- DNSOpt_OwnerData_ID_Wake_Space
;
2143 case kDNSType_NSEC
: {
2144 // For our simplified use of NSEC synthetic records:
2145 // nextname is always the record's own name,
2146 // the block number is always 0,
2147 // the count byte is a value in the range 1-32,
2148 // followed by the 1-32 data bytes
2150 for (i
=sizeof(rdataNSEC
); i
>0; i
--) if (rdb
->nsec
.bitmap
[i
-1]) break;
2151 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
2152 if (!ptr
) return(mDNSNULL
);
2153 if (ptr
+ 2 + i
> limit
) return(mDNSNULL
);
2156 for (j
=0; j
<i
; j
++) *ptr
++ = rdb
->nsec
.bitmap
[j
];
2160 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
2161 if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
2162 mDNSPlatformMemCopy(ptr
, rdb
->data
, rr
->rdlength
);
2163 return(ptr
+ rr
->rdlength
);
2167 #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2169 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
2172 mDNSu16 actualLength
;
2173 // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2174 const DNSMessage
*const rdatacompressionbase
= (IsUnicastUpdate(msg
) && rr
->rrtype
== kDNSType_SRV
) ? mDNSNULL
: msg
;
2176 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
2178 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
2182 if (!ptr
) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL
); }
2184 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
2185 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2186 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
2187 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
2188 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
2189 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
2190 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
2191 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
2192 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
2193 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
2194 endofrdata
= putRData(rdatacompressionbase
, ptr
+10, limit
, rr
);
2195 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
2197 // Go back and fill in the actual number of data bytes we wrote
2198 // (actualLength can be less than rdlength when domain name compression is used)
2199 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
2200 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
2201 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
2203 if (count
) (*count
)++;
2204 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
2208 mDNSlocal mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, mDNSu16
*count
, const AuthRecord
*rr
)
2210 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
2211 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2212 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
2213 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
2214 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
2215 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
2216 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
2217 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
2222 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
2224 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2225 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2226 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2227 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2228 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
2229 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
2230 msg
->h
.numQuestions
++;
2234 // for dynamic updates
2235 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
2237 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
2238 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
2239 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
2240 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
2241 *ptr
++ = zoneClass
.b
[0];
2242 *ptr
++ = zoneClass
.b
[1];
2243 msg
->h
.mDNS_numZones
++;
2247 // for dynamic updates
2248 mDNSexport mDNSu8
*putPrereqNameNotInUse(const domainname
*const name
, DNSMessage
*const msg
, mDNSu8
*const ptr
, mDNSu8
*const end
)
2251 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
2252 AssignDomainName(&prereq
.namestorage
, name
);
2253 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
2254 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
2255 return putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
2258 // for dynamic updates
2259 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
2261 // deletion: specify record w/ TTL 0, class NONE
2262 const mDNSu16 origclass
= rr
->rrclass
;
2263 rr
->rrclass
= kDNSClass_NONE
;
2264 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
2265 rr
->rrclass
= origclass
;
2269 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
2271 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2272 mDNSu16
class = kDNSQClass_ANY
;
2274 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2275 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2276 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2277 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2278 ptr
[2] = (mDNSu8
)(class >> 8);
2279 ptr
[3] = (mDNSu8
)(class & 0xFF);
2280 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2281 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2283 msg
->h
.mDNS_numUpdates
++;
2287 // for dynamic updates
2288 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
2290 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2291 mDNSu16
class = kDNSQClass_ANY
;
2292 mDNSu16 rrtype
= kDNSQType_ANY
;
2294 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2295 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2296 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2297 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2298 ptr
[2] = (mDNSu8
)(class >> 8);
2299 ptr
[3] = (mDNSu8
)(class & 0xFF);
2300 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2301 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2303 msg
->h
.mDNS_numUpdates
++;
2307 // for dynamic updates
2308 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
2311 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
2312 rr
.resrec
.rrclass
= NormalMaxDNSMessageData
;
2313 rr
.resrec
.rdlength
= sizeof(rdataOPT
); // One option in this OPT record
2314 rr
.resrec
.rdestimate
= sizeof(rdataOPT
);
2315 rr
.resrec
.rdata
->u
.opt
[0].opt
= kDNSOpt_Lease
;
2316 rr
.resrec
.rdata
->u
.opt
[0].u
.updatelease
= lease
;
2317 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, &rr
.resrec
, 0);
2318 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
2322 mDNSexport mDNSu8
*putHINFO(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
, DomainAuthInfo
*authInfo
)
2324 if (authInfo
&& authInfo
->AutoTunnel
)
2327 mDNSu8
*h
= hinfo
.rdatastorage
.u
.data
;
2328 mDNSu16 len
= 2 + m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0];
2330 mDNS_SetupResourceRecord(&hinfo
, mDNSNULL
, mDNSInterface_Any
, kDNSType_HINFO
, 0, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
2331 AppendDomainLabel(&hinfo
.namestorage
, &m
->hostlabel
);
2332 AppendDomainName (&hinfo
.namestorage
, &authInfo
->domain
);
2333 hinfo
.resrec
.rroriginalttl
= 0;
2334 mDNSPlatformMemCopy(h
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
2336 mDNSPlatformMemCopy(h
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
2337 hinfo
.resrec
.rdlength
= len
;
2338 hinfo
.resrec
.rdestimate
= len
;
2339 newptr
= PutResourceRecord(msg
, end
, &msg
->h
.numAdditionals
, &hinfo
.resrec
);
2346 // ***************************************************************************
2347 #if COMPILER_LIKES_PRAGMA_MARK
2349 #pragma mark - DNS Message Parsing Functions
2352 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
2357 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
2359 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
2360 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
2361 sum
= (sum
<<3) | (sum
>>29);
2363 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
2367 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
2372 rr
->rdata
= NewRData
;
2373 rr
->rdlength
= rdlength
;
2375 // Must not try to get target pointer until after updating rr->rdata
2376 target
= GetRRDomainNameTarget(rr
);
2377 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
2378 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
2379 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
);
2382 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
2386 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2387 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2389 while (1) // Read sequence of labels
2391 const mDNSu8 len
= *ptr
++; // Read length of this label
2392 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
2395 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2396 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2397 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
2398 { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL
); }
2403 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
2404 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
2405 case 0xC0: return(ptr
+1);
2410 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2411 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
2412 domainname
*const name
)
2414 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
2415 mDNSu8
*np
= name
->c
; // Name pointer
2416 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
2418 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2419 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2421 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2423 while (1) // Read sequence of labels
2425 const mDNSu8 len
= *ptr
++; // Read length of this label
2426 if (len
== 0) break; // If length is zero, that means this name is complete
2432 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2433 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2434 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
2435 { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL
); }
2437 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
2438 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2441 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
2444 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
2446 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
2447 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
2448 ptr
= (mDNSu8
*)msg
+ offset
;
2449 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2450 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
2452 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
2457 if (nextbyte
) return(nextbyte
);
2461 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2463 mDNSu16 pktrdlength
;
2465 ptr
= skipDomainName(msg
, ptr
, end
);
2466 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2468 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2469 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2471 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2473 return(ptr
+ pktrdlength
);
2476 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
2478 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
2479 *ptr
+= sizeof(mDNSOpaque16
);
2483 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
,
2484 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*const largecr
)
2486 CacheRecord
*const rr
= &largecr
->r
;
2487 RDataBody2
*const rdb
= (RDataBody2
*)rr
->smallrdatastorage
.data
;
2488 mDNSu16 pktrdlength
;
2490 if (largecr
== &m
->rec
&& m
->rec
.r
.resrec
.RecordType
)
2492 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &m
->rec
.r
));
2498 rr
->next
= mDNSNULL
;
2499 rr
->resrec
.name
= &largecr
->namestorage
;
2501 rr
->NextInKAList
= mDNSNULL
;
2502 rr
->TimeRcvd
= m
? m
->timenow
: 0;
2503 rr
->DelayDelivery
= 0;
2504 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2505 rr
->LastUsed
= m
? m
->timenow
: 0;
2506 rr
->CRActiveQuestion
= mDNSNULL
;
2507 rr
->UnansweredQueries
= 0;
2508 rr
->LastUnansweredTime
= 0;
2509 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
2510 rr
->MPUnansweredQ
= 0;
2511 rr
->MPLastUnansweredQT
= 0;
2512 rr
->MPUnansweredKA
= 0;
2513 rr
->MPExpectingKA
= mDNSfalse
;
2515 rr
->NextInCFList
= mDNSNULL
;
2517 rr
->resrec
.InterfaceID
= InterfaceID
;
2518 ptr
= getDomainName(msg
, ptr
, end
, &largecr
->namestorage
);
2519 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2521 if (ptr
+ 10 > end
) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2523 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2524 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
2525 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
2526 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
2527 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2528 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2529 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2530 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2532 // If mDNS record has cache-flush bit set, we mark it unique
2533 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2534 // authoritative for the entire RRSet), unless this is a truncated response
2535 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8) || (!InterfaceID
&& !(msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)))
2536 RecordType
|= kDNSRecordTypePacketUniqueMask
;
2538 if (ptr
+ pktrdlength
> end
) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2539 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
2541 rr
->resrec
.rdata
= (RData
*)&rr
->smallrdatastorage
;
2542 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
2544 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
2546 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2547 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2548 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2549 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2550 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2551 if (rr
->resrec
.rrclass
== kDNSQClass_ANY
&& pktrdlength
== 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2552 rr
->resrec
.rdlength
= 0;
2553 else switch (rr
->resrec
.rrtype
)
2555 case kDNSType_A
: if (pktrdlength
!= sizeof(mDNSv4Addr
)) return(mDNSNULL
);
2556 rdb
->ipv4
.b
[0] = ptr
[0];
2557 rdb
->ipv4
.b
[1] = ptr
[1];
2558 rdb
->ipv4
.b
[2] = ptr
[2];
2559 rdb
->ipv4
.b
[3] = ptr
[3];
2563 case kDNSType_CNAME
:
2565 case kDNSType_DNAME
:ptr
= getDomainName(msg
, ptr
, end
, &rdb
->name
);
2566 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
2567 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength);
2570 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rdb
->soa
.mname
);
2571 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
2572 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->soa
.rname
);
2573 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
2574 if (ptr
+ 0x14 != end
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
2575 rdb
->soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
2576 rdb
->soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
2577 rdb
->soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
2578 rdb
->soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
2579 rdb
->soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
2583 case kDNSType_HINFO
:
2589 case kDNSType_DHCID
:if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2591 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2592 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2595 rr
->resrec
.rdlength
= pktrdlength
;
2596 mDNSPlatformMemCopy(rdb
->data
, ptr
, pktrdlength
);
2600 case kDNSType_AFSDB
:
2602 case kDNSType_KX
: if (pktrdlength
< 3) return(mDNSNULL
); // Preference + domainname
2603 rdb
->mx
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2604 ptr
= getDomainName(msg
, ptr
+2, end
, &rdb
->mx
.exchange
);
2605 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL
); }
2606 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2609 case kDNSType_RP
: ptr
= getDomainName(msg
, ptr
, end
, &rdb
->rp
.mbox
); // Domainname + domainname
2610 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL
; }
2611 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->rp
.txt
);
2612 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL
; }
2615 case kDNSType_PX
: if (pktrdlength
< 4) return(mDNSNULL
); // Preference + domainname + domainname
2616 rdb
->px
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2617 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->px
.map822
);
2618 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL
; }
2619 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->px
.mapx400
);
2620 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL
; }
2623 case kDNSType_AAAA
: if (pktrdlength
!= sizeof(mDNSv6Addr
)) return(mDNSNULL
);
2624 mDNSPlatformMemCopy(&rdb
->ipv6
, ptr
, sizeof(rdb
->ipv6
));
2627 case kDNSType_SRV
: if (pktrdlength
< 7) return(mDNSNULL
); // Priority + weight + port + domainname
2628 rdb
->srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2629 rdb
->srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
2630 rdb
->srv
.port
.b
[0] = ptr
[4];
2631 rdb
->srv
.port
.b
[1] = ptr
[5];
2632 ptr
= getDomainName(msg
, ptr
+6, end
, &rdb
->srv
.target
);
2633 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
2634 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2637 case kDNSType_OPT
: {
2638 rdataOPT
*opt
= rr
->resrec
.rdata
->u
.opt
;
2639 rr
->resrec
.rdlength
= 0;
2640 while (ptr
< end
&& (mDNSu8
*)(opt
+1) < &rr
->resrec
.rdata
->u
.data
[MaximumRDSize
])
2642 if (ptr
+ 4 > end
) { LogMsg("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); return(mDNSNULL
); }
2643 opt
->opt
= getVal16(&ptr
);
2644 opt
->optlen
= getVal16(&ptr
);
2645 if (!ValidDNSOpt(opt
)) { LogMsg("GetLargeResourceRecord: opt %d optlen %d wrong", opt
->opt
, opt
->optlen
); return(mDNSNULL
); }
2646 if (ptr
+ opt
->optlen
> end
) { LogMsg("GetLargeResourceRecord: ptr + opt->optlen > end"); return(mDNSNULL
); }
2650 opt
->u
.llq
.vers
= getVal16(&ptr
);
2651 opt
->u
.llq
.llqOp
= getVal16(&ptr
);
2652 opt
->u
.llq
.err
= getVal16(&ptr
);
2653 mDNSPlatformMemCopy(opt
->u
.llq
.id
.b
, ptr
, 8);
2655 opt
->u
.llq
.llqlease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
2656 if (opt
->u
.llq
.llqlease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
2657 opt
->u
.llq
.llqlease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2658 ptr
+= sizeof(mDNSOpaque32
);
2661 opt
->u
.updatelease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
2662 if (opt
->u
.updatelease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
2663 opt
->u
.updatelease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2664 ptr
+= sizeof(mDNSs32
);
2667 opt
->u
.owner
.vers
= ptr
[0];
2668 opt
->u
.owner
.seq
= ptr
[1];
2669 mDNSPlatformMemCopy(opt
->u
.owner
.HMAC
.b
, ptr
+2, 6); // 6-byte MAC address
2670 mDNSPlatformMemCopy(opt
->u
.owner
.IMAC
.b
, ptr
+2, 6); // 6-byte MAC address
2671 opt
->u
.owner
.password
= zeroEthAddr
;
2672 if (opt
->optlen
>= DNSOpt_OwnerData_ID_Wake_Space
-4)
2674 mDNSPlatformMemCopy(opt
->u
.owner
.IMAC
.b
, ptr
+8, 6); // 6-byte MAC address
2675 if (opt
->optlen
> DNSOpt_OwnerData_ID_Wake_Space
-4)
2676 mDNSPlatformMemCopy(opt
->u
.owner
.password
.b
, ptr
+14, opt
->optlen
- (DNSOpt_OwnerData_ID_Wake_Space
-4));
2681 opt
++; // increment pointer into rdatabody
2683 rr
->resrec
.rdlength
= (mDNSu8
*)opt
- rr
->resrec
.rdata
->u
.data
;
2684 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL
); }
2688 case kDNSType_NSEC
: {
2691 ptr
= getDomainName(msg
, ptr
, end
, &d
); // Ignored for our simplified use of NSEC synthetic records
2692 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed NSEC nextname"); return mDNSNULL
; }
2693 if (*ptr
++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); return mDNSNULL
; }
2695 if (i
< 1 || i
> sizeof(rdataNSEC
)) { debugf("GetLargeResourceRecord: invalid block length %d", i
); return mDNSNULL
; }
2696 mDNSPlatformMemZero(rdb
->nsec
.bitmap
, sizeof(rdb
->nsec
.bitmap
));
2697 for (j
=0; j
<i
; j
++) rdb
->nsec
.bitmap
[j
] = *ptr
++;
2698 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed NSEC"); return(mDNSNULL
); }
2702 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2704 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2705 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2708 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2709 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
2710 // Note: Just because we don't understand the record type, that doesn't
2711 // mean we fail. The DNS protocol specifies rdlength, so we can
2712 // safely skip over unknown records and ignore them.
2713 // We also grab a binary copy of the rdata anyway, since the caller
2714 // might know how to interpret it even if we don't.
2715 rr
->resrec
.rdlength
= pktrdlength
;
2716 mDNSPlatformMemCopy(rdb
->data
, ptr
, pktrdlength
);
2720 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2721 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rdlength, rdestimate, rdatahash for us
2723 // Success! Now fill in RecordType to show this record contains valid data
2724 rr
->resrec
.RecordType
= RecordType
;
2728 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2730 ptr
= skipDomainName(msg
, ptr
, end
);
2731 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
2732 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2736 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
2737 DNSQuestion
*question
)
2739 mDNSPlatformMemZero(question
, sizeof(*question
));
2740 question
->InterfaceID
= InterfaceID
;
2741 if (!InterfaceID
) question
->TargetQID
= onesID
; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
2742 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
2743 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
2744 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2746 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
2747 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
2748 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
2752 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2755 const mDNSu8
*ptr
= msg
->data
;
2756 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
2760 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2763 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
2764 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2768 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2771 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
2772 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2776 mDNSexport
const mDNSu8
*LocateOptRR(const DNSMessage
*const msg
, const mDNSu8
*const end
, int minsize
)
2779 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2781 // Locate the OPT record.
2782 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2783 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2784 // but not necessarily the *last* entry in the Additional Section.
2785 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2787 if (ptr
+ DNSOpt_Header_Space
+ minsize
<= end
&& // Make sure we have 11+minsize bytes of data
2788 ptr
[0] == 0 && // Name must be root label
2789 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2790 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2791 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)minsize
)
2794 ptr
= skipResourceRecord(msg
, ptr
, end
);
2799 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2800 // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
2801 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2802 // The code that currently calls this assumes there's only one, instead of iterating through the set
2803 mDNSexport
const rdataOPT
*GetLLQOptData(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2805 const mDNSu8
*ptr
= LocateOptRR(msg
, end
, DNSOpt_LLQData_Space
);
2808 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2809 if (ptr
) return(&m
->rec
.r
.resrec
.rdata
->u
.opt
[0]);
2814 // Get the lease life of records in a dynamic update
2815 // returns 0 on error or if no lease present
2816 mDNSexport mDNSu32
GetPktLease(mDNS
*m
, DNSMessage
*msg
, const mDNSu8
*end
)
2819 const mDNSu8
*ptr
= LocateOptRR(msg
, end
, DNSOpt_LeaseData_Space
);
2820 if (ptr
) ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2821 if (ptr
&& m
->rec
.r
.resrec
.rdlength
>= DNSOpt_LeaseData_Space
&& m
->rec
.r
.resrec
.rdata
->u
.opt
[0].opt
== kDNSOpt_Lease
)
2822 result
= m
->rec
.r
.resrec
.rdata
->u
.opt
[0].u
.updatelease
;
2823 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
2827 mDNSlocal
const mDNSu8
*DumpRecords(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
, int count
, char *label
)
2830 LogMsg("%2d %s", count
, label
);
2831 for (i
= 0; i
< count
&& ptr
; i
++)
2833 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2834 // but since it's only used for debugging (and probably only on OS X, not on
2835 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2836 LargeCacheRecord largecr
;
2837 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, mDNSInterface_Any
, kDNSRecordTypePacketAns
, &largecr
);
2838 if (ptr
) LogMsg("%2d TTL%8d %s", i
, largecr
.r
.resrec
.rroriginalttl
, CRDisplayString(m
, &largecr
.r
));
2840 if (!ptr
) LogMsg("ERROR: Premature end of packet data");
2844 #define DNS_OP_Name(X) ( \
2845 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2846 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2847 (X) == kDNSFlag0_OP_Status ? "Status " : \
2848 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2849 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2850 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2852 #define DNS_RC_Name(X) ( \
2853 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2854 (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
2855 (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
2856 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2857 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2858 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2859 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2860 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2861 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2862 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2863 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2865 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2866 mDNSexport
void DumpPacket(mDNS
*const m
, mStatus status
, mDNSBool sent
, char *transport
,
2867 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
,
2868 const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2870 mDNSBool IsUpdate
= ((msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
) == kDNSFlag0_OP_Update
);
2871 const mDNSu8
*ptr
= msg
->data
;
2874 char tbuffer
[64], sbuffer
[64], dbuffer
[64] = "";
2875 if (!status
) tbuffer
[mDNS_snprintf(tbuffer
, sizeof(tbuffer
), sent
? "Sent" : "Received" )] = 0;
2876 else tbuffer
[mDNS_snprintf(tbuffer
, sizeof(tbuffer
), "ERROR %d %sing", status
, sent
? "Send" : "Receiv")] = 0;
2877 if (sent
) sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "port " )] = 0;
2878 else sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "%#a:", srcaddr
)] = 0;
2879 if (dstaddr
|| !mDNSIPPortIsZero(dstport
))
2880 dbuffer
[mDNS_snprintf(dbuffer
, sizeof(dbuffer
), " to %#a:%d", dstaddr
, mDNSVal16(dstport
))] = 0;
2882 LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
2884 DNS_OP_Name(msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
),
2885 msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
? "Response" : "Query",
2886 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1],
2887 DNS_RC_Name(msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
),
2888 msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
,
2889 msg
->h
.flags
.b
[0] & kDNSFlag0_AA
? "AA " : "",
2890 msg
->h
.flags
.b
[0] & kDNSFlag0_TC
? "TC " : "",
2891 msg
->h
.flags
.b
[0] & kDNSFlag0_RD
? "RD " : "",
2892 msg
->h
.flags
.b
[1] & kDNSFlag1_RA
? "RA " : "",
2893 msg
->h
.flags
.b
[1] & kDNSFlag1_AD
? "AD " : "",
2894 msg
->h
.flags
.b
[1] & kDNSFlag1_CD
? "CD " : "",
2895 mDNSVal16(msg
->h
.id
),
2897 sbuffer
, mDNSVal16(srcport
), dbuffer
,
2898 (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
) ? " (truncated)" : ""
2901 LogMsg("%2d %s", msg
->h
.numQuestions
, IsUpdate
? "Zone" : "Questions");
2902 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++)
2904 ptr
= getQuestion(msg
, ptr
, end
, mDNSInterface_Any
, &q
);
2905 if (ptr
) LogMsg("%2d %##s %s", i
, q
.qname
.c
, DNSTypeName(q
.qtype
));
2907 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAnswers
, IsUpdate
? "Prerequisites" : "Answers");
2908 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAuthorities
, IsUpdate
? "Updates" : "Authorities");
2909 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAdditionals
, "Additionals");
2910 LogMsg("--------------");
2913 // ***************************************************************************
2914 #if COMPILER_LIKES_PRAGMA_MARK
2916 #pragma mark - Packet Sending Functions
2919 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2920 struct TCPSocket_struct
{ TCPSocketFlags flags
; /* ... */ };
2922 struct UDPSocket_struct
2924 mDNSIPPort port
; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
2927 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2928 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2929 mDNSexport mStatus
mDNSSendDNSMessage(mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2930 mDNSInterfaceID InterfaceID
, UDPSocket
*src
, const mDNSAddr
*dst
, mDNSIPPort dstport
, TCPSocket
*sock
, DomainAuthInfo
*authInfo
)
2932 mStatus status
= mStatus_NoError
;
2933 const mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2936 // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
2937 if (end
< msg
->data
|| end
- msg
->data
> AbsoluteMaxDNSMessageData
)
2939 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg
->data
, end
, end
- msg
->data
);
2940 return mStatus_BadParamErr
;
2943 newend
= putHINFO(m
, msg
, end
, authInfo
);
2944 if (!newend
) LogMsg("mDNSSendDNSMessage: putHINFO failed"); // Not fatal
2947 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2948 SwapDNSHeaderBytes(msg
);
2950 if (authInfo
) DNSDigest_SignMessage(msg
, &end
, authInfo
, 0); // DNSDigest_SignMessage operates on message in network byte order
2951 if (!end
) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status
= mStatus_NoMemoryErr
; }
2954 // Send the packet on the wire
2956 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, src
, dst
, dstport
);
2959 mDNSu16 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2960 mDNSu8 lenbuf
[2] = { (mDNSu8
)(msglen
>> 8), (mDNSu8
)(msglen
& 0xFF) };
2961 long nsent
= mDNSPlatformWriteTCP(sock
, (char*)lenbuf
, 2); // Should do scatter/gather here -- this is probably going out as two packets
2962 if (nsent
!= 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent
, 2); status
= mStatus_ConnFailed
; }
2965 nsent
= mDNSPlatformWriteTCP(sock
, (char *)msg
, msglen
);
2966 if (nsent
!= msglen
) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent
, msglen
); status
= mStatus_ConnFailed
; }
2971 // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
2972 SwapDNSHeaderBytes(msg
);
2974 // Dump the packet with the HINFO and TSIG
2975 if (mDNS_PacketLoggingEnabled
&& !mDNSOpaque16IsZero(msg
->h
.id
))
2976 DumpPacket(m
, status
, mDNStrue
, sock
&& (sock
->flags
& kTCPSocketFlags_UseTLS
) ? "TLS" : sock
? "TCP" : "UDP", mDNSNULL
, src
? src
->port
: MulticastDNSPort
, dst
, dstport
, msg
, end
);
2978 // put the number of additionals back the way it was
2979 msg
->h
.numAdditionals
= numAdditionals
;
2984 // ***************************************************************************
2985 #if COMPILER_LIKES_PRAGMA_MARK
2987 #pragma mark - RR List Management & Task Management
2990 mDNSexport
void mDNS_Lock_(mDNS
*const m
)
2992 // MUST grab the platform lock FIRST!
2993 mDNSPlatformLock(m
);
2995 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2996 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2997 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2998 // If mDNS_busy != mDNS_reentrancy that's a bad sign
3000 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
3002 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
3007 // If this is an initial entry into the mDNSCore code, set m->timenow
3008 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3009 if (m
->mDNS_busy
== 0)
3012 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
3013 m
->timenow
= mDNS_TimeNow_NoLock(m
);
3014 if (m
->timenow
== 0) m
->timenow
= 1;
3016 else if (m
->timenow
== 0)
3018 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
3019 m
->timenow
= mDNS_TimeNow_NoLock(m
);
3020 if (m
->timenow
== 0) m
->timenow
= 1;
3023 if (m
->timenow_last
- m
->timenow
> 0)
3025 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
3026 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
3027 m
->timenow
= m
->timenow_last
;
3029 m
->timenow_last
= m
->timenow
;
3031 // Increment mDNS_busy so we'll recognise re-entrant calls
3035 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
3037 mDNSs32 e
= m
->timenow
+ 0x78000000;
3038 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) return(e
);
3039 if (m
->NewQuestions
)
3041 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
3042 else return(m
->timenow
);
3044 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
3045 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
3046 if (m
->SPSProxyListChanged
) return(m
->timenow
);
3047 #ifndef UNICAST_DISABLED
3048 if (e
- m
->NextuDNSEvent
> 0) e
= m
->NextuDNSEvent
;
3049 if (e
- m
->NextScheduledNATOp
> 0) e
= m
->NextScheduledNATOp
;
3051 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
3052 if (e
- m
->NextScheduledSPS
> 0) e
= m
->NextScheduledSPS
;
3053 // NextScheduledSPRetry only valid when DelaySleep not set
3054 if (!m
->DelaySleep
&& m
->SleepLimit
&& e
- m
->NextScheduledSPRetry
> 0) e
= m
->NextScheduledSPRetry
;
3055 if (m
->DelaySleep
&& e
- m
->DelaySleep
> 0) e
= m
->DelaySleep
;
3057 if (m
->SuppressSending
)
3059 if (e
- m
->SuppressSending
> 0) e
= m
->SuppressSending
;
3063 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
3064 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
3065 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
3071 mDNSexport
void ShowTaskSchedulingError(mDNS
*const m
)
3075 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
3077 // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3079 if (m
->NewQuestions
&& (!m
->NewQuestions
->DelayAnswering
|| m
->timenow
- m
->NewQuestions
->DelayAnswering
>= 0))
3080 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
3081 m
->NewQuestions
->qname
.c
, DNSTypeName(m
->NewQuestions
->qtype
));
3083 if (m
->NewLocalOnlyQuestions
)
3084 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3085 m
->NewLocalOnlyQuestions
->qname
.c
, DNSTypeName(m
->NewLocalOnlyQuestions
->qtype
));
3087 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
))
3088 LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m
, m
->NewLocalRecords
));
3090 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
3091 LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m
->timenow
- m
->NextScheduledEvent
);
3092 if (m
->SuppressSending
&& m
->timenow
- m
->SuppressSending
>= 0)
3093 LogMsg("Task Scheduling Error: m->SuppressSending %d", m
->timenow
- m
->SuppressSending
);
3094 if (m
->timenow
- m
->NextCacheCheck
>= 0)
3095 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m
->timenow
- m
->NextCacheCheck
);
3096 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3097 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m
->timenow
- m
->NextScheduledQuery
);
3098 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3099 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m
->timenow
- m
->NextScheduledProbe
);
3100 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
3101 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m
->timenow
- m
->NextScheduledResponse
);
3102 if (m
->timenow
- m
->NextScheduledNATOp
>= 0)
3103 LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m
->timenow
- m
->NextScheduledNATOp
);
3104 if (m
->timenow
- m
->NextScheduledSPS
>= 0)
3105 LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m
->timenow
- m
->NextScheduledSPS
);
3106 if (!m
->DelaySleep
&& m
->SleepLimit
&& m
->timenow
- m
->NextScheduledSPRetry
>= 0)
3107 LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m
->timenow
- m
->NextScheduledSPRetry
);
3108 if (m
->DelaySleep
&& m
->timenow
- m
->DelaySleep
>= 0)
3109 LogMsg("Task Scheduling Error: m->DelaySleep %d", m
->timenow
- m
->DelaySleep
);
3110 #ifndef UNICAST_DISABLED
3111 if (m
->timenow
- m
->NextuDNSEvent
>= 0)
3112 LogMsg("Task Scheduling Error: NextuDNSEvent %d", m
->timenow
- m
->NextuDNSEvent
);
3118 mDNSexport
void mDNS_Unlock_(mDNS
*const m
)
3120 // Decrement mDNS_busy
3123 // Check for locking failures
3125 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
3127 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
3132 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
3133 if (m
->mDNS_busy
== 0)
3135 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
3136 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
3140 // MUST release the platform lock LAST!
3141 mDNSPlatformUnlock(m
);
3144 // ***************************************************************************
3145 #if COMPILER_LIKES_PRAGMA_MARK
3147 #pragma mark - Specialized mDNS version of vsnprintf
3150 static const struct mDNSprintf_format
3152 unsigned leftJustify
: 1;
3153 unsigned forceSign
: 1;
3154 unsigned zeroPad
: 1;
3155 unsigned havePrecision
: 1;
3159 char sign
; // +, - or space
3160 unsigned int fieldWidth
;
3161 unsigned int precision
;
3162 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3164 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
3166 mDNSu32 nwritten
= 0;
3168 if (buflen
== 0) return(0);
3169 buflen
--; // Pre-reserve one space in the buffer for the terminating null
3170 if (buflen
== 0) goto exit
;
3172 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
3176 *sbuffer
++ = (char)c
;
3177 if (++nwritten
>= buflen
) goto exit
;
3181 unsigned int i
=0, j
;
3182 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
3183 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
3184 // The size needs to be enough for a 256-byte domain name plus some error text.
3185 #define mDNS_VACB_Size 300
3186 char mDNS_VACB
[mDNS_VACB_Size
];
3187 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
3188 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
3189 char *s
= mDNS_VACB_Lim
, *digits
;
3190 struct mDNSprintf_format F
= mDNSprintf_format_default
;
3192 while (1) // decode flags
3195 if (c
== '-') F
.leftJustify
= 1;
3196 else if (c
== '+') F
.forceSign
= 1;
3197 else if (c
== ' ') F
.sign
= ' ';
3198 else if (c
== '#') F
.altForm
++;
3199 else if (c
== '0') F
.zeroPad
= 1;
3203 if (c
== '*') // decode field width
3205 int f
= va_arg(arg
, int);
3206 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
3207 F
.fieldWidth
= (unsigned int)f
;
3212 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
3213 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
3216 if (c
== '.') // decode precision
3218 if ((c
= *++fmt
) == '*')
3219 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
3220 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
3221 F
.precision
= (10 * F
.precision
) + (c
- '0');
3222 F
.havePrecision
= 1;
3225 if (F
.leftJustify
) F
.zeroPad
= 0;
3228 switch (c
) // perform appropriate conversion
3231 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
3232 case 'l' : // fall through
3233 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
3235 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
3236 else n
= (unsigned long)va_arg(arg
, int);
3237 if (F
.hSize
) n
= (short) n
;
3238 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
3239 else if (F
.forceSign
) F
.sign
= '+';
3241 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3242 else n
= va_arg(arg
, unsigned int);
3243 if (F
.hSize
) n
= (unsigned short) n
;
3246 decimal
: if (!F
.havePrecision
)
3250 F
.precision
= F
.fieldWidth
;
3251 if (F
.sign
) --F
.precision
;
3253 if (F
.precision
< 1) F
.precision
= 1;
3255 if (F
.precision
> mDNS_VACB_Size
- 1)
3256 F
.precision
= mDNS_VACB_Size
- 1;
3257 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
3258 for (; i
< F
.precision
; i
++) *--s
= '0';
3259 if (F
.sign
) { *--s
= F
.sign
; i
++; }
3262 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3263 else n
= va_arg(arg
, unsigned int);
3264 if (F
.hSize
) n
= (unsigned short) n
;
3265 if (!F
.havePrecision
)
3267 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
3268 if (F
.precision
< 1) F
.precision
= 1;
3270 if (F
.precision
> mDNS_VACB_Size
- 1)
3271 F
.precision
= mDNS_VACB_Size
- 1;
3272 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
3273 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
3274 for (; i
< F
.precision
; i
++) *--s
= '0';
3278 unsigned char *a
= va_arg(arg
, unsigned char *);
3279 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
3282 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3285 mDNSAddr
*ip
= (mDNSAddr
*)a
;
3288 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
3289 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
3290 default: F
.precision
= 0; break;
3293 if (F
.altForm
&& !F
.precision
)
3294 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "«ZERO ADDRESS»");
3295 else switch (F
.precision
)
3297 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
3298 a
[0], a
[1], a
[2], a
[3]); break;
3299 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
3300 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
3301 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
3302 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
3303 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
3304 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
3305 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify"
3306 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
3312 case 'p' : F
.havePrecision
= F
.lSize
= 1;
3314 case 'X' : digits
= "0123456789ABCDEF";
3316 case 'x' : digits
= "0123456789abcdef";
3317 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3318 else n
= va_arg(arg
, unsigned int);
3319 if (F
.hSize
) n
= (unsigned short) n
;
3320 if (!F
.havePrecision
)
3324 F
.precision
= F
.fieldWidth
;
3325 if (F
.altForm
) F
.precision
-= 2;
3327 if (F
.precision
< 1) F
.precision
= 1;
3329 if (F
.precision
> mDNS_VACB_Size
- 1)
3330 F
.precision
= mDNS_VACB_Size
- 1;
3331 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
3332 for (; i
< F
.precision
; i
++) *--s
= '0';
3333 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
3336 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
3338 case 's' : s
= va_arg(arg
, char *);
3339 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
3340 else switch (F
.altForm
)
3343 if (!F
.havePrecision
) // C string
3347 while ((i
< F
.precision
) && s
[i
]) i
++;
3348 // Make sure we don't truncate in the middle of a UTF-8 character
3349 // If last character we got was any kind of UTF-8 multi-byte character,
3350 // then see if we have to back up.
3351 // This is not as easy as the similar checks below, because
3352 // here we can't assume it's safe to examine the *next* byte, so we
3353 // have to confine ourselves to working only backwards in the string.
3354 j
= i
; // Record where we got to
3355 // Now, back up until we find first non-continuation-char
3356 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
3357 // Now s[i-1] is the first non-continuation-char
3358 // and (j-i) is the number of continuation-chars we found
3359 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
3361 i
--; // Tentatively eliminate this start-char as well
3362 // Now (j-i) is the number of characters we're considering eliminating.
3363 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
3364 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
3365 // (with sign extension) then the result has to be 0xFE.
3366 // If this is right, then we reinstate the tentatively eliminated bytes.
3367 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
3371 case 1: i
= (unsigned char) *s
++; break; // Pascal string
3372 case 2: { // DNS label-sequence name
3373 unsigned char *a
= (unsigned char *)s
;
3374 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3375 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
3380 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
3381 if (s
+ *a
>= &mDNS_VACB
[254])
3382 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
3383 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3384 // so it's clear what's a literal dot and what's a label separator
3385 ConvertDomainLabelToCString((domainlabel
*)a
, buf
);
3386 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%s.", buf
);
3389 i
= (mDNSu32
)(s
- mDNS_VACB
);
3390 s
= mDNS_VACB
; // Reset s back to the start of the buffer
3394 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3395 if (F
.havePrecision
&& i
> F
.precision
)
3396 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3399 case 'n' : s
= va_arg(arg
, char *);
3400 if (F
.hSize
) * (short *) s
= (short)nwritten
;
3401 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
3402 else * (int *) s
= (int)nwritten
;
3405 default: s
= mDNS_VACB
;
3406 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
3408 case '%' : *sbuffer
++ = (char)c
;
3409 if (++nwritten
>= buflen
) goto exit
;
3413 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
3416 if (++nwritten
>= buflen
) goto exit
;
3417 } while (i
< --F
.fieldWidth
);
3419 // Make sure we don't truncate in the middle of a UTF-8 character.
3420 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3421 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3422 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3423 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3424 if (i
> buflen
- nwritten
)
3425 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3426 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
3428 if (nwritten
>= buflen
) goto exit
;
3430 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
3433 if (++nwritten
>= buflen
) goto exit
;
3442 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
3448 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);