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.199.2.1 2008/07/25 07:25:08 mcguire
21 merge of <rdar://3988320&6041178> to SUSB for <rdar://problem/5662487&6090114>
23 Revision 1.199 2008/03/14 19:58:38 mcguire
24 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
25 Make sure we add the record when sending LLQ refreshes
27 Revision 1.198 2008/03/07 23:29:24 cheshire
28 Fixed cosmetic byte order display issue in DumpPacket output
30 Revision 1.197 2008/03/05 22:51:29 mcguire
31 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
32 Even further refinements
34 Revision 1.196 2008/03/05 22:01:53 cheshire
35 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
36 Now that we optionally add the HINFO record, when rewriting the header fields into network byte
37 order, we need to use our updated msg->h.numAdditionals, not the stack variable numAdditionals
39 Revision 1.195 2008/03/05 19:06:30 mcguire
40 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
43 Revision 1.194 2008/03/05 00:26:06 cheshire
44 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
46 Revision 1.193 2007/12/17 23:42:36 cheshire
47 Added comments about DNSDigest_SignMessage()
49 Revision 1.192 2007/12/17 21:24:09 cheshire
50 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
51 We suspend sending of mDNS queries responses when going to sleep, so calculate GetNextScheduledEvent() time accordingly
53 Revision 1.191 2007/12/14 00:59:36 cheshire
54 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
55 While going to sleep, don't block event scheduling
57 Revision 1.190 2007/12/13 20:20:17 cheshire
58 Minor efficiency tweaks -- converted IdenticalResourceRecord, IdenticalSameNameRecord, and
59 SameRData from functions to macros, which allows the code to be inlined (the compiler can't
60 inline a function defined in a different compilation unit) and therefore optimized better.
62 Revision 1.189 2007/12/13 00:17:32 cheshire
63 RDataHashValue was not calculating hash value reliably for RDATA types that have 'holes' in the
64 in-memory representation (particularly SOA was affected by this, resulting in multiple duplicate
65 cache entities for the same SOA record, because they had erroneously different rdatahash values).
67 Revision 1.188 2007/12/13 00:13:03 cheshire
68 Simplified RDataHashValue to take a single ResourceRecord pointer, instead of separate rdlength and RDataBody
70 Revision 1.187 2007/12/08 00:35:20 cheshire
71 <rdar://problem/5636422> Updating TXT records is too slow
72 m->SuppressSending should not suppress all activity, just mDNS Query/Probe/Response
74 Revision 1.186 2007/11/15 22:52:29 cheshire
75 <rdar://problem/5589039> ERROR: mDNSPlatformWriteTCP - send Broken pipe
77 Revision 1.185 2007/10/10 20:22:03 cheshire
78 Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
79 apparently caused by trying to sign zero-length messages
81 Revision 1.184 2007/10/05 17:56:07 cheshire
82 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
84 Revision 1.183 2007/10/02 18:33:46 cheshire
85 Improved GetRRDisplayString to show all constituent strings within a text record
86 (up to the usual MaxMsg 120-character limit)
88 Revision 1.182 2007/10/01 19:45:01 cheshire
89 <rdar://problem/5514859> BTMM: Sometimes Back to My Mac autotunnel registrations are malformed
91 Revision 1.181 2007/10/01 18:36:53 cheshire
92 Yet another fix to finally get the DumpPacket RCODE display right
94 Revision 1.180 2007/09/29 21:30:38 cheshire
95 In DumpPacket/DumpRecords, show an error line if we run out of packet data
97 Revision 1.179 2007/09/29 20:44:56 cheshire
98 Fix error in DumpPacket where it was not displaying the RCODE field properly
100 Revision 1.178 2007/09/27 21:11:44 cheshire
101 Fixed spelling mistake: ROCDE -> RCODE
103 Revision 1.177 2007/09/27 18:51:26 cheshire
104 Improved DumpPacket to use "Zone/Prerequisites/Updates" nomenclature when displaying a DNS Update packet
106 Revision 1.176 2007/09/27 17:53:37 cheshire
107 Add display of RCODE and flags in DumpPacket output
109 Revision 1.175 2007/09/26 22:26:40 cheshire
110 Also show DNS query/response ID in DumpPacket output
112 Revision 1.174 2007/09/26 16:36:02 cheshire
113 In DumpPacket output, begin header line with "-- " to make it visually stand out better
115 Revision 1.173 2007/09/26 00:49:46 cheshire
116 Improve packet logging to show sent and received packets,
117 transport protocol (UDP/TCP/TLS) and source/destination address:port
119 Revision 1.172 2007/09/21 23:14:39 cheshire
120 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents in verbose debug mode
121 Changed DumpRecords to use LargeCacheRecord on the stack instead of the shared m->rec storage,
122 to eliminate "GetLargeResourceRecord: m->rec appears to be already in use" warnings
124 Revision 1.171 2007/09/21 21:12:36 cheshire
125 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
127 Revision 1.170 2007/09/07 21:16:58 cheshire
128 Add new symbol "NATPMPAnnouncementPort" (5350)
130 Revision 1.169 2007/08/30 00:31:20 cheshire
131 Improve "locking failure" debugging messages to show function name using __func__ macro
133 Revision 1.168 2007/08/28 23:58:42 cheshire
134 Rename HostTarget -> AutoTarget
136 Revision 1.167 2007/08/10 23:10:05 vazquez
137 <rdar://problem/5389850> mDNS: Reverse lookups of IPv6 link-local addresses always fail
139 Revision 1.166 2007/08/01 16:09:13 cheshire
140 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
142 Revision 1.165 2007/08/01 00:04:13 cheshire
143 <rdar://problem/5261696> Crash in tcpKQSocketCallback
144 Half-open TCP connections were not being cancelled properly
146 Revision 1.164 2007/07/27 20:48:43 cheshire
147 In DumpRecords(), include record TTL in output
149 Revision 1.163 2007/07/16 20:10:11 vazquez
150 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
151 Added SSDP port number
153 Revision 1.162 2007/07/10 01:59:33 cheshire
154 <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
155 Fixed GetPktLease to use shared m->rec instead of putting LargeCacheRecord on the stack
157 Revision 1.161 2007/07/06 18:56:26 cheshire
158 Check m->NextScheduledNATOp in GetNextScheduledEvent()
160 Revision 1.160 2007/06/29 00:06:42 vazquez
161 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
163 Revision 1.159 2007/06/28 21:17:17 cheshire
164 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
166 Revision 1.158 2007/05/25 00:25:43 cheshire
167 <rdar://problem/5227737> Need to enhance putRData to output all current known types
169 Revision 1.157 2007/05/23 00:32:15 cheshire
170 Don't treat uDNS responses as an entire RRSet (kDNSRecordTypePacketUniqueMask)
171 when received in a truncated UDP response
173 Revision 1.156 2007/05/15 00:29:00 cheshire
174 Print «ZERO ADDRESS» for %#a with a zero mDNSAddr
176 Revision 1.155 2007/05/07 22:07:47 cheshire
177 <rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types
179 Revision 1.154 2007/05/04 20:19:53 cheshire
180 Improve DumpPacket() output
182 Revision 1.153 2007/05/01 21:46:31 cheshire
183 Move GetLLQOptData/GetPktLease from uDNS.c into DNSCommon.c so that dnsextd can use them
185 Revision 1.152 2007/04/27 19:28:01 cheshire
186 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
187 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
188 -- it would start a query and then quickly cancel it, and then when
189 StartGetZoneData completed, it had a dangling pointer and crashed.)
191 Revision 1.151 2007/04/26 13:35:25 cheshire
192 Add kDNSType_SOA case in SameRDataBody, and a comment in GetLargeResourceRecord about why this is important
194 Revision 1.150 2007/04/24 00:17:33 cheshire
195 Made LocateLLQOptData guard against packets with bogus numAdditionals value
197 Revision 1.149 2007/04/23 21:43:00 cheshire
198 Remove debugging check
200 Revision 1.148 2007/04/23 04:55:29 cheshire
201 Add some defensive null pointer checks
203 Revision 1.147 2007/04/22 20:18:10 cheshire
204 Add comment about mDNSRandom()
206 Revision 1.146 2007/04/22 06:02:02 cheshire
207 <rdar://problem/4615977> Query should immediately return failure when no server
209 Revision 1.145 2007/04/20 21:17:24 cheshire
210 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
212 Revision 1.144 2007/04/19 18:02:43 cheshire
213 <rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit
215 Revision 1.143 2007/04/16 21:53:49 cheshire
216 Improve display of negative cache entries
218 Revision 1.142 2007/04/05 22:55:35 cheshire
219 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
221 Revision 1.141 2007/04/04 01:33:11 cheshire
222 <rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record
223 Overly defensive code was zeroing too much of the AuthRecord structure
225 Revision 1.140 2007/04/03 19:37:58 cheshire
226 Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918()
228 Revision 1.139 2007/04/03 19:18:39 cheshire
229 Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly
231 Revision 1.138 2007/03/28 21:14:08 cheshire
232 The rrclass field of an OPT pseudo-RR holds the sender's UDP payload size
234 Revision 1.137 2007/03/28 20:59:26 cheshire
235 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
237 Revision 1.136 2007/03/28 15:56:37 cheshire
238 <rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output
240 Revision 1.135 2007/03/28 01:20:05 cheshire
241 <rdar://problem/4883206> Improve/create logging for secure browse
243 Revision 1.134 2007/03/27 23:25:35 cheshire
244 Fix error caching SOA records
245 (cache entry was size of wire-format packed data, not size of in-memory structure)
247 Revision 1.133 2007/03/26 22:55:45 cheshire
248 Add OPT and TSIG to list of types DNSTypeName() knows about
250 Revision 1.132 2007/03/22 18:31:48 cheshire
251 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
253 Revision 1.131 2007/03/21 21:55:20 cheshire
254 <rdar://problem/5069688> Hostname gets ; or : which are illegal characters
255 Error in AppendLabelSuffix() for numbers close to the 32-bit limit
257 Revision 1.130 2007/03/21 19:23:37 cheshire
258 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
259 Make check less strict so we don't break Bonjour Browser
261 Revision 1.129 2007/03/21 01:00:45 cheshire
262 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
263 DeconstructServiceName() needs to be more defensive about what it considers legal
265 Revision 1.128 2007/03/21 00:30:02 cheshire
266 <rdar://problem/4789455> Multiple errors in DNameList-related code
268 Revision 1.127 2007/03/20 17:07:15 cheshire
269 Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket"
271 Revision 1.126 2007/03/10 03:26:44 cheshire
272 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
274 Revision 1.125 2007/03/07 00:08:58 cheshire
275 <rdar://problem/4347550> Don't allow hyphens at start of service type
277 Revision 1.124 2007/01/19 18:04:05 cheshire
278 For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT
280 Revision 1.123 2007/01/10 22:45:51 cheshire
281 Cast static strings to "(const domainname*)", not "(domainname*)"
283 Revision 1.122 2007/01/06 00:47:35 cheshire
284 Improve GetRRDisplayString to indicate when record has zero-length rdata
286 Revision 1.121 2007/01/05 08:30:39 cheshire
287 Trim excessive "$Log" checkin history from before 2006
288 (checkin history still available via "cvs log ..." of course)
290 Revision 1.120 2007/01/05 05:23:00 cheshire
291 Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd)
293 Revision 1.119 2007/01/05 04:30:16 cheshire
294 Change a couple of "(domainname *)" casts to "(const domainname *)"
296 Revision 1.118 2007/01/04 20:21:59 cheshire
297 <rdar://problem/4720673> uDNS: Need to start caching unicast records
298 Don't return multicast answers in response to unicast questions
300 Revision 1.117 2006/12/22 20:59:49 cheshire
301 <rdar://problem/4742742> Read *all* DNS keys from keychain,
302 not just key for the system-wide default registration domain
304 Revision 1.116 2006/12/21 00:04:07 cheshire
305 To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord()
307 Revision 1.115 2006/12/20 04:07:34 cheshire
308 Remove uDNS_info substructure from AuthRecord_struct
310 Revision 1.114 2006/12/19 22:40:04 cheshire
311 Fix compiler warnings
313 Revision 1.113 2006/12/19 02:21:08 cheshire
314 Delete spurious spaces
316 Revision 1.112 2006/12/15 20:42:10 cheshire
317 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
318 Additional defensive coding in GetLargeResourceRecord() to reject apparently-valid
319 rdata that actually runs past the end of the received packet data.
321 Revision 1.111 2006/12/15 19:09:57 cheshire
322 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
323 Made DomainNameLength() more defensive by adding a limit parameter, so it can be
324 safely used to inspect potentially malformed data received from external sources.
325 Without this, a domain name that starts off apparently valid, but extends beyond the end of
326 the received packet data, could have appeared valid if the random bytes are already in memory
327 beyond the end of the packet just happened to have reasonable values (e.g. all zeroes).
329 Revision 1.110 2006/11/18 05:01:30 cheshire
330 Preliminary support for unifying the uDNS and mDNS code,
331 including caching of uDNS answers
333 Revision 1.109 2006/11/10 00:54:14 cheshire
334 <rdar://problem/4816598> Changing case of Computer Name doesn't work
336 Revision 1.108 2006/10/05 23:11:18 cheshire
337 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
339 Revision 1.107 2006/09/15 21:20:14 cheshire
340 Remove uDNS_info substructure from mDNS_struct
342 Revision 1.106 2006/08/14 23:24:22 cheshire
343 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
345 Revision 1.105 2006/07/15 02:01:28 cheshire
346 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
347 Fix broken "empty string" browsing
349 Revision 1.104 2006/07/05 23:09:13 cheshire
350 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
351 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
353 Revision 1.103 2006/06/29 07:42:14 cheshire
354 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
356 Revision 1.102 2006/06/22 19:49:11 cheshire
357 Added (commented out) definitions for the LLMNR UDP port and multicast addresses
359 Revision 1.101 2006/06/15 21:35:15 cheshire
360 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
361 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
363 Revision 1.100 2006/06/08 22:58:46 cheshire
364 <rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
366 Revision 1.99 2006/05/18 01:32:33 cheshire
367 <rdar://problem/4472706> iChat: Lost connection with Bonjour
368 (mDNSResponder insufficiently defensive against malformed browsing PTR responses)
370 Revision 1.98 2006/03/19 17:00:58 cheshire
371 Define symbol MaxMsg instead of using hard-coded constant value '80'
373 Revision 1.97 2006/03/18 21:47:56 cheshire
374 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
376 Revision 1.96 2006/03/10 21:51:42 cheshire
377 <rdar://problem/4111464> After record update, old record sometimes remains in cache
378 Split out SameRDataBody() into a separate routine so it can be called from other code
380 Revision 1.95 2006/03/08 22:43:11 cheshire
381 Use "localdomain" symbol instead of literal string
383 Revision 1.94 2006/03/02 21:59:55 cheshire
384 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
385 Improve sanity checks & debugging support in GetLargeResourceRecord()
387 Revision 1.93 2006/03/02 20:30:47 cheshire
388 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
392 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
393 #define mDNS_InstantiateInlines 1
394 #include "DNSCommon.h"
396 // Disable certain benign warnings with Microsoft compilers
397 #if (defined(_MSC_VER))
398 // Disable "conditional expression is constant" warning for debug macros.
399 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
400 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
401 #pragma warning(disable:4127)
402 // Disable "array is too small to include a terminating null character" warning
403 // -- domain labels have an initial length byte, not a terminating null character
404 #pragma warning(disable:4295)
407 // ***************************************************************************
408 #if COMPILER_LIKES_PRAGMA_MARK
409 #pragma mark - Program Constants
412 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
413 mDNSexport
const mDNSv4Addr zerov4Addr
= { { 0 } };
414 mDNSexport
const mDNSv6Addr zerov6Addr
= { { 0 } };
415 mDNSexport
const mDNSEthAddr zeroEthAddr
= { { 0 } };
416 mDNSexport
const mDNSv4Addr onesIPv4Addr
= { { 255, 255, 255, 255 } };
417 mDNSexport
const mDNSv6Addr onesIPv6Addr
= { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
418 mDNSexport
const mDNSAddr zeroAddr
= { mDNSAddrType_None
, {{{ 0 }}} };
420 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
421 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
422 mDNSexport
const mDNSInterfaceID mDNSInterface_Unicast
= (mDNSInterfaceID
)2;
424 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
425 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
426 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
427 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
428 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
429 // with Microsoft's LLMNR client code.
431 #define SSDPPortAsNumber 1900
433 #define UnicastDNSPortAsNumber 53
434 #define NATPMPAnnouncementPortAsNumber 5350
435 #define NATPMPPortAsNumber 5351
436 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
437 #define MulticastDNSPortAsNumber 5353
438 #define LoopbackIPCPortAsNumber 5354
439 //#define MulticastDNSPortAsNumber 5355 // LLMNR
441 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
442 #define PrivateDNSPortAsNumber 5533
444 mDNSexport
const mDNSIPPort SSDPPort
= { { SSDPPortAsNumber
>> 8, SSDPPortAsNumber
& 0xFF } };
446 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
447 mDNSexport
const mDNSIPPort NATPMPAnnouncementPort
= { { NATPMPAnnouncementPortAsNumber
>> 8, NATPMPAnnouncementPortAsNumber
& 0xFF } };
448 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
449 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
450 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
451 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
453 mDNSexport
const mDNSIPPort NSIPCPort
= { { NSIPCPortAsNumber
>> 8, NSIPCPortAsNumber
& 0xFF } };
454 mDNSexport
const mDNSIPPort PrivateDNSPort
= { { PrivateDNSPortAsNumber
>> 8, PrivateDNSPortAsNumber
& 0xFF } };
456 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
457 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
458 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
459 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
460 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
462 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
463 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
464 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
465 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
466 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
467 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
469 mDNSexport
const mDNSOpaque64 zeroOpaque64
= { { 0 } };
471 // ***************************************************************************
472 #if COMPILER_LIKES_PRAGMA_MARK
474 #pragma mark - General Utility Functions
477 // return true for RFC1918 private addresses
478 mDNSexport mDNSBool
mDNSv4AddrIsRFC1918(mDNSv4Addr
*addr
)
480 return ((addr
->b
[0] == 10) || // 10/8 prefix
481 (addr
->b
[0] == 172 && (addr
->b
[1] & 0xF0) == 16) || // 172.16/12
482 (addr
->b
[0] == 192 && addr
->b
[1] == 168)); // 192.168/16
485 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
487 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
491 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
493 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
494 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
497 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
499 mDNSu32 slot
, used
= 0;
502 FORALL_CACHERECORDS(slot
, cg
, rr
)
503 if (rr
->resrec
.InterfaceID
== id
) used
++;
507 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
511 case kDNSType_A
: return("Addr");
512 case kDNSType_NS
: return("NS");
513 case kDNSType_CNAME
:return("CNAME");
514 case kDNSType_SOA
: return("SOA");
515 case kDNSType_NULL
: return("NULL");
516 case kDNSType_PTR
: return("PTR");
517 case kDNSType_HINFO
:return("HINFO");
518 case kDNSType_TXT
: return("TXT");
519 case kDNSType_AAAA
: return("AAAA");
520 case kDNSType_SRV
: return("SRV");
521 case kDNSType_OPT
: return("OPT");
522 case kDNSType_TSIG
: return("TSIG");
523 case kDNSQType_ANY
: return("ANY");
525 static char buffer
[16];
526 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
532 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
533 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
534 // long as this routine is only used for debugging messages, it probably isn't a big problem.
535 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*rr
, RDataBody
*rd
, char *buffer
)
537 #define Max (MaxMsg-1)
539 mDNSu32 length
= mDNS_snprintf(buffer
, Max
, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
540 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) return(buffer
);
541 if (!rr
->rdlength
) { mDNS_snprintf(buffer
+length
, Max
-length
, "<< ZERO RDATA LENGTH >>"); return(buffer
); }
545 case kDNSType_A
: mDNS_snprintf(buffer
+length
, Max
-length
, "%.4a", &rd
->ipv4
); break;
547 case kDNSType_NS
: // Same as PTR
548 case kDNSType_CNAME
:// Same as PTR
549 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, Max
-length
, "%##s", rd
->name
.c
); break;
551 case kDNSType_SOA
: mDNS_snprintf(buffer
+length
, Max
-length
, "%##s %##s %d %d %d %d %d",
552 rd
->soa
.mname
.c
, rd
->soa
.rname
.c
,
553 rd
->soa
.serial
, rd
->soa
.refresh
, rd
->soa
.retry
, rd
->soa
.expire
, rd
->soa
.min
);
556 case kDNSType_HINFO
:// Display this the same as TXT (show all constituent strings)
558 mDNSu8
*t
= rd
->txt
.c
;
559 while (t
< rd
->txt
.c
+ rr
->rdlength
)
561 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "%s%#s", t
> rd
->txt
.c
? "¦" : "", t
);
566 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, Max
-length
, "%.16a", &rd
->ipv6
); break;
567 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, Max
-length
, "%u %u %u %##s",
568 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
569 case kDNSType_OPT
: length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "%d Len %d ", rd
->opt
.opt
, rd
->opt
.optlen
);
570 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Max UDP %d ", rr
->rrclass
);
571 if (rd
->opt
.opt
== kDNSOpt_LLQ
)
573 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Vers %d ", rd
->opt
.OptData
.llq
.vers
);
574 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Op %d ", rd
->opt
.OptData
.llq
.llqOp
);
575 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Err/Port %d ", rd
->opt
.OptData
.llq
.err
);
576 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "ID %08X%08X ", rd
->opt
.OptData
.llq
.id
.l
[0], rd
->opt
.OptData
.llq
.id
.l
[1]);
577 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Lease %d", rd
->opt
.OptData
.llq
.llqlease
);
579 else if (rd
->opt
.opt
== kDNSOpt_Lease
)
580 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "kDNSOpt_Lease Lease %d", rd
->opt
.OptData
.updatelease
);
582 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Unknown opt %d", rd
->opt
.opt
);
584 default: mDNS_snprintf(buffer
+length
, Max
-length
, "RDLen %d: %s", rr
->rdlength
, rd
->data
);
585 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
586 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
= '.';
592 // Long-term we need to make this cross-platform, either by using our own embedded RC4 code
593 // to generate the pseudo-random sequence, or by adding another mDNSPlatformXXX() call.
594 // The former would be preferable because it makes our code self-contained, so it will run anywhere.
595 // The latter is less desirable because it increases the burden on people writing platform support layers
596 // to now implement one more function (and an important one at that, that needs to be cryptographically strong).
597 // For now, as a temporary fix, if we're building mDNSResponder for OS X we just use arc4random() directly here.
599 #if _BUILDING_XCODE_PROJECT_
603 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
) // Returns pseudo-random result from zero to max inclusive
605 static mDNSu32 seed
= 0;
611 seed
= mDNSPlatformRandomSeed(); // Pick an initial seed
612 for (i
=0; i
<100; i
++) seed
= seed
* 21 + 1; // And mix it up a bit
614 while (mask
< max
) mask
= (mask
<< 1) | 1;
616 #if _BUILDING_XCODE_PROJECT_
617 do seed
= arc4random();
619 do seed
= seed
* 21 + 1;
621 while ((seed
& mask
) > max
);
623 return (seed
& mask
);
626 mDNSexport mDNSu32
mDNSRandomFromFixedSeed(mDNSu32 seed
, mDNSu32 max
)
629 while (mask
< max
) mask
= (mask
<< 1) | 1;
630 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
631 return (seed
& mask
);
634 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
636 if (ip1
->type
== ip2
->type
)
640 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
641 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
642 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
648 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
652 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip
->ip
.v4
, AllDNSLinkGroup_v4
.ip
.v4
));
653 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip
->ip
.v6
, AllDNSLinkGroup_v6
.ip
.v6
));
654 default: return(mDNSfalse
);
658 // ***************************************************************************
659 #if COMPILER_LIKES_PRAGMA_MARK
661 #pragma mark - Domain Name Utility Functions
664 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
667 const int len
= *a
++;
669 if (len
> MAX_DOMAIN_LABEL
)
670 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
672 if (len
!= *b
++) return(mDNSfalse
);
673 for (i
=0; i
<len
; i
++)
677 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
678 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
679 if (ac
!= bc
) return(mDNSfalse
);
684 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
686 const mDNSu8
* a
= d1
->c
;
687 const mDNSu8
* b
= d2
->c
;
688 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
692 if (a
+ 1 + *a
>= max
)
693 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
694 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
702 mDNSexport mDNSBool
SameDomainNameCS(const domainname
*const d1
, const domainname
*const d2
)
704 mDNSu16 l1
= DomainNameLength(d1
);
705 mDNSu16 l2
= DomainNameLength(d2
);
706 return(l1
<= MAX_DOMAIN_NAME
&& l1
== l2
&& mDNSPlatformMemSame(d1
, d2
, l1
));
709 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
711 // Domains that are defined to be resolved via link-local multicast are:
712 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
713 static const domainname
*nL
= (const domainname
*)"\x5" "local";
714 static const domainname
*nR
= (const domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
715 static const domainname
*n8
= (const domainname
*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
716 static const domainname
*n9
= (const domainname
*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
717 static const domainname
*nA
= (const domainname
*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
718 static const domainname
*nB
= (const domainname
*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
720 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
; // Top-level domain, second-level domain, etc.
721 d1
= d2
= d3
= d4
= d5
= mDNSNULL
;
724 d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
725 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
728 if (d1
&& SameDomainName(d1
, nL
)) return(mDNStrue
);
729 if (d4
&& SameDomainName(d4
, nR
)) return(mDNStrue
);
730 if (d5
&& SameDomainName(d5
, n8
)) return(mDNStrue
);
731 if (d5
&& SameDomainName(d5
, n9
)) return(mDNStrue
);
732 if (d5
&& SameDomainName(d5
, nA
)) return(mDNStrue
);
733 if (d5
&& SameDomainName(d5
, nB
)) return(mDNStrue
);
737 // Returns length of a domain name INCLUDING the byte for the final null label
738 // e.g. for the root label "." it returns one
739 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
740 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
741 // If the given domainname is invalid, result is 256 (MAX_DOMAIN_NAME+1)
742 mDNSexport mDNSu16
DomainNameLengthLimit(const domainname
*const name
, const mDNSu8
*limit
)
744 const mDNSu8
*src
= name
->c
;
745 while (src
< limit
&& *src
<= MAX_DOMAIN_LABEL
)
747 if (*src
== 0) return((mDNSu16
)(src
- name
->c
+ 1));
750 return(MAX_DOMAIN_NAME
+1);
753 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
754 // for the final null label, e.g. for the root label "." it returns one.
755 // E.g. for the FQDN "foo.com." it returns 9
756 // (length, three data bytes, length, three more data bytes, final zero).
757 // In the case where a parent domain name is provided, and the given name is a child
758 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
759 // of the child name, plus TWO bytes for the compression pointer.
760 // E.g. for the name "foo.com." with parent "com.", it returns 6
761 // (length, three data bytes, two-byte compression pointer).
762 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
764 const mDNSu8
*src
= name
->c
;
765 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
768 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
769 if (parent
&& SameDomainName((const domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
771 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
773 return((mDNSu16
)(src
- name
->c
+ 1));
776 // CountLabels() returns number of labels in name, excluding final root label
777 // (e.g. for "apple.com." CountLabels returns 2.)
778 mDNSexport
int CountLabels(const domainname
*d
)
782 for (ptr
= d
->c
; *ptr
; ptr
= ptr
+ ptr
[0] + 1) count
++;
786 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
787 // returning a pointer to the suffix with 'skip' labels removed.
788 mDNSexport
const domainname
*SkipLeadingLabels(const domainname
*d
, int skip
)
790 while (skip
> 0 && d
->c
[0]) { d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]); skip
--; }
794 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
795 // The C string contains the label as-is, with no escaping, etc.
796 // Any dots in the name are literal dots, not label separators
797 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
798 // in the domainname bufer (i.e. the next byte after the terminating zero).
799 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
800 // AppendLiteralLabelString returns mDNSNULL.
801 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
803 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
804 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
805 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
806 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
807 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
809 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
810 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
811 *ptr
++ = 0; // Put the null root label on the end
812 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
813 else return(ptr
); // Success: return new value of ptr
816 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
817 // The C string is in conventional DNS syntax:
818 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
819 // If successful, AppendDNSNameString returns a pointer to the next unused byte
820 // in the domainname bufer (i.e. the next byte after the terminating zero).
821 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
822 // AppendDNSNameString returns mDNSNULL.
823 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
825 const char *cstr
= cstring
;
826 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
827 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
828 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
830 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
831 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
832 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
834 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
835 if (c
== '\\') // If escape character, check next character
837 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
838 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
839 { // If three decimal digits,
840 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
841 int v1
= cstr
[ 0] - '0';
842 int v2
= cstr
[ 1] - '0';
843 int val
= v0
* 100 + v1
* 10 + v2
;
844 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
847 *ptr
++ = c
; // Write the character
849 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
850 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
852 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
855 *ptr
++ = 0; // Put the null root label on the end
856 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
857 else return(ptr
); // Success: return new value of ptr
860 // AppendDomainLabel appends a single label to a name.
861 // If successful, AppendDomainLabel returns a pointer to the next unused byte
862 // in the domainname bufer (i.e. the next byte after the terminating zero).
863 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
864 // AppendDomainLabel returns mDNSNULL.
865 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
868 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
870 // Check label is legal
871 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
873 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
874 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
876 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
877 *ptr
++ = 0; // Put the null root label on the end
881 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
883 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
884 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
885 const mDNSu8
* src
= append
->c
;
889 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
890 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
891 *ptr
= 0; // Put the null root label on the end
897 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
898 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
899 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
900 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
901 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
902 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
903 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
905 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
906 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
907 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
908 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
909 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
912 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
913 // The C string is in conventional DNS syntax:
914 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
915 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
916 // in the domainname bufer (i.e. the next byte after the terminating zero).
917 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
918 // MakeDomainNameFromDNSNameString returns mDNSNULL.
919 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
921 name
->c
[0] = 0; // Make an empty domain name
922 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
925 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
927 const mDNSu8
* src
= label
->c
; // Domain label we're reading
928 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
929 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
930 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
931 while (src
< end
) // While we have characters in the label
936 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
937 *ptr
++ = esc
; // Output escape character
938 else if (c
<= ' ') // If non-printing ascii,
939 { // Output decimal escape sequence
941 *ptr
++ = (char) ('0' + (c
/ 100) );
942 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
943 c
= (mDNSu8
)('0' + (c
) % 10);
946 *ptr
++ = (char)c
; // Copy the character
948 *ptr
= 0; // Null-terminate the string
949 return(ptr
); // and return
952 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
953 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
955 const mDNSu8
*src
= name
->c
; // Domain name we're reading
956 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
958 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
960 while (*src
) // While more characters in the domain name
962 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
963 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
964 if (!ptr
) return(mDNSNULL
);
966 *ptr
++ = '.'; // Write the dot after the label
969 *ptr
++ = 0; // Null-terminate the string
970 return(ptr
); // and return
974 // Host names must start with a letter, end with a letter or digit,
975 // and have as interior characters only letters, digits, and hyphen.
976 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
978 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
980 const mDNSu8
* src
= &UTF8Name
[1];
981 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
982 mDNSu8
* ptr
= &hostlabel
->c
[1];
983 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
986 // Delete apostrophes from source name
987 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
988 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
989 { src
+= 3; continue; } // Unicode curly apostrophe
992 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
993 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
997 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
998 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
1001 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
1002 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
1003 ((X)[4] | 0x20) == 'p')
1005 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
1006 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
1009 mDNSu8
*dst
= fqdn
->c
;
1011 const char *errormsg
;
1013 // In the case where there is no name (and ONLY in that case),
1014 // a single-label subtype is allowed as the first label of a three-part "type"
1017 const mDNSu8
*s0
= type
->c
;
1018 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
1020 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
1021 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1023 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
1024 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
1026 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
1027 src
= s0
; // Copy the first label
1029 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
1030 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
1031 type
= (const domainname
*)s1
;
1033 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1034 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1035 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1036 if (SameDomainName((domainname
*)s0
, (const domainname
*)"\x09_services\x07_dns-sd\x04_udp"))
1037 dst
-= sizeof(SubTypeLabel
);
1043 if (name
&& name
->c
[0])
1045 src
= name
->c
; // Put the service name into the domain name
1047 if (len
>= 0x40) { errormsg
= "Service instance name too long"; goto fail
; }
1048 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1051 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1053 src
= type
->c
; // Put the service type into the domain name
1055 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
)))
1057 errormsg
= "Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
1060 if (src
[1] != '_') { errormsg
= "Application protocol name must begin with underscore"; goto fail
; }
1061 for (i
=2; i
<=len
; i
++)
1063 // Letters and digits are allowed anywhere
1064 if (mdnsIsLetter(src
[i
]) || mdnsIsDigit(src
[i
])) continue;
1065 // Hyphens are only allowed as interior characters
1066 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1067 // with the same rule as hyphens
1068 if ((src
[i
] == '-' || src
[i
] == '_') && i
> 2 && i
< len
) continue;
1069 errormsg
= "Application protocol name must contain only letters, digits, and hyphens"; goto fail
;
1071 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1074 if (!ValidTransportProtocol(src
)) { errormsg
= "Transport protocol name must be _udp or _tcp"; goto fail
; }
1075 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1077 if (*src
) { errormsg
= "Service type must have only two labels"; goto fail
; }
1080 if (!domain
->c
[0]) { errormsg
= "Service domain must be non-empty"; goto fail
; }
1081 if (SameDomainName(domain
, (const domainname
*)"\x05" "local" "\x04" "arpa"))
1082 { errormsg
= "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
1083 dst
= AppendDomainName(fqdn
, domain
);
1084 if (!dst
) { errormsg
= "Service domain too long"; goto fail
; }
1088 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
1092 // A service name has the form: instance.application-protocol.transport-protocol.domain
1093 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1094 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1095 // However, if the given FQDN doesn't contain at least three labels,
1096 // DeconstructServiceName will reject it and return mDNSfalse.
1097 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
1098 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
1101 const mDNSu8
*src
= fqdn
->c
;
1102 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
1105 dst
= name
->c
; // Extract the service name
1107 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
1108 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
1109 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1111 dst
= type
->c
; // Extract the service type
1113 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
1114 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
1115 if (src
[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse
); }
1116 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1119 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
1120 // Can't do this check right now, until Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1121 //if (!ValidTransportProtocol(src))
1122 // { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
1123 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1124 *dst
++ = 0; // Put terminator on the end of service type
1126 dst
= domain
->c
; // Extract the service domain
1131 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
1132 if (src
+ 1 + len
+ 1 >= max
)
1133 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
1134 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1136 *dst
++ = 0; // Put the null root label on the end
1142 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1143 // 10xxxxxx is a continuation byte of a multi-byte character
1144 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1145 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1146 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1147 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1148 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1150 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1151 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1152 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1153 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1154 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1156 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
1160 mDNSu8 c1
= string
[max
]; // First byte after cut point
1161 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : (mDNSu8
)0xB0; // Second byte after cut point
1162 length
= max
; // Trim length down
1165 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1166 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1167 // If so, then we continue to chop more bytes until we get to a legal chop point.
1168 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1169 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1170 if (!continuation
&& !secondsurrogate
) break;
1172 c1
= string
[--length
];
1174 // Having truncated characters off the end of our string, also cut off any residual white space
1175 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1180 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1181 // name ends in "-nnn", where n is some decimal number.
1182 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1184 mDNSu16 l
= name
->c
[0];
1188 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1189 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1190 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1192 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1193 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1197 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1198 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1200 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1201 return (name
->c
[l
] == '-');
1205 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1206 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1207 // from the suffix that was removed.
1208 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1210 mDNSu32 val
= 0, multiplier
= 1;
1212 // Chop closing parentheses from RichText suffix
1213 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1215 // Get any existing numerical suffix off the name
1216 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
1217 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1219 // Chop opening parentheses or dash from suffix
1222 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1226 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1232 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1233 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1234 mDNSexport
void AppendLabelSuffix(domainlabel
*const name
, mDNSu32 val
, const mDNSBool RichText
)
1236 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1237 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1239 // Truncate trailing spaces from RichText names
1240 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1242 while (divisor
< 0xFFFFFFFFUL
/10 && val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1244 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1246 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1247 else { name
->c
[++name
->c
[0]] = '-'; }
1251 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1256 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1259 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1263 if (LabelContainsSuffix(name
, RichText
))
1264 val
= RemoveLabelSuffix(name
, RichText
);
1266 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1267 // If existing suffix in the range 2-9, increment it.
1268 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1269 // so add a random increment to improve the chances of finding an available name next time.
1270 if (val
== 0) val
= 2;
1271 else if (val
< 10) val
++;
1272 else val
+= 1 + mDNSRandom(99);
1274 AppendLabelSuffix(name
, val
, RichText
);
1277 // ***************************************************************************
1278 #if COMPILER_LIKES_PRAGMA_MARK
1280 #pragma mark - Resource Record Utility Functions
1283 // Set up a AuthRecord with sensible default values.
1284 // These defaults may be overwritten with new values before mDNS_Register is called
1285 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
1286 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
1288 // Don't try to store a TTL bigger than we can represent in platform time units
1289 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
1290 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
1291 else if (ttl
== 0) // And Zero TTL is illegal
1292 ttl
= DefaultTTLforRRType(rrtype
);
1294 // Field Group 1: The actual information pertaining to this resource record
1295 rr
->resrec
.RecordType
= RecordType
;
1296 rr
->resrec
.InterfaceID
= InterfaceID
;
1297 rr
->resrec
.name
= &rr
->namestorage
;
1298 rr
->resrec
.rrtype
= rrtype
;
1299 rr
->resrec
.rrclass
= kDNSClass_IN
;
1300 rr
->resrec
.rroriginalttl
= ttl
;
1301 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1302 // rr->resrec.rdestimate = set in mDNS_Register_internal
1303 // rr->resrec.rdata = MUST be set by client
1306 rr
->resrec
.rdata
= RDataStorage
;
1309 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1310 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1313 // Field Group 2: Persistent metadata for Authoritative Records
1314 rr
->Additional1
= mDNSNULL
;
1315 rr
->Additional2
= mDNSNULL
;
1316 rr
->DependentOn
= mDNSNULL
;
1317 rr
->RRSet
= mDNSNULL
;
1318 rr
->RecordCallback
= Callback
;
1319 rr
->RecordContext
= Context
;
1321 rr
->AutoTarget
= Target_Manual
;
1322 rr
->AllowRemoteQuery
= mDNSfalse
;
1323 rr
->ForceMCast
= mDNSfalse
;
1325 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1326 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1328 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1329 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1330 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1331 rr
->state
= regState_Zero
;
1337 rr
->UpdateServer
= zeroAddr
;
1338 rr
->UpdatePort
= zeroIPPort
;
1343 rr
->InFlightRData
= 0;
1344 rr
->InFlightRDLen
= 0;
1345 rr
->QueuedRData
= 0;
1346 rr
->QueuedRDLen
= 0;
1348 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
1351 mDNSexport mDNSu32
RDataHashValue(const ResourceRecord
*const rr
)
1356 case kDNSType_CNAME
:
1358 case kDNSType_DNAME
: return DomainNameHashValue(&rr
->rdata
->u
.name
);
1360 case kDNSType_SOA
: return rr
->rdata
->u
.soa
.serial
+
1361 rr
->rdata
->u
.soa
.refresh
+
1362 rr
->rdata
->u
.soa
.retry
+
1363 rr
->rdata
->u
.soa
.expire
+
1364 rr
->rdata
->u
.soa
.min
+
1365 DomainNameHashValue(&rr
->rdata
->u
.soa
.mname
) +
1366 DomainNameHashValue(&rr
->rdata
->u
.soa
.rname
);
1369 case kDNSType_AFSDB
:
1371 case kDNSType_KX
: return DomainNameHashValue(&rr
->rdata
->u
.mx
.exchange
);
1373 case kDNSType_RP
: return DomainNameHashValue(&rr
->rdata
->u
.rp
.mbox
) + DomainNameHashValue(&rr
->rdata
->u
.rp
.txt
);
1375 case kDNSType_PX
: return DomainNameHashValue(&rr
->rdata
->u
.px
.map822
) + DomainNameHashValue(&rr
->rdata
->u
.px
.mapx400
);
1377 case kDNSType_SRV
: return DomainNameHashValue(&rr
->rdata
->u
.srv
.target
);
1379 case kDNSType_OPT
: // Okay to use blind memory sum because there are no 'holes' in the in-memory representation
1385 for (i
=0; i
+1 < rr
->rdlength
; i
+=2)
1387 sum
+= (((mDNSu32
)(rr
->rdata
->u
.data
[i
])) << 8) | rr
->rdata
->u
.data
[i
+1];
1388 sum
= (sum
<<3) | (sum
>>29);
1390 if (i
< rr
->rdlength
)
1392 sum
+= ((mDNSu32
)(rr
->rdata
->u
.data
[i
])) << 8;
1399 // r1 has to be a full ResourceRecord including rrtype and rdlength
1400 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1401 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1406 case kDNSType_CNAME
:
1408 case kDNSType_DNAME
:return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->name
));
1410 case kDNSType_SOA
: return(mDNSBool
)( r1
->rdata
->u
.soa
.serial
== r2
->soa
.serial
&&
1411 r1
->rdata
->u
.soa
.refresh
== r2
->soa
.refresh
&&
1412 r1
->rdata
->u
.soa
.retry
== r2
->soa
.retry
&&
1413 r1
->rdata
->u
.soa
.expire
== r2
->soa
.expire
&&
1414 r1
->rdata
->u
.soa
.min
== r2
->soa
.min
&&
1415 SameDomainName(&r1
->rdata
->u
.soa
.mname
, &r2
->soa
.mname
) &&
1416 SameDomainName(&r1
->rdata
->u
.soa
.rname
, &r2
->soa
.rname
));
1419 case kDNSType_AFSDB
:
1421 case kDNSType_KX
: return(mDNSBool
)( r1
->rdata
->u
.mx
.preference
== r2
->mx
.preference
&&
1422 SameDomainName(&r1
->rdata
->u
.mx
.exchange
, &r2
->mx
.exchange
));
1424 case kDNSType_RP
: return(mDNSBool
)( SameDomainName(&r1
->rdata
->u
.rp
.mbox
, &r2
->rp
.mbox
) &&
1425 SameDomainName(&r1
->rdata
->u
.rp
.txt
, &r2
->rp
.txt
));
1427 case kDNSType_PX
: return(mDNSBool
)( r1
->rdata
->u
.px
.preference
== r2
->px
.preference
&&
1428 SameDomainName(&r1
->rdata
->u
.px
.map822
, &r2
->px
.map822
) &&
1429 SameDomainName(&r1
->rdata
->u
.px
.mapx400
, &r2
->px
.mapx400
));
1431 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->srv
.priority
&&
1432 r1
->rdata
->u
.srv
.weight
== r2
->srv
.weight
&&
1433 mDNSSameIPPort(r1
->rdata
->u
.srv
.port
, r2
->srv
.port
) &&
1434 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->srv
.target
));
1436 case kDNSType_OPT
: // Okay to use blind memory compare because there are no 'holes' in the in-memory representation
1438 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->data
, r1
->rdlength
));
1442 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1443 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1444 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1445 // because it has to check all the way to the end of the names to be sure.
1446 // In cases where we know in advance that the names match it's especially advantageous to skip the
1447 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1449 mDNSexport mDNSBool
SameNameRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1451 if (rr
->InterfaceID
&&
1452 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1453 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1455 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1456 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1458 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1459 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1460 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1465 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1467 if (rr
->InterfaceID
&&
1468 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1469 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1471 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1472 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1474 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1475 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1476 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1477 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1480 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1482 const RDataBody
*rd
= &rr
->rdata
->u
;
1483 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1484 if (rr
->rrclass
== kDNSQClass_ANY
) return(rr
->rdlength
); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1485 else switch (rr
->rrtype
)
1487 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1490 case kDNSType_CNAME
:
1492 case kDNSType_DNAME
:return(CompressedDomainNameLength(&rd
->name
, name
));
1494 case kDNSType_SOA
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1495 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1496 5 * sizeof(mDNSOpaque32
));
1504 case kDNSType_DHCID
:return(rr
->rdlength
); // Not self-describing, so have to just trust rdlength
1506 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1509 case kDNSType_AFSDB
:
1511 case kDNSType_KX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->mx
.exchange
, name
));
1513 case kDNSType_RP
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->rp
.mbox
, name
) +
1514 CompressedDomainNameLength(&rd
->rp
.txt
, name
));
1516 case kDNSType_PX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->px
.map822
, name
) +
1517 CompressedDomainNameLength(&rd
->px
.mapx400
, name
));
1519 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1521 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1523 case kDNSType_OPT
: return(rr
->rdlength
);
1525 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1526 return(rr
->rdlength
);
1530 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1536 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1538 case kDNSType_NS
: // Same as PTR
1539 case kDNSType_MD
: // Same as PTR
1540 case kDNSType_MF
: // Same as PTR
1541 case kDNSType_CNAME
:// Same as PTR
1542 //case kDNSType_SOA not checked
1543 case kDNSType_MB
: // Same as PTR
1544 case kDNSType_MG
: // Same as PTR
1545 case kDNSType_MR
: // Same as PTR
1546 //case kDNSType_NULL not checked (no specified format, so always valid)
1547 //case kDNSType_WKS not checked
1548 case kDNSType_PTR
: len
= DomainNameLengthLimit(&rd
->u
.name
, rd
->u
.data
+ rdlength
);
1549 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1551 case kDNSType_HINFO
:// Same as TXT (roughly)
1552 case kDNSType_MINFO
:// Same as TXT (roughly)
1553 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1555 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1556 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1557 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1558 return (ptr
== end
);
1561 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1563 case kDNSType_MX
: // Must be at least two-byte preference, plus domainname
1564 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1565 len
= DomainNameLengthLimit(&rd
->u
.mx
.exchange
, rd
->u
.data
+ rdlength
);
1566 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1568 case kDNSType_SRV
: // Must be at least priority+weight+port, plus domainname
1569 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1570 len
= DomainNameLengthLimit(&rd
->u
.srv
.target
, rd
->u
.data
+ rdlength
);
1571 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1573 default: return(mDNStrue
); // Allow all other types without checking
1577 // ***************************************************************************
1578 #if COMPILER_LIKES_PRAGMA_MARK
1580 #pragma mark - DNS Message Creation Functions
1583 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1587 h
->numQuestions
= 0;
1589 h
->numAuthorities
= 0;
1590 h
->numAdditionals
= 0;
1593 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1595 const mDNSu8
*result
= end
- *domname
- 1;
1597 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1599 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1600 while (result
>= base
)
1602 // If the length byte and first character of the label match, then check further to see
1603 // if this location in the packet will yield a useful name compression pointer.
1604 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1606 const mDNSu8
*name
= domname
;
1607 const mDNSu8
*targ
= result
;
1608 while (targ
+ *name
< end
)
1610 // First see if this label matches
1612 const mDNSu8
*pointertarget
;
1613 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1614 if (i
<= *name
) break; // If label did not match, bail out
1615 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1616 name
+= 1 + *name
; // and proceed to check next label
1617 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1618 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1620 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1621 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1622 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1623 if (targ
+1 >= end
) break; // Second byte not present!
1624 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1625 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1626 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1627 targ
= pointertarget
;
1630 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1635 // Put a string of dot-separated labels as length-prefixed labels
1636 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1637 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1638 // end points to the end of the message so far
1639 // ptr points to where we want to put the name
1640 // limit points to one byte past the end of the buffer that we must not overrun
1641 // domainname is the name to put
1642 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1643 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1645 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1646 const mDNSu8
* np
= name
->c
;
1647 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1648 const mDNSu8
* pointer
= mDNSNULL
;
1649 const mDNSu8
*const searchlimit
= ptr
;
1651 if (!ptr
) { LogMsg("putDomainNameAsLabels ptr is null"); return(mDNSNULL
); }
1653 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1655 if (*np
> MAX_DOMAIN_LABEL
)
1656 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1658 // This check correctly allows for the final trailing root label:
1660 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1661 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1662 // We know that max will be at name->c[255]
1663 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1664 // six bytes, then exit the loop, write the final terminating root label, and the domain
1665 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1666 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1667 if (np
+ 1 + *np
>= max
)
1668 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1670 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1671 if (pointer
) // Use a compression pointer if we can
1673 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1674 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1675 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1678 else // Else copy one label and try again
1682 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1684 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1688 if (ptr
< limit
) // If we didn't run out of space
1690 *ptr
++ = 0; // Put the final root label
1691 return(ptr
); // and return
1697 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1699 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1700 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1701 return ptr
+ sizeof(mDNSOpaque16
);
1704 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
1706 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
1707 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
1708 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
1709 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
1710 return ptr
+ sizeof(mDNSu32
);
1713 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, const ResourceRecord
*const rr
)
1718 while (nput
< rr
->rdlength
)
1720 // check if space for opt/optlen
1721 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1722 opt
= (rdataOPT
*)(rr
->rdata
->u
.data
+ nput
);
1723 ptr
= putVal16(ptr
, opt
->opt
);
1724 ptr
= putVal16(ptr
, opt
->optlen
);
1725 nput
+= 2 * sizeof(mDNSu16
);
1726 if (opt
->opt
== kDNSOpt_LLQ
)
1728 if (ptr
+ LLQ_OPTLEN
> limit
) goto space_err
;
1729 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1730 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1731 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1732 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
.b
, 8); // 8-byte id
1734 ptr
= putVal32(ptr
, opt
->OptData
.llq
.llqlease
);
1737 else if (opt
->opt
== kDNSOpt_Lease
)
1739 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1740 ptr
= putVal32(ptr
, opt
->OptData
.updatelease
);
1741 nput
+= sizeof(mDNSs32
);
1743 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1749 LogMsg("ERROR: putOptRData - out of space");
1753 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1755 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1756 *ptr
+= sizeof(mDNSOpaque16
);
1760 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*const limit
, LargeCacheRecord
*const cr
, mDNSu16 pktRDLen
)
1763 ResourceRecord
*const rr
= &cr
->r
.resrec
;
1764 rdataOPT
*opt
= (rdataOPT
*)rr
->rdata
->u
.data
;
1766 while (nread
< pktRDLen
&& (mDNSu8
*)opt
< rr
->rdata
->u
.data
+ MaximumRDSize
- sizeof(rdataOPT
))
1768 // space for opt + optlen
1769 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1770 opt
->opt
= getVal16(&ptr
);
1771 opt
->optlen
= getVal16(&ptr
);
1772 nread
+= 2 * sizeof(mDNSu16
);
1773 if (opt
->opt
== kDNSOpt_LLQ
)
1775 if ((unsigned)(limit
- ptr
) < LLQ_OPTLEN
) goto space_err
;
1776 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1777 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1778 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1779 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
.b
, ptr
, 8);
1781 opt
->OptData
.llq
.llqlease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1782 if (opt
->OptData
.llq
.llqlease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1783 opt
->OptData
.llq
.llqlease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1784 ptr
+= sizeof(mDNSOpaque32
);
1785 nread
+= LLQ_OPTLEN
;
1787 else if (opt
->opt
== kDNSOpt_Lease
)
1789 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1791 opt
->OptData
.updatelease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1792 if (opt
->OptData
.updatelease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1793 opt
->OptData
.updatelease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1794 ptr
+= sizeof(mDNSs32
);
1795 nread
+= sizeof(mDNSs32
);
1797 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1798 opt
++; // increment pointer into rdatabody
1801 rr
->rdlength
= pktRDLen
;
1805 LogMsg("ERROR: getLLQRdata - out of space");
1809 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1810 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const ResourceRecord
*const rr
)
1814 case kDNSType_A
: if (rr
->rdlength
!= 4)
1816 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1819 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1820 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[0];
1821 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[1];
1822 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[2];
1823 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[3];
1827 case kDNSType_CNAME
:
1829 case kDNSType_DNAME
:return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1831 case kDNSType_SOA
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.soa
.mname
);
1832 if (!ptr
) return(mDNSNULL
);
1833 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.soa
.rname
);
1834 if (!ptr
|| ptr
+ 20 > limit
) return(mDNSNULL
);
1835 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.serial
);
1836 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.refresh
);
1837 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.retry
);
1838 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.expire
);
1839 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.min
);
1843 case kDNSType_HINFO
:
1849 case kDNSType_DHCID
:if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1850 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, rr
->rdlength
);
1851 return(ptr
+ rr
->rdlength
);
1854 case kDNSType_AFSDB
:
1856 case kDNSType_KX
: if (ptr
+ 3 > limit
) return(mDNSNULL
);
1857 ptr
= putVal16(ptr
, rr
->rdata
->u
.mx
.preference
);
1858 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.mx
.exchange
));
1860 case kDNSType_RP
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.rp
.mbox
);
1861 if (!ptr
) return(mDNSNULL
);
1862 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.rp
.txt
);
1865 case kDNSType_PX
: if (ptr
+ 5 > limit
) return(mDNSNULL
);
1866 ptr
= putVal16(ptr
, rr
->rdata
->u
.px
.preference
);
1867 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.px
.map822
);
1868 if (!ptr
) return(mDNSNULL
);
1869 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.px
.mapx400
);
1872 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1874 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1877 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1878 mDNSPlatformMemCopy(ptr
, &rr
->rdata
->u
.ipv6
, sizeof(rr
->rdata
->u
.ipv6
));
1879 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1881 case kDNSType_SRV
: if (ptr
+ 7 > limit
) return(mDNSNULL
);
1882 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1883 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1884 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1885 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1886 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1887 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1888 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1890 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1892 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1893 if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1894 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, rr
->rdlength
);
1895 return(ptr
+ rr
->rdlength
);
1899 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
1901 mDNSu16 rrclass
= (rr
->rrtype
== kDNSType_OPT
) ? NormalMaxDNSMessageData
: rr
->rrclass
;
1903 mDNSu16 actualLength
;
1905 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1907 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1911 if (!ptr
) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL
); }
1913 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
1914 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1915 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1916 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1917 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1918 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1919 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1920 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1921 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1922 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1923 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1924 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1926 // Go back and fill in the actual number of data bytes we wrote
1927 // (actualLength can be less than rdlength when domain name compression is used)
1928 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1929 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1930 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1932 if (count
) (*count
)++;
1933 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1937 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1940 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1941 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1944 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1945 mDNSu16
*count
, const AuthRecord
*rr
)
1947 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
1948 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1949 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1950 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1951 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1952 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1953 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1954 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1959 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1961 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1962 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1963 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1964 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1965 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1966 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1967 msg
->h
.numQuestions
++;
1971 // for dynamic updates
1972 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
1974 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
1975 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
1976 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
1977 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
1978 *ptr
++ = zoneClass
.b
[0];
1979 *ptr
++ = zoneClass
.b
[1];
1980 msg
->h
.mDNS_numZones
++;
1984 // for dynamic updates
1985 mDNSexport mDNSu8
*putPrereqNameNotInUse(const domainname
*const name
, DNSMessage
*const msg
, mDNSu8
*const ptr
, mDNSu8
*const end
)
1988 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1989 AssignDomainName(&prereq
.namestorage
, name
);
1990 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
1991 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
1992 return putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
1995 // for dynamic updates
1996 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
1998 // deletion: specify record w/ TTL 0, class NONE
1999 const mDNSu16 origclass
= rr
->rrclass
;
2000 rr
->rrclass
= kDNSClass_NONE
;
2001 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
2002 rr
->rrclass
= origclass
;
2006 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
2008 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2009 mDNSu16
class = kDNSQClass_ANY
;
2011 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2012 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2013 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2014 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2015 ptr
[2] = (mDNSu8
)(class >> 8);
2016 ptr
[3] = (mDNSu8
)(class & 0xFF);
2017 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2018 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2020 msg
->h
.mDNS_numUpdates
++;
2024 // for dynamic updates
2025 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
2027 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2028 mDNSu16
class = kDNSQClass_ANY
;
2029 mDNSu16 rrtype
= kDNSQType_ANY
;
2031 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2032 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2033 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2034 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2035 ptr
[2] = (mDNSu8
)(class >> 8);
2036 ptr
[3] = (mDNSu8
)(class & 0xFF);
2037 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2038 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2040 msg
->h
.mDNS_numUpdates
++;
2044 // for dynamic updates
2045 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
2048 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
2049 rr
.resrec
.rrclass
= NormalMaxDNSMessageData
;
2050 rr
.resrec
.rdlength
= LEASE_OPT_RDLEN
;
2051 rr
.resrec
.rdestimate
= LEASE_OPT_RDLEN
;
2052 rr
.resrec
.rdata
->u
.opt
.opt
= kDNSOpt_Lease
;
2053 rr
.resrec
.rdata
->u
.opt
.optlen
= sizeof(mDNSs32
);
2054 rr
.resrec
.rdata
->u
.opt
.OptData
.updatelease
= lease
;
2055 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, &rr
.resrec
, 0);
2056 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
2060 mDNSexport mDNSu8
*putHINFO(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
, DomainAuthInfo
*authInfo
)
2062 if (authInfo
&& authInfo
->AutoTunnel
)
2065 mDNSu8
*h
= hinfo
.rdatastorage
.u
.data
;
2066 mDNSu16 len
= 2 + m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0];
2068 mDNS_SetupResourceRecord(&hinfo
, mDNSNULL
, mDNSInterface_Any
, kDNSType_HINFO
, 0, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
2069 AppendDomainLabel(&hinfo
.namestorage
, &m
->hostlabel
);
2070 AppendDomainName (&hinfo
.namestorage
, &authInfo
->domain
);
2071 hinfo
.resrec
.rroriginalttl
= 0;
2072 mDNSPlatformMemCopy(h
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
2074 mDNSPlatformMemCopy(h
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
2075 hinfo
.resrec
.rdlength
= len
;
2076 hinfo
.resrec
.rdestimate
= len
;
2077 newptr
= PutResourceRecord(msg
, end
, &msg
->h
.numAdditionals
, &hinfo
.resrec
);
2084 // ***************************************************************************
2085 #if COMPILER_LIKES_PRAGMA_MARK
2087 #pragma mark - DNS Message Parsing Functions
2090 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
2095 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
2097 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
2098 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
2099 sum
= (sum
<<3) | (sum
>>29);
2101 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
2105 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
2110 rr
->rdata
= NewRData
;
2111 rr
->rdlength
= rdlength
;
2113 // Must not try to get target pointer until after updating rr->rdata
2114 target
= GetRRDomainNameTarget(rr
);
2115 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
2116 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
2117 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
);
2120 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
2124 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2125 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2127 while (1) // Read sequence of labels
2129 const mDNSu8 len
= *ptr
++; // Read length of this label
2130 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
2133 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2134 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2135 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
2136 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
2141 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
2142 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
2143 case 0xC0: return(ptr
+1);
2148 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2149 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
2150 domainname
*const name
)
2152 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
2153 mDNSu8
*np
= name
->c
; // Name pointer
2154 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
2156 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2157 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2159 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2161 while (1) // Read sequence of labels
2163 const mDNSu8 len
= *ptr
++; // Read length of this label
2164 if (len
== 0) break; // If length is zero, that means this name is complete
2170 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2171 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2172 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
2173 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
2175 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
2176 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2179 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
2182 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
2184 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
2185 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
2186 ptr
= (mDNSu8
*)msg
+ offset
;
2187 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2188 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
2190 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
2195 if (nextbyte
) return(nextbyte
);
2199 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2201 mDNSu16 pktrdlength
;
2203 ptr
= skipDomainName(msg
, ptr
, end
);
2204 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2206 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2207 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2209 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2211 return(ptr
+ pktrdlength
);
2214 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
,
2215 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*largecr
)
2217 CacheRecord
*rr
= &largecr
->r
;
2218 mDNSu16 pktrdlength
;
2220 if (largecr
== &m
->rec
&& largecr
->r
.resrec
.RecordType
)
2221 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &largecr
->r
));
2223 rr
->next
= mDNSNULL
;
2224 rr
->resrec
.name
= &largecr
->namestorage
;
2226 rr
->NextInKAList
= mDNSNULL
;
2227 rr
->TimeRcvd
= m
? m
->timenow
: 0;
2228 rr
->DelayDelivery
= 0;
2229 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2230 rr
->LastUsed
= m
? m
->timenow
: 0;
2231 rr
->CRActiveQuestion
= mDNSNULL
;
2232 rr
->UnansweredQueries
= 0;
2233 rr
->LastUnansweredTime
= 0;
2234 rr
->MPUnansweredQ
= 0;
2235 rr
->MPLastUnansweredQT
= 0;
2236 rr
->MPUnansweredKA
= 0;
2237 rr
->MPExpectingKA
= mDNSfalse
;
2238 rr
->NextInCFList
= mDNSNULL
;
2240 rr
->resrec
.InterfaceID
= InterfaceID
;
2241 ptr
= getDomainName(msg
, ptr
, end
, &largecr
->namestorage
);
2242 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2244 if (ptr
+ 10 > end
) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2246 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2247 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
2248 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
2249 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
2250 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2251 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2252 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2253 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2255 // If mDNS record has cache-flush bit set, we mark it unique
2256 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2257 // authoritative for the entire RRSet), unless this is a truncated response
2258 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8) || (!InterfaceID
&& !(msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)))
2259 RecordType
|= kDNSRecordTypePacketUniqueMask
;
2261 if (ptr
+ pktrdlength
> end
) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2262 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
2264 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
2265 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
2267 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
2269 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2270 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2271 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2272 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2273 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2274 if (rr
->resrec
.rrclass
== kDNSQClass_ANY
&& pktrdlength
== 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2275 rr
->resrec
.rdlength
= 0;
2276 else switch (rr
->resrec
.rrtype
)
2278 case kDNSType_A
: if (pktrdlength
!= sizeof(mDNSv4Addr
)) return(mDNSNULL
);
2279 rr
->resrec
.rdata
->u
.ipv4
.b
[0] = ptr
[0];
2280 rr
->resrec
.rdata
->u
.ipv4
.b
[1] = ptr
[1];
2281 rr
->resrec
.rdata
->u
.ipv4
.b
[2] = ptr
[2];
2282 rr
->resrec
.rdata
->u
.ipv4
.b
[3] = ptr
[3];
2286 case kDNSType_CNAME
:
2288 case kDNSType_DNAME
:ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
);
2289 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
2290 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
2293 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
);
2294 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
2295 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
);
2296 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
2297 if (ptr
+ 0x14 != end
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
2298 rr
->resrec
.rdata
->u
.soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
2299 rr
->resrec
.rdata
->u
.soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
2300 rr
->resrec
.rdata
->u
.soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
2301 rr
->resrec
.rdata
->u
.soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
2302 rr
->resrec
.rdata
->u
.soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
2306 case kDNSType_HINFO
:
2312 case kDNSType_DHCID
:if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2314 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2315 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2318 rr
->resrec
.rdlength
= pktrdlength
;
2319 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, ptr
, pktrdlength
);
2323 case kDNSType_AFSDB
:
2325 case kDNSType_KX
: if (pktrdlength
< 3) return(mDNSNULL
); // Preference + domainname
2326 rr
->resrec
.rdata
->u
.mx
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2327 ptr
= getDomainName(msg
, ptr
+2, end
, &rr
->resrec
.rdata
->u
.mx
.exchange
);
2328 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL
); }
2329 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2332 case kDNSType_RP
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.rp
.mbox
); // Domainname + domainname
2333 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL
; }
2334 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.rp
.txt
);
2335 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL
; }
2338 case kDNSType_PX
: if (pktrdlength
< 4) return(mDNSNULL
); // Preference + domainname + domainname
2339 rr
->resrec
.rdata
->u
.px
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2340 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.px
.map822
);
2341 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL
; }
2342 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.px
.mapx400
);
2343 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL
; }
2346 case kDNSType_AAAA
: if (pktrdlength
!= sizeof(mDNSv6Addr
)) return(mDNSNULL
);
2347 mDNSPlatformMemCopy(&rr
->resrec
.rdata
->u
.ipv6
, ptr
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
2350 case kDNSType_SRV
: if (pktrdlength
< 7) return(mDNSNULL
); // Priority + weight + port + domainname
2351 rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2352 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
2353 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
2354 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
2355 ptr
= getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
);
2356 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
2357 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2360 case kDNSType_OPT
: ptr
= getOptRdata(ptr
, end
, largecr
, pktrdlength
); break;
2361 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL
); }
2363 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2365 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2366 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2369 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2370 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
2371 // Note: Just because we don't understand the record type, that doesn't
2372 // mean we fail. The DNS protocol specifies rdlength, so we can
2373 // safely skip over unknown records and ignore them.
2374 // We also grab a binary copy of the rdata anyway, since the caller
2375 // might know how to interpret it even if we don't.
2376 rr
->resrec
.rdlength
= pktrdlength
;
2377 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, ptr
, pktrdlength
);
2381 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2382 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rdlength, rdestimate, rdatahash for us
2384 // Success! Now fill in RecordType to show this record contains valid data
2385 rr
->resrec
.RecordType
= RecordType
;
2389 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2391 ptr
= skipDomainName(msg
, ptr
, end
);
2392 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
2393 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2397 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
2398 DNSQuestion
*question
)
2400 mDNSPlatformMemZero(question
, sizeof(*question
));
2401 question
->InterfaceID
= InterfaceID
;
2402 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
2403 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
2404 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2406 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
2407 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
2408 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
2412 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2415 const mDNSu8
*ptr
= msg
->data
;
2416 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
2420 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2423 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
2424 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2428 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2431 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
2432 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2436 mDNSexport
const mDNSu8
*LocateLLQOptData(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2439 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2441 // Locate the OPT record.
2442 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2443 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2444 // but not necessarily the *last* entry in the Additional Section.
2445 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2447 if (ptr
+ 10 + LLQ_OPT_RDLEN
<= end
&& // Make sure we have 10+22 bytes of data
2448 ptr
[0] == 0 && // Name must be root label
2449 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2450 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2451 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)LLQ_OPT_RDLEN
)
2454 ptr
= skipResourceRecord(msg
, ptr
, end
);
2459 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2460 // it is callers responsibilty to clear m->rec.r.resrec.RecordType after use
2461 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2462 // The code that currently calls this assumes there's only one, instead of iterating through the set
2463 mDNSexport
const rdataOPT
*GetLLQOptData(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2465 const mDNSu8
*ptr
= LocateLLQOptData(msg
, end
);
2468 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2469 if (ptr
) return(&m
->rec
.r
.resrec
.rdata
->u
.opt
);
2474 mDNSexport
const mDNSu8
*LocateLeaseOptData(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2477 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2479 // Locate the OPT record.
2480 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2481 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2482 // but not necessarily the *last* entry in the Additional Section.
2483 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2485 if (ptr
+ 10 + LEASE_OPT_RDLEN
<= end
&& // Make sure we have 10+8 bytes of data
2486 ptr
[0] == 0 && // Name must be root label
2487 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2488 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2489 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)LEASE_OPT_RDLEN
)
2492 ptr
= skipResourceRecord(msg
, ptr
, end
);
2497 // Get the lease life of records in a dynamic update
2498 // returns 0 on error or if no lease present
2499 mDNSexport mDNSu32
GetPktLease(mDNS
*m
, DNSMessage
*msg
, const mDNSu8
*end
)
2502 const mDNSu8
*ptr
= LocateLeaseOptData(msg
, end
);
2503 if (ptr
) ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2504 if (ptr
&& m
->rec
.r
.resrec
.rdlength
>= LEASE_OPT_RDLEN
&& m
->rec
.r
.resrec
.rdata
->u
.opt
.opt
== kDNSOpt_Lease
)
2505 result
= m
->rec
.r
.resrec
.rdata
->u
.opt
.OptData
.updatelease
;
2506 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
2510 mDNSlocal
const mDNSu8
*DumpRecords(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
, int count
, char *label
)
2513 LogMsg("%2d %s", count
, label
);
2514 for (i
= 0; i
< count
&& ptr
; i
++)
2516 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2517 // but since it's only used for debugging (and probably only on OS X, not on
2518 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2519 LargeCacheRecord largecr
;
2520 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, mDNSInterface_Any
, kDNSRecordTypePacketAns
, &largecr
);
2521 if (ptr
) LogMsg("%2d TTL%7d %s", i
, largecr
.r
.resrec
.rroriginalttl
, CRDisplayString(m
, &largecr
.r
));
2523 if (!ptr
) LogMsg("ERROR: Premature end of packet data");
2527 #define DNS_OP_Name(X) ( \
2528 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2529 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2530 (X) == kDNSFlag0_OP_Status ? "Status " : \
2531 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2532 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2533 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2535 #define DNS_RC_Name(X) ( \
2536 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2537 (X) == kDNSFlag1_RC_FmtErr ? "FmtErr" : \
2538 (X) == kDNSFlag1_RC_SrvErr ? "SrvErr" : \
2539 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2540 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2541 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2542 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2543 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2544 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2545 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2546 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2548 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2549 mDNSexport
void DumpPacket(mDNS
*const m
, mDNSBool sent
, char *transport
,
2550 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
,
2551 const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2553 mDNSBool IsUpdate
= ((msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
) == kDNSFlag0_OP_Update
);
2554 const mDNSu8
*ptr
= msg
->data
;
2557 char sbuffer
[64], dbuffer
[64] = "";
2558 if (sent
) sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "port " )] = 0;
2559 else sbuffer
[mDNS_snprintf(sbuffer
, sizeof(sbuffer
), "%#a:", srcaddr
)] = 0;
2560 if (dstaddr
|| !mDNSIPPortIsZero(dstport
))
2561 dbuffer
[mDNS_snprintf(dbuffer
, sizeof(dbuffer
), " to %#a:%d", dstaddr
, mDNSVal16(dstport
))] = 0;
2563 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 --",
2564 sent
? "Sent" : "Received", transport
,
2565 DNS_OP_Name(msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
),
2566 msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
? "Response" : "Query",
2567 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1],
2568 DNS_RC_Name(msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
),
2569 msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
,
2570 msg
->h
.flags
.b
[0] & kDNSFlag0_AA
? "AA " : "",
2571 msg
->h
.flags
.b
[0] & kDNSFlag0_TC
? "TC " : "",
2572 msg
->h
.flags
.b
[0] & kDNSFlag0_RD
? "RD " : "",
2573 msg
->h
.flags
.b
[1] & kDNSFlag1_RA
? "RA " : "",
2574 msg
->h
.flags
.b
[1] & kDNSFlag1_AD
? "AD " : "",
2575 msg
->h
.flags
.b
[1] & kDNSFlag1_CD
? "CD " : "",
2576 mDNSVal16(msg
->h
.id
),
2578 sbuffer
, mDNSVal16(srcport
), dbuffer
,
2579 (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
) ? " (truncated)" : ""
2582 LogMsg("%2d %s", msg
->h
.numQuestions
, IsUpdate
? "Zone" : "Questions");
2583 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++)
2585 ptr
= getQuestion(msg
, ptr
, end
, mDNSInterface_Any
, &q
);
2586 if (ptr
) LogMsg("%2d %##s %s", i
, q
.qname
.c
, DNSTypeName(q
.qtype
));
2588 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAnswers
, IsUpdate
? "Prerequisites" : "Answers");
2589 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAuthorities
, IsUpdate
? "Updates" : "Authorities");
2590 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAdditionals
, "Additionals");
2591 LogMsg("--------------");
2594 // ***************************************************************************
2595 #if COMPILER_LIKES_PRAGMA_MARK
2597 #pragma mark - Packet Sending Functions
2600 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2601 struct TCPSocket_struct
{ TCPSocketFlags flags
; /* ... */ };
2603 struct UDPSocket_struct
2605 mDNSIPPort port
; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
2608 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2609 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2610 mDNSexport mStatus
mDNSSendDNSMessage(mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2611 mDNSInterfaceID InterfaceID
, UDPSocket
*src
, const mDNSAddr
*dst
, mDNSIPPort dstport
, TCPSocket
*sock
, DomainAuthInfo
*authInfo
)
2613 mStatus status
= mStatus_NoError
;
2614 const mDNSu16 numQuestions
= msg
->h
.numQuestions
;
2615 const mDNSu16 numAnswers
= msg
->h
.numAnswers
;
2616 const mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
2617 const mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2618 mDNSu16 tmpNumAdditionals
= numAdditionals
;
2619 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
2621 if (end
<= msg
->data
|| end
- msg
->data
> AbsoluteMaxDNSMessageData
)
2623 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg
->data
, end
, end
- msg
->data
);
2624 return mStatus_BadParamErr
;
2627 end
= putHINFO(m
, msg
, end
, authInfo
);
2628 if (!end
) { LogMsg("mDNSSendDNSMessage: putHINFO failed"); status
= mStatus_NoMemoryErr
; }
2631 tmpNumAdditionals
= msg
->h
.numAdditionals
;
2633 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2634 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
2635 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
2636 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
2637 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
2638 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
2639 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
2640 *ptr
++ = (mDNSu8
)(tmpNumAdditionals
>> 8);
2641 *ptr
++ = (mDNSu8
)(tmpNumAdditionals
& 0xFF);
2643 if (authInfo
) DNSDigest_SignMessage(msg
, &end
, authInfo
, 0); // DNSDigest_SignMessage operates on message in network byte order
2644 if (!end
) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status
= mStatus_NoMemoryErr
; }
2647 // Send the packet on the wire
2649 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, src
, dst
, dstport
);
2652 mDNSu16 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2653 mDNSu8 lenbuf
[2] = { (mDNSu8
)(msglen
>> 8), (mDNSu8
)(msglen
& 0xFF) };
2654 long nsent
= mDNSPlatformWriteTCP(sock
, (char*)lenbuf
, 2); // Should do scatter/gather here -- this is probably going out as two packets
2655 if (nsent
!= 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent
, 2); status
= mStatus_ConnFailed
; }
2658 nsent
= mDNSPlatformWriteTCP(sock
, (char *)msg
, msglen
);
2659 if (nsent
!= msglen
) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent
, msglen
); status
= mStatus_ConnFailed
; }
2665 // Put all the integer values back the way they were before we return
2666 msg
->h
.numQuestions
= numQuestions
;
2667 msg
->h
.numAnswers
= numAnswers
;
2668 msg
->h
.numAuthorities
= numAuthorities
;
2670 // Dump the packet with the HINFO and TSIG
2671 if (mDNS_LogLevel
>= MDNS_LOG_VERBOSE_DEBUG
&& !mDNSOpaque16IsZero(msg
->h
.id
))
2673 ptr
= (mDNSu8
*)&msg
->h
.numAdditionals
;
2674 msg
->h
.numAdditionals
= (mDNSu16
)ptr
[0] << 8 | (mDNSu16
)ptr
[1];
2675 DumpPacket(m
, mDNStrue
, sock
&& (sock
->flags
& kTCPSocketFlags_UseTLS
) ? "TLS" : sock
? "TCP" : "UDP", mDNSNULL
, src
? src
->port
: MulticastDNSPort
, dst
, dstport
, msg
, end
);
2678 // put the final integer value back the way it was
2679 msg
->h
.numAdditionals
= numAdditionals
;
2684 // ***************************************************************************
2685 #if COMPILER_LIKES_PRAGMA_MARK
2687 #pragma mark - RR List Management & Task Management
2690 mDNSexport
void mDNS_Lock_(mDNS
*const m
)
2692 // MUST grab the platform lock FIRST!
2693 mDNSPlatformLock(m
);
2695 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2696 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2697 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2698 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2700 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2702 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2707 // If this is an initial entry into the mDNSCore code, set m->timenow
2708 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2709 if (m
->mDNS_busy
== 0)
2712 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
2713 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2714 if (m
->timenow
== 0) m
->timenow
= 1;
2716 else if (m
->timenow
== 0)
2718 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
2719 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2720 if (m
->timenow
== 0) m
->timenow
= 1;
2723 if (m
->timenow_last
- m
->timenow
> 0)
2725 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
2726 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
2727 m
->timenow
= m
->timenow_last
;
2729 m
->timenow_last
= m
->timenow
;
2731 // Increment mDNS_busy so we'll recognise re-entrant calls
2735 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
2737 mDNSs32 e
= m
->timenow
+ 0x78000000;
2738 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) return(e
);
2739 if (m
->NewQuestions
)
2741 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
2742 else return(m
->timenow
);
2744 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
2745 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
2746 #ifndef UNICAST_DISABLED
2747 if (e
- m
->NextuDNSEvent
> 0) e
= m
->NextuDNSEvent
;
2748 if (e
- m
->NextScheduledNATOp
> 0) e
= m
->NextScheduledNATOp
;
2750 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
2754 if (m
->SuppressSending
)
2756 if (e
- m
->SuppressSending
> 0) e
= m
->SuppressSending
;
2760 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
2761 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
2762 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
2768 mDNSexport
void mDNS_Unlock_(mDNS
*const m
)
2770 // Decrement mDNS_busy
2773 // Check for locking failures
2775 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2777 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2782 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2783 if (m
->mDNS_busy
== 0)
2785 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
2786 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2790 // MUST release the platform lock LAST!
2791 mDNSPlatformUnlock(m
);
2794 // ***************************************************************************
2795 #if COMPILER_LIKES_PRAGMA_MARK
2797 #pragma mark - Specialized mDNS version of vsnprintf
2800 static const struct mDNSprintf_format
2802 unsigned leftJustify
: 1;
2803 unsigned forceSign
: 1;
2804 unsigned zeroPad
: 1;
2805 unsigned havePrecision
: 1;
2809 char sign
; // +, - or space
2810 unsigned int fieldWidth
;
2811 unsigned int precision
;
2812 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2814 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
2816 mDNSu32 nwritten
= 0;
2818 if (buflen
== 0) return(0);
2819 buflen
--; // Pre-reserve one space in the buffer for the terminating null
2820 if (buflen
== 0) goto exit
;
2822 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
2826 *sbuffer
++ = (char)c
;
2827 if (++nwritten
>= buflen
) goto exit
;
2831 unsigned int i
=0, j
;
2832 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
2833 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
2834 // The size needs to be enough for a 256-byte domain name plus some error text.
2835 #define mDNS_VACB_Size 300
2836 char mDNS_VACB
[mDNS_VACB_Size
];
2837 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
2838 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
2839 char *s
= mDNS_VACB_Lim
, *digits
;
2840 struct mDNSprintf_format F
= mDNSprintf_format_default
;
2842 while (1) // decode flags
2845 if (c
== '-') F
.leftJustify
= 1;
2846 else if (c
== '+') F
.forceSign
= 1;
2847 else if (c
== ' ') F
.sign
= ' ';
2848 else if (c
== '#') F
.altForm
++;
2849 else if (c
== '0') F
.zeroPad
= 1;
2853 if (c
== '*') // decode field width
2855 int f
= va_arg(arg
, int);
2856 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
2857 F
.fieldWidth
= (unsigned int)f
;
2862 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
2863 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
2866 if (c
== '.') // decode precision
2868 if ((c
= *++fmt
) == '*')
2869 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
2870 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
2871 F
.precision
= (10 * F
.precision
) + (c
- '0');
2872 F
.havePrecision
= 1;
2875 if (F
.leftJustify
) F
.zeroPad
= 0;
2878 switch (c
) // perform appropriate conversion
2881 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
2882 case 'l' : // fall through
2883 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
2885 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
2886 else n
= (unsigned long)va_arg(arg
, int);
2887 if (F
.hSize
) n
= (short) n
;
2888 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
2889 else if (F
.forceSign
) F
.sign
= '+';
2891 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2892 else n
= va_arg(arg
, unsigned int);
2893 if (F
.hSize
) n
= (unsigned short) n
;
2896 decimal
: if (!F
.havePrecision
)
2900 F
.precision
= F
.fieldWidth
;
2901 if (F
.sign
) --F
.precision
;
2903 if (F
.precision
< 1) F
.precision
= 1;
2905 if (F
.precision
> mDNS_VACB_Size
- 1)
2906 F
.precision
= mDNS_VACB_Size
- 1;
2907 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
2908 for (; i
< F
.precision
; i
++) *--s
= '0';
2909 if (F
.sign
) { *--s
= F
.sign
; i
++; }
2912 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2913 else n
= va_arg(arg
, unsigned int);
2914 if (F
.hSize
) n
= (unsigned short) n
;
2915 if (!F
.havePrecision
)
2917 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
2918 if (F
.precision
< 1) F
.precision
= 1;
2920 if (F
.precision
> mDNS_VACB_Size
- 1)
2921 F
.precision
= mDNS_VACB_Size
- 1;
2922 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
2923 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
2924 for (; i
< F
.precision
; i
++) *--s
= '0';
2928 unsigned char *a
= va_arg(arg
, unsigned char *);
2929 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2932 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2935 mDNSAddr
*ip
= (mDNSAddr
*)a
;
2938 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
2939 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
2940 default: F
.precision
= 0; break;
2943 if (F
.altForm
&& !F
.precision
)
2944 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "«ZERO ADDRESS»");
2945 else switch (F
.precision
)
2947 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
2948 a
[0], a
[1], a
[2], a
[3]); break;
2949 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
2950 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
2951 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
2952 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
2953 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
2954 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
2955 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify"
2956 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
2962 case 'p' : F
.havePrecision
= F
.lSize
= 1;
2964 case 'X' : digits
= "0123456789ABCDEF";
2966 case 'x' : digits
= "0123456789abcdef";
2967 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2968 else n
= va_arg(arg
, unsigned int);
2969 if (F
.hSize
) n
= (unsigned short) n
;
2970 if (!F
.havePrecision
)
2974 F
.precision
= F
.fieldWidth
;
2975 if (F
.altForm
) F
.precision
-= 2;
2977 if (F
.precision
< 1) F
.precision
= 1;
2979 if (F
.precision
> mDNS_VACB_Size
- 1)
2980 F
.precision
= mDNS_VACB_Size
- 1;
2981 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
2982 for (; i
< F
.precision
; i
++) *--s
= '0';
2983 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
2986 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
2988 case 's' : s
= va_arg(arg
, char *);
2989 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2990 else switch (F
.altForm
)
2993 if (!F
.havePrecision
) // C string
2997 while ((i
< F
.precision
) && s
[i
]) i
++;
2998 // Make sure we don't truncate in the middle of a UTF-8 character
2999 // If last character we got was any kind of UTF-8 multi-byte character,
3000 // then see if we have to back up.
3001 // This is not as easy as the similar checks below, because
3002 // here we can't assume it's safe to examine the *next* byte, so we
3003 // have to confine ourselves to working only backwards in the string.
3004 j
= i
; // Record where we got to
3005 // Now, back up until we find first non-continuation-char
3006 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
3007 // Now s[i-1] is the first non-continuation-char
3008 // and (j-i) is the number of continuation-chars we found
3009 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
3011 i
--; // Tentatively eliminate this start-char as well
3012 // Now (j-i) is the number of characters we're considering eliminating.
3013 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
3014 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
3015 // (with sign extension) then the result has to be 0xFE.
3016 // If this is right, then we reinstate the tentatively eliminated bytes.
3017 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
3021 case 1: i
= (unsigned char) *s
++; break; // Pascal string
3022 case 2: { // DNS label-sequence name
3023 unsigned char *a
= (unsigned char *)s
;
3024 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
3025 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
3030 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
3031 if (s
+ *a
>= &mDNS_VACB
[254])
3032 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
3033 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3034 // so it's clear what's a literal dot and what's a label separator
3035 ConvertDomainLabelToCString((domainlabel
*)a
, buf
);
3036 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%s.", buf
);
3039 i
= (mDNSu32
)(s
- mDNS_VACB
);
3040 s
= mDNS_VACB
; // Reset s back to the start of the buffer
3044 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3045 if (F
.havePrecision
&& i
> F
.precision
)
3046 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3049 case 'n' : s
= va_arg(arg
, char *);
3050 if (F
.hSize
) * (short *) s
= (short)nwritten
;
3051 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
3052 else * (int *) s
= (int)nwritten
;
3055 default: s
= mDNS_VACB
;
3056 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
3058 case '%' : *sbuffer
++ = (char)c
;
3059 if (++nwritten
>= buflen
) goto exit
;
3063 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
3066 if (++nwritten
>= buflen
) goto exit
;
3067 } while (i
< --F
.fieldWidth
);
3069 // Make sure we don't truncate in the middle of a UTF-8 character.
3070 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3071 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3072 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3073 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3074 if (i
> buflen
- nwritten
)
3075 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3076 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
3078 if (nwritten
>= buflen
) goto exit
;
3080 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
3083 if (++nwritten
>= buflen
) goto exit
;
3092 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
3098 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);