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