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.185 2007/10/10 20:22:03 cheshire
21 Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
22 apparently caused by trying to sign zero-length messages
24 Revision 1.184 2007/10/05 17:56:07 cheshire
25 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
27 Revision 1.183 2007/10/02 18:33:46 cheshire
28 Improved GetRRDisplayString to show all constituent strings within a text record
29 (up to the usual MaxMsg 120-character limit)
31 Revision 1.182 2007/10/01 19:45:01 cheshire
32 <rdar://problem/5514859> BTMM: Sometimes Back to My Mac autotunnel registrations are malformed
34 Revision 1.181 2007/10/01 18:36:53 cheshire
35 Yet another fix to finally get the DumpPacket RCODE display right
37 Revision 1.180 2007/09/29 21:30:38 cheshire
38 In DumpPacket/DumpRecords, show an error line if we run out of packet data
40 Revision 1.179 2007/09/29 20:44:56 cheshire
41 Fix error in DumpPacket where it was not displaying the RCODE field properly
43 Revision 1.178 2007/09/27 21:11:44 cheshire
44 Fixed spelling mistake: ROCDE -> RCODE
46 Revision 1.177 2007/09/27 18:51:26 cheshire
47 Improved DumpPacket to use "Zone/Prerequisites/Updates" nomenclature when displaying a DNS Update packet
49 Revision 1.176 2007/09/27 17:53:37 cheshire
50 Add display of RCODE and flags in DumpPacket output
52 Revision 1.175 2007/09/26 22:26:40 cheshire
53 Also show DNS query/response ID in DumpPacket output
55 Revision 1.174 2007/09/26 16:36:02 cheshire
56 In DumpPacket output, begin header line with "-- " to make it visually stand out better
58 Revision 1.173 2007/09/26 00:49:46 cheshire
59 Improve packet logging to show sent and received packets,
60 transport protocol (UDP/TCP/TLS) and source/destination address:port
62 Revision 1.172 2007/09/21 23:14:39 cheshire
63 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents in verbose debug mode
64 Changed DumpRecords to use LargeCacheRecord on the stack instead of the shared m->rec storage,
65 to eliminate "GetLargeResourceRecord: m->rec appears to be already in use" warnings
67 Revision 1.171 2007/09/21 21:12:36 cheshire
68 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
70 Revision 1.170 2007/09/07 21:16:58 cheshire
71 Add new symbol "NATPMPAnnouncementPort" (5350)
73 Revision 1.169 2007/08/30 00:31:20 cheshire
74 Improve "locking failure" debugging messages to show function name using __func__ macro
76 Revision 1.168 2007/08/28 23:58:42 cheshire
77 Rename HostTarget -> AutoTarget
79 Revision 1.167 2007/08/10 23:10:05 vazquez
80 <rdar://problem/5389850> mDNS: Reverse lookups of IPv6 link-local addresses always fail
82 Revision 1.166 2007/08/01 16:09:13 cheshire
83 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
85 Revision 1.165 2007/08/01 00:04:13 cheshire
86 <rdar://problem/5261696> Crash in tcpKQSocketCallback
87 Half-open TCP connections were not being cancelled properly
89 Revision 1.164 2007/07/27 20:48:43 cheshire
90 In DumpRecords(), include record TTL in output
92 Revision 1.163 2007/07/16 20:10:11 vazquez
93 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
94 Added SSDP port number
96 Revision 1.162 2007/07/10 01:59:33 cheshire
97 <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
98 Fixed GetPktLease to use shared m->rec instead of putting LargeCacheRecord on the stack
100 Revision 1.161 2007/07/06 18:56:26 cheshire
101 Check m->NextScheduledNATOp in GetNextScheduledEvent()
103 Revision 1.160 2007/06/29 00:06:42 vazquez
104 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
106 Revision 1.159 2007/06/28 21:17:17 cheshire
107 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
109 Revision 1.158 2007/05/25 00:25:43 cheshire
110 <rdar://problem/5227737> Need to enhance putRData to output all current known types
112 Revision 1.157 2007/05/23 00:32:15 cheshire
113 Don't treat uDNS responses as an entire RRSet (kDNSRecordTypePacketUniqueMask)
114 when received in a truncated UDP response
116 Revision 1.156 2007/05/15 00:29:00 cheshire
117 Print «ZERO ADDRESS» for %#a with a zero mDNSAddr
119 Revision 1.155 2007/05/07 22:07:47 cheshire
120 <rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types
122 Revision 1.154 2007/05/04 20:19:53 cheshire
123 Improve DumpPacket() output
125 Revision 1.153 2007/05/01 21:46:31 cheshire
126 Move GetLLQOptData/GetPktLease from uDNS.c into DNSCommon.c so that dnsextd can use them
128 Revision 1.152 2007/04/27 19:28:01 cheshire
129 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
130 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
131 -- it would start a query and then quickly cancel it, and then when
132 StartGetZoneData completed, it had a dangling pointer and crashed.)
134 Revision 1.151 2007/04/26 13:35:25 cheshire
135 Add kDNSType_SOA case in SameRDataBody, and a comment in GetLargeResourceRecord about why this is important
137 Revision 1.150 2007/04/24 00:17:33 cheshire
138 Made LocateLLQOptData guard against packets with bogus numAdditionals value
140 Revision 1.149 2007/04/23 21:43:00 cheshire
141 Remove debugging check
143 Revision 1.148 2007/04/23 04:55:29 cheshire
144 Add some defensive null pointer checks
146 Revision 1.147 2007/04/22 20:18:10 cheshire
147 Add comment about mDNSRandom()
149 Revision 1.146 2007/04/22 06:02:02 cheshire
150 <rdar://problem/4615977> Query should immediately return failure when no server
152 Revision 1.145 2007/04/20 21:17:24 cheshire
153 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
155 Revision 1.144 2007/04/19 18:02:43 cheshire
156 <rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit
158 Revision 1.143 2007/04/16 21:53:49 cheshire
159 Improve display of negative cache entries
161 Revision 1.142 2007/04/05 22:55:35 cheshire
162 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
164 Revision 1.141 2007/04/04 01:33:11 cheshire
165 <rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record
166 Overly defensive code was zeroing too much of the AuthRecord structure
168 Revision 1.140 2007/04/03 19:37:58 cheshire
169 Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918()
171 Revision 1.139 2007/04/03 19:18:39 cheshire
172 Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly
174 Revision 1.138 2007/03/28 21:14:08 cheshire
175 The rrclass field of an OPT pseudo-RR holds the sender's UDP payload size
177 Revision 1.137 2007/03/28 20:59:26 cheshire
178 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
180 Revision 1.136 2007/03/28 15:56:37 cheshire
181 <rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output
183 Revision 1.135 2007/03/28 01:20:05 cheshire
184 <rdar://problem/4883206> Improve/create logging for secure browse
186 Revision 1.134 2007/03/27 23:25:35 cheshire
187 Fix error caching SOA records
188 (cache entry was size of wire-format packed data, not size of in-memory structure)
190 Revision 1.133 2007/03/26 22:55:45 cheshire
191 Add OPT and TSIG to list of types DNSTypeName() knows about
193 Revision 1.132 2007/03/22 18:31:48 cheshire
194 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
196 Revision 1.131 2007/03/21 21:55:20 cheshire
197 <rdar://problem/5069688> Hostname gets ; or : which are illegal characters
198 Error in AppendLabelSuffix() for numbers close to the 32-bit limit
200 Revision 1.130 2007/03/21 19:23:37 cheshire
201 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
202 Make check less strict so we don't break Bonjour Browser
204 Revision 1.129 2007/03/21 01:00:45 cheshire
205 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
206 DeconstructServiceName() needs to be more defensive about what it considers legal
208 Revision 1.128 2007/03/21 00:30:02 cheshire
209 <rdar://problem/4789455> Multiple errors in DNameList-related code
211 Revision 1.127 2007/03/20 17:07:15 cheshire
212 Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket"
214 Revision 1.126 2007/03/10 03:26:44 cheshire
215 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
217 Revision 1.125 2007/03/07 00:08:58 cheshire
218 <rdar://problem/4347550> Don't allow hyphens at start of service type
220 Revision 1.124 2007/01/19 18:04:05 cheshire
221 For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT
223 Revision 1.123 2007/01/10 22:45:51 cheshire
224 Cast static strings to "(const domainname*)", not "(domainname*)"
226 Revision 1.122 2007/01/06 00:47:35 cheshire
227 Improve GetRRDisplayString to indicate when record has zero-length rdata
229 Revision 1.121 2007/01/05 08:30:39 cheshire
230 Trim excessive "$Log" checkin history from before 2006
231 (checkin history still available via "cvs log ..." of course)
233 Revision 1.120 2007/01/05 05:23:00 cheshire
234 Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd)
236 Revision 1.119 2007/01/05 04:30:16 cheshire
237 Change a couple of "(domainname *)" casts to "(const domainname *)"
239 Revision 1.118 2007/01/04 20:21:59 cheshire
240 <rdar://problem/4720673> uDNS: Need to start caching unicast records
241 Don't return multicast answers in response to unicast questions
243 Revision 1.117 2006/12/22 20:59:49 cheshire
244 <rdar://problem/4742742> Read *all* DNS keys from keychain,
245 not just key for the system-wide default registration domain
247 Revision 1.116 2006/12/21 00:04:07 cheshire
248 To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord()
250 Revision 1.115 2006/12/20 04:07:34 cheshire
251 Remove uDNS_info substructure from AuthRecord_struct
253 Revision 1.114 2006/12/19 22:40:04 cheshire
254 Fix compiler warnings
256 Revision 1.113 2006/12/19 02:21:08 cheshire
257 Delete spurious spaces
259 Revision 1.112 2006/12/15 20:42:10 cheshire
260 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
261 Additional defensive coding in GetLargeResourceRecord() to reject apparently-valid
262 rdata that actually runs past the end of the received packet data.
264 Revision 1.111 2006/12/15 19:09:57 cheshire
265 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
266 Made DomainNameLength() more defensive by adding a limit parameter, so it can be
267 safely used to inspect potentially malformed data received from external sources.
268 Without this, a domain name that starts off apparently valid, but extends beyond the end of
269 the received packet data, could have appeared valid if the random bytes are already in memory
270 beyond the end of the packet just happened to have reasonable values (e.g. all zeroes).
272 Revision 1.110 2006/11/18 05:01:30 cheshire
273 Preliminary support for unifying the uDNS and mDNS code,
274 including caching of uDNS answers
276 Revision 1.109 2006/11/10 00:54:14 cheshire
277 <rdar://problem/4816598> Changing case of Computer Name doesn't work
279 Revision 1.108 2006/10/05 23:11:18 cheshire
280 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
282 Revision 1.107 2006/09/15 21:20:14 cheshire
283 Remove uDNS_info substructure from mDNS_struct
285 Revision 1.106 2006/08/14 23:24:22 cheshire
286 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
288 Revision 1.105 2006/07/15 02:01:28 cheshire
289 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
290 Fix broken "empty string" browsing
292 Revision 1.104 2006/07/05 23:09:13 cheshire
293 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
294 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
296 Revision 1.103 2006/06/29 07:42:14 cheshire
297 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
299 Revision 1.102 2006/06/22 19:49:11 cheshire
300 Added (commented out) definitions for the LLMNR UDP port and multicast addresses
302 Revision 1.101 2006/06/15 21:35:15 cheshire
303 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
304 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
306 Revision 1.100 2006/06/08 22:58:46 cheshire
307 <rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
309 Revision 1.99 2006/05/18 01:32:33 cheshire
310 <rdar://problem/4472706> iChat: Lost connection with Bonjour
311 (mDNSResponder insufficiently defensive against malformed browsing PTR responses)
313 Revision 1.98 2006/03/19 17:00:58 cheshire
314 Define symbol MaxMsg instead of using hard-coded constant value '80'
316 Revision 1.97 2006/03/18 21:47:56 cheshire
317 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
319 Revision 1.96 2006/03/10 21:51:42 cheshire
320 <rdar://problem/4111464> After record update, old record sometimes remains in cache
321 Split out SameRDataBody() into a separate routine so it can be called from other code
323 Revision 1.95 2006/03/08 22:43:11 cheshire
324 Use "localdomain" symbol instead of literal string
326 Revision 1.94 2006/03/02 21:59:55 cheshire
327 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
328 Improve sanity checks & debugging support in GetLargeResourceRecord()
330 Revision 1.93 2006/03/02 20:30:47 cheshire
331 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
335 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
336 #define mDNS_InstantiateInlines 1
337 #include "DNSCommon.h"
339 // Disable certain benign warnings with Microsoft compilers
340 #if (defined(_MSC_VER))
341 // Disable "conditional expression is constant" warning for debug macros.
342 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
343 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
344 #pragma warning(disable:4127)
345 // Disable "array is too small to include a terminating null character" warning
346 // -- domain labels have an initial length byte, not a terminating null character
347 #pragma warning(disable:4295)
350 // ***************************************************************************
351 #if COMPILER_LIKES_PRAGMA_MARK
352 #pragma mark - Program Constants
355 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
356 mDNSexport
const mDNSv4Addr zerov4Addr
= { { 0 } };
357 mDNSexport
const mDNSv6Addr zerov6Addr
= { { 0 } };
358 mDNSexport
const mDNSEthAddr zeroEthAddr
= { { 0 } };
359 mDNSexport
const mDNSv4Addr onesIPv4Addr
= { { 255, 255, 255, 255 } };
360 mDNSexport
const mDNSv6Addr onesIPv6Addr
= { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
361 mDNSexport
const mDNSAddr zeroAddr
= { mDNSAddrType_None
, {{{ 0 }}} };
363 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
364 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
365 mDNSexport
const mDNSInterfaceID mDNSInterface_Unicast
= (mDNSInterfaceID
)2;
367 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
368 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
369 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
370 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
371 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
372 // with Microsoft's LLMNR client code.
374 #define SSDPPortAsNumber 1900
376 #define UnicastDNSPortAsNumber 53
377 #define NATPMPAnnouncementPortAsNumber 5350
378 #define NATPMPPortAsNumber 5351
379 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
380 #define MulticastDNSPortAsNumber 5353
381 #define LoopbackIPCPortAsNumber 5354
382 //#define MulticastDNSPortAsNumber 5355 // LLMNR
384 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
385 #define PrivateDNSPortAsNumber 5533
387 mDNSexport
const mDNSIPPort SSDPPort
= { { SSDPPortAsNumber
>> 8, SSDPPortAsNumber
& 0xFF } };
389 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
390 mDNSexport
const mDNSIPPort NATPMPAnnouncementPort
= { { NATPMPAnnouncementPortAsNumber
>> 8, NATPMPAnnouncementPortAsNumber
& 0xFF } };
391 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
392 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
393 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
394 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
396 mDNSexport
const mDNSIPPort NSIPCPort
= { { NSIPCPortAsNumber
>> 8, NSIPCPortAsNumber
& 0xFF } };
397 mDNSexport
const mDNSIPPort PrivateDNSPort
= { { PrivateDNSPortAsNumber
>> 8, PrivateDNSPortAsNumber
& 0xFF } };
399 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
400 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
401 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
402 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
403 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
405 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
406 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
407 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
408 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
409 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
410 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
412 mDNSexport
const mDNSOpaque64 zeroOpaque64
= { { 0 } };
414 // ***************************************************************************
415 #if COMPILER_LIKES_PRAGMA_MARK
417 #pragma mark - General Utility Functions
420 // return true for RFC1918 private addresses
421 mDNSexport mDNSBool
mDNSv4AddrIsRFC1918(mDNSv4Addr
*addr
)
423 return ((addr
->b
[0] == 10) || // 10/8 prefix
424 (addr
->b
[0] == 172 && (addr
->b
[1] & 0xF0) == 16) || // 172.16/12
425 (addr
->b
[0] == 192 && addr
->b
[1] == 168)); // 192.168/16
428 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
430 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
434 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
436 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
437 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
440 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
442 mDNSu32 slot
, used
= 0;
445 FORALL_CACHERECORDS(slot
, cg
, rr
)
446 if (rr
->resrec
.InterfaceID
== id
) used
++;
450 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
454 case kDNSType_A
: return("Addr");
455 case kDNSType_NS
: return("NS");
456 case kDNSType_CNAME
:return("CNAME");
457 case kDNSType_SOA
: return("SOA");
458 case kDNSType_NULL
: return("NULL");
459 case kDNSType_PTR
: return("PTR");
460 case kDNSType_HINFO
:return("HINFO");
461 case kDNSType_TXT
: return("TXT");
462 case kDNSType_AAAA
: return("AAAA");
463 case kDNSType_SRV
: return("SRV");
464 case kDNSType_OPT
: return("OPT");
465 case kDNSType_TSIG
: return("TSIG");
466 case kDNSQType_ANY
: return("ANY");
468 static char buffer
[16];
469 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
475 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
476 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
477 // long as this routine is only used for debugging messages, it probably isn't a big problem.
478 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*rr
, RDataBody
*rd
, char *buffer
)
480 #define Max (MaxMsg-1)
482 mDNSu32 length
= mDNS_snprintf(buffer
, Max
, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
483 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) return(buffer
);
484 if (!rr
->rdlength
) { mDNS_snprintf(buffer
+length
, Max
-length
, "<< ZERO RDATA LENGTH >>"); return(buffer
); }
488 case kDNSType_A
: mDNS_snprintf(buffer
+length
, Max
-length
, "%.4a", &rd
->ipv4
); break;
490 case kDNSType_NS
: // Same as PTR
491 case kDNSType_CNAME
:// Same as PTR
492 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, Max
-length
, "%##s", rd
->name
.c
); break;
494 case kDNSType_SOA
: mDNS_snprintf(buffer
+length
, Max
-length
, "%##s %##s %d %d %d %d %d",
495 rd
->soa
.mname
.c
, rd
->soa
.rname
.c
,
496 rd
->soa
.serial
, rd
->soa
.refresh
, rd
->soa
.retry
, rd
->soa
.expire
, rd
->soa
.min
);
499 case kDNSType_HINFO
:// Display this the same as TXT (show all constituent string)
501 mDNSu8
*t
= rd
->txt
.c
;
502 while (t
< rd
->txt
.c
+ rr
->rdlength
)
504 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "%s%#s", t
> rd
->txt
.c
? "¦" : "", t
);
509 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, Max
-length
, "%.16a", &rd
->ipv6
); break;
510 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, Max
-length
, "%u %u %u %##s",
511 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
512 case kDNSType_OPT
: length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "%d Len %d ", rd
->opt
.opt
, rd
->opt
.optlen
);
513 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Max UDP %d ", rr
->rrclass
);
514 if (rd
->opt
.opt
== kDNSOpt_LLQ
)
516 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Vers %d ", rd
->opt
.OptData
.llq
.vers
);
517 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Op %d ", rd
->opt
.OptData
.llq
.llqOp
);
518 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Err/Port %d ", rd
->opt
.OptData
.llq
.err
);
519 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "ID %08X%08X ", rd
->opt
.OptData
.llq
.id
.l
[0], rd
->opt
.OptData
.llq
.id
.l
[1]);
520 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Lease %d", rd
->opt
.OptData
.llq
.llqlease
);
522 else if (rd
->opt
.opt
== kDNSOpt_Lease
)
523 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "kDNSOpt_Lease Lease %d", rd
->opt
.OptData
.updatelease
);
525 length
+= mDNS_snprintf(buffer
+length
, Max
-length
, "Unknown opt %d", rd
->opt
.opt
);
527 default: mDNS_snprintf(buffer
+length
, Max
-length
, "RDLen %d: %s", rr
->rdlength
, rd
->data
);
528 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
529 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
= '.';
535 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
) // Returns pseudo-random result from zero to max inclusive
537 static mDNSu32 seed
= 0;
543 seed
= mDNSPlatformRandomSeed(); // Pick an initial seed
544 for (i
=0; i
<100; i
++) seed
= seed
* 21 + 1; // And mix it up a bit
546 while (mask
< max
) mask
= (mask
<< 1) | 1;
547 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
548 return (seed
& mask
);
551 mDNSexport mDNSu32
mDNSRandomFromFixedSeed(mDNSu32 seed
, mDNSu32 max
)
554 while (mask
< max
) mask
= (mask
<< 1) | 1;
555 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
556 return (seed
& mask
);
559 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
561 if (ip1
->type
== ip2
->type
)
565 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
566 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
567 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
573 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
577 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip
->ip
.v4
, AllDNSLinkGroup_v4
.ip
.v4
));
578 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip
->ip
.v6
, AllDNSLinkGroup_v6
.ip
.v6
));
579 default: return(mDNSfalse
);
583 // ***************************************************************************
584 #if COMPILER_LIKES_PRAGMA_MARK
586 #pragma mark - Domain Name Utility Functions
589 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
592 const int len
= *a
++;
594 if (len
> MAX_DOMAIN_LABEL
)
595 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
597 if (len
!= *b
++) return(mDNSfalse
);
598 for (i
=0; i
<len
; i
++)
602 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
603 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
604 if (ac
!= bc
) return(mDNSfalse
);
609 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
611 const mDNSu8
* a
= d1
->c
;
612 const mDNSu8
* b
= d2
->c
;
613 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
617 if (a
+ 1 + *a
>= max
)
618 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
619 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
627 mDNSexport mDNSBool
SameDomainNameCS(const domainname
*const d1
, const domainname
*const d2
)
629 mDNSu16 l1
= DomainNameLength(d1
);
630 mDNSu16 l2
= DomainNameLength(d2
);
631 return(l1
<= MAX_DOMAIN_NAME
&& l1
== l2
&& mDNSPlatformMemSame(d1
, d2
, l1
));
634 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
636 // Domains that are defined to be resolved via link-local multicast are:
637 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
638 static const domainname
*nL
= (const domainname
*)"\x5" "local";
639 static const domainname
*nR
= (const domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
640 static const domainname
*n8
= (const domainname
*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
641 static const domainname
*n9
= (const domainname
*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
642 static const domainname
*nA
= (const domainname
*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
643 static const domainname
*nB
= (const domainname
*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
645 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
; // Top-level domain, second-level domain, etc.
646 d1
= d2
= d3
= d4
= d5
= mDNSNULL
;
649 d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
650 d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]);
653 if (d1
&& SameDomainName(d1
, nL
)) return(mDNStrue
);
654 if (d4
&& SameDomainName(d4
, nR
)) return(mDNStrue
);
655 if (d5
&& SameDomainName(d5
, n8
)) return(mDNStrue
);
656 if (d5
&& SameDomainName(d5
, n9
)) return(mDNStrue
);
657 if (d5
&& SameDomainName(d5
, nA
)) return(mDNStrue
);
658 if (d5
&& SameDomainName(d5
, nB
)) return(mDNStrue
);
662 // Returns length of a domain name INCLUDING the byte for the final null label
663 // e.g. for the root label "." it returns one
664 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
665 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
666 // If the given domainname is invalid, result is 256 (MAX_DOMAIN_NAME+1)
667 mDNSexport mDNSu16
DomainNameLengthLimit(const domainname
*const name
, const mDNSu8
*limit
)
669 const mDNSu8
*src
= name
->c
;
670 while (src
< limit
&& *src
<= MAX_DOMAIN_LABEL
)
672 if (*src
== 0) return((mDNSu16
)(src
- name
->c
+ 1));
675 return(MAX_DOMAIN_NAME
+1);
678 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
679 // for the final null label, e.g. for the root label "." it returns one.
680 // E.g. for the FQDN "foo.com." it returns 9
681 // (length, three data bytes, length, three more data bytes, final zero).
682 // In the case where a parent domain name is provided, and the given name is a child
683 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
684 // of the child name, plus TWO bytes for the compression pointer.
685 // E.g. for the name "foo.com." with parent "com.", it returns 6
686 // (length, three data bytes, two-byte compression pointer).
687 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
689 const mDNSu8
*src
= name
->c
;
690 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
693 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
694 if (parent
&& SameDomainName((const domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
696 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
698 return((mDNSu16
)(src
- name
->c
+ 1));
701 // CountLabels() returns number of labels in name, excluding final root label
702 // (e.g. for "apple.com." CountLabels returns 2.)
703 mDNSexport
int CountLabels(const domainname
*d
)
707 for (ptr
= d
->c
; *ptr
; ptr
= ptr
+ ptr
[0] + 1) count
++;
711 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
712 // returning a pointer to the suffix with 'skip' labels removed.
713 mDNSexport
const domainname
*SkipLeadingLabels(const domainname
*d
, int skip
)
715 while (skip
> 0 && d
->c
[0]) { d
= (const domainname
*)(d
->c
+ 1 + d
->c
[0]); skip
--; }
719 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
720 // The C string contains the label as-is, with no escaping, etc.
721 // Any dots in the name are literal dots, not label separators
722 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
723 // in the domainname bufer (i.e. the next byte after the terminating zero).
724 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
725 // AppendLiteralLabelString returns mDNSNULL.
726 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
728 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
729 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
730 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
731 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
732 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
734 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
735 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
736 *ptr
++ = 0; // Put the null root label on the end
737 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
738 else return(ptr
); // Success: return new value of ptr
741 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
742 // The C string is in conventional DNS syntax:
743 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
744 // If successful, AppendDNSNameString returns a pointer to the next unused byte
745 // in the domainname bufer (i.e. the next byte after the terminating zero).
746 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
747 // AppendDNSNameString returns mDNSNULL.
748 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
750 const char *cstr
= cstring
;
751 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
752 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
753 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
755 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
756 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
757 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
759 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
760 if (c
== '\\') // If escape character, check next character
762 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
763 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
764 { // If three decimal digits,
765 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
766 int v1
= cstr
[ 0] - '0';
767 int v2
= cstr
[ 1] - '0';
768 int val
= v0
* 100 + v1
* 10 + v2
;
769 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
772 *ptr
++ = c
; // Write the character
774 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
775 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
777 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
780 *ptr
++ = 0; // Put the null root label on the end
781 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
782 else return(ptr
); // Success: return new value of ptr
785 // AppendDomainLabel appends a single label to a name.
786 // If successful, AppendDomainLabel returns a pointer to the next unused byte
787 // in the domainname bufer (i.e. the next byte after the terminating zero).
788 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
789 // AppendDomainLabel returns mDNSNULL.
790 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
793 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
795 // Check label is legal
796 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
798 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
799 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
801 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
802 *ptr
++ = 0; // Put the null root label on the end
806 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
808 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
809 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
810 const mDNSu8
* src
= append
->c
;
814 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
815 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
816 *ptr
= 0; // Put the null root label on the end
822 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
823 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
824 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
825 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
826 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
827 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
828 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
830 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
831 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
832 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
833 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
834 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
837 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
838 // The C string is in conventional DNS syntax:
839 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
840 // If successful, MakeDomainNameFromDNSNameString 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 // MakeDomainNameFromDNSNameString returns mDNSNULL.
844 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
846 name
->c
[0] = 0; // Make an empty domain name
847 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
850 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
852 const mDNSu8
* src
= label
->c
; // Domain label we're reading
853 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
854 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
855 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
856 while (src
< end
) // While we have characters in the label
861 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
862 *ptr
++ = esc
; // Output escape character
863 else if (c
<= ' ') // If non-printing ascii,
864 { // Output decimal escape sequence
866 *ptr
++ = (char) ('0' + (c
/ 100) );
867 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
868 c
= (mDNSu8
)('0' + (c
) % 10);
871 *ptr
++ = (char)c
; // Copy the character
873 *ptr
= 0; // Null-terminate the string
874 return(ptr
); // and return
877 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
878 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
880 const mDNSu8
*src
= name
->c
; // Domain name we're reading
881 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
883 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
885 while (*src
) // While more characters in the domain name
887 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
888 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
889 if (!ptr
) return(mDNSNULL
);
891 *ptr
++ = '.'; // Write the dot after the label
894 *ptr
++ = 0; // Null-terminate the string
895 return(ptr
); // and return
899 // Host names must start with a letter, end with a letter or digit,
900 // and have as interior characters only letters, digits, and hyphen.
901 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
903 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
905 const mDNSu8
* src
= &UTF8Name
[1];
906 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
907 mDNSu8
* ptr
= &hostlabel
->c
[1];
908 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
911 // Delete apostrophes from source name
912 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
913 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
914 { src
+= 3; continue; } // Unicode curly apostrophe
917 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
918 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
922 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
923 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
926 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
927 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
928 ((X)[4] | 0x20) == 'p')
930 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
931 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
934 mDNSu8
*dst
= fqdn
->c
;
936 const char *errormsg
;
938 // In the case where there is no name (and ONLY in that case),
939 // a single-label subtype is allowed as the first label of a three-part "type"
942 const mDNSu8
*s0
= type
->c
;
943 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
945 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
946 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
948 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
949 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
951 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
952 src
= s0
; // Copy the first label
954 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
955 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
956 type
= (const domainname
*)s1
;
958 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
959 // For these queries, we retract the "._sub" we just added between the subtype and the main type
960 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
961 if (SameDomainName((domainname
*)s0
, (const domainname
*)"\x09_services\x07_dns-sd\x04_udp"))
962 dst
-= sizeof(SubTypeLabel
);
968 if (name
&& name
->c
[0])
970 src
= name
->c
; // Put the service name into the domain name
972 if (len
>= 0x40) { errormsg
= "Service instance name too long"; goto fail
; }
973 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
976 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
978 src
= type
->c
; // Put the service type into the domain name
980 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
)))
982 errormsg
= "Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
985 if (src
[1] != '_') { errormsg
= "Application protocol name must begin with underscore"; goto fail
; }
986 for (i
=2; i
<=len
; i
++)
988 // Letters and digits are allowed anywhere
989 if (mdnsIsLetter(src
[i
]) || mdnsIsDigit(src
[i
])) continue;
990 // Hyphens are only allowed as interior characters
991 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
992 // with the same rule as hyphens
993 if ((src
[i
] == '-' || src
[i
] == '_') && i
> 2 && i
< len
) continue;
994 errormsg
= "Application protocol name must contain only letters, digits, and hyphens"; goto fail
;
996 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
999 if (!ValidTransportProtocol(src
)) { errormsg
= "Transport protocol name must be _udp or _tcp"; goto fail
; }
1000 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1002 if (*src
) { errormsg
= "Service type must have only two labels"; goto fail
; }
1005 if (!domain
->c
[0]) { errormsg
= "Service domain must be non-empty"; goto fail
; }
1006 if (SameDomainName(domain
, (const domainname
*)"\x05" "local" "\x04" "arpa"))
1007 { errormsg
= "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
1008 dst
= AppendDomainName(fqdn
, domain
);
1009 if (!dst
) { errormsg
= "Service domain too long"; goto fail
; }
1013 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
1017 // A service name has the form: instance.application-protocol.transport-protocol.domain
1018 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1019 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1020 // However, if the given FQDN doesn't contain at least three labels,
1021 // DeconstructServiceName will reject it and return mDNSfalse.
1022 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
1023 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
1026 const mDNSu8
*src
= fqdn
->c
;
1027 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
1030 dst
= name
->c
; // Extract the service name
1032 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
1033 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
1034 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1036 dst
= type
->c
; // Extract the service type
1038 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
1039 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
1040 if (src
[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse
); }
1041 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1044 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
1045 // Can't do this check right now, until Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1046 //if (!ValidTransportProtocol(src))
1047 // { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
1048 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1049 *dst
++ = 0; // Put terminator on the end of service type
1051 dst
= domain
->c
; // Extract the service domain
1056 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
1057 if (src
+ 1 + len
+ 1 >= max
)
1058 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
1059 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
1061 *dst
++ = 0; // Put the null root label on the end
1067 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1068 // 10xxxxxx is a continuation byte of a multi-byte character
1069 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1070 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1071 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1072 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1073 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1075 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1076 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1077 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1078 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1079 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1081 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
1085 mDNSu8 c1
= string
[max
]; // First byte after cut point
1086 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : (mDNSu8
)0xB0; // Second byte after cut point
1087 length
= max
; // Trim length down
1090 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1091 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1092 // If so, then we continue to chop more bytes until we get to a legal chop point.
1093 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1094 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1095 if (!continuation
&& !secondsurrogate
) break;
1097 c1
= string
[--length
];
1099 // Having truncated characters off the end of our string, also cut off any residual white space
1100 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1105 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1106 // name ends in "-nnn", where n is some decimal number.
1107 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1109 mDNSu16 l
= name
->c
[0];
1113 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1114 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1115 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1117 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1118 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1122 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1123 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1125 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1126 return (name
->c
[l
] == '-');
1130 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1131 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1132 // from the suffix that was removed.
1133 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1135 mDNSu32 val
= 0, multiplier
= 1;
1137 // Chop closing parentheses from RichText suffix
1138 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1140 // Get any existing numerical suffix off the name
1141 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
1142 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1144 // Chop opening parentheses or dash from suffix
1147 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1151 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1157 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1158 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1159 mDNSexport
void AppendLabelSuffix(domainlabel
*const name
, mDNSu32 val
, const mDNSBool RichText
)
1161 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1162 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1164 // Truncate trailing spaces from RichText names
1165 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1167 while (divisor
< 0xFFFFFFFFUL
/10 && val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1169 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1171 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1172 else { name
->c
[++name
->c
[0]] = '-'; }
1176 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1181 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1184 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1188 if (LabelContainsSuffix(name
, RichText
))
1189 val
= RemoveLabelSuffix(name
, RichText
);
1191 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1192 // If existing suffix in the range 2-9, increment it.
1193 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1194 // so add a random increment to improve the chances of finding an available name next time.
1195 if (val
== 0) val
= 2;
1196 else if (val
< 10) val
++;
1197 else val
+= 1 + mDNSRandom(99);
1199 AppendLabelSuffix(name
, val
, RichText
);
1202 // ***************************************************************************
1203 #if COMPILER_LIKES_PRAGMA_MARK
1205 #pragma mark - Resource Record Utility Functions
1208 // Set up a AuthRecord with sensible default values.
1209 // These defaults may be overwritten with new values before mDNS_Register is called
1210 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
1211 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
1213 // Don't try to store a TTL bigger than we can represent in platform time units
1214 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
1215 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
1216 else if (ttl
== 0) // And Zero TTL is illegal
1217 ttl
= DefaultTTLforRRType(rrtype
);
1219 // Field Group 1: The actual information pertaining to this resource record
1220 rr
->resrec
.RecordType
= RecordType
;
1221 rr
->resrec
.InterfaceID
= InterfaceID
;
1222 rr
->resrec
.name
= &rr
->namestorage
;
1223 rr
->resrec
.rrtype
= rrtype
;
1224 rr
->resrec
.rrclass
= kDNSClass_IN
;
1225 rr
->resrec
.rroriginalttl
= ttl
;
1226 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1227 // rr->resrec.rdestimate = set in mDNS_Register_internal
1228 // rr->resrec.rdata = MUST be set by client
1231 rr
->resrec
.rdata
= RDataStorage
;
1234 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1235 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1238 // Field Group 2: Persistent metadata for Authoritative Records
1239 rr
->Additional1
= mDNSNULL
;
1240 rr
->Additional2
= mDNSNULL
;
1241 rr
->DependentOn
= mDNSNULL
;
1242 rr
->RRSet
= mDNSNULL
;
1243 rr
->RecordCallback
= Callback
;
1244 rr
->RecordContext
= Context
;
1246 rr
->AutoTarget
= Target_Manual
;
1247 rr
->AllowRemoteQuery
= mDNSfalse
;
1248 rr
->ForceMCast
= mDNSfalse
;
1250 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1251 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1253 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1254 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1255 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1256 rr
->state
= regState_Zero
;
1262 rr
->UpdateServer
= zeroAddr
;
1263 rr
->UpdatePort
= zeroIPPort
;
1268 rr
->InFlightRData
= 0;
1269 rr
->InFlightRDLen
= 0;
1270 rr
->QueuedRData
= 0;
1271 rr
->QueuedRDLen
= 0;
1273 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
1276 mDNSexport mDNSu32
RDataHashValue(mDNSu16
const rdlength
, const RDataBody
*const rdb
)
1280 for (i
=0; i
+1 < rdlength
; i
+=2)
1282 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1283 sum
= (sum
<<3) | (sum
>>29);
1287 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1292 // r1 has to be a full ResourceRecord including rrtype and rdlength
1293 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1294 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1299 case kDNSType_CNAME
:
1301 case kDNSType_DNAME
:return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->name
));
1303 case kDNSType_SOA
: return(mDNSBool
)( r1
->rdata
->u
.soa
.serial
== r2
->soa
.serial
&&
1304 r1
->rdata
->u
.soa
.refresh
== r2
->soa
.refresh
&&
1305 r1
->rdata
->u
.soa
.retry
== r2
->soa
.retry
&&
1306 r1
->rdata
->u
.soa
.expire
== r2
->soa
.expire
&&
1307 r1
->rdata
->u
.soa
.min
== r2
->soa
.min
&&
1308 SameDomainName(&r1
->rdata
->u
.soa
.mname
, &r2
->soa
.mname
) &&
1309 SameDomainName(&r1
->rdata
->u
.soa
.rname
, &r2
->soa
.rname
));
1312 case kDNSType_AFSDB
:
1314 case kDNSType_KX
: return(mDNSBool
)( r1
->rdata
->u
.mx
.preference
== r2
->mx
.preference
&&
1315 SameDomainName(&r1
->rdata
->u
.mx
.exchange
, &r2
->mx
.exchange
));
1317 case kDNSType_RP
: return(mDNSBool
)( SameDomainName(&r1
->rdata
->u
.rp
.mbox
, &r2
->rp
.mbox
) &&
1318 SameDomainName(&r1
->rdata
->u
.rp
.txt
, &r2
->rp
.txt
));
1320 case kDNSType_PX
: return(mDNSBool
)( r1
->rdata
->u
.px
.preference
== r2
->px
.preference
&&
1321 SameDomainName(&r1
->rdata
->u
.px
.map822
, &r2
->px
.map822
) &&
1322 SameDomainName(&r1
->rdata
->u
.px
.mapx400
, &r2
->px
.mapx400
));
1324 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->srv
.priority
&&
1325 r1
->rdata
->u
.srv
.weight
== r2
->srv
.weight
&&
1326 mDNSSameIPPort(r1
->rdata
->u
.srv
.port
, r2
->srv
.port
) &&
1327 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->srv
.target
));
1329 case kDNSType_OPT
: // Okay to use memory compare because there are no 'holes' in the in-memory representation
1331 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->data
, r1
->rdlength
));
1335 mDNSexport mDNSBool
SameRData(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
1337 if (r1
->rrtype
!= r2
->rrtype
) return(mDNSfalse
);
1338 if (r1
->rdlength
!= r2
->rdlength
) return(mDNSfalse
);
1339 if (r1
->rdatahash
!= r2
->rdatahash
) return(mDNSfalse
);
1340 return(SameRDataBody(r1
, &r2
->rdata
->u
));
1343 mDNSexport mDNSBool
SameResourceRecord(ResourceRecord
*r1
, ResourceRecord
*r2
)
1345 return (r1
->namehash
== r2
->namehash
&&
1346 r1
->rrtype
== r2
->rrtype
&&
1347 SameDomainName(r1
->name
, r2
->name
) &&
1351 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1352 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1353 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1354 // because it has to check all the way to the end of the names to be sure.
1355 // In cases where we know in advance that the names match it's especially advantageous to skip the
1356 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1358 mDNSexport mDNSBool
SameNameRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1360 if (rr
->InterfaceID
&&
1361 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1362 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1364 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1365 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1367 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1368 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1369 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1371 #if VerifySameNameAssumptions
1372 if (rr
->namehash
!= q
->qnamehash
|| !SameDomainName(rr
->name
, &q
->qname
))
1374 LogMsg("Bogus SameNameRecordAnswersQuestion call: RR %##s does not match Q %##s", rr
->name
->c
, q
->qname
.c
);
1382 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1384 if (rr
->InterfaceID
&&
1385 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1386 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1388 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1389 if (rr
->InterfaceID
&& !mDNSOpaque16IsZero(q
->TargetQID
)) return(mDNSfalse
);
1391 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1392 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1393 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1394 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1397 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1399 const RDataBody
*rd
= &rr
->rdata
->u
;
1400 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1401 if (rr
->rrclass
== kDNSQClass_ANY
) return(rr
->rdlength
); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1402 else switch (rr
->rrtype
)
1404 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1407 case kDNSType_CNAME
:
1409 case kDNSType_DNAME
:return(CompressedDomainNameLength(&rd
->name
, name
));
1411 case kDNSType_SOA
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1412 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1413 5 * sizeof(mDNSOpaque32
));
1421 case kDNSType_DHCID
:return(rr
->rdlength
); // Not self-describing, so have to just trust rdlength
1423 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1426 case kDNSType_AFSDB
:
1428 case kDNSType_KX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->mx
.exchange
, name
));
1430 case kDNSType_RP
: return(mDNSu16
)(CompressedDomainNameLength(&rd
->rp
.mbox
, name
) +
1431 CompressedDomainNameLength(&rd
->rp
.txt
, name
));
1433 case kDNSType_PX
: return(mDNSu16
)(2 + CompressedDomainNameLength(&rd
->px
.map822
, name
) +
1434 CompressedDomainNameLength(&rd
->px
.mapx400
, name
));
1436 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1438 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1440 case kDNSType_OPT
: return(rr
->rdlength
);
1442 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1443 return(rr
->rdlength
);
1447 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1453 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1455 case kDNSType_NS
: // Same as PTR
1456 case kDNSType_MD
: // Same as PTR
1457 case kDNSType_MF
: // Same as PTR
1458 case kDNSType_CNAME
:// Same as PTR
1459 //case kDNSType_SOA not checked
1460 case kDNSType_MB
: // Same as PTR
1461 case kDNSType_MG
: // Same as PTR
1462 case kDNSType_MR
: // Same as PTR
1463 //case kDNSType_NULL not checked (no specified format, so always valid)
1464 //case kDNSType_WKS not checked
1465 case kDNSType_PTR
: len
= DomainNameLengthLimit(&rd
->u
.name
, rd
->u
.data
+ rdlength
);
1466 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1468 case kDNSType_HINFO
:// Same as TXT (roughly)
1469 case kDNSType_MINFO
:// Same as TXT (roughly)
1470 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1472 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1473 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1474 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1475 return (ptr
== end
);
1478 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1480 case kDNSType_MX
: // Must be at least two-byte preference, plus domainname
1481 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1482 len
= DomainNameLengthLimit(&rd
->u
.mx
.exchange
, rd
->u
.data
+ rdlength
);
1483 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1485 case kDNSType_SRV
: // Must be at least priority+weight+port, plus domainname
1486 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1487 len
= DomainNameLengthLimit(&rd
->u
.srv
.target
, rd
->u
.data
+ rdlength
);
1488 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1490 default: return(mDNStrue
); // Allow all other types without checking
1494 // ***************************************************************************
1495 #if COMPILER_LIKES_PRAGMA_MARK
1497 #pragma mark - DNS Message Creation Functions
1500 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1504 h
->numQuestions
= 0;
1506 h
->numAuthorities
= 0;
1507 h
->numAdditionals
= 0;
1510 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1512 const mDNSu8
*result
= end
- *domname
- 1;
1514 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1516 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1517 while (result
>= base
)
1519 // If the length byte and first character of the label match, then check further to see
1520 // if this location in the packet will yield a useful name compression pointer.
1521 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1523 const mDNSu8
*name
= domname
;
1524 const mDNSu8
*targ
= result
;
1525 while (targ
+ *name
< end
)
1527 // First see if this label matches
1529 const mDNSu8
*pointertarget
;
1530 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1531 if (i
<= *name
) break; // If label did not match, bail out
1532 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1533 name
+= 1 + *name
; // and proceed to check next label
1534 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1535 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1537 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1538 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1539 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1540 if (targ
+1 >= end
) break; // Second byte not present!
1541 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1542 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1543 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1544 targ
= pointertarget
;
1547 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1552 // Put a string of dot-separated labels as length-prefixed labels
1553 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1554 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1555 // end points to the end of the message so far
1556 // ptr points to where we want to put the name
1557 // limit points to one byte past the end of the buffer that we must not overrun
1558 // domainname is the name to put
1559 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1560 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1562 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1563 const mDNSu8
* np
= name
->c
;
1564 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1565 const mDNSu8
* pointer
= mDNSNULL
;
1566 const mDNSu8
*const searchlimit
= ptr
;
1568 if (!ptr
) { LogMsg("putDomainNameAsLabels ptr is null"); return(mDNSNULL
); }
1570 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1572 if (*np
> MAX_DOMAIN_LABEL
)
1573 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1575 // This check correctly allows for the final trailing root label:
1577 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1578 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1579 // We know that max will be at name->c[255]
1580 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1581 // six bytes, then exit the loop, write the final terminating root label, and the domain
1582 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1583 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1584 if (np
+ 1 + *np
>= max
)
1585 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1587 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1588 if (pointer
) // Use a compression pointer if we can
1590 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1591 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1592 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1595 else // Else copy one label and try again
1599 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1601 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1605 if (ptr
< limit
) // If we didn't run out of space
1607 *ptr
++ = 0; // Put the final root label
1608 return(ptr
); // and return
1614 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1616 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1617 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1618 return ptr
+ sizeof(mDNSOpaque16
);
1621 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
1623 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
1624 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
1625 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
1626 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
1627 return ptr
+ sizeof(mDNSu32
);
1630 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, const ResourceRecord
*const rr
)
1635 while (nput
< rr
->rdlength
)
1637 // check if space for opt/optlen
1638 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1639 opt
= (rdataOPT
*)(rr
->rdata
->u
.data
+ nput
);
1640 ptr
= putVal16(ptr
, opt
->opt
);
1641 ptr
= putVal16(ptr
, opt
->optlen
);
1642 nput
+= 2 * sizeof(mDNSu16
);
1643 if (opt
->opt
== kDNSOpt_LLQ
)
1645 if (ptr
+ LLQ_OPTLEN
> limit
) goto space_err
;
1646 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1647 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1648 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1649 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
.b
, 8); // 8-byte id
1651 ptr
= putVal32(ptr
, opt
->OptData
.llq
.llqlease
);
1654 else if (opt
->opt
== kDNSOpt_Lease
)
1656 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1657 ptr
= putVal32(ptr
, opt
->OptData
.updatelease
);
1658 nput
+= sizeof(mDNSs32
);
1660 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1666 LogMsg("ERROR: putOptRData - out of space");
1670 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1672 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1673 *ptr
+= sizeof(mDNSOpaque16
);
1677 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*const limit
, LargeCacheRecord
*const cr
, mDNSu16 pktRDLen
)
1680 ResourceRecord
*const rr
= &cr
->r
.resrec
;
1681 rdataOPT
*opt
= (rdataOPT
*)rr
->rdata
->u
.data
;
1683 while (nread
< pktRDLen
&& (mDNSu8
*)opt
< rr
->rdata
->u
.data
+ MaximumRDSize
- sizeof(rdataOPT
))
1685 // space for opt + optlen
1686 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1687 opt
->opt
= getVal16(&ptr
);
1688 opt
->optlen
= getVal16(&ptr
);
1689 nread
+= 2 * sizeof(mDNSu16
);
1690 if (opt
->opt
== kDNSOpt_LLQ
)
1692 if ((unsigned)(limit
- ptr
) < LLQ_OPTLEN
) goto space_err
;
1693 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1694 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1695 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1696 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
.b
, ptr
, 8);
1698 opt
->OptData
.llq
.llqlease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1699 if (opt
->OptData
.llq
.llqlease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1700 opt
->OptData
.llq
.llqlease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1701 ptr
+= sizeof(mDNSOpaque32
);
1702 nread
+= LLQ_OPTLEN
;
1704 else if (opt
->opt
== kDNSOpt_Lease
)
1706 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1708 opt
->OptData
.updatelease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1709 if (opt
->OptData
.updatelease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1710 opt
->OptData
.updatelease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1711 ptr
+= sizeof(mDNSs32
);
1712 nread
+= sizeof(mDNSs32
);
1714 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1715 opt
++; // increment pointer into rdatabody
1718 rr
->rdlength
= pktRDLen
;
1722 LogMsg("ERROR: getLLQRdata - out of space");
1726 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1727 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const ResourceRecord
*const rr
)
1731 case kDNSType_A
: if (rr
->rdlength
!= 4)
1733 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1736 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1737 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[0];
1738 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[1];
1739 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[2];
1740 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[3];
1744 case kDNSType_CNAME
:
1746 case kDNSType_DNAME
:return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1748 case kDNSType_SOA
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.soa
.mname
);
1749 if (!ptr
) return(mDNSNULL
);
1750 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.soa
.rname
);
1751 if (!ptr
|| ptr
+ 20 > limit
) return(mDNSNULL
);
1752 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.serial
);
1753 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.refresh
);
1754 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.retry
);
1755 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.expire
);
1756 ptr
= putVal32(ptr
, rr
->rdata
->u
.soa
.min
);
1760 case kDNSType_HINFO
:
1766 case kDNSType_DHCID
:if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1767 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, rr
->rdlength
);
1768 return(ptr
+ rr
->rdlength
);
1771 case kDNSType_AFSDB
:
1773 case kDNSType_KX
: if (ptr
+ 3 > limit
) return(mDNSNULL
);
1774 ptr
= putVal16(ptr
, rr
->rdata
->u
.mx
.preference
);
1775 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.mx
.exchange
));
1777 case kDNSType_RP
: ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.rp
.mbox
);
1778 if (!ptr
) return(mDNSNULL
);
1779 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.rp
.txt
);
1782 case kDNSType_PX
: if (ptr
+ 5 > limit
) return(mDNSNULL
);
1783 ptr
= putVal16(ptr
, rr
->rdata
->u
.px
.preference
);
1784 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.px
.map822
);
1785 if (!ptr
) return(mDNSNULL
);
1786 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.px
.mapx400
);
1789 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1791 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1794 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1795 mDNSPlatformMemCopy(ptr
, &rr
->rdata
->u
.ipv6
, sizeof(rr
->rdata
->u
.ipv6
));
1796 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1798 case kDNSType_SRV
: if (ptr
+ 7 > limit
) return(mDNSNULL
);
1799 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1800 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1801 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1802 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1803 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1804 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1805 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1807 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1809 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1810 if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1811 mDNSPlatformMemCopy(ptr
, rr
->rdata
->u
.data
, rr
->rdlength
);
1812 return(ptr
+ rr
->rdlength
);
1816 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
1818 mDNSu16 rrclass
= (rr
->rrtype
== kDNSType_OPT
) ? NormalMaxDNSMessageData
: rr
->rrclass
;
1820 mDNSu16 actualLength
;
1822 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1824 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1828 if (!ptr
) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL
); }
1830 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
1831 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1832 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1833 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1834 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1835 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1836 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1837 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1838 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1839 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1840 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1841 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1843 // Go back and fill in the actual number of data bytes we wrote
1844 // (actualLength can be less than rdlength when domain name compression is used)
1845 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1846 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1847 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1849 if (count
) (*count
)++;
1850 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1854 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1857 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1858 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1861 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1862 mDNSu16
*count
, const AuthRecord
*rr
)
1864 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
1865 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1866 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1867 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1868 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1869 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1870 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1871 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1876 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1878 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1879 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1880 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1881 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1882 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1883 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1884 msg
->h
.numQuestions
++;
1888 // for dynamic updates
1889 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
1891 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
1892 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
1893 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
1894 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
1895 *ptr
++ = zoneClass
.b
[0];
1896 *ptr
++ = zoneClass
.b
[1];
1897 msg
->h
.mDNS_numZones
++;
1901 // for dynamic updates
1902 mDNSexport mDNSu8
*putPrereqNameNotInUse(const domainname
*const name
, DNSMessage
*const msg
, mDNSu8
*const ptr
, mDNSu8
*const end
)
1905 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1906 AssignDomainName(&prereq
.namestorage
, name
);
1907 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
1908 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
1909 return putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
1912 // for dynamic updates
1913 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
1915 // deletion: specify record w/ TTL 0, class NONE
1916 const mDNSu16 origclass
= rr
->rrclass
;
1917 rr
->rrclass
= kDNSClass_NONE
;
1918 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
1919 rr
->rrclass
= origclass
;
1923 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
1925 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1926 mDNSu16
class = kDNSQClass_ANY
;
1928 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1929 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1930 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1931 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1932 ptr
[2] = (mDNSu8
)(class >> 8);
1933 ptr
[3] = (mDNSu8
)(class & 0xFF);
1934 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1935 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1937 msg
->h
.mDNS_numUpdates
++;
1941 // for dynamic updates
1942 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
1944 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1945 mDNSu16
class = kDNSQClass_ANY
;
1946 mDNSu16 rrtype
= kDNSQType_ANY
;
1948 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1949 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1950 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1951 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1952 ptr
[2] = (mDNSu8
)(class >> 8);
1953 ptr
[3] = (mDNSu8
)(class & 0xFF);
1954 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1955 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1957 msg
->h
.mDNS_numUpdates
++;
1961 // for dynamic updates
1962 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
1965 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
1966 rr
.resrec
.rrclass
= NormalMaxDNSMessageData
;
1967 rr
.resrec
.rdlength
= LEASE_OPT_RDLEN
;
1968 rr
.resrec
.rdestimate
= LEASE_OPT_RDLEN
;
1969 rr
.resrec
.rdata
->u
.opt
.opt
= kDNSOpt_Lease
;
1970 rr
.resrec
.rdata
->u
.opt
.optlen
= sizeof(mDNSs32
);
1971 rr
.resrec
.rdata
->u
.opt
.OptData
.updatelease
= lease
;
1972 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, &rr
.resrec
, 0);
1973 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
1977 // ***************************************************************************
1978 #if COMPILER_LIKES_PRAGMA_MARK
1980 #pragma mark - DNS Message Parsing Functions
1983 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
1988 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
1990 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
1991 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
1992 sum
= (sum
<<3) | (sum
>>29);
1994 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
1998 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
2003 rr
->rdata
= NewRData
;
2004 rr
->rdlength
= rdlength
;
2006 // Must not try to get target pointer until after updating rr->rdata
2007 target
= GetRRDomainNameTarget(rr
);
2008 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
2009 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
2010 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->rdlength
, &rr
->rdata
->u
);
2013 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
2017 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2018 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2020 while (1) // Read sequence of labels
2022 const mDNSu8 len
= *ptr
++; // Read length of this label
2023 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
2026 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2027 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2028 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
2029 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
2034 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
2035 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
2036 case 0xC0: return(ptr
+1);
2041 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2042 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
2043 domainname
*const name
)
2045 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
2046 mDNSu8
*np
= name
->c
; // Name pointer
2047 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
2049 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2050 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
2052 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2054 while (1) // Read sequence of labels
2056 const mDNSu8 len
= *ptr
++; // Read length of this label
2057 if (len
== 0) break; // If length is zero, that means this name is complete
2063 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
2064 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
2065 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
2066 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
2068 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
2069 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2072 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
2075 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
2077 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
2078 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
2079 ptr
= (mDNSu8
*)msg
+ offset
;
2080 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
2081 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
2083 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
2088 if (nextbyte
) return(nextbyte
);
2092 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2094 mDNSu16 pktrdlength
;
2096 ptr
= skipDomainName(msg
, ptr
, end
);
2097 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2099 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2100 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2102 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2104 return(ptr
+ pktrdlength
);
2107 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
,
2108 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*largecr
)
2110 CacheRecord
*rr
= &largecr
->r
;
2111 mDNSu16 pktrdlength
;
2113 if (largecr
== &m
->rec
&& largecr
->r
.resrec
.RecordType
)
2114 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &largecr
->r
));
2116 rr
->next
= mDNSNULL
;
2117 rr
->resrec
.name
= &largecr
->namestorage
;
2119 rr
->NextInKAList
= mDNSNULL
;
2120 rr
->TimeRcvd
= m
? m
->timenow
: 0;
2121 rr
->DelayDelivery
= 0;
2122 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2123 rr
->LastUsed
= m
? m
->timenow
: 0;
2124 rr
->CRActiveQuestion
= mDNSNULL
;
2125 rr
->UnansweredQueries
= 0;
2126 rr
->LastUnansweredTime
= 0;
2127 rr
->MPUnansweredQ
= 0;
2128 rr
->MPLastUnansweredQT
= 0;
2129 rr
->MPUnansweredKA
= 0;
2130 rr
->MPExpectingKA
= mDNSfalse
;
2131 rr
->NextInCFList
= mDNSNULL
;
2133 rr
->resrec
.InterfaceID
= InterfaceID
;
2134 ptr
= getDomainName(msg
, ptr
, end
, &largecr
->namestorage
);
2135 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL
); }
2137 if (ptr
+ 10 > end
) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
2139 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2140 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
2141 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
2142 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
2143 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
2144 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2145 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2146 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
2148 // If mDNS record has cache-flush bit set, we mark it unique
2149 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2150 // authoritative for the entire RRSet), unless this is a truncated response
2151 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8) || (!InterfaceID
&& !(msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)))
2152 RecordType
|= kDNSRecordTypePacketUniqueMask
;
2154 if (ptr
+ pktrdlength
> end
) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
2155 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
2157 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
2158 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
2160 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
2162 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have
2163 // a corresponding case in SameRDataBody() to do a semantic comparison of the structure instead of a blind
2164 // bitwise memory compare. This is because a domainname is a fixed size structure holding variable-length data.
2165 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2166 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2167 if (rr
->resrec
.rrclass
== kDNSQClass_ANY
&& pktrdlength
== 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2168 rr
->resrec
.rdlength
= 0;
2169 else switch (rr
->resrec
.rrtype
)
2171 case kDNSType_A
: if (pktrdlength
!= sizeof(mDNSv4Addr
)) return(mDNSNULL
);
2172 rr
->resrec
.rdata
->u
.ipv4
.b
[0] = ptr
[0];
2173 rr
->resrec
.rdata
->u
.ipv4
.b
[1] = ptr
[1];
2174 rr
->resrec
.rdata
->u
.ipv4
.b
[2] = ptr
[2];
2175 rr
->resrec
.rdata
->u
.ipv4
.b
[3] = ptr
[3];
2179 case kDNSType_CNAME
:
2181 case kDNSType_DNAME
:ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
);
2182 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
2183 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
2186 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
);
2187 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
2188 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
);
2189 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
2190 if (ptr
+ 0x14 != end
) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
2191 rr
->resrec
.rdata
->u
.soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
2192 rr
->resrec
.rdata
->u
.soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
2193 rr
->resrec
.rdata
->u
.soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
2194 rr
->resrec
.rdata
->u
.soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
2195 rr
->resrec
.rdata
->u
.soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
2199 case kDNSType_HINFO
:
2205 case kDNSType_DHCID
:if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2207 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2208 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2211 rr
->resrec
.rdlength
= pktrdlength
;
2212 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, ptr
, pktrdlength
);
2216 case kDNSType_AFSDB
:
2218 case kDNSType_KX
: if (pktrdlength
< 3) return(mDNSNULL
); // Preference + domainname
2219 rr
->resrec
.rdata
->u
.mx
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2220 ptr
= getDomainName(msg
, ptr
+2, end
, &rr
->resrec
.rdata
->u
.mx
.exchange
);
2221 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL
); }
2222 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2225 case kDNSType_RP
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.rp
.mbox
); // Domainname + domainname
2226 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL
; }
2227 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.rp
.txt
);
2228 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL
; }
2231 case kDNSType_PX
: if (pktrdlength
< 4) return(mDNSNULL
); // Preference + domainname + domainname
2232 rr
->resrec
.rdata
->u
.px
.preference
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2233 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.px
.map822
);
2234 if (!ptr
) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL
; }
2235 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.px
.mapx400
);
2236 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL
; }
2239 case kDNSType_AAAA
: if (pktrdlength
!= sizeof(mDNSv6Addr
)) return(mDNSNULL
);
2240 mDNSPlatformMemCopy(&rr
->resrec
.rdata
->u
.ipv6
, ptr
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
2243 case kDNSType_SRV
: if (pktrdlength
< 7) return(mDNSNULL
); // Priority + weight + port + domainname
2244 rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
2245 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
2246 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
2247 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
2248 ptr
= getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
);
2249 if (ptr
!= end
) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
2250 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2253 case kDNSType_OPT
: ptr
= getOptRdata(ptr
, end
, largecr
, pktrdlength
); break;
2254 if (ptr
!= end
) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL
); }
2256 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
2258 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2259 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
2262 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2263 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
2264 // Note: Just because we don't understand the record type, that doesn't
2265 // mean we fail. The DNS protocol specifies rdlength, so we can
2266 // safely skip over unknown records and ignore them.
2267 // We also grab a binary copy of the rdata anyway, since the caller
2268 // might know how to interpret it even if we don't.
2269 rr
->resrec
.rdlength
= pktrdlength
;
2270 mDNSPlatformMemCopy(rr
->resrec
.rdata
->u
.data
, ptr
, pktrdlength
);
2274 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2275 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Sets rdlength, rdestimate, rdatahash for us
2277 // Success! Now fill in RecordType to show this record contains valid data
2278 rr
->resrec
.RecordType
= RecordType
;
2282 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
2284 ptr
= skipDomainName(msg
, ptr
, end
);
2285 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
2286 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2290 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
2291 DNSQuestion
*question
)
2293 mDNSPlatformMemZero(question
, sizeof(*question
));
2294 question
->InterfaceID
= InterfaceID
;
2295 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
2296 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
2297 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
2299 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
2300 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
2301 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
2305 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2308 const mDNSu8
*ptr
= msg
->data
;
2309 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
2313 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2316 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
2317 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2321 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2324 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
2325 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2329 mDNSexport
const mDNSu8
*LocateLLQOptData(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2332 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2334 // Locate the OPT record.
2335 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2336 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2337 // but not necessarily the *last* entry in the Additional Section.
2338 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2340 if (ptr
+ 10 + LLQ_OPT_RDLEN
<= end
&& // Make sure we have 10+22 bytes of data
2341 ptr
[0] == 0 && // Name must be root label
2342 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2343 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2344 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)LLQ_OPT_RDLEN
)
2347 ptr
= skipResourceRecord(msg
, ptr
, end
);
2352 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2353 // it is callers responsibilty to clear m->rec.r.resrec.RecordType after use
2354 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2355 // The code that currently calls this assumes there's only one, instead of iterating through the set
2356 mDNSexport
const rdataOPT
*GetLLQOptData(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2358 const mDNSu8
*ptr
= LocateLLQOptData(msg
, end
);
2361 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2362 if (ptr
) return(&m
->rec
.r
.resrec
.rdata
->u
.opt
);
2367 mDNSexport
const mDNSu8
*LocateLeaseOptData(const DNSMessage
*const msg
, const mDNSu8
*const end
)
2370 const mDNSu8
*ptr
= LocateAdditionals(msg
, end
);
2372 // Locate the OPT record.
2373 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2374 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2375 // but not necessarily the *last* entry in the Additional Section.
2376 for (i
= 0; ptr
&& i
< msg
->h
.numAdditionals
; i
++)
2378 if (ptr
+ 10 + LEASE_OPT_RDLEN
<= end
&& // Make sure we have 10+8 bytes of data
2379 ptr
[0] == 0 && // Name must be root label
2380 ptr
[1] == (kDNSType_OPT
>> 8 ) && // rrtype OPT
2381 ptr
[2] == (kDNSType_OPT
& 0xFF) &&
2382 ((mDNSu16
)ptr
[9] << 8 | (mDNSu16
)ptr
[10]) >= (mDNSu16
)LEASE_OPT_RDLEN
)
2385 ptr
= skipResourceRecord(msg
, ptr
, end
);
2390 // Get the lease life of records in a dynamic update
2391 // returns 0 on error or if no lease present
2392 mDNSexport mDNSu32
GetPktLease(mDNS
*m
, DNSMessage
*msg
, const mDNSu8
*end
)
2395 const mDNSu8
*ptr
= LocateLeaseOptData(msg
, end
);
2396 if (ptr
) ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &m
->rec
);
2397 if (ptr
&& m
->rec
.r
.resrec
.rdlength
>= LEASE_OPT_RDLEN
&& m
->rec
.r
.resrec
.rdata
->u
.opt
.opt
== kDNSOpt_Lease
)
2398 result
= m
->rec
.r
.resrec
.rdata
->u
.opt
.OptData
.updatelease
;
2399 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
2403 mDNSlocal
const mDNSu8
*DumpRecords(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
, int count
, char *label
)
2406 LogMsg("%2d %s", count
, label
);
2407 for (i
= 0; i
< count
&& ptr
; i
++)
2409 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2410 // but since it's only used for debugging (and probably only on OS X, not on
2411 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2412 LargeCacheRecord largecr
;
2413 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, mDNSInterface_Any
, kDNSRecordTypePacketAns
, &largecr
);
2414 if (ptr
) LogMsg("%2d TTL%7d %s", i
, largecr
.r
.resrec
.rroriginalttl
, CRDisplayString(m
, &largecr
.r
));
2416 if (!ptr
) LogMsg("ERROR: Premature end of packet data");
2420 #define DNS_OP_Name(X) ( \
2421 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2422 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2423 (X) == kDNSFlag0_OP_Status ? "Status " : \
2424 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2425 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2426 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2428 #define DNS_RC_Name(X) ( \
2429 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2430 (X) == kDNSFlag1_RC_FmtErr ? "FmtErr" : \
2431 (X) == kDNSFlag1_RC_SrvErr ? "SrvErr" : \
2432 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2433 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2434 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2435 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2436 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2437 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2438 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2439 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2441 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2442 mDNSexport
void DumpPacket(mDNS
*const m
, mDNSBool sent
, char *transport
, const mDNSAddr
*addr
, mDNSIPPort port
, const DNSMessage
*const msg
, const mDNSu8
*const end
)
2444 mDNSBool IsUpdate
= ((msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
) == kDNSFlag0_OP_Update
);
2445 const mDNSu8
*ptr
= msg
->data
;
2449 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 --",
2450 sent
? "Sent" : "Received", transport
,
2451 DNS_OP_Name(msg
->h
.flags
.b
[0] & kDNSFlag0_OP_Mask
),
2452 msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
? "Response" : "Query",
2453 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1],
2454 DNS_RC_Name(msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
),
2455 msg
->h
.flags
.b
[1] & kDNSFlag1_RC_Mask
,
2456 msg
->h
.flags
.b
[0] & kDNSFlag0_AA
? "AA " : "",
2457 msg
->h
.flags
.b
[0] & kDNSFlag0_TC
? "TC " : "",
2458 msg
->h
.flags
.b
[0] & kDNSFlag0_RD
? "RD " : "",
2459 msg
->h
.flags
.b
[1] & kDNSFlag1_RA
? "RA " : "",
2460 msg
->h
.flags
.b
[1] & kDNSFlag1_AD
? "AD " : "",
2461 msg
->h
.flags
.b
[1] & kDNSFlag1_CD
? "CD " : "",
2462 mDNSVal16(msg
->h
.id
),
2464 sent
? "to" : "from", addr
, mDNSVal16(port
),
2465 (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
) ? " (truncated)" : ""
2468 LogMsg("%2d %s", msg
->h
.numQuestions
, IsUpdate
? "Zone" : "Questions");
2469 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++)
2471 ptr
= getQuestion(msg
, ptr
, end
, mDNSInterface_Any
, &q
);
2472 if (ptr
) LogMsg("%2d %##s %s", i
, q
.qname
.c
, DNSTypeName(q
.qtype
));
2474 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAnswers
, IsUpdate
? "Prerequisites" : "Answers");
2475 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAuthorities
, IsUpdate
? "Updates" : "Authorities");
2476 ptr
= DumpRecords(m
, msg
, ptr
, end
, msg
->h
.numAdditionals
, "Additionals");
2477 LogMsg("--------------");
2480 // ***************************************************************************
2481 #if COMPILER_LIKES_PRAGMA_MARK
2483 #pragma mark - Packet Sending Functions
2486 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2487 struct TCPSocket_struct
{ TCPSocketFlags flags
; /* ... */ };
2489 mDNSexport mStatus
mDNSSendDNSMessage(mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2490 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, TCPSocket
*sock
, DomainAuthInfo
*authInfo
)
2494 unsigned long msglen
;
2496 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
2497 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
2498 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
2499 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2500 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
2502 if (end
<= msg
->data
|| end
- msg
->data
> AbsoluteMaxDNSMessageData
)
2504 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg
->data
, end
, end
- msg
->data
);
2505 return mStatus_BadParamErr
;
2508 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2509 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
2510 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
2511 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
2512 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
2513 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
2514 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
2515 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
2516 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
2520 end
= DNSDigest_SignMessage(msg
, &end
, authInfo
, 0);
2521 if (!end
) return mStatus_UnknownErr
;
2524 // Send the packet on the wire
2527 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2528 lenbuf
[0] = (mDNSu8
)(msglen
>> 8); // host->network byte conversion
2529 lenbuf
[1] = (mDNSu8
)(msglen
& 0xFF);
2531 nsent
= mDNSPlatformWriteTCP(sock
, (char*)lenbuf
, 2);
2532 //!!!KRS make sure kernel is sending these as 1 packet!
2533 if (nsent
!= 2) goto tcp_error
;
2535 nsent
= mDNSPlatformWriteTCP(sock
, (char *)msg
, msglen
);
2536 if (nsent
!= (long)msglen
) goto tcp_error
;
2537 status
= mStatus_NoError
;
2541 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, dst
, dstport
);
2544 // Put all the integer values back the way they were before we return
2545 msg
->h
.numQuestions
= numQuestions
;
2546 msg
->h
.numAnswers
= numAnswers
;
2547 msg
->h
.numAuthorities
= numAuthorities
;
2548 msg
->h
.numAdditionals
= numAdditionals
;
2550 if (mDNS_LogLevel
>= MDNS_LOG_VERBOSE_DEBUG
&& !mDNSOpaque16IsZero(msg
->h
.id
))
2552 if (authInfo
) msg
->h
.numAdditionals
++; // Want to include TSIG in DumpPacket output
2553 DumpPacket(m
, mDNStrue
, sock
&& (sock
->flags
& kTCPSocketFlags_UseTLS
) ? "TLS" : sock
? "TCP" : "UDP", dst
, dstport
, msg
, end
);
2554 if (authInfo
) msg
->h
.numAdditionals
--;
2560 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2561 return mStatus_UnknownErr
;
2564 // ***************************************************************************
2565 #if COMPILER_LIKES_PRAGMA_MARK
2567 #pragma mark - RR List Management & Task Management
2570 mDNSexport
void mDNS_Lock_(mDNS
*const m
)
2572 // MUST grab the platform lock FIRST!
2573 mDNSPlatformLock(m
);
2575 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2576 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2577 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2578 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2580 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2582 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2587 // If this is an initial entry into the mDNSCore code, set m->timenow
2588 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2589 if (m
->mDNS_busy
== 0)
2592 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
2593 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2594 if (m
->timenow
== 0) m
->timenow
= 1;
2596 else if (m
->timenow
== 0)
2598 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
2599 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2600 if (m
->timenow
== 0) m
->timenow
= 1;
2603 if (m
->timenow_last
- m
->timenow
> 0)
2605 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
2606 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
2607 m
->timenow
= m
->timenow_last
;
2609 m
->timenow_last
= m
->timenow
;
2611 // Increment mDNS_busy so we'll recognise re-entrant calls
2615 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
2617 mDNSs32 e
= m
->timenow
+ 0x78000000;
2618 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) return(e
);
2619 if (m
->NewQuestions
)
2621 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
2622 else return(m
->timenow
);
2624 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
2625 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
2626 if (m
->SuppressSending
) return(m
->SuppressSending
);
2627 #ifndef UNICAST_DISABLED
2628 if (e
- m
->NextuDNSEvent
> 0) e
= m
->NextuDNSEvent
;
2630 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
2631 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
2632 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
2633 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
2634 if (e
- m
->NextScheduledNATOp
> 0) e
= m
->NextScheduledNATOp
;
2638 mDNSexport
void mDNS_Unlock_(mDNS
*const m
)
2640 // Decrement mDNS_busy
2643 // Check for locking failures
2645 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2647 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2652 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2653 if (m
->mDNS_busy
== 0)
2655 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
2656 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2660 // MUST release the platform lock LAST!
2661 mDNSPlatformUnlock(m
);
2664 // ***************************************************************************
2665 #if COMPILER_LIKES_PRAGMA_MARK
2667 #pragma mark - Specialized mDNS version of vsnprintf
2670 static const struct mDNSprintf_format
2672 unsigned leftJustify
: 1;
2673 unsigned forceSign
: 1;
2674 unsigned zeroPad
: 1;
2675 unsigned havePrecision
: 1;
2679 char sign
; // +, - or space
2680 unsigned int fieldWidth
;
2681 unsigned int precision
;
2682 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2684 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
2686 mDNSu32 nwritten
= 0;
2688 if (buflen
== 0) return(0);
2689 buflen
--; // Pre-reserve one space in the buffer for the terminating null
2690 if (buflen
== 0) goto exit
;
2692 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
2696 *sbuffer
++ = (char)c
;
2697 if (++nwritten
>= buflen
) goto exit
;
2701 unsigned int i
=0, j
;
2702 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
2703 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
2704 // The size needs to be enough for a 256-byte domain name plus some error text.
2705 #define mDNS_VACB_Size 300
2706 char mDNS_VACB
[mDNS_VACB_Size
];
2707 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
2708 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
2709 char *s
= mDNS_VACB_Lim
, *digits
;
2710 struct mDNSprintf_format F
= mDNSprintf_format_default
;
2712 while (1) // decode flags
2715 if (c
== '-') F
.leftJustify
= 1;
2716 else if (c
== '+') F
.forceSign
= 1;
2717 else if (c
== ' ') F
.sign
= ' ';
2718 else if (c
== '#') F
.altForm
++;
2719 else if (c
== '0') F
.zeroPad
= 1;
2723 if (c
== '*') // decode field width
2725 int f
= va_arg(arg
, int);
2726 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
2727 F
.fieldWidth
= (unsigned int)f
;
2732 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
2733 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
2736 if (c
== '.') // decode precision
2738 if ((c
= *++fmt
) == '*')
2739 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
2740 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
2741 F
.precision
= (10 * F
.precision
) + (c
- '0');
2742 F
.havePrecision
= 1;
2745 if (F
.leftJustify
) F
.zeroPad
= 0;
2748 switch (c
) // perform appropriate conversion
2751 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
2752 case 'l' : // fall through
2753 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
2755 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
2756 else n
= (unsigned long)va_arg(arg
, int);
2757 if (F
.hSize
) n
= (short) n
;
2758 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
2759 else if (F
.forceSign
) F
.sign
= '+';
2761 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2762 else n
= va_arg(arg
, unsigned int);
2763 if (F
.hSize
) n
= (unsigned short) n
;
2766 decimal
: if (!F
.havePrecision
)
2770 F
.precision
= F
.fieldWidth
;
2771 if (F
.sign
) --F
.precision
;
2773 if (F
.precision
< 1) F
.precision
= 1;
2775 if (F
.precision
> mDNS_VACB_Size
- 1)
2776 F
.precision
= mDNS_VACB_Size
- 1;
2777 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
2778 for (; i
< F
.precision
; i
++) *--s
= '0';
2779 if (F
.sign
) { *--s
= F
.sign
; i
++; }
2782 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2783 else n
= va_arg(arg
, unsigned int);
2784 if (F
.hSize
) n
= (unsigned short) n
;
2785 if (!F
.havePrecision
)
2787 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
2788 if (F
.precision
< 1) F
.precision
= 1;
2790 if (F
.precision
> mDNS_VACB_Size
- 1)
2791 F
.precision
= mDNS_VACB_Size
- 1;
2792 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
2793 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
2794 for (; i
< F
.precision
; i
++) *--s
= '0';
2798 unsigned char *a
= va_arg(arg
, unsigned char *);
2799 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2802 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2805 mDNSAddr
*ip
= (mDNSAddr
*)a
;
2808 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
2809 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
2810 default: F
.precision
= 0; break;
2813 if (F
.altForm
&& !F
.precision
)
2814 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "«ZERO ADDRESS»");
2815 else switch (F
.precision
)
2817 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
2818 a
[0], a
[1], a
[2], a
[3]); break;
2819 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
2820 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
2821 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
2822 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
2823 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
2824 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
2825 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify"
2826 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
2832 case 'p' : F
.havePrecision
= F
.lSize
= 1;
2834 case 'X' : digits
= "0123456789ABCDEF";
2836 case 'x' : digits
= "0123456789abcdef";
2837 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
2838 else n
= va_arg(arg
, unsigned int);
2839 if (F
.hSize
) n
= (unsigned short) n
;
2840 if (!F
.havePrecision
)
2844 F
.precision
= F
.fieldWidth
;
2845 if (F
.altForm
) F
.precision
-= 2;
2847 if (F
.precision
< 1) F
.precision
= 1;
2849 if (F
.precision
> mDNS_VACB_Size
- 1)
2850 F
.precision
= mDNS_VACB_Size
- 1;
2851 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
2852 for (; i
< F
.precision
; i
++) *--s
= '0';
2853 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
2856 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
2858 case 's' : s
= va_arg(arg
, char *);
2859 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2860 else switch (F
.altForm
)
2863 if (!F
.havePrecision
) // C string
2867 while ((i
< F
.precision
) && s
[i
]) i
++;
2868 // Make sure we don't truncate in the middle of a UTF-8 character
2869 // If last character we got was any kind of UTF-8 multi-byte character,
2870 // then see if we have to back up.
2871 // This is not as easy as the similar checks below, because
2872 // here we can't assume it's safe to examine the *next* byte, so we
2873 // have to confine ourselves to working only backwards in the string.
2874 j
= i
; // Record where we got to
2875 // Now, back up until we find first non-continuation-char
2876 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
2877 // Now s[i-1] is the first non-continuation-char
2878 // and (j-i) is the number of continuation-chars we found
2879 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
2881 i
--; // Tentatively eliminate this start-char as well
2882 // Now (j-i) is the number of characters we're considering eliminating.
2883 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
2884 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
2885 // (with sign extension) then the result has to be 0xFE.
2886 // If this is right, then we reinstate the tentatively eliminated bytes.
2887 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
2891 case 1: i
= (unsigned char) *s
++; break; // Pascal string
2892 case 2: { // DNS label-sequence name
2893 unsigned char *a
= (unsigned char *)s
;
2894 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2895 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
2900 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
2901 if (s
+ *a
>= &mDNS_VACB
[254])
2902 { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
2903 // Need to use ConvertDomainLabelToCString to do proper escaping here,
2904 // so it's clear what's a literal dot and what's a label separator
2905 ConvertDomainLabelToCString((domainlabel
*)a
, buf
);
2906 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%s.", buf
);
2909 i
= (mDNSu32
)(s
- mDNS_VACB
);
2910 s
= mDNS_VACB
; // Reset s back to the start of the buffer
2914 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
2915 if (F
.havePrecision
&& i
> F
.precision
)
2916 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2919 case 'n' : s
= va_arg(arg
, char *);
2920 if (F
.hSize
) * (short *) s
= (short)nwritten
;
2921 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
2922 else * (int *) s
= (int)nwritten
;
2925 default: s
= mDNS_VACB
;
2926 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
2928 case '%' : *sbuffer
++ = (char)c
;
2929 if (++nwritten
>= buflen
) goto exit
;
2933 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
2936 if (++nwritten
>= buflen
) goto exit
;
2937 } while (i
< --F
.fieldWidth
);
2939 // Make sure we don't truncate in the middle of a UTF-8 character.
2940 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
2941 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
2942 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
2943 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
2944 if (i
> buflen
- nwritten
)
2945 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2946 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
2948 if (nwritten
>= buflen
) goto exit
;
2950 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
2953 if (++nwritten
>= buflen
) goto exit
;
2962 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
2968 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);