1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16
17 Change History (most recent first):
18
19 $Log: DNSCommon.c,v $
20 Revision 1.100.2.1 2006/08/29 06:24:22 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
22
23 Revision 1.100 2006/06/08 22:58:46 cheshire
24 <rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
25
26 Revision 1.99 2006/05/18 01:32:33 cheshire
27 <rdar://problem/4472706> iChat: Lost connection with Bonjour
28 (mDNSResponder insufficiently defensive against malformed browsing PTR responses)
29
30 Revision 1.98 2006/03/19 17:00:58 cheshire
31 Define symbol MaxMsg instead of using hard-coded constant value '80'
32
33 Revision 1.97 2006/03/18 21:47:56 cheshire
34 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
35
36 Revision 1.96 2006/03/10 21:51:42 cheshire
37 <rdar://problem/4111464> After record update, old record sometimes remains in cache
38 Split out SameRDataBody() into a separate routine so it can be called from other code
39
40 Revision 1.95 2006/03/08 22:43:11 cheshire
41 Use "localdomain" symbol instead of literal string
42
43 Revision 1.94 2006/03/02 21:59:55 cheshire
44 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
45 Improve sanity checks & debugging support in GetLargeResourceRecord()
46
47 Revision 1.93 2006/03/02 20:30:47 cheshire
48 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
49
50 Revision 1.92 2005/09/16 21:06:49 cheshire
51 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
52
53 Revision 1.91 2005/07/10 22:10:37 cheshire
54 The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to
55 hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that
56 large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires.
57
58 Revision 1.90 2005/03/21 00:33:51 shersche
59 <rdar://problem/4021486> Fix build warnings on Win32 platform
60
61 Revision 1.89 2005/03/17 18:59:38 ksekar
62 <rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows
63
64 Revision 1.88 2005/03/16 00:42:32 ksekar
65 <rdar://problem/4012279> Long-lived queries not working on Windows
66
67 Revision 1.87 2005/02/25 04:21:00 cheshire
68 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
69
70 Revision 1.86 2005/02/18 00:43:12 cheshire
71 <rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
72
73 Revision 1.85 2005/02/10 22:35:17 cheshire
74 <rdar://problem/3727944> Update name
75
76 Revision 1.84 2005/02/03 00:44:38 cheshire
77 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
78
79 Revision 1.83 2005/01/27 22:57:55 cheshire
80 Fix compile errors on gcc4
81
82 Revision 1.82 2005/01/19 03:27:03 cheshire
83 <rdar://problem/3961051> CPU Spin in mDNSResponder
84 GetNextScheduledEvent() needs to check LocalRecordReady()
85
86 Revision 1.81 2004/12/18 03:13:45 cheshire
87 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
88
89 Revision 1.80 2004/12/16 21:46:43 cheshire
90 Add DNSTypeName case for kDNSType_SOA
91
92 Revision 1.79 2004/12/16 21:38:37 cheshire
93 Add DNSTypeName case for kDNSType_NS
94
95 Revision 1.78 2004/12/16 21:27:37 ksekar
96 Fixed build failures when compiled with verbose debugging messages
97
98 Revision 1.77 2004/12/16 20:12:59 cheshire
99 <rdar://problem/3324626> Cache memory management improvements
100
101 Revision 1.76 2004/12/16 08:05:29 shersche
102 Remove extranenous semicolons that cause compilation errors on Windows
103
104 Revision 1.75 2004/12/15 02:11:22 ksekar
105 <rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
106
107 Revision 1.74 2004/12/09 22:49:15 ksekar
108 <rdar://problem/3913653> Wide-Area Goodbyes broken
109
110 Revision 1.73 2004/12/07 22:49:06 cheshire
111 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
112
113 Revision 1.72 2004/12/06 21:15:20 ksekar
114 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
115
116 Revision 1.71 2004/12/04 02:12:45 cheshire
117 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
118
119 Revision 1.70 2004/12/03 19:52:44 ksekar
120 Use PutResourceRecordTTLJumbo for putDeletionRecord()
121
122 Revision 1.69 2004/12/03 07:20:50 ksekar
123 <rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
124
125 Revision 1.68 2004/11/24 00:10:43 cheshire
126 <rdar://problem/3869241> For unicast operations, verify that service types are legal
127
128 Revision 1.67 2004/10/26 03:52:02 cheshire
129 Update checkin comments
130
131 Revision 1.66 2004/10/23 01:16:00 cheshire
132 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
133
134 Revision 1.65 2004/10/20 02:15:09 cheshire
135 Add case in GetRRDisplayString() to display NS rdata
136
137 Revision 1.64 2004/10/13 00:24:02 cheshire
138 Disable "array is too small to include a terminating null character" warning on Windows
139
140 Revision 1.63 2004/10/10 06:57:14 cheshire
141 Change definition of "localdomain" to make code compile a little smaller
142
143 Revision 1.62 2004/10/06 01:44:19 cheshire
144 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
145
146 Revision 1.61 2004/09/30 00:24:56 ksekar
147 <rdar://problem/3695802> Dynamically update default registration domains on config change
148
149 Revision 1.60 2004/09/27 23:25:30 cheshire
150 Fix compiler warning: soa.serial is signed, not unsigned
151
152 Revision 1.59 2004/09/27 22:53:45 ksekar
153 Fixed getLargeResourceRecord for SOA rdata.
154
155 Revision 1.58 2004/09/25 02:41:39 cheshire
156 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
157
158 Revision 1.57 2004/09/25 02:24:27 cheshire
159 Removed unused rr->UseCount
160
161 Revision 1.56 2004/09/24 20:57:39 cheshire
162 <rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
163
164 Revision 1.55 2004/09/17 01:08:48 cheshire
165 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
166 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
167 declared in that file are ONLY appropriate to single-address-space embedded applications.
168 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
169
170 Revision 1.54 2004/09/17 00:49:51 cheshire
171 Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
172 is GetLargeResourceRecord
173
174 Revision 1.53 2004/09/17 00:31:51 cheshire
175 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
176
177 Revision 1.52 2004/09/17 00:19:10 cheshire
178 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
179
180 Revision 1.51 2004/09/16 02:29:39 cheshire
181 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
182 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
183
184 Revision 1.50 2004/09/16 01:58:14 cheshire
185 Fix compiler warnings
186
187 Revision 1.49 2004/09/14 23:42:35 cheshire
188 <rdar://problem/3801296> Need to seed random number generator from platform-layer data
189
190 Revision 1.48 2004/09/14 23:27:46 cheshire
191 Fix compile errors
192
193 Revision 1.47 2004/08/25 02:50:04 cheshire
194 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
195 Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal
196
197 Revision 1.46 2004/08/18 17:35:40 ksekar
198 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
199
200 Revision 1.45 2004/08/15 18:26:00 cheshire
201 Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead
202 (A "struct domainname" is a collection of packed pascal strings, not a C string.)
203
204 Revision 1.44 2004/08/13 23:46:58 cheshire
205 "asyncronous" -> "asynchronous"
206
207 Revision 1.43 2004/08/12 02:55:46 ksekar
208 Fix param order error moving putPrereqNameNotInUse from uDNS.c using
209 ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
210
211 Revision 1.42 2004/08/10 23:19:14 ksekar
212 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
213 Moved routines/constants to allow extern access for garbage collection daemon
214
215 Revision 1.41 2004/08/10 01:10:01 cheshire
216 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
217 Minor revision from Roger Pantos
218
219 Revision 1.40 2004/08/04 22:10:46 cheshire
220 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
221 Change to use "._sub." instead of ".s." to mark subtypes.
222
223 Revision 1.39 2004/07/13 21:24:24 rpantos
224 Fix for <rdar://problem/3701120>.
225
226 Revision 1.38 2004/06/18 21:08:58 cheshire
227 <rdar://problem/3540040> Applications are registering invalid records
228 Attempts to create domain names like "www..apple.com." now logged to aid debugging
229
230 Revision 1.37 2004/06/18 20:25:42 cheshire
231 <rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa".
232
233 Revision 1.36 2004/06/18 19:09:59 cheshire
234 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
235
236 Revision 1.35 2004/06/05 00:14:44 cheshire
237 Fix signed/unsigned and other compiler warnings
238
239 Revision 1.34 2004/06/04 00:25:25 cheshire
240 Fix misaligned write exception that occurs on some platforms
241
242 Revision 1.33 2004/06/04 00:16:18 cheshire
243 Remove non-portable use of 'inline'
244
245 Revision 1.32 2004/06/03 03:09:58 ksekar
246 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
247
248 Revision 1.31 2004/05/28 23:42:36 ksekar
249 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
250
251 Revision 1.30 2004/05/26 09:08:04 bradley
252 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
253
254 Revision 1.29 2004/05/18 23:51:25 cheshire
255 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
256
257 Revision 1.28 2004/05/13 04:54:20 ksekar
258 Unified list copy/free code. Added symetric list for
259
260 Revision 1.27 2004/04/22 20:29:07 cheshire
261 Log error message if no count field passed to PutResourceRecordTTL()
262
263 Revision 1.26 2004/04/22 04:07:01 cheshire
264 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
265
266 Revision 1.25 2004/04/22 03:05:28 cheshire
267 kDNSClass_ANY should be kDNSQClass_ANY
268
269 Revision 1.24 2004/04/22 02:51:20 cheshire
270 Use common code for HINFO/TXT and TSIG cases in putRData
271
272 Revision 1.23 2004/04/15 00:51:28 bradley
273 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
274 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
275
276 Revision 1.22 2004/04/14 23:09:28 ksekar
277 Support for TSIG signed dynamic updates.
278
279 Revision 1.21 2004/04/09 16:47:28 cheshire
280 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
281
282 Revision 1.20 2004/04/09 16:37:15 cheshire
283 Suggestion from Bob Bradley:
284 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
285
286 Revision 1.19 2004/04/02 19:34:38 cheshire
287 Fix broken comment
288
289 Revision 1.18 2004/03/30 06:45:00 cheshire
290 Compiler warning fixes from Don Woodward at Roku Labs
291
292 Revision 1.17 2004/03/19 22:25:20 cheshire
293 <rdar://problem/3579561>: Need to limit service types to fourteen characters
294 Won't actually do this for now, but keep the code around just in case
295
296 Revision 1.16 2004/03/08 02:45:35 cheshire
297 Minor change to make a couple of the log messages a bit shorter
298
299 Revision 1.15 2004/03/08 02:44:09 cheshire
300 <rdar://problem/3579561>: Need to limit service types to fourteen characters
301
302 Revision 1.14 2004/02/21 02:06:24 cheshire
303 Can't use anonymous unions -- they're non-standard and don't work on all compilers
304
305 Revision 1.13 2004/02/06 23:04:18 ksekar
306 Basic Dynamic Update support via mDNS_Register (dissabled via
307 UNICAST_REGISTRATION #define)
308
309 Revision 1.12 2004/02/03 22:37:10 cheshire
310 Delete unused (commented-out) code
311
312 Revision 1.11 2004/02/03 22:35:34 cheshire
313 <rdar://problem/3548256>: Should not allow empty string for resolve domain
314
315 Revision 1.10 2004/02/03 19:47:36 ksekar
316 Added an asynchronous state machine mechanism to uDNS.c, including
317 calls to find the parent zone for a domain name. Changes include code
318 in repository previously dissabled via "#if 0 incomplete". Codepath
319 is currently unused, and will be called to create update records, etc.
320
321 Revision 1.9 2004/01/27 20:15:22 cheshire
322 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
323
324 Revision 1.8 2004/01/24 23:24:36 cheshire
325 Expanded out the list of local domains to reduce risk of mistakes in future
326
327 Revision 1.7 2004/01/24 08:32:30 bradley
328 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
329 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
330 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
331
332 Revision 1.6 2004/01/24 04:59:15 cheshire
333 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
334
335 Revision 1.5 2004/01/23 23:23:14 ksekar
336 Added TCP support for truncated unicast messages.
337
338 Revision 1.4 2004/01/22 02:15:33 cheshire
339 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
340
341 Revision 1.3 2004/01/21 21:16:29 cheshire
342 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
343
344 Revision 1.2 2003/12/13 05:47:48 bradley
345 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
346 expression is constant warning when building with Microsoft compilers.
347
348 Revision 1.1 2003/12/13 03:05:27 ksekar
349 <rdar://problem/3192548>: DynDNS: Unicast query of service records
350
351 */
352
353 #pragma ident "%Z%%M% %I% %E% SMI"
354
355 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
356 #define mDNS_InstantiateInlines 1
357 #include "DNSCommon.h"
358
359 // Disable certain benign warnings with Microsoft compilers
360 #if (defined(_MSC_VER))
361 // Disable "conditional expression is constant" warning for debug macros.
362 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
363 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
364 #pragma warning(disable:4127)
365 // Disable "array is too small to include a terminating null character" warning
366 // -- domain labels have an initial length byte, not a terminating null character
367 #pragma warning(disable:4295)
368 #endif
369
370 // ***************************************************************************
371 #if COMPILER_LIKES_PRAGMA_MARK
372 #pragma mark -
373 #pragma mark - DNameList copy/deallocation routines
374 #endif
375
mDNS_CopyDNameList(const DNameListElem * orig)376 mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig)
377 {
378 DNameListElem *copy = mDNSNULL, *newelem;
379 const DNameListElem *ptr;
380
381 for (ptr = orig; ptr; ptr = ptr->next)
382 {
383 newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem));
384 if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; }
385 AssignDomainName(&newelem->name, &ptr->name);
386 newelem->next = copy;
387 copy = newelem;
388 }
389 return copy;
390 }
391
mDNS_FreeDNameList(DNameListElem * list)392 mDNSexport void mDNS_FreeDNameList(DNameListElem *list)
393 {
394 DNameListElem *fptr;
395
396 while (list)
397 {
398 fptr = list;
399 list = list->next;
400 mDNSPlatformMemFree(fptr);
401 }
402 }
403
404 // ***************************************************************************
405 #if COMPILER_LIKES_PRAGMA_MARK
406 #pragma mark -
407 #pragma mark - General Utility Functions
408 #endif
409
410 // return true for RFC1918 private addresses
IsPrivateV4Addr(mDNSAddr * addr)411 mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr)
412 {
413 mDNSu8 *b;
414
415 if (addr->type != mDNSAddrType_IPv4) return mDNSfalse;
416 b = addr->ip.v4.b;
417
418 return ((b[0] == 10) || // 10/8 prefix
419 (b[0] == 172 && b[1] > 15 && b[1] < 32) || // 172.16/12
420 (b[0] == 192 && b[1] == 168)); // 192.168/16
421 }
422
GetFirstActiveInterface(const NetworkInterfaceInfo * intf)423 mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf)
424 {
425 while (intf && !intf->InterfaceActive) intf = intf->next;
426 return(intf);
427 }
428
GetNextActiveInterfaceID(const NetworkInterfaceInfo * intf)429 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
430 {
431 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
432 if (next) return(next->InterfaceID); else return(mDNSNULL);
433 }
434
NumCacheRecordsForInterfaceID(const mDNS * const m,mDNSInterfaceID id)435 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
436 {
437 mDNSu32 slot, used = 0;
438 CacheGroup *cg;
439 CacheRecord *rr;
440 FORALL_CACHERECORDS(slot, cg, rr)
441 if (rr->resrec.InterfaceID == id) used++;
442 return(used);
443 }
444
DNSTypeName(mDNSu16 rrtype)445 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
446 {
447 switch (rrtype)
448 {
449 case kDNSType_A: return("Addr");
450 case kDNSType_NS: return("NS");
451 case kDNSType_CNAME:return("CNAME");
452 case kDNSType_SOA: return("SOA");
453 case kDNSType_NULL: return("NULL");
454 case kDNSType_PTR: return("PTR");
455 case kDNSType_HINFO:return("HINFO");
456 case kDNSType_TXT: return("TXT");
457 case kDNSType_AAAA: return("AAAA");
458 case kDNSType_SRV: return("SRV");
459 case kDNSQType_ANY: return("ANY");
460 default: {
461 static char buffer[16];
462 mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
463 return(buffer);
464 }
465 }
466 }
467
468 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
469 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
470 // long as this routine is only used for debugging messages, it probably isn't a big problem.
GetRRDisplayString_rdb(const ResourceRecord * rr,RDataBody * rd,char * buffer)471 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer)
472 {
473 #define Max (MaxMsg-1)
474 char *ptr = buffer;
475 mDNSu32 length = mDNS_snprintf(buffer, Max, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
476 switch (rr->rrtype)
477 {
478 case kDNSType_A: mDNS_snprintf(buffer+length, Max-length, "%.4a", &rd->ipv4); break;
479
480 case kDNSType_NS: // Same as PTR
481 case kDNSType_CNAME:// Same as PTR
482 case kDNSType_PTR: mDNS_snprintf(buffer+length, Max-length, "%##s", rd->name.c); break;
483
484 case kDNSType_HINFO:// Display this the same as TXT (just show first string)
485 case kDNSType_TXT: mDNS_snprintf(buffer+length, Max-length, "%#s", rd->txt.c); break;
486
487 case kDNSType_AAAA: mDNS_snprintf(buffer+length, Max-length, "%.16a", &rd->ipv6); break;
488 case kDNSType_SRV: mDNS_snprintf(buffer+length, Max-length, "%u %u %u %##s",
489 rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
490 default: mDNS_snprintf(buffer+length, Max-length, "RDLen %d: %s", rr->rdlength, rd->data); break;
491 }
492 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
493 return(buffer);
494 }
495
mDNSRandom(mDNSu32 max)496 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)
497 {
498 static mDNSu32 seed = 0;
499 mDNSu32 mask = 1;
500
501 if (!seed)
502 {
503 int i;
504 seed = mDNSPlatformRandomSeed(); // Pick an initial seed
505 for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit
506 }
507 while (mask < max) mask = (mask << 1) | 1;
508 do seed = seed * 21 + 1; while ((seed & mask) > max);
509 return (seed & mask);
510 }
511
mDNSRandomFromFixedSeed(mDNSu32 seed,mDNSu32 max)512 mDNSexport mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max)
513 {
514 mDNSu32 mask = 1;
515 while (mask < max) mask = (mask << 1) | 1;
516 do seed = seed * 21 + 1; while ((seed & mask) > max);
517 return (seed & mask);
518 }
519
mDNSSameAddress(const mDNSAddr * ip1,const mDNSAddr * ip2)520 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
521 {
522 if (ip1->type == ip2->type)
523 {
524 switch (ip1->type)
525 {
526 case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal
527 case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
528 case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
529 }
530 }
531 return(mDNSfalse);
532 }
533
mDNSAddrIsDNSMulticast(const mDNSAddr * ip)534 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
535 {
536 switch(ip->type)
537 {
538 case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroupv4.NotAnInteger);
539 case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] &&
540 ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] &&
541 ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] &&
542 ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] );
543 default: return(mDNSfalse);
544 }
545 }
546
547 // ***************************************************************************
548 #if COMPILER_LIKES_PRAGMA_MARK
549 #pragma mark -
550 #pragma mark - Domain Name Utility Functions
551 #endif
552
SameDomainLabel(const mDNSu8 * a,const mDNSu8 * b)553 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
554 {
555 int i;
556 const int len = *a++;
557
558 if (len > MAX_DOMAIN_LABEL)
559 { debugf("Malformed label (too long)"); return(mDNSfalse); }
560
561 if (len != *b++) return(mDNSfalse);
562 for (i=0; i<len; i++)
563 {
564 mDNSu8 ac = *a++;
565 mDNSu8 bc = *b++;
566 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
567 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
568 if (ac != bc) return(mDNSfalse);
569 }
570 return(mDNStrue);
571 }
572
SameDomainName(const domainname * const d1,const domainname * const d2)573 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
574 {
575 const mDNSu8 * a = d1->c;
576 const mDNSu8 * b = d2->c;
577 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
578
579 while (*a || *b)
580 {
581 if (a + 1 + *a >= max)
582 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); }
583 if (!SameDomainLabel(a, b)) return(mDNSfalse);
584 a += 1 + *a;
585 b += 1 + *b;
586 }
587
588 return(mDNStrue);
589 }
590
IsLocalDomain(const domainname * d)591 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
592 {
593 // Domains that are defined to be resolved via link-local multicast are:
594 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
595 static const domainname *nL = (domainname*)"\x5" "local";
596 static const domainname *nR = (domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
597 static const domainname *n8 = (domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
598 static const domainname *n9 = (domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
599 static const domainname *nA = (domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
600 static const domainname *nB = (domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
601
602 const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc.
603 d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL;
604 while (d->c[0])
605 {
606 d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
607 d = (domainname*)(d->c + 1 + d->c[0]);
608 }
609
610 if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
611 if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
612 if (d6 && SameDomainName(d6, n8)) return(mDNStrue);
613 if (d6 && SameDomainName(d6, n9)) return(mDNStrue);
614 if (d6 && SameDomainName(d6, nA)) return(mDNStrue);
615 if (d6 && SameDomainName(d6, nB)) return(mDNStrue);
616 return(mDNSfalse);
617 }
618
619 // Returns length of a domain name INCLUDING the byte for the final null label
620 // i.e. for the root label "." it returns one
621 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
622 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
623 // If the given domainname is invalid, result is 256
DomainNameLength(const domainname * const name)624 mDNSexport mDNSu16 DomainNameLength(const domainname *const name)
625 {
626 const mDNSu8 *src = name->c;
627 while (*src)
628 {
629 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
630 src += 1 + *src;
631 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
632 }
633 return((mDNSu16)(src - name->c + 1));
634 }
635
636 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
637 // for the final null label i.e. for the root label "." it returns one.
638 // E.g. for the FQDN "foo.com." it returns 9
639 // (length, three data bytes, length, three more data bytes, final zero).
640 // In the case where a parent domain name is provided, and the given name is a child
641 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
642 // of the child name, plus TWO bytes for the compression pointer.
643 // E.g. for the name "foo.com." with parent "com.", it returns 6
644 // (length, three data bytes, two-byte compression pointer).
CompressedDomainNameLength(const domainname * const name,const domainname * parent)645 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
646 {
647 const mDNSu8 *src = name->c;
648 if (parent && parent->c[0] == 0) parent = mDNSNULL;
649 while (*src)
650 {
651 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
652 if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
653 src += 1 + *src;
654 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
655 }
656 return((mDNSu16)(src - name->c + 1));
657 }
658
659 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
660 // The C string contains the label as-is, with no escaping, etc.
661 // Any dots in the name are literal dots, not label separators
662 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
663 // in the domainname bufer (i.e., the next byte after the terminating zero).
664 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
665 // AppendLiteralLabelString returns mDNSNULL.
AppendLiteralLabelString(domainname * const name,const char * cstr)666 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
667 {
668 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
669 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
670 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
671 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
672 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
673
674 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
675 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
676 *ptr++ = 0; // Put the null root label on the end
677 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
678 else return(ptr); // Success: return new value of ptr
679 }
680
681 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
682 // The C string is in conventional DNS syntax:
683 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
684 // If successful, AppendDNSNameString returns a pointer to the next unused byte
685 // in the domainname bufer (i.e., the next byte after the terminating zero).
686 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
687 // AppendDNSNameString returns mDNSNULL.
AppendDNSNameString(domainname * const name,const char * cstring)688 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
689 {
690 const char *cstr = cstring;
691 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
692 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
693 while (*cstr && ptr < lim) // While more characters, and space to put them...
694 {
695 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
696 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
697 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
698 {
699 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
700 if (c == '\\') // If escape character, check next character
701 {
702 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
703 if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]))
704 { // If three decimal digits,
705 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
706 int v1 = cstr[ 0] - '0';
707 int v2 = cstr[ 1] - '0';
708 int val = v0 * 100 + v1 * 10 + v2;
709 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
710 }
711 }
712 *ptr++ = c; // Write the character
713 }
714 if (*cstr) cstr++; // Skip over the trailing dot (if present)
715 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
716 return(mDNSNULL);
717 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
718 }
719
720 *ptr++ = 0; // Put the null root label on the end
721 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
722 else return(ptr); // Success: return new value of ptr
723 }
724
725 // AppendDomainLabel appends a single label to a name.
726 // If successful, AppendDomainLabel returns a pointer to the next unused byte
727 // in the domainname bufer (i.e., the next byte after the terminating zero).
728 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
729 // AppendDomainLabel returns mDNSNULL.
AppendDomainLabel(domainname * const name,const domainlabel * const label)730 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
731 {
732 int i;
733 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
734
735 // Check label is legal
736 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
737
738 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
739 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
740
741 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
742 *ptr++ = 0; // Put the null root label on the end
743 return(ptr);
744 }
745
AppendDomainName(domainname * const name,const domainname * const append)746 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
747 {
748 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
749 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
750 const mDNSu8 * src = append->c;
751 while(src[0])
752 {
753 int i;
754 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
755 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
756 *ptr = 0; // Put the null root label on the end
757 src += i;
758 }
759 return(ptr);
760 }
761
762 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
763 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
764 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
765 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
766 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
767 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
MakeDomainLabelFromLiteralString(domainlabel * const label,const char * cstr)768 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
769 {
770 mDNSu8 * ptr = label->c + 1; // Where we're putting it
771 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
772 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
773 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
774 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
775 }
776
777 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
778 // The C string is in conventional DNS syntax:
779 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
780 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
781 // in the domainname bufer (i.e., the next byte after the terminating zero).
782 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
783 // MakeDomainNameFromDNSNameString returns mDNSNULL.
MakeDomainNameFromDNSNameString(domainname * const name,const char * cstr)784 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
785 {
786 name->c[0] = 0; // Make an empty domain name
787 return(AppendDNSNameString(name, cstr)); // And then add this string to it
788 }
789
ConvertDomainLabelToCString_withescape(const domainlabel * const label,char * ptr,char esc)790 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
791 {
792 const mDNSu8 * src = label->c; // Domain label we're reading
793 const mDNSu8 len = *src++; // Read length of this (non-null) label
794 const mDNSu8 *const end = src + len; // Work out where the label ends
795 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
796 while (src < end) // While we have characters in the label
797 {
798 mDNSu8 c = *src++;
799 if (esc)
800 {
801 if (c == '.' || c == esc) // If character is a dot or the escape character
802 *ptr++ = esc; // Output escape character
803 else if (c <= ' ') // If non-printing ascii,
804 { // Output decimal escape sequence
805 *ptr++ = esc;
806 *ptr++ = (char) ('0' + (c / 100) );
807 *ptr++ = (char) ('0' + (c / 10) % 10);
808 c = (mDNSu8)('0' + (c ) % 10);
809 }
810 }
811 *ptr++ = (char)c; // Copy the character
812 }
813 *ptr = 0; // Null-terminate the string
814 return(ptr); // and return
815 }
816
817 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
ConvertDomainNameToCString_withescape(const domainname * const name,char * ptr,char esc)818 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
819 {
820 const mDNSu8 *src = name->c; // Domain name we're reading
821 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
822
823 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
824
825 while (*src) // While more characters in the domain name
826 {
827 if (src + 1 + *src >= max) return(mDNSNULL);
828 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
829 if (!ptr) return(mDNSNULL);
830 src += 1 + *src;
831 *ptr++ = '.'; // Write the dot after the label
832 }
833
834 *ptr++ = 0; // Null-terminate the string
835 return(ptr); // and return
836 }
837
838 // RFC 1034 rules:
839 // Host names must start with a letter, end with a letter or digit,
840 // and have as interior characters only letters, digits, and hyphen.
841 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
842
ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[],domainlabel * const hostlabel)843 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
844 {
845 const mDNSu8 * src = &UTF8Name[1];
846 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
847 mDNSu8 * ptr = &hostlabel->c[1];
848 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
849 while (src < end)
850 {
851 // Delete apostrophes from source name
852 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
853 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
854 { src += 3; continue; } // Unicode curly apostrophe
855 if (ptr < lim)
856 {
857 if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
858 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
859 }
860 src++;
861 }
862 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
863 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
864 }
865
ConstructServiceName(domainname * const fqdn,const domainlabel * name,const domainname * type,const domainname * const domain)866 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
867 const domainlabel *name, const domainname *type, const domainname *const domain)
868 {
869 int i, len;
870 mDNSu8 *dst = fqdn->c;
871 const mDNSu8 *src;
872 const char *errormsg;
873
874 // In the case where there is no name (and ONLY in that case),
875 // a single-label subtype is allowed as the first label of a three-part "type"
876 if (!name && type)
877 {
878 const mDNSu8 *s0 = type->c;
879 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
880 {
881 const mDNSu8 * s1 = s0 + 1 + s0[0];
882 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
883 {
884 const mDNSu8 *s2 = s1 + 1 + s1[0];
885 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
886 {
887 static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
888 src = s0; // Copy the first label
889 len = *src;
890 for (i=0; i <= len; i++) *dst++ = *src++;
891 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
892 type = (domainname *)s1;
893
894 // Special support for queries done by some third-party network monitoring software
895 // For these queries, we retract the "._sub" we just added between the subtype and the main type
896 if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") ||
897 SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp"))
898 dst -= sizeof(SubTypeLabel);
899 }
900 }
901 }
902 }
903
904 if (name && name->c[0])
905 {
906 src = name->c; // Put the service name into the domain name
907 len = *src;
908 if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; }
909 for (i=0; i<=len; i++) *dst++ = *src++;
910 }
911 else
912 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
913
914 src = type->c; // Put the service type into the domain name
915 len = *src;
916 if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain)))
917 {
918 errormsg="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
919 goto fail;
920 }
921 if (src[1] != '_') { errormsg="Application protocol name must begin with underscore"; goto fail; }
922 for (i=2; i<=len; i++)
923 if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_')
924 { errormsg="Application protocol name must contain only letters, digits, and hyphens"; goto fail; }
925 for (i=0; i<=len; i++) *dst++ = *src++;
926
927 len = *src;
928 if (!(len == 4 && src[1] == '_' &&
929 (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) &&
930 (src[4] | 0x20) == 'p'))
931 { errormsg="Transport protocol name must be _udp or _tcp"; goto fail; }
932 for (i=0; i<=len; i++) *dst++ = *src++;
933
934 if (*src) { errormsg="Service type must have only two labels"; goto fail; }
935
936 *dst = 0;
937 if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; }
938 if (SameDomainName(domain, (domainname*)"\x05" "local" "\x04" "arpa"))
939 { errormsg="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
940 dst = AppendDomainName(fqdn, domain);
941 if (!dst) { errormsg="Service domain too long"; goto fail; }
942 return(dst);
943
944 fail:
945 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
946 return(mDNSNULL);
947 }
948
949 // A service name has the form: instance.application-protocol.transport-protocol.domain
950 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
951 // set or length limits for the protocol names, and the final domain is allowed to be empty.
952 // However, if the given FQDN doesn't contain at least three labels,
953 // DeconstructServiceName will reject it and return mDNSfalse.
DeconstructServiceName(const domainname * const fqdn,domainlabel * const name,domainname * const type,domainname * const domain)954 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
955 domainlabel *const name, domainname *const type, domainname *const domain)
956 {
957 int i, len;
958 const mDNSu8 *src = fqdn->c;
959 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
960 mDNSu8 *dst;
961
962 dst = name->c; // Extract the service name
963 len = *src;
964 if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); }
965 if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
966 for (i=0; i<=len; i++) *dst++ = *src++;
967
968 dst = type->c; // Extract the service type
969 len = *src;
970 if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); }
971 if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
972 for (i=0; i<=len; i++) *dst++ = *src++;
973
974 len = *src;
975 if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
976 if (len >= 0x40) { debugf("DeconstructServiceName: Transport protocol name too long"); return(mDNSfalse); }
977 for (i=0; i<=len; i++) *dst++ = *src++;
978 *dst++ = 0; // Put terminator on the end of service type
979
980 dst = domain->c; // Extract the service domain
981 while (*src)
982 {
983 len = *src;
984 if (len >= 0x40)
985 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
986 if (src + 1 + len + 1 >= max)
987 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
988 for (i=0; i<=len; i++) *dst++ = *src++;
989 }
990 *dst++ = 0; // Put the null root label on the end
991
992 return(mDNStrue);
993 }
994
995 // Notes on UTF-8:
996 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
997 // 10xxxxxx is a continuation byte of a multi-byte character
998 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
999 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1000 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1001 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1002 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1003 //
1004 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1005 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1006 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1007 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1008 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1009
TruncateUTF8ToLength(mDNSu8 * string,mDNSu32 length,mDNSu32 max)1010 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
1011 {
1012 if (length > max)
1013 {
1014 mDNSu8 c1 = string[max]; // First byte after cut point
1015 mDNSu8 c2 = (max+1 < length) ? string[max+1] : 0xB0; // Second byte after cut point
1016 length = max; // Trim length down
1017 while (length > 0)
1018 {
1019 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1020 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1021 // If so, then we continue to chop more bytes until we get to a legal chop point.
1022 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
1023 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
1024 if (!continuation && !secondsurrogate) break;
1025 c2 = c1;
1026 c1 = string[--length];
1027 }
1028 // Having truncated characters off the end of our string, also cut off any residual white space
1029 while (length > 0 && string[length-1] <= ' ') length--;
1030 }
1031 return(length);
1032 }
1033
1034 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1035 // name ends in "-nnn", where n is some decimal number.
LabelContainsSuffix(const domainlabel * const name,const mDNSBool RichText)1036 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
1037 {
1038 mDNSu16 l = name->c[0];
1039
1040 if (RichText)
1041 {
1042 if (l < 4) return mDNSfalse; // Need at least " (2)"
1043 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
1044 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
1045 l--;
1046 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
1047 return (name->c[l] == '(' && name->c[l - 1] == ' ');
1048 }
1049 else
1050 {
1051 if (l < 2) return mDNSfalse; // Need at least "-2"
1052 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
1053 l--;
1054 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
1055 return (name->c[l] == '-');
1056 }
1057 }
1058
1059 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1060 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1061 // from the suffix that was removed.
RemoveLabelSuffix(domainlabel * name,mDNSBool RichText)1062 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
1063 {
1064 mDNSu32 val = 0, multiplier = 1;
1065
1066 // Chop closing parentheses from RichText suffix
1067 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1068
1069 // Get any existing numerical suffix off the name
1070 while (mdnsIsDigit(name->c[name->c[0]]))
1071 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
1072
1073 // Chop opening parentheses or dash from suffix
1074 if (RichText)
1075 {
1076 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1077 }
1078 else
1079 {
1080 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1081 }
1082
1083 return(val);
1084 }
1085
1086 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1087 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
AppendLabelSuffix(domainlabel * name,mDNSu32 val,mDNSBool RichText)1088 mDNSexport void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText)
1089 {
1090 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1091 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1092
1093 // Truncate trailing spaces from RichText names
1094 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1095
1096 while (val >= divisor * 10) { divisor *= 10; chars++; }
1097
1098 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1099
1100 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1101 else { name->c[++name->c[0]] = '-'; }
1102
1103 while (divisor)
1104 {
1105 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1106 val %= divisor;
1107 divisor /= 10;
1108 }
1109
1110 if (RichText) name->c[++name->c[0]] = ')';
1111 }
1112
IncrementLabelSuffix(domainlabel * name,mDNSBool RichText)1113 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1114 {
1115 mDNSu32 val = 0;
1116
1117 if (LabelContainsSuffix(name, RichText))
1118 val = RemoveLabelSuffix(name, RichText);
1119
1120 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1121 // If existing suffix in the range 2-9, increment it.
1122 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1123 // so add a random increment to improve the chances of finding an available name next time.
1124 if (val == 0) val = 2;
1125 else if (val < 10) val++;
1126 else val += 1 + mDNSRandom(99);
1127
1128 AppendLabelSuffix(name, val, RichText);
1129 }
1130
1131 // ***************************************************************************
1132 #if COMPILER_LIKES_PRAGMA_MARK
1133 #pragma mark -
1134 #pragma mark - Resource Record Utility Functions
1135 #endif
1136
RDataHashValue(mDNSu16 const rdlength,const RDataBody * const rdb)1137 mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb)
1138 {
1139 mDNSu32 sum = 0;
1140 int i;
1141 for (i=0; i+1 < rdlength; i+=2)
1142 {
1143 sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
1144 sum = (sum<<3) | (sum>>29);
1145 }
1146 if (i < rdlength)
1147 {
1148 sum += ((mDNSu32)(rdb->data[i])) << 8;
1149 }
1150 return(sum);
1151 }
1152
1153 // r1 has to be a full ResourceRecord including rrtype and rdlength
1154 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
SameRDataBody(const ResourceRecord * const r1,const RDataBody * const r2)1155 mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2)
1156 {
1157 switch(r1->rrtype)
1158 {
1159 case kDNSType_CNAME:// Same as PTR
1160 case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->name));
1161
1162 case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->srv.priority &&
1163 r1->rdata->u.srv.weight == r2->srv.weight &&
1164 r1->rdata->u.srv.port.NotAnInteger == r2->srv.port.NotAnInteger &&
1165 SameDomainName(&r1->rdata->u.srv.target, &r2->srv.target) );
1166
1167 default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->data, r1->rdlength));
1168 }
1169 }
1170
SameRData(const ResourceRecord * const r1,const ResourceRecord * const r2)1171 mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
1172 {
1173 if (r1->rrtype != r2->rrtype) return(mDNSfalse);
1174 if (r1->rdlength != r2->rdlength) return(mDNSfalse);
1175 if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
1176 return(SameRDataBody(r1, &r2->rdata->u));
1177 }
1178
SameResourceRecord(ResourceRecord * r1,ResourceRecord * r2)1179 mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2)
1180 {
1181 return (r1->namehash == r2->namehash &&
1182 r1->rrtype == r2->rrtype &&
1183 SameDomainName(r1->name, r2->name) &&
1184 SameRData(r1, r2));
1185 }
1186
ResourceRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q)1187 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1188 {
1189 if (rr->InterfaceID &&
1190 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1191 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1192
1193 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1194 if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse);
1195 if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1196 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1197 }
1198
GetRDLength(const ResourceRecord * const rr,mDNSBool estimate)1199 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1200 {
1201 const RDataBody *rd = &rr->rdata->u;
1202 const domainname *const name = estimate ? rr->name : mDNSNULL;
1203 switch (rr->rrtype)
1204 {
1205 case kDNSType_A: return(sizeof(rd->ipv4));
1206 case kDNSType_CNAME:// Same as PTR
1207 case kDNSType_NS: // Same as PTR
1208 case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name));
1209 case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
1210 case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength
1211 case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength
1212 case kDNSType_AAAA: return(sizeof(rd->ipv6));
1213 case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
1214 case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
1215 CompressedDomainNameLength(&rd->soa.rname, name) +
1216 5 * sizeof(mDNSOpaque32));
1217 case kDNSType_OPT: return(rr->rdlength);
1218 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
1219 return(rr->rdlength);
1220 }
1221 }
1222
ValidateRData(const mDNSu16 rrtype,const mDNSu16 rdlength,const RData * const rd)1223 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1224 {
1225 mDNSu16 len;
1226
1227 switch(rrtype)
1228 {
1229 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1230
1231 case kDNSType_NS: // Same as PTR
1232 case kDNSType_MD: // Same as PTR
1233 case kDNSType_MF: // Same as PTR
1234 case kDNSType_CNAME:// Same as PTR
1235 //case kDNSType_SOA not checked
1236 case kDNSType_MB: // Same as PTR
1237 case kDNSType_MG: // Same as PTR
1238 case kDNSType_MR: // Same as PTR
1239 //case kDNSType_NULL not checked (no specified format, so always valid)
1240 //case kDNSType_WKS not checked
1241 case kDNSType_PTR: if (!rdlength) return(mDNSfalse);
1242 len = DomainNameLength(&rd->u.name);
1243 return(len <= MAX_DOMAIN_NAME && rdlength == len);
1244
1245 case kDNSType_HINFO:// Same as TXT (roughly)
1246 case kDNSType_MINFO:// Same as TXT (roughly)
1247 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
1248 {
1249 const mDNSu8 *ptr = rd->u.txt.c;
1250 const mDNSu8 *end = rd->u.txt.c + rdlength;
1251 while (ptr < end) ptr += 1 + ptr[0];
1252 return (ptr == end);
1253 }
1254
1255 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1256
1257 case kDNSType_MX: if (!rdlength) return(mDNSfalse);
1258 len = DomainNameLength(&rd->u.mx.exchange);
1259 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1260
1261 case kDNSType_SRV: if (!rdlength) return(mDNSfalse);
1262 len = DomainNameLength(&rd->u.srv.target);
1263 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1264
1265 default: return(mDNStrue); // Allow all other types without checking
1266 }
1267 }
1268
1269 // ***************************************************************************
1270 #if COMPILER_LIKES_PRAGMA_MARK
1271 #pragma mark -
1272 #pragma mark -
1273 #pragma mark - DNS Message Creation Functions
1274 #endif
1275
InitializeDNSMessage(DNSMessageHeader * h,mDNSOpaque16 id,mDNSOpaque16 flags)1276 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1277 {
1278 h->id = id;
1279 h->flags = flags;
1280 h->numQuestions = 0;
1281 h->numAnswers = 0;
1282 h->numAuthorities = 0;
1283 h->numAdditionals = 0;
1284 }
1285
FindCompressionPointer(const mDNSu8 * const base,const mDNSu8 * const end,const mDNSu8 * const domname)1286 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1287 {
1288 const mDNSu8 *result = end - *domname - 1;
1289
1290 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1291
1292 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1293 while (result >= base)
1294 {
1295 // If the length byte and first character of the label match, then check further to see
1296 // if this location in the packet will yield a useful name compression pointer.
1297 if (result[0] == domname[0] && result[1] == domname[1])
1298 {
1299 const mDNSu8 *name = domname;
1300 const mDNSu8 *targ = result;
1301 while (targ + *name < end)
1302 {
1303 // First see if this label matches
1304 int i;
1305 const mDNSu8 *pointertarget;
1306 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
1307 if (i <= *name) break; // If label did not match, bail out
1308 targ += 1 + *name; // Else, did match, so advance target pointer
1309 name += 1 + *name; // and proceed to check next label
1310 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
1311 if (*name == 0) break; // If no more labels to match, we failed, so bail out
1312
1313 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1314 if (targ[0] < 0x40) continue; // If length value, continue to check next label
1315 if (targ[0] < 0xC0) break; // If 40-BF, not valid
1316 if (targ+1 >= end) break; // Second byte not present!
1317 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
1318 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
1319 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1320 targ = pointertarget;
1321 }
1322 }
1323 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1324 }
1325 return(mDNSNULL);
1326 }
1327
1328 // Put a string of dot-separated labels as length-prefixed labels
1329 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1330 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1331 // end points to the end of the message so far
1332 // ptr points to where we want to put the name
1333 // limit points to one byte past the end of the buffer that we must not overrun
1334 // domainname is the name to put
putDomainNameAsLabels(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name)1335 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1336 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1337 {
1338 const mDNSu8 *const base = (const mDNSu8 *)msg;
1339 const mDNSu8 * np = name->c;
1340 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1341 const mDNSu8 * pointer = mDNSNULL;
1342 const mDNSu8 *const searchlimit = ptr;
1343
1344 while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
1345 {
1346 if (*np > MAX_DOMAIN_LABEL)
1347 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1348
1349 // This check correctly allows for the final trailing root label:
1350 // e.g.
1351 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1352 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1353 // We know that max will be at name->c[255]
1354 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1355 // six bytes, then exit the loop, write the final terminating root label, and the domain
1356 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1357 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1358 if (np + 1 + *np >= max)
1359 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); }
1360
1361 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1362 if (pointer) // Use a compression pointer if we can
1363 {
1364 mDNSu16 offset = (mDNSu16)(pointer - base);
1365 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1366 *ptr++ = (mDNSu8)( offset & 0xFF);
1367 return(ptr);
1368 }
1369 else // Else copy one label and try again
1370 {
1371 int i;
1372 mDNSu8 len = *np++;
1373 if (ptr + 1 + len >= limit) return(mDNSNULL);
1374 *ptr++ = len;
1375 for (i=0; i<len; i++) *ptr++ = *np++;
1376 }
1377 }
1378
1379 if (ptr < limit) // If we didn't run out of space
1380 {
1381 *ptr++ = 0; // Put the final root label
1382 return(ptr); // and return
1383 }
1384
1385 return(mDNSNULL);
1386 }
1387
putVal16(mDNSu8 * ptr,mDNSu16 val)1388 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1389 {
1390 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1391 ptr[1] = (mDNSu8)((val ) & 0xFF);
1392 return ptr + sizeof(mDNSOpaque16);
1393 }
1394
putVal32(mDNSu8 * ptr,mDNSu32 val)1395 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
1396 {
1397 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
1398 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
1399 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
1400 ptr[3] = (mDNSu8)((val ) & 0xFF);
1401 return ptr + sizeof(mDNSu32);
1402 }
1403
putOptRData(mDNSu8 * ptr,const mDNSu8 * limit,ResourceRecord * rr)1404 mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr)
1405 {
1406 int nput = 0;
1407 rdataOpt *opt;
1408
1409 while (nput < rr->rdlength)
1410 {
1411 // check if space for opt/optlen
1412 if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err;
1413 opt = (rdataOpt *)(rr->rdata->u.data + nput);
1414 ptr = putVal16(ptr, opt->opt);
1415 ptr = putVal16(ptr, opt->optlen);
1416 nput += 2 * sizeof(mDNSu16);
1417 if (opt->opt == kDNSOpt_LLQ)
1418 {
1419 if (ptr + LLQ_OPTLEN > limit) goto space_err;
1420 ptr = putVal16(ptr, opt->OptData.llq.vers);
1421 ptr = putVal16(ptr, opt->OptData.llq.llqOp);
1422 ptr = putVal16(ptr, opt->OptData.llq.err);
1423 mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id
1424 ptr += 8;
1425 ptr = putVal32(ptr, opt->OptData.llq.lease);
1426 nput += LLQ_OPTLEN;
1427 }
1428 else if (opt->opt == kDNSOpt_Lease)
1429 {
1430 if (ptr + sizeof(mDNSs32) > limit) goto space_err;
1431 ptr = putVal32(ptr, opt->OptData.lease);
1432 nput += sizeof(mDNSs32);
1433 }
1434 else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; }
1435 }
1436
1437 return ptr;
1438
1439 space_err:
1440 LogMsg("ERROR: putOptRData - out of space");
1441 return mDNSNULL;
1442 }
1443
getVal16(const mDNSu8 ** ptr)1444 mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
1445 {
1446 mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
1447 *ptr += sizeof(mDNSOpaque16);
1448 return val;
1449 }
1450
getOptRdata(const mDNSu8 * ptr,const mDNSu8 * const limit,LargeCacheRecord * const cr,mDNSu16 pktRDLen)1451 mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen)
1452 {
1453 int nread = 0;
1454 ResourceRecord *const rr = &cr->r.resrec;
1455 rdataOpt *opt = (rdataOpt *)rr->rdata->u.data;
1456
1457 while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOpt))
1458 {
1459 // space for opt + optlen
1460 if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err;
1461 opt->opt = getVal16(&ptr);
1462 opt->optlen = getVal16(&ptr);
1463 nread += 2 * sizeof(mDNSu16);
1464 if (opt->opt == kDNSOpt_LLQ)
1465 {
1466 if ((unsigned)(limit - ptr) < LLQ_OPTLEN) goto space_err;
1467 opt->OptData.llq.vers = getVal16(&ptr);
1468 opt->OptData.llq.llqOp = getVal16(&ptr);
1469 opt->OptData.llq.err = getVal16(&ptr);
1470 mDNSPlatformMemCopy(ptr, opt->OptData.llq.id, 8);
1471 ptr += 8;
1472 opt->OptData.llq.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1473 if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond)
1474 opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond;
1475 ptr += sizeof(mDNSOpaque32);
1476 nread += LLQ_OPTLEN;
1477 }
1478 else if (opt->opt == kDNSOpt_Lease)
1479 {
1480 if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err;
1481
1482 opt->OptData.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1483 if (opt->OptData.lease > 0x70000000UL / mDNSPlatformOneSecond)
1484 opt->OptData.lease = 0x70000000UL / mDNSPlatformOneSecond;
1485 ptr += sizeof(mDNSs32);
1486 nread += sizeof(mDNSs32);
1487 }
1488 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
1489 opt++; // increment pointer into rdatabody
1490 }
1491
1492 rr->rdlength = pktRDLen;
1493 return ptr;
1494
1495 space_err:
1496 LogMsg("ERROR: getLLQRdata - out of space");
1497 return mDNSNULL;
1498 }
1499
putRData(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,ResourceRecord * rr)1500 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr)
1501 {
1502 switch (rr->rrtype)
1503 {
1504 case kDNSType_A: if (rr->rdlength != 4)
1505 {
1506 debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength);
1507 return(mDNSNULL);
1508 }
1509 if (ptr + 4 > limit) return(mDNSNULL);
1510 *ptr++ = rr->rdata->u.ipv4.b[0];
1511 *ptr++ = rr->rdata->u.ipv4.b[1];
1512 *ptr++ = rr->rdata->u.ipv4.b[2];
1513 *ptr++ = rr->rdata->u.ipv4.b[3];
1514 return(ptr);
1515
1516 case kDNSType_CNAME:// Same as PTR
1517 case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
1518
1519 case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
1520 {
1521 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
1522 return(mDNSNULL);
1523 }
1524 if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL);
1525 mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6));
1526 return(ptr + sizeof(rr->rdata->u.ipv6));
1527
1528 case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
1529 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8);
1530 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF);
1531 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8);
1532 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF);
1533 *ptr++ = rr->rdata->u.srv.port.b[0];
1534 *ptr++ = rr->rdata->u.srv.port.b[1];
1535 return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target));
1536 case kDNSType_OPT: return putOptRData(ptr, limit, rr);
1537
1538 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
1539 // Fall through to common code below
1540 case kDNSType_HINFO:
1541 case kDNSType_TXT:
1542 case kDNSType_TSIG: if (ptr + rr->rdlength > limit) return(mDNSNULL);
1543 mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength);
1544 return(ptr + rr->rdlength);
1545 }
1546 }
1547
PutResourceRecordTTLWithLimit(DNSMessage * const msg,mDNSu8 * ptr,mDNSu16 * count,ResourceRecord * rr,mDNSu32 ttl,const mDNSu8 * limit)1548 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
1549 {
1550 mDNSu8 *endofrdata;
1551 mDNSu16 actualLength;
1552
1553 if (rr->RecordType == kDNSRecordTypeUnregistered)
1554 {
1555 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1556 return(ptr);
1557 }
1558
1559 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
1560 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1561 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
1562 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
1563 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
1564 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
1565 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
1566 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
1567 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
1568 ptr[7] = (mDNSu8)( ttl & 0xFF);
1569 endofrdata = putRData(msg, ptr+10, limit, rr);
1570 if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
1571
1572 // Go back and fill in the actual number of data bytes we wrote
1573 // (actualLength can be less than rdlength when domain name compression is used)
1574 actualLength = (mDNSu16)(endofrdata - ptr - 10);
1575 ptr[8] = (mDNSu8)(actualLength >> 8);
1576 ptr[9] = (mDNSu8)(actualLength & 0xFF);
1577
1578 if (count) (*count)++;
1579 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1580 return(endofrdata);
1581 }
1582
PutResourceRecordCappedTTL(DNSMessage * const msg,mDNSu8 * ptr,mDNSu16 * count,ResourceRecord * rr,mDNSu32 maxttl)1583 mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32
1584 maxttl)
1585 {
1586 if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
1587 return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
1588 }
1589
putEmptyResourceRecord(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,mDNSu16 * count,const AuthRecord * rr)1590 mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1591 mDNSu16 *count, const AuthRecord *rr)
1592 {
1593 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
1594 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1595 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
1596 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
1597 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
1598 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
1599 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
1600 ptr[8] = ptr[9] = 0; // RDATA length is zero
1601 (*count)++;
1602 return(ptr + 10);
1603 }
1604
putQuestion(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name,mDNSu16 rrtype,mDNSu16 rrclass)1605 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
1606 {
1607 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1608 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1609 ptr[0] = (mDNSu8)(rrtype >> 8);
1610 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1611 ptr[2] = (mDNSu8)(rrclass >> 8);
1612 ptr[3] = (mDNSu8)(rrclass & 0xFF);
1613 msg->h.numQuestions++;
1614 return(ptr+4);
1615 }
1616
1617 // for dynamic updates
putZone(DNSMessage * const msg,mDNSu8 * ptr,mDNSu8 * limit,const domainname * zone,mDNSOpaque16 zoneClass)1618 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
1619 {
1620 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
1621 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
1622 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
1623 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
1624 *ptr++ = zoneClass.b[0];
1625 *ptr++ = zoneClass.b[1];
1626 msg->h.mDNS_numZones++;
1627 return ptr;
1628 }
1629
1630 // for dynamic updates
putPrereqNameNotInUse(domainname * name,DNSMessage * msg,mDNSu8 * ptr,mDNSu8 * end)1631 mDNSexport mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end)
1632 {
1633 AuthRecord prereq;
1634
1635 mDNSPlatformMemZero(&prereq, sizeof(AuthRecord));
1636 mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1637 AssignDomainName(prereq.resrec.name, name);
1638 prereq.resrec.rrtype = kDNSQType_ANY;
1639 prereq.resrec.rrclass = kDNSClass_NONE;
1640 ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
1641 return ptr;
1642 }
1643
1644 // for dynamic updates
putDeletionRecord(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr)1645 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
1646 {
1647 mDNSu16 origclass;
1648 // deletion: specify record w/ TTL 0, class NONE
1649
1650 origclass = rr->rrclass;
1651 rr->rrclass = kDNSClass_NONE;
1652 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
1653 rr->rrclass = origclass;
1654 return ptr;
1655 }
1656
putDeleteRRSet(DNSMessage * msg,mDNSu8 * ptr,const domainname * name,mDNSu16 rrtype)1657 mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype)
1658 {
1659 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1660 mDNSu16 class = kDNSQClass_ANY;
1661
1662 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1663 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
1664 ptr[0] = (mDNSu8)(rrtype >> 8);
1665 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1666 ptr[2] = (mDNSu8)(class >> 8);
1667 ptr[3] = (mDNSu8)(class & 0xFF);
1668 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
1669 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
1670
1671 msg->h.mDNS_numUpdates++;
1672 return ptr + 10;
1673 }
1674
1675 // for dynamic updates
putDeleteAllRRSets(DNSMessage * msg,mDNSu8 * ptr,const domainname * name)1676 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
1677 {
1678 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1679 mDNSu16 class = kDNSQClass_ANY;
1680 mDNSu16 rrtype = kDNSQType_ANY;
1681
1682 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1683 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
1684 ptr[0] = (mDNSu8)(rrtype >> 8);
1685 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1686 ptr[2] = (mDNSu8)(class >> 8);
1687 ptr[3] = (mDNSu8)(class & 0xFF);
1688 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
1689 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
1690
1691 msg->h.mDNS_numUpdates++;
1692 return ptr + 10;
1693 }
1694
1695 // for dynamic updates
putUpdateLease(DNSMessage * msg,mDNSu8 * end,mDNSu32 lease)1696 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
1697 {
1698 AuthRecord rr;
1699 ResourceRecord *opt = &rr.resrec;
1700 rdataOpt *optRD;
1701
1702 mDNSPlatformMemZero(&rr, sizeof(AuthRecord));
1703 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1704
1705 opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers
1706 opt->rrtype = kDNSType_OPT;
1707 opt->rdlength = LEASE_OPT_RDLEN;
1708 opt->rdestimate = LEASE_OPT_RDLEN;
1709
1710 optRD = &rr.resrec.rdata->u.opt;
1711 optRD->opt = kDNSOpt_Lease;
1712 optRD->optlen = sizeof(mDNSs32);
1713 optRD->OptData.lease = lease;
1714 end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, opt, 0);
1715 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
1716
1717 return end;
1718 }
1719
1720 // ***************************************************************************
1721 #if COMPILER_LIKES_PRAGMA_MARK
1722 #pragma mark -
1723 #pragma mark - DNS Message Parsing Functions
1724 #endif
1725
DomainNameHashValue(const domainname * const name)1726 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
1727 {
1728 mDNSu32 sum = 0;
1729 const mDNSu8 *c;
1730
1731 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
1732 {
1733 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
1734 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
1735 sum = (sum<<3) | (sum>>29);
1736 }
1737 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
1738 return(sum);
1739 }
1740
SetNewRData(ResourceRecord * const rr,RData * NewRData,mDNSu16 rdlength)1741 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
1742 {
1743 domainname *target;
1744 if (NewRData)
1745 {
1746 rr->rdata = NewRData;
1747 rr->rdlength = rdlength;
1748 }
1749 // Must not try to get target pointer until after updating rr->rdata
1750 target = GetRRDomainNameTarget(rr);
1751 rr->rdlength = GetRDLength(rr, mDNSfalse);
1752 rr->rdestimate = GetRDLength(rr, mDNStrue);
1753 rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->rdlength, &rr->rdata->u);
1754 }
1755
skipDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end)1756 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
1757 {
1758 mDNSu16 total = 0;
1759
1760 if (ptr < (mDNSu8*)msg || ptr >= end)
1761 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1762
1763 while (1) // Read sequence of labels
1764 {
1765 const mDNSu8 len = *ptr++; // Read length of this label
1766 if (len == 0) return(ptr); // If length is zero, that means this name is complete
1767 switch (len & 0xC0)
1768 {
1769 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1770 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1771 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
1772 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1773 ptr += len;
1774 total += 1 + len;
1775 break;
1776
1777 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
1778 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
1779 case 0xC0: return(ptr+1);
1780 }
1781 }
1782 }
1783
1784 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
getDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end,domainname * const name)1785 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
1786 domainname *const name)
1787 {
1788 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
1789 mDNSu8 *np = name->c; // Name pointer
1790 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
1791
1792 if (ptr < (mDNSu8*)msg || ptr >= end)
1793 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1794
1795 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1796
1797 while (1) // Read sequence of labels
1798 {
1799 const mDNSu8 len = *ptr++; // Read length of this label
1800 if (len == 0) break; // If length is zero, that means this name is complete
1801 switch (len & 0xC0)
1802 {
1803 int i;
1804 mDNSu16 offset;
1805
1806 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1807 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1808 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
1809 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1810 *np++ = len;
1811 for (i=0; i<len; i++) *np++ = *ptr++;
1812 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1813 break;
1814
1815 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
1816 return(mDNSNULL);
1817
1818 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
1819
1820 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
1821 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
1822 ptr = (mDNSu8 *)msg + offset;
1823 if (ptr < (mDNSu8*)msg || ptr >= end)
1824 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
1825 if (*ptr & 0xC0)
1826 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
1827 break;
1828 }
1829 }
1830
1831 if (nextbyte) return(nextbyte);
1832 else return(ptr);
1833 }
1834
skipResourceRecord(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)1835 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1836 {
1837 mDNSu16 pktrdlength;
1838
1839 ptr = skipDomainName(msg, ptr, end);
1840 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
1841
1842 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1843 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1844 ptr += 10;
1845 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1846
1847 return(ptr + pktrdlength);
1848 }
1849
GetLargeResourceRecord(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,mDNSu8 RecordType,LargeCacheRecord * largecr)1850 mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr,
1851 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr)
1852 {
1853 CacheRecord *rr = &largecr->r;
1854 mDNSu16 pktrdlength;
1855
1856 if (largecr == &m->rec && largecr->r.resrec.RecordType)
1857 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &largecr->r));
1858
1859 rr->next = mDNSNULL;
1860 rr->resrec.name = &largecr->namestorage;
1861
1862 rr->NextInKAList = mDNSNULL;
1863 rr->TimeRcvd = m ? m->timenow : 0;
1864 rr->DelayDelivery = 0;
1865 rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
1866 rr->LastUsed = m ? m->timenow : 0;
1867 rr->CRActiveQuestion = mDNSNULL;
1868 rr->UnansweredQueries = 0;
1869 rr->LastUnansweredTime= 0;
1870 rr->MPUnansweredQ = 0;
1871 rr->MPLastUnansweredQT= 0;
1872 rr->MPUnansweredKA = 0;
1873 rr->MPExpectingKA = mDNSfalse;
1874 rr->NextInCFList = mDNSNULL;
1875
1876 rr->resrec.InterfaceID = InterfaceID;
1877 ptr = getDomainName(msg, ptr, end, rr->resrec.name);
1878 if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); }
1879
1880 if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1881
1882 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
1883 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
1884 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
1885 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
1886 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
1887 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1888 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1889 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1890 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
1891 RecordType |= kDNSRecordTypePacketUniqueMask;
1892 ptr += 10;
1893 if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1894 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
1895
1896 rr->resrec.rdata = (RData*)&rr->rdatastorage;
1897 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
1898
1899 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
1900
1901 switch (rr->resrec.rrtype)
1902 {
1903 case kDNSType_A: rr->resrec.rdata->u.ipv4.b[0] = ptr[0];
1904 rr->resrec.rdata->u.ipv4.b[1] = ptr[1];
1905 rr->resrec.rdata->u.ipv4.b[2] = ptr[2];
1906 rr->resrec.rdata->u.ipv4.b[3] = ptr[3];
1907 break;
1908
1909 case kDNSType_CNAME:// Same as PTR
1910 case kDNSType_NS:
1911 case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name))
1912 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
1913 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1914 break;
1915
1916 case kDNSType_NULL: //Same as TXT
1917 case kDNSType_HINFO://Same as TXT
1918 case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1919 {
1920 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1921 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1922 return(mDNSNULL);
1923 }
1924 rr->resrec.rdlength = pktrdlength;
1925 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1926 break;
1927
1928 case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6));
1929 break;
1930
1931 case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
1932 rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
1933 rr->resrec.rdata->u.srv.port.b[0] = ptr[4];
1934 rr->resrec.rdata->u.srv.port.b[1] = ptr[5];
1935 if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target))
1936 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
1937 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1938 break;
1939
1940 case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname);
1941 if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; }
1942 ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname);
1943 if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; }
1944 if (ptr + 0x14 != end) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; }
1945 rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
1946 rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
1947 rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
1948 rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
1949 rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
1950 break;
1951
1952 case kDNSType_OPT: getOptRdata(ptr, end, largecr, pktrdlength); break;
1953
1954 default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1955 {
1956 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1957 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1958 return(mDNSNULL);
1959 }
1960 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1961 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
1962 // Note: Just because we don't understand the record type, that doesn't
1963 // mean we fail. The DNS protocol specifies rdlength, so we can
1964 // safely skip over unknown records and ignore them.
1965 // We also grab a binary copy of the rdata anyway, since the caller
1966 // might know how to interpret it even if we don't.
1967 rr->resrec.rdlength = pktrdlength;
1968 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1969 break;
1970 }
1971
1972 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
1973 SetNewRData(&rr->resrec, mDNSNULL, 0);
1974
1975 // Success! Now fill in RecordType to show this record contains valid data
1976 rr->resrec.RecordType = RecordType;
1977 return(ptr + pktrdlength);
1978 }
1979
skipQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)1980 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1981 {
1982 ptr = skipDomainName(msg, ptr, end);
1983 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
1984 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1985 return(ptr+4);
1986 }
1987
getQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,DNSQuestion * question)1988 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
1989 DNSQuestion *question)
1990 {
1991 question->InterfaceID = InterfaceID;
1992 ptr = getDomainName(msg, ptr, end, &question->qname);
1993 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
1994 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1995
1996 question->qnamehash = DomainNameHashValue(&question->qname);
1997 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
1998 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
1999 return(ptr+4);
2000 }
2001
LocateAnswers(const DNSMessage * const msg,const mDNSu8 * const end)2002 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
2003 {
2004 int i;
2005 const mDNSu8 *ptr = msg->data;
2006 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
2007 return(ptr);
2008 }
2009
LocateAuthorities(const DNSMessage * const msg,const mDNSu8 * const end)2010 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
2011 {
2012 int i;
2013 const mDNSu8 *ptr = LocateAnswers(msg, end);
2014 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
2015 return(ptr);
2016 }
2017
LocateAdditionals(const DNSMessage * const msg,const mDNSu8 * const end)2018 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
2019 {
2020 int i;
2021 const mDNSu8 *ptr = LocateAuthorities(msg, end);
2022 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
2023 return (ptr);
2024 }
2025
2026 // ***************************************************************************
2027 #if COMPILER_LIKES_PRAGMA_MARK
2028 #pragma mark -
2029 #pragma mark -
2030 #pragma mark - Packet Sending Functions
2031 #endif
2032
mDNSSendDNSMessage(const mDNS * const m,DNSMessage * const msg,mDNSu8 * end,mDNSInterfaceID InterfaceID,const mDNSAddr * dst,mDNSIPPort dstport,int sd,uDNS_AuthInfo * authInfo)2033 mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
2034 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo)
2035 {
2036 mStatus status;
2037 int nsent;
2038 mDNSs32 msglen;
2039 mDNSu8 lenbuf[2];
2040 mDNSu16 numQuestions = msg->h.numQuestions;
2041 mDNSu16 numAnswers = msg->h.numAnswers;
2042 mDNSu16 numAuthorities = msg->h.numAuthorities;
2043 mDNSu16 numAdditionals = msg->h.numAdditionals;
2044 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
2045
2046 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2047 *ptr++ = (mDNSu8)(numQuestions >> 8);
2048 *ptr++ = (mDNSu8)(numQuestions & 0xFF);
2049 *ptr++ = (mDNSu8)(numAnswers >> 8);
2050 *ptr++ = (mDNSu8)(numAnswers & 0xFF);
2051 *ptr++ = (mDNSu8)(numAuthorities >> 8);
2052 *ptr++ = (mDNSu8)(numAuthorities & 0xFF);
2053 *ptr++ = (mDNSu8)(numAdditionals >> 8);
2054 *ptr++ = (mDNSu8)(numAdditionals & 0xFF);
2055
2056 if (authInfo)
2057 {
2058 end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo);
2059 if (!end) return mStatus_UnknownErr;
2060 }
2061
2062 // Send the packet on the wire
2063
2064 if (sd >= 0)
2065 {
2066 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
2067 lenbuf[0] = (mDNSu8)(msglen >> 8); // host->network byte conversion
2068 lenbuf[1] = (mDNSu8)(msglen & 0xFF);
2069 nsent = mDNSPlatformWriteTCP(sd, (char*)lenbuf, 2);
2070 //!!!KRS make sure kernel is sending these as 1 packet!
2071 if (nsent != 2) goto tcp_error;
2072 nsent = mDNSPlatformWriteTCP(sd, (char *)msg, msglen);
2073 if (nsent != msglen) goto tcp_error;
2074 status = mStatus_NoError;
2075 }
2076 else
2077 {
2078 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport);
2079 }
2080
2081 // Put all the integer values back the way they were before we return
2082 msg->h.numQuestions = numQuestions;
2083 msg->h.numAnswers = numAnswers;
2084 msg->h.numAuthorities = numAuthorities;
2085 msg->h.numAdditionals = (mDNSu16)(authInfo ? numAdditionals - 1 : numAdditionals);
2086
2087 return(status);
2088
2089 tcp_error:
2090 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2091 return mStatus_UnknownErr;
2092 }
2093
2094 // ***************************************************************************
2095 #if COMPILER_LIKES_PRAGMA_MARK
2096 #pragma mark -
2097 #pragma mark - RR List Management & Task Management
2098 #endif
2099
mDNS_Lock(mDNS * const m)2100 mDNSexport void mDNS_Lock(mDNS *const m)
2101 {
2102 // MUST grab the platform lock FIRST!
2103 mDNSPlatformLock(m);
2104
2105 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2106 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2107 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2108 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2109 if (m->mDNS_busy != m->mDNS_reentrancy)
2110 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
2111
2112 // If this is an initial entry into the mDNSCore code, set m->timenow
2113 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2114 if (m->mDNS_busy == 0)
2115 {
2116 if (m->timenow)
2117 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m));
2118 m->timenow = mDNS_TimeNow_NoLock(m);
2119 if (m->timenow == 0) m->timenow = 1;
2120 }
2121 else if (m->timenow == 0)
2122 {
2123 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
2124 m->timenow = mDNS_TimeNow_NoLock(m);
2125 if (m->timenow == 0) m->timenow = 1;
2126 }
2127
2128 if (m->timenow_last - m->timenow > 0)
2129 {
2130 m->timenow_adjust += m->timenow_last - m->timenow;
2131 LogMsgNoIdent("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust);
2132 m->timenow = m->timenow_last;
2133 }
2134 m->timenow_last = m->timenow;
2135
2136 // Increment mDNS_busy so we'll recognise re-entrant calls
2137 m->mDNS_busy++;
2138 }
2139
GetNextScheduledEvent(const mDNS * const m)2140 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
2141 {
2142 mDNSs32 e = m->timenow + 0x78000000;
2143 if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e);
2144 if (m->NewQuestions)
2145 {
2146 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
2147 else return(m->timenow);
2148 }
2149 if (m->NewLocalOnlyQuestions) return(m->timenow);
2150 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) return(m->timenow);
2151 if (m->SuppressSending) return(m->SuppressSending);
2152 #ifndef UNICAST_DISABLED
2153 if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent;
2154 #endif
2155 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
2156 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
2157 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
2158 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
2159 return(e);
2160 }
2161
mDNS_Unlock(mDNS * const m)2162 mDNSexport void mDNS_Unlock(mDNS *const m)
2163 {
2164 // Decrement mDNS_busy
2165 m->mDNS_busy--;
2166
2167 // Check for locking failures
2168 if (m->mDNS_busy != m->mDNS_reentrancy)
2169 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
2170
2171 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2172 if (m->mDNS_busy == 0)
2173 {
2174 m->NextScheduledEvent = GetNextScheduledEvent(m);
2175 if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2176 m->timenow = 0;
2177 }
2178
2179 // MUST release the platform lock LAST!
2180 mDNSPlatformUnlock(m);
2181 }
2182