]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/DNSCommon.c
6e317a9f2ce68cf167c44529ebe1954d5d931b08
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24
25 Change History (most recent first):
26
27 $Log: DNSCommon.c,v $
28 Revision 1.35 2004/06/05 00:14:44 cheshire
29 Fix signed/unsigned and other compiler warnings
30
31 Revision 1.34 2004/06/04 00:25:25 cheshire
32 Fix misaligned write exception that occurs on some platforms
33
34 Revision 1.33 2004/06/04 00:16:18 cheshire
35 Remove non-portable use of 'inline'
36
37 Revision 1.32 2004/06/03 03:09:58 ksekar
38 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
39
40 Revision 1.31 2004/05/28 23:42:36 ksekar
41 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
42
43 Revision 1.30 2004/05/26 09:08:04 bradley
44 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
45
46 Revision 1.29 2004/05/18 23:51:25 cheshire
47 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
48
49 Revision 1.28 2004/05/13 04:54:20 ksekar
50 Unified list copy/free code. Added symetric list for
51
52 Revision 1.27 2004/04/22 20:29:07 cheshire
53 Log error message if no count field passed to PutResourceRecordTTL()
54
55 Revision 1.26 2004/04/22 04:07:01 cheshire
56 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
57
58 Revision 1.25 2004/04/22 03:05:28 cheshire
59 kDNSClass_ANY should be kDNSQClass_ANY
60
61 Revision 1.24 2004/04/22 02:51:20 cheshire
62 Use common code for HINFO/TXT and TSIG cases in putRData
63
64 Revision 1.23 2004/04/15 00:51:28 bradley
65 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
66 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
67
68 Revision 1.22 2004/04/14 23:09:28 ksekar
69 Support for TSIG signed dynamic updates.
70
71 Revision 1.21 2004/04/09 16:47:28 cheshire
72 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
73
74 Revision 1.20 2004/04/09 16:37:15 cheshire
75 Suggestion from Bob Bradley:
76 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
77
78 Revision 1.19 2004/04/02 19:34:38 cheshire
79 Fix broken comment
80
81 Revision 1.18 2004/03/30 06:45:00 cheshire
82 Compiler warning fixes from Don Woodward at Roku Labs
83
84 Revision 1.17 2004/03/19 22:25:20 cheshire
85 <rdar://problem/3579561>: Need to limit service types to fourteen characters
86 Won't actually do this for now, but keep the code around just in case
87
88 Revision 1.16 2004/03/08 02:45:35 cheshire
89 Minor change to make a couple of the log messages a bit shorter
90
91 Revision 1.15 2004/03/08 02:44:09 cheshire
92 <rdar://problem/3579561>: Need to limit service types to fourteen characters
93
94 Revision 1.14 2004/02/21 02:06:24 cheshire
95 Can't use anonymous unions -- they're non-standard and don't work on all compilers
96
97 Revision 1.13 2004/02/06 23:04:18 ksekar
98 Basic Dynamic Update support via mDNS_Register (dissabled via
99 UNICAST_REGISTRATION #define)
100
101 Revision 1.12 2004/02/03 22:37:10 cheshire
102 Delete unused (commented-out) code
103
104 Revision 1.11 2004/02/03 22:35:34 cheshire
105 <rdar://problem/3548256>: Should not allow empty string for resolve domain
106
107 Revision 1.10 2004/02/03 19:47:36 ksekar
108 Added an asyncronous state machine mechanism to uDNS.c, including
109 calls to find the parent zone for a domain name. Changes include code
110 in repository previously dissabled via "#if 0 //incomplete". Codepath
111 is currently unused, and will be called to create update records, etc.
112
113 Revision 1.9 2004/01/27 20:15:22 cheshire
114 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
115
116 Revision 1.8 2004/01/24 23:24:36 cheshire
117 Expanded out the list of local domains to reduce risk of mistakes in future
118
119 Revision 1.7 2004/01/24 08:32:30 bradley
120 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
121 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
122 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
123
124 Revision 1.6 2004/01/24 04:59:15 cheshire
125 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
126
127 Revision 1.5 2004/01/23 23:23:14 ksekar
128 Added TCP support for truncated unicast messages.
129
130 Revision 1.4 2004/01/22 02:15:33 cheshire
131 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
132
133 Revision 1.3 2004/01/21 21:16:29 cheshire
134 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
135
136 Revision 1.2 2003/12/13 05:47:48 bradley
137 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
138 expression is constant warning when building with Microsoft compilers.
139
140 Revision 1.1 2003/12/13 03:05:27 ksekar
141 <rdar://problem/3192548>: DynDNS: Unicast query of service records
142
143 */
144
145 // Set mDNS_InstantiateInlines to tell mDNSClientAPI.h to instantiate inline functions, if necessary
146 #define mDNS_InstantiateInlines 1
147 #include "DNSCommon.h"
148
149 // Disable certain benign warnings with Microsoft compilers
150 #if (defined(_MSC_VER))
151 // Disable "conditional expression is constant" warning for debug macros.
152 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
153 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
154 #pragma warning(disable:4127)
155 #endif
156
157 // ***************************************************************************
158 #if COMPILER_LIKES_PRAGMA_MARK
159 #pragma mark -
160 #pragma mark - DNameList copy/deallocation routines
161 #endif
162
163 mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig)
164 {
165 DNameListElem *copy = mDNSNULL, *newelem;
166 const DNameListElem *ptr;
167
168 for (ptr = orig; ptr; ptr = ptr->next)
169 {
170 newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem));
171 if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; }
172 mDNSPlatformStrCopy(ptr->name.c, newelem->name.c);
173 newelem->next = copy;
174 copy = newelem;
175 }
176 return copy;
177 }
178
179 mDNSexport void mDNS_FreeDNameList(DNameListElem *list)
180 {
181 DNameListElem *fptr;
182
183 while (list)
184 {
185 fptr = list;
186 list = list->next;
187 mDNSPlatformMemFree(fptr);
188 }
189 }
190
191 // ***************************************************************************
192 #if COMPILER_LIKES_PRAGMA_MARK
193 #pragma mark -
194 #pragma mark - General Utility Functions
195 #endif
196
197 mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf)
198 {
199 while (intf && !intf->InterfaceActive) intf = intf->next;
200 return(intf);
201 }
202
203 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
204 {
205 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
206 if (next) return(next->InterfaceID); else return(mDNSNULL);
207 }
208
209 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
210 {
211 mDNSu32 slot, used = 0;
212 CacheRecord *rr;
213 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
214 for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
215 if (rr->resrec.InterfaceID == id) used++;
216 return(used);
217 }
218
219 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
220 {
221 switch (rrtype)
222 {
223 case kDNSType_A: return("Addr");
224 case kDNSType_CNAME:return("CNAME");
225 case kDNSType_NULL: return("NULL");
226 case kDNSType_PTR: return("PTR");
227 case kDNSType_HINFO:return("HINFO");
228 case kDNSType_TXT: return("TXT");
229 case kDNSType_AAAA: return("AAAA");
230 case kDNSType_SRV: return("SRV");
231 case kDNSQType_ANY: return("ANY");
232 default: {
233 static char buffer[16];
234 mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
235 return(buffer);
236 }
237 }
238 }
239
240 mDNSexport char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd)
241 {
242 char *ptr = m->MsgBuffer;
243 mDNSu32 length = mDNS_snprintf(m->MsgBuffer, 79, "%4d %##s %s ", rr->rdlength, rr->name.c, DNSTypeName(rr->rrtype));
244 switch (rr->rrtype)
245 {
246 case kDNSType_A: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.4a", &rd->ip); break;
247 case kDNSType_CNAME:// Same as PTR
248 case kDNSType_PTR: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->name); break;
249 case kDNSType_HINFO:// Display this the same as TXT (just show first string)
250 case kDNSType_TXT: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%#s", rd->txt.c); break;
251 case kDNSType_AAAA: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.16a", &rd->ipv6); break;
252 case kDNSType_SRV: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->srv.target); break;
253 default: mDNS_snprintf(m->MsgBuffer+length, 79-length, "RDLen %d: %s",
254 rr->rdlength, rd->data); break;
255 }
256 for (ptr = m->MsgBuffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
257 return(m->MsgBuffer);
258 }
259
260 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)
261 {
262 static mDNSu32 seed = 0;
263 mDNSu32 mask = 1;
264
265 if (!seed) seed = (mDNSu32)mDNSPlatformTimeNow();
266 while (mask < max) mask = (mask << 1) | 1;
267 do seed = seed * 21 + 1; while ((seed & mask) > max);
268 return (seed & mask);
269 }
270
271 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
272 {
273 if (ip1->type == ip2->type)
274 {
275 switch (ip1->type)
276 {
277 case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
278 case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
279 }
280 }
281 return(mDNSfalse);
282 }
283
284 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
285 {
286 switch(ip->type)
287 {
288 case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroup.NotAnInteger);
289 case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] &&
290 ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] &&
291 ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] &&
292 ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] );
293 default: return(mDNSfalse);
294 }
295 }
296
297 // ***************************************************************************
298 #if COMPILER_LIKES_PRAGMA_MARK
299 #pragma mark -
300 #pragma mark - Domain Name Utility Functions
301 #endif
302
303 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
304 {
305 int i;
306 const int len = *a++;
307
308 if (len > MAX_DOMAIN_LABEL)
309 { debugf("Malformed label (too long)"); return(mDNSfalse); }
310
311 if (len != *b++) return(mDNSfalse);
312 for (i=0; i<len; i++)
313 {
314 mDNSu8 ac = *a++;
315 mDNSu8 bc = *b++;
316 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
317 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
318 if (ac != bc) return(mDNSfalse);
319 }
320 return(mDNStrue);
321 }
322
323 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
324 {
325 const mDNSu8 * a = d1->c;
326 const mDNSu8 * b = d2->c;
327 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
328
329 while (*a || *b)
330 {
331 if (a + 1 + *a >= max)
332 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); }
333 if (!SameDomainLabel(a, b)) return(mDNSfalse);
334 a += 1 + *a;
335 b += 1 + *b;
336 }
337
338 return(mDNStrue);
339 }
340
341 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
342 {
343 // Domains that are defined to be resolved via link-local multicast are:
344 // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa.
345 static const domainname *n0 = (domainname*)"\x5" "local";
346 static const domainname *n1 = (domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
347 static const domainname *n2 = (domainname*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
348
349 const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc.
350 d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL;
351 while (d->c[0])
352 {
353 d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
354 d = (domainname*)(d->c + 1 + d->c[0]);
355 }
356
357 if (d1 && SameDomainName(d1, n0)) return(mDNStrue);
358 if (d4 && SameDomainName(d4, n1)) return(mDNStrue);
359 if (d6 && SameDomainName(d6, n2)) return(mDNStrue);
360 return(mDNSfalse);
361 }
362
363 // Returns length of a domain name INCLUDING the byte for the final null label
364 // i.e. for the root label "." it returns one
365 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
366 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
367 // If the given domainname is invalid, result is 256
368 mDNSexport mDNSu16 DomainNameLength(const domainname *const name)
369 {
370 const mDNSu8 *src = name->c;
371 while (*src)
372 {
373 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
374 src += 1 + *src;
375 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
376 }
377 return((mDNSu16)(src - name->c + 1));
378 }
379
380 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
381 // for the final null label i.e. for the root label "." it returns one.
382 // E.g. for the FQDN "foo.com." it returns 9
383 // (length, three data bytes, length, three more data bytes, final zero).
384 // In the case where a parent domain name is provided, and the given name is a child
385 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
386 // of the child name, plus TWO bytes for the compression pointer.
387 // E.g. for the name "foo.com." with parent "com.", it returns 6
388 // (length, three data bytes, two-byte compression pointer).
389 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
390 {
391 const mDNSu8 *src = name->c;
392 if (parent && parent->c[0] == 0) parent = mDNSNULL;
393 while (*src)
394 {
395 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
396 if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
397 src += 1 + *src;
398 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
399 }
400 return((mDNSu16)(src - name->c + 1));
401 }
402
403 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
404 // The C string contains the label as-is, with no escaping, etc.
405 // Any dots in the name are literal dots, not label separators
406 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
407 // in the domainname bufer (i.e., the next byte after the terminating zero).
408 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
409 // AppendLiteralLabelString returns mDNSNULL.
410 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
411 {
412 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
413 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
414 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
415 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
416 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
417
418 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
419 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
420 *ptr++ = 0; // Put the null root label on the end
421 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
422 else return(ptr); // Success: return new value of ptr
423 }
424
425 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
426 // The C string is in conventional DNS syntax:
427 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
428 // If successful, AppendDNSNameString returns a pointer to the next unused byte
429 // in the domainname bufer (i.e., the next byte after the terminating zero).
430 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
431 // AppendDNSNameString returns mDNSNULL.
432 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstr)
433 {
434 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
435 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
436 while (*cstr && ptr < lim) // While more characters, and space to put them...
437 {
438 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
439 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
440 {
441 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
442 if (c == '\\') // If escape character, check next character
443 {
444 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
445 if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]))
446 { // If three decimal digits,
447 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
448 int v1 = cstr[ 0] - '0';
449 int v2 = cstr[ 1] - '0';
450 int val = v0 * 100 + v1 * 10 + v2;
451 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
452 }
453 }
454 *ptr++ = c; // Write the character
455 }
456 if (*cstr) cstr++; // Skip over the trailing dot (if present)
457 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
458 return(mDNSNULL);
459 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
460 }
461
462 *ptr++ = 0; // Put the null root label on the end
463 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
464 else return(ptr); // Success: return new value of ptr
465 }
466
467 // AppendDomainLabel appends a single label to a name.
468 // If successful, AppendDomainLabel returns a pointer to the next unused byte
469 // in the domainname bufer (i.e., the next byte after the terminating zero).
470 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
471 // AppendDomainLabel returns mDNSNULL.
472 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
473 {
474 int i;
475 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
476
477 // Check label is legal
478 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
479
480 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
481 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
482
483 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
484 *ptr++ = 0; // Put the null root label on the end
485 return(ptr);
486 }
487
488 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
489 {
490 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
491 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
492 const mDNSu8 * src = append->c;
493 while(src[0])
494 {
495 int i;
496 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
497 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
498 *ptr = 0; // Put the null root label on the end
499 src += i;
500 }
501 return(ptr);
502 }
503
504 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
505 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
506 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
507 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
508 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
509 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
510 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
511 {
512 mDNSu8 * ptr = label->c + 1; // Where we're putting it
513 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
514 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
515 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
516 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
517 }
518
519 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
520 // The C string is in conventional DNS syntax:
521 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
522 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
523 // in the domainname bufer (i.e., the next byte after the terminating zero).
524 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
525 // MakeDomainNameFromDNSNameString returns mDNSNULL.
526 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
527 {
528 name->c[0] = 0; // Make an empty domain name
529 return(AppendDNSNameString(name, cstr)); // And then add this string to it
530 }
531
532 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
533 {
534 const mDNSu8 * src = label->c; // Domain label we're reading
535 const mDNSu8 len = *src++; // Read length of this (non-null) label
536 const mDNSu8 *const end = src + len; // Work out where the label ends
537 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
538 while (src < end) // While we have characters in the label
539 {
540 mDNSu8 c = *src++;
541 if (esc)
542 {
543 if (c == '.' || c == esc) // If character is a dot or the escape character
544 *ptr++ = esc; // Output escape character
545 else if (c <= ' ') // If non-printing ascii,
546 { // Output decimal escape sequence
547 *ptr++ = esc;
548 *ptr++ = (char) ('0' + (c / 100) );
549 *ptr++ = (char) ('0' + (c / 10) % 10);
550 c = (mDNSu8)('0' + (c ) % 10);
551 }
552 }
553 *ptr++ = (char)c; // Copy the character
554 }
555 *ptr = 0; // Null-terminate the string
556 return(ptr); // and return
557 }
558
559 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
560 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
561 {
562 const mDNSu8 *src = name->c; // Domain name we're reading
563 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
564
565 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
566
567 while (*src) // While more characters in the domain name
568 {
569 if (src + 1 + *src >= max) return(mDNSNULL);
570 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
571 if (!ptr) return(mDNSNULL);
572 src += 1 + *src;
573 *ptr++ = '.'; // Write the dot after the label
574 }
575
576 *ptr++ = 0; // Null-terminate the string
577 return(ptr); // and return
578 }
579
580 // RFC 1034 rules:
581 // Host names must start with a letter, end with a letter or digit,
582 // and have as interior characters only letters, digits, and hyphen.
583 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
584
585 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
586 {
587 const mDNSu8 * src = &UTF8Name[1];
588 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
589 mDNSu8 * ptr = &hostlabel->c[1];
590 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
591 while (src < end)
592 {
593 // Delete apostrophes from source name
594 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
595 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
596 { src += 3; continue; } // Unicode curly apostrophe
597 if (ptr < lim)
598 {
599 if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
600 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
601 }
602 src++;
603 }
604 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
605 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
606 }
607
608 #if MDNS_ENFORCE_SERVICE_TYPE_LENGTH
609 mDNSlocal mDNSBool AllowedServiceNameException(const mDNSu8 *const src)
610 {
611 if (SameDomainLabel(src, (mDNSu8*)"\x12_MacOSXDupSuppress")) return(mDNStrue);
612 LogMsg("Application protocol name %#s too long; see <http://www.dns-sd.org/ServiceTypes.html>", src);
613 return(mDNSfalse);
614 }
615 #endif
616
617 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
618 const domainlabel *name, const domainname *type, const domainname *const domain)
619 {
620 int i, len;
621 mDNSu8 *dst = fqdn->c;
622 const mDNSu8 *src;
623 const char *errormsg;
624
625 // In the case where there is no name (and ONLY in that case),
626 // a single-label subtype is allowed as the first label of a three-part "type"
627 if (!name)
628 {
629 const mDNSu8 *s2 = type->c + 1 + type->c[0];
630 if (type->c[0] > 0 && type->c[0] < 0x40 &&
631 s2[0] > 0 && s2[0] < 0x40 &&
632 s2[1+s2[0]] > 0 && s2[1+s2[0]] < 0x40)
633 {
634 name = (domainlabel *)type;
635 type = (domainname *)s2;
636 }
637 }
638
639 if (name && name->c[0])
640 {
641 src = name->c; // Put the service name into the domain name
642 len = *src;
643 if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; }
644 for (i=0; i<=len; i++) *dst++ = *src++;
645 }
646 else
647 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
648
649 src = type->c; // Put the service type into the domain name
650 len = *src;
651 #if MDNS_ENFORCE_SERVICE_TYPE_LENGTH
652 if (len < 2 || len > 15)
653 if (!AllowedServiceNameException(src)) // If length not legal, check our grandfather-exceptions list
654 { errormsg="Application protocol name must be underscore plus 1-14 characters"; goto fail; }
655 #else
656 if (len < 2 || len >= 0x40) { errormsg="Application protocol name should be underscore plus 1-14 characters"; goto fail; }
657 #endif
658 if (src[1] != '_') { errormsg="Application protocol name must begin with underscore"; goto fail; }
659 for (i=2; i<=len; i++)
660 if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_')
661 { errormsg="Application protocol name must contain only letters, digits, and hyphens"; goto fail; }
662 for (i=0; i<=len; i++) *dst++ = *src++;
663
664 len = *src;
665 if (!(len == 4 && src[1] == '_' &&
666 (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) &&
667 (src[4] | 0x20) == 'p'))
668 { errormsg="Service transport protocol name must be _udp or _tcp"; goto fail; }
669 for (i=0; i<=len; i++) *dst++ = *src++;
670
671 if (*src) { errormsg="Service type must have only two labels"; goto fail; }
672
673 *dst = 0;
674 if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; }
675 dst = AppendDomainName(fqdn, domain);
676 if (!dst) { errormsg="Service domain too long"; goto fail; }
677 return(dst);
678
679 fail:
680 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
681 return(mDNSNULL);
682 }
683
684 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
685 domainlabel *const name, domainname *const type, domainname *const domain)
686 {
687 int i, len;
688 const mDNSu8 *src = fqdn->c;
689 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
690 mDNSu8 *dst;
691
692 dst = name->c; // Extract the service name from the domain name
693 len = *src;
694 if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); }
695 for (i=0; i<=len; i++) *dst++ = *src++;
696
697 dst = type->c; // Extract the service type from the domain name
698 len = *src;
699 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
700 for (i=0; i<=len; i++) *dst++ = *src++;
701
702 len = *src;
703 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
704 for (i=0; i<=len; i++) *dst++ = *src++;
705 *dst++ = 0; // Put the null root label on the end of the service type
706
707 dst = domain->c; // Extract the service domain from the domain name
708 while (*src)
709 {
710 len = *src;
711 if (len >= 0x40)
712 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); }
713 if (src + 1 + len + 1 >= max)
714 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse); }
715 for (i=0; i<=len; i++) *dst++ = *src++;
716 }
717 *dst++ = 0; // Put the null root label on the end
718
719 return(mDNStrue);
720 }
721
722 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
723 // name ends in "-nnn", where n is some decimal number.
724 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
725 {
726 mDNSu16 l = name->c[0];
727
728 if (RichText)
729 {
730 if (l < 4) return mDNSfalse; // Need at least " (2)"
731 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
732 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
733 l--;
734 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
735 return (name->c[l] == '(' && name->c[l - 1] == ' ');
736 }
737 else
738 {
739 if (l < 2) return mDNSfalse; // Need at least "-2"
740 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
741 l--;
742 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
743 return (name->c[l] == '-');
744 }
745 }
746
747 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
748 // responsible for ensuring that the label does indeed contain a suffix. returns the number
749 // from the suffix that was removed.
750 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
751 {
752 mDNSu32 val = 0, multiplier = 1;
753
754 // Chop closing parentheses from RichText suffix
755 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
756
757 // Get any existing numerical suffix off the name
758 while (mdnsIsDigit(name->c[name->c[0]]))
759 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
760
761 // Chop opening parentheses or dash from suffix
762 if (RichText)
763 {
764 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
765 }
766 else
767 {
768 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
769 }
770
771 return(val);
772 }
773
774 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
775 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
776 mDNSexport void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText)
777 {
778 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
779 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
780
781 // Truncate trailing spaces from RichText names
782 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
783
784 while (val >= divisor * 10) { divisor *= 10; chars++; }
785
786 if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - chars))
787 {
788 name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - chars);
789 // If the following character is a UTF-8 continuation character,
790 // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point
791 while (name->c[0] > 0 && (name->c[name->c[0]+1] & 0xC0) == 0x80) name->c[0]--;
792 }
793
794 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
795 else { name->c[++name->c[0]] = '-'; }
796
797 while (divisor)
798 {
799 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
800 val %= divisor;
801 divisor /= 10;
802 }
803
804 if (RichText) name->c[++name->c[0]] = ')';
805 }
806
807 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
808 {
809 mDNSu32 val = 0;
810
811 if (LabelContainsSuffix(name, RichText))
812 val = RemoveLabelSuffix(name, RichText);
813
814 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
815 // If existing suffix in the range 2-9, increment it.
816 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
817 // so add a random increment to improve the chances of finding an available name next time.
818 if (val == 0) val = 2;
819 else if (val < 10) val++;
820 else val += 1 + mDNSRandom(99);
821
822 AppendLabelSuffix(name, val, RichText);
823 }
824
825 // ***************************************************************************
826 #if COMPILER_LIKES_PRAGMA_MARK
827 #pragma mark -
828 #pragma mark - Resource Record Utility Functions
829 #endif
830
831 mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb)
832 {
833 mDNSu32 sum = 0;
834 int i;
835 for (i=0; i+1 < rdlength; i+=2)
836 {
837 sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
838 sum = (sum<<3) | (sum>>29);
839 }
840 if (i < rdlength)
841 {
842 sum += ((mDNSu32)(rdb->data[i])) << 8;
843 }
844 return(sum);
845 }
846
847 mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
848 {
849 if (r1->rrtype != r2->rrtype) return(mDNSfalse);
850 if (r1->rdlength != r2->rdlength) return(mDNSfalse);
851 if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
852 if (r1->rdnamehash != r2->rdnamehash) return(mDNSfalse);
853 switch(r1->rrtype)
854 {
855 case kDNSType_CNAME:// Same as PTR
856 case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name));
857
858 case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->rdata->u.srv.priority &&
859 r1->rdata->u.srv.weight == r2->rdata->u.srv.weight &&
860 r1->rdata->u.srv.port.NotAnInteger == r2->rdata->u.srv.port.NotAnInteger &&
861 SameDomainName(&r1->rdata->u.srv.target, &r2->rdata->u.srv.target) );
862
863 default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength));
864 }
865 }
866
867 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
868 {
869 if (rr->InterfaceID &&
870 q ->InterfaceID &&
871 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
872
873 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
874 if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse);
875 if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
876 return(rr->namehash == q->qnamehash && SameDomainName(&rr->name, &q->qname));
877 }
878
879 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
880 {
881 RDataBody *rd = &rr->rdata->u;
882 const domainname *const name = estimate ? &rr->name : mDNSNULL;
883 switch (rr->rrtype)
884 {
885 case kDNSType_A: return(sizeof(rd->ip));
886 case kDNSType_CNAME:// Same as PTR
887 case kDNSType_NS: // Same as PTR
888 case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name));
889 case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
890 case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength
891 case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength
892 case kDNSType_AAAA: return(sizeof(rd->ipv6));
893 case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
894 case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
895 CompressedDomainNameLength(&rd->soa.rname, name) +
896 5 * sizeof(mDNSOpaque32));
897 case kDNSType_OPT: return(rr->rdlength);
898 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
899 return(rr->rdlength);
900 }
901 }
902
903 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
904 {
905 mDNSu16 len;
906 switch(rrtype)
907 {
908 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
909
910 case kDNSType_NS: // Same as PTR
911 case kDNSType_MD: // Same as PTR
912 case kDNSType_MF: // Same as PTR
913 case kDNSType_CNAME:// Same as PTR
914 //case kDNSType_SOA not checked
915 case kDNSType_MB: // Same as PTR
916 case kDNSType_MG: // Same as PTR
917 case kDNSType_MR: // Same as PTR
918 //case kDNSType_NULL not checked (no specified format, so always valid)
919 //case kDNSType_WKS not checked
920 case kDNSType_PTR: len = DomainNameLength(&rd->u.name);
921 return(len <= MAX_DOMAIN_NAME && rdlength == len);
922
923 case kDNSType_HINFO:// Same as TXT (roughly)
924 case kDNSType_MINFO:// Same as TXT (roughly)
925 case kDNSType_TXT: {
926 const mDNSu8 *ptr = rd->u.txt.c;
927 const mDNSu8 *end = rd->u.txt.c + rdlength;
928 while (ptr < end) ptr += 1 + ptr[0];
929 return (ptr == end);
930 }
931
932 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
933
934 case kDNSType_MX: len = DomainNameLength(&rd->u.mx.exchange);
935 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
936
937 case kDNSType_SRV: len = DomainNameLength(&rd->u.srv.target);
938 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
939
940 default: return(mDNStrue); // Allow all other types without checking
941 }
942 }
943
944 // ***************************************************************************
945 #if COMPILER_LIKES_PRAGMA_MARK
946 #pragma mark -
947 #pragma mark -
948 #pragma mark - DNS Message Creation Functions
949 #endif
950
951 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
952 {
953 h->id = id;
954 h->flags = flags;
955 h->numQuestions = 0;
956 h->numAnswers = 0;
957 h->numAuthorities = 0;
958 h->numAdditionals = 0;
959 }
960
961 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
962 {
963 const mDNSu8 *result = end - *domname - 1;
964
965 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
966
967 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
968 while (result >= base)
969 {
970 // If the length byte and first character of the label match, then check further to see
971 // if this location in the packet will yield a useful name compression pointer.
972 if (result[0] == domname[0] && result[1] == domname[1])
973 {
974 const mDNSu8 *name = domname;
975 const mDNSu8 *targ = result;
976 while (targ + *name < end)
977 {
978 // First see if this label matches
979 int i;
980 const mDNSu8 *pointertarget;
981 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
982 if (i <= *name) break; // If label did not match, bail out
983 targ += 1 + *name; // Else, did match, so advance target pointer
984 name += 1 + *name; // and proceed to check next label
985 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
986 if (*name == 0) break; // If no more labels to match, we failed, so bail out
987
988 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
989 if (targ[0] < 0x40) continue; // If length value, continue to check next label
990 if (targ[0] < 0xC0) break; // If 40-BF, not valid
991 if (targ+1 >= end) break; // Second byte not present!
992 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
993 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
994 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
995 targ = pointertarget;
996 }
997 }
998 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
999 }
1000 return(mDNSNULL);
1001 }
1002
1003 // Put a string of dot-separated labels as length-prefixed labels
1004 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1005 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1006 // end points to the end of the message so far
1007 // ptr points to where we want to put the name
1008 // limit points to one byte past the end of the buffer that we must not overrun
1009 // domainname is the name to put
1010 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1011 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1012 {
1013 const mDNSu8 *const base = (const mDNSu8 *)msg;
1014 const mDNSu8 * np = name->c;
1015 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1016 const mDNSu8 * pointer = mDNSNULL;
1017 const mDNSu8 *const searchlimit = ptr;
1018
1019 while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
1020 {
1021 if (*np > MAX_DOMAIN_LABEL)
1022 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1023
1024 // This check correctly allows for the final trailing root label:
1025 // e.g.
1026 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1027 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1028 // We know that max will be at name->c[255]
1029 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1030 // six bytes, then exit the loop, write the final terminating root label, and the domain
1031 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1032 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1033 if (np + 1 + *np >= max)
1034 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); }
1035
1036 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1037 if (pointer) // Use a compression pointer if we can
1038 {
1039 mDNSu16 offset = (mDNSu16)(pointer - base);
1040 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1041 *ptr++ = (mDNSu8)( offset & 0xFF);
1042 return(ptr);
1043 }
1044 else // Else copy one label and try again
1045 {
1046 int i;
1047 mDNSu8 len = *np++;
1048 if (ptr + 1 + len >= limit) return(mDNSNULL);
1049 *ptr++ = len;
1050 for (i=0; i<len; i++) *ptr++ = *np++;
1051 }
1052 }
1053
1054 if (ptr < limit) // If we didn't run out of space
1055 {
1056 *ptr++ = 0; // Put the final root label
1057 return(ptr); // and return
1058 }
1059
1060 return(mDNSNULL);
1061 }
1062
1063 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1064 {
1065 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1066 ptr[1] = (mDNSu8)((val ) & 0xFF);
1067 return ptr + sizeof(mDNSOpaque16);
1068 }
1069
1070 mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr)
1071 {
1072 int nput = 0;
1073 rdataOpt *opt;
1074
1075 while (nput < rr->rdlength)
1076 {
1077 // check if space for opt/optlen
1078 if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err;
1079 (mDNSu8 *)opt = rr->rdata->u.data + nput;
1080 ptr = putVal16(ptr, opt->opt);
1081 ptr = putVal16(ptr, opt->optlen);
1082 nput += 2 * sizeof(mDNSu16);
1083 if (opt->opt == kDNSOpt_LLQ)
1084 {
1085 if (ptr + sizeof(LLQOptData) > limit) goto space_err;
1086 ptr = putVal16(ptr, opt->OptData.llq.vers);
1087 ptr = putVal16(ptr, opt->OptData.llq.llqOp);
1088 ptr = putVal16(ptr, opt->OptData.llq.err);
1089 mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id
1090 ptr += 8;
1091 *(mDNSOpaque32 *)ptr = mDNSOpaque32fromIntVal(opt->OptData.llq.lease);
1092 ptr += sizeof(mDNSOpaque32);
1093 nput += sizeof(LLQOptData);
1094 }
1095 else if (opt->opt == kDNSOpt_Lease)
1096 {
1097 if (ptr + sizeof(mDNSs32) > limit) goto space_err;
1098 *(mDNSOpaque32 *)ptr = mDNSOpaque32fromIntVal(opt->OptData.lease);
1099 ptr += sizeof(mDNSs32);
1100 nput += sizeof(mDNSs32);
1101 }
1102 else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; }
1103 }
1104
1105 return ptr;
1106
1107 space_err:
1108 LogMsg("ERROR: putOptRData - out of space");
1109 return mDNSNULL;
1110 }
1111
1112 mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
1113 {
1114 mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
1115 *ptr += sizeof(mDNSOpaque16);
1116 return val;
1117 }
1118
1119 mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr, mDNSu16 pktRDLen)
1120 {
1121 int nread = 0;
1122 rdataOpt *opt;
1123
1124 while (nread < pktRDLen)
1125 {
1126 opt = (rdataOpt *)(rr->rdata->u.data + nread);
1127 // space for opt + optlen
1128 if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err;
1129 opt->opt = getVal16(&ptr);
1130 opt->optlen = getVal16(&ptr);
1131 nread += 2 * sizeof(mDNSu16);
1132 if (opt->opt == kDNSOpt_LLQ)
1133 {
1134 if ((unsigned)(limit - ptr) < sizeof(LLQOptData)) goto space_err;
1135 opt->OptData.llq.vers = getVal16(&ptr);
1136 opt->OptData.llq.llqOp = getVal16(&ptr);
1137 opt->OptData.llq.err = getVal16(&ptr);
1138 mDNSPlatformMemCopy(ptr, opt->OptData.llq.id, 8);
1139 ptr += 8;
1140 opt->OptData.llq.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1141 if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond)
1142 opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond;
1143 ptr += sizeof(mDNSOpaque32);
1144 nread += sizeof(LLQOptData);
1145 }
1146 else if (opt->opt == kDNSOpt_Lease)
1147 {
1148 if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err;
1149
1150 opt->OptData.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1151 if (opt->OptData.lease > 0x70000000UL / mDNSPlatformOneSecond)
1152 opt->OptData.lease = 0x70000000UL / mDNSPlatformOneSecond;
1153 ptr += sizeof(mDNSs32);
1154 nread += sizeof(mDNSs32);
1155 }
1156 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
1157 }
1158
1159 rr->rdlength = pktRDLen;
1160 return ptr;
1161
1162 space_err:
1163 LogMsg("ERROR: getLLQRdata - out of space");
1164 return mDNSNULL;
1165 }
1166
1167 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr)
1168 {
1169 switch (rr->rrtype)
1170 {
1171 case kDNSType_A: if (rr->rdlength != 4)
1172 {
1173 debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength);
1174 return(mDNSNULL);
1175 }
1176 if (ptr + 4 > limit) return(mDNSNULL);
1177 *ptr++ = rr->rdata->u.ip.b[0];
1178 *ptr++ = rr->rdata->u.ip.b[1];
1179 *ptr++ = rr->rdata->u.ip.b[2];
1180 *ptr++ = rr->rdata->u.ip.b[3];
1181 return(ptr);
1182
1183 case kDNSType_CNAME:// Same as PTR
1184 case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
1185
1186 case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
1187 {
1188 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
1189 return(mDNSNULL);
1190 }
1191 if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL);
1192 mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6));
1193 return(ptr + sizeof(rr->rdata->u.ipv6));
1194
1195 case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
1196 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8);
1197 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF);
1198 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8);
1199 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF);
1200 *ptr++ = rr->rdata->u.srv.port.b[0];
1201 *ptr++ = rr->rdata->u.srv.port.b[1];
1202 return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target));
1203 case kDNSType_OPT: return putOptRData(ptr, limit, rr);
1204
1205 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
1206 // Fall through to common code below
1207 case kDNSType_HINFO:
1208 case kDNSType_TXT:
1209 case kDNSType_TSIG: if (ptr + rr->rdlength > limit) return(mDNSNULL);
1210 mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength);
1211 return(ptr + rr->rdlength);
1212 }
1213 }
1214
1215 mDNSexport mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl)
1216 {
1217 mDNSu8 *endofrdata;
1218 mDNSu16 actualLength;
1219 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1220
1221 // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
1222 // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
1223 if (msg->h.numAnswers || msg->h.numAuthorities || msg->h.numAdditionals)
1224 limit = msg->data + NormalMaxDNSMessageData;
1225
1226 if (rr->RecordType == kDNSRecordTypeUnregistered)
1227 {
1228 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
1229 return(ptr);
1230 }
1231
1232 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name);
1233 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1234 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
1235 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
1236 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
1237 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
1238 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
1239 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
1240 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
1241 ptr[7] = (mDNSu8)( ttl & 0xFF);
1242 endofrdata = putRData(msg, ptr+10, limit, rr);
1243 if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
1244
1245 // Go back and fill in the actual number of data bytes we wrote
1246 // (actualLength can be less than rdlength when domain name compression is used)
1247 actualLength = (mDNSu16)(endofrdata - ptr - 10);
1248 ptr[8] = (mDNSu8)(actualLength >> 8);
1249 ptr[9] = (mDNSu8)(actualLength & 0xFF);
1250
1251 if (count) (*count)++;
1252 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
1253 return(endofrdata);
1254 }
1255
1256 mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32
1257 maxttl)
1258 {
1259 if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
1260 return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
1261 }
1262
1263 mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1264 mDNSu16 *count, const AuthRecord *rr)
1265 {
1266 ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->resrec.name);
1267 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1268 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
1269 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
1270 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
1271 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
1272 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
1273 ptr[8] = ptr[9] = 0; // RDATA length is zero
1274 (*count)++;
1275 return(ptr + 10);
1276 }
1277
1278 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
1279 {
1280 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1281 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1282 ptr[0] = (mDNSu8)(rrtype >> 8);
1283 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1284 ptr[2] = (mDNSu8)(rrclass >> 8);
1285 ptr[3] = (mDNSu8)(rrclass & 0xFF);
1286 msg->h.numQuestions++;
1287 return(ptr+4);
1288 }
1289
1290 // ***************************************************************************
1291 #if COMPILER_LIKES_PRAGMA_MARK
1292 #pragma mark -
1293 #pragma mark - DNS Message Parsing Functions
1294 #endif
1295
1296 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
1297 {
1298 mDNSu32 sum = 0;
1299 const mDNSu8 *c;
1300
1301 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
1302 {
1303 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
1304 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
1305 sum = (sum<<3) | (sum>>29);
1306 }
1307 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
1308 return(sum);
1309 }
1310
1311 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
1312 {
1313 domainname *target;
1314 if (NewRData)
1315 {
1316 rr->rdata = NewRData;
1317 rr->rdlength = rdlength;
1318 }
1319 // Must not try to get target pointer until after updating rr->rdata
1320 target = GetRRDomainNameTarget(rr);
1321 rr->rdlength = GetRDLength(rr, mDNSfalse);
1322 rr->rdestimate = GetRDLength(rr, mDNStrue);
1323 rr->rdatahash = RDataHashValue(rr->rdlength, &rr->rdata->u);
1324 rr->rdnamehash = target ? DomainNameHashValue(target) : 0;
1325 }
1326
1327 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
1328 {
1329 mDNSu16 total = 0;
1330
1331 if (ptr < (mDNSu8*)msg || ptr >= end)
1332 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1333
1334 while (1) // Read sequence of labels
1335 {
1336 const mDNSu8 len = *ptr++; // Read length of this label
1337 if (len == 0) return(ptr); // If length is zero, that means this name is complete
1338 switch (len & 0xC0)
1339 {
1340 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1341 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1342 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
1343 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1344 ptr += len;
1345 total += 1 + len;
1346 break;
1347
1348 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
1349 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
1350 case 0xC0: return(ptr+1);
1351 }
1352 }
1353 }
1354
1355 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1356 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
1357 domainname *const name)
1358 {
1359 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
1360 mDNSu8 *np = name->c; // Name pointer
1361 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
1362
1363 if (ptr < (mDNSu8*)msg || ptr >= end)
1364 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1365
1366 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1367
1368 while (1) // Read sequence of labels
1369 {
1370 const mDNSu8 len = *ptr++; // Read length of this label
1371 if (len == 0) break; // If length is zero, that means this name is complete
1372 switch (len & 0xC0)
1373 {
1374 int i;
1375 mDNSu16 offset;
1376
1377 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1378 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1379 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
1380 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1381 *np++ = len;
1382 for (i=0; i<len; i++) *np++ = *ptr++;
1383 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1384 break;
1385
1386 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
1387 return(mDNSNULL);
1388
1389 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
1390
1391 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
1392 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
1393 ptr = (mDNSu8 *)msg + offset;
1394 if (ptr < (mDNSu8*)msg || ptr >= end)
1395 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
1396 if (*ptr & 0xC0)
1397 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
1398 break;
1399 }
1400 }
1401
1402 if (nextbyte) return(nextbyte);
1403 else return(ptr);
1404 }
1405
1406 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1407 {
1408 mDNSu16 pktrdlength;
1409
1410 ptr = skipDomainName(msg, ptr, end);
1411 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
1412
1413 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1414 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1415 ptr += 10;
1416 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1417
1418 return(ptr + pktrdlength);
1419 }
1420
1421 mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr,
1422 const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage)
1423 {
1424 mDNSu16 pktrdlength;
1425
1426 rr->next = mDNSNULL;
1427 rr->resrec.RecordType = RecordType;
1428
1429 rr->NextInKAList = mDNSNULL;
1430 rr->TimeRcvd = m->timenow;
1431 rr->NextRequiredQuery = m->timenow; // Will be updated to the real value when we call SetNextCacheCheckTime()
1432 rr->LastUsed = m->timenow;
1433 rr->UseCount = 0;
1434 rr->CRActiveQuestion = mDNSNULL;
1435 rr->UnansweredQueries = 0;
1436 rr->LastUnansweredTime= 0;
1437 rr->MPUnansweredQ = 0;
1438 rr->MPLastUnansweredQT= 0;
1439 rr->MPUnansweredKA = 0;
1440 rr->MPExpectingKA = mDNSfalse;
1441 rr->NextInCFList = mDNSNULL;
1442
1443 rr->resrec.InterfaceID = InterfaceID;
1444 ptr = getDomainName(msg, ptr, end, &rr->resrec.name);
1445 if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); }
1446
1447 if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1448
1449 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
1450 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
1451 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
1452 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
1453 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
1454 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1455 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1456 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1457 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
1458 rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask;
1459 ptr += 10;
1460 if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1461
1462 if (RDataStorage)
1463 rr->resrec.rdata = RDataStorage;
1464 else
1465 {
1466 rr->resrec.rdata = (RData*)&rr->rdatastorage;
1467 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
1468 }
1469
1470 switch (rr->resrec.rrtype)
1471 {
1472 case kDNSType_A: rr->resrec.rdata->u.ip.b[0] = ptr[0];
1473 rr->resrec.rdata->u.ip.b[1] = ptr[1];
1474 rr->resrec.rdata->u.ip.b[2] = ptr[2];
1475 rr->resrec.rdata->u.ip.b[3] = ptr[3];
1476 break;
1477
1478 case kDNSType_CNAME:// Same as PTR
1479 case kDNSType_NS:
1480 case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name))
1481 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
1482 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1483 break;
1484
1485 case kDNSType_NULL: //Same as TXT
1486 case kDNSType_HINFO://Same as TXT
1487 case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1488 {
1489 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1490 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1491 return(mDNSNULL);
1492 }
1493 rr->resrec.rdlength = pktrdlength;
1494 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1495 break;
1496
1497 case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6));
1498 break;
1499
1500 case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
1501 rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
1502 rr->resrec.rdata->u.srv.port.b[0] = ptr[4];
1503 rr->resrec.rdata->u.srv.port.b[1] = ptr[5];
1504 if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target))
1505 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
1506 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1507 break;
1508
1509 case kDNSType_SOA: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname) ||
1510 !getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname))
1511 { debugf("GetResourceRecord: Malformed SOA RDATA mname/rname"); return mDNSNULL; }
1512 if ((unsigned)(end - ptr) < 5 * sizeof(mDNSOpaque32))
1513 { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; }
1514 rr->resrec.rdata->u.soa.serial.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4;
1515 rr->resrec.rdata->u.soa.refresh.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4;
1516 rr->resrec.rdata->u.soa.retry.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4;
1517 rr->resrec.rdata->u.soa.expire.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4;
1518 rr->resrec.rdata->u.soa.min.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger;
1519 break;
1520
1521 case kDNSType_OPT: getOptRdata(ptr, end, &rr->resrec, pktrdlength); break;
1522
1523 default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1524 {
1525 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1526 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1527 return(mDNSNULL);
1528 }
1529 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1530 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
1531 // Note: Just because we don't understand the record type, that doesn't
1532 // mean we fail. The DNS protocol specifies rdlength, so we can
1533 // safely skip over unknown records and ignore them.
1534 // We also grab a binary copy of the rdata anyway, since the caller
1535 // might know how to interpret it even if we don't.
1536 rr->resrec.rdlength = pktrdlength;
1537 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1538 break;
1539 }
1540
1541 rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name);
1542 SetNewRData(&rr->resrec, mDNSNULL, 0);
1543
1544 return(ptr + pktrdlength);
1545 }
1546
1547 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1548 {
1549 ptr = skipDomainName(msg, ptr, end);
1550 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
1551 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1552 return(ptr+4);
1553 }
1554
1555 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
1556 DNSQuestion *question)
1557 {
1558 question->InterfaceID = InterfaceID;
1559 ptr = getDomainName(msg, ptr, end, &question->qname);
1560 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
1561 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1562
1563 question->qnamehash = DomainNameHashValue(&question->qname);
1564 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
1565 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
1566 return(ptr+4);
1567 }
1568
1569 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
1570 {
1571 int i;
1572 const mDNSu8 *ptr = msg->data;
1573 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
1574 return(ptr);
1575 }
1576
1577 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
1578 {
1579 int i;
1580 const mDNSu8 *ptr = LocateAnswers(msg, end);
1581 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
1582 return(ptr);
1583 }
1584
1585 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
1586 {
1587 int i;
1588 const mDNSu8 *ptr = LocateAuthorities(msg, end);
1589 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
1590 return (ptr);
1591 }
1592
1593 // ***************************************************************************
1594 #if COMPILER_LIKES_PRAGMA_MARK
1595 #pragma mark -
1596 #pragma mark -
1597 #pragma mark - Packet Sending Functions
1598 #endif
1599
1600 mDNSlocal mStatus sendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
1601 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo)
1602 {
1603 mStatus status;
1604 int nsent;
1605 mDNSs32 msglen;
1606 mDNSu8 lenbuf[2];
1607 mDNSu16 numQuestions = msg->h.numQuestions;
1608 mDNSu16 numAnswers = msg->h.numAnswers;
1609 mDNSu16 numAuthorities = msg->h.numAuthorities;
1610 mDNSu16 numAdditionals = msg->h.numAdditionals;
1611 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
1612
1613 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1614 *ptr++ = (mDNSu8)(numQuestions >> 8);
1615 *ptr++ = (mDNSu8)(numQuestions & 0xFF);
1616 *ptr++ = (mDNSu8)(numAnswers >> 8);
1617 *ptr++ = (mDNSu8)(numAnswers & 0xFF);
1618 *ptr++ = (mDNSu8)(numAuthorities >> 8);
1619 *ptr++ = (mDNSu8)(numAuthorities & 0xFF);
1620 *ptr++ = (mDNSu8)(numAdditionals >> 8);
1621 *ptr++ = (mDNSu8)(numAdditionals & 0xFF);
1622
1623 if (authInfo)
1624 {
1625 end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo);
1626 if (!end) return mStatus_UnknownErr;
1627 }
1628
1629 // Send the packet on the wire
1630
1631 if (sd >= 0)
1632 {
1633 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
1634 lenbuf[0] = (mDNSu8)(msglen >> 8); // host->network byte conversion
1635 lenbuf[1] = (mDNSu8)(msglen & 0xFF);
1636 nsent = mDNSPlatformWriteTCP(sd, (char*)lenbuf, 2);
1637 //!!!KRS make sure kernel is sending these as 1 packet!
1638 if (nsent != 2) goto tcp_error;
1639 nsent = mDNSPlatformWriteTCP(sd, (char *)msg, msglen);
1640 if (nsent != msglen) goto tcp_error;
1641 status = mStatus_NoError;
1642 }
1643 else
1644 {
1645 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport);
1646 }
1647
1648 // Put all the integer values back the way they were before we return
1649 msg->h.numQuestions = numQuestions;
1650 msg->h.numAnswers = numAnswers;
1651 msg->h.numAuthorities = numAuthorities;
1652 msg->h.numAdditionals = (mDNSu16)(authInfo ? numAdditionals - 1 : numAdditionals);
1653
1654 return(status);
1655
1656 tcp_error:
1657 LogMsg("sendDNSMessage: error sending message over tcp");
1658 return mStatus_UnknownErr;
1659
1660 }
1661
1662 mDNSexport mStatus mDNSSendDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd)
1663 {
1664 if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; }
1665 return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL);
1666 }
1667
1668 mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end,
1669 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport)
1670 {
1671 return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, mDNSNULL);
1672 }
1673
1674 mDNSexport mStatus mDNSSendSignedDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end,
1675 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, uDNS_AuthInfo *authInfo)
1676 {
1677 return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, authInfo);
1678 }
1679
1680 mDNSexport mStatus mDNSSendSignedDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd, uDNS_AuthInfo *authInfo)
1681 {
1682 if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; }
1683 return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, authInfo);
1684 }