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