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