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