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 2008/03/14 19:58:38 mcguire
21 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
22 Make sure we add the record when sending LLQ refreshes
24 Revision 1.198 2008/03/07 23:29:24 cheshire
25 Fixed cosmetic byte order display issue in DumpPacket output
27 Revision 1.197 2008/03/05 22:51:29 mcguire
28 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
29 Even further refinements
31 Revision 1.196 2008/03/05 22:01:53 cheshire
32 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
33 Now that we optionally add the HINFO record, when rewriting the header fields into network byte
34 order, we need to use our updated msg->h.numAdditionals, not the stack variable numAdditionals
36 Revision 1.195 2008/03/05 19:06:30 mcguire
37 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
40 Revision 1.194 2008/03/05 00:26:06 cheshire
41 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
43 Revision 1.193 2007/12/17 23:42:36 cheshire
44 Added comments about DNSDigest_SignMessage()
46 Revision 1.192 2007/12/17 21:24:09 cheshire
47 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
48 We suspend sending of mDNS queries responses when going to sleep, so calculate GetNextScheduledEvent() time accordingly
50 Revision 1.191 2007/12/14 00:59:36 cheshire
51 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
52 While going to sleep, don't block event scheduling
54 Revision 1.190 2007/12/13 20:20:17 cheshire
55 Minor efficiency tweaks -- converted IdenticalResourceRecord, IdenticalSameNameRecord, and
56 SameRData from functions to macros, which allows the code to be inlined (the compiler can't
57 inline a function defined in a different compilation unit) and therefore optimized better.
59 Revision 1.189 2007/12/13 00:17:32 cheshire
60 RDataHashValue was not calculating hash value reliably for RDATA types that have 'holes' in the
61 in-memory representation (particularly SOA was affected by this, resulting in multiple duplicate
62 cache entities for the same SOA record, because they had erroneously different rdatahash values).
64 Revision 1.188 2007/12/13 00:13:03 cheshire
65 Simplified RDataHashValue to take a single ResourceRecord pointer, instead of separate rdlength and RDataBody
67 Revision 1.187 2007/12/08 00:35:20 cheshire
68 <rdar://problem/5636422> Updating TXT records is too slow
69 m->SuppressSending should not suppress all activity, just mDNS Query/Probe/Response
71 Revision 1.186 2007/11/15 22:52:29 cheshire
72 <rdar://problem/5589039> ERROR: mDNSPlatformWriteTCP - send Broken pipe
74 Revision 1.185 2007/10/10 20:22:03 cheshire
75 Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
76 apparently caused by trying to sign zero-length messages
78 Revision 1.184 2007/10/05 17:56:07 cheshire
79 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
81 Revision 1.183 2007/10/02 18:33:46 cheshire
82 Improved GetRRDisplayString to show all constituent strings within a text record
83 (up to the usual MaxMsg 120-character limit)
85 Revision 1.182 2007/10/01 19:45:01 cheshire
86 <rdar://problem/5514859> BTMM: Sometimes Back to My Mac autotunnel registrations are malformed
88 Revision 1.181 2007/10/01 18:36:53 cheshire
89 Yet another fix to finally get the DumpPacket RCODE display right
91 Revision 1.180 2007/09/29 21:30:38 cheshire
92 In DumpPacket/DumpRecords, show an error line if we run out of packet data
94 Revision 1.179 2007/09/29 20:44:56 cheshire
95 Fix error in DumpPacket where it was not displaying the RCODE field properly
97 Revision 1.178 2007/09/27 21:11:44 cheshire
98 Fixed spelling mistake: ROCDE -> RCODE
100 Revision 1.177 2007/09/27 18:51:26 cheshire
101 Improved DumpPacket to use "Zone/Prerequisites/Updates" nomenclature when displaying a DNS Update packet
103 Revision 1.176 2007/09/27 17:53:37 cheshire
104 Add display of RCODE and flags in DumpPacket output
106 Revision 1.175 2007/09/26 22:26:40 cheshire
107 Also show DNS query/response ID in DumpPacket output
109 Revision 1.174 2007/09/26 16:36:02 cheshire
110 In DumpPacket output, begin header line with "-- " to make it visually stand out better
112 Revision 1.173 2007/09/26 00:49:46 cheshire
113 Improve packet logging to show sent and received packets,
114 transport protocol (UDP/TCP/TLS) and source/destination address:port
116 Revision 1.172 2007/09/21 23:14:39 cheshire
117 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents in verbose debug mode
118 Changed DumpRecords to use LargeCacheRecord on the stack instead of the shared m->rec storage,
119 to eliminate "GetLargeResourceRecord: m->rec appears to be already in use" warnings
121 Revision 1.171 2007/09/21 21:12:36 cheshire
122 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
124 Revision 1.170 2007/09/07 21:16:58 cheshire
125 Add new symbol "NATPMPAnnouncementPort" (5350)
127 Revision 1.169 2007/08/30 00:31:20 cheshire
128 Improve "locking failure" debugging messages to show function name using __func__ macro
130 Revision 1.168 2007/08/28 23:58:42 cheshire
131 Rename HostTarget -> AutoTarget
133 Revision 1.167 2007/08/10 23:10:05 vazquez
134 <rdar://problem/5389850> mDNS: Reverse lookups of IPv6 link-local addresses always fail
136 Revision 1.166 2007/08/01 16:09:13 cheshire
137 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
139 Revision 1.165 2007/08/01 00:04:13 cheshire
140 <rdar://problem/5261696> Crash in tcpKQSocketCallback
141 Half-open TCP connections were not being cancelled properly
143 Revision 1.164 2007/07/27 20:48:43 cheshire
144 In DumpRecords(), include record TTL in output
146 Revision 1.163 2007/07/16 20:10:11 vazquez
147 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
148 Added SSDP port number
150 Revision 1.162 2007/07/10 01:59:33 cheshire
151 <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
152 Fixed GetPktLease to use shared m->rec instead of putting LargeCacheRecord on the stack
154 Revision 1.161 2007/07/06 18:56:26 cheshire
155 Check m->NextScheduledNATOp in GetNextScheduledEvent()
157 Revision 1.160 2007/06/29 00:06:42 vazquez
158 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
160 Revision 1.159 2007/06/28 21:17:17 cheshire
161 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
163 Revision 1.158 2007/05/25 00:25:43 cheshire
164 <rdar://problem/5227737> Need to enhance putRData to output all current known types
166 Revision 1.157 2007/05/23 00:32:15 cheshire
167 Don't treat uDNS responses as an entire RRSet (kDNSRecordTypePacketUniqueMask)
168 when received in a truncated UDP response
170 Revision 1.156 2007/05/15 00:29:00 cheshire
171 Print «ZERO ADDRESS» for %#a with a zero mDNSAddr
173 Revision 1.155 2007/05/07 22:07:47 cheshire
174 <rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types
176 Revision 1.154 2007/05/04 20:19:53 cheshire
177 Improve DumpPacket() output
179 Revision 1.153 2007/05/01 21:46:31 cheshire
180 Move GetLLQOptData/GetPktLease from uDNS.c into DNSCommon.c so that dnsextd can use them
182 Revision 1.152 2007/04/27 19:28:01 cheshire
183 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
184 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
185 -- it would start a query and then quickly cancel it, and then when
186 StartGetZoneData completed, it had a dangling pointer and crashed.)
188 Revision 1.151 2007/04/26 13:35:25 cheshire
189 Add kDNSType_SOA case in SameRDataBody, and a comment in GetLargeResourceRecord about why this is important
191 Revision 1.150 2007/04/24 00:17:33 cheshire
192 Made LocateLLQOptData guard against packets with bogus numAdditionals value
194 Revision 1.149 2007/04/23 21:43:00 cheshire
195 Remove debugging check
197 Revision 1.148 2007/04/23 04:55:29 cheshire
198 Add some defensive null pointer checks
200 Revision 1.147 2007/04/22 20:18:10 cheshire
201 Add comment about mDNSRandom()
203 Revision 1.146 2007/04/22 06:02:02 cheshire
204 <rdar://problem/4615977> Query should immediately return failure when no server
206 Revision 1.145 2007/04/20 21:17:24 cheshire
207 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
209 Revision 1.144 2007/04/19 18:02:43 cheshire
210 <rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit
212 Revision 1.143 2007/04/16 21:53:49 cheshire
213 Improve display of negative cache entries
215 Revision 1.142 2007/04/05 22:55:35 cheshire
216 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
218 Revision 1.141 2007/04/04 01:33:11 cheshire
219 <rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record
220 Overly defensive code was zeroing too much of the AuthRecord structure
222 Revision 1.140 2007/04/03 19:37:58 cheshire
223 Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918()
225 Revision 1.139 2007/04/03 19:18:39 cheshire
226 Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly
228 Revision 1.138 2007/03/28 21:14:08 cheshire
229 The rrclass field of an OPT pseudo-RR holds the sender's UDP payload size
231 Revision 1.137 2007/03/28 20:59:26 cheshire
232 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
234 Revision 1.136 2007/03/28 15:56:37 cheshire
235 <rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output
237 Revision 1.135 2007/03/28 01:20:05 cheshire
238 <rdar://problem/4883206> Improve/create logging for secure browse
240 Revision 1.134 2007/03/27 23:25:35 cheshire
241 Fix error caching SOA records
242 (cache entry was size of wire-format packed data, not size of in-memory structure)
244 Revision 1.133 2007/03/26 22:55:45 cheshire
245 Add OPT and TSIG to list of types DNSTypeName() knows about
247 Revision 1.132 2007/03/22 18:31:48 cheshire
248 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
250 Revision 1.131 2007/03/21 21:55:20 cheshire
251 <rdar://problem/5069688> Hostname gets ; or : which are illegal characters
252 Error in AppendLabelSuffix() for numbers close to the 32-bit limit
254 Revision 1.130 2007/03/21 19:23:37 cheshire
255 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
256 Make check less strict so we don't break Bonjour Browser
258 Revision 1.129 2007/03/21 01:00:45 cheshire
259 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
260 DeconstructServiceName() needs to be more defensive about what it considers legal
262 Revision 1.128 2007/03/21 00:30:02 cheshire
263 <rdar://problem/4789455> Multiple errors in DNameList-related code
265 Revision 1.127 2007/03/20 17:07:15 cheshire
266 Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket"
268 Revision 1.126 2007/03/10 03:26:44 cheshire
269 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
271 Revision 1.125 2007/03/07 00:08:58 cheshire
272 <rdar://problem/4347550> Don't allow hyphens at start of service type
274 Revision 1.124 2007/01/19 18:04:05 cheshire
275 For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT
277 Revision 1.123 2007/01/10 22:45:51 cheshire
278 Cast static strings to "(const domainname*)", not "(domainname*)"
280 Revision 1.122 2007/01/06 00:47:35 cheshire
281 Improve GetRRDisplayString to indicate when record has zero-length rdata
283 Revision 1.121 2007/01/05 08:30:39 cheshire
284 Trim excessive "$Log" checkin history from before 2006
285 (checkin history still available via "cvs log ..." of course)
287 Revision 1.120 2007/01/05 05:23:00 cheshire
288 Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd)
290 Revision 1.119 2007/01/05 04:30:16 cheshire
291 Change a couple of "(domainname *)" casts to "(const domainname *)"
293 Revision 1.118 2007/01/04 20:21:59 cheshire
294 <rdar://problem/4720673> uDNS: Need to start caching unicast records
295 Don't return multicast answers in response to unicast questions
297 Revision 1.117 2006/12/22 20:59:49 cheshire
298 <rdar://problem/4742742> Read *all* DNS keys from keychain,
299 not just key for the system-wide default registration domain
301 Revision 1.116 2006/12/21 00:04:07 cheshire
302 To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord()
304 Revision 1.115 2006/12/20 04:07:34 cheshire
305 Remove uDNS_info substructure from AuthRecord_struct
307 Revision 1.114 2006/12/19 22:40:04 cheshire
308 Fix compiler warnings
310 Revision 1.113 2006/12/19 02:21:08 cheshire
311 Delete spurious spaces
313 Revision 1.112 2006/12/15 20:42:10 cheshire
314 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
315 Additional defensive coding in GetLargeResourceRecord() to reject apparently-valid
316 rdata that actually runs past the end of the received packet data.
318 Revision 1.111 2006/12/15 19:09:57 cheshire
319 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
320 Made DomainNameLength() more defensive by adding a limit parameter, so it can be
321 safely used to inspect potentially malformed data received from external sources.
322 Without this, a domain name that starts off apparently valid, but extends beyond the end of
323 the received packet data, could have appeared valid if the random bytes are already in memory
324 beyond the end of the packet just happened to have reasonable values (e.g. all zeroes).
326 Revision 1.110 2006/11/18 05:01:30 cheshire
327 Preliminary support for unifying the uDNS and mDNS code,
328 including caching of uDNS answers
330 Revision 1.109 2006/11/10 00:54:14 cheshire
331 <rdar://problem/4816598> Changing case of Computer Name doesn't work
333 Revision 1.108 2006/10/05 23:11:18 cheshire
334 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
336 Revision 1.107 2006/09/15 21:20:14 cheshire
337 Remove uDNS_info substructure from mDNS_struct
339 Revision 1.106 2006/08/14 23:24:22 cheshire
340 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
342 Revision 1.105 2006/07/15 02:01:28 cheshire
343 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
344 Fix broken "empty string" browsing
346 Revision 1.104 2006/07/05 23:09:13 cheshire
347 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
348 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
350 Revision 1.103 2006/06/29 07:42:14 cheshire
351 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
353 Revision 1.102 2006/06/22 19:49:11 cheshire
354 Added (commented out) definitions for the LLMNR UDP port and multicast addresses
356 Revision 1.101 2006/06/15 21:35:15 cheshire
357 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
358 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
360 Revision 1.100 2006/06/08 22:58:46 cheshire
361 <rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
363 Revision 1.99 2006/05/18 01:32:33 cheshire
364 <rdar://problem/4472706> iChat: Lost connection with Bonjour
365 (mDNSResponder insufficiently defensive against malformed browsing PTR responses)
367 Revision 1.98 2006/03/19 17:00:58 cheshire
368 Define symbol MaxMsg instead of using hard-coded constant value '80'
370 Revision 1.97 2006/03/18 21:47:56 cheshire
371 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
373 Revision 1.96 2006/03/10 21:51:42 cheshire
374 <rdar://problem/4111464> After record update, old record sometimes remains in cache
375 Split out SameRDataBody() into a separate routine so it can be called from other code
377 Revision 1.95 2006/03/08 22:43:11 cheshire
378 Use "localdomain" symbol instead of literal string
380 Revision 1.94 2006/03/02 21:59:55 cheshire
381 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
382 Improve sanity checks & debugging support in GetLargeResourceRecord()
384 Revision 1.93 2006/03/02 20:30:47 cheshire
385 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
389 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
390 #define mDNS_InstantiateInlines 1
391 #include "DNSCommon.h"
393 // Disable certain benign warnings with Microsoft compilers
394 #if (defined(_MSC_VER))
395 // Disable "conditional expression is constant" warning for debug macros.
396 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
397 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
398 #pragma warning(disable:4127)
399 // Disable "array is too small to include a terminating null character" warning
400 // -- domain labels have an initial length byte, not a terminating null character
401 #pragma warning(disable:4295)
404 // ***************************************************************************
405 #if COMPILER_LIKES_PRAGMA_MARK
406 #pragma mark - Program Constants
409 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
410 mDNSexport
const mDNSv4Addr zerov4Addr
= { { 0 } };
411 mDNSexport
const mDNSv6Addr zerov6Addr
= { { 0 } };
412 mDNSexport
const mDNSEthAddr zeroEthAddr
= { { 0 } };
413 mDNSexport
const mDNSv4Addr onesIPv4Addr
= { { 255, 255, 255, 255 } };
414 mDNSexport
const mDNSv6Addr onesIPv6Addr
= { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
415 mDNSexport
const mDNSAddr zeroAddr
= { mDNSAddrType_None
, {{{ 0 }}} };
417 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
418 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
419 mDNSexport
const mDNSInterfaceID mDNSInterface_Unicast
= (mDNSInterfaceID
)2;
421 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
422 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
423 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
424 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
425 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
426 // with Microsoft's LLMNR client code.
428 #define SSDPPortAsNumber 1900
430 #define UnicastDNSPortAsNumber 53
431 #define NATPMPAnnouncementPortAsNumber 5350
432 #define NATPMPPortAsNumber 5351
433 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
434 #define MulticastDNSPortAsNumber 5353
435 #define LoopbackIPCPortAsNumber 5354
436 //#define MulticastDNSPortAsNumber 5355 // LLMNR
438 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
439 #define PrivateDNSPortAsNumber 5533
441 mDNSexport
const mDNSIPPort SSDPPort
= { { SSDPPortAsNumber
>> 8, SSDPPortAsNumber
& 0xFF } };
443 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
444 mDNSexport
const mDNSIPPort NATPMPAnnouncementPort
= { { NATPMPAnnouncementPortAsNumber
>> 8, NATPMPAnnouncementPortAsNumber
& 0xFF } };
445 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
446 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
447 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
448 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
450 mDNSexport
const mDNSIPPort NSIPCPort
= { { NSIPCPortAsNumber
>> 8, NSIPCPortAsNumber
& 0xFF } };
451 mDNSexport
const mDNSIPPort PrivateDNSPort
= { { PrivateDNSPortAsNumber
>> 8, PrivateDNSPortAsNumber
& 0xFF } };
453 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
454 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
455 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
456 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
457 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
459 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
460 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
461 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
462 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
463 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
464 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
466 mDNSexport
const mDNSOpaque64 zeroOpaque64
= { { 0 } };
468 // ***************************************************************************
469 #if COMPILER_LIKES_PRAGMA_MARK
471 #pragma mark - General Utility Functions
474 // return true for RFC1918 private addresses
475 mDNSexport mDNSBool
mDNSv4AddrIsRFC1918(mDNSv4Addr
*addr
)
477 return ((addr
->b
[0] == 10) || // 10/8 prefix
478 (addr
->b
[0] == 172 && (addr
->b
[1] & 0xF0) == 16) || // 172.16/12
479 (addr
->b
[0] == 192 && addr
->b
[1] == 168)); // 192.168/16
482 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
484 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
488 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
490 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
491 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
494 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
496 mDNSu32 slot
, used
= 0;
499 FORALL_CACHERECORDS(slot
, cg
, rr
)
500 if (rr
->resrec
.InterfaceID
== id
) used
++;
504 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
508 case kDNSType_A
: return("Addr");
509 case kDNSType_NS
: return("NS");
510 case kDNSType_CNAME
:return("CNAME");
511 case kDNSType_SOA
: return("SOA");
512 case kDNSType_NULL
: return("NULL");
513 case kDNSType_PTR
: return("PTR");
514 case kDNSType_HINFO
:return("HINFO");
515 case kDNSType_TXT
: return("TXT");
516 case kDNSType_AAAA
: return("AAAA");
517 case kDNSType_SRV
: return("SRV");
518 case kDNSType_OPT
: return("OPT");
519 case kDNSType_TSIG
: return("TSIG");
520 case kDNSQType_ANY
: return("ANY");
522 static char buffer
[16];
523 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
529 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
530 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
531 // long as this routine is only used for debugging messages, it probably isn't a big problem.
532 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*rr
, RDataBody
*rd
, char *buffer
)
534 #define Max (MaxMsg-1)
536 mDNSu32 length
= mDNS_snprintf(buffer
, Max
, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
537 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) return(buffer
);
538 if (!rr
->rdlength
) { mDNS_snprintf(buffer
+length
, Max
-length
, "<< ZERO RDATA LENGTH >>"); return(buffer
); }
542 case kDNSType_A
: mDNS_snprintf(buffer
+length
, Max
-length
, "%.4a", &rd
->ipv4
); break;
544 case kDNSType_NS
: // Same as PTR
545 case kDNSType_CNAME
:// Same as PTR
546 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, Max
-length
, "%##s", rd
->name
.c
); break;
548 case kDNSType_SOA
: mDNS_snprintf(buffer
+length
, Max
-length
, "%##s %##s %d %d %d %d %d",
549 rd
->soa
.mname
.c
, rd
->soa
.rname
.c
,
550 rd
->soa
.serial
, rd
->soa
.refresh
, rd
->soa
.retry
, rd
->soa
.expire
, rd
->soa
.min
);
553 case kDNSType_HINFO
:// Display this the same as TXT (show all constituent strings)
555 mDNSu8
*t
= rd
->txt
.c
;
556 while (t
< rd
->txt
.c
+ rr
->rdlength
)
558 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "%s%#s", t
> rd
->txt
.c
? "¦" : "", t
);
563 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, Max
-length
, "%.16a", &rd
->ipv6
); break;
564 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, Max
-length
, "%u %u %u %##s",
565 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
566 case kDNSType_OPT
: length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "%d Len %d ", rd
->opt
.opt
, rd
->opt
.optlen
);
567 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Max UDP %d ", rr
->rrclass
);
568 if (rd
->opt
.opt
== kDNSOpt_LLQ
)
570 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Vers %d ", rd
->opt
.OptData
.llq
.vers
);
571 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Op %d ", rd
->opt
.OptData
.llq
.llqOp
);
572 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Err/Port %d ", rd
->opt
.OptData
.llq
.err
);
573 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "ID %08X%08X ", rd
->opt
.OptData
.llq
.id
.l
[0], rd
->opt
.OptData
.llq
.id
.l
[1]);
574 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Lease %d", rd
->opt
.OptData
.llq
.llqlease
);
576 else if (rd
->opt
.opt
== kDNSOpt_Lease
)
577 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "kDNSOpt_Lease Lease %d", rd
->opt
.OptData
.updatelease
);
579 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Unknown opt %d", rd
->opt
.opt
);
581 default: mDNS_snprintf(buffer
+length
, Max
-length
, "RDLen %d: %s", rr
->rdlength
, rd
->data
);
582 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
583 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
= '.';
589 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
) // Returns pseudo-random result from zero to max inclusive
591 static mDNSu32 seed
= 0;
597 seed
= mDNSPlatformRandomSeed(); // Pick an initial seed
598 for (i
=0; i
<100; i
++) seed
= seed
* 21 + 1; // And mix it up a bit
600 while (mask
< max
) mask
= (mask
<< 1) | 1;
601 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
602 return (seed
& mask
);
605 mDNSexport mDNSu32
mDNSRandomFromFixedSeed(mDNSu32 seed
, mDNSu32 max
)
608 while (mask
< max
) mask
= (mask
<< 1) | 1;
609 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
610 return (seed
& mask
);
613 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
615 if (ip1
->type
== ip2
->type
)
619 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
620 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
621 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
627 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
631 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip
->ip
.v4
, AllDNSLinkGroup_v4
.ip
.v4
));
632 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip
->ip
.v6
, AllDNSLinkGroup_v6
.ip
.v6
));
633 default: return(mDNSfalse
);
637 // ***************************************************************************
638 #if COMPILER_LIKES_PRAGMA_MARK
640 #pragma mark - Domain Name Utility Functions
643 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
646 const int len
= *a
++;
648 if (len
> MAX_DOMAIN_LABEL
)
649 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
651 if (len
!= *b
++) return(mDNSfalse
);
652 for (i
=0; i
<len
; i
++)
656 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
657 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
658 if (ac
!= bc
) return(mDNSfalse
);
663 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
665 const mDNSu8
* a
= d1
->c
;
666 const mDNSu8
* b
= d2
->c
;
667 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
671 if (a
+ 1 + *a
>= max
)
672 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
673 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
681 mDNSexport mDNSBool
SameDomainNameCS(const domainname
*const d1
, const domainname
*const d2
)
683 mDNSu16 l1
= DomainNameLength(d1
);
684 mDNSu16 l2
= DomainNameLength(d2
);
685 return(l1
<= MAX_DOMAIN_NAME
&& l1
== l2
&& mDNSPlatformMemSame(d1
, d2
, l1
));
688 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
690 // Domains that are defined to be resolved via link-local multicast are:
691 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
692 static const domainname
*nL
= (const domainname
*)"\x5" "local";
693 static const domainname
*nR
= (const domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
694 static const domainname
*n8
= (const domainname
*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
695 static const domainname
*n9
= (const domainname
*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
696 static const domainname
*nA
= (const domainname
*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
697 static const domainname
*nB
= (const domainname
*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
699 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
; // Top-level domain, second-level domain, etc.
700 d1
= d2
= d3
= d4
= d5
= mDNSNULL
;
703 d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
704 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
707 if (d1
&& SameDomainName(d1
, nL
)) return(mDNStrue
);
708 if (d4
&& SameDomainName(d4
, nR
)) return(mDNStrue
);
709 if (d5
&& SameDomainName(d5
, n8
)) return(mDNStrue
);
710 if (d5
&& SameDomainName(d5
, n9
)) return(mDNStrue
);
711 if (d5
&& SameDomainName(d5
, nA
)) return(mDNStrue
);
712 if (d5
&& SameDomainName(d5
, nB
)) return(mDNStrue
);
716 // Returns length of a domain name INCLUDING the byte for the final null label
717 // e.g. for the root label "." it returns one
718 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
719 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
720 // If the given domainname is invalid, result is 256 (MAX_DOMAIN_NAME+1)
721 mDNSexport mDNSu16
DomainNameLengthLimit(const domainname
*const name
, const mDNSu8
*limit
)
723 const mDNSu8
*src
= name
->c
;
724 while (src
< limit
&& *src
<= MAX_DOMAIN_LABEL
)
726 if (*src
== 0) return((mDNSu16
)(src
- name
->c
+ 1));
729 return(MAX_DOMAIN_NAME
+1);
732 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
733 // for the final null label, e.g. for the root label "." it returns one.
734 // E.g. for the FQDN "foo.com." it returns 9
735 // (length, three data bytes, length, three more data bytes, final zero).
736 // In the case where a parent domain name is provided, and the given name is a child
737 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
738 // of the child name, plus TWO bytes for the compression pointer.
739 // E.g. for the name "foo.com." with parent "com.", it returns 6
740 // (length, three data bytes, two-byte compression pointer).
741 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
743 const mDNSu8
*src
= name
->c
;
744 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
747 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
748 if (parent
&& SameDomainName((const domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
750 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
752 return((mDNSu16
)(src
- name
->c
+ 1));
755 // CountLabels() returns number of labels in name, excluding final root label
756 // (e.g. for "apple.com." CountLabels returns 2.)
757 mDNSexport
int CountLabels(const domainname
*d
)
761 for (ptr
= d
->c
; *ptr
; ptr
= ptr
+ ptr
[0] + 1) count
++;
765 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
766 // returning a pointer to the suffix with 'skip' labels removed.
767 mDNSexport
const domainname
*SkipLeadingLabels(const domainname
*d
, int skip
)
769 while (skip
> 0 && d
->c
[0]) { d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]); skip
--; }
773 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
774 // The C string contains the label as-is, with no escaping, etc.
775 // Any dots in the name are literal dots, not label separators
776 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
777 // in the domainname bufer (i.e. the next byte after the terminating zero).
778 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
779 // AppendLiteralLabelString returns mDNSNULL.
780 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
782 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
783 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
784 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
785 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
786 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
788 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
789 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
790 *ptr
++ = 0; // Put the null root label on the end
791 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
792 else return(ptr
); // Success: return new value of ptr
795 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
796 // The C string is in conventional DNS syntax:
797 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
798 // If successful, AppendDNSNameString returns a pointer to the next unused byte
799 // in the domainname bufer (i.e. the next byte after the terminating zero).
800 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
801 // AppendDNSNameString returns mDNSNULL.
802 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
804 const char *cstr
= cstring
;
805 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
806 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
807 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
809 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
810 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
811 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
813 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
814 if (c
== '\\') // If escape character, check next character
816 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
817 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
818 { // If three decimal digits,
819 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
820 int v1
= cstr
[ 0] - '0';
821 int v2
= cstr
[ 1] - '0';
822 int val
= v0
* 100 + v1
* 10 + v2
;
823 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
826 *ptr
++ = c
; // Write the character
828 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
829 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
831 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
834 *ptr
++ = 0; // Put the null root label on the end
835 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
836 else return(ptr
); // Success: return new value of ptr
839 // AppendDomainLabel appends a single label to a name.
840 // If successful, AppendDomainLabel returns a pointer to the next unused byte
841 // in the domainname bufer (i.e. the next byte after the terminating zero).
842 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
843 // AppendDomainLabel returns mDNSNULL.
844 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
847 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
849 // Check label is legal
850 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
852 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
853 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
855 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
856 *ptr
++ = 0; // Put the null root label on the end
860 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
862 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
863 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
864 const mDNSu8
* src
= append
->c
;
868 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
869 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
870 *ptr
= 0; // Put the null root label on the end
876 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
877 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
878 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
879 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
880 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
881 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
882 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
884 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
885 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
886 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
887 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
888 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
891 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
892 // The C string is in conventional DNS syntax:
893 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
894 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
895 // in the domainname bufer (i.e. the next byte after the terminating zero).
896 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
897 // MakeDomainNameFromDNSNameString returns mDNSNULL.
898 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
900 name
->c
[0] = 0; // Make an empty domain name
901 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
904 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
906 const mDNSu8
* src
= label
->c
; // Domain label we're reading
907 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
908 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
909 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
910 while (src
< end
) // While we have characters in the label
915 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
916 *ptr
++ = esc
; // Output escape character
917 else if (c
<= ' ') // If non-printing ascii,
918 { // Output decimal escape sequence
920 *ptr
++ = (char) ('0' + (c
/ 100) );
921 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
922 c
= (mDNSu8
)('0' + (c
) % 10);
925 *ptr
++ = (char)c
; // Copy the character
927 *ptr
= 0; // Null-terminate the string
928 return(ptr
); // and return
931 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
932 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
934 const mDNSu8
*src
= name
->c
; // Domain name we're reading
935 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
937 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
939 while (*src
) // While more characters in the domain name
941 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
942 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
943 if (!ptr
) return(mDNSNULL
);
945 *ptr
++ = '.'; // Write the dot after the label
948 *ptr
++ = 0; // Null-terminate the string
949 return(ptr
); // and return
953 // Host names must start with a letter, end with a letter or digit,
954 // and have as interior characters only letters, digits, and hyphen.
955 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
957 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
959 const mDNSu8
* src
= &UTF8Name
[1];
960 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
961 mDNSu8
* ptr
= &hostlabel
->c
[1];
962 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
965 // Delete apostrophes from source name
966 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
967 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
968 { src
+= 3; continue; } // Unicode curly apostrophe
971 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
972 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
976 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
977 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
980 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
981 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
982 ((X)[4] | 0x20) == 'p')
984 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
985 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
988 mDNSu8
*dst
= fqdn
->c
;
990 const char *errormsg
;
992 // In the case where there is no name (and ONLY in that case),
993 // a single-label subtype is allowed as the first label of a three-part "type"
996 const mDNSu8
*s0
= type
->c
;
997 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
999 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
1000 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1002 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
1003 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
1005 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
1006 src
= s0
; // Copy the first label
1008 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
1009 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
1010 type
= (const domainname
*)s1
;
1012 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1013 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1014 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1015 if (SameDomainName((domainname
*)s0
, (const domainname
*)"\x09_services\x07_dns-sd\x04_udp"))
1016 dst
-= sizeof(SubTypeLabel
);
1022 if (name
&& name
->c
[0])
1024 src
= name
->c
; // Put the service name into the domain name
1026 if (len
>= 0x40) { errormsg
= "Service instance name too long"; goto fail
; }
1027 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1030 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1032 src
= type
->c
; // Put the service type into the domain name
1034 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
)))
1036 errormsg
= "Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
1039 if (src
[1] != '_') { errormsg
= "Application protocol name must begin with underscore"; goto fail
; }
1040 for (i
=2; i
<=len
; i
++)
1042 // Letters and digits are allowed anywhere
1043 if (mdnsIsLetter(src
[i
]) || mdnsIsDigit(src
[i
])) continue;
1044 // Hyphens are only allowed as interior characters
1045 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1046 // with the same rule as hyphens
1047 if ((src
[i
] == '-' || src
[i
] == '_') && i
> 2 && i
< len
) continue;
1048 errormsg
= "Application protocol name must contain only letters, digits, and hyphens"; goto fail
;
1050 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1053 if (!ValidTransportProtocol(src
)) { errormsg
= "Transport protocol name must be _udp or _tcp"; goto fail
; }
1054 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1056 if (*src
) { errormsg
= "Service type must have only two labels"; goto fail
; }
1059 if (!domain
->c
[0]) { errormsg
= "Service domain must be non-empty"; goto fail
; }
1060 if (SameDomainName(domain
, (const domainname
*)"\x05" "local" "\x04" "arpa"))
1061 { errormsg
= "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
1062 dst
= AppendDomainName(fqdn
, domain
);
1063 if (!dst
) { errormsg
= "Service domain too long"; goto fail
; }
1067 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
1071 // A service name has the form: instance.application-protocol.transport-protocol.domain
1072 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1073 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1074 // However, if the given FQDN doesn't contain at least three labels,
1075 // DeconstructServiceName will reject it and return mDNSfalse.
1076 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
1077 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
1080 const mDNSu8
*src
= fqdn
->c
;
1081 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
1084 dst
= name
->c
; // Extract the service name
1086 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
1087 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
1088 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1090 dst
= type
->c
; // Extract the service type
1092 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
1093 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
1094 if (src
[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse
); }
1095 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1098 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
1099 // Can't do this check right now, until Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1100 //if (!ValidTransportProtocol(src))
1101 // { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
1102 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1103 *dst
++ = 0; // Put terminator on the end of service type
1105 dst
= domain
->c
; // Extract the service domain
1110 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
1111 if (src
+ 1 + len
+ 1 >= max
)
1112 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
1113 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1115 *dst
++ = 0; // Put the null root label on the end
1121 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1122 // 10xxxxxx is a continuation byte of a multi-byte character
1123 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1124 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1125 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1126 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1127 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1129 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1130 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1131 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1132 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1133 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1135 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
1139 mDNSu8 c1
= string
[max
]; // First byte after cut point
1140 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : (mDNSu8
)0xB0; // Second byte after cut point
1141 length
= max
; // Trim length down
1144 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1145 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1146 // If so, then we continue to chop more bytes until we get to a legal chop point.
1147 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1148 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1149 if (!continuation
&& !secondsurrogate
) break;
1151 c1
= string
[--length
];
1153 // Having truncated characters off the end of our string, also cut off any residual white space
1154 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1159 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1160 // name ends in "-nnn", where n is some decimal number.
1161 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1163 mDNSu16 l
= name
->c
[0];
1167 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1168 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1169 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1171 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1172 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1176 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1177 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1179 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1180 return (name
->c
[l
] == '-');
1184 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1185 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1186 // from the suffix that was removed.
1187 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1189 mDNSu32 val
= 0, multiplier
= 1;
1191 // Chop closing parentheses from RichText suffix
1192 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1194 // Get any existing numerical suffix off the name
1195 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
1196 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1198 // Chop opening parentheses or dash from suffix
1201 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1205 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1211 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1212 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1213 mDNSexport
void AppendLabelSuffix(domainlabel
*const name
, mDNSu32 val
, const mDNSBool RichText
)
1215 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1216 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1218 // Truncate trailing spaces from RichText names
1219 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1221 while (divisor
< 0xFFFFFFFFUL
/10 && val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1223 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1225 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1226 else { name
->c
[++name
->c
[0]] = '-'; }
1230 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1235 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1238 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1242 if (LabelContainsSuffix(name
, RichText
))
1243 val
= RemoveLabelSuffix(name
, RichText
);
1245 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1246 // If existing suffix in the range 2-9, increment it.
1247 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1248 // so add a random increment to improve the chances of finding an available name next time.
1249 if (val
== 0) val
= 2;
1250 else if (val
< 10) val
++;
1251 else val
+= 1 + mDNSRandom(99);
1253 AppendLabelSuffix(name
, val
, RichText
);
1256 // ***************************************************************************
1257 #if COMPILER_LIKES_PRAGMA_MARK
1259 #pragma mark - Resource Record Utility Functions
1262 // Set up a AuthRecord with sensible default values.
1263 // These defaults may be overwritten with new values before mDNS_Register is called
1264 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
1265 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
1267 // Don't try to store a TTL bigger than we can represent in platform time units
1268 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
1269 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
1270 else if (ttl
== 0) // And Zero TTL is illegal
1271 ttl
= DefaultTTLforRRType(rrtype
);
1273 // Field Group 1: The actual information pertaining to this resource record
1274 rr
->resrec
.RecordType
= RecordType
;
1275 rr
->resrec
.InterfaceID
= InterfaceID
;
1276 rr
->resrec
.name
= &rr
->namestorage
;
1277 rr
->resrec
.rrtype
= rrtype
;
1278 rr
->resrec
.rrclass
= kDNSClass_IN
;
1279 rr
->resrec
.rroriginalttl
= ttl
;
1280 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1281 // rr->resrec.rdestimate = set in mDNS_Register_internal
1282 // rr->resrec.rdata = MUST be set by client
1285 rr
->resrec
.rdata
= RDataStorage
;
1288 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1289 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1292 // Field Group 2: Persistent metadata for Authoritative Records
1293 rr
->Additional1
= mDNSNULL
;
1294 rr
->Additional2
= mDNSNULL
;
1295 rr
->DependentOn
= mDNSNULL
;
1296 rr
->RRSet
= mDNSNULL
;
1297 rr
->RecordCallback
= Callback
;
1298 rr
->RecordContext
= Context
;
1300 rr
->AutoTarget
= Target_Manual
;
1301 rr
->AllowRemoteQuery
= mDNSfalse
;
1302 rr
->ForceMCast
= mDNSfalse
;
1304 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1305 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1307 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1308 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1309 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1310 rr
->state
= regState_Zero
;
1316 rr
->UpdateServer
= zeroAddr
;
1317 rr
->UpdatePort
= zeroIPPort
;
1322 rr
->InFlightRData
= 0;
1323 rr
->InFlightRDLen
= 0;
1324 rr
->QueuedRData
= 0;
1325 rr
->QueuedRDLen
= 0;
1327 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
1330 mDNSexport mDNSu32
RDataHashValue(const ResourceRecord
*const rr
)
1335 case kDNSType_CNAME
:
1337 case kDNSType_DNAME
: return DomainNameHashValue(&rr
->rdata
->u
.name
);
1339 case kDNSType_SOA
: return rr
->rdata
->u
.soa
.serial
+
1340 rr
->rdata
->u
.soa
.refresh
+
1341 rr
->rdata
->u
.soa
.retry
+
1342 rr
->rdata
->u
.soa
.expire
+
1343 rr
->rdata
->u
.soa
.min
+
1344 DomainNameHashValue(&rr
->rdata
->u
.soa
.mname
) +
1345 DomainNameHashValue(&rr
->rdata
->u
.soa
.rname
);
1348 case kDNSType_AFSDB
:
1350 case kDNSType_KX
: return DomainNameHashValue(&rr
->rdata
->u
.mx
.exchange
);
1352 case kDNSType_RP
: return DomainNameHashValue(&rr
->rdata
->u
.rp
.mbox
) + DomainNameHashValue(&rr
->rdata
->u
.rp
.txt
);
1354 case kDNSType_PX
: return DomainNameHashValue(&rr
->rdata
->u
.px
.map822
) + DomainNameHashValue(&rr
->rdata
->u
.px
.mapx400
);
1356 case kDNSType_SRV
: return DomainNameHashValue(&rr
->rdata
->u
.srv
.target
);
1358 case kDNSType_OPT
: // Okay to use blind memory sum because there are no 'holes' in the in-memory representation
1364 for (i
=0; i
+1 < rr
->rdlength
; i
+=2)
1366 sum
+= (((mDNSu32
)(rr
->rdata
->u
.data
[i
])) << 8) | rr
->rdata
->u
.data
[i
+1];
1367 sum
= (sum
<<3) | (sum
>>29);
1369 if (i
< rr
->rdlength
)
1371 sum
+= ((mDNSu32
)(rr
->rdata
->u
.data
[i
])) << 8;
1378 // r1 has to be a full ResourceRecord including rrtype and rdlength
1379 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1380 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1385 case kDNSType_CNAME
:
1387 case kDNSType_DNAME
:return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->name
));
1389 case kDNSType_SOA
: return(mDNSBool
)( r1
->rdata
->u
.soa
.serial
== r2
->soa
.serial
&&
1390 r1
->rdata
->u
.soa
.refresh
== r2
->soa
.refresh
&&
1391 r1
->rdata
->u
.soa
.retry
== r2
->soa
.retry
&&
1392 r1
->rdata
->u
.soa
.expire
== r2
->soa
.expire
&&
1393 r1
->rdata
->u
.soa
.min
== r2
->soa
.min
&&
1394 SameDomainName(&r1
->rdata
->u
.soa
.mname
, &r2
->soa
.mname
) &&
1395 SameDomainName(&r1
->rdata
->u
.soa
.rname
, &r2
->soa
.rname
));
1398 case kDNSType_AFSDB
:
1400 case kDNSType_KX
: return(mDNSBool
)( r1
->rdata
->u
.mx
.preference
== r2
->mx
.preference
&&
1401 SameDomainName(&r1
->rdata
->u
.mx
.exchange
, &r2
->mx
.exchange
));
1403 case kDNSType_RP
: return(mDNSBool
)( SameDomainName(&r1
->rdata
->u
.rp
.mbox
, &r2
->rp
.mbox
) &&
1404 SameDomainName(&r1
->rdata
->u
.rp
.txt
, &r2
->rp
.txt
));
1406 case kDNSType_PX
: return(mDNSBool
)( r1
->rdata
->u
.px
.preference
== r2
->px
.preference
&&
1407 SameDomainName(&r1
->rdata
->u
.px
.map822
, &r2
->px
.map822
) &&
1408 SameDomainName(&r1
->rdata
->u
.px
.mapx400
, &r2
->px
.mapx400
));
1410 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->srv
.priority
&&
1411 r1
->rdata
->u
.srv
.weight
== r2
->srv
.weight
&&
1412 mDNSSameIPPort(r1
->rdata
->u
.srv
.port
, r2
->srv
.port
) &&
1413 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->srv
.target
));
1415 case kDNSType_OPT
: // Okay to use blind memory compare because there are no 'holes' in the in-memory representation
1417 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->data
, r1
->rdlength
));
1421 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1422 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1423 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1424 // because it has to check all the way to the end of the names to be sure.
1425 // In cases where we know in advance that the names match it's especially advantageous to skip the
1426 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1428 mDNSexport mDNSBool
SameNameRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1430 if (rr
->InterfaceID
&&
1431 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1432 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1434 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1435 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1437 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1438 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1439 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1444 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1446 if (rr
->InterfaceID
&&
1447 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1448 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1450 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1451 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1453 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1454 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1455 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1456 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1459 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1461 const RDataBody
*rd
= &rr
->rdata
->u
;
1462 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1463 if (rr
->rrclass
== kDNSQClass_ANY
) return(rr
->rdlength
); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1464 else switch (rr
->rrtype
)
1466 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1469 case kDNSType_CNAME
:
1471 case kDNSType_DNAME
:return(CompressedDomainNameLength(&rd
->name
, name
));
1473 case kDNSType_SOA
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1474 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1475 5 * sizeof(mDNSOpaque32
));
1483 case kDNSType_DHCID
:return(rr
->rdlength
); // Not self-describing, so have to just trust rdlength
1485 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1488 case kDNSType_AFSDB
:
1490 case kDNSType_KX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->mx
.exchange
, name
));
1492 case kDNSType_RP
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->rp
.mbox
, name
) +
1493 CompressedDomainNameLength(&rd
->rp
.txt
, name
));
1495 case kDNSType_PX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->px
.map822
, name
) +
1496 CompressedDomainNameLength(&rd
->px
.mapx400
, name
));
1498 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1500 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1502 case kDNSType_OPT
: return(rr
->rdlength
);
1504 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1505 return(rr
->rdlength
);
1509 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1515 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1517 case kDNSType_NS
: // Same as PTR
1518 case kDNSType_MD
: // Same as PTR
1519 case kDNSType_MF
: // Same as PTR
1520 case kDNSType_CNAME
:// Same as PTR
1521 //case kDNSType_SOA not checked
1522 case kDNSType_MB
: // Same as PTR
1523 case kDNSType_MG
: // Same as PTR
1524 case kDNSType_MR
: // Same as PTR
1525 //case kDNSType_NULL not checked (no specified format, so always valid)
1526 //case kDNSType_WKS not checked
1527 case kDNSType_PTR
: len
= DomainNameLengthLimit(&rd
->u
.name
, rd
->u
.data
+ rdlength
);
1528 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1530 case kDNSType_HINFO
:// Same as TXT (roughly)
1531 case kDNSType_MINFO
:// Same as TXT (roughly)
1532 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1534 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1535 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1536 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1537 return (ptr
== end
);
1540 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1542 case kDNSType_MX
: // Must be at least two-byte preference, plus domainname
1543 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1544 len
= DomainNameLengthLimit(&rd
->u
.mx
.exchange
, rd
->u
.data
+ rdlength
);
1545 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1547 case kDNSType_SRV
: // Must be at least priority+weight+port, plus domainname
1548 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1549 len
= DomainNameLengthLimit(&rd
->u
.srv
.target
, rd
->u
.data
+ rdlength
);
1550 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1552 default: return(mDNStrue
); // Allow all other types without checking
1556 // ***************************************************************************
1557 #if COMPILER_LIKES_PRAGMA_MARK
1559 #pragma mark - DNS Message Creation Functions
1562 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1566 h
->numQuestions
= 0;
1568 h
->numAuthorities
= 0;
1569 h
->numAdditionals
= 0;
1572 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1574 const mDNSu8
*result
= end
- *domname
- 1;
1576 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1578 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1579 while (result
>= base
)
1581 // If the length byte and first character of the label match, then check further to see
1582 // if this location in the packet will yield a useful name compression pointer.
1583 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1585 const mDNSu8
*name
= domname
;
1586 const mDNSu8
*targ
= result
;
1587 while (targ
+ *name
< end
)
1589 // First see if this label matches
1591 const mDNSu8
*pointertarget
;
1592 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1593 if (i
<= *name
) break; // If label did not match, bail out
1594 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1595 name
+= 1 + *name
; // and proceed to check next label
1596 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1597 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1599 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1600 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1601 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1602 if (targ
+1 >= end
) break; // Second byte not present!
1603 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1604 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1605 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1606 targ
= pointertarget
;
1609 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1614 // Put a string of dot-separated labels as length-prefixed labels
1615 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1616 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1617 // end points to the end of the message so far
1618 // ptr points to where we want to put the name
1619 // limit points to one byte past the end of the buffer that we must not overrun
1620 // domainname is the name to put
1621 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1622 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1624 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1625 const mDNSu8
* np
= name
->c
;
1626 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1627 const mDNSu8
* pointer
= mDNSNULL
;
1628 const mDNSu8
*const searchlimit
= ptr
;
1630 if (!ptr
) { LogMsg("putDomainNameAsLabels ptr is null"); return(mDNSNULL
); }
1632 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1634 if (*np
> MAX_DOMAIN_LABEL
)
1635 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1637 // This check correctly allows for the final trailing root label:
1639 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1640 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1641 // We know that max will be at name->c[255]
1642 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1643 // six bytes, then exit the loop, write the final terminating root label, and the domain
1644 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1645 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1646 if (np
+ 1 + *np
>= max
)
1647 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1649 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1650 if (pointer
) // Use a compression pointer if we can
1652 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1653 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1654 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1657 else // Else copy one label and try again
1661 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1663 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1667 if (ptr
< limit
) // If we didn't run out of space
1669 *ptr
++ = 0; // Put the final root label
1670 return(ptr
); // and return
1676 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1678 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1679 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1680 return ptr
+ sizeof(mDNSOpaque16
);
1683 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
1685 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
1686 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
1687 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
1688 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
1689 return ptr
+ sizeof(mDNSu32
);
1692 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, const ResourceRecord
*const rr
)
1697 while (nput
< rr
->rdlength
)
1699 // check if space for opt/optlen
1700 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1701 opt
= (rdataOPT
*)(rr
->rdata
->u
.data
+ nput
);
1702 ptr
= putVal16(ptr
, opt
->opt
);
1703 ptr
= putVal16(ptr
, opt
->optlen
);
1704 nput
+= 2 * sizeof(mDNSu16
);
1705 if (opt
->opt
== kDNSOpt_LLQ
)
1707 if (ptr
+ LLQ_OPTLEN
> limit
) goto space_err
;
1708 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1709 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1710 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1711 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
.b
, 8); // 8-byte id
1713 ptr
= putVal32(ptr
, opt
->OptData
.llq
.llqlease
);
1716 else if (opt
->opt
== kDNSOpt_Lease
)
1718 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1719 ptr
= putVal32(ptr
, opt
->OptData
.updatelease
);
1720 nput
+= sizeof(mDNSs32
);
1722 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1728 LogMsg("ERROR: putOptRData - out of space");
1732 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1734 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1735 *ptr
+= sizeof(mDNSOpaque16
);
1739 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*const limit
, LargeCacheRecord
*const cr
, mDNSu16 pktRDLen
)
1742 ResourceRecord
*const rr
= &cr
->r
.resrec
;
1743 rdataOPT
*opt
= (rdataOPT
*)rr
->rdata
->u
.data
;
1745 while (nread
< pktRDLen
&& (mDNSu8
*)opt
< rr
->rdata
->u
.data
+ MaximumRDSize
- sizeof(rdataOPT
))
1747 // space for opt + optlen
1748 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1749 opt
->opt
= getVal16(&ptr
);
1750 opt
->optlen
= getVal16(&ptr
);
1751 nread
+= 2 * sizeof(mDNSu16
);
1752 if (opt
->opt
== kDNSOpt_LLQ
)
1754 if ((unsigned)(limit
- ptr
) < LLQ_OPTLEN
) goto space_err
;
1755 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1756 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1757 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1758 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
.b
, ptr
, 8);
1760 opt
->OptData
.llq
.llqlease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1761 if (opt
->OptData
.llq
.llqlease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1762 opt
->OptData
.llq
.llqlease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1763 ptr
+= sizeof(mDNSOpaque32
);
1764 nread
+= LLQ_OPTLEN
;
1766 else if (opt
->opt
== kDNSOpt_Lease
)
1768 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1770 opt
->OptData
.updatelease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1771 if (opt
->OptData
.updatelease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1772 opt
->OptData
.updatelease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1773 ptr
+= sizeof(mDNSs32
);
1774 nread
+= sizeof(mDNSs32
);
1776 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1777 opt
++; // increment pointer into rdatabody
1780 rr
->rdlength
= pktRDLen
;
1784 LogMsg("ERROR: getLLQRdata - out of space");
1788 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1789 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const ResourceRecord
*const rr
)
1793 case kDNSType_A
: if (rr
->rdlength
!= 4)
1795 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1798 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1799 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[0];
1800 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[1];
1801 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[2];
1802 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[3];
1806 case kDNSType_CNAME
:
1808 case kDNSType_DNAME
:return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1810 case kDNSType_SOA
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.soa
.mname
);
1811 if (!ptr
) return(mDNSNULL
);
1812 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.soa
.rname
);
1813 if (!ptr
|| ptr
+ 20 > limit
) return(mDNSNULL
);
1814 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.serial
);
1815 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.refresh
);
1816 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.retry
);
1817 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.expire
);
1818 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.min
);
1822 case kDNSType_HINFO
:
1828 case kDNSType_DHCID
:if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1829 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, rr
->rdlength
);
1830 return(ptr
+ rr
->rdlength
);
1833 case kDNSType_AFSDB
:
1835 case kDNSType_KX
: if (ptr
+ 3 > limit
) return(mDNSNULL
);
1836 ptr
= putVal16(ptr
, rr
->rdata
->u
.mx
.preference
);
1837 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.mx
.exchange
));
1839 case kDNSType_RP
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.rp
.mbox
);
1840 if (!ptr
) return(mDNSNULL
);
1841 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.rp
.txt
);
1844 case kDNSType_PX
: if (ptr
+ 5 > limit
) return(mDNSNULL
);
1845 ptr
= putVal16(ptr
, rr
->rdata
->u
.px
.preference
);
1846 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.px
.map822
);
1847 if (!ptr
) return(mDNSNULL
);
1848 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.px
.mapx400
);
1851 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1853 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1856 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1857 mDNSPlatformMemCopy(ptr
, &rr
->rdata
->u
.ipv6
, sizeof(rr
->rdata
->u
.ipv6
));
1858 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1860 case kDNSType_SRV
: if (ptr
+ 7 > limit
) return(mDNSNULL
);
1861 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1862 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1863 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1864 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1865 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1866 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1867 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1869 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1871 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1872 if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1873 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, rr
->rdlength
);
1874 return(ptr
+ rr
->rdlength
);
1878 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
1880 mDNSu16 rrclass
= (rr
->rrtype
== kDNSType_OPT
) ? NormalMaxDNSMessageData
: rr
->rrclass
;
1882 mDNSu16 actualLength
;
1884 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1886 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1890 if (!ptr
) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL
); }
1892 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
1893 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1894 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1895 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1896 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1897 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1898 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1899 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1900 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1901 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1902 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1903 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1905 // Go back and fill in the actual number of data bytes we wrote
1906 // (actualLength can be less than rdlength when domain name compression is used)
1907 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1908 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1909 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1911 if (count
) (*count
)++;
1912 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1916 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1919 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1920 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1923 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1924 mDNSu16
*count
, const AuthRecord
*rr
)
1926 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
1927 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1928 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1929 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1930 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1931 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1932 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1933 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1938 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1940 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1941 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1942 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1943 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1944 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1945 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1946 msg
->h
.numQuestions
++;
1950 // for dynamic updates
1951 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
1953 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
1954 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
1955 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
1956 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
1957 *ptr
++ = zoneClass
.b
[0];
1958 *ptr
++ = zoneClass
.b
[1];
1959 msg
->h
.mDNS_numZones
++;
1963 // for dynamic updates
1964 mDNSexport mDNSu8
*putPrereqNameNotInUse(const domainname
*const name
, DNSMessage
*const msg
, mDNSu8
*const ptr
, mDNSu8
*const end
)
1967 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1968 AssignDomainName(&prereq
.namestorage
, name
);
1969 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
1970 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
1971 return putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
1974 // for dynamic updates
1975 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
1977 // deletion: specify record w/ TTL 0, class NONE
1978 const mDNSu16 origclass
= rr
->rrclass
;
1979 rr
->rrclass
= kDNSClass_NONE
;
1980 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
1981 rr
->rrclass
= origclass
;
1985 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
1987 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1988 mDNSu16
class = kDNSQClass_ANY
;
1990 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1991 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1992 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1993 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1994 ptr
[2] = (mDNSu8
)(class >> 8);
1995 ptr
[3] = (mDNSu8
)(class & 0xFF);
1996 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1997 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1999 msg
->h
.mDNS_numUpdates
++;
2003 // for dynamic updates
2004 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
2006 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
2007 mDNSu16
class = kDNSQClass_ANY
;
2008 mDNSu16 rrtype
= kDNSQType_ANY
;
2010 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
2011 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
2012 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
2013 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
2014 ptr
[2] = (mDNSu8
)(class >> 8);
2015 ptr
[3] = (mDNSu8
)(class & 0xFF);
2016 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
2017 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
2019 msg
->h
.mDNS_numUpdates
++;
2023 // for dynamic updates
2024 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
2027 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
2028 rr
.resrec
.rrclass
= NormalMaxDNSMessageData
;
2029 rr
.resrec
.rdlength
= LEASE_OPT_RDLEN
;
2030 rr
.resrec
.rdestimate
= LEASE_OPT_RDLEN
;
2031 rr
.resrec
.rdata
->u
.opt
.opt
= kDNSOpt_Lease
;
2032 rr
.resrec
.rdata
->u
.opt
.optlen
= sizeof(mDNSs32
);
2033 rr
.resrec
.rdata
->u
.opt
.OptData
.updatelease
= lease
;
2034 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, &rr
.resrec
, 0);
2035 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
2039 mDNSexport mDNSu8
*putHINFO(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
, DomainAuthInfo
*authInfo
)
2041 if (authInfo
&& authInfo
->AutoTunnel
)
2044 mDNSu8
*h
= hinfo
.rdatastorage
.u
.data
;
2045 mDNSu16 len
= 2 + m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0];
2047 mDNS_SetupResourceRecord(&hinfo
, mDNSNULL
, mDNSInterface_Any
, kDNSType_HINFO
, 0, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
2048 AppendDomainLabel(&hinfo
.namestorage
, &m
->hostlabel
);
2049 AppendDomainName (&hinfo
.namestorage
, &authInfo
->domain
);
2050 hinfo
.resrec
.rroriginalttl
= 0;
2051 mDNSPlatformMemCopy(h
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
2053 mDNSPlatformMemCopy(h
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
2054 hinfo
.resrec
.rdlength
= len
;
2055 hinfo
.resrec
.rdestimate
= len
;
2056 newptr
= PutResourceRecord(msg
, end
, &msg
->h
.numAdditionals
, &hinfo
.resrec
);
2063 // ***************************************************************************
2064 #if COMPILER_LIKES_PRAGMA_MARK
2066 #pragma mark - DNS Message Parsing Functions
2069 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
2074 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
2076 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
2077 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
2078 sum
= (sum
<<3) | (sum
>>29);
2080 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
2084 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
2089 rr
->rdata
= NewRData
;
2090 rr
->rdlength
= rdlength
;
2092 // Must not try to get target pointer until after updating rr->rdata
2093 target
= GetRRDomainNameTarget(rr
);
2094 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
2095 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
2096 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
);
2099 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
2103 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2104 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2106 while (1) // Read sequence of labels
2108 const mDNSu8 len
= *ptr
++; // Read length of this label
2109 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
2112 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2113 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2114 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
2115 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
2120 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
2121 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
2122 case 0xC0: return(ptr
+1);
2127 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2128 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
2129 domainname
*const name
)
2131 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
2132 mDNSu8
*np
= name
->c
; // Name pointer
2133 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
2135 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2136 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2138 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2140 while (1) // Read sequence of labels
2142 const mDNSu8 len
= *ptr
++; // Read length of this label
2143 if (len
== 0) break; // If length is zero, that means this name is complete
2149 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2150 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2151 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
2152 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
2154 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
2155 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2158 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
2161 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
2163 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
2164 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
2165 ptr
= (mDNSu8
*)msg
+ offset
;
2166 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2167 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
2169 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
2174 if (nextbyte
) return(nextbyte
);
2178 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2180 mDNSu16 pktrdlength
;
2182 ptr
= skipDomainName(msg
, ptr
, end
);
2183 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2185 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2186 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2188 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2190 return(ptr
+ pktrdlength
);
2193 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
,
2194 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*largecr
)
2196 CacheRecord
*rr
= &largecr
->r
;
2197 mDNSu16 pktrdlength
;
2199 if (largecr
== &m
->rec
&& largecr
->r
.resrec
.RecordType
)
2200 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &largecr
->r
));
2202 rr
->next
= mDNSNULL
;
2203 rr
->resrec
.name
= &largecr
->namestorage
;
2205 rr
->NextInKAList
= mDNSNULL
;
2206 rr
->TimeRcvd
= m
? m
->timenow
: 0;
2207 rr
->DelayDelivery
= 0;
2208 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2209 rr
->LastUsed
= m
? m
->timenow
: 0;
2210 rr
->CRActiveQuestion
= mDNSNULL
;
2211 rr
->UnansweredQueries
= 0;
2212 rr
->LastUnansweredTime
= 0;
2213 rr
->MPUnansweredQ
= 0;
2214 rr
->MPLastUnansweredQT
= 0;
2215 rr
->MPUnansweredKA
= 0;
2216 rr
->MPExpectingKA
= mDNSfalse
;
2217 rr
->NextInCFList
= mDNSNULL
;
2219 rr
->resrec
.InterfaceID
= InterfaceID
;
2220 ptr
= getDomainName(msg
, ptr
, end
, &largecr
->namestorage
);
2221 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2223 if (ptr
+ 10 > end
) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2225 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2226 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
2227 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
2228 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
2229 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2230 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2231 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2232 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2234 // If mDNS record has cache-flush bit set, we mark it unique
2235 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2236 // authoritative for the entire RRSet), unless this is a truncated response
2237 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8) || (!InterfaceID
&& !(msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)))
2238 RecordType
|= kDNSRecordTypePacketUniqueMask
;
2240 if (ptr
+ pktrdlength
> end
) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2241 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
2243 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
2244 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
2246 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
2248 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2249 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2250 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2251 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2252 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2253 if (rr
->resrec
.rrclass
== kDNSQClass_ANY
&& pktrdlength
== 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2254 rr
->resrec
.rdlength
= 0;
2255 else switch (rr
->resrec
.rrtype
)
2257 case kDNSType_A
: if (pktrdlength
!= sizeof(mDNSv4Addr
)) return(mDNSNULL
);
2258 rr
->resrec
.rdata
->u
.ipv4
.b
[0] = ptr
[0];
2259 rr
->resrec
.rdata
->u
.ipv4
.b
[1] = ptr
[1];
2260 rr
->resrec
.rdata
->u
.ipv4
.b
[2] = ptr
[2];
2261 rr
->resrec
.rdata
->u
.ipv4
.b
[3] = ptr
[3];
2265 case kDNSType_CNAME
:
2267 case kDNSType_DNAME
:ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
);
2268 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
2269 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
2272 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
);
2273 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
2274 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
);
2275 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
2276 if (ptr
+ 0x14 != end
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
2277 rr
->resrec
.rdata
->u
.soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
2278 rr
->resrec
.rdata
->u
.soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
2279 rr
->resrec
.rdata
->u
.soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
2280 rr
->resrec
.rdata
->u
.soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
2281 rr
->resrec
.rdata
->u
.soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
2285 case kDNSType_HINFO
:
2291 case kDNSType_DHCID
:if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2293 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2294 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2297 rr
->resrec
.rdlength
= pktrdlength
;
2298 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, ptr
, pktrdlength
);
2302 case kDNSType_AFSDB
:
2304 case kDNSType_KX
: if (pktrdlength
< 3) return(mDNSNULL
); // Preference + domainname
2305 rr
->resrec
.rdata
->u
.mx
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2306 ptr
= getDomainName(msg
, ptr
+2, end
, &rr
->resrec
.rdata
->u
.mx
.exchange
);
2307 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL
); }
2308 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2311 case kDNSType_RP
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.rp
.mbox
); // Domainname + domainname
2312 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL
; }
2313 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.rp
.txt
);
2314 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL
; }
2317 case kDNSType_PX
: if (pktrdlength
< 4) return(mDNSNULL
); // Preference + domainname + domainname
2318 rr
->resrec
.rdata
->u
.px
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2319 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.px
.map822
);
2320 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL
; }
2321 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.px
.mapx400
);
2322 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL
; }
2325 case kDNSType_AAAA
: if (pktrdlength
!= sizeof(mDNSv6Addr
)) return(mDNSNULL
);
2326 mDNSPlatformMemCopy(&rr
->resrec
.rdata
->u
.ipv6
, ptr
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
2329 case kDNSType_SRV
: if (pktrdlength
< 7) return(mDNSNULL
); // Priority + weight + port + domainname
2330 rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2331 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
2332 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
2333 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
2334 ptr
= getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
);
2335 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
2336 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2339 case kDNSType_OPT
: ptr
= getOptRdata(ptr
, end
, largecr
, pktrdlength
); break;
2340 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL
); }
2342 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2344 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2345 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2348 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2349 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
2350 // Note: Just because we don't understand the record type, that doesn't
2351 // mean we fail. The DNS protocol specifies rdlength, so we can
2352 // safely skip over unknown records and ignore them.
2353 // We also grab a binary copy of the rdata anyway, since the caller
2354 // might know how to interpret it even if we don't.
2355 rr
->resrec
.rdlength
= pktrdlength
;
2356 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, ptr
, pktrdlength
);
2360 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2361 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rdlength, rdestimate, rdatahash for us
2363 // Success! Now fill in RecordType to show this record contains valid data
2364 rr
->resrec
.RecordType
= RecordType
;
2368 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2370 ptr
= skipDomainName(msg
, ptr
, end
);
2371 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
2372 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2376 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
2377 DNSQuestion
*question
)
2379 mDNSPlatformMemZero(question
, sizeof(*question
));
2380 question
->InterfaceID
= InterfaceID
;
2381 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
2382 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
2383 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2385 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
2386 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
2387 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
2391 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2394 const mDNSu8
*ptr
= msg
->data
;
2395 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
2399 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2402 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
2403 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2407 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2410 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
2411 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2415 mDNSexport
const mDNSu8
*LocateLLQOptData(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2418 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2420 // Locate the OPT record.
2421 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2422 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2423 // but not necessarily the *last* entry in the Additional Section.
2424 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2426 if (ptr
+ 10 + LLQ_OPT_RDLEN
<= end
&& // Make sure we have 10+22 bytes of data
2427 ptr
[0] == 0 && // Name must be root label
2428 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2429 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2430 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)LLQ_OPT_RDLEN
)
2433 ptr
= skipResourceRecord(msg
, ptr
, end
);
2438 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2439 // it is callers responsibilty to clear m->rec.r.resrec.RecordType after use
2440 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2441 // The code that currently calls this assumes there's only one, instead of iterating through the set
2442 mDNSexport
const rdataOPT
*GetLLQOptData(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2444 const mDNSu8
*ptr
= LocateLLQOptData(msg
, end
);
2447 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2448 if (ptr
) return(&m
->rec
.r
.resrec
.rdata
->u
.opt
);
2453 mDNSexport
const mDNSu8
*LocateLeaseOptData(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2456 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2458 // Locate the OPT record.
2459 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2460 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2461 // but not necessarily the *last* entry in the Additional Section.
2462 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2464 if (ptr
+ 10 + LEASE_OPT_RDLEN
<= end
&& // Make sure we have 10+8 bytes of data
2465 ptr
[0] == 0 && // Name must be root label
2466 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2467 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2468 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)LEASE_OPT_RDLEN
)
2471 ptr
= skipResourceRecord(msg
, ptr
, end
);
2476 // Get the lease life of records in a dynamic update
2477 // returns 0 on error or if no lease present
2478 mDNSexport mDNSu32
GetPktLease(mDNS
*m
, DNSMessage
*msg
, const mDNSu8
*end
)
2481 const mDNSu8
*ptr
= LocateLeaseOptData(msg
, end
);
2482 if (ptr
) ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2483 if (ptr
&& m
->rec
.r
.resrec
.rdlength
>= LEASE_OPT_RDLEN
&& m
->rec
.r
.resrec
.rdata
->u
.opt
.opt
== kDNSOpt_Lease
)
2484 result
= m
->rec
.r
.resrec
.rdata
->u
.opt
.OptData
.updatelease
;
2485 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
2489 mDNSlocal
const mDNSu8
*DumpRecords(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
, int count
, char *label
)
2492 LogMsg("%2d %s", count
, label
);
2493 for (i
= 0; i
< count
&& ptr
; i
++)
2495 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2496 // but since it's only used for debugging (and probably only on OS X, not on
2497 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2498 LargeCacheRecord largecr
;
2499 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, mDNSInterface_Any
, kDNSRecordTypePacketAns
, &largecr
);
2500 if (ptr
) LogMsg("%2d TTL%7d %s", i
, largecr
.r
.resrec
.rroriginalttl
, CRDisplayString(m
, &largecr
.r
));
2502 if (!ptr
) LogMsg("ERROR: Premature end of packet data");
2506 #define DNS_OP_Name(X) ( \
2507 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2508 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2509 (X) == kDNSFlag0_OP_Status ? "Status " : \
2510 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2511 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2512 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2514 #define DNS_RC_Name(X) ( \
2515 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2516 (X) == kDNSFlag1_RC_FmtErr ? "FmtErr" : \
2517 (X) == kDNSFlag1_RC_SrvErr ? "SrvErr" : \
2518 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2519 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2520 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2521 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2522 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2523 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2524 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2525 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2527 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2528 mDNSexport
void DumpPacket(mDNS
*const m
, mDNSBool sent
, char *transport
, const mDNSAddr
*addr
, mDNSIPPort port
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2530 mDNSBool IsUpdate
= ((msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
) == kDNSFlag0_OP_Update
);
2531 const mDNSu8
*ptr
= msg
->data
;
2535 LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes %s %#a:%d%s --",
2536 sent
? "Sent" : "Received", transport
,
2537 DNS_OP_Name(msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
),
2538 msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
? "Response" : "Query",
2539 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1],
2540 DNS_RC_Name(msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
),
2541 msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
,
2542 msg
->h
.flags
.b
[0] & kDNSFlag0_AA
? "AA " : "",
2543 msg
->h
.flags
.b
[0] & kDNSFlag0_TC
? "TC " : "",
2544 msg
->h
.flags
.b
[0] & kDNSFlag0_RD
? "RD " : "",
2545 msg
->h
.flags
.b
[1] & kDNSFlag1_RA
? "RA " : "",
2546 msg
->h
.flags
.b
[1] & kDNSFlag1_AD
? "AD " : "",
2547 msg
->h
.flags
.b
[1] & kDNSFlag1_CD
? "CD " : "",
2548 mDNSVal16(msg
->h
.id
),
2550 sent
? "to" : "from", addr
, mDNSVal16(port
),
2551 (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
) ? " (truncated)" : ""
2554 LogMsg("%2d %s", msg
->h
.numQuestions
, IsUpdate
? "Zone" : "Questions");
2555 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++)
2557 ptr
= getQuestion(msg
, ptr
, end
, mDNSInterface_Any
, &q
);
2558 if (ptr
) LogMsg("%2d %##s %s", i
, q
.qname
.c
, DNSTypeName(q
.qtype
));
2560 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAnswers
, IsUpdate
? "Prerequisites" : "Answers");
2561 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAuthorities
, IsUpdate
? "Updates" : "Authorities");
2562 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAdditionals
, "Additionals");
2563 LogMsg("--------------");
2566 // ***************************************************************************
2567 #if COMPILER_LIKES_PRAGMA_MARK
2569 #pragma mark - Packet Sending Functions
2572 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2573 struct TCPSocket_struct
{ TCPSocketFlags flags
; /* ... */ };
2575 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2576 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2577 mDNSexport mStatus
mDNSSendDNSMessage(mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2578 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, TCPSocket
*sock
, DomainAuthInfo
*authInfo
)
2580 mStatus status
= mStatus_NoError
;
2581 const mDNSu16 numQuestions
= msg
->h
.numQuestions
;
2582 const mDNSu16 numAnswers
= msg
->h
.numAnswers
;
2583 const mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
2584 const mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2585 mDNSu16 tmpNumAdditionals
= numAdditionals
;
2586 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
2588 if (end
<= msg
->data
|| end
- msg
->data
> AbsoluteMaxDNSMessageData
)
2590 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg
->data
, end
, end
- msg
->data
);
2591 return mStatus_BadParamErr
;
2594 end
= putHINFO(m
, msg
, end
, authInfo
);
2595 if (!end
) { LogMsg("mDNSSendDNSMessage: putHINFO failed"); status
= mStatus_NoMemoryErr
; }
2598 tmpNumAdditionals
= msg
->h
.numAdditionals
;
2600 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2601 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
2602 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
2603 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
2604 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
2605 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
2606 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
2607 *ptr
++ = (mDNSu8
)(tmpNumAdditionals
>> 8);
2608 *ptr
++ = (mDNSu8
)(tmpNumAdditionals
& 0xFF);
2610 if (authInfo
) DNSDigest_SignMessage(msg
, &end
, authInfo
, 0); // DNSDigest_SignMessage operates on message in network byte order
2611 if (!end
) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status
= mStatus_NoMemoryErr
; }
2614 // Send the packet on the wire
2616 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, dst
, dstport
);
2619 mDNSu16 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2620 mDNSu8 lenbuf
[2] = { (mDNSu8
)(msglen
>> 8), (mDNSu8
)(msglen
& 0xFF) };
2621 long nsent
= mDNSPlatformWriteTCP(sock
, (char*)lenbuf
, 2); // Should do scatter/gather here -- this is probably going out as two packets
2622 if (nsent
!= 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent
, 2); status
= mStatus_ConnFailed
; }
2625 nsent
= mDNSPlatformWriteTCP(sock
, (char *)msg
, msglen
);
2626 if (nsent
!= msglen
) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent
, msglen
); status
= mStatus_ConnFailed
; }
2632 // Put all the integer values back the way they were before we return
2633 msg
->h
.numQuestions
= numQuestions
;
2634 msg
->h
.numAnswers
= numAnswers
;
2635 msg
->h
.numAuthorities
= numAuthorities
;
2637 // Dump the packet with the HINFO and TSIG
2638 if (mDNS_LogLevel
>= MDNS_LOG_VERBOSE_DEBUG
&& !mDNSOpaque16IsZero(msg
->h
.id
))
2640 ptr
= (mDNSu8
*)&msg
->h
.numAdditionals
;
2641 msg
->h
.numAdditionals
= (mDNSu16
)ptr
[0] << 8 | (mDNSu16
)ptr
[1];
2642 DumpPacket(m
, mDNStrue
, sock
&& (sock
->flags
& kTCPSocketFlags_UseTLS
) ? "TLS" : sock
? "TCP" : "UDP", dst
, dstport
, msg
, end
);
2645 // put the final integer value back the way it was
2646 msg
->h
.numAdditionals
= numAdditionals
;
2651 // ***************************************************************************
2652 #if COMPILER_LIKES_PRAGMA_MARK
2654 #pragma mark - RR List Management & Task Management
2657 mDNSexport
void mDNS_Lock_(mDNS
*const m
)
2659 // MUST grab the platform lock FIRST!
2660 mDNSPlatformLock(m
);
2662 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2663 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2664 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2665 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2667 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2669 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2674 // If this is an initial entry into the mDNSCore code, set m->timenow
2675 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2676 if (m
->mDNS_busy
== 0)
2679 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
2680 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2681 if (m
->timenow
== 0) m
->timenow
= 1;
2683 else if (m
->timenow
== 0)
2685 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
2686 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2687 if (m
->timenow
== 0) m
->timenow
= 1;
2690 if (m
->timenow_last
- m
->timenow
> 0)
2692 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
2693 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
2694 m
->timenow
= m
->timenow_last
;
2696 m
->timenow_last
= m
->timenow
;
2698 // Increment mDNS_busy so we'll recognise re-entrant calls
2702 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
2704 mDNSs32 e
= m
->timenow
+ 0x78000000;
2705 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) return(e
);
2706 if (m
->NewQuestions
)
2708 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
2709 else return(m
->timenow
);
2711 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
2712 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
2713 #ifndef UNICAST_DISABLED
2714 if (e
- m
->NextuDNSEvent
> 0) e
= m
->NextuDNSEvent
;
2715 if (e
- m
->NextScheduledNATOp
> 0) e
= m
->NextScheduledNATOp
;
2717 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
2721 if (m
->SuppressSending
)
2723 if (e
- m
->SuppressSending
> 0) e
= m
->SuppressSending
;
2727 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
2728 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
2729 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
2735 mDNSexport
void mDNS_Unlock_(mDNS
*const m
)
2737 // Decrement mDNS_busy
2740 // Check for locking failures
2742 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2744 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2749 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2750 if (m
->mDNS_busy
== 0)
2752 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
2753 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2757 // MUST release the platform lock LAST!
2758 mDNSPlatformUnlock(m
);
2761 // ***************************************************************************
2762 #if COMPILER_LIKES_PRAGMA_MARK
2764 #pragma mark - Specialized mDNS version of vsnprintf
2767 static const struct mDNSprintf_format
2769 unsigned leftJustify
: 1;
2770 unsigned forceSign
: 1;
2771 unsigned zeroPad
: 1;
2772 unsigned havePrecision
: 1;
2776 char sign
; // +, - or space
2777 unsigned int fieldWidth
;
2778 unsigned int precision
;
2779 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2781 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
2783 mDNSu32 nwritten
= 0;
2785 if (buflen
== 0) return(0);
2786 buflen
--; // Pre-reserve one space in the buffer for the terminating null
2787 if (buflen
== 0) goto exit
;
2789 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
2793 *sbuffer
++ = (char)c
;
2794 if (++nwritten
>= buflen
) goto exit
;
2798 unsigned int i
=0, j
;
2799 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
2800 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
2801 // The size needs to be enough for a 256-byte domain name plus some error text.
2802 #define mDNS_VACB_Size 300
2803 char mDNS_VACB
[mDNS_VACB_Size
];
2804 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
2805 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
2806 char *s
= mDNS_VACB_Lim
, *digits
;
2807 struct mDNSprintf_format F
= mDNSprintf_format_default
;
2809 while (1) // decode flags
2812 if (c
== '-') F
.leftJustify
= 1;
2813 else if (c
== '+') F
.forceSign
= 1;
2814 else if (c
== ' ') F
.sign
= ' ';
2815 else if (c
== '#') F
.altForm
++;
2816 else if (c
== '0') F
.zeroPad
= 1;
2820 if (c
== '*') // decode field width
2822 int f
= va_arg(arg
, int);
2823 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
2824 F
.fieldWidth
= (unsigned int)f
;
2829 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
2830 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
2833 if (c
== '.') // decode precision
2835 if ((c
= *++fmt
) == '*')
2836 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
2837 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
2838 F
.precision
= (10 * F
.precision
) + (c
- '0');
2839 F
.havePrecision
= 1;
2842 if (F
.leftJustify
) F
.zeroPad
= 0;
2845 switch (c
) // perform appropriate conversion
2848 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
2849 case 'l' : // fall through
2850 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
2852 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
2853 else n
= (unsigned long)va_arg(arg
, int);
2854 if (F
.hSize
) n
= (short) n
;
2855 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
2856 else if (F
.forceSign
) F
.sign
= '+';
2858 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2859 else n
= va_arg(arg
, unsigned int);
2860 if (F
.hSize
) n
= (unsigned short) n
;
2863 decimal
: if (!F
.havePrecision
)
2867 F
.precision
= F
.fieldWidth
;
2868 if (F
.sign
) --F
.precision
;
2870 if (F
.precision
< 1) F
.precision
= 1;
2872 if (F
.precision
> mDNS_VACB_Size
- 1)
2873 F
.precision
= mDNS_VACB_Size
- 1;
2874 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
2875 for (; i
< F
.precision
; i
++) *--s
= '0';
2876 if (F
.sign
) { *--s
= F
.sign
; i
++; }
2879 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2880 else n
= va_arg(arg
, unsigned int);
2881 if (F
.hSize
) n
= (unsigned short) n
;
2882 if (!F
.havePrecision
)
2884 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
2885 if (F
.precision
< 1) F
.precision
= 1;
2887 if (F
.precision
> mDNS_VACB_Size
- 1)
2888 F
.precision
= mDNS_VACB_Size
- 1;
2889 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
2890 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
2891 for (; i
< F
.precision
; i
++) *--s
= '0';
2895 unsigned char *a
= va_arg(arg
, unsigned char *);
2896 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2899 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2902 mDNSAddr
*ip
= (mDNSAddr
*)a
;
2905 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
2906 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
2907 default: F
.precision
= 0; break;
2910 if (F
.altForm
&& !F
.precision
)
2911 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "«ZERO ADDRESS»");
2912 else switch (F
.precision
)
2914 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
2915 a
[0], a
[1], a
[2], a
[3]); break;
2916 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
2917 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
2918 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
2919 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
2920 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
2921 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
2922 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify"
2923 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
2929 case 'p' : F
.havePrecision
= F
.lSize
= 1;
2931 case 'X' : digits
= "0123456789ABCDEF";
2933 case 'x' : digits
= "0123456789abcdef";
2934 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2935 else n
= va_arg(arg
, unsigned int);
2936 if (F
.hSize
) n
= (unsigned short) n
;
2937 if (!F
.havePrecision
)
2941 F
.precision
= F
.fieldWidth
;
2942 if (F
.altForm
) F
.precision
-= 2;
2944 if (F
.precision
< 1) F
.precision
= 1;
2946 if (F
.precision
> mDNS_VACB_Size
- 1)
2947 F
.precision
= mDNS_VACB_Size
- 1;
2948 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
2949 for (; i
< F
.precision
; i
++) *--s
= '0';
2950 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
2953 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
2955 case 's' : s
= va_arg(arg
, char *);
2956 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2957 else switch (F
.altForm
)
2960 if (!F
.havePrecision
) // C string
2964 while ((i
< F
.precision
) && s
[i
]) i
++;
2965 // Make sure we don't truncate in the middle of a UTF-8 character
2966 // If last character we got was any kind of UTF-8 multi-byte character,
2967 // then see if we have to back up.
2968 // This is not as easy as the similar checks below, because
2969 // here we can't assume it's safe to examine the *next* byte, so we
2970 // have to confine ourselves to working only backwards in the string.
2971 j
= i
; // Record where we got to
2972 // Now, back up until we find first non-continuation-char
2973 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
2974 // Now s[i-1] is the first non-continuation-char
2975 // and (j-i) is the number of continuation-chars we found
2976 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
2978 i
--; // Tentatively eliminate this start-char as well
2979 // Now (j-i) is the number of characters we're considering eliminating.
2980 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
2981 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
2982 // (with sign extension) then the result has to be 0xFE.
2983 // If this is right, then we reinstate the tentatively eliminated bytes.
2984 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
2988 case 1: i
= (unsigned char) *s
++; break; // Pascal string
2989 case 2: { // DNS label-sequence name
2990 unsigned char *a
= (unsigned char *)s
;
2991 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2992 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
2997 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
2998 if (s
+ *a
>= &mDNS_VACB
[254])
2999 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
3000 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3001 // so it's clear what's a literal dot and what's a label separator
3002 ConvertDomainLabelToCString((domainlabel
*)a
, buf
);
3003 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%s.", buf
);
3006 i
= (mDNSu32
)(s
- mDNS_VACB
);
3007 s
= mDNS_VACB
; // Reset s back to the start of the buffer
3011 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3012 if (F
.havePrecision
&& i
> F
.precision
)
3013 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3016 case 'n' : s
= va_arg(arg
, char *);
3017 if (F
.hSize
) * (short *) s
= (short)nwritten
;
3018 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
3019 else * (int *) s
= (int)nwritten
;
3022 default: s
= mDNS_VACB
;
3023 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
3025 case '%' : *sbuffer
++ = (char)c
;
3026 if (++nwritten
>= buflen
) goto exit
;
3030 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
3033 if (++nwritten
>= buflen
) goto exit
;
3034 } while (i
< --F
.fieldWidth
);
3036 // Make sure we don't truncate in the middle of a UTF-8 character.
3037 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3038 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3039 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3040 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3041 if (i
> buflen
- nwritten
)
3042 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
3043 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
3045 if (nwritten
>= buflen
) goto exit
;
3047 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
3050 if (++nwritten
>= buflen
) goto exit
;
3059 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
3065 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);