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 mDNSInterfaceMark
= (mDNSInterfaceID
)-1;
596 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)-2;
597 mDNSexport
const mDNSInterfaceID mDNSInterface_Unicast
= (mDNSInterfaceID
)-3;
599 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
600 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
601 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
602 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
603 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
604 // with Microsoft's LLMNR client code.
606 #define DiscardPortAsNumber 9
607 #define SSHPortAsNumber 22
608 #define UnicastDNSPortAsNumber 53
609 #define SSDPPortAsNumber 1900
610 #define IPSECPortAsNumber 4500
611 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
612 #define NATPMPAnnouncementPortAsNumber 5350
613 #define NATPMPPortAsNumber 5351
614 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
615 #define MulticastDNSPortAsNumber 5353
616 #define LoopbackIPCPortAsNumber 5354
617 //#define MulticastDNSPortAsNumber 5355 // LLMNR
618 #define PrivateDNSPortAsNumber 5533
620 mDNSexport
const mDNSIPPort DiscardPort
= { { DiscardPortAsNumber
>> 8, DiscardPortAsNumber
& 0xFF } };
621 mDNSexport
const mDNSIPPort SSHPort
= { { SSHPortAsNumber
>> 8, SSHPortAsNumber
& 0xFF } };
622 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
623 mDNSexport
const mDNSIPPort SSDPPort
= { { SSDPPortAsNumber
>> 8, SSDPPortAsNumber
& 0xFF } };
624 mDNSexport
const mDNSIPPort IPSECPort
= { { IPSECPortAsNumber
>> 8, IPSECPortAsNumber
& 0xFF } };
625 mDNSexport
const mDNSIPPort NSIPCPort
= { { NSIPCPortAsNumber
>> 8, NSIPCPortAsNumber
& 0xFF } };
626 mDNSexport
const mDNSIPPort NATPMPAnnouncementPort
= { { NATPMPAnnouncementPortAsNumber
>> 8, NATPMPAnnouncementPortAsNumber
& 0xFF } };
627 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
628 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
629 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
630 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
631 mDNSexport
const mDNSIPPort PrivateDNSPort
= { { PrivateDNSPortAsNumber
>> 8, PrivateDNSPortAsNumber
& 0xFF } };
633 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
634 mDNSexport
const mDNSv4Addr AllSystemsMcast
= { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements
635 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
636 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
637 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
638 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
640 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
641 mDNSexport
const mDNSOpaque16 onesID
= { { 255, 255 } };
642 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
643 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
644 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
645 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
646 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
648 mDNSexport
const mDNSOpaque64 zeroOpaque64
= { { 0 } };
650 // ***************************************************************************
651 #if COMPILER_LIKES_PRAGMA_MARK
653 #pragma mark - General Utility Functions
656 // return true for RFC1918 private addresses
657 mDNSexport mDNSBool
mDNSv4AddrIsRFC1918(mDNSv4Addr
*addr
)
659 return ((addr
->b
[0] == 10) || // 10/8 prefix
660 (addr
->b
[0] == 172 && (addr
->b
[1] & 0xF0) == 16) || // 172.16/12
661 (addr
->b
[0] == 192 && addr
->b
[1] == 168)); // 192.168/16
664 mDNSexport NetworkInterfaceInfo
*GetFirstActiveInterface(NetworkInterfaceInfo
*intf
)
666 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
670 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
672 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
673 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
676 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
678 mDNSu32 slot
, used
= 0;
680 const CacheRecord
*rr
;
681 FORALL_CACHERECORDS(slot
, cg
, rr
)
682 if (rr
->resrec
.InterfaceID
== id
) used
++;
686 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
690 case kDNSType_A
: return("Addr");
691 case kDNSType_NS
: return("NS");
692 case kDNSType_CNAME
:return("CNAME");
693 case kDNSType_SOA
: return("SOA");
694 case kDNSType_NULL
: return("NULL");
695 case kDNSType_PTR
: return("PTR");
696 case kDNSType_HINFO
:return("HINFO");
697 case kDNSType_TXT
: return("TXT");
698 case kDNSType_AAAA
: return("AAAA");
699 case kDNSType_SRV
: return("SRV");
700 case kDNSType_OPT
: return("OPT");
701 case kDNSType_NSEC
: return("NSEC");
702 case kDNSType_TSIG
: return("TSIG");
703 case kDNSQType_ANY
: return("ANY");
705 static char buffer
[16];
706 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
712 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
713 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
714 // long as this routine is only used for debugging messages, it probably isn't a big problem.
715 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*const rr
, const RDataBody
*const rd1
, char *const buffer
)
717 const RDataBody2
*const rd
= (RDataBody2
*)rd1
;
718 #define RemSpc (MaxMsg-1-length)
720 mDNSu32 length
= mDNS_snprintf(buffer
, MaxMsg
-1, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
721 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) return(buffer
);
722 if (!rr
->rdlength
) { mDNS_snprintf(buffer
+length
, RemSpc
, "<< ZERO RDATA LENGTH >>"); return(buffer
); }
726 case kDNSType_A
: mDNS_snprintf(buffer
+length
, RemSpc
, "%.4a", &rd
->ipv4
); break;
728 case kDNSType_NS
: // Same as PTR
729 case kDNSType_CNAME
:// Same as PTR
730 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, RemSpc
, "%##s", rd
->name
.c
); break;
732 case kDNSType_SOA
: mDNS_snprintf(buffer
+length
, RemSpc
, "%##s %##s %d %d %d %d %d",
733 rd
->soa
.mname
.c
, rd
->soa
.rname
.c
,
734 rd
->soa
.serial
, rd
->soa
.refresh
, rd
->soa
.retry
, rd
->soa
.expire
, rd
->soa
.min
);
737 case kDNSType_HINFO
:// Display this the same as TXT (show all constituent strings)
739 const mDNSu8
*t
= rd
->txt
.c
;
740 while (t
< rd
->txt
.c
+ rr
->rdlength
)
742 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "%s%#s", t
> rd
->txt
.c
? "¦" : "", t
);
747 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, RemSpc
, "%.16a", &rd
->ipv6
); break;
748 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, RemSpc
, "%u %u %u %##s",
749 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
753 const rdataOPT
*const end
= (const rdataOPT
*)&rd
->data
[rr
->rdlength
];
754 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "Max %d", rr
->rrclass
);
755 for (opt
= &rd
->opt
[0]; opt
< end
; opt
++)
760 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Vers %d", opt
->u
.llq
.vers
);
761 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Op %d", opt
->u
.llq
.llqOp
);
762 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Err/Port %d", opt
->u
.llq
.err
);
763 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " ID %08X%08X", opt
->u
.llq
.id
.l
[0], opt
->u
.llq
.id
.l
[1]);
764 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Lease %d", opt
->u
.llq
.llqlease
);
767 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Lease %d", opt
->u
.updatelease
);
770 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Vers %d", opt
->u
.owner
.vers
);
771 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Seq %3d", (mDNSu8
)opt
->u
.owner
.seq
); // Display as unsigned
772 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " MAC %.6a", opt
->u
.owner
.HMAC
.b
);
773 if (opt
->optlen
>= DNSOpt_OwnerData_ID_Wake_Space
-4)
775 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " I-MAC %.6a", opt
->u
.owner
.IMAC
.b
);
776 if (opt
->optlen
> DNSOpt_OwnerData_ID_Wake_Space
-4)
777 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Password %.6a", opt
->u
.owner
.password
.b
);
781 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, " Unknown %d", opt
->opt
);
788 case kDNSType_NSEC
: {
790 for (i
=0; i
<255; i
++)
791 if (rd
->nsec
.bitmap
[i
>>3] & (128 >> (i
&7)))
792 length
+= mDNS_snprintf(buffer
+length
, RemSpc
, "%s ", DNSTypeName(i
));
796 default: mDNS_snprintf(buffer
+length
, RemSpc
, "RDLen %d: %s", rr
->rdlength
, rd
->data
);
797 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
798 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
= '.';
804 // See comments in mDNSEmbeddedAPI.h
805 #if _PLATFORM_HAS_STRONG_PRNG_
806 #define mDNSRandomNumber mDNSPlatformRandomNumber
808 mDNSlocal mDNSu32
mDNSRandomFromSeed(mDNSu32 seed
)
810 return seed
* 21 + 1;
813 mDNSlocal mDNSu32
mDNSMixRandomSeed(mDNSu32 seed
, mDNSu8 iteration
)
815 return iteration
? mDNSMixRandomSeed(mDNSRandomFromSeed(seed
), --iteration
) : seed
;
818 mDNSlocal mDNSu32
mDNSRandomNumber()
820 static mDNSBool seeded
= mDNSfalse
;
821 static mDNSu32 seed
= 0;
824 seed
= mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
827 return (seed
= mDNSRandomFromSeed(seed
));
829 #endif // ! _PLATFORM_HAS_STRONG_PRNG_
831 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
) // Returns pseudo-random result from zero to max inclusive
836 while (mask
< max
) mask
= (mask
<< 1) | 1;
838 do ret
= mDNSRandomNumber() & mask
;
844 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
846 if (ip1
->type
== ip2
->type
)
850 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
851 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
852 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
858 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
862 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip
->ip
.v4
, AllDNSLinkGroup_v4
.ip
.v4
));
863 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip
->ip
.v6
, AllDNSLinkGroup_v6
.ip
.v6
));
864 default: return(mDNSfalse
);
868 // ***************************************************************************
869 #if COMPILER_LIKES_PRAGMA_MARK
871 #pragma mark - Domain Name Utility Functions
874 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
877 const int len
= *a
++;
879 if (len
> MAX_DOMAIN_LABEL
)
880 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
882 if (len
!= *b
++) return(mDNSfalse
);
883 for (i
=0; i
<len
; i
++)
887 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
888 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
889 if (ac
!= bc
) return(mDNSfalse
);
894 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
896 const mDNSu8
* a
= d1
->c
;
897 const mDNSu8
* b
= d2
->c
;
898 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
902 if (a
+ 1 + *a
>= max
)
903 { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse
); }
904 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
912 mDNSexport mDNSBool
SameDomainNameCS(const domainname
*const d1
, const domainname
*const d2
)
914 mDNSu16 l1
= DomainNameLength(d1
);
915 mDNSu16 l2
= DomainNameLength(d2
);
916 return(l1
<= MAX_DOMAIN_NAME
&& l1
== l2
&& mDNSPlatformMemSame(d1
, d2
, l1
));
919 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
921 // Domains that are defined to be resolved via link-local multicast are:
922 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
923 static const domainname
*nL
= (const domainname
*)"\x5" "local";
924 static const domainname
*nR
= (const domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
925 static const domainname
*n8
= (const domainname
*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
926 static const domainname
*n9
= (const domainname
*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
927 static const domainname
*nA
= (const domainname
*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
928 static const domainname
*nB
= (const domainname
*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
930 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
; // Top-level domain, second-level domain, etc.
931 d1
= d2
= d3
= d4
= d5
= mDNSNULL
;
934 d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
935 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
938 if (d1
&& SameDomainName(d1
, nL
)) return(mDNStrue
);
939 if (d4
&& SameDomainName(d4
, nR
)) return(mDNStrue
);
940 if (d5
&& SameDomainName(d5
, n8
)) return(mDNStrue
);
941 if (d5
&& SameDomainName(d5
, n9
)) return(mDNStrue
);
942 if (d5
&& SameDomainName(d5
, nA
)) return(mDNStrue
);
943 if (d5
&& SameDomainName(d5
, nB
)) return(mDNStrue
);
947 mDNSexport
const mDNSu8
*LastLabel(const domainname
*d
)
949 const mDNSu8
*p
= d
->c
;
953 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
958 // Returns length of a domain name INCLUDING the byte for the final null label
959 // e.g. for the root label "." it returns one
960 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
961 // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
962 // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
963 mDNSexport mDNSu16
DomainNameLengthLimit(const domainname
*const name
, const mDNSu8
*limit
)
965 const mDNSu8
*src
= name
->c
;
966 while (src
< limit
&& *src
<= MAX_DOMAIN_LABEL
)
968 if (*src
== 0) return((mDNSu16
)(src
- name
->c
+ 1));
971 return(MAX_DOMAIN_NAME
+1);
974 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
975 // for the final null label, e.g. for the root label "." it returns one.
976 // E.g. for the FQDN "foo.com." it returns 9
977 // (length, three data bytes, length, three more data bytes, final zero).
978 // In the case where a parent domain name is provided, and the given name is a child
979 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
980 // of the child name, plus TWO bytes for the compression pointer.
981 // E.g. for the name "foo.com." with parent "com.", it returns 6
982 // (length, three data bytes, two-byte compression pointer).
983 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
985 const mDNSu8
*src
= name
->c
;
986 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
989 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
990 if (parent
&& SameDomainName((const domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
992 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
994 return((mDNSu16
)(src
- name
->c
+ 1));
997 // CountLabels() returns number of labels in name, excluding final root label
998 // (e.g. for "apple.com." CountLabels returns 2.)
999 mDNSexport
int CountLabels(const domainname
*d
)
1003 for (ptr
= d
->c
; *ptr
; ptr
= ptr
+ ptr
[0] + 1) count
++;
1007 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
1008 // returning a pointer to the suffix with 'skip' labels removed.
1009 mDNSexport
const domainname
*SkipLeadingLabels(const domainname
*d
, int skip
)
1011 while (skip
> 0 && d
->c
[0]) { d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]); skip
--; }
1015 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
1016 // The C string contains the label as-is, with no escaping, etc.
1017 // Any dots in the name are literal dots, not label separators
1018 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
1019 // in the domainname bufer (i.e. the next byte after the terminating zero).
1020 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1021 // AppendLiteralLabelString returns mDNSNULL.
1022 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
1024 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1025 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1026 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
1027 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
1028 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
1030 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
1031 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
1032 *ptr
++ = 0; // Put the null root label on the end
1033 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
1034 else return(ptr
); // Success: return new value of ptr
1037 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
1038 // The C string is in conventional DNS syntax:
1039 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1040 // If successful, AppendDNSNameString returns a pointer to the next unused byte
1041 // in the domainname bufer (i.e. the next byte after the terminating zero).
1042 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1043 // AppendDNSNameString returns mDNSNULL.
1044 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
1046 const char *cstr
= cstring
;
1047 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1048 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1049 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
1051 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
1052 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
1053 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
1055 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
1056 if (c
== '\\') // If escape character, check next character
1058 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
1059 if (mDNSIsDigit(cstr
[-1]) && mDNSIsDigit(cstr
[0]) && mDNSIsDigit(cstr
[1]))
1060 { // If three decimal digits,
1061 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
1062 int v1
= cstr
[ 0] - '0';
1063 int v2
= cstr
[ 1] - '0';
1064 int val
= v0
* 100 + v1
* 10 + v2
;
1065 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
1068 *ptr
++ = c
; // Write the character
1070 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
1071 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
1073 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
1076 *ptr
++ = 0; // Put the null root label on the end
1077 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
1078 else return(ptr
); // Success: return new value of ptr
1081 // AppendDomainLabel appends a single label to a name.
1082 // If successful, AppendDomainLabel returns a pointer to the next unused byte
1083 // in the domainname bufer (i.e. the next byte after the terminating zero).
1084 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1085 // AppendDomainLabel returns mDNSNULL.
1086 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
1089 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
1091 // Check label is legal
1092 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
1094 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
1095 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
1097 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
1098 *ptr
++ = 0; // Put the null root label on the end
1102 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
1104 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
1105 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
1106 const mDNSu8
* src
= append
->c
;
1110 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
1111 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
1112 *ptr
= 0; // Put the null root label on the end
1118 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
1119 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
1120 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
1121 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
1122 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
1123 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
1124 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
1126 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
1127 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
1128 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
1129 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
1130 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
1133 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
1134 // The C string is in conventional DNS syntax:
1135 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
1136 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
1137 // in the domainname bufer (i.e. the next byte after the terminating zero).
1138 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
1139 // MakeDomainNameFromDNSNameString returns mDNSNULL.
1140 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
1142 name
->c
[0] = 0; // Make an empty domain name
1143 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
1146 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
1148 const mDNSu8
* src
= label
->c
; // Domain label we're reading
1149 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
1150 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
1151 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
1152 while (src
< end
) // While we have characters in the label
1157 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
1158 *ptr
++ = esc
; // Output escape character
1159 else if (c
<= ' ') // If non-printing ascii,
1160 { // Output decimal escape sequence
1162 *ptr
++ = (char) ('0' + (c
/ 100) );
1163 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
1164 c
= (mDNSu8
)('0' + (c
) % 10);
1167 *ptr
++ = (char)c
; // Copy the character
1169 *ptr
= 0; // Null-terminate the string
1170 return(ptr
); // and return
1173 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
1174 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
1176 const mDNSu8
*src
= name
->c
; // Domain name we're reading
1177 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1179 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
1181 while (*src
) // While more characters in the domain name
1183 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
1184 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
1185 if (!ptr
) return(mDNSNULL
);
1187 *ptr
++ = '.'; // Write the dot after the label
1190 *ptr
++ = 0; // Null-terminate the string
1191 return(ptr
); // and return
1195 // Host names must start with a letter, end with a letter or digit,
1196 // and have as interior characters only letters, digits, and hyphen.
1197 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
1199 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
1201 const mDNSu8
* src
= &UTF8Name
[1];
1202 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
1203 mDNSu8
* ptr
= &hostlabel
->c
[1];
1204 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
1207 // Delete apostrophes from source name
1208 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
1209 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
1210 { src
+= 3; continue; } // Unicode curly apostrophe
1213 if (mDNSValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
1214 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
1218 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
1219 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
1222 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
1223 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
1224 ((X)[4] | 0x20) == 'p')
1226 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
1227 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
1230 mDNSu8
*dst
= fqdn
->c
;
1232 const char *errormsg
;
1233 #if APPLE_OSX_mDNSResponder
1234 mDNSBool loggedUnderscore
= mDNSfalse
;
1235 static char typeBuf
[MAX_ESCAPED_DOMAIN_NAME
];
1238 // In the case where there is no name (and ONLY in that case),
1239 // a single-label subtype is allowed as the first label of a three-part "type"
1242 const mDNSu8
*s0
= type
->c
;
1243 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
1245 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
1246 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1248 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
1249 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
1251 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
1252 src
= s0
; // Copy the first label
1254 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
1255 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
1256 type
= (const domainname
*)s1
;
1258 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1259 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1260 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1261 if (SameDomainName((domainname
*)s0
, (const domainname
*)"\x09_services\x07_dns-sd\x04_udp"))
1262 dst
-= sizeof(SubTypeLabel
);
1268 if (name
&& name
->c
[0])
1270 src
= name
->c
; // Put the service name into the domain name
1272 if (len
>= 0x40) { errormsg
= "Service instance name too long"; goto fail
; }
1273 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1276 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1278 src
= type
->c
; // Put the service type into the domain name
1280 if (len
< 2 || len
> 15)
1282 LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-14 characters. "
1283 "See <http://www.dns-sd.org/ServiceTypes.html>", name
->c
, type
->c
, domain
->c
);
1284 #if APPLE_OSX_mDNSResponder
1285 ConvertDomainNameToCString(type
, typeBuf
);
1286 mDNSASLLog(mDNSNULL
, "serviceType.nameTooLong", "noop", typeBuf
, "");
1289 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
))) return(mDNSNULL
);
1290 if (src
[1] != '_') { errormsg
= "Application protocol name must begin with underscore"; goto fail
; }
1291 for (i
=2; i
<=len
; i
++)
1293 // Letters and digits are allowed anywhere
1294 if (mDNSIsLetter(src
[i
]) || mDNSIsDigit(src
[i
])) continue;
1295 // Hyphens are only allowed as interior characters
1296 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1297 // with the same rule as hyphens
1298 if ((src
[i
] == '-' || src
[i
] == '_') && i
> 2 && i
< len
)
1300 #if APPLE_OSX_mDNSResponder
1301 if (src
[i
] == '_' && loggedUnderscore
== mDNSfalse
)
1303 ConvertDomainNameToCString(type
, typeBuf
);
1304 mDNSASLLog(mDNSNULL
, "serviceType.nameWithUnderscore", "noop", typeBuf
, "");
1305 loggedUnderscore
= mDNStrue
;
1310 errormsg
= "Application protocol name must contain only letters, digits, and hyphens";
1311 #if APPLE_OSX_mDNSResponder
1313 ConvertDomainNameToCString(type
, typeBuf
);
1314 mDNSASLLog(mDNSNULL
, "serviceType.nameWithIllegalCharacters", "noop", typeBuf
, "");
1319 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1322 if (!ValidTransportProtocol(src
)) { errormsg
= "Transport protocol name must be _udp or _tcp"; goto fail
; }
1323 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1325 if (*src
) { errormsg
= "Service type must have only two labels"; goto fail
; }
1328 if (!domain
->c
[0]) { errormsg
= "Service domain must be non-empty"; goto fail
; }
1329 if (SameDomainName(domain
, (const domainname
*)"\x05" "local" "\x04" "arpa"))
1330 { errormsg
= "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
1331 dst
= AppendDomainName(fqdn
, domain
);
1332 if (!dst
) { errormsg
= "Service domain too long"; goto fail
; }
1336 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
1340 // A service name has the form: instance.application-protocol.transport-protocol.domain
1341 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1342 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1343 // However, if the given FQDN doesn't contain at least three labels,
1344 // DeconstructServiceName will reject it and return mDNSfalse.
1345 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
1346 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
1349 const mDNSu8
*src
= fqdn
->c
;
1350 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
1353 dst
= name
->c
; // Extract the service name
1355 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
1356 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
1357 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1359 dst
= type
->c
; // Extract the service type
1361 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
1362 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
1363 if (src
[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse
); }
1364 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1367 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
1368 if (!ValidTransportProtocol(src
))
1369 { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse
); }
1370 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1371 *dst
++ = 0; // Put terminator on the end of service type
1373 dst
= domain
->c
; // Extract the service domain
1378 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
1379 if (src
+ 1 + len
+ 1 >= max
)
1380 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
1381 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1383 *dst
++ = 0; // Put the null root label on the end
1389 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1390 // 10xxxxxx is a continuation byte of a multi-byte character
1391 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1392 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1393 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1394 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1395 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1397 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1398 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1399 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1400 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1401 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1403 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
1407 mDNSu8 c1
= string
[max
]; // First byte after cut point
1408 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : (mDNSu8
)0xB0; // Second byte after cut point
1409 length
= max
; // Trim length down
1412 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1413 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1414 // If so, then we continue to chop more bytes until we get to a legal chop point.
1415 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1416 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1417 if (!continuation
&& !secondsurrogate
) break;
1419 c1
= string
[--length
];
1421 // Having truncated characters off the end of our string, also cut off any residual white space
1422 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1427 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1428 // name ends in "-nnn", where n is some decimal number.
1429 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1431 mDNSu16 l
= name
->c
[0];
1435 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1436 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1437 if (!mDNSIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1439 while (l
> 2 && mDNSIsDigit(name
->c
[l
])) l
--; // Strip off digits
1440 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1444 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1445 if (!mDNSIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1447 while (l
> 2 && mDNSIsDigit(name
->c
[l
])) l
--; // Strip off digits
1448 return (name
->c
[l
] == '-');
1452 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1453 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1454 // from the suffix that was removed.
1455 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1457 mDNSu32 val
= 0, multiplier
= 1;
1459 // Chop closing parentheses from RichText suffix
1460 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1462 // Get any existing numerical suffix off the name
1463 while (mDNSIsDigit(name
->c
[name
->c
[0]]))
1464 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1466 // Chop opening parentheses or dash from suffix
1469 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1473 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1479 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1480 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1481 mDNSexport
void AppendLabelSuffix(domainlabel
*const name
, mDNSu32 val
, const mDNSBool RichText
)
1483 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1484 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1486 // Truncate trailing spaces from RichText names
1487 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1489 while (divisor
< 0xFFFFFFFFUL
/10 && val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1491 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1493 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1494 else { name
->c
[++name
->c
[0]] = '-'; }
1498 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1503 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1506 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1510 if (LabelContainsSuffix(name
, RichText
))
1511 val
= RemoveLabelSuffix(name
, RichText
);
1513 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1514 // If existing suffix in the range 2-9, increment it.
1515 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1516 // so add a random increment to improve the chances of finding an available name next time.
1517 if (val
== 0) val
= 2;
1518 else if (val
< 10) val
++;
1519 else val
+= 1 + mDNSRandom(99);
1521 AppendLabelSuffix(name
, val
, RichText
);
1524 // ***************************************************************************
1525 #if COMPILER_LIKES_PRAGMA_MARK
1527 #pragma mark - Resource Record Utility Functions
1530 // Set up a AuthRecord with sensible default values.
1531 // These defaults may be overwritten with new values before mDNS_Register is called
1532 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
1533 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
1535 // Don't try to store a TTL bigger than we can represent in platform time units
1536 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
1537 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
1538 else if (ttl
== 0) // And Zero TTL is illegal
1539 ttl
= DefaultTTLforRRType(rrtype
);
1541 // Field Group 1: The actual information pertaining to this resource record
1542 rr
->resrec
.RecordType
= RecordType
;
1543 rr
->resrec
.InterfaceID
= InterfaceID
;
1544 rr
->resrec
.name
= &rr
->namestorage
;
1545 rr
->resrec
.rrtype
= rrtype
;
1546 rr
->resrec
.rrclass
= kDNSClass_IN
;
1547 rr
->resrec
.rroriginalttl
= ttl
;
1548 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1549 // rr->resrec.rdestimate = set in mDNS_Register_internal
1550 // rr->resrec.rdata = MUST be set by client
1553 rr
->resrec
.rdata
= RDataStorage
;
1556 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1557 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1560 // Field Group 2: Persistent metadata for Authoritative Records
1561 rr
->Additional1
= mDNSNULL
;
1562 rr
->Additional2
= mDNSNULL
;
1563 rr
->DependentOn
= mDNSNULL
;
1564 rr
->RRSet
= mDNSNULL
;
1565 rr
->RecordCallback
= Callback
;
1566 rr
->RecordContext
= Context
;
1568 rr
->AutoTarget
= Target_Manual
;
1569 rr
->AllowRemoteQuery
= mDNSfalse
;
1570 rr
->ForceMCast
= mDNSfalse
;
1572 rr
->WakeUp
= zeroOwner
;
1573 rr
->AddressProxy
= zeroAddr
;
1577 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1578 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1580 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1581 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1582 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1583 rr
->state
= regState_Zero
;
1587 rr
->updateid
= zeroID
;
1588 rr
->zone
= rr
->resrec
.name
;
1589 rr
->UpdateServer
= zeroAddr
;
1590 rr
->UpdatePort
= zeroIPPort
;
1595 rr
->InFlightRData
= 0;
1596 rr
->InFlightRDLen
= 0;
1597 rr
->QueuedRData
= 0;
1598 rr
->QueuedRDLen
= 0;
1600 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
1603 mDNSexport
void mDNS_SetupQuestion(DNSQuestion
*const q
, const mDNSInterfaceID InterfaceID
, const domainname
*const name
,
1604 const mDNSu16 qtype
, mDNSQuestionCallback
*const callback
, void *const context
)
1606 q
->InterfaceID
= InterfaceID
;
1607 q
->Target
= zeroAddr
;
1608 AssignDomainName(&q
->qname
, name
);
1610 q
->qclass
= kDNSClass_IN
;
1611 q
->LongLived
= (qtype
== kDNSType_PTR
);
1612 q
->ExpectUnique
= (qtype
!= kDNSType_PTR
);
1613 q
->ForceMCast
= mDNSfalse
;
1614 q
->ReturnIntermed
= mDNSfalse
;
1615 q
->QuestionCallback
= callback
;
1616 q
->QuestionContext
= context
;
1619 mDNSexport mDNSu32
RDataHashValue(const ResourceRecord
*const rr
)
1621 int len
= rr
->rdlength
;
1622 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
1626 case kDNSType_CNAME
:
1628 case kDNSType_DNAME
: return DomainNameHashValue(&rdb
->name
);
1630 case kDNSType_SOA
: return rdb
->soa
.serial
+
1635 DomainNameHashValue(&rdb
->soa
.mname
) +
1636 DomainNameHashValue(&rdb
->soa
.rname
);
1639 case kDNSType_AFSDB
:
1641 case kDNSType_KX
: return DomainNameHashValue(&rdb
->mx
.exchange
);
1643 case kDNSType_RP
: return DomainNameHashValue(&rdb
->rp
.mbox
) + DomainNameHashValue(&rdb
->rp
.txt
);
1645 case kDNSType_PX
: return DomainNameHashValue(&rdb
->px
.map822
) + DomainNameHashValue(&rdb
->px
.mapx400
);
1647 case kDNSType_SRV
: return DomainNameHashValue(&rdb
->srv
.target
);
1649 case kDNSType_OPT
: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1651 case kDNSType_NSEC
: len
= sizeof(rdataNSEC
); // Use in-memory length of 32, and fall through default checksum computation below
1657 for (i
=0; i
+1 < len
; i
+=2)
1659 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1660 sum
= (sum
<<3) | (sum
>>29);
1664 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1671 // r1 has to be a full ResourceRecord including rrtype and rdlength
1672 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1673 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1675 const RDataBody2
*const b1
= (RDataBody2
*)r1
->rdata
->u
.data
;
1676 const RDataBody2
*const b2
= (RDataBody2
*)r2
;
1680 case kDNSType_CNAME
:
1682 case kDNSType_DNAME
:return(SameDomainName(&b1
->name
, &b2
->name
));
1684 case kDNSType_SOA
: return(mDNSBool
)( b1
->soa
.serial
== b2
->soa
.serial
&&
1685 b1
->soa
.refresh
== b2
->soa
.refresh
&&
1686 b1
->soa
.retry
== b2
->soa
.retry
&&
1687 b1
->soa
.expire
== b2
->soa
.expire
&&
1688 b1
->soa
.min
== b2
->soa
.min
&&
1689 SameDomainName(&b1
->soa
.mname
, &b2
->soa
.mname
) &&
1690 SameDomainName(&b1
->soa
.rname
, &b2
->soa
.rname
));
1693 case kDNSType_AFSDB
:
1695 case kDNSType_KX
: return(mDNSBool
)( b1
->mx
.preference
== b2
->mx
.preference
&&
1696 SameDomainName(&b1
->mx
.exchange
, &b2
->mx
.exchange
));
1698 case kDNSType_RP
: return(mDNSBool
)( SameDomainName(&b1
->rp
.mbox
, &b2
->rp
.mbox
) &&
1699 SameDomainName(&b1
->rp
.txt
, &b2
->rp
.txt
));
1701 case kDNSType_PX
: return(mDNSBool
)( b1
->px
.preference
== b2
->px
.preference
&&
1702 SameDomainName(&b1
->px
.map822
, &b2
->px
.map822
) &&
1703 SameDomainName(&b1
->px
.mapx400
, &b2
->px
.mapx400
));
1705 case kDNSType_SRV
: return(mDNSBool
)( b1
->srv
.priority
== b2
->srv
.priority
&&
1706 b1
->srv
.weight
== b2
->srv
.weight
&&
1707 mDNSSameIPPort(b1
->srv
.port
, b2
->srv
.port
) &&
1708 SameDomainName(&b1
->srv
.target
, &b2
->srv
.target
));
1710 case kDNSType_OPT
: return mDNSfalse
; // OPT is a pseudo-RR container structure; makes no sense to compare
1712 case kDNSType_NSEC
: return(mDNSPlatformMemSame(b1
->data
, b2
->data
, sizeof(rdataNSEC
)));
1714 default: return(mDNSPlatformMemSame(b1
->data
, b2
->data
, r1
->rdlength
));
1718 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1719 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1720 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1721 // because it has to check all the way to the end of the names to be sure.
1722 // In cases where we know in advance that the names match it's especially advantageous to skip the
1723 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1725 mDNSexport mDNSBool
SameNameRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1727 if (rr
->InterfaceID
&&
1728 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1729 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1731 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1732 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1734 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1735 if (!RRTypeAnswersQuestionType(rr
,q
->qtype
)) return(mDNSfalse
);
1736 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1741 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1743 if (rr
->InterfaceID
&&
1744 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1745 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1747 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1748 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1750 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1751 if (!RRTypeAnswersQuestionType(rr
,q
->qtype
)) return(mDNSfalse
);
1752 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1754 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1757 mDNSexport mDNSBool
AnyTypeRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1759 if (rr
->InterfaceID
&&
1760 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1761 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1763 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1764 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1766 if (rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1768 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1771 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1773 const RDataBody2
*const rd
= (RDataBody2
*)rr
->rdata
->u
.data
;
1774 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1775 if (rr
->rrclass
== kDNSQClass_ANY
) return(rr
->rdlength
); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1776 else switch (rr
->rrtype
)
1778 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1781 case kDNSType_CNAME
:
1783 case kDNSType_DNAME
:return(CompressedDomainNameLength(&rd
->name
, name
));
1785 case kDNSType_SOA
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1786 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1787 5 * sizeof(mDNSOpaque32
));
1795 case kDNSType_DHCID
:return(rr
->rdlength
); // Not self-describing, so have to just trust rdlength
1797 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1800 case kDNSType_AFSDB
:
1802 case kDNSType_KX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->mx
.exchange
, name
));
1804 case kDNSType_RP
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->rp
.mbox
, name
) +
1805 CompressedDomainNameLength(&rd
->rp
.txt
, name
));
1807 case kDNSType_PX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->px
.map822
, name
) +
1808 CompressedDomainNameLength(&rd
->px
.mapx400
, name
));
1810 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1812 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1814 case kDNSType_OPT
: return(rr
->rdlength
);
1816 case kDNSType_NSEC
: {
1818 for (i
=sizeof(rdataNSEC
); i
>0; i
--) if (rd
->nsec
.bitmap
[i
-1]) break;
1819 // For our simplified use of NSEC synthetic records:
1820 // nextname is always the record's own name,
1821 // the block number is always 0,
1822 // the count byte is a value in the range 1-32,
1823 // followed by the 1-32 data bytes
1824 return((estimate
? 1 : DomainNameLength(rr
->name
)) + 2 + i
);
1827 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1828 return(rr
->rdlength
);
1832 // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
1833 // to help reduce the risk of bogus malformed data on the network
1834 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1840 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1842 case kDNSType_NS
: // Same as PTR
1843 case kDNSType_MD
: // Same as PTR
1844 case kDNSType_MF
: // Same as PTR
1845 case kDNSType_CNAME
:// Same as PTR
1846 //case kDNSType_SOA not checked
1847 case kDNSType_MB
: // Same as PTR
1848 case kDNSType_MG
: // Same as PTR
1849 case kDNSType_MR
: // Same as PTR
1850 //case kDNSType_NULL not checked (no specified format, so always valid)
1851 //case kDNSType_WKS not checked
1852 case kDNSType_PTR
: len
= DomainNameLengthLimit(&rd
->u
.name
, rd
->u
.data
+ rdlength
);
1853 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1855 case kDNSType_HINFO
:// Same as TXT (roughly)
1856 case kDNSType_MINFO
:// Same as TXT (roughly)
1857 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1859 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1860 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1861 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1862 return (ptr
== end
);
1865 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1867 case kDNSType_MX
: // Must be at least two-byte preference, plus domainname
1868 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1869 len
= DomainNameLengthLimit(&rd
->u
.mx
.exchange
, rd
->u
.data
+ rdlength
);
1870 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1872 case kDNSType_SRV
: // Must be at least priority+weight+port, plus domainname
1873 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1874 len
= DomainNameLengthLimit(&rd
->u
.srv
.target
, rd
->u
.data
+ rdlength
);
1875 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1877 //case kDNSType_NSEC not checked
1879 default: return(mDNStrue
); // Allow all other types without checking
1883 // ***************************************************************************
1884 #if COMPILER_LIKES_PRAGMA_MARK
1886 #pragma mark - DNS Message Creation Functions
1889 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1893 h
->numQuestions
= 0;
1895 h
->numAuthorities
= 0;
1896 h
->numAdditionals
= 0;
1899 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1901 const mDNSu8
*result
= end
- *domname
- 1;
1903 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1905 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1906 while (result
>= base
)
1908 // If the length byte and first character of the label match, then check further to see
1909 // if this location in the packet will yield a useful name compression pointer.
1910 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1912 const mDNSu8
*name
= domname
;
1913 const mDNSu8
*targ
= result
;
1914 while (targ
+ *name
< end
)
1916 // First see if this label matches
1918 const mDNSu8
*pointertarget
;
1919 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1920 if (i
<= *name
) break; // If label did not match, bail out
1921 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1922 name
+= 1 + *name
; // and proceed to check next label
1923 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1924 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1926 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1927 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1928 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1929 if (targ
+1 >= end
) break; // Second byte not present!
1930 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1931 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1932 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1933 targ
= pointertarget
;
1936 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1941 // Put a string of dot-separated labels as length-prefixed labels
1942 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1943 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1944 // end points to the end of the message so far
1945 // ptr points to where we want to put the name
1946 // limit points to one byte past the end of the buffer that we must not overrun
1947 // domainname is the name to put
1948 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1949 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1951 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1952 const mDNSu8
* np
= name
->c
;
1953 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1954 const mDNSu8
* pointer
= mDNSNULL
;
1955 const mDNSu8
*const searchlimit
= ptr
;
1957 if (!ptr
) { LogMsg("putDomainNameAsLabels %##s ptr is null", name
->c
); return(mDNSNULL
); }
1959 if (!*np
) // If just writing one-byte root label, make sure we have space for that
1961 if (ptr
>= limit
) return(mDNSNULL
);
1963 else // else, loop through writing labels and/or a compression offset
1966 if (*np
> MAX_DOMAIN_LABEL
)
1967 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1969 // This check correctly allows for the final trailing root label:
1971 // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
1972 // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
1973 // We know that max will be at name->c[256]
1974 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1975 // six bytes, then exit the loop, write the final terminating root label, and the domain
1976 // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
1977 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1978 if (np
+ 1 + *np
>= max
)
1979 { LogMsg("Malformed domain name %##s (more than 256 bytes)", name
->c
); return(mDNSNULL
); }
1981 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1982 if (pointer
) // Use a compression pointer if we can
1984 const mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1985 if (ptr
+2 > limit
) return(mDNSNULL
); // If we don't have two bytes of space left, give up
1986 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1987 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1990 else // Else copy one label and try again
1994 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
1995 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1997 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1999 } while (*np
); // While we've got characters remaining in the name, continue
2002 *ptr
++ = 0; // Put the final root label
2006 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
2008 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
2009 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
2010 return ptr
+ sizeof(mDNSOpaque16
);
2013 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
2015 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
2016 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
2017 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
2018 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
2019 return ptr
+ sizeof(mDNSu32
);
2022 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
2023 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const ResourceRecord
*const rr
)
2025 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
2028 case kDNSType_A
: if (rr
->rdlength
!= 4)
2029 { debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
); return(mDNSNULL
); }
2030 if (ptr
+ 4 > limit
) return(mDNSNULL
);
2031 *ptr
++ = rdb
->ipv4
.b
[0];
2032 *ptr
++ = rdb
->ipv4
.b
[1];
2033 *ptr
++ = rdb
->ipv4
.b
[2];
2034 *ptr
++ = rdb
->ipv4
.b
[3];
2038 case kDNSType_CNAME
:
2040 case kDNSType_DNAME
:return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->name
));
2042 case kDNSType_SOA
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->soa
.mname
);
2043 if (!ptr
) return(mDNSNULL
);
2044 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->soa
.rname
);
2045 if (!ptr
|| ptr
+ 20 > limit
) return(mDNSNULL
);
2046 ptr
= putVal32(ptr
, rdb
->soa
.serial
);
2047 ptr
= putVal32(ptr
, rdb
->soa
.refresh
);
2048 ptr
= putVal32(ptr
, rdb
->soa
.retry
);
2049 ptr
= putVal32(ptr
, rdb
->soa
.expire
);
2050 ptr
= putVal32(ptr
, rdb
->soa
.min
);
2054 case kDNSType_HINFO
:
2060 case kDNSType_DHCID
:if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
2061 mDNSPlatformMemCopy(ptr
, rdb
->data
, rr
->rdlength
);
2062 return(ptr
+ rr
->rdlength
);
2065 case kDNSType_AFSDB
:
2067 case kDNSType_KX
: if (ptr
+ 3 > limit
) return(mDNSNULL
);
2068 ptr
= putVal16(ptr
, rdb
->mx
.preference
);
2069 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->mx
.exchange
));
2071 case kDNSType_RP
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->rp
.mbox
);
2072 if (!ptr
) return(mDNSNULL
);
2073 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->rp
.txt
);
2076 case kDNSType_PX
: if (ptr
+ 5 > limit
) return(mDNSNULL
);
2077 ptr
= putVal16(ptr
, rdb
->px
.preference
);
2078 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->px
.map822
);
2079 if (!ptr
) return(mDNSNULL
);
2080 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->px
.mapx400
);
2083 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rdb
->ipv6
))
2084 { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
); return(mDNSNULL
); }
2085 if (ptr
+ sizeof(rdb
->ipv6
) > limit
) return(mDNSNULL
);
2086 mDNSPlatformMemCopy(ptr
, &rdb
->ipv6
, sizeof(rdb
->ipv6
));
2087 return(ptr
+ sizeof(rdb
->ipv6
));
2089 case kDNSType_SRV
: if (ptr
+ 7 > limit
) return(mDNSNULL
);
2090 *ptr
++ = (mDNSu8
)(rdb
->srv
.priority
>> 8);
2091 *ptr
++ = (mDNSu8
)(rdb
->srv
.priority
& 0xFF);
2092 *ptr
++ = (mDNSu8
)(rdb
->srv
.weight
>> 8);
2093 *ptr
++ = (mDNSu8
)(rdb
->srv
.weight
& 0xFF);
2094 *ptr
++ = rdb
->srv
.port
.b
[0];
2095 *ptr
++ = rdb
->srv
.port
.b
[1];
2096 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rdb
->srv
.target
));
2098 case kDNSType_OPT
: {
2100 const rdataOPT
*opt
;
2101 const rdataOPT
*const end
= (const rdataOPT
*)&rr
->rdata
->u
.data
[rr
->rdlength
];
2102 for (opt
= &rr
->rdata
->u
.opt
[0]; opt
< end
; opt
++) len
+= DNSOpt_Data_Space(opt
);
2103 if (ptr
+ len
> limit
) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL
; }
2105 for (opt
= &rr
->rdata
->u
.opt
[0]; opt
< end
; opt
++)
2107 const int space
= DNSOpt_Data_Space(opt
);
2108 ptr
= putVal16(ptr
, opt
->opt
);
2109 ptr
= putVal16(ptr
, space
- 4);
2113 ptr
= putVal16(ptr
, opt
->u
.llq
.vers
);
2114 ptr
= putVal16(ptr
, opt
->u
.llq
.llqOp
);
2115 ptr
= putVal16(ptr
, opt
->u
.llq
.err
);
2116 mDNSPlatformMemCopy(ptr
, opt
->u
.llq
.id
.b
, 8); // 8-byte id
2118 ptr
= putVal32(ptr
, opt
->u
.llq
.llqlease
);
2121 ptr
= putVal32(ptr
, opt
->u
.updatelease
);
2124 *ptr
++ = opt
->u
.owner
.vers
;
2125 *ptr
++ = opt
->u
.owner
.seq
;
2126 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.HMAC
.b
, 6); // 6-byte Host identifier
2128 if (space
>= DNSOpt_OwnerData_ID_Wake_Space
)
2130 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.IMAC
.b
, 6); // 6-byte interface MAC
2132 if (space
> DNSOpt_OwnerData_ID_Wake_Space
)
2134 mDNSPlatformMemCopy(ptr
, opt
->u
.owner
.password
.b
, space
- DNSOpt_OwnerData_ID_Wake_Space
);
2135 ptr
+= space
- DNSOpt_OwnerData_ID_Wake_Space
;
2144 case kDNSType_NSEC
: {
2145 // For our simplified use of NSEC synthetic records:
2146 // nextname is always the record's own name,
2147 // the block number is always 0,
2148 // the count byte is a value in the range 1-32,
2149 // followed by the 1-32 data bytes
2151 for (i
=sizeof(rdataNSEC
); i
>0; i
--) if (rdb
->nsec
.bitmap
[i
-1]) break;
2152 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
2153 if (!ptr
) return(mDNSNULL
);
2154 if (ptr
+ 2 + i
> limit
) return(mDNSNULL
);
2157 for (j
=0; j
<i
; j
++) *ptr
++ = rdb
->nsec
.bitmap
[j
];
2161 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
2162 if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
2163 mDNSPlatformMemCopy(ptr
, rdb
->data
, rr
->rdlength
);
2164 return(ptr
+ rr
->rdlength
);
2168 #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2170 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
2173 mDNSu16 actualLength
;
2174 // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2175 const DNSMessage
*const rdatacompressionbase
= (IsUnicastUpdate(msg
) && rr
->rrtype
== kDNSType_SRV
) ? mDNSNULL
: msg
;
2177 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
2179 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
2183 if (!ptr
) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL
); }
2185 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
2186 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2187 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
2188 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
2189 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
2190 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
2191 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
2192 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
2193 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
2194 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
2195 endofrdata
= putRData(rdatacompressionbase
, ptr
+10, limit
, rr
);
2196 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
2198 // Go back and fill in the actual number of data bytes we wrote
2199 // (actualLength can be less than rdlength when domain name compression is used)
2200 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
2201 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
2202 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
2204 if (count
) (*count
)++;
2205 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
2209 mDNSlocal mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, mDNSu16
*count
, const AuthRecord
*rr
)
2211 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
2212 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2213 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
2214 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
2215 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
2216 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
2217 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
2218 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
2223 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
2225 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2226 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
2227 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2228 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2229 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
2230 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
2231 msg
->h
.numQuestions
++;
2235 // for dynamic updates
2236 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
2238 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
2239 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
2240 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
2241 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
2242 *ptr
++ = zoneClass
.b
[0];
2243 *ptr
++ = zoneClass
.b
[1];
2244 msg
->h
.mDNS_numZones
++;
2248 // for dynamic updates
2249 mDNSexport mDNSu8
*putPrereqNameNotInUse(const domainname
*const name
, DNSMessage
*const msg
, mDNSu8
*const ptr
, mDNSu8
*const end
)
2252 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
2253 AssignDomainName(&prereq
.namestorage
, name
);
2254 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
2255 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
2256 return putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
2259 // for dynamic updates
2260 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
2262 // deletion: specify record w/ TTL 0, class NONE
2263 const mDNSu16 origclass
= rr
->rrclass
;
2264 rr
->rrclass
= kDNSClass_NONE
;
2265 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
2266 rr
->rrclass
= origclass
;
2270 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
2272 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2273 mDNSu16
class = kDNSQClass_ANY
;
2275 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2276 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2277 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2278 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2279 ptr
[2] = (mDNSu8
)(class >> 8);
2280 ptr
[3] = (mDNSu8
)(class & 0xFF);
2281 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2282 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2284 msg
->h
.mDNS_numUpdates
++;
2288 // for dynamic updates
2289 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
2291 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2292 mDNSu16
class = kDNSQClass_ANY
;
2293 mDNSu16 rrtype
= kDNSQType_ANY
;
2295 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2296 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2297 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2298 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2299 ptr
[2] = (mDNSu8
)(class >> 8);
2300 ptr
[3] = (mDNSu8
)(class & 0xFF);
2301 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2302 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2304 msg
->h
.mDNS_numUpdates
++;
2308 // for dynamic updates
2309 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
2312 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
2313 rr
.resrec
.rrclass
= NormalMaxDNSMessageData
;
2314 rr
.resrec
.rdlength
= sizeof(rdataOPT
); // One option in this OPT record
2315 rr
.resrec
.rdestimate
= sizeof(rdataOPT
);
2316 rr
.resrec
.rdata
->u
.opt
[0].opt
= kDNSOpt_Lease
;
2317 rr
.resrec
.rdata
->u
.opt
[0].u
.updatelease
= lease
;
2318 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, &rr
.resrec
, 0);
2319 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
2323 mDNSexport mDNSu8
*putHINFO(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
, DomainAuthInfo
*authInfo
)
2325 if (authInfo
&& authInfo
->AutoTunnel
)
2328 mDNSu8
*h
= hinfo
.rdatastorage
.u
.data
;
2329 mDNSu16 len
= 2 + m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0];
2331 mDNS_SetupResourceRecord(&hinfo
, mDNSNULL
, mDNSInterface_Any
, kDNSType_HINFO
, 0, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
2332 AppendDomainLabel(&hinfo
.namestorage
, &m
->hostlabel
);
2333 AppendDomainName (&hinfo
.namestorage
, &authInfo
->domain
);
2334 hinfo
.resrec
.rroriginalttl
= 0;
2335 mDNSPlatformMemCopy(h
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
2337 mDNSPlatformMemCopy(h
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
2338 hinfo
.resrec
.rdlength
= len
;
2339 hinfo
.resrec
.rdestimate
= len
;
2340 newptr
= PutResourceRecord(msg
, end
, &msg
->h
.numAdditionals
, &hinfo
.resrec
);
2347 // ***************************************************************************
2348 #if COMPILER_LIKES_PRAGMA_MARK
2350 #pragma mark - DNS Message Parsing Functions
2353 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
2358 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
2360 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
2361 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
2362 sum
= (sum
<<3) | (sum
>>29);
2364 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
2368 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
2373 rr
->rdata
= NewRData
;
2374 rr
->rdlength
= rdlength
;
2376 // Must not try to get target pointer until after updating rr->rdata
2377 target
= GetRRDomainNameTarget(rr
);
2378 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
2379 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
2380 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
);
2383 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
2387 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2388 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2390 while (1) // Read sequence of labels
2392 const mDNSu8 len
= *ptr
++; // Read length of this label
2393 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
2396 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2397 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2398 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
2399 { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL
); }
2404 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
2405 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
2406 case 0xC0: return(ptr
+1);
2411 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2412 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
2413 domainname
*const name
)
2415 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
2416 mDNSu8
*np
= name
->c
; // Name pointer
2417 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
2419 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2420 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2422 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2424 while (1) // Read sequence of labels
2426 const mDNSu8 len
= *ptr
++; // Read length of this label
2427 if (len
== 0) break; // If length is zero, that means this name is complete
2433 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2434 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2435 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
2436 { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL
); }
2438 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
2439 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2442 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
2445 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
2447 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
2448 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
2449 ptr
= (mDNSu8
*)msg
+ offset
;
2450 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2451 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
2453 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
2458 if (nextbyte
) return(nextbyte
);
2462 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2464 mDNSu16 pktrdlength
;
2466 ptr
= skipDomainName(msg
, ptr
, end
);
2467 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2469 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2470 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2472 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2474 return(ptr
+ pktrdlength
);
2477 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
2479 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
2480 *ptr
+= sizeof(mDNSOpaque16
);
2484 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
,
2485 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*const largecr
)
2487 CacheRecord
*const rr
= &largecr
->r
;
2488 RDataBody2
*const rdb
= (RDataBody2
*)rr
->smallrdatastorage
.data
;
2489 mDNSu16 pktrdlength
;
2491 if (largecr
== &m
->rec
&& m
->rec
.r
.resrec
.RecordType
)
2493 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &m
->rec
.r
));
2499 rr
->next
= mDNSNULL
;
2500 rr
->resrec
.name
= &largecr
->namestorage
;
2502 rr
->NextInKAList
= mDNSNULL
;
2503 rr
->TimeRcvd
= m
? m
->timenow
: 0;
2504 rr
->DelayDelivery
= 0;
2505 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2506 rr
->LastUsed
= m
? m
->timenow
: 0;
2507 rr
->CRActiveQuestion
= mDNSNULL
;
2508 rr
->UnansweredQueries
= 0;
2509 rr
->LastUnansweredTime
= 0;
2510 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
2511 rr
->MPUnansweredQ
= 0;
2512 rr
->MPLastUnansweredQT
= 0;
2513 rr
->MPUnansweredKA
= 0;
2514 rr
->MPExpectingKA
= mDNSfalse
;
2516 rr
->NextInCFList
= mDNSNULL
;
2518 rr
->resrec
.InterfaceID
= InterfaceID
;
2519 ptr
= getDomainName(msg
, ptr
, end
, &largecr
->namestorage
);
2520 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2522 if (ptr
+ 10 > end
) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2524 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2525 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
2526 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
2527 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
2528 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2529 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2530 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2531 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2533 // If mDNS record has cache-flush bit set, we mark it unique
2534 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2535 // authoritative for the entire RRSet), unless this is a truncated response
2536 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8) || (!InterfaceID
&& !(msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)))
2537 RecordType
|= kDNSRecordTypePacketUniqueMask
;
2539 if (ptr
+ pktrdlength
> end
) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2540 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
2542 rr
->resrec
.rdata
= (RData
*)&rr
->smallrdatastorage
;
2543 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
2545 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
2547 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2548 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2549 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2550 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2551 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2552 if (rr
->resrec
.rrclass
== kDNSQClass_ANY
&& pktrdlength
== 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2553 rr
->resrec
.rdlength
= 0;
2554 else switch (rr
->resrec
.rrtype
)
2556 case kDNSType_A
: if (pktrdlength
!= sizeof(mDNSv4Addr
)) return(mDNSNULL
);
2557 rdb
->ipv4
.b
[0] = ptr
[0];
2558 rdb
->ipv4
.b
[1] = ptr
[1];
2559 rdb
->ipv4
.b
[2] = ptr
[2];
2560 rdb
->ipv4
.b
[3] = ptr
[3];
2564 case kDNSType_CNAME
:
2566 case kDNSType_DNAME
:ptr
= getDomainName(msg
, ptr
, end
, &rdb
->name
);
2567 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
2568 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength);
2571 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rdb
->soa
.mname
);
2572 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
2573 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->soa
.rname
);
2574 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
2575 if (ptr
+ 0x14 != end
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
2576 rdb
->soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
2577 rdb
->soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
2578 rdb
->soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
2579 rdb
->soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
2580 rdb
->soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
2584 case kDNSType_HINFO
:
2590 case kDNSType_DHCID
:if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2592 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2593 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2596 rr
->resrec
.rdlength
= pktrdlength
;
2597 mDNSPlatformMemCopy(rdb
->data
, ptr
, pktrdlength
);
2601 case kDNSType_AFSDB
:
2603 case kDNSType_KX
: if (pktrdlength
< 3) return(mDNSNULL
); // Preference + domainname
2604 rdb
->mx
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2605 ptr
= getDomainName(msg
, ptr
+2, end
, &rdb
->mx
.exchange
);
2606 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL
); }
2607 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2610 case kDNSType_RP
: ptr
= getDomainName(msg
, ptr
, end
, &rdb
->rp
.mbox
); // Domainname + domainname
2611 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL
; }
2612 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->rp
.txt
);
2613 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL
; }
2616 case kDNSType_PX
: if (pktrdlength
< 4) return(mDNSNULL
); // Preference + domainname + domainname
2617 rdb
->px
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2618 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->px
.map822
);
2619 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL
; }
2620 ptr
= getDomainName(msg
, ptr
, end
, &rdb
->px
.mapx400
);
2621 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL
; }
2624 case kDNSType_AAAA
: if (pktrdlength
!= sizeof(mDNSv6Addr
)) return(mDNSNULL
);
2625 mDNSPlatformMemCopy(&rdb
->ipv6
, ptr
, sizeof(rdb
->ipv6
));
2628 case kDNSType_SRV
: if (pktrdlength
< 7) return(mDNSNULL
); // Priority + weight + port + domainname
2629 rdb
->srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2630 rdb
->srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
2631 rdb
->srv
.port
.b
[0] = ptr
[4];
2632 rdb
->srv
.port
.b
[1] = ptr
[5];
2633 ptr
= getDomainName(msg
, ptr
+6, end
, &rdb
->srv
.target
);
2634 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
2635 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
2638 case kDNSType_OPT
: {
2639 rdataOPT
*opt
= rr
->resrec
.rdata
->u
.opt
;
2640 rr
->resrec
.rdlength
= 0;
2641 while (ptr
< end
&& (mDNSu8
*)(opt
+1) < &rr
->resrec
.rdata
->u
.data
[MaximumRDSize
])
2643 if (ptr
+ 4 > end
) { LogMsg("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); return(mDNSNULL
); }
2644 opt
->opt
= getVal16(&ptr
);
2645 opt
->optlen
= getVal16(&ptr
);
2646 if (!ValidDNSOpt(opt
)) { LogMsg("GetLargeResourceRecord: opt %d optlen %d wrong", opt
->opt
, opt
->optlen
); return(mDNSNULL
); }
2647 if (ptr
+ opt
->optlen
> end
) { LogMsg("GetLargeResourceRecord: ptr + opt->optlen > end"); return(mDNSNULL
); }
2651 opt
->u
.llq
.vers
= getVal16(&ptr
);
2652 opt
->u
.llq
.llqOp
= getVal16(&ptr
);
2653 opt
->u
.llq
.err
= getVal16(&ptr
);
2654 mDNSPlatformMemCopy(opt
->u
.llq
.id
.b
, ptr
, 8);
2656 opt
->u
.llq
.llqlease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
2657 if (opt
->u
.llq
.llqlease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
2658 opt
->u
.llq
.llqlease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2659 ptr
+= sizeof(mDNSOpaque32
);
2662 opt
->u
.updatelease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
2663 if (opt
->u
.updatelease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
2664 opt
->u
.updatelease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2665 ptr
+= sizeof(mDNSs32
);
2668 opt
->u
.owner
.vers
= ptr
[0];
2669 opt
->u
.owner
.seq
= ptr
[1];
2670 mDNSPlatformMemCopy(opt
->u
.owner
.HMAC
.b
, ptr
+2, 6); // 6-byte MAC address
2671 mDNSPlatformMemCopy(opt
->u
.owner
.IMAC
.b
, ptr
+2, 6); // 6-byte MAC address
2672 opt
->u
.owner
.password
= zeroEthAddr
;
2673 if (opt
->optlen
>= DNSOpt_OwnerData_ID_Wake_Space
-4)
2675 mDNSPlatformMemCopy(opt
->u
.owner
.IMAC
.b
, ptr
+8, 6); // 6-byte MAC address
2676 if (opt
->optlen
> DNSOpt_OwnerData_ID_Wake_Space
-4)
2677 mDNSPlatformMemCopy(opt
->u
.owner
.password
.b
, ptr
+14, opt
->optlen
- (DNSOpt_OwnerData_ID_Wake_Space
-4));
2682 opt
++; // increment pointer into rdatabody
2684 rr
->resrec
.rdlength
= (mDNSu8
*)opt
- rr
->resrec
.rdata
->u
.data
;
2685 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL
); }
2689 case kDNSType_NSEC
: {
2692 ptr
= getDomainName(msg
, ptr
, end
, &d
); // Ignored for our simplified use of NSEC synthetic records
2693 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed NSEC nextname"); return mDNSNULL
; }
2694 if (*ptr
++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); return mDNSNULL
; }
2696 if (i
< 1 || i
> sizeof(rdataNSEC
)) { debugf("GetLargeResourceRecord: invalid block length %d", i
); return mDNSNULL
; }
2697 mDNSPlatformMemZero(rdb
->nsec
.bitmap
, sizeof(rdb
->nsec
.bitmap
));
2698 for (j
=0; j
<i
; j
++) rdb
->nsec
.bitmap
[j
] = *ptr
++;
2699 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed NSEC"); return(mDNSNULL
); }
2703 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2705 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2706 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2709 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2710 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
2711 // Note: Just because we don't understand the record type, that doesn't
2712 // mean we fail. The DNS protocol specifies rdlength, so we can
2713 // safely skip over unknown records and ignore them.
2714 // We also grab a binary copy of the rdata anyway, since the caller
2715 // might know how to interpret it even if we don't.
2716 rr
->resrec
.rdlength
= pktrdlength
;
2717 mDNSPlatformMemCopy(rdb
->data
, ptr
, pktrdlength
);
2721 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2722 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rdlength, rdestimate, rdatahash for us
2724 // Success! Now fill in RecordType to show this record contains valid data
2725 rr
->resrec
.RecordType
= RecordType
;
2729 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2731 ptr
= skipDomainName(msg
, ptr
, end
);
2732 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
2733 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2737 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
2738 DNSQuestion
*question
)
2740 mDNSPlatformMemZero(question
, sizeof(*question
));
2741 question
->InterfaceID
= InterfaceID
;
2742 if (!InterfaceID
) question
->TargetQID
= onesID
; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
2743 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
2744 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
2745 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2747 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
2748 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
2749 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
2753 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2756 const mDNSu8
*ptr
= msg
->data
;
2757 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
2761 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2764 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
2765 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2769 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2772 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
2773 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2777 mDNSexport
const mDNSu8
*LocateOptRR(const DNSMessage
*const msg
, const mDNSu8
*const end
, int minsize
)
2780 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2782 // Locate the OPT record.
2783 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2784 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2785 // but not necessarily the *last* entry in the Additional Section.
2786 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2788 if (ptr
+ DNSOpt_Header_Space
+ minsize
<= end
&& // Make sure we have 11+minsize bytes of data
2789 ptr
[0] == 0 && // Name must be root label
2790 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2791 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2792 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)minsize
)
2795 ptr
= skipResourceRecord(msg
, ptr
, end
);
2800 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2801 // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
2802 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2803 // The code that currently calls this assumes there's only one, instead of iterating through the set
2804 mDNSexport
const rdataOPT
*GetLLQOptData(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2806 const mDNSu8
*ptr
= LocateOptRR(msg
, end
, DNSOpt_LLQData_Space
);
2809 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2810 if (ptr
) return(&m
->rec
.r
.resrec
.rdata
->u
.opt
[0]);
2815 // Get the lease life of records in a dynamic update
2816 // returns 0 on error or if no lease present
2817 mDNSexport mDNSu32
GetPktLease(mDNS
*m
, DNSMessage
*msg
, const mDNSu8
*end
)
2820 const mDNSu8
*ptr
= LocateOptRR(msg
, end
, DNSOpt_LeaseData_Space
);
2821 if (ptr
) ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2822 if (ptr
&& m
->rec
.r
.resrec
.rdlength
>= DNSOpt_LeaseData_Space
&& m
->rec
.r
.resrec
.rdata
->u
.opt
[0].opt
== kDNSOpt_Lease
)
2823 result
= m
->rec
.r
.resrec
.rdata
->u
.opt
[0].u
.updatelease
;
2824 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
2828 mDNSlocal
const mDNSu8
*DumpRecords(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
, int count
, char *label
)
2831 LogMsg("%2d %s", count
, label
);
2832 for (i
= 0; i
< count
&& ptr
; i
++)
2834 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2835 // but since it's only used for debugging (and probably only on OS X, not on
2836 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2837 LargeCacheRecord largecr
;
2838 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, mDNSInterface_Any
, kDNSRecordTypePacketAns
, &largecr
);
2839 if (ptr
) LogMsg("%2d TTL%8d %s", i
, largecr
.r
.resrec
.rroriginalttl
, CRDisplayString(m
, &largecr
.r
));
2841 if (!ptr
) LogMsg("ERROR: Premature end of packet data");
2845 #define DNS_OP_Name(X) ( \
2846 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2847 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2848 (X) == kDNSFlag0_OP_Status ? "Status " : \
2849 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2850 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2851 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2853 #define DNS_RC_Name(X) ( \
2854 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2855 (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
2856 (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
2857 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2858 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2859 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2860 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2861 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2862 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2863 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2864 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2866 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2867 mDNSexport
void DumpPacket(mDNS
*const m
, mStatus status
, mDNSBool sent
, char *transport
,
2868 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
,
2869 const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2871 mDNSBool IsUpdate
= ((msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
) == kDNSFlag0_OP_Update
);
2872 const mDNSu8
*ptr
= msg
->data
;
2875 char tbuffer
[64], sbuffer
[64], dbuffer
[64] = "";
2876 if (!status
) tbuffer
[mDNS_snprintf(tbuffer
, sizeof(tbuffer
), sent
? "Sent" : "Received" )] = 0;
2877 else tbuffer
[mDNS_snprintf(tbuffer
, sizeof(tbuffer
), "ERROR %d %sing", status
, sent
? "Send" : "Receiv")] = 0;
2878 if (sent
) sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "port " )] = 0;
2879 else sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "%#a:", srcaddr
)] = 0;
2880 if (dstaddr
|| !mDNSIPPortIsZero(dstport
))
2881 dbuffer
[mDNS_snprintf(dbuffer
, sizeof(dbuffer
), " to %#a:%d", dstaddr
, mDNSVal16(dstport
))] = 0;
2883 LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
2885 DNS_OP_Name(msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
),
2886 msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
? "Response" : "Query",
2887 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1],
2888 DNS_RC_Name(msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
),
2889 msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
,
2890 msg
->h
.flags
.b
[0] & kDNSFlag0_AA
? "AA " : "",
2891 msg
->h
.flags
.b
[0] & kDNSFlag0_TC
? "TC " : "",
2892 msg
->h
.flags
.b
[0] & kDNSFlag0_RD
? "RD " : "",
2893 msg
->h
.flags
.b
[1] & kDNSFlag1_RA
? "RA " : "",
2894 msg
->h
.flags
.b
[1] & kDNSFlag1_AD
? "AD " : "",
2895 msg
->h
.flags
.b
[1] & kDNSFlag1_CD
? "CD " : "",
2896 mDNSVal16(msg
->h
.id
),
2898 sbuffer
, mDNSVal16(srcport
), dbuffer
,
2899 (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
) ? " (truncated)" : ""
2902 LogMsg("%2d %s", msg
->h
.numQuestions
, IsUpdate
? "Zone" : "Questions");
2903 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++)
2905 ptr
= getQuestion(msg
, ptr
, end
, mDNSInterface_Any
, &q
);
2906 if (ptr
) LogMsg("%2d %##s %s", i
, q
.qname
.c
, DNSTypeName(q
.qtype
));
2908 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAnswers
, IsUpdate
? "Prerequisites" : "Answers");
2909 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAuthorities
, IsUpdate
? "Updates" : "Authorities");
2910 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAdditionals
, "Additionals");
2911 LogMsg("--------------");
2914 // ***************************************************************************
2915 #if COMPILER_LIKES_PRAGMA_MARK
2917 #pragma mark - Packet Sending Functions
2920 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2921 struct TCPSocket_struct
{ TCPSocketFlags flags
; /* ... */ };
2923 struct UDPSocket_struct
2925 mDNSIPPort port
; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
2928 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2929 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2930 mDNSexport mStatus
mDNSSendDNSMessage(mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2931 mDNSInterfaceID InterfaceID
, UDPSocket
*src
, const mDNSAddr
*dst
, mDNSIPPort dstport
, TCPSocket
*sock
, DomainAuthInfo
*authInfo
)
2933 mStatus status
= mStatus_NoError
;
2934 const mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2937 // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
2938 if (end
< msg
->data
|| end
- msg
->data
> AbsoluteMaxDNSMessageData
)
2940 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg
->data
, end
, end
- msg
->data
);
2941 return mStatus_BadParamErr
;
2944 newend
= putHINFO(m
, msg
, end
, authInfo
);
2945 if (!newend
) LogMsg("mDNSSendDNSMessage: putHINFO failed"); // Not fatal
2948 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2949 SwapDNSHeaderBytes(msg
);
2951 if (authInfo
) DNSDigest_SignMessage(msg
, &end
, authInfo
, 0); // DNSDigest_SignMessage operates on message in network byte order
2952 if (!end
) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status
= mStatus_NoMemoryErr
; }
2955 // Send the packet on the wire
2957 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, src
, dst
, dstport
);
2960 mDNSu16 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2961 mDNSu8 lenbuf
[2] = { (mDNSu8
)(msglen
>> 8), (mDNSu8
)(msglen
& 0xFF) };
2962 long nsent
= mDNSPlatformWriteTCP(sock
, (char*)lenbuf
, 2); // Should do scatter/gather here -- this is probably going out as two packets
2963 if (nsent
!= 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent
, 2); status
= mStatus_ConnFailed
; }
2966 nsent
= mDNSPlatformWriteTCP(sock
, (char *)msg
, msglen
);
2967 if (nsent
!= msglen
) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent
, msglen
); status
= mStatus_ConnFailed
; }
2972 // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
2973 SwapDNSHeaderBytes(msg
);
2975 // Dump the packet with the HINFO and TSIG
2976 if (mDNS_PacketLoggingEnabled
&& !mDNSOpaque16IsZero(msg
->h
.id
))
2977 DumpPacket(m
, status
, mDNStrue
, sock
&& (sock
->flags
& kTCPSocketFlags_UseTLS
) ? "TLS" : sock
? "TCP" : "UDP", mDNSNULL
, src
? src
->port
: MulticastDNSPort
, dst
, dstport
, msg
, end
);
2979 // put the number of additionals back the way it was
2980 msg
->h
.numAdditionals
= numAdditionals
;
2985 // ***************************************************************************
2986 #if COMPILER_LIKES_PRAGMA_MARK
2988 #pragma mark - RR List Management & Task Management
2991 mDNSexport
void mDNS_Lock_(mDNS
*const m
)
2993 // MUST grab the platform lock FIRST!
2994 mDNSPlatformLock(m
);
2996 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2997 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2998 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2999 // If mDNS_busy != mDNS_reentrancy that's a bad sign
3001 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
3003 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
3008 // If this is an initial entry into the mDNSCore code, set m->timenow
3009 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3010 if (m
->mDNS_busy
== 0)
3013 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
3014 m
->timenow
= mDNS_TimeNow_NoLock(m
);
3015 if (m
->timenow
== 0) m
->timenow
= 1;
3017 else if (m
->timenow
== 0)
3019 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
3020 m
->timenow
= mDNS_TimeNow_NoLock(m
);
3021 if (m
->timenow
== 0) m
->timenow
= 1;
3024 if (m
->timenow_last
- m
->timenow
> 0)
3026 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
3027 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
3028 m
->timenow
= m
->timenow_last
;
3030 m
->timenow_last
= m
->timenow
;
3032 // Increment mDNS_busy so we'll recognise re-entrant calls
3036 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
3038 mDNSs32 e
= m
->timenow
+ 0x78000000;
3039 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) return(e
);
3040 if (m
->NewQuestions
)
3042 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
3043 else return(m
->timenow
);
3045 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
3046 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
3047 if (m
->SPSProxyListChanged
) return(m
->timenow
);
3048 #ifndef UNICAST_DISABLED
3049 if (e
- m
->NextuDNSEvent
> 0) e
= m
->NextuDNSEvent
;
3050 if (e
- m
->NextScheduledNATOp
> 0) e
= m
->NextScheduledNATOp
;
3052 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
3053 if (e
- m
->NextScheduledSPS
> 0) e
= m
->NextScheduledSPS
;
3054 // NextScheduledSPRetry only valid when DelaySleep not set
3055 if (!m
->DelaySleep
&& m
->SleepLimit
&& e
- m
->NextScheduledSPRetry
> 0) e
= m
->NextScheduledSPRetry
;
3056 if (m
->DelaySleep
&& e
- m
->DelaySleep
> 0) e
= m
->DelaySleep
;
3058 if (m
->SuppressSending
)
3060 if (e
- m
->SuppressSending
> 0) e
= m
->SuppressSending
;
3064 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
3065 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
3066 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
3072 mDNSexport
void ShowTaskSchedulingError(mDNS
*const m
)
3076 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
3078 // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3080 if (m
->NewQuestions
&& (!m
->NewQuestions
->DelayAnswering
|| m
->timenow
- m
->NewQuestions
->DelayAnswering
>= 0))
3081 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
3082 m
->NewQuestions
->qname
.c
, DNSTypeName(m
->NewQuestions
->qtype
));
3084 if (m
->NewLocalOnlyQuestions
)
3085 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3086 m
->NewLocalOnlyQuestions
->qname
.c
, DNSTypeName(m
->NewLocalOnlyQuestions
->qtype
));
3088 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
))
3089 LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m
, m
->NewLocalRecords
));
3091 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
3092 LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m
->timenow
- m
->NextScheduledEvent
);
3093 if (m
->SuppressSending
&& m
->timenow
- m
->SuppressSending
>= 0)
3094 LogMsg("Task Scheduling Error: m->SuppressSending %d", m
->timenow
- m
->SuppressSending
);
3095 if (m
->timenow
- m
->NextCacheCheck
>= 0)
3096 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m
->timenow
- m
->NextCacheCheck
);
3097 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3098 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m
->timenow
- m
->NextScheduledQuery
);
3099 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3100 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m
->timenow
- m
->NextScheduledProbe
);
3101 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
3102 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m
->timenow
- m
->NextScheduledResponse
);
3103 if (m
->timenow
- m
->NextScheduledNATOp
>= 0)
3104 LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m
->timenow
- m
->NextScheduledNATOp
);
3105 if (m
->timenow
- m
->NextScheduledSPS
>= 0)
3106 LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m
->timenow
- m
->NextScheduledSPS
);
3107 if (!m
->DelaySleep
&& m
->SleepLimit
&& m
->timenow
- m
->NextScheduledSPRetry
>= 0)
3108 LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m
->timenow
- m
->NextScheduledSPRetry
);
3109 if (m
->DelaySleep
&& m
->timenow
- m
->DelaySleep
>= 0)
3110 LogMsg("Task Scheduling Error: m->DelaySleep %d", m
->timenow
- m
->DelaySleep
);
3111 #ifndef UNICAST_DISABLED
3112 if (m
->timenow
- m
->NextuDNSEvent
>= 0)
3113 LogMsg("Task Scheduling Error: NextuDNSEvent %d", m
->timenow
- m
->NextuDNSEvent
);
3119 mDNSexport
void mDNS_Unlock_(mDNS
*const m
)
3121 // Decrement mDNS_busy
3124 // Check for locking failures
3126 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
3128 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
3133 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
3134 if (m
->mDNS_busy
== 0)
3136 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
3137 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
3141 // MUST release the platform lock LAST!
3142 mDNSPlatformUnlock(m
);
3145 // ***************************************************************************
3146 #if COMPILER_LIKES_PRAGMA_MARK
3148 #pragma mark - Specialized mDNS version of vsnprintf
3151 static const struct mDNSprintf_format
3153 unsigned leftJustify
: 1;
3154 unsigned forceSign
: 1;
3155 unsigned zeroPad
: 1;
3156 unsigned havePrecision
: 1;
3160 char sign
; // +, - or space
3161 unsigned int fieldWidth
;
3162 unsigned int precision
;
3163 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3165 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
3167 mDNSu32 nwritten
= 0;
3169 if (buflen
== 0) return(0);
3170 buflen
--; // Pre-reserve one space in the buffer for the terminating null
3171 if (buflen
== 0) goto exit
;
3173 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
3177 *sbuffer
++ = (char)c
;
3178 if (++nwritten
>= buflen
) goto exit
;
3182 unsigned int i
=0, j
;
3183 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
3184 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
3185 // The size needs to be enough for a 256-byte domain name plus some error text.
3186 #define mDNS_VACB_Size 300
3187 char mDNS_VACB
[mDNS_VACB_Size
];
3188 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
3189 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
3190 char *s
= mDNS_VACB_Lim
, *digits
;
3191 struct mDNSprintf_format F
= mDNSprintf_format_default
;
3193 while (1) // decode flags
3196 if (c
== '-') F
.leftJustify
= 1;
3197 else if (c
== '+') F
.forceSign
= 1;
3198 else if (c
== ' ') F
.sign
= ' ';
3199 else if (c
== '#') F
.altForm
++;
3200 else if (c
== '0') F
.zeroPad
= 1;
3204 if (c
== '*') // decode field width
3206 int f
= va_arg(arg
, int);
3207 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
3208 F
.fieldWidth
= (unsigned int)f
;
3213 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
3214 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
3217 if (c
== '.') // decode precision
3219 if ((c
= *++fmt
) == '*')
3220 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
3221 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
3222 F
.precision
= (10 * F
.precision
) + (c
- '0');
3223 F
.havePrecision
= 1;
3226 if (F
.leftJustify
) F
.zeroPad
= 0;
3229 switch (c
) // perform appropriate conversion
3232 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
3233 case 'l' : // fall through
3234 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
3236 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
3237 else n
= (unsigned long)va_arg(arg
, int);
3238 if (F
.hSize
) n
= (short) n
;
3239 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
3240 else if (F
.forceSign
) F
.sign
= '+';
3242 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3243 else n
= va_arg(arg
, unsigned int);
3244 if (F
.hSize
) n
= (unsigned short) n
;
3247 decimal
: if (!F
.havePrecision
)
3251 F
.precision
= F
.fieldWidth
;
3252 if (F
.sign
) --F
.precision
;
3254 if (F
.precision
< 1) F
.precision
= 1;
3256 if (F
.precision
> mDNS_VACB_Size
- 1)
3257 F
.precision
= mDNS_VACB_Size
- 1;
3258 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
3259 for (; i
< F
.precision
; i
++) *--s
= '0';
3260 if (F
.sign
) { *--s
= F
.sign
; i
++; }
3263 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3264 else n
= va_arg(arg
, unsigned int);
3265 if (F
.hSize
) n
= (unsigned short) n
;
3266 if (!F
.havePrecision
)
3268 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
3269 if (F
.precision
< 1) F
.precision
= 1;
3271 if (F
.precision
> mDNS_VACB_Size
- 1)
3272 F
.precision
= mDNS_VACB_Size
- 1;
3273 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
3274 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
3275 for (; i
< F
.precision
; i
++) *--s
= '0';
3279 unsigned char *a
= va_arg(arg
, unsigned char *);
3280 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
3283 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3286 mDNSAddr
*ip
= (mDNSAddr
*)a
;
3289 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
3290 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
3291 default: F
.precision
= 0; break;
3294 if (F
.altForm
&& !F
.precision
)
3295 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "«ZERO ADDRESS»");
3296 else switch (F
.precision
)
3298 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
3299 a
[0], a
[1], a
[2], a
[3]); break;
3300 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
3301 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
3302 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
3303 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
3304 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
3305 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
3306 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify"
3307 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
3313 case 'p' : F
.havePrecision
= F
.lSize
= 1;
3315 case 'X' : digits
= "0123456789ABCDEF";
3317 case 'x' : digits
= "0123456789abcdef";
3318 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
3319 else n
= va_arg(arg
, unsigned int);
3320 if (F
.hSize
) n
= (unsigned short) n
;
3321 if (!F
.havePrecision
)
3325 F
.precision
= F
.fieldWidth
;
3326 if (F
.altForm
) F
.precision
-= 2;
3328 if (F
.precision
< 1) F
.precision
= 1;
3330 if (F
.precision
> mDNS_VACB_Size
- 1)
3331 F
.precision
= mDNS_VACB_Size
- 1;
3332 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
3333 for (; i
< F
.precision
; i
++) *--s
= '0';
3334 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
3337 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
3339 case 's' : s
= va_arg(arg
, char *);
3340 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
3341 else switch (F
.altForm
)
3344 if (!F
.havePrecision
) // C string
3348 while ((i
< F
.precision
) && s
[i
]) i
++;
3349 // Make sure we don't truncate in the middle of a UTF-8 character
3350 // If last character we got was any kind of UTF-8 multi-byte character,
3351 // then see if we have to back up.
3352 // This is not as easy as the similar checks below, because
3353 // here we can't assume it's safe to examine the *next* byte, so we
3354 // have to confine ourselves to working only backwards in the string.
3355 j
= i
; // Record where we got to
3356 // Now, back up until we find first non-continuation-char
3357 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
3358 // Now s[i-1] is the first non-continuation-char
3359 // and (j-i) is the number of continuation-chars we found
3360 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
3362 i
--; // Tentatively eliminate this start-char as well
3363 // Now (j-i) is the number of characters we're considering eliminating.
3364 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
3365 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
3366 // (with sign extension) then the result has to be 0xFE.
3367 // If this is right, then we reinstate the tentatively eliminated bytes.
3368 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
3372 case 1: i
= (unsigned char) *s
++; break; // Pascal string
3373 case 2: { // DNS label-sequence name
3374 unsigned char *a
= (unsigned char *)s
;
3375 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3376 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
3381 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
3382 if (s
+ *a
>= &mDNS_VACB
[254])
3383 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
3384 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3385 // so it's clear what's a literal dot and what's a label separator
3386 ConvertDomainLabelToCString((domainlabel
*)a
, buf
);
3387 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%s.", buf
);
3390 i
= (mDNSu32
)(s
- mDNS_VACB
);
3391 s
= mDNS_VACB
; // Reset s back to the start of the buffer
3395 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3396 if (F
.havePrecision
&& i
> F
.precision
)
3397 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3400 case 'n' : s
= va_arg(arg
, char *);
3401 if (F
.hSize
) * (short *) s
= (short)nwritten
;
3402 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
3403 else * (int *) s
= (int)nwritten
;
3406 default: s
= mDNS_VACB
;
3407 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
3409 case '%' : *sbuffer
++ = (char)c
;
3410 if (++nwritten
>= buflen
) goto exit
;
3414 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
3417 if (++nwritten
>= buflen
) goto exit
;
3418 } while (i
< --F
.fieldWidth
);
3420 // Make sure we don't truncate in the middle of a UTF-8 character.
3421 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3422 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3423 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3424 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3425 if (i
> buflen
- nwritten
)
3426 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3427 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
3429 if (nwritten
>= buflen
) goto exit
;
3431 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
3434 if (++nwritten
>= buflen
) goto exit
;
3443 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
3449 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);