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