]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/DNSCommon.c
mDNSResponder-171.4.tar.gz
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16
17 Change History (most recent first):
18
19 $Log: DNSCommon.c,v $
20 Revision 1.199 2008/03/14 19:58:38 mcguire
21 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
22 Make sure we add the record when sending LLQ refreshes
23
24 Revision 1.198 2008/03/07 23:29:24 cheshire
25 Fixed cosmetic byte order display issue in DumpPacket output
26
27 Revision 1.197 2008/03/05 22:51:29 mcguire
28 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
29 Even further refinements
30
31 Revision 1.196 2008/03/05 22:01:53 cheshire
32 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
33 Now that we optionally add the HINFO record, when rewriting the header fields into network byte
34 order, we need to use our updated msg->h.numAdditionals, not the stack variable numAdditionals
35
36 Revision 1.195 2008/03/05 19:06:30 mcguire
37 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
38 further refinements
39
40 Revision 1.194 2008/03/05 00:26:06 cheshire
41 <rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client
42
43 Revision 1.193 2007/12/17 23:42:36 cheshire
44 Added comments about DNSDigest_SignMessage()
45
46 Revision 1.192 2007/12/17 21:24:09 cheshire
47 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
48 We suspend sending of mDNS queries responses when going to sleep, so calculate GetNextScheduledEvent() time accordingly
49
50 Revision 1.191 2007/12/14 00:59:36 cheshire
51 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
52 While going to sleep, don't block event scheduling
53
54 Revision 1.190 2007/12/13 20:20:17 cheshire
55 Minor efficiency tweaks -- converted IdenticalResourceRecord, IdenticalSameNameRecord, and
56 SameRData from functions to macros, which allows the code to be inlined (the compiler can't
57 inline a function defined in a different compilation unit) and therefore optimized better.
58
59 Revision 1.189 2007/12/13 00:17:32 cheshire
60 RDataHashValue was not calculating hash value reliably for RDATA types that have 'holes' in the
61 in-memory representation (particularly SOA was affected by this, resulting in multiple duplicate
62 cache entities for the same SOA record, because they had erroneously different rdatahash values).
63
64 Revision 1.188 2007/12/13 00:13:03 cheshire
65 Simplified RDataHashValue to take a single ResourceRecord pointer, instead of separate rdlength and RDataBody
66
67 Revision 1.187 2007/12/08 00:35:20 cheshire
68 <rdar://problem/5636422> Updating TXT records is too slow
69 m->SuppressSending should not suppress all activity, just mDNS Query/Probe/Response
70
71 Revision 1.186 2007/11/15 22:52:29 cheshire
72 <rdar://problem/5589039> ERROR: mDNSPlatformWriteTCP - send Broken pipe
73
74 Revision 1.185 2007/10/10 20:22:03 cheshire
75 Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
76 apparently caused by trying to sign zero-length messages
77
78 Revision 1.184 2007/10/05 17:56:07 cheshire
79 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
80
81 Revision 1.183 2007/10/02 18:33:46 cheshire
82 Improved GetRRDisplayString to show all constituent strings within a text record
83 (up to the usual MaxMsg 120-character limit)
84
85 Revision 1.182 2007/10/01 19:45:01 cheshire
86 <rdar://problem/5514859> BTMM: Sometimes Back to My Mac autotunnel registrations are malformed
87
88 Revision 1.181 2007/10/01 18:36:53 cheshire
89 Yet another fix to finally get the DumpPacket RCODE display right
90
91 Revision 1.180 2007/09/29 21:30:38 cheshire
92 In DumpPacket/DumpRecords, show an error line if we run out of packet data
93
94 Revision 1.179 2007/09/29 20:44:56 cheshire
95 Fix error in DumpPacket where it was not displaying the RCODE field properly
96
97 Revision 1.178 2007/09/27 21:11:44 cheshire
98 Fixed spelling mistake: ROCDE -> RCODE
99
100 Revision 1.177 2007/09/27 18:51:26 cheshire
101 Improved DumpPacket to use "Zone/Prerequisites/Updates" nomenclature when displaying a DNS Update packet
102
103 Revision 1.176 2007/09/27 17:53:37 cheshire
104 Add display of RCODE and flags in DumpPacket output
105
106 Revision 1.175 2007/09/26 22:26:40 cheshire
107 Also show DNS query/response ID in DumpPacket output
108
109 Revision 1.174 2007/09/26 16:36:02 cheshire
110 In DumpPacket output, begin header line with "-- " to make it visually stand out better
111
112 Revision 1.173 2007/09/26 00:49:46 cheshire
113 Improve packet logging to show sent and received packets,
114 transport protocol (UDP/TCP/TLS) and source/destination address:port
115
116 Revision 1.172 2007/09/21 23:14:39 cheshire
117 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents in verbose debug mode
118 Changed DumpRecords to use LargeCacheRecord on the stack instead of the shared m->rec storage,
119 to eliminate "GetLargeResourceRecord: m->rec appears to be already in use" warnings
120
121 Revision 1.171 2007/09/21 21:12:36 cheshire
122 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
123
124 Revision 1.170 2007/09/07 21:16:58 cheshire
125 Add new symbol "NATPMPAnnouncementPort" (5350)
126
127 Revision 1.169 2007/08/30 00:31:20 cheshire
128 Improve "locking failure" debugging messages to show function name using __func__ macro
129
130 Revision 1.168 2007/08/28 23:58:42 cheshire
131 Rename HostTarget -> AutoTarget
132
133 Revision 1.167 2007/08/10 23:10:05 vazquez
134 <rdar://problem/5389850> mDNS: Reverse lookups of IPv6 link-local addresses always fail
135
136 Revision 1.166 2007/08/01 16:09:13 cheshire
137 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
138
139 Revision 1.165 2007/08/01 00:04:13 cheshire
140 <rdar://problem/5261696> Crash in tcpKQSocketCallback
141 Half-open TCP connections were not being cancelled properly
142
143 Revision 1.164 2007/07/27 20:48:43 cheshire
144 In DumpRecords(), include record TTL in output
145
146 Revision 1.163 2007/07/16 20:10:11 vazquez
147 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
148 Added SSDP port number
149
150 Revision 1.162 2007/07/10 01:59:33 cheshire
151 <rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
152 Fixed GetPktLease to use shared m->rec instead of putting LargeCacheRecord on the stack
153
154 Revision 1.161 2007/07/06 18:56:26 cheshire
155 Check m->NextScheduledNATOp in GetNextScheduledEvent()
156
157 Revision 1.160 2007/06/29 00:06:42 vazquez
158 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
159
160 Revision 1.159 2007/06/28 21:17:17 cheshire
161 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
162
163 Revision 1.158 2007/05/25 00:25:43 cheshire
164 <rdar://problem/5227737> Need to enhance putRData to output all current known types
165
166 Revision 1.157 2007/05/23 00:32:15 cheshire
167 Don't treat uDNS responses as an entire RRSet (kDNSRecordTypePacketUniqueMask)
168 when received in a truncated UDP response
169
170 Revision 1.156 2007/05/15 00:29:00 cheshire
171 Print «ZERO ADDRESS» for %#a with a zero mDNSAddr
172
173 Revision 1.155 2007/05/07 22:07:47 cheshire
174 <rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types
175
176 Revision 1.154 2007/05/04 20:19:53 cheshire
177 Improve DumpPacket() output
178
179 Revision 1.153 2007/05/01 21:46:31 cheshire
180 Move GetLLQOptData/GetPktLease from uDNS.c into DNSCommon.c so that dnsextd can use them
181
182 Revision 1.152 2007/04/27 19:28:01 cheshire
183 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
184 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
185 -- it would start a query and then quickly cancel it, and then when
186 StartGetZoneData completed, it had a dangling pointer and crashed.)
187
188 Revision 1.151 2007/04/26 13:35:25 cheshire
189 Add kDNSType_SOA case in SameRDataBody, and a comment in GetLargeResourceRecord about why this is important
190
191 Revision 1.150 2007/04/24 00:17:33 cheshire
192 Made LocateLLQOptData guard against packets with bogus numAdditionals value
193
194 Revision 1.149 2007/04/23 21:43:00 cheshire
195 Remove debugging check
196
197 Revision 1.148 2007/04/23 04:55:29 cheshire
198 Add some defensive null pointer checks
199
200 Revision 1.147 2007/04/22 20:18:10 cheshire
201 Add comment about mDNSRandom()
202
203 Revision 1.146 2007/04/22 06:02:02 cheshire
204 <rdar://problem/4615977> Query should immediately return failure when no server
205
206 Revision 1.145 2007/04/20 21:17:24 cheshire
207 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
208
209 Revision 1.144 2007/04/19 18:02:43 cheshire
210 <rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit
211
212 Revision 1.143 2007/04/16 21:53:49 cheshire
213 Improve display of negative cache entries
214
215 Revision 1.142 2007/04/05 22:55:35 cheshire
216 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
217
218 Revision 1.141 2007/04/04 01:33:11 cheshire
219 <rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record
220 Overly defensive code was zeroing too much of the AuthRecord structure
221
222 Revision 1.140 2007/04/03 19:37:58 cheshire
223 Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918()
224
225 Revision 1.139 2007/04/03 19:18:39 cheshire
226 Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly
227
228 Revision 1.138 2007/03/28 21:14:08 cheshire
229 The rrclass field of an OPT pseudo-RR holds the sender's UDP payload size
230
231 Revision 1.137 2007/03/28 20:59:26 cheshire
232 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
233
234 Revision 1.136 2007/03/28 15:56:37 cheshire
235 <rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output
236
237 Revision 1.135 2007/03/28 01:20:05 cheshire
238 <rdar://problem/4883206> Improve/create logging for secure browse
239
240 Revision 1.134 2007/03/27 23:25:35 cheshire
241 Fix error caching SOA records
242 (cache entry was size of wire-format packed data, not size of in-memory structure)
243
244 Revision 1.133 2007/03/26 22:55:45 cheshire
245 Add OPT and TSIG to list of types DNSTypeName() knows about
246
247 Revision 1.132 2007/03/22 18:31:48 cheshire
248 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
249
250 Revision 1.131 2007/03/21 21:55:20 cheshire
251 <rdar://problem/5069688> Hostname gets ; or : which are illegal characters
252 Error in AppendLabelSuffix() for numbers close to the 32-bit limit
253
254 Revision 1.130 2007/03/21 19:23:37 cheshire
255 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
256 Make check less strict so we don't break Bonjour Browser
257
258 Revision 1.129 2007/03/21 01:00:45 cheshire
259 <rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari
260 DeconstructServiceName() needs to be more defensive about what it considers legal
261
262 Revision 1.128 2007/03/21 00:30:02 cheshire
263 <rdar://problem/4789455> Multiple errors in DNameList-related code
264
265 Revision 1.127 2007/03/20 17:07:15 cheshire
266 Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket"
267
268 Revision 1.126 2007/03/10 03:26:44 cheshire
269 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
270
271 Revision 1.125 2007/03/07 00:08:58 cheshire
272 <rdar://problem/4347550> Don't allow hyphens at start of service type
273
274 Revision 1.124 2007/01/19 18:04:05 cheshire
275 For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT
276
277 Revision 1.123 2007/01/10 22:45:51 cheshire
278 Cast static strings to "(const domainname*)", not "(domainname*)"
279
280 Revision 1.122 2007/01/06 00:47:35 cheshire
281 Improve GetRRDisplayString to indicate when record has zero-length rdata
282
283 Revision 1.121 2007/01/05 08:30:39 cheshire
284 Trim excessive "$Log" checkin history from before 2006
285 (checkin history still available via "cvs log ..." of course)
286
287 Revision 1.120 2007/01/05 05:23:00 cheshire
288 Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd)
289
290 Revision 1.119 2007/01/05 04:30:16 cheshire
291 Change a couple of "(domainname *)" casts to "(const domainname *)"
292
293 Revision 1.118 2007/01/04 20:21:59 cheshire
294 <rdar://problem/4720673> uDNS: Need to start caching unicast records
295 Don't return multicast answers in response to unicast questions
296
297 Revision 1.117 2006/12/22 20:59:49 cheshire
298 <rdar://problem/4742742> Read *all* DNS keys from keychain,
299 not just key for the system-wide default registration domain
300
301 Revision 1.116 2006/12/21 00:04:07 cheshire
302 To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord()
303
304 Revision 1.115 2006/12/20 04:07:34 cheshire
305 Remove uDNS_info substructure from AuthRecord_struct
306
307 Revision 1.114 2006/12/19 22:40:04 cheshire
308 Fix compiler warnings
309
310 Revision 1.113 2006/12/19 02:21:08 cheshire
311 Delete spurious spaces
312
313 Revision 1.112 2006/12/15 20:42:10 cheshire
314 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
315 Additional defensive coding in GetLargeResourceRecord() to reject apparently-valid
316 rdata that actually runs past the end of the received packet data.
317
318 Revision 1.111 2006/12/15 19:09:57 cheshire
319 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
320 Made DomainNameLength() more defensive by adding a limit parameter, so it can be
321 safely used to inspect potentially malformed data received from external sources.
322 Without this, a domain name that starts off apparently valid, but extends beyond the end of
323 the received packet data, could have appeared valid if the random bytes are already in memory
324 beyond the end of the packet just happened to have reasonable values (e.g. all zeroes).
325
326 Revision 1.110 2006/11/18 05:01:30 cheshire
327 Preliminary support for unifying the uDNS and mDNS code,
328 including caching of uDNS answers
329
330 Revision 1.109 2006/11/10 00:54:14 cheshire
331 <rdar://problem/4816598> Changing case of Computer Name doesn't work
332
333 Revision 1.108 2006/10/05 23:11:18 cheshire
334 <rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records
335
336 Revision 1.107 2006/09/15 21:20:14 cheshire
337 Remove uDNS_info substructure from mDNS_struct
338
339 Revision 1.106 2006/08/14 23:24:22 cheshire
340 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
341
342 Revision 1.105 2006/07/15 02:01:28 cheshire
343 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
344 Fix broken "empty string" browsing
345
346 Revision 1.104 2006/07/05 23:09:13 cheshire
347 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
348 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
349
350 Revision 1.103 2006/06/29 07:42:14 cheshire
351 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
352
353 Revision 1.102 2006/06/22 19:49:11 cheshire
354 Added (commented out) definitions for the LLMNR UDP port and multicast addresses
355
356 Revision 1.101 2006/06/15 21:35:15 cheshire
357 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
358 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
359
360 Revision 1.100 2006/06/08 22:58:46 cheshire
361 <rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
362
363 Revision 1.99 2006/05/18 01:32:33 cheshire
364 <rdar://problem/4472706> iChat: Lost connection with Bonjour
365 (mDNSResponder insufficiently defensive against malformed browsing PTR responses)
366
367 Revision 1.98 2006/03/19 17:00:58 cheshire
368 Define symbol MaxMsg instead of using hard-coded constant value '80'
369
370 Revision 1.97 2006/03/18 21:47:56 cheshire
371 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
372
373 Revision 1.96 2006/03/10 21:51:42 cheshire
374 <rdar://problem/4111464> After record update, old record sometimes remains in cache
375 Split out SameRDataBody() into a separate routine so it can be called from other code
376
377 Revision 1.95 2006/03/08 22:43:11 cheshire
378 Use "localdomain" symbol instead of literal string
379
380 Revision 1.94 2006/03/02 21:59:55 cheshire
381 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
382 Improve sanity checks & debugging support in GetLargeResourceRecord()
383
384 Revision 1.93 2006/03/02 20:30:47 cheshire
385 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
386
387 */
388
389 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
390 #define mDNS_InstantiateInlines 1
391 #include "DNSCommon.h"
392
393 // Disable certain benign warnings with Microsoft compilers
394 #if (defined(_MSC_VER))
395 // Disable "conditional expression is constant" warning for debug macros.
396 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
397 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
398 #pragma warning(disable:4127)
399 // Disable "array is too small to include a terminating null character" warning
400 // -- domain labels have an initial length byte, not a terminating null character
401 #pragma warning(disable:4295)
402 #endif
403
404 // ***************************************************************************
405 #if COMPILER_LIKES_PRAGMA_MARK
406 #pragma mark - Program Constants
407 #endif
408
409 mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
410 mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
411 mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
412 mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
413 mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
414 mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
415 mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
416
417 mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
418 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1;
419 mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)2;
420
421 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
422 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
423 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
424 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
425 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
426 // with Microsoft's LLMNR client code.
427
428 #define SSDPPortAsNumber 1900
429
430 #define UnicastDNSPortAsNumber 53
431 #define NATPMPAnnouncementPortAsNumber 5350
432 #define NATPMPPortAsNumber 5351
433 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
434 #define MulticastDNSPortAsNumber 5353
435 #define LoopbackIPCPortAsNumber 5354
436 //#define MulticastDNSPortAsNumber 5355 // LLMNR
437
438 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
439 #define PrivateDNSPortAsNumber 5533
440
441 mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } };
442
443 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
444 mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
445 mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } };
446 mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } };
447 mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
448 mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } };
449
450 mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } };
451 mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } };
452
453 mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
454 mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
455 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
456 mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
457 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
458
459 mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
460 mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
461 mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
462 mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
463 mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
464 mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
465
466 mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } };
467
468 // ***************************************************************************
469 #if COMPILER_LIKES_PRAGMA_MARK
470 #pragma mark -
471 #pragma mark - General Utility Functions
472 #endif
473
474 // return true for RFC1918 private addresses
475 mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr)
476 {
477 return ((addr->b[0] == 10) || // 10/8 prefix
478 (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12
479 (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16
480 }
481
482 mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf)
483 {
484 while (intf && !intf->InterfaceActive) intf = intf->next;
485 return(intf);
486 }
487
488 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
489 {
490 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
491 if (next) return(next->InterfaceID); else return(mDNSNULL);
492 }
493
494 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
495 {
496 mDNSu32 slot, used = 0;
497 CacheGroup *cg;
498 CacheRecord *rr;
499 FORALL_CACHERECORDS(slot, cg, rr)
500 if (rr->resrec.InterfaceID == id) used++;
501 return(used);
502 }
503
504 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
505 {
506 switch (rrtype)
507 {
508 case kDNSType_A: return("Addr");
509 case kDNSType_NS: return("NS");
510 case kDNSType_CNAME:return("CNAME");
511 case kDNSType_SOA: return("SOA");
512 case kDNSType_NULL: return("NULL");
513 case kDNSType_PTR: return("PTR");
514 case kDNSType_HINFO:return("HINFO");
515 case kDNSType_TXT: return("TXT");
516 case kDNSType_AAAA: return("AAAA");
517 case kDNSType_SRV: return("SRV");
518 case kDNSType_OPT: return("OPT");
519 case kDNSType_TSIG: return("TSIG");
520 case kDNSQType_ANY: return("ANY");
521 default: {
522 static char buffer[16];
523 mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
524 return(buffer);
525 }
526 }
527 }
528
529 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
530 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
531 // long as this routine is only used for debugging messages, it probably isn't a big problem.
532 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer)
533 {
534 #define Max (MaxMsg-1)
535 char *ptr = buffer;
536 mDNSu32 length = mDNS_snprintf(buffer, Max, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
537 if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
538 if (!rr->rdlength) { mDNS_snprintf(buffer+length, Max-length, "<< ZERO RDATA LENGTH >>"); return(buffer); }
539
540 switch (rr->rrtype)
541 {
542 case kDNSType_A: mDNS_snprintf(buffer+length, Max-length, "%.4a", &rd->ipv4); break;
543
544 case kDNSType_NS: // Same as PTR
545 case kDNSType_CNAME:// Same as PTR
546 case kDNSType_PTR: mDNS_snprintf(buffer+length, Max-length, "%##s", rd->name.c); break;
547
548 case kDNSType_SOA: mDNS_snprintf(buffer+length, Max-length, "%##s %##s %d %d %d %d %d",
549 rd->soa.mname.c, rd->soa.rname.c,
550 rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
551 break;
552
553 case kDNSType_HINFO:// Display this the same as TXT (show all constituent strings)
554 case kDNSType_TXT: {
555 mDNSu8 *t = rd->txt.c;
556 while (t < rd->txt.c + rr->rdlength)
557 {
558 length += mDNS_snprintf(buffer+length, Max-length, "%s%#s", t > rd->txt.c ? "¦" : "", t);
559 t += 1 + t[0];
560 }
561 } break;
562
563 case kDNSType_AAAA: mDNS_snprintf(buffer+length, Max-length, "%.16a", &rd->ipv6); break;
564 case kDNSType_SRV: mDNS_snprintf(buffer+length, Max-length, "%u %u %u %##s",
565 rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
566 case kDNSType_OPT: length += mDNS_snprintf(buffer+length, Max-length, "%d Len %d ", rd->opt.opt, rd->opt.optlen);
567 length += mDNS_snprintf(buffer+length, Max-length, "Max UDP %d ", rr->rrclass);
568 if (rd->opt.opt == kDNSOpt_LLQ)
569 {
570 length += mDNS_snprintf(buffer+length, Max-length, "Vers %d ", rd->opt.OptData.llq.vers);
571 length += mDNS_snprintf(buffer+length, Max-length, "Op %d ", rd->opt.OptData.llq.llqOp);
572 length += mDNS_snprintf(buffer+length, Max-length, "Err/Port %d ", rd->opt.OptData.llq.err);
573 length += mDNS_snprintf(buffer+length, Max-length, "ID %08X%08X ", rd->opt.OptData.llq.id.l[0], rd->opt.OptData.llq.id.l[1]);
574 length += mDNS_snprintf(buffer+length, Max-length, "Lease %d", rd->opt.OptData.llq.llqlease);
575 }
576 else if (rd->opt.opt == kDNSOpt_Lease)
577 length += mDNS_snprintf(buffer+length, Max-length, "kDNSOpt_Lease Lease %d", rd->opt.OptData.updatelease);
578 else
579 length += mDNS_snprintf(buffer+length, Max-length, "Unknown opt %d", rd->opt.opt);
580 break;
581 default: mDNS_snprintf(buffer+length, Max-length, "RDLen %d: %s", rr->rdlength, rd->data);
582 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
583 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
584 break;
585 }
586 return(buffer);
587 }
588
589 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive
590 {
591 static mDNSu32 seed = 0;
592 mDNSu32 mask = 1;
593
594 if (!seed)
595 {
596 int i;
597 seed = mDNSPlatformRandomSeed(); // Pick an initial seed
598 for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit
599 }
600 while (mask < max) mask = (mask << 1) | 1;
601 do seed = seed * 21 + 1; while ((seed & mask) > max);
602 return (seed & mask);
603 }
604
605 mDNSexport mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max)
606 {
607 mDNSu32 mask = 1;
608 while (mask < max) mask = (mask << 1) | 1;
609 do seed = seed * 21 + 1; while ((seed & mask) > max);
610 return (seed & mask);
611 }
612
613 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
614 {
615 if (ip1->type == ip2->type)
616 {
617 switch (ip1->type)
618 {
619 case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal
620 case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
621 case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
622 }
623 }
624 return(mDNSfalse);
625 }
626
627 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
628 {
629 switch(ip->type)
630 {
631 case mDNSAddrType_IPv4: return(mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
632 case mDNSAddrType_IPv6: return(mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
633 default: return(mDNSfalse);
634 }
635 }
636
637 // ***************************************************************************
638 #if COMPILER_LIKES_PRAGMA_MARK
639 #pragma mark -
640 #pragma mark - Domain Name Utility Functions
641 #endif
642
643 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
644 {
645 int i;
646 const int len = *a++;
647
648 if (len > MAX_DOMAIN_LABEL)
649 { debugf("Malformed label (too long)"); return(mDNSfalse); }
650
651 if (len != *b++) return(mDNSfalse);
652 for (i=0; i<len; i++)
653 {
654 mDNSu8 ac = *a++;
655 mDNSu8 bc = *b++;
656 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
657 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
658 if (ac != bc) return(mDNSfalse);
659 }
660 return(mDNStrue);
661 }
662
663 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
664 {
665 const mDNSu8 * a = d1->c;
666 const mDNSu8 * b = d2->c;
667 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
668
669 while (*a || *b)
670 {
671 if (a + 1 + *a >= max)
672 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); }
673 if (!SameDomainLabel(a, b)) return(mDNSfalse);
674 a += 1 + *a;
675 b += 1 + *b;
676 }
677
678 return(mDNStrue);
679 }
680
681 mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
682 {
683 mDNSu16 l1 = DomainNameLength(d1);
684 mDNSu16 l2 = DomainNameLength(d2);
685 return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
686 }
687
688 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
689 {
690 // Domains that are defined to be resolved via link-local multicast are:
691 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
692 static const domainname *nL = (const domainname*)"\x5" "local";
693 static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
694 static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
695 static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
696 static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
697 static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
698
699 const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc.
700 d1 = d2 = d3 = d4 = d5 = mDNSNULL;
701 while (d->c[0])
702 {
703 d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
704 d = (const domainname*)(d->c + 1 + d->c[0]);
705 }
706
707 if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
708 if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
709 if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
710 if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
711 if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
712 if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
713 return(mDNSfalse);
714 }
715
716 // Returns length of a domain name INCLUDING the byte for the final null label
717 // e.g. for the root label "." it returns one
718 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
719 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
720 // If the given domainname is invalid, result is 256 (MAX_DOMAIN_NAME+1)
721 mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
722 {
723 const mDNSu8 *src = name->c;
724 while (src < limit && *src <= MAX_DOMAIN_LABEL)
725 {
726 if (*src == 0) return((mDNSu16)(src - name->c + 1));
727 src += 1 + *src;
728 }
729 return(MAX_DOMAIN_NAME+1);
730 }
731
732 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
733 // for the final null label, e.g. for the root label "." it returns one.
734 // E.g. for the FQDN "foo.com." it returns 9
735 // (length, three data bytes, length, three more data bytes, final zero).
736 // In the case where a parent domain name is provided, and the given name is a child
737 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
738 // of the child name, plus TWO bytes for the compression pointer.
739 // E.g. for the name "foo.com." with parent "com.", it returns 6
740 // (length, three data bytes, two-byte compression pointer).
741 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
742 {
743 const mDNSu8 *src = name->c;
744 if (parent && parent->c[0] == 0) parent = mDNSNULL;
745 while (*src)
746 {
747 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
748 if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
749 src += 1 + *src;
750 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
751 }
752 return((mDNSu16)(src - name->c + 1));
753 }
754
755 // CountLabels() returns number of labels in name, excluding final root label
756 // (e.g. for "apple.com." CountLabels returns 2.)
757 mDNSexport int CountLabels(const domainname *d)
758 {
759 int count = 0;
760 const mDNSu8 *ptr;
761 for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
762 return count;
763 }
764
765 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
766 // returning a pointer to the suffix with 'skip' labels removed.
767 mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
768 {
769 while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
770 return(d);
771 }
772
773 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
774 // The C string contains the label as-is, with no escaping, etc.
775 // Any dots in the name are literal dots, not label separators
776 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
777 // in the domainname bufer (i.e. the next byte after the terminating zero).
778 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
779 // AppendLiteralLabelString returns mDNSNULL.
780 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
781 {
782 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
783 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
784 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
785 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
786 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
787
788 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
789 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
790 *ptr++ = 0; // Put the null root label on the end
791 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
792 else return(ptr); // Success: return new value of ptr
793 }
794
795 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
796 // The C string is in conventional DNS syntax:
797 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
798 // If successful, AppendDNSNameString returns a pointer to the next unused byte
799 // in the domainname bufer (i.e. the next byte after the terminating zero).
800 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
801 // AppendDNSNameString returns mDNSNULL.
802 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
803 {
804 const char *cstr = cstring;
805 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
806 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
807 while (*cstr && ptr < lim) // While more characters, and space to put them...
808 {
809 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
810 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
811 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
812 {
813 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
814 if (c == '\\') // If escape character, check next character
815 {
816 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
817 if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]))
818 { // If three decimal digits,
819 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
820 int v1 = cstr[ 0] - '0';
821 int v2 = cstr[ 1] - '0';
822 int val = v0 * 100 + v1 * 10 + v2;
823 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
824 }
825 }
826 *ptr++ = c; // Write the character
827 }
828 if (*cstr) cstr++; // Skip over the trailing dot (if present)
829 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
830 return(mDNSNULL);
831 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
832 }
833
834 *ptr++ = 0; // Put the null root label on the end
835 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
836 else return(ptr); // Success: return new value of ptr
837 }
838
839 // AppendDomainLabel appends a single label to a name.
840 // If successful, AppendDomainLabel returns a pointer to the next unused byte
841 // in the domainname bufer (i.e. the next byte after the terminating zero).
842 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
843 // AppendDomainLabel returns mDNSNULL.
844 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
845 {
846 int i;
847 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
848
849 // Check label is legal
850 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
851
852 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
853 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
854
855 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
856 *ptr++ = 0; // Put the null root label on the end
857 return(ptr);
858 }
859
860 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
861 {
862 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
863 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
864 const mDNSu8 * src = append->c;
865 while (src[0])
866 {
867 int i;
868 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
869 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
870 *ptr = 0; // Put the null root label on the end
871 src += i;
872 }
873 return(ptr);
874 }
875
876 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
877 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
878 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
879 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
880 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
881 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
882 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
883 {
884 mDNSu8 * ptr = label->c + 1; // Where we're putting it
885 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
886 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
887 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
888 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
889 }
890
891 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
892 // The C string is in conventional DNS syntax:
893 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
894 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
895 // in the domainname bufer (i.e. the next byte after the terminating zero).
896 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
897 // MakeDomainNameFromDNSNameString returns mDNSNULL.
898 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
899 {
900 name->c[0] = 0; // Make an empty domain name
901 return(AppendDNSNameString(name, cstr)); // And then add this string to it
902 }
903
904 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
905 {
906 const mDNSu8 * src = label->c; // Domain label we're reading
907 const mDNSu8 len = *src++; // Read length of this (non-null) label
908 const mDNSu8 *const end = src + len; // Work out where the label ends
909 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
910 while (src < end) // While we have characters in the label
911 {
912 mDNSu8 c = *src++;
913 if (esc)
914 {
915 if (c == '.' || c == esc) // If character is a dot or the escape character
916 *ptr++ = esc; // Output escape character
917 else if (c <= ' ') // If non-printing ascii,
918 { // Output decimal escape sequence
919 *ptr++ = esc;
920 *ptr++ = (char) ('0' + (c / 100) );
921 *ptr++ = (char) ('0' + (c / 10) % 10);
922 c = (mDNSu8)('0' + (c ) % 10);
923 }
924 }
925 *ptr++ = (char)c; // Copy the character
926 }
927 *ptr = 0; // Null-terminate the string
928 return(ptr); // and return
929 }
930
931 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
932 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
933 {
934 const mDNSu8 *src = name->c; // Domain name we're reading
935 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
936
937 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
938
939 while (*src) // While more characters in the domain name
940 {
941 if (src + 1 + *src >= max) return(mDNSNULL);
942 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
943 if (!ptr) return(mDNSNULL);
944 src += 1 + *src;
945 *ptr++ = '.'; // Write the dot after the label
946 }
947
948 *ptr++ = 0; // Null-terminate the string
949 return(ptr); // and return
950 }
951
952 // RFC 1034 rules:
953 // Host names must start with a letter, end with a letter or digit,
954 // and have as interior characters only letters, digits, and hyphen.
955 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
956
957 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
958 {
959 const mDNSu8 * src = &UTF8Name[1];
960 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
961 mDNSu8 * ptr = &hostlabel->c[1];
962 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
963 while (src < end)
964 {
965 // Delete apostrophes from source name
966 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
967 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
968 { src += 3; continue; } // Unicode curly apostrophe
969 if (ptr < lim)
970 {
971 if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
972 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
973 }
974 src++;
975 }
976 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
977 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
978 }
979
980 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
981 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
982 ((X)[4] | 0x20) == 'p')
983
984 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
985 const domainlabel *name, const domainname *type, const domainname *const domain)
986 {
987 int i, len;
988 mDNSu8 *dst = fqdn->c;
989 const mDNSu8 *src;
990 const char *errormsg;
991
992 // In the case where there is no name (and ONLY in that case),
993 // a single-label subtype is allowed as the first label of a three-part "type"
994 if (!name && type)
995 {
996 const mDNSu8 *s0 = type->c;
997 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
998 {
999 const mDNSu8 * s1 = s0 + 1 + s0[0];
1000 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
1001 {
1002 const mDNSu8 *s2 = s1 + 1 + s1[0];
1003 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
1004 {
1005 static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
1006 src = s0; // Copy the first label
1007 len = *src;
1008 for (i=0; i <= len; i++) *dst++ = *src++;
1009 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
1010 type = (const domainname *)s1;
1011
1012 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
1013 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1014 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1015 if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
1016 dst -= sizeof(SubTypeLabel);
1017 }
1018 }
1019 }
1020 }
1021
1022 if (name && name->c[0])
1023 {
1024 src = name->c; // Put the service name into the domain name
1025 len = *src;
1026 if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
1027 for (i=0; i<=len; i++) *dst++ = *src++;
1028 }
1029 else
1030 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
1031
1032 src = type->c; // Put the service type into the domain name
1033 len = *src;
1034 if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain)))
1035 {
1036 errormsg = "Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
1037 goto fail;
1038 }
1039 if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
1040 for (i=2; i<=len; i++)
1041 {
1042 // Letters and digits are allowed anywhere
1043 if (mdnsIsLetter(src[i]) || mdnsIsDigit(src[i])) continue;
1044 // Hyphens are only allowed as interior characters
1045 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1046 // with the same rule as hyphens
1047 if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len) continue;
1048 errormsg = "Application protocol name must contain only letters, digits, and hyphens"; goto fail;
1049 }
1050 for (i=0; i<=len; i++) *dst++ = *src++;
1051
1052 len = *src;
1053 if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
1054 for (i=0; i<=len; i++) *dst++ = *src++;
1055
1056 if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
1057
1058 *dst = 0;
1059 if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
1060 if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
1061 { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
1062 dst = AppendDomainName(fqdn, domain);
1063 if (!dst) { errormsg = "Service domain too long"; goto fail; }
1064 return(dst);
1065
1066 fail:
1067 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
1068 return(mDNSNULL);
1069 }
1070
1071 // A service name has the form: instance.application-protocol.transport-protocol.domain
1072 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
1073 // set or length limits for the protocol names, and the final domain is allowed to be empty.
1074 // However, if the given FQDN doesn't contain at least three labels,
1075 // DeconstructServiceName will reject it and return mDNSfalse.
1076 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
1077 domainlabel *const name, domainname *const type, domainname *const domain)
1078 {
1079 int i, len;
1080 const mDNSu8 *src = fqdn->c;
1081 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
1082 mDNSu8 *dst;
1083
1084 dst = name->c; // Extract the service name
1085 len = *src;
1086 if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); }
1087 if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
1088 for (i=0; i<=len; i++) *dst++ = *src++;
1089
1090 dst = type->c; // Extract the service type
1091 len = *src;
1092 if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); }
1093 if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
1094 if (src[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); }
1095 for (i=0; i<=len; i++) *dst++ = *src++;
1096
1097 len = *src;
1098 if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
1099 // Can't do this check right now, until Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1100 //if (!ValidTransportProtocol(src))
1101 // { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
1102 for (i=0; i<=len; i++) *dst++ = *src++;
1103 *dst++ = 0; // Put terminator on the end of service type
1104
1105 dst = domain->c; // Extract the service domain
1106 while (*src)
1107 {
1108 len = *src;
1109 if (len >= 0x40)
1110 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
1111 if (src + 1 + len + 1 >= max)
1112 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
1113 for (i=0; i<=len; i++) *dst++ = *src++;
1114 }
1115 *dst++ = 0; // Put the null root label on the end
1116
1117 return(mDNStrue);
1118 }
1119
1120 // Notes on UTF-8:
1121 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1122 // 10xxxxxx is a continuation byte of a multi-byte character
1123 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1124 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1125 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1126 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1127 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1128 //
1129 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1130 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1131 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1132 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1133 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1134
1135 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
1136 {
1137 if (length > max)
1138 {
1139 mDNSu8 c1 = string[max]; // First byte after cut point
1140 mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point
1141 length = max; // Trim length down
1142 while (length > 0)
1143 {
1144 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1145 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1146 // If so, then we continue to chop more bytes until we get to a legal chop point.
1147 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
1148 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
1149 if (!continuation && !secondsurrogate) break;
1150 c2 = c1;
1151 c1 = string[--length];
1152 }
1153 // Having truncated characters off the end of our string, also cut off any residual white space
1154 while (length > 0 && string[length-1] <= ' ') length--;
1155 }
1156 return(length);
1157 }
1158
1159 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1160 // name ends in "-nnn", where n is some decimal number.
1161 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
1162 {
1163 mDNSu16 l = name->c[0];
1164
1165 if (RichText)
1166 {
1167 if (l < 4) return mDNSfalse; // Need at least " (2)"
1168 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
1169 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
1170 l--;
1171 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
1172 return (name->c[l] == '(' && name->c[l - 1] == ' ');
1173 }
1174 else
1175 {
1176 if (l < 2) return mDNSfalse; // Need at least "-2"
1177 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
1178 l--;
1179 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
1180 return (name->c[l] == '-');
1181 }
1182 }
1183
1184 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1185 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1186 // from the suffix that was removed.
1187 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
1188 {
1189 mDNSu32 val = 0, multiplier = 1;
1190
1191 // Chop closing parentheses from RichText suffix
1192 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1193
1194 // Get any existing numerical suffix off the name
1195 while (mdnsIsDigit(name->c[name->c[0]]))
1196 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
1197
1198 // Chop opening parentheses or dash from suffix
1199 if (RichText)
1200 {
1201 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1202 }
1203 else
1204 {
1205 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1206 }
1207
1208 return(val);
1209 }
1210
1211 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1212 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1213 mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
1214 {
1215 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1216 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1217
1218 // Truncate trailing spaces from RichText names
1219 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1220
1221 while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
1222
1223 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1224
1225 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1226 else { name->c[++name->c[0]] = '-'; }
1227
1228 while (divisor)
1229 {
1230 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1231 val %= divisor;
1232 divisor /= 10;
1233 }
1234
1235 if (RichText) name->c[++name->c[0]] = ')';
1236 }
1237
1238 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1239 {
1240 mDNSu32 val = 0;
1241
1242 if (LabelContainsSuffix(name, RichText))
1243 val = RemoveLabelSuffix(name, RichText);
1244
1245 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1246 // If existing suffix in the range 2-9, increment it.
1247 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1248 // so add a random increment to improve the chances of finding an available name next time.
1249 if (val == 0) val = 2;
1250 else if (val < 10) val++;
1251 else val += 1 + mDNSRandom(99);
1252
1253 AppendLabelSuffix(name, val, RichText);
1254 }
1255
1256 // ***************************************************************************
1257 #if COMPILER_LIKES_PRAGMA_MARK
1258 #pragma mark -
1259 #pragma mark - Resource Record Utility Functions
1260 #endif
1261
1262 // Set up a AuthRecord with sensible default values.
1263 // These defaults may be overwritten with new values before mDNS_Register is called
1264 mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
1265 mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
1266 {
1267 // Don't try to store a TTL bigger than we can represent in platform time units
1268 if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
1269 ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
1270 else if (ttl == 0) // And Zero TTL is illegal
1271 ttl = DefaultTTLforRRType(rrtype);
1272
1273 // Field Group 1: The actual information pertaining to this resource record
1274 rr->resrec.RecordType = RecordType;
1275 rr->resrec.InterfaceID = InterfaceID;
1276 rr->resrec.name = &rr->namestorage;
1277 rr->resrec.rrtype = rrtype;
1278 rr->resrec.rrclass = kDNSClass_IN;
1279 rr->resrec.rroriginalttl = ttl;
1280 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1281 // rr->resrec.rdestimate = set in mDNS_Register_internal
1282 // rr->resrec.rdata = MUST be set by client
1283
1284 if (RDataStorage)
1285 rr->resrec.rdata = RDataStorage;
1286 else
1287 {
1288 rr->resrec.rdata = &rr->rdatastorage;
1289 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
1290 }
1291
1292 // Field Group 2: Persistent metadata for Authoritative Records
1293 rr->Additional1 = mDNSNULL;
1294 rr->Additional2 = mDNSNULL;
1295 rr->DependentOn = mDNSNULL;
1296 rr->RRSet = mDNSNULL;
1297 rr->RecordCallback = Callback;
1298 rr->RecordContext = Context;
1299
1300 rr->AutoTarget = Target_Manual;
1301 rr->AllowRemoteQuery = mDNSfalse;
1302 rr->ForceMCast = mDNSfalse;
1303
1304 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1305 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1306
1307 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1308 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1309 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1310 rr->state = regState_Zero;
1311 rr->uselease = 0;
1312 rr->expire = 0;
1313 rr->Private = 0;
1314 rr->id = zeroID;
1315 rr->zone.c[0] = 0;
1316 rr->UpdateServer = zeroAddr;
1317 rr->UpdatePort = zeroIPPort;
1318 rr->nta = mDNSNULL;
1319 rr->tcp = mDNSNULL;
1320 rr->OrigRData = 0;
1321 rr->OrigRDLen = 0;
1322 rr->InFlightRData = 0;
1323 rr->InFlightRDLen = 0;
1324 rr->QueuedRData = 0;
1325 rr->QueuedRDLen = 0;
1326
1327 rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
1328 }
1329
1330 mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
1331 {
1332 switch(rr->rrtype)
1333 {
1334 case kDNSType_NS:
1335 case kDNSType_CNAME:
1336 case kDNSType_PTR:
1337 case kDNSType_DNAME: return DomainNameHashValue(&rr->rdata->u.name);
1338
1339 case kDNSType_SOA: return rr->rdata->u.soa.serial +
1340 rr->rdata->u.soa.refresh +
1341 rr->rdata->u.soa.retry +
1342 rr->rdata->u.soa.expire +
1343 rr->rdata->u.soa.min +
1344 DomainNameHashValue(&rr->rdata->u.soa.mname) +
1345 DomainNameHashValue(&rr->rdata->u.soa.rname);
1346
1347 case kDNSType_MX:
1348 case kDNSType_AFSDB:
1349 case kDNSType_RT:
1350 case kDNSType_KX: return DomainNameHashValue(&rr->rdata->u.mx.exchange);
1351
1352 case kDNSType_RP: return DomainNameHashValue(&rr->rdata->u.rp.mbox) + DomainNameHashValue(&rr->rdata->u.rp.txt);
1353
1354 case kDNSType_PX: return DomainNameHashValue(&rr->rdata->u.px.map822) + DomainNameHashValue(&rr->rdata->u.px.mapx400);
1355
1356 case kDNSType_SRV: return DomainNameHashValue(&rr->rdata->u.srv.target);
1357
1358 case kDNSType_OPT: // Okay to use blind memory sum because there are no 'holes' in the in-memory representation
1359
1360 default:
1361 {
1362 mDNSu32 sum = 0;
1363 int i;
1364 for (i=0; i+1 < rr->rdlength; i+=2)
1365 {
1366 sum += (((mDNSu32)(rr->rdata->u.data[i])) << 8) | rr->rdata->u.data[i+1];
1367 sum = (sum<<3) | (sum>>29);
1368 }
1369 if (i < rr->rdlength)
1370 {
1371 sum += ((mDNSu32)(rr->rdata->u.data[i])) << 8;
1372 }
1373 return(sum);
1374 }
1375 }
1376 }
1377
1378 // r1 has to be a full ResourceRecord including rrtype and rdlength
1379 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1380 mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2)
1381 {
1382 switch(r1->rrtype)
1383 {
1384 case kDNSType_NS:
1385 case kDNSType_CNAME:
1386 case kDNSType_PTR:
1387 case kDNSType_DNAME:return(SameDomainName(&r1->rdata->u.name, &r2->name));
1388
1389 case kDNSType_SOA: return(mDNSBool)( r1->rdata->u.soa.serial == r2->soa.serial &&
1390 r1->rdata->u.soa.refresh == r2->soa.refresh &&
1391 r1->rdata->u.soa.retry == r2->soa.retry &&
1392 r1->rdata->u.soa.expire == r2->soa.expire &&
1393 r1->rdata->u.soa.min == r2->soa.min &&
1394 SameDomainName(&r1->rdata->u.soa.mname, &r2->soa.mname) &&
1395 SameDomainName(&r1->rdata->u.soa.rname, &r2->soa.rname));
1396
1397 case kDNSType_MX:
1398 case kDNSType_AFSDB:
1399 case kDNSType_RT:
1400 case kDNSType_KX: return(mDNSBool)( r1->rdata->u.mx.preference == r2->mx.preference &&
1401 SameDomainName(&r1->rdata->u.mx.exchange, &r2->mx.exchange));
1402
1403 case kDNSType_RP: return(mDNSBool)( SameDomainName(&r1->rdata->u.rp.mbox, &r2->rp.mbox) &&
1404 SameDomainName(&r1->rdata->u.rp.txt, &r2->rp.txt));
1405
1406 case kDNSType_PX: return(mDNSBool)( r1->rdata->u.px.preference == r2->px.preference &&
1407 SameDomainName(&r1->rdata->u.px.map822, &r2->px.map822) &&
1408 SameDomainName(&r1->rdata->u.px.mapx400, &r2->px.mapx400));
1409
1410 case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->srv.priority &&
1411 r1->rdata->u.srv.weight == r2->srv.weight &&
1412 mDNSSameIPPort(r1->rdata->u.srv.port, r2->srv.port) &&
1413 SameDomainName(&r1->rdata->u.srv.target, &r2->srv.target));
1414
1415 case kDNSType_OPT: // Okay to use blind memory compare because there are no 'holes' in the in-memory representation
1416
1417 default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->data, r1->rdlength));
1418 }
1419 }
1420
1421 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1422 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1423 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1424 // because it has to check all the way to the end of the names to be sure.
1425 // In cases where we know in advance that the names match it's especially advantageous to skip the
1426 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1427
1428 mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1429 {
1430 if (rr->InterfaceID &&
1431 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1432 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1433
1434 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1435 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1436
1437 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1438 if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse);
1439 if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1440
1441 return(mDNStrue);
1442 }
1443
1444 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1445 {
1446 if (rr->InterfaceID &&
1447 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1448 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1449
1450 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1451 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1452
1453 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1454 if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse);
1455 if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1456 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1457 }
1458
1459 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1460 {
1461 const RDataBody *rd = &rr->rdata->u;
1462 const domainname *const name = estimate ? rr->name : mDNSNULL;
1463 if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1464 else switch (rr->rrtype)
1465 {
1466 case kDNSType_A: return(sizeof(rd->ipv4));
1467
1468 case kDNSType_NS:
1469 case kDNSType_CNAME:
1470 case kDNSType_PTR:
1471 case kDNSType_DNAME:return(CompressedDomainNameLength(&rd->name, name));
1472
1473 case kDNSType_SOA: return(mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
1474 CompressedDomainNameLength(&rd->soa.rname, name) +
1475 5 * sizeof(mDNSOpaque32));
1476
1477 case kDNSType_NULL:
1478 case kDNSType_TSIG:
1479 case kDNSType_TXT:
1480 case kDNSType_X25:
1481 case kDNSType_ISDN:
1482 case kDNSType_LOC:
1483 case kDNSType_DHCID:return(rr->rdlength); // Not self-describing, so have to just trust rdlength
1484
1485 case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
1486
1487 case kDNSType_MX:
1488 case kDNSType_AFSDB:
1489 case kDNSType_RT:
1490 case kDNSType_KX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
1491
1492 case kDNSType_RP: return(mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
1493 CompressedDomainNameLength(&rd->rp.txt, name));
1494
1495 case kDNSType_PX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
1496 CompressedDomainNameLength(&rd->px.mapx400, name));
1497
1498 case kDNSType_AAAA: return(sizeof(rd->ipv6));
1499
1500 case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
1501
1502 case kDNSType_OPT: return(rr->rdlength);
1503
1504 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
1505 return(rr->rdlength);
1506 }
1507 }
1508
1509 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1510 {
1511 mDNSu16 len;
1512
1513 switch(rrtype)
1514 {
1515 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1516
1517 case kDNSType_NS: // Same as PTR
1518 case kDNSType_MD: // Same as PTR
1519 case kDNSType_MF: // Same as PTR
1520 case kDNSType_CNAME:// Same as PTR
1521 //case kDNSType_SOA not checked
1522 case kDNSType_MB: // Same as PTR
1523 case kDNSType_MG: // Same as PTR
1524 case kDNSType_MR: // Same as PTR
1525 //case kDNSType_NULL not checked (no specified format, so always valid)
1526 //case kDNSType_WKS not checked
1527 case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
1528 return(len <= MAX_DOMAIN_NAME && rdlength == len);
1529
1530 case kDNSType_HINFO:// Same as TXT (roughly)
1531 case kDNSType_MINFO:// Same as TXT (roughly)
1532 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
1533 {
1534 const mDNSu8 *ptr = rd->u.txt.c;
1535 const mDNSu8 *end = rd->u.txt.c + rdlength;
1536 while (ptr < end) ptr += 1 + ptr[0];
1537 return (ptr == end);
1538 }
1539
1540 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1541
1542 case kDNSType_MX: // Must be at least two-byte preference, plus domainname
1543 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1544 len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
1545 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1546
1547 case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname
1548 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1549 len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
1550 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1551
1552 default: return(mDNStrue); // Allow all other types without checking
1553 }
1554 }
1555
1556 // ***************************************************************************
1557 #if COMPILER_LIKES_PRAGMA_MARK
1558 #pragma mark -
1559 #pragma mark - DNS Message Creation Functions
1560 #endif
1561
1562 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1563 {
1564 h->id = id;
1565 h->flags = flags;
1566 h->numQuestions = 0;
1567 h->numAnswers = 0;
1568 h->numAuthorities = 0;
1569 h->numAdditionals = 0;
1570 }
1571
1572 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1573 {
1574 const mDNSu8 *result = end - *domname - 1;
1575
1576 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1577
1578 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1579 while (result >= base)
1580 {
1581 // If the length byte and first character of the label match, then check further to see
1582 // if this location in the packet will yield a useful name compression pointer.
1583 if (result[0] == domname[0] && result[1] == domname[1])
1584 {
1585 const mDNSu8 *name = domname;
1586 const mDNSu8 *targ = result;
1587 while (targ + *name < end)
1588 {
1589 // First see if this label matches
1590 int i;
1591 const mDNSu8 *pointertarget;
1592 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
1593 if (i <= *name) break; // If label did not match, bail out
1594 targ += 1 + *name; // Else, did match, so advance target pointer
1595 name += 1 + *name; // and proceed to check next label
1596 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
1597 if (*name == 0) break; // If no more labels to match, we failed, so bail out
1598
1599 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1600 if (targ[0] < 0x40) continue; // If length value, continue to check next label
1601 if (targ[0] < 0xC0) break; // If 40-BF, not valid
1602 if (targ+1 >= end) break; // Second byte not present!
1603 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
1604 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
1605 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1606 targ = pointertarget;
1607 }
1608 }
1609 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1610 }
1611 return(mDNSNULL);
1612 }
1613
1614 // Put a string of dot-separated labels as length-prefixed labels
1615 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1616 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1617 // end points to the end of the message so far
1618 // ptr points to where we want to put the name
1619 // limit points to one byte past the end of the buffer that we must not overrun
1620 // domainname is the name to put
1621 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1622 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1623 {
1624 const mDNSu8 *const base = (const mDNSu8 *)msg;
1625 const mDNSu8 * np = name->c;
1626 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1627 const mDNSu8 * pointer = mDNSNULL;
1628 const mDNSu8 *const searchlimit = ptr;
1629
1630 if (!ptr) { LogMsg("putDomainNameAsLabels ptr is null"); return(mDNSNULL); }
1631
1632 while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
1633 {
1634 if (*np > MAX_DOMAIN_LABEL)
1635 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1636
1637 // This check correctly allows for the final trailing root label:
1638 // e.g.
1639 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1640 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1641 // We know that max will be at name->c[255]
1642 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1643 // six bytes, then exit the loop, write the final terminating root label, and the domain
1644 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1645 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1646 if (np + 1 + *np >= max)
1647 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); }
1648
1649 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1650 if (pointer) // Use a compression pointer if we can
1651 {
1652 mDNSu16 offset = (mDNSu16)(pointer - base);
1653 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1654 *ptr++ = (mDNSu8)( offset & 0xFF);
1655 return(ptr);
1656 }
1657 else // Else copy one label and try again
1658 {
1659 int i;
1660 mDNSu8 len = *np++;
1661 if (ptr + 1 + len >= limit) return(mDNSNULL);
1662 *ptr++ = len;
1663 for (i=0; i<len; i++) *ptr++ = *np++;
1664 }
1665 }
1666
1667 if (ptr < limit) // If we didn't run out of space
1668 {
1669 *ptr++ = 0; // Put the final root label
1670 return(ptr); // and return
1671 }
1672
1673 return(mDNSNULL);
1674 }
1675
1676 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1677 {
1678 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1679 ptr[1] = (mDNSu8)((val ) & 0xFF);
1680 return ptr + sizeof(mDNSOpaque16);
1681 }
1682
1683 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
1684 {
1685 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
1686 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
1687 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
1688 ptr[3] = (mDNSu8)((val ) & 0xFF);
1689 return ptr + sizeof(mDNSu32);
1690 }
1691
1692 mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, const ResourceRecord *const rr)
1693 {
1694 int nput = 0;
1695 rdataOPT *opt;
1696
1697 while (nput < rr->rdlength)
1698 {
1699 // check if space for opt/optlen
1700 if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err;
1701 opt = (rdataOPT *)(rr->rdata->u.data + nput);
1702 ptr = putVal16(ptr, opt->opt);
1703 ptr = putVal16(ptr, opt->optlen);
1704 nput += 2 * sizeof(mDNSu16);
1705 if (opt->opt == kDNSOpt_LLQ)
1706 {
1707 if (ptr + LLQ_OPTLEN > limit) goto space_err;
1708 ptr = putVal16(ptr, opt->OptData.llq.vers);
1709 ptr = putVal16(ptr, opt->OptData.llq.llqOp);
1710 ptr = putVal16(ptr, opt->OptData.llq.err);
1711 mDNSPlatformMemCopy(ptr, opt->OptData.llq.id.b, 8); // 8-byte id
1712 ptr += 8;
1713 ptr = putVal32(ptr, opt->OptData.llq.llqlease);
1714 nput += LLQ_OPTLEN;
1715 }
1716 else if (opt->opt == kDNSOpt_Lease)
1717 {
1718 if (ptr + sizeof(mDNSs32) > limit) goto space_err;
1719 ptr = putVal32(ptr, opt->OptData.updatelease);
1720 nput += sizeof(mDNSs32);
1721 }
1722 else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; }
1723 }
1724
1725 return ptr;
1726
1727 space_err:
1728 LogMsg("ERROR: putOptRData - out of space");
1729 return mDNSNULL;
1730 }
1731
1732 mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
1733 {
1734 mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
1735 *ptr += sizeof(mDNSOpaque16);
1736 return val;
1737 }
1738
1739 mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen)
1740 {
1741 int nread = 0;
1742 ResourceRecord *const rr = &cr->r.resrec;
1743 rdataOPT *opt = (rdataOPT *)rr->rdata->u.data;
1744
1745 while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOPT))
1746 {
1747 // space for opt + optlen
1748 if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err;
1749 opt->opt = getVal16(&ptr);
1750 opt->optlen = getVal16(&ptr);
1751 nread += 2 * sizeof(mDNSu16);
1752 if (opt->opt == kDNSOpt_LLQ)
1753 {
1754 if ((unsigned)(limit - ptr) < LLQ_OPTLEN) goto space_err;
1755 opt->OptData.llq.vers = getVal16(&ptr);
1756 opt->OptData.llq.llqOp = getVal16(&ptr);
1757 opt->OptData.llq.err = getVal16(&ptr);
1758 mDNSPlatformMemCopy(opt->OptData.llq.id.b, ptr, 8);
1759 ptr += 8;
1760 opt->OptData.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1761 if (opt->OptData.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
1762 opt->OptData.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
1763 ptr += sizeof(mDNSOpaque32);
1764 nread += LLQ_OPTLEN;
1765 }
1766 else if (opt->opt == kDNSOpt_Lease)
1767 {
1768 if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err;
1769
1770 opt->OptData.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1771 if (opt->OptData.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
1772 opt->OptData.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
1773 ptr += sizeof(mDNSs32);
1774 nread += sizeof(mDNSs32);
1775 }
1776 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
1777 opt++; // increment pointer into rdatabody
1778 }
1779
1780 rr->rdlength = pktRDLen;
1781 return ptr;
1782
1783 space_err:
1784 LogMsg("ERROR: getLLQRdata - out of space");
1785 return mDNSNULL;
1786 }
1787
1788 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1789 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
1790 {
1791 switch (rr->rrtype)
1792 {
1793 case kDNSType_A: if (rr->rdlength != 4)
1794 {
1795 debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength);
1796 return(mDNSNULL);
1797 }
1798 if (ptr + 4 > limit) return(mDNSNULL);
1799 *ptr++ = rr->rdata->u.ipv4.b[0];
1800 *ptr++ = rr->rdata->u.ipv4.b[1];
1801 *ptr++ = rr->rdata->u.ipv4.b[2];
1802 *ptr++ = rr->rdata->u.ipv4.b[3];
1803 return(ptr);
1804
1805 case kDNSType_NS:
1806 case kDNSType_CNAME:
1807 case kDNSType_PTR:
1808 case kDNSType_DNAME:return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
1809
1810 case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.soa.mname);
1811 if (!ptr) return(mDNSNULL);
1812 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.soa.rname);
1813 if (!ptr || ptr + 20 > limit) return(mDNSNULL);
1814 ptr = putVal32(ptr, rr->rdata->u.soa.serial);
1815 ptr = putVal32(ptr, rr->rdata->u.soa.refresh);
1816 ptr = putVal32(ptr, rr->rdata->u.soa.retry);
1817 ptr = putVal32(ptr, rr->rdata->u.soa.expire);
1818 ptr = putVal32(ptr, rr->rdata->u.soa.min);
1819 return(ptr);
1820
1821 case kDNSType_NULL:
1822 case kDNSType_HINFO:
1823 case kDNSType_TSIG:
1824 case kDNSType_TXT:
1825 case kDNSType_X25:
1826 case kDNSType_ISDN:
1827 case kDNSType_LOC:
1828 case kDNSType_DHCID:if (ptr + rr->rdlength > limit) return(mDNSNULL);
1829 mDNSPlatformMemCopy(ptr, rr->rdata->u.data, rr->rdlength);
1830 return(ptr + rr->rdlength);
1831
1832 case kDNSType_MX:
1833 case kDNSType_AFSDB:
1834 case kDNSType_RT:
1835 case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL);
1836 ptr = putVal16(ptr, rr->rdata->u.mx.preference);
1837 return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.mx.exchange));
1838
1839 case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.rp.mbox);
1840 if (!ptr) return(mDNSNULL);
1841 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.rp.txt);
1842 return(ptr);
1843
1844 case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL);
1845 ptr = putVal16(ptr, rr->rdata->u.px.preference);
1846 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.px.map822);
1847 if (!ptr) return(mDNSNULL);
1848 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.px.mapx400);
1849 return(ptr);
1850
1851 case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
1852 {
1853 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
1854 return(mDNSNULL);
1855 }
1856 if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL);
1857 mDNSPlatformMemCopy(ptr, &rr->rdata->u.ipv6, sizeof(rr->rdata->u.ipv6));
1858 return(ptr + sizeof(rr->rdata->u.ipv6));
1859
1860 case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL);
1861 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8);
1862 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF);
1863 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8);
1864 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF);
1865 *ptr++ = rr->rdata->u.srv.port.b[0];
1866 *ptr++ = rr->rdata->u.srv.port.b[1];
1867 return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target));
1868
1869 case kDNSType_OPT: return putOptRData(ptr, limit, rr);
1870
1871 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
1872 if (ptr + rr->rdlength > limit) return(mDNSNULL);
1873 mDNSPlatformMemCopy(ptr, rr->rdata->u.data, rr->rdlength);
1874 return(ptr + rr->rdlength);
1875 }
1876 }
1877
1878 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
1879 {
1880 mDNSu16 rrclass = (rr->rrtype == kDNSType_OPT) ? NormalMaxDNSMessageData : rr->rrclass;
1881 mDNSu8 *endofrdata;
1882 mDNSu16 actualLength;
1883
1884 if (rr->RecordType == kDNSRecordTypeUnregistered)
1885 {
1886 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1887 return(ptr);
1888 }
1889
1890 if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); }
1891
1892 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
1893 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1894 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
1895 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
1896 ptr[2] = (mDNSu8)(rrclass >> 8);
1897 ptr[3] = (mDNSu8)(rrclass & 0xFF);
1898 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
1899 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
1900 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
1901 ptr[7] = (mDNSu8)( ttl & 0xFF);
1902 endofrdata = putRData(msg, ptr+10, limit, rr);
1903 if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
1904
1905 // Go back and fill in the actual number of data bytes we wrote
1906 // (actualLength can be less than rdlength when domain name compression is used)
1907 actualLength = (mDNSu16)(endofrdata - ptr - 10);
1908 ptr[8] = (mDNSu8)(actualLength >> 8);
1909 ptr[9] = (mDNSu8)(actualLength & 0xFF);
1910
1911 if (count) (*count)++;
1912 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1913 return(endofrdata);
1914 }
1915
1916 mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32
1917 maxttl)
1918 {
1919 if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
1920 return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
1921 }
1922
1923 mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1924 mDNSu16 *count, const AuthRecord *rr)
1925 {
1926 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
1927 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1928 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
1929 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
1930 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
1931 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
1932 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
1933 ptr[8] = ptr[9] = 0; // RDATA length is zero
1934 (*count)++;
1935 return(ptr + 10);
1936 }
1937
1938 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
1939 {
1940 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1941 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1942 ptr[0] = (mDNSu8)(rrtype >> 8);
1943 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1944 ptr[2] = (mDNSu8)(rrclass >> 8);
1945 ptr[3] = (mDNSu8)(rrclass & 0xFF);
1946 msg->h.numQuestions++;
1947 return(ptr+4);
1948 }
1949
1950 // for dynamic updates
1951 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
1952 {
1953 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
1954 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
1955 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
1956 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
1957 *ptr++ = zoneClass.b[0];
1958 *ptr++ = zoneClass.b[1];
1959 msg->h.mDNS_numZones++;
1960 return ptr;
1961 }
1962
1963 // for dynamic updates
1964 mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
1965 {
1966 AuthRecord prereq;
1967 mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1968 AssignDomainName(&prereq.namestorage, name);
1969 prereq.resrec.rrtype = kDNSQType_ANY;
1970 prereq.resrec.rrclass = kDNSClass_NONE;
1971 return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
1972 }
1973
1974 // for dynamic updates
1975 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
1976 {
1977 // deletion: specify record w/ TTL 0, class NONE
1978 const mDNSu16 origclass = rr->rrclass;
1979 rr->rrclass = kDNSClass_NONE;
1980 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
1981 rr->rrclass = origclass;
1982 return ptr;
1983 }
1984
1985 mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype)
1986 {
1987 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1988 mDNSu16 class = kDNSQClass_ANY;
1989
1990 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1991 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
1992 ptr[0] = (mDNSu8)(rrtype >> 8);
1993 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1994 ptr[2] = (mDNSu8)(class >> 8);
1995 ptr[3] = (mDNSu8)(class & 0xFF);
1996 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
1997 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
1998
1999 msg->h.mDNS_numUpdates++;
2000 return ptr + 10;
2001 }
2002
2003 // for dynamic updates
2004 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
2005 {
2006 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
2007 mDNSu16 class = kDNSQClass_ANY;
2008 mDNSu16 rrtype = kDNSQType_ANY;
2009
2010 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2011 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2012 ptr[0] = (mDNSu8)(rrtype >> 8);
2013 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2014 ptr[2] = (mDNSu8)(class >> 8);
2015 ptr[3] = (mDNSu8)(class & 0xFF);
2016 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2017 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2018
2019 msg->h.mDNS_numUpdates++;
2020 return ptr + 10;
2021 }
2022
2023 // for dynamic updates
2024 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
2025 {
2026 AuthRecord rr;
2027 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
2028 rr.resrec.rrclass = NormalMaxDNSMessageData;
2029 rr.resrec.rdlength = LEASE_OPT_RDLEN;
2030 rr.resrec.rdestimate = LEASE_OPT_RDLEN;
2031 rr.resrec.rdata->u.opt.opt = kDNSOpt_Lease;
2032 rr.resrec.rdata->u.opt.optlen = sizeof(mDNSs32);
2033 rr.resrec.rdata->u.opt.OptData.updatelease = lease;
2034 end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0);
2035 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
2036 return end;
2037 }
2038
2039 mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo)
2040 {
2041 if (authInfo && authInfo->AutoTunnel)
2042 {
2043 AuthRecord hinfo;
2044 mDNSu8 *h = hinfo.rdatastorage.u.data;
2045 mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
2046 mDNSu8 *newptr;
2047 mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
2048 AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
2049 AppendDomainName (&hinfo.namestorage, &authInfo->domain);
2050 hinfo.resrec.rroriginalttl = 0;
2051 mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]);
2052 h += 1 + (int)h[0];
2053 mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]);
2054 hinfo.resrec.rdlength = len;
2055 hinfo.resrec.rdestimate = len;
2056 newptr = PutResourceRecord(msg, end, &msg->h.numAdditionals, &hinfo.resrec);
2057 return newptr;
2058 }
2059 else
2060 return end;
2061 }
2062
2063 // ***************************************************************************
2064 #if COMPILER_LIKES_PRAGMA_MARK
2065 #pragma mark -
2066 #pragma mark - DNS Message Parsing Functions
2067 #endif
2068
2069 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
2070 {
2071 mDNSu32 sum = 0;
2072 const mDNSu8 *c;
2073
2074 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
2075 {
2076 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
2077 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
2078 sum = (sum<<3) | (sum>>29);
2079 }
2080 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
2081 return(sum);
2082 }
2083
2084 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
2085 {
2086 domainname *target;
2087 if (NewRData)
2088 {
2089 rr->rdata = NewRData;
2090 rr->rdlength = rdlength;
2091 }
2092 // Must not try to get target pointer until after updating rr->rdata
2093 target = GetRRDomainNameTarget(rr);
2094 rr->rdlength = GetRDLength(rr, mDNSfalse);
2095 rr->rdestimate = GetRDLength(rr, mDNStrue);
2096 rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr);
2097 }
2098
2099 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
2100 {
2101 mDNSu16 total = 0;
2102
2103 if (ptr < (mDNSu8*)msg || ptr >= end)
2104 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2105
2106 while (1) // Read sequence of labels
2107 {
2108 const mDNSu8 len = *ptr++; // Read length of this label
2109 if (len == 0) return(ptr); // If length is zero, that means this name is complete
2110 switch (len & 0xC0)
2111 {
2112 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2113 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2114 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
2115 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
2116 ptr += len;
2117 total += 1 + len;
2118 break;
2119
2120 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
2121 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
2122 case 0xC0: return(ptr+1);
2123 }
2124 }
2125 }
2126
2127 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2128 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
2129 domainname *const name)
2130 {
2131 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
2132 mDNSu8 *np = name->c; // Name pointer
2133 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
2134
2135 if (ptr < (mDNSu8*)msg || ptr >= end)
2136 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2137
2138 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2139
2140 while (1) // Read sequence of labels
2141 {
2142 const mDNSu8 len = *ptr++; // Read length of this label
2143 if (len == 0) break; // If length is zero, that means this name is complete
2144 switch (len & 0xC0)
2145 {
2146 int i;
2147 mDNSu16 offset;
2148
2149 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2150 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2151 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
2152 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
2153 *np++ = len;
2154 for (i=0; i<len; i++) *np++ = *ptr++;
2155 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2156 break;
2157
2158 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
2159 return(mDNSNULL);
2160
2161 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
2162
2163 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
2164 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
2165 ptr = (mDNSu8 *)msg + offset;
2166 if (ptr < (mDNSu8*)msg || ptr >= end)
2167 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
2168 if (*ptr & 0xC0)
2169 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
2170 break;
2171 }
2172 }
2173
2174 if (nextbyte) return(nextbyte);
2175 else return(ptr);
2176 }
2177
2178 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2179 {
2180 mDNSu16 pktrdlength;
2181
2182 ptr = skipDomainName(msg, ptr, end);
2183 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
2184
2185 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2186 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
2187 ptr += 10;
2188 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2189
2190 return(ptr + pktrdlength);
2191 }
2192
2193 mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
2194 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr)
2195 {
2196 CacheRecord *rr = &largecr->r;
2197 mDNSu16 pktrdlength;
2198
2199 if (largecr == &m->rec && largecr->r.resrec.RecordType)
2200 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &largecr->r));
2201
2202 rr->next = mDNSNULL;
2203 rr->resrec.name = &largecr->namestorage;
2204
2205 rr->NextInKAList = mDNSNULL;
2206 rr->TimeRcvd = m ? m->timenow : 0;
2207 rr->DelayDelivery = 0;
2208 rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
2209 rr->LastUsed = m ? m->timenow : 0;
2210 rr->CRActiveQuestion = mDNSNULL;
2211 rr->UnansweredQueries = 0;
2212 rr->LastUnansweredTime= 0;
2213 rr->MPUnansweredQ = 0;
2214 rr->MPLastUnansweredQT= 0;
2215 rr->MPUnansweredKA = 0;
2216 rr->MPExpectingKA = mDNSfalse;
2217 rr->NextInCFList = mDNSNULL;
2218
2219 rr->resrec.InterfaceID = InterfaceID;
2220 ptr = getDomainName(msg, ptr, end, &largecr->namestorage);
2221 if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
2222
2223 if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2224
2225 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
2226 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
2227 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
2228 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
2229 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
2230 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
2231 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
2232 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
2233
2234 // If mDNS record has cache-flush bit set, we mark it unique
2235 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
2236 // authoritative for the entire RRSet), unless this is a truncated response
2237 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC)))
2238 RecordType |= kDNSRecordTypePacketUniqueMask;
2239 ptr += 10;
2240 if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2241 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
2242
2243 rr->resrec.rdata = (RData*)&rr->rdatastorage;
2244 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
2245
2246 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
2247
2248 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
2249 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
2250 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
2251 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
2252 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
2253 if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
2254 rr->resrec.rdlength = 0;
2255 else switch (rr->resrec.rrtype)
2256 {
2257 case kDNSType_A: if (pktrdlength != sizeof(mDNSv4Addr)) return(mDNSNULL);
2258 rr->resrec.rdata->u.ipv4.b[0] = ptr[0];
2259 rr->resrec.rdata->u.ipv4.b[1] = ptr[1];
2260 rr->resrec.rdata->u.ipv4.b[2] = ptr[2];
2261 rr->resrec.rdata->u.ipv4.b[3] = ptr[3];
2262 break;
2263
2264 case kDNSType_NS:
2265 case kDNSType_CNAME:
2266 case kDNSType_PTR:
2267 case kDNSType_DNAME:ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name);
2268 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
2269 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
2270 break;
2271
2272 case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname);
2273 if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; }
2274 ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname);
2275 if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; }
2276 if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL; }
2277 rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
2278 rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
2279 rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
2280 rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
2281 rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
2282 break;
2283
2284 case kDNSType_NULL:
2285 case kDNSType_HINFO:
2286 case kDNSType_TSIG:
2287 case kDNSType_TXT:
2288 case kDNSType_X25:
2289 case kDNSType_ISDN:
2290 case kDNSType_LOC:
2291 case kDNSType_DHCID:if (pktrdlength > rr->resrec.rdata->MaxRDLength)
2292 {
2293 debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
2294 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
2295 return(mDNSNULL);
2296 }
2297 rr->resrec.rdlength = pktrdlength;
2298 mDNSPlatformMemCopy(rr->resrec.rdata->u.data, ptr, pktrdlength);
2299 break;
2300
2301 case kDNSType_MX:
2302 case kDNSType_AFSDB:
2303 case kDNSType_RT:
2304 case kDNSType_KX: if (pktrdlength < 3) return(mDNSNULL); // Preference + domainname
2305 rr->resrec.rdata->u.mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2306 ptr = getDomainName(msg, ptr+2, end, &rr->resrec.rdata->u.mx.exchange);
2307 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL); }
2308 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2309 break;
2310
2311 case kDNSType_RP: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.rp.mbox); // Domainname + domainname
2312 if (!ptr) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL; }
2313 ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.rp.txt);
2314 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL; }
2315 break;
2316
2317 case kDNSType_PX: if (pktrdlength < 4) return(mDNSNULL); // Preference + domainname + domainname
2318 rr->resrec.rdata->u.px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2319 ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.px.map822);
2320 if (!ptr) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL; }
2321 ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.px.mapx400);
2322 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL; }
2323 break;
2324
2325 case kDNSType_AAAA: if (pktrdlength != sizeof(mDNSv6Addr)) return(mDNSNULL);
2326 mDNSPlatformMemCopy(&rr->resrec.rdata->u.ipv6, ptr, sizeof(rr->resrec.rdata->u.ipv6));
2327 break;
2328
2329 case kDNSType_SRV: if (pktrdlength < 7) return(mDNSNULL); // Priority + weight + port + domainname
2330 rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2331 rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2332 rr->resrec.rdata->u.srv.port.b[0] = ptr[4];
2333 rr->resrec.rdata->u.srv.port.b[1] = ptr[5];
2334 ptr = getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target);
2335 if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
2336 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
2337 break;
2338
2339 case kDNSType_OPT: ptr = getOptRdata(ptr, end, largecr, pktrdlength); break;
2340 if (ptr != end) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL); }
2341
2342 default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
2343 {
2344 debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
2345 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
2346 return(mDNSNULL);
2347 }
2348 debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
2349 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
2350 // Note: Just because we don't understand the record type, that doesn't
2351 // mean we fail. The DNS protocol specifies rdlength, so we can
2352 // safely skip over unknown records and ignore them.
2353 // We also grab a binary copy of the rdata anyway, since the caller
2354 // might know how to interpret it even if we don't.
2355 rr->resrec.rdlength = pktrdlength;
2356 mDNSPlatformMemCopy(rr->resrec.rdata->u.data, ptr, pktrdlength);
2357 break;
2358 }
2359
2360 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
2361 SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us
2362
2363 // Success! Now fill in RecordType to show this record contains valid data
2364 rr->resrec.RecordType = RecordType;
2365 return(end);
2366 }
2367
2368 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2369 {
2370 ptr = skipDomainName(msg, ptr, end);
2371 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
2372 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
2373 return(ptr+4);
2374 }
2375
2376 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
2377 DNSQuestion *question)
2378 {
2379 mDNSPlatformMemZero(question, sizeof(*question));
2380 question->InterfaceID = InterfaceID;
2381 ptr = getDomainName(msg, ptr, end, &question->qname);
2382 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
2383 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
2384
2385 question->qnamehash = DomainNameHashValue(&question->qname);
2386 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
2387 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
2388 return(ptr+4);
2389 }
2390
2391 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
2392 {
2393 int i;
2394 const mDNSu8 *ptr = msg->data;
2395 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
2396 return(ptr);
2397 }
2398
2399 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
2400 {
2401 int i;
2402 const mDNSu8 *ptr = LocateAnswers(msg, end);
2403 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
2404 return(ptr);
2405 }
2406
2407 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
2408 {
2409 int i;
2410 const mDNSu8 *ptr = LocateAuthorities(msg, end);
2411 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
2412 return (ptr);
2413 }
2414
2415 mDNSexport const mDNSu8 *LocateLLQOptData(const DNSMessage *const msg, const mDNSu8 *const end)
2416 {
2417 int i;
2418 const mDNSu8 *ptr = LocateAdditionals(msg, end);
2419
2420 // Locate the OPT record.
2421 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2422 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2423 // but not necessarily the *last* entry in the Additional Section.
2424 for (i = 0; ptr && i < msg->h.numAdditionals; i++)
2425 {
2426 if (ptr + 10 + LLQ_OPT_RDLEN <= end && // Make sure we have 10+22 bytes of data
2427 ptr[0] == 0 && // Name must be root label
2428 ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT
2429 ptr[2] == (kDNSType_OPT & 0xFF) &&
2430 ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)LLQ_OPT_RDLEN)
2431 return(ptr);
2432 else
2433 ptr = skipResourceRecord(msg, ptr, end);
2434 }
2435 return(mDNSNULL);
2436 }
2437
2438 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
2439 // it is callers responsibilty to clear m->rec.r.resrec.RecordType after use
2440 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
2441 // The code that currently calls this assumes there's only one, instead of iterating through the set
2442 mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
2443 {
2444 const mDNSu8 *ptr = LocateLLQOptData(msg, end);
2445 if (ptr)
2446 {
2447 ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
2448 if (ptr) return(&m->rec.r.resrec.rdata->u.opt);
2449 }
2450 return(mDNSNULL);
2451 }
2452
2453 mDNSexport const mDNSu8 *LocateLeaseOptData(const DNSMessage *const msg, const mDNSu8 *const end)
2454 {
2455 int i;
2456 const mDNSu8 *ptr = LocateAdditionals(msg, end);
2457
2458 // Locate the OPT record.
2459 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
2460 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
2461 // but not necessarily the *last* entry in the Additional Section.
2462 for (i = 0; ptr && i < msg->h.numAdditionals; i++)
2463 {
2464 if (ptr + 10 + LEASE_OPT_RDLEN <= end && // Make sure we have 10+8 bytes of data
2465 ptr[0] == 0 && // Name must be root label
2466 ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT
2467 ptr[2] == (kDNSType_OPT & 0xFF) &&
2468 ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)LEASE_OPT_RDLEN)
2469 return(ptr);
2470 else
2471 ptr = skipResourceRecord(msg, ptr, end);
2472 }
2473 return(mDNSNULL);
2474 }
2475
2476 // Get the lease life of records in a dynamic update
2477 // returns 0 on error or if no lease present
2478 mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end)
2479 {
2480 mDNSu32 result = 0;
2481 const mDNSu8 *ptr = LocateLeaseOptData(msg, end);
2482 if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
2483 if (ptr && m->rec.r.resrec.rdlength >= LEASE_OPT_RDLEN && m->rec.r.resrec.rdata->u.opt.opt == kDNSOpt_Lease)
2484 result = m->rec.r.resrec.rdata->u.opt.OptData.updatelease;
2485 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
2486 return(result);
2487 }
2488
2489 mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
2490 {
2491 int i;
2492 LogMsg("%2d %s", count, label);
2493 for (i = 0; i < count && ptr; i++)
2494 {
2495 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
2496 // but since it's only used for debugging (and probably only on OS X, not on
2497 // embedded systems) putting a 9kB object on the stack isn't a big problem.
2498 LargeCacheRecord largecr;
2499 ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
2500 if (ptr) LogMsg("%2d TTL%7d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
2501 }
2502 if (!ptr) LogMsg("ERROR: Premature end of packet data");
2503 return(ptr);
2504 }
2505
2506 #define DNS_OP_Name(X) ( \
2507 (X) == kDNSFlag0_OP_StdQuery ? "" : \
2508 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
2509 (X) == kDNSFlag0_OP_Status ? "Status " : \
2510 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
2511 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
2512 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
2513
2514 #define DNS_RC_Name(X) ( \
2515 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
2516 (X) == kDNSFlag1_RC_FmtErr ? "FmtErr" : \
2517 (X) == kDNSFlag1_RC_SrvErr ? "SrvErr" : \
2518 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
2519 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
2520 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
2521 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
2522 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
2523 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
2524 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
2525 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
2526
2527 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
2528 mDNSexport void DumpPacket(mDNS *const m, mDNSBool sent, char *transport, const mDNSAddr *addr, mDNSIPPort port, const DNSMessage *const msg, const mDNSu8 *const end)
2529 {
2530 mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
2531 const mDNSu8 *ptr = msg->data;
2532 int i;
2533 DNSQuestion q;
2534
2535 LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes %s %#a:%d%s --",
2536 sent ? "Sent" : "Received", transport,
2537 DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
2538 msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
2539 msg->h.flags.b[0], msg->h.flags.b[1],
2540 DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
2541 msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
2542 msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "",
2543 msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "",
2544 msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "",
2545 msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "",
2546 msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "",
2547 msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "",
2548 mDNSVal16(msg->h.id),
2549 end - msg->data,
2550 sent ? "to" : "from", addr, mDNSVal16(port),
2551 (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
2552 );
2553
2554 LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
2555 for (i = 0; i < msg->h.numQuestions && ptr; i++)
2556 {
2557 ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
2558 if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
2559 }
2560 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers");
2561 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities");
2562 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
2563 LogMsg("--------------");
2564 }
2565
2566 // ***************************************************************************
2567 #if COMPILER_LIKES_PRAGMA_MARK
2568 #pragma mark -
2569 #pragma mark - Packet Sending Functions
2570 #endif
2571
2572 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
2573 struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
2574
2575 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
2576 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
2577 mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
2578 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo)
2579 {
2580 mStatus status = mStatus_NoError;
2581 const mDNSu16 numQuestions = msg->h.numQuestions;
2582 const mDNSu16 numAnswers = msg->h.numAnswers;
2583 const mDNSu16 numAuthorities = msg->h.numAuthorities;
2584 const mDNSu16 numAdditionals = msg->h.numAdditionals;
2585 mDNSu16 tmpNumAdditionals = numAdditionals;
2586 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
2587
2588 if (end <= msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
2589 {
2590 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
2591 return mStatus_BadParamErr;
2592 }
2593
2594 end = putHINFO(m, msg, end, authInfo);
2595 if (!end) { LogMsg("mDNSSendDNSMessage: putHINFO failed"); status = mStatus_NoMemoryErr; }
2596 else
2597 {
2598 tmpNumAdditionals = msg->h.numAdditionals;
2599
2600 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2601 *ptr++ = (mDNSu8)(numQuestions >> 8);
2602 *ptr++ = (mDNSu8)(numQuestions & 0xFF);
2603 *ptr++ = (mDNSu8)(numAnswers >> 8);
2604 *ptr++ = (mDNSu8)(numAnswers & 0xFF);
2605 *ptr++ = (mDNSu8)(numAuthorities >> 8);
2606 *ptr++ = (mDNSu8)(numAuthorities & 0xFF);
2607 *ptr++ = (mDNSu8)(tmpNumAdditionals >> 8);
2608 *ptr++ = (mDNSu8)(tmpNumAdditionals & 0xFF);
2609
2610 if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order
2611 if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
2612 else
2613 {
2614 // Send the packet on the wire
2615 if (!sock)
2616 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport);
2617 else
2618 {
2619 mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
2620 mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
2621 long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets
2622 if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; }
2623 else
2624 {
2625 nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
2626 if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; }
2627 }
2628 }
2629 }
2630 }
2631
2632 // Put all the integer values back the way they were before we return
2633 msg->h.numQuestions = numQuestions;
2634 msg->h.numAnswers = numAnswers;
2635 msg->h.numAuthorities = numAuthorities;
2636
2637 // Dump the packet with the HINFO and TSIG
2638 if (mDNS_LogLevel >= MDNS_LOG_VERBOSE_DEBUG && !mDNSOpaque16IsZero(msg->h.id))
2639 {
2640 ptr = (mDNSu8 *)&msg->h.numAdditionals;
2641 msg->h.numAdditionals = (mDNSu16)ptr[0] << 8 | (mDNSu16)ptr[1];
2642 DumpPacket(m, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", dst, dstport, msg, end);
2643 }
2644
2645 // put the final integer value back the way it was
2646 msg->h.numAdditionals = numAdditionals;
2647
2648 return(status);
2649 }
2650
2651 // ***************************************************************************
2652 #if COMPILER_LIKES_PRAGMA_MARK
2653 #pragma mark -
2654 #pragma mark - RR List Management & Task Management
2655 #endif
2656
2657 mDNSexport void mDNS_Lock_(mDNS *const m)
2658 {
2659 // MUST grab the platform lock FIRST!
2660 mDNSPlatformLock(m);
2661
2662 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2663 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2664 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2665 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2666 #if ForceAlerts
2667 if (m->mDNS_busy != m->mDNS_reentrancy)
2668 {
2669 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
2670 *(long*)0 = 0;
2671 }
2672 #endif
2673
2674 // If this is an initial entry into the mDNSCore code, set m->timenow
2675 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2676 if (m->mDNS_busy == 0)
2677 {
2678 if (m->timenow)
2679 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m));
2680 m->timenow = mDNS_TimeNow_NoLock(m);
2681 if (m->timenow == 0) m->timenow = 1;
2682 }
2683 else if (m->timenow == 0)
2684 {
2685 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
2686 m->timenow = mDNS_TimeNow_NoLock(m);
2687 if (m->timenow == 0) m->timenow = 1;
2688 }
2689
2690 if (m->timenow_last - m->timenow > 0)
2691 {
2692 m->timenow_adjust += m->timenow_last - m->timenow;
2693 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust);
2694 m->timenow = m->timenow_last;
2695 }
2696 m->timenow_last = m->timenow;
2697
2698 // Increment mDNS_busy so we'll recognise re-entrant calls
2699 m->mDNS_busy++;
2700 }
2701
2702 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
2703 {
2704 mDNSs32 e = m->timenow + 0x78000000;
2705 if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
2706 if (m->NewQuestions)
2707 {
2708 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
2709 else return(m->timenow);
2710 }
2711 if (m->NewLocalOnlyQuestions) return(m->timenow);
2712 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) return(m->timenow);
2713 #ifndef UNICAST_DISABLED
2714 if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent;
2715 if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp;
2716 #endif
2717 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
2718
2719 if (!m->SleepState)
2720 {
2721 if (m->SuppressSending)
2722 {
2723 if (e - m->SuppressSending > 0) e = m->SuppressSending;
2724 }
2725 else
2726 {
2727 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
2728 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
2729 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
2730 }
2731 }
2732 return(e);
2733 }
2734
2735 mDNSexport void mDNS_Unlock_(mDNS *const m)
2736 {
2737 // Decrement mDNS_busy
2738 m->mDNS_busy--;
2739
2740 // Check for locking failures
2741 #if ForceAlerts
2742 if (m->mDNS_busy != m->mDNS_reentrancy)
2743 {
2744 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
2745 *(long*)0 = 0;
2746 }
2747 #endif
2748
2749 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2750 if (m->mDNS_busy == 0)
2751 {
2752 m->NextScheduledEvent = GetNextScheduledEvent(m);
2753 if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2754 m->timenow = 0;
2755 }
2756
2757 // MUST release the platform lock LAST!
2758 mDNSPlatformUnlock(m);
2759 }
2760
2761 // ***************************************************************************
2762 #if COMPILER_LIKES_PRAGMA_MARK
2763 #pragma mark -
2764 #pragma mark - Specialized mDNS version of vsnprintf
2765 #endif
2766
2767 static const struct mDNSprintf_format
2768 {
2769 unsigned leftJustify : 1;
2770 unsigned forceSign : 1;
2771 unsigned zeroPad : 1;
2772 unsigned havePrecision : 1;
2773 unsigned hSize : 1;
2774 unsigned lSize : 1;
2775 char altForm;
2776 char sign; // +, - or space
2777 unsigned int fieldWidth;
2778 unsigned int precision;
2779 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2780
2781 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
2782 {
2783 mDNSu32 nwritten = 0;
2784 int c;
2785 if (buflen == 0) return(0);
2786 buflen--; // Pre-reserve one space in the buffer for the terminating null
2787 if (buflen == 0) goto exit;
2788
2789 for (c = *fmt; c != 0; c = *++fmt)
2790 {
2791 if (c != '%')
2792 {
2793 *sbuffer++ = (char)c;
2794 if (++nwritten >= buflen) goto exit;
2795 }
2796 else
2797 {
2798 unsigned int i=0, j;
2799 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
2800 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
2801 // The size needs to be enough for a 256-byte domain name plus some error text.
2802 #define mDNS_VACB_Size 300
2803 char mDNS_VACB[mDNS_VACB_Size];
2804 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
2805 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
2806 char *s = mDNS_VACB_Lim, *digits;
2807 struct mDNSprintf_format F = mDNSprintf_format_default;
2808
2809 while (1) // decode flags
2810 {
2811 c = *++fmt;
2812 if (c == '-') F.leftJustify = 1;
2813 else if (c == '+') F.forceSign = 1;
2814 else if (c == ' ') F.sign = ' ';
2815 else if (c == '#') F.altForm++;
2816 else if (c == '0') F.zeroPad = 1;
2817 else break;
2818 }
2819
2820 if (c == '*') // decode field width
2821 {
2822 int f = va_arg(arg, int);
2823 if (f < 0) { f = -f; F.leftJustify = 1; }
2824 F.fieldWidth = (unsigned int)f;
2825 c = *++fmt;
2826 }
2827 else
2828 {
2829 for (; c >= '0' && c <= '9'; c = *++fmt)
2830 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
2831 }
2832
2833 if (c == '.') // decode precision
2834 {
2835 if ((c = *++fmt) == '*')
2836 { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
2837 else for (; c >= '0' && c <= '9'; c = *++fmt)
2838 F.precision = (10 * F.precision) + (c - '0');
2839 F.havePrecision = 1;
2840 }
2841
2842 if (F.leftJustify) F.zeroPad = 0;
2843
2844 conv:
2845 switch (c) // perform appropriate conversion
2846 {
2847 unsigned long n;
2848 case 'h' : F.hSize = 1; c = *++fmt; goto conv;
2849 case 'l' : // fall through
2850 case 'L' : F.lSize = 1; c = *++fmt; goto conv;
2851 case 'd' :
2852 case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long);
2853 else n = (unsigned long)va_arg(arg, int);
2854 if (F.hSize) n = (short) n;
2855 if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
2856 else if (F.forceSign) F.sign = '+';
2857 goto decimal;
2858 case 'u' : if (F.lSize) n = va_arg(arg, unsigned long);
2859 else n = va_arg(arg, unsigned int);
2860 if (F.hSize) n = (unsigned short) n;
2861 F.sign = 0;
2862 goto decimal;
2863 decimal: if (!F.havePrecision)
2864 {
2865 if (F.zeroPad)
2866 {
2867 F.precision = F.fieldWidth;
2868 if (F.sign) --F.precision;
2869 }
2870 if (F.precision < 1) F.precision = 1;
2871 }
2872 if (F.precision > mDNS_VACB_Size - 1)
2873 F.precision = mDNS_VACB_Size - 1;
2874 for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
2875 for (; i < F.precision; i++) *--s = '0';
2876 if (F.sign) { *--s = F.sign; i++; }
2877 break;
2878
2879 case 'o' : if (F.lSize) n = va_arg(arg, unsigned long);
2880 else n = va_arg(arg, unsigned int);
2881 if (F.hSize) n = (unsigned short) n;
2882 if (!F.havePrecision)
2883 {
2884 if (F.zeroPad) F.precision = F.fieldWidth;
2885 if (F.precision < 1) F.precision = 1;
2886 }
2887 if (F.precision > mDNS_VACB_Size - 1)
2888 F.precision = mDNS_VACB_Size - 1;
2889 for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
2890 if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
2891 for (; i < F.precision; i++) *--s = '0';
2892 break;
2893
2894 case 'a' : {
2895 unsigned char *a = va_arg(arg, unsigned char *);
2896 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
2897 else
2898 {
2899 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
2900 if (F.altForm)
2901 {
2902 mDNSAddr *ip = (mDNSAddr*)a;
2903 switch (ip->type)
2904 {
2905 case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
2906 case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
2907 default: F.precision = 0; break;
2908 }
2909 }
2910 if (F.altForm && !F.precision)
2911 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
2912 else switch (F.precision)
2913 {
2914 case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
2915 a[0], a[1], a[2], a[3]); break;
2916 case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
2917 a[0], a[1], a[2], a[3], a[4], a[5]); break;
2918 case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
2919 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
2920 a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
2921 a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
2922 default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
2923 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
2924 }
2925 }
2926 }
2927 break;
2928
2929 case 'p' : F.havePrecision = F.lSize = 1;
2930 F.precision = 8;
2931 case 'X' : digits = "0123456789ABCDEF";
2932 goto hexadecimal;
2933 case 'x' : digits = "0123456789abcdef";
2934 hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
2935 else n = va_arg(arg, unsigned int);
2936 if (F.hSize) n = (unsigned short) n;
2937 if (!F.havePrecision)
2938 {
2939 if (F.zeroPad)
2940 {
2941 F.precision = F.fieldWidth;
2942 if (F.altForm) F.precision -= 2;
2943 }
2944 if (F.precision < 1) F.precision = 1;
2945 }
2946 if (F.precision > mDNS_VACB_Size - 1)
2947 F.precision = mDNS_VACB_Size - 1;
2948 for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
2949 for (; i < F.precision; i++) *--s = '0';
2950 if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
2951 break;
2952
2953 case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
2954
2955 case 's' : s = va_arg(arg, char *);
2956 if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
2957 else switch (F.altForm)
2958 {
2959 case 0: i=0;
2960 if (!F.havePrecision) // C string
2961 while (s[i]) i++;
2962 else
2963 {
2964 while ((i < F.precision) && s[i]) i++;
2965 // Make sure we don't truncate in the middle of a UTF-8 character
2966 // If last character we got was any kind of UTF-8 multi-byte character,
2967 // then see if we have to back up.
2968 // This is not as easy as the similar checks below, because
2969 // here we can't assume it's safe to examine the *next* byte, so we
2970 // have to confine ourselves to working only backwards in the string.
2971 j = i; // Record where we got to
2972 // Now, back up until we find first non-continuation-char
2973 while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
2974 // Now s[i-1] is the first non-continuation-char
2975 // and (j-i) is the number of continuation-chars we found
2976 if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char
2977 {
2978 i--; // Tentatively eliminate this start-char as well
2979 // Now (j-i) is the number of characters we're considering eliminating.
2980 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
2981 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
2982 // (with sign extension) then the result has to be 0xFE.
2983 // If this is right, then we reinstate the tentatively eliminated bytes.
2984 if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
2985 }
2986 }
2987 break;
2988 case 1: i = (unsigned char) *s++; break; // Pascal string
2989 case 2: { // DNS label-sequence name
2990 unsigned char *a = (unsigned char *)s;
2991 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
2992 if (*a == 0) *s++ = '.'; // Special case for root DNS name
2993 while (*a)
2994 {
2995 char buf[63*4+1];
2996 if (*a > 63)
2997 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
2998 if (s + *a >= &mDNS_VACB[254])
2999 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
3000 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3001 // so it's clear what's a literal dot and what's a label separator
3002 ConvertDomainLabelToCString((domainlabel*)a, buf);
3003 s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
3004 a += 1 + *a;
3005 }
3006 i = (mDNSu32)(s - mDNS_VACB);
3007 s = mDNS_VACB; // Reset s back to the start of the buffer
3008 break;
3009 }
3010 }
3011 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3012 if (F.havePrecision && i > F.precision)
3013 { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
3014 break;
3015
3016 case 'n' : s = va_arg(arg, char *);
3017 if (F.hSize) * (short *) s = (short)nwritten;
3018 else if (F.lSize) * (long *) s = (long)nwritten;
3019 else * (int *) s = (int)nwritten;
3020 continue;
3021
3022 default: s = mDNS_VACB;
3023 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
3024
3025 case '%' : *sbuffer++ = (char)c;
3026 if (++nwritten >= buflen) goto exit;
3027 break;
3028 }
3029
3030 if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
3031 do {
3032 *sbuffer++ = ' ';
3033 if (++nwritten >= buflen) goto exit;
3034 } while (i < --F.fieldWidth);
3035
3036 // Make sure we don't truncate in the middle of a UTF-8 character.
3037 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3038 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3039 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3040 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3041 if (i > buflen - nwritten)
3042 { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
3043 for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
3044 nwritten += i;
3045 if (nwritten >= buflen) goto exit;
3046
3047 for (; i < F.fieldWidth; i++) // Pad on the right
3048 {
3049 *sbuffer++ = ' ';
3050 if (++nwritten >= buflen) goto exit;
3051 }
3052 }
3053 }
3054 exit:
3055 *sbuffer++ = 0;
3056 return(nwritten);
3057 }
3058
3059 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
3060 {
3061 mDNSu32 length;
3062
3063 va_list ptr;
3064 va_start(ptr,fmt);
3065 length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
3066 va_end(ptr);
3067
3068 return(length);
3069 }