1 /* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (c) 2002-2020 Apple Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #ifndef STANDALONE
19 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
20 #define mDNS_InstantiateInlines 1
21 #include "DNSCommon.h"
22 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
23 #include "dnssec_v2.h"
24 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
25
26 // Disable certain benign warnings with Microsoft compilers
27 #if (defined(_MSC_VER))
28 // Disable "conditional expression is constant" warning for debug macros.
29 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
30 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
31 #pragma warning(disable:4127)
32 // Disable "array is too small to include a terminating null character" warning
33 // -- domain labels have an initial length byte, not a terminating null character
34 #pragma warning(disable:4295)
35 #endif
36
37 // ***************************************************************************
38 #if COMPILER_LIKES_PRAGMA_MARK
39 #pragma mark - Program Constants
40 #endif
41
42 mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
43 mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1;
44 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
45 mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-3;
46 mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-4;
47 mDNSexport const mDNSInterfaceID mDNSInterface_BLE = (mDNSInterfaceID)-5;
48
49 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
50 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
51 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
52 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
53 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
54 // with Microsoft's LLMNR client code.
55
56 #define DiscardPortAsNumber 9
57 #define SSHPortAsNumber 22
58 #define UnicastDNSPortAsNumber 53
59 #define SSDPPortAsNumber 1900
60 #define IPSECPortAsNumber 4500
61 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
62 #define NATPMPAnnouncementPortAsNumber 5350
63 #define NATPMPPortAsNumber 5351
64 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
65 #define MulticastDNSPortAsNumber 5353
66 #define LoopbackIPCPortAsNumber 5354
67 //#define MulticastDNSPortAsNumber 5355 // LLMNR
68 #define PrivateDNSPortAsNumber 5533
69
70 mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber >> 8, DiscardPortAsNumber & 0xFF } };
71 mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } };
72 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
73 mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } };
74 mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } };
75 mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } };
76 mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
77 mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } };
78 mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } };
79 mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
80 mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } };
81 mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } };
82
83 mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
84
85 mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
86 mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
87 mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
88 mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
89 mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
90 mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
91 mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } };
92 mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
93
94 mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
95 mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP & PCP Annoucements
96 mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
97 mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104
98 mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
99 mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
100 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
101 mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
102 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
103
104 mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
105 mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } };
106 mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
107 mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
108 mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
109 mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
110 mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
111
112 mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } };
113 mDNSexport const mDNSOpaque128 zeroOpaque128 = { { 0 } };
114
115 extern mDNS mDNSStorage;
116
117 // ***************************************************************************
118 #if COMPILER_LIKES_PRAGMA_MARK
119 #pragma mark -
120 #pragma mark - General Utility Functions
121 #endif
122
123 // return true for RFC1918 private addresses
mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)124 mDNSexport mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)
125 {
126 return ((addr->b[0] == 10) || // 10/8 prefix
127 (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12
128 (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16
129 }
130
DNSScopeToString(mDNSu32 scope)131 mDNSexport const char *DNSScopeToString(mDNSu32 scope)
132 {
133 switch (scope)
134 {
135 case kScopeNone:
136 return "Unscoped";
137 case kScopeInterfaceID:
138 return "InterfaceScoped";
139 case kScopeServiceID:
140 return "ServiceScoped";
141 default:
142 return "Unknown";
143 }
144 }
145
mDNSAddrMapIPv4toIPv6(mDNSv4Addr * in,mDNSv6Addr * out)146 mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out)
147 {
148 out->l[0] = 0;
149 out->l[1] = 0;
150 out->w[4] = 0;
151 out->w[5] = 0xffff;
152 out->b[12] = in->b[0];
153 out->b[13] = in->b[1];
154 out->b[14] = in->b[2];
155 out->b[15] = in->b[3];
156 }
157
mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr * in,mDNSv4Addr * out)158 mDNSexport mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr* out)
159 {
160 if (in->l[0] != 0 || in->l[1] != 0 || in->w[4] != 0 || in->w[5] != 0xffff)
161 return mDNSfalse;
162
163 out->NotAnInteger = in->l[3];
164 return mDNStrue;
165 }
166
GetFirstActiveInterface(NetworkInterfaceInfo * intf)167 mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
168 {
169 while (intf && !intf->InterfaceActive) intf = intf->next;
170 return(intf);
171 }
172
GetNextActiveInterfaceID(const NetworkInterfaceInfo * intf)173 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
174 {
175 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
176 if (next) return(next->InterfaceID);else return(mDNSNULL);
177 }
178
NumCacheRecordsForInterfaceID(const mDNS * const m,mDNSInterfaceID id)179 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
180 {
181 mDNSu32 slot, used = 0;
182 CacheGroup *cg;
183 const CacheRecord *rr;
184 FORALL_CACHERECORDS(slot, cg, rr)
185 {
186 if (rr->resrec.InterfaceID == id)
187 used++;
188 }
189 return(used);
190 }
191
DNSTypeName(mDNSu16 rrtype)192 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
193 {
194 switch (rrtype)
195 {
196 case kDNSType_A: return("Addr");
197 case kDNSType_NS: return("NS");
198 case kDNSType_CNAME: return("CNAME");
199 case kDNSType_SOA: return("SOA");
200 case kDNSType_NULL: return("NULL");
201 case kDNSType_PTR: return("PTR");
202 case kDNSType_HINFO: return("HINFO");
203 case kDNSType_TXT: return("TXT");
204 case kDNSType_AAAA: return("AAAA");
205 case kDNSType_SRV: return("SRV");
206 case kDNSType_OPT: return("OPT");
207 case kDNSType_NSEC: return("NSEC");
208 case kDNSType_NSEC3: return("NSEC3");
209 case kDNSType_NSEC3PARAM: return("NSEC3PARAM");
210 case kDNSType_TSIG: return("TSIG");
211 case kDNSType_RRSIG: return("RRSIG");
212 case kDNSType_DNSKEY: return("DNSKEY");
213 case kDNSType_DS: return("DS");
214 case kDNSType_SVCB: return("SVCB");
215 case kDNSType_HTTPS: return("HTTPS");
216 case kDNSQType_ANY: return("ANY");
217 default: {
218 static char buffer[16];
219 mDNS_snprintf(buffer, sizeof(buffer), "TYPE%d", rrtype);
220 return(buffer);
221 }
222 }
223 }
224
mStatusDescription(mStatus error)225 mDNSexport const char *mStatusDescription(mStatus error)
226 {
227 const char *error_description;
228 switch (error) {
229 case mStatus_NoError:
230 error_description = "mStatus_NoError";
231 break;
232 case mStatus_BadParamErr:
233 error_description = "mStatus_BadParamErr";
234 break;
235
236 default:
237 error_description = "mStatus_UnknownDescription";
238 break;
239 }
240
241 return error_description;
242 }
243
swap32(mDNSu32 x)244 mDNSexport mDNSu32 swap32(mDNSu32 x)
245 {
246 mDNSu8 *ptr = (mDNSu8 *)&x;
247 return (mDNSu32)((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
248 }
249
swap16(mDNSu16 x)250 mDNSexport mDNSu16 swap16(mDNSu16 x)
251 {
252 mDNSu8 *ptr = (mDNSu8 *)&x;
253 return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
254 }
255
PrintTypeBitmap(const mDNSu8 * bmap,int bitmaplen,char * const buffer,mDNSu32 length)256 mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length)
257 {
258 int win, wlen, type;
259
260 while (bitmaplen > 0)
261 {
262 int i;
263
264 if (bitmaplen < 3)
265 {
266 LogMsg("PrintTypeBitmap: malformed bitmap, bitmaplen %d short", bitmaplen);
267 break;
268 }
269
270 win = *bmap++;
271 wlen = *bmap++;
272 bitmaplen -= 2;
273 if (bitmaplen < wlen || wlen < 1 || wlen > 32)
274 {
275 LogInfo("PrintTypeBitmap: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen);
276 break;
277 }
278 if (win < 0 || win >= 256)
279 {
280 LogInfo("PrintTypeBitmap: malformed nsec, bad window win %d", win);
281 break;
282 }
283 type = win * 256;
284 for (i = 0; i < wlen * 8; i++)
285 {
286 if (bmap[i>>3] & (128 >> (i&7)))
287 length += mDNS_snprintf(buffer+length, (MaxMsg - 1) - length, "%s ", DNSTypeName(type + i));
288 }
289 bmap += wlen;
290 bitmaplen -= wlen;
291 }
292 }
293
294 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
295 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
296 // long as this routine is only used for debugging messages, it probably isn't a big problem.
GetRRDisplayString_rdb(const ResourceRecord * const rr,const RDataBody * const rd1,char * const buffer)297 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer)
298 {
299 const RDataBody2 *const rd = (RDataBody2 *)rd1;
300 #define RemSpc (MaxMsg-1-length)
301 char *ptr = buffer;
302 mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
303 if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
304 if (!rr->rdlength && rr->rrtype != kDNSType_OPT) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); }
305
306 switch (rr->rrtype)
307 {
308 case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break;
309
310 case kDNSType_NS: // Same as PTR
311 case kDNSType_CNAME: // Same as PTR
312 case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break;
313
314 case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d",
315 rd->soa.mname.c, rd->soa.rname.c,
316 rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
317 break;
318
319 case kDNSType_HINFO: // Display this the same as TXT (show all constituent strings)
320 case kDNSType_TXT: {
321 const mDNSu8 *t = rd->txt.c;
322 const mDNSu8 *const rdLimit = rd->data + rr->rdlength;
323 const char *separator = "";
324
325 while (t < rdLimit)
326 {
327 mDNSu32 characterStrLength = *t;
328 if (characterStrLength + 1 > (mDNSu32)(rdLimit - t)) // Character string goes out of boundary.
329 {
330 const mDNSu8 *const remainderStart = t + 1;
331 const mDNSu32 remainderLength = (mDNSu32)(rdLimit - remainderStart);
332 length += mDNS_snprintf(buffer + length, RemSpc, "%s%.*s<<OUT OF BOUNDARY CHARACTER STRING>>", separator,
333 remainderLength, remainderStart);
334 (void)length; // Acknowledge "dead store" analyzer warning.
335 break;
336 }
337 length += mDNS_snprintf(buffer+length, RemSpc, "%s%.*s", separator, characterStrLength, t + 1);
338 separator = "¦";
339 t += 1 + characterStrLength;
340 }
341 }
342 break;
343
344 case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break;
345 case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s",
346 rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
347
348 case kDNSType_OPT: {
349 const rdataOPT *opt;
350 const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength];
351 length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass);
352 for (opt = &rd->opt[0]; opt < end; opt++)
353 {
354 switch(opt->opt)
355 {
356 case kDNSOpt_LLQ:
357 length += mDNS_snprintf(buffer+length, RemSpc, " LLQ");
358 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers);
359 length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp);
360 length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err);
361 length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]);
362 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease);
363 break;
364 case kDNSOpt_Lease:
365 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease);
366 break;
367 case kDNSOpt_Owner:
368 length += mDNS_snprintf(buffer+length, RemSpc, " Owner");
369 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers);
370 length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned
371 length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b);
372 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
373 {
374 length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b);
375 if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
376 length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
377 }
378 break;
379 case kDNSOpt_Trace:
380 length += mDNS_snprintf(buffer+length, RemSpc, " Trace");
381 length += mDNS_snprintf(buffer+length, RemSpc, " Platform %d", opt->u.tracer.platf);
382 length += mDNS_snprintf(buffer+length, RemSpc, " mDNSVers %d", opt->u.tracer.mDNSv);
383 break;
384 default:
385 length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt);
386 break;
387 }
388 }
389 }
390 break;
391
392 case kDNSType_NSEC: {
393 domainname *next = (domainname *)rd->data;
394 int len, bitmaplen;
395 mDNSu8 *bmap;
396 len = DomainNameLength(next);
397 bitmaplen = rr->rdlength - len;
398 bmap = (mDNSu8 *)((mDNSu8 *)next + len);
399
400 if (UNICAST_NSEC(rr))
401 length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c);
402 PrintTypeBitmap(bmap, bitmaplen, buffer, length);
403
404 }
405 break;
406
407 default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %.*s", rr->rdlength, rr->rdlength, rd->data);
408 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
409 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
410 break;
411 }
412
413 return(buffer);
414 }
415
416 // See comments in mDNSEmbeddedAPI.h
417 #if _PLATFORM_HAS_STRONG_PRNG_
418 #define mDNSRandomNumber mDNSPlatformRandomNumber
419 #else
mDNSRandomFromSeed(mDNSu32 seed)420 mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed)
421 {
422 return seed * 21 + 1;
423 }
424
mDNSMixRandomSeed(mDNSu32 seed,mDNSu8 iteration)425 mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration)
426 {
427 return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed;
428 }
429
mDNSRandomNumber()430 mDNSlocal mDNSu32 mDNSRandomNumber()
431 {
432 static mDNSBool seeded = mDNSfalse;
433 static mDNSu32 seed = 0;
434 if (!seeded)
435 {
436 seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
437 seeded = mDNStrue;
438 }
439 return (seed = mDNSRandomFromSeed(seed));
440 }
441 #endif // ! _PLATFORM_HAS_STRONG_PRNG_
442
mDNSRandom(mDNSu32 max)443 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive
444 {
445 mDNSu32 ret = 0;
446 mDNSu32 mask = 1;
447
448 while (mask < max) mask = (mask << 1) | 1;
449
450 do ret = mDNSRandomNumber() & mask;
451 while (ret > max);
452
453 return ret;
454 }
455
mDNSSameAddress(const mDNSAddr * ip1,const mDNSAddr * ip2)456 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
457 {
458 if (ip1->type == ip2->type)
459 {
460 switch (ip1->type)
461 {
462 case mDNSAddrType_None: return(mDNStrue); // Empty addresses have no data and are therefore always equal
463 case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
464 case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
465 }
466 }
467 return(mDNSfalse);
468 }
469
mDNSAddrIsDNSMulticast(const mDNSAddr * ip)470 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
471 {
472 switch(ip->type)
473 {
474 case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
475 case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
476 default: return(mDNSfalse);
477 }
478 }
479
480 // ***************************************************************************
481 #if COMPILER_LIKES_PRAGMA_MARK
482 #pragma mark -
483 #pragma mark - Domain Name Utility Functions
484 #endif
485
486 #if !APPLE_OSX_mDNSResponder
487
SameDomainLabel(const mDNSu8 * a,const mDNSu8 * b)488 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
489 {
490 int i;
491 const int len = *a++;
492
493 if (len > MAX_DOMAIN_LABEL)
494 { debugf("Malformed label (too long)"); return(mDNSfalse); }
495
496 if (len != *b++) return(mDNSfalse);
497 for (i=0; i<len; i++)
498 {
499 mDNSu8 ac = *a++;
500 mDNSu8 bc = *b++;
501 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
502 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
503 if (ac != bc) return(mDNSfalse);
504 }
505 return(mDNStrue);
506 }
507
508 #endif // !APPLE_OSX_mDNSResponder
509
SameDomainName(const domainname * const d1,const domainname * const d2)510 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
511 {
512 const mDNSu8 * a = d1->c;
513 const mDNSu8 * b = d2->c;
514 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
515
516 while (*a || *b)
517 {
518 if (a + 1 + *a >= max)
519 { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); }
520 if (!SameDomainLabel(a, b)) return(mDNSfalse);
521 a += 1 + *a;
522 b += 1 + *b;
523 }
524
525 return(mDNStrue);
526 }
527
SameDomainNameCS(const domainname * const d1,const domainname * const d2)528 mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
529 {
530 mDNSu16 l1 = DomainNameLength(d1);
531 mDNSu16 l2 = DomainNameLength(d2);
532 return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
533 }
534
IsLocalDomain(const domainname * d)535 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
536 {
537 // Domains that are defined to be resolved via link-local multicast are:
538 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
539 static const domainname *nL = (const domainname*)"\x5" "local";
540 static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
541 static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
542 static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
543 static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
544 static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
545
546 const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc.
547 d1 = d2 = d3 = d4 = d5 = mDNSNULL;
548 while (d->c[0])
549 {
550 d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
551 d = (const domainname*)(d->c + 1 + d->c[0]);
552 }
553
554 if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
555 if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
556 if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
557 if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
558 if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
559 if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
560 return(mDNSfalse);
561 }
562
LastLabel(const domainname * d)563 mDNSexport const mDNSu8 *LastLabel(const domainname *d)
564 {
565 const mDNSu8 *p = d->c;
566 while (d->c[0])
567 {
568 p = d->c;
569 d = (const domainname*)(d->c + 1 + d->c[0]);
570 }
571 return(p);
572 }
573
574 // Returns length of a domain name INCLUDING the byte for the final null label
575 // e.g. for the root label "." it returns one
576 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
577 // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
578 // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
DomainNameLengthLimit(const domainname * const name,const mDNSu8 * limit)579 mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
580 {
581 const mDNSu8 *src = name->c;
582 while (src < limit && *src <= MAX_DOMAIN_LABEL)
583 {
584 if (*src == 0) return((mDNSu16)(src - name->c + 1));
585 src += 1 + *src;
586 }
587 return(MAX_DOMAIN_NAME+1);
588 }
589
590 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
591 // for the final null label, e.g. for the root label "." it returns one.
592 // E.g. for the FQDN "foo.com." it returns 9
593 // (length, three data bytes, length, three more data bytes, final zero).
594 // In the case where a parent domain name is provided, and the given name is a child
595 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
596 // of the child name, plus TWO bytes for the compression pointer.
597 // E.g. for the name "foo.com." with parent "com.", it returns 6
598 // (length, three data bytes, two-byte compression pointer).
CompressedDomainNameLength(const domainname * const name,const domainname * parent)599 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
600 {
601 const mDNSu8 *src = name->c;
602 if (parent && parent->c[0] == 0) parent = mDNSNULL;
603 while (*src)
604 {
605 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
606 if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
607 src += 1 + *src;
608 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
609 }
610 return((mDNSu16)(src - name->c + 1));
611 }
612
613 // CountLabels() returns number of labels in name, excluding final root label
614 // (e.g. for "apple.com." CountLabels returns 2.)
CountLabels(const domainname * d)615 mDNSexport int CountLabels(const domainname *d)
616 {
617 int count = 0;
618 const mDNSu8 *ptr;
619 for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
620 return count;
621 }
622
623 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
624 // returning a pointer to the suffix with 'skip' labels removed.
SkipLeadingLabels(const domainname * d,int skip)625 mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
626 {
627 while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
628 return(d);
629 }
630
631 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
632 // The C string contains the label as-is, with no escaping, etc.
633 // Any dots in the name are literal dots, not label separators
634 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
635 // in the domainname bufer (i.e. the next byte after the terminating zero).
636 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
637 // AppendLiteralLabelString returns mDNSNULL.
AppendLiteralLabelString(domainname * const name,const char * cstr)638 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
639 {
640 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
641 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
642 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
643 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
644 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
645
646 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
647 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
648 *ptr++ = 0; // Put the null root label on the end
649 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
650 else return(ptr); // Success: return new value of ptr
651 }
652
653 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
654 // The C string is in conventional DNS syntax:
655 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
656 // If successful, AppendDNSNameString returns a pointer to the next unused byte
657 // in the domainname bufer (i.e. the next byte after the terminating zero).
658 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
659 // AppendDNSNameString returns mDNSNULL.
AppendDNSNameString(domainname * const name,const char * cstring)660 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
661 {
662 const char *cstr = cstring;
663 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
664 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
665 while (*cstr && ptr < lim) // While more characters, and space to put them...
666 {
667 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
668 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
669 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
670 {
671 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
672 if (c == '\\') // If escape character, check next character
673 {
674 if (*cstr == '\0') break; // If this is the end of the string, then break
675 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
676 if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1]))
677 { // If three decimal digits,
678 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
679 int v1 = cstr[ 0] - '0';
680 int v2 = cstr[ 1] - '0';
681 int val = v0 * 100 + v1 * 10 + v2;
682 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
683 }
684 }
685 *ptr++ = c; // Write the character
686 }
687 if (*cstr == '.') cstr++; // Skip over the trailing dot (if present)
688 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
689 return(mDNSNULL);
690 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
691 }
692
693 *ptr++ = 0; // Put the null root label on the end
694 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
695 else return(ptr); // Success: return new value of ptr
696 }
697
698 // AppendDomainLabel appends a single label to a name.
699 // If successful, AppendDomainLabel returns a pointer to the next unused byte
700 // in the domainname bufer (i.e. the next byte after the terminating zero).
701 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
702 // AppendDomainLabel returns mDNSNULL.
AppendDomainLabel(domainname * const name,const domainlabel * const label)703 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
704 {
705 int i;
706 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
707
708 // Check label is legal
709 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
710
711 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
712 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
713
714 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
715 *ptr++ = 0; // Put the null root label on the end
716 return(ptr);
717 }
718
AppendDomainName(domainname * const name,const domainname * const append)719 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
720 {
721 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
722 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
723 const mDNSu8 * src = append->c;
724 while (src[0])
725 {
726 int i;
727 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
728 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
729 *ptr = 0; // Put the null root label on the end
730 src += i;
731 }
732 return(ptr);
733 }
734
735 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
736 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
737 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
738 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
739 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
740 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
MakeDomainLabelFromLiteralString(domainlabel * const label,const char * cstr)741 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
742 {
743 mDNSu8 * ptr = label->c + 1; // Where we're putting it
744 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
745 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
746 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
747 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
748 }
749
750 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
751 // The C string is in conventional DNS syntax:
752 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
753 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
754 // in the domainname bufer (i.e. the next byte after the terminating zero).
755 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
756 // MakeDomainNameFromDNSNameString returns mDNSNULL.
MakeDomainNameFromDNSNameString(domainname * const name,const char * cstr)757 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
758 {
759 name->c[0] = 0; // Make an empty domain name
760 return(AppendDNSNameString(name, cstr)); // And then add this string to it
761 }
762
ConvertDomainLabelToCString_withescape(const domainlabel * const label,char * ptr,char esc)763 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
764 {
765 const mDNSu8 * src = label->c; // Domain label we're reading
766 const mDNSu8 len = *src++; // Read length of this (non-null) label
767 const mDNSu8 *const end = src + len; // Work out where the label ends
768 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
769 while (src < end) // While we have characters in the label
770 {
771 mDNSu8 c = *src++;
772 if (esc)
773 {
774 if (c == '.' || c == esc) // If character is a dot or the escape character
775 *ptr++ = esc; // Output escape character
776 else if (c <= ' ') // If non-printing ascii,
777 { // Output decimal escape sequence
778 *ptr++ = esc;
779 *ptr++ = (char) ('0' + (c / 100) );
780 *ptr++ = (char) ('0' + (c / 10) % 10);
781 c = (mDNSu8)('0' + (c ) % 10);
782 }
783 }
784 *ptr++ = (char)c; // Copy the character
785 }
786 *ptr = 0; // Null-terminate the string
787 return(ptr); // and return
788 }
789
790 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
ConvertDomainNameToCString_withescape(const domainname * const name,char * ptr,char esc)791 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
792 {
793 const mDNSu8 *src = name->c; // Domain name we're reading
794 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
795
796 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
797
798 while (*src) // While more characters in the domain name
799 {
800 if (src + 1 + *src >= max) return(mDNSNULL);
801 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
802 if (!ptr) return(mDNSNULL);
803 src += 1 + *src;
804 *ptr++ = '.'; // Write the dot after the label
805 }
806
807 *ptr++ = 0; // Null-terminate the string
808 return(ptr); // and return
809 }
810
811 // RFC 1034 rules:
812 // Host names must start with a letter, end with a letter or digit,
813 // and have as interior characters only letters, digits, and hyphen.
814 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
815
ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[],domainlabel * const hostlabel)816 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
817 {
818 const mDNSu8 * src = &UTF8Name[1];
819 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
820 mDNSu8 * ptr = &hostlabel->c[1];
821 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
822 while (src < end)
823 {
824 // Delete apostrophes from source name
825 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
826 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
827 { src += 3; continue; } // Unicode curly apostrophe
828 if (ptr < lim)
829 {
830 if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
831 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
832 }
833 src++;
834 }
835 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
836 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
837 }
838
ConstructServiceName(domainname * const fqdn,const domainlabel * name,const domainname * type,const domainname * const domain)839 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
840 const domainlabel *name, const domainname *type, const domainname *const domain)
841 {
842 int i, len;
843 mDNSu8 *dst = fqdn->c;
844 const mDNSu8 *src;
845 const char *errormsg;
846 #if APPLE_OSX_mDNSResponder
847 mDNSBool loggedUnderscore = mDNSfalse;
848 static char typeBuf[MAX_ESCAPED_DOMAIN_NAME];
849 #endif
850
851 // In the case where there is no name (and ONLY in that case),
852 // a single-label subtype is allowed as the first label of a three-part "type"
853 if (!name)
854 {
855 const mDNSu8 *s0 = type->c;
856 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
857 {
858 const mDNSu8 * s1 = s0 + 1 + s0[0];
859 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
860 {
861 const mDNSu8 *s2 = s1 + 1 + s1[0];
862 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
863 {
864 static const mDNSu8 SubTypeLabel[5] = mDNSSubTypeLabel;
865 src = s0; // Copy the first label
866 len = *src;
867 for (i=0; i <= len; i++) *dst++ = *src++;
868 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
869 type = (const domainname *)s1;
870
871 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
872 // For these queries, we retract the "._sub" we just added between the subtype and the main type
873 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
874 if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
875 dst -= sizeof(SubTypeLabel);
876 }
877 }
878 }
879 }
880
881 if (name && name->c[0])
882 {
883 src = name->c; // Put the service name into the domain name
884 len = *src;
885 if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
886 for (i=0; i<=len; i++) *dst++ = *src++;
887 }
888 else
889 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
890
891 src = type->c; // Put the service type into the domain name
892 len = *src;
893 if (len < 2 || len > 16)
894 {
895 LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. "
896 "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c);
897 }
898 if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL);
899 if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
900 for (i=2; i<=len; i++)
901 {
902 // Letters and digits are allowed anywhere
903 if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue;
904 // Hyphens are only allowed as interior characters
905 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
906 // with the same rule as hyphens
907 if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len)
908 {
909 #if APPLE_OSX_mDNSResponder
910 if (src[i] == '_' && loggedUnderscore == mDNSfalse)
911 {
912 ConvertDomainNameToCString(type, typeBuf);
913 LogInfo("ConstructServiceName: Service type with non-leading underscore %s", typeBuf);
914 loggedUnderscore = mDNStrue;
915 }
916 #endif
917 continue;
918 }
919 errormsg = "Application protocol name must contain only letters, digits, and hyphens";
920 goto fail;
921 }
922 for (i=0; i<=len; i++) *dst++ = *src++;
923
924 len = *src;
925 if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
926 for (i=0; i<=len; i++) *dst++ = *src++;
927
928 if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
929
930 *dst = 0;
931 if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
932 if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
933 { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
934 dst = AppendDomainName(fqdn, domain);
935 if (!dst) { errormsg = "Service domain too long"; goto fail; }
936 return(dst);
937
938 fail:
939 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
940 return(mDNSNULL);
941 }
942
943 // A service name has the form: instance.application-protocol.transport-protocol.domain
944 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
945 // set or length limits for the protocol names, and the final domain is allowed to be empty.
946 // However, if the given FQDN doesn't contain at least three labels,
947 // DeconstructServiceName will reject it and return mDNSfalse.
DeconstructServiceName(const domainname * const fqdn,domainlabel * const name,domainname * const type,domainname * const domain)948 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
949 domainlabel *const name, domainname *const type, domainname *const domain)
950 {
951 int i, len;
952 const mDNSu8 *src = fqdn->c;
953 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
954 mDNSu8 *dst;
955
956 dst = name->c; // Extract the service name
957 len = *src;
958 if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); }
959 if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
960 for (i=0; i<=len; i++) *dst++ = *src++;
961
962 dst = type->c; // Extract the service type
963 len = *src;
964 if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); }
965 if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
966 if (src[1] != '_') { debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); }
967 for (i=0; i<=len; i++) *dst++ = *src++;
968
969 len = *src;
970 if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
971 if (!ValidTransportProtocol(src))
972 { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
973 for (i=0; i<=len; i++) *dst++ = *src++;
974 *dst++ = 0; // Put terminator on the end of service type
975
976 dst = domain->c; // Extract the service domain
977 while (*src)
978 {
979 len = *src;
980 if (len >= 0x40)
981 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
982 if (src + 1 + len + 1 >= max)
983 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
984 for (i=0; i<=len; i++) *dst++ = *src++;
985 }
986 *dst++ = 0; // Put the null root label on the end
987
988 return(mDNStrue);
989 }
990
DNSNameToLowerCase(domainname * d,domainname * result)991 mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result)
992 {
993 const mDNSu8 *a = d->c;
994 mDNSu8 *b = result->c;
995 const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME;
996 int i, len;
997
998 while (*a)
999 {
1000 if (a + 1 + *a >= max)
1001 {
1002 LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name");
1003 return mStatus_BadParamErr;
1004 }
1005 len = *a++;
1006 *b++ = len;
1007 for (i = 0; i < len; i++)
1008 {
1009 mDNSu8 ac = *a++;
1010 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
1011 *b++ = ac;
1012 }
1013 }
1014 *b = 0;
1015
1016 return mStatus_NoError;
1017 }
1018
1019 // Notes on UTF-8:
1020 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1021 // 10xxxxxx is a continuation byte of a multi-byte character
1022 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1023 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1024 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1025 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1026 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1027 //
1028 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1029 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1030 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1031 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1032 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1033
TruncateUTF8ToLength(mDNSu8 * string,mDNSu32 length,mDNSu32 max)1034 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
1035 {
1036 if (length > max)
1037 {
1038 mDNSu8 c1 = string[max]; // First byte after cut point
1039 mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point
1040 length = max; // Trim length down
1041 while (length > 0)
1042 {
1043 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1044 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1045 // If so, then we continue to chop more bytes until we get to a legal chop point.
1046 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
1047 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
1048 if (!continuation && !secondsurrogate) break;
1049 c2 = c1;
1050 c1 = string[--length];
1051 }
1052 // Having truncated characters off the end of our string, also cut off any residual white space
1053 while (length > 0 && string[length-1] <= ' ') length--;
1054 }
1055 return(length);
1056 }
1057
1058 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1059 // name ends in "-nnn", where n is some decimal number.
LabelContainsSuffix(const domainlabel * const name,const mDNSBool RichText)1060 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
1061 {
1062 mDNSu16 l = name->c[0];
1063
1064 if (RichText)
1065 {
1066 if (l < 4) return mDNSfalse; // Need at least " (2)"
1067 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
1068 if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
1069 l--;
1070 while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
1071 return (name->c[l] == '(' && name->c[l - 1] == ' ');
1072 }
1073 else
1074 {
1075 if (l < 2) return mDNSfalse; // Need at least "-2"
1076 if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
1077 l--;
1078 while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
1079 return (name->c[l] == '-');
1080 }
1081 }
1082
1083 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1084 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1085 // from the suffix that was removed.
RemoveLabelSuffix(domainlabel * name,mDNSBool RichText)1086 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
1087 {
1088 mDNSu32 val = 0, multiplier = 1;
1089
1090 // Chop closing parentheses from RichText suffix
1091 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1092
1093 // Get any existing numerical suffix off the name
1094 while (mDNSIsDigit(name->c[name->c[0]]))
1095 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
1096
1097 // Chop opening parentheses or dash from suffix
1098 if (RichText)
1099 {
1100 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1101 }
1102 else
1103 {
1104 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1105 }
1106
1107 return(val);
1108 }
1109
1110 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1111 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
AppendLabelSuffix(domainlabel * const name,mDNSu32 val,const mDNSBool RichText)1112 mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
1113 {
1114 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1115 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1116
1117 // Truncate trailing spaces from RichText names
1118 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1119
1120 while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
1121
1122 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1123
1124 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1125 else { name->c[++name->c[0]] = '-'; }
1126
1127 while (divisor)
1128 {
1129 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1130 val %= divisor;
1131 divisor /= 10;
1132 }
1133
1134 if (RichText) name->c[++name->c[0]] = ')';
1135 }
1136
IncrementLabelSuffix(domainlabel * name,mDNSBool RichText)1137 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1138 {
1139 mDNSu32 val = 0;
1140
1141 if (LabelContainsSuffix(name, RichText))
1142 val = RemoveLabelSuffix(name, RichText);
1143
1144 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1145 // If existing suffix in the range 2-9, increment it.
1146 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1147 // so add a random increment to improve the chances of finding an available name next time.
1148 if (val == 0) val = 2;
1149 else if (val < 10) val++;
1150 else val += 1 + mDNSRandom(99);
1151
1152 AppendLabelSuffix(name, val, RichText);
1153 }
1154
1155 // ***************************************************************************
1156 #if COMPILER_LIKES_PRAGMA_MARK
1157 #pragma mark -
1158 #pragma mark - Resource Record Utility Functions
1159 #endif
1160
1161 // Set up a AuthRecord with sensible default values.
1162 // These defaults may be overwritten with new values before mDNS_Register is called
mDNS_SetupResourceRecord(AuthRecord * rr,RData * RDataStorage,mDNSInterfaceID InterfaceID,mDNSu16 rrtype,mDNSu32 ttl,mDNSu8 RecordType,AuthRecType artype,mDNSRecordCallback Callback,void * Context)1163 mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
1164 mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context)
1165 {
1166 //
1167 // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID.
1168 // Most of the applications normally create with LocalOnly InterfaceID and we store them as
1169 // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID.
1170 // LocalOnly resource records can also be created with valid InterfaceID which happens today
1171 // when we create LocalOnly records for /etc/hosts.
1172
1173 if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly)
1174 {
1175 LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype);
1176 }
1177 else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P)
1178 {
1179 LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype);
1180 }
1181 else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly))
1182 {
1183 LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype);
1184 }
1185
1186 // Don't try to store a TTL bigger than we can represent in platform time units
1187 if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
1188 ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
1189 else if (ttl == 0) // And Zero TTL is illegal
1190 ttl = DefaultTTLforRRType(rrtype);
1191
1192 // Field Group 1: The actual information pertaining to this resource record
1193 rr->resrec.RecordType = RecordType;
1194 rr->resrec.InterfaceID = InterfaceID;
1195 rr->resrec.name = &rr->namestorage;
1196 rr->resrec.rrtype = rrtype;
1197 rr->resrec.rrclass = kDNSClass_IN;
1198 rr->resrec.rroriginalttl = ttl;
1199 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1200 rr->resrec.dnsservice = NULL;
1201 #else
1202 rr->resrec.rDNSServer = mDNSNULL;
1203 #endif
1204 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1205 // rr->resrec.rdestimate = set in mDNS_Register_internal
1206 // rr->resrec.rdata = MUST be set by client
1207
1208 if (RDataStorage)
1209 rr->resrec.rdata = RDataStorage;
1210 else
1211 {
1212 rr->resrec.rdata = &rr->rdatastorage;
1213 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
1214 }
1215
1216 // Field Group 2: Persistent metadata for Authoritative Records
1217 rr->Additional1 = mDNSNULL;
1218 rr->Additional2 = mDNSNULL;
1219 rr->DependentOn = mDNSNULL;
1220 rr->RRSet = mDNSNULL;
1221 rr->RecordCallback = Callback;
1222 rr->RecordContext = Context;
1223
1224 rr->AutoTarget = Target_Manual;
1225 rr->AllowRemoteQuery = mDNSfalse;
1226 rr->ForceMCast = mDNSfalse;
1227
1228 rr->WakeUp = zeroOwner;
1229 rr->AddressProxy = zeroAddr;
1230 rr->TimeRcvd = 0;
1231 rr->TimeExpire = 0;
1232 rr->ARType = artype;
1233 rr->AuthFlags = 0;
1234
1235 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1236 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1237
1238 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1239 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1240 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1241 rr->state = regState_Zero;
1242 rr->uselease = 0;
1243 rr->expire = 0;
1244 rr->Private = 0;
1245 rr->updateid = zeroID;
1246 rr->zone = rr->resrec.name;
1247 rr->nta = mDNSNULL;
1248 rr->tcp = mDNSNULL;
1249 rr->OrigRData = 0;
1250 rr->OrigRDLen = 0;
1251 rr->InFlightRData = 0;
1252 rr->InFlightRDLen = 0;
1253 rr->QueuedRData = 0;
1254 rr->QueuedRDLen = 0;
1255 mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
1256 rr->SRVChanged = mDNSfalse;
1257 rr->mState = mergeState_Zero;
1258
1259 rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
1260 }
1261
mDNS_SetupQuestion(DNSQuestion * const q,const mDNSInterfaceID InterfaceID,const domainname * const name,const mDNSu16 qtype,mDNSQuestionCallback * const callback,void * const context)1262 mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
1263 const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context)
1264 {
1265 q->InterfaceID = InterfaceID;
1266 q->flags = 0;
1267 AssignDomainName(&q->qname, name);
1268 q->qtype = qtype;
1269 q->qclass = kDNSClass_IN;
1270 q->LongLived = (qtype == kDNSType_PTR);
1271 q->ExpectUnique = (qtype != kDNSType_PTR);
1272 q->ForceMCast = mDNSfalse;
1273 q->ReturnIntermed = mDNSfalse;
1274 q->SuppressUnusable = mDNSfalse;
1275 q->AppendSearchDomains = 0;
1276 q->TimeoutQuestion = 0;
1277 q->WakeOnResolve = 0;
1278 q->UseBackgroundTraffic = mDNSfalse;
1279 q->ProxyQuestion = 0;
1280 q->pid = mDNSPlatformGetPID();
1281 q->euid = 0;
1282 q->BlockedByPolicy = mDNSfalse;
1283 q->ServiceID = -1;
1284 q->QuestionCallback = callback;
1285 q->QuestionContext = context;
1286 }
1287
RDataHashValue(const ResourceRecord * const rr)1288 mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
1289 {
1290 int len = rr->rdlength;
1291 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1292 const mDNSu8 *ptr = rdb->data;
1293 mDNSu32 sum = 0;
1294
1295 switch(rr->rrtype)
1296 {
1297 case kDNSType_NS:
1298 case kDNSType_MD:
1299 case kDNSType_MF:
1300 case kDNSType_CNAME:
1301 case kDNSType_MB:
1302 case kDNSType_MG:
1303 case kDNSType_MR:
1304 case kDNSType_PTR:
1305 case kDNSType_NSAP_PTR:
1306 case kDNSType_DNAME: return DomainNameHashValue(&rdb->name);
1307
1308 case kDNSType_SOA: return rdb->soa.serial +
1309 rdb->soa.refresh +
1310 rdb->soa.retry +
1311 rdb->soa.expire +
1312 rdb->soa.min +
1313 DomainNameHashValue(&rdb->soa.mname) +
1314 DomainNameHashValue(&rdb->soa.rname);
1315
1316 case kDNSType_MX:
1317 case kDNSType_AFSDB:
1318 case kDNSType_RT:
1319 case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange);
1320
1321 case kDNSType_MINFO:
1322 case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt);
1323
1324 case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400);
1325
1326 case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target);
1327
1328 case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1329
1330 case kDNSType_NSEC: {
1331 int dlen;
1332 dlen = DomainNameLength((domainname *)rdb->data);
1333 sum = DomainNameHashValue((domainname *)rdb->data);
1334 ptr += dlen;
1335 len -= dlen;
1336 /* FALLTHROUGH */
1337 }
1338 /* FALLTHROUGH */
1339
1340 default:
1341 {
1342 int i;
1343 for (i=0; i+1 < len; i+=2)
1344 {
1345 sum += (((mDNSu32)(ptr[i])) << 8) | ptr[i+1];
1346 sum = (sum<<3) | (sum>>29);
1347 }
1348 if (i < len)
1349 {
1350 sum += ((mDNSu32)(ptr[i])) << 8;
1351 }
1352 return(sum);
1353 }
1354 }
1355 }
1356
1357 // r1 has to be a full ResourceRecord including rrtype and rdlength
1358 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
SameRDataBody(const ResourceRecord * const r1,const RDataBody * const r2,DomainNameComparisonFn * samename)1359 mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename)
1360 {
1361 const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data;
1362 const RDataBody2 *const b2 = (RDataBody2 *)r2;
1363 switch(r1->rrtype)
1364 {
1365 case kDNSType_NS:
1366 case kDNSType_MD:
1367 case kDNSType_MF:
1368 case kDNSType_CNAME:
1369 case kDNSType_MB:
1370 case kDNSType_MG:
1371 case kDNSType_MR:
1372 case kDNSType_PTR:
1373 case kDNSType_NSAP_PTR:
1374 case kDNSType_DNAME: return(SameDomainName(&b1->name, &b2->name));
1375
1376 case kDNSType_SOA: return (mDNSBool)( b1->soa.serial == b2->soa.serial &&
1377 b1->soa.refresh == b2->soa.refresh &&
1378 b1->soa.retry == b2->soa.retry &&
1379 b1->soa.expire == b2->soa.expire &&
1380 b1->soa.min == b2->soa.min &&
1381 samename(&b1->soa.mname, &b2->soa.mname) &&
1382 samename(&b1->soa.rname, &b2->soa.rname));
1383
1384 case kDNSType_MX:
1385 case kDNSType_AFSDB:
1386 case kDNSType_RT:
1387 case kDNSType_KX: return (mDNSBool)( b1->mx.preference == b2->mx.preference &&
1388 samename(&b1->mx.exchange, &b2->mx.exchange));
1389
1390 case kDNSType_MINFO:
1391 case kDNSType_RP: return (mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) &&
1392 samename(&b1->rp.txt, &b2->rp.txt));
1393
1394 case kDNSType_PX: return (mDNSBool)( b1->px.preference == b2->px.preference &&
1395 samename(&b1->px.map822, &b2->px.map822) &&
1396 samename(&b1->px.mapx400, &b2->px.mapx400));
1397
1398 case kDNSType_SRV: return (mDNSBool)( b1->srv.priority == b2->srv.priority &&
1399 b1->srv.weight == b2->srv.weight &&
1400 mDNSSameIPPort(b1->srv.port, b2->srv.port) &&
1401 samename(&b1->srv.target, &b2->srv.target));
1402
1403 case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare
1404 case kDNSType_NSEC: {
1405 // If the "nxt" name changes in case, we want to delete the old
1406 // and store just the new one. If the caller passes in SameDomainCS for "samename",
1407 // we would return "false" when the only change between the two rdata is the case
1408 // change in "nxt".
1409 //
1410 // Note: rdlength of both the RData are same (ensured by the caller) and hence we can
1411 // use just r1->rdlength below
1412
1413 int dlen1 = DomainNameLength((domainname *)b1->data);
1414 int dlen2 = DomainNameLength((domainname *)b2->data);
1415 return (mDNSBool)(dlen1 == dlen2 &&
1416 samename((domainname *)b1->data, (domainname *)b2->data) &&
1417 mDNSPlatformMemSame(b1->data + dlen1, b2->data + dlen2, r1->rdlength - dlen1));
1418 }
1419
1420 default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength));
1421 }
1422 }
1423
BitmapTypeCheck(mDNSu8 * bmap,int bitmaplen,mDNSu16 type)1424 mDNSexport mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type)
1425 {
1426 int win, wlen;
1427 int wintype;
1428
1429 // The window that this type belongs to. NSEC has 256 windows that
1430 // comprises of 256 types.
1431 wintype = type >> 8;
1432
1433 while (bitmaplen > 0)
1434 {
1435 if (bitmaplen < 3)
1436 {
1437 LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d short", bitmaplen);
1438 return mDNSfalse;
1439 }
1440
1441 win = *bmap++;
1442 wlen = *bmap++;
1443 bitmaplen -= 2;
1444 if (bitmaplen < wlen || wlen < 1 || wlen > 32)
1445 {
1446 LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win);
1447 return mDNSfalse;
1448 }
1449 if (win < 0 || win >= 256)
1450 {
1451 LogInfo("BitmapTypeCheck: malformed nsec, wlen %d", wlen);
1452 return mDNSfalse;
1453 }
1454 if (win == wintype)
1455 {
1456 // First byte in the window serves 0 to 7, the next one serves 8 to 15 and so on.
1457 // Calculate the right byte offset first.
1458 int boff = (type & 0xff ) >> 3;
1459 if (wlen <= boff)
1460 return mDNSfalse;
1461 // The last three bits values 0 to 7 corresponds to bit positions
1462 // within the byte.
1463 return (bmap[boff] & (0x80 >> (type & 7)));
1464 }
1465 else
1466 {
1467 // If the windows are ordered, then we could check to see
1468 // if wintype > win and then return early.
1469 bmap += wlen;
1470 bitmaplen -= wlen;
1471 }
1472 }
1473 return mDNSfalse;
1474 }
1475
1476 // Don't call this function if the resource record is not NSEC. It will return false
1477 // which means that the type does not exist.
RRAssertsExistence(const ResourceRecord * const rr,mDNSu16 type)1478 mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type)
1479 {
1480 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1481 mDNSu8 *nsec = (mDNSu8 *)rdb->data;
1482 int len, bitmaplen;
1483 mDNSu8 *bmap;
1484
1485 if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
1486
1487 len = DomainNameLength((domainname *)nsec);
1488
1489 bitmaplen = rr->rdlength - len;
1490 bmap = nsec + len;
1491 return (BitmapTypeCheck(bmap, bitmaplen, type));
1492 }
1493
1494 // Don't call this function if the resource record is not NSEC. It will return false
1495 // which means that the type exists.
RRAssertsNonexistence(const ResourceRecord * const rr,mDNSu16 type)1496 mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type)
1497 {
1498 if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
1499
1500 return !RRAssertsExistence(rr, type);
1501 }
1502
1503 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1504 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1505 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1506 // because it has to check all the way to the end of the names to be sure.
1507 // In cases where we know in advance that the names match it's especially advantageous to skip the
1508 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1509
SameNameRecordAnswersQuestion(const ResourceRecord * const rr,mDNSBool isAuthRecord,const DNSQuestion * const q)1510 mDNSlocal mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, mDNSBool isAuthRecord, const DNSQuestion *const q)
1511 {
1512 mDNSBool checkType = mDNStrue;
1513
1514 // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
1515 // are handled in LocalOnlyRecordAnswersQuestion
1516 if (LocalOnlyOrP2PInterface(rr->InterfaceID))
1517 {
1518 LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
1519 return mDNSfalse;
1520 }
1521 if (q->Suppressed)
1522 return mDNSfalse;
1523
1524 if (rr->InterfaceID &&
1525 q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1526 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1527
1528 // Resource record received via unicast, the resolver group ID should match ?
1529 if (!isAuthRecord && !rr->InterfaceID)
1530 {
1531 if (mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1532 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1533 if (rr->dnsservice != q->dnsservice) return(mDNSfalse);
1534 #else
1535 const mDNSu32 idr = rr->rDNSServer ? rr->rDNSServer->resGroupID : 0;
1536 const mDNSu32 idq = q->qDNSServer ? q->qDNSServer->resGroupID : 0;
1537 if (idr != idq) return(mDNSfalse);
1538 #endif
1539 }
1540
1541 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1542 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1543
1544 // CNAME answers question of any type and a negative cache record should not prevent us from querying other
1545 // valid types at the same name.
1546 if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype)
1547 return mDNSfalse;
1548
1549 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
1550 if (enables_dnssec_validation(q) && record_type_answers_dnssec_question(rr, q->qtype)) checkType = mDNSfalse;
1551 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
1552
1553 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1554 if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1555 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1556
1557 #if APPLE_OSX_mDNSResponder
1558 if (!mDNSPlatformValidRecordForQuestion(rr, q))
1559 return mDNSfalse;
1560 #endif // APPLE_OSX_mDNSResponder
1561
1562 return(mDNStrue);
1563 }
1564
SameNameCacheRecordAnswersQuestion(const CacheRecord * const cr,const DNSQuestion * const q)1565 mDNSexport mDNSBool SameNameCacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q)
1566 {
1567 return SameNameRecordAnswersQuestion(&cr->resrec, mDNSfalse, q);
1568 }
1569
RecordAnswersQuestion(const ResourceRecord * const rr,mDNSBool isAuthRecord,const DNSQuestion * const q)1570 mDNSlocal mDNSBool RecordAnswersQuestion(const ResourceRecord *const rr, mDNSBool isAuthRecord, const DNSQuestion *const q)
1571 {
1572 if (!SameNameRecordAnswersQuestion(rr, isAuthRecord, q))
1573 return mDNSfalse;
1574
1575 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1576 }
1577
ResourceRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q)1578 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1579 {
1580 return RecordAnswersQuestion(rr, mDNSfalse, q);
1581 }
1582
AuthRecordAnswersQuestion(const AuthRecord * const ar,const DNSQuestion * const q)1583 mDNSexport mDNSBool AuthRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q)
1584 {
1585 return RecordAnswersQuestion(&ar->resrec, mDNStrue, q);
1586 }
1587
CacheRecordAnswersQuestion(const CacheRecord * const cr,const DNSQuestion * const q)1588 mDNSexport mDNSBool CacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q)
1589 {
1590 return RecordAnswersQuestion(&cr->resrec, mDNSfalse, q);
1591 }
1592
1593 // We have a separate function to handle LocalOnly AuthRecords because they can be created with
1594 // a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
1595 // multicast resource records (which has a valid InterfaceID) which can't be used to answer
1596 // unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
1597 // a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
1598 // LocalOnly records are truly identified by ARType in the AuthRecord. As P2P and LocalOnly record
1599 // are kept in the same hash table, we use the same function to make it easy for the callers when
1600 // they walk the hash table to answer LocalOnly/P2P questions
1601 //
LocalOnlyRecordAnswersQuestion(AuthRecord * const ar,const DNSQuestion * const q)1602 mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
1603 {
1604 ResourceRecord *rr = &ar->resrec;
1605
1606 // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
1607 // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
1608 if (RRAny(ar))
1609 {
1610 LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
1611 return mDNSfalse;
1612 }
1613
1614 // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
1615 // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
1616 // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
1617 // the InterfaceID in the resource record.
1618
1619 if (rr->InterfaceID &&
1620 q->InterfaceID != mDNSInterface_LocalOnly &&
1621 ((q->InterfaceID && rr->InterfaceID != q->InterfaceID) ||
1622 (!q->InterfaceID && !LocalOnlyOrP2PInterface(rr->InterfaceID)))) return(mDNSfalse);
1623
1624 // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
1625 // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
1626 // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped).
1627 //
1628 // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
1629 //
1630 // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because
1631 // traditionally applications never specify scope e.g., getaddrinfo, but need to be able
1632 // to get to /etc/hosts entries.
1633 //
1634 // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
1635 // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
1636 // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
1637 // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
1638 //
1639 // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
1640 // answered with any resource record where as if it has a valid InterfaceID, the scope should match.
1641 //
1642 // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
1643 // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
1644 // against the question.
1645 //
1646 // For P2P, InterfaceIDs of the question and the record should match.
1647
1648 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
1649 // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
1650 // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then
1651 // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
1652 // with names that don't end in local and have mDNSInterface_LocalOnly set.
1653 //
1654 // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
1655 // a question to match its names, it also has to end in .local and that question can't be a unicast question (See
1656 // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
1657 // and also makes it future proof.
1658
1659 if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1660
1661 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1662 if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1663 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1664
1665 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1666 }
1667
AnyTypeRecordAnswersQuestion(const AuthRecord * const ar,const DNSQuestion * const q)1668 mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q)
1669 {
1670 const ResourceRecord *const rr = &ar->resrec;
1671 // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
1672 // are handled in LocalOnlyRecordAnswersQuestion
1673 if (LocalOnlyOrP2PInterface(rr->InterfaceID))
1674 {
1675 LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
1676 return mDNSfalse;
1677 }
1678 if (rr->InterfaceID &&
1679 q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1680 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1681
1682 // Resource record received via unicast, the resolver group ID should match ?
1683 // Note that Auth Records are normally setup with NULL InterfaceID and
1684 // both the DNSServers are assumed to be NULL in that case
1685 if (!rr->InterfaceID)
1686 {
1687 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1688 if (rr->dnsservice != q->dnsservice) return(mDNSfalse);
1689 #else
1690 const mDNSu32 idr = rr->rDNSServer ? rr->rDNSServer->resGroupID : 0;
1691 const mDNSu32 idq = q->qDNSServer ? q->qDNSServer->resGroupID : 0;
1692 if (idr != idq) return(mDNSfalse);
1693 #endif
1694 #if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
1695 if (!mDNSPlatformValidRecordForInterface(ar, q->InterfaceID)) return(mDNSfalse);
1696 #endif
1697 }
1698
1699 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1700 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1701
1702 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1703
1704 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1705 }
1706
1707 // This is called with both unicast resource record and multicast resource record. The question that
1708 // received the unicast response could be the regular unicast response from a DNS server or a response
1709 // to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the
1710 // question and the resource record because the resource record is not completely initialized in
1711 // mDNSCoreReceiveResponse when this function is called.
ResourceRecordAnswersUnicastResponse(const ResourceRecord * const rr,const DNSQuestion * const q)1712 mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q)
1713 {
1714 mDNSBool checkType = mDNStrue;
1715
1716 if (q->Suppressed)
1717 return mDNSfalse;
1718
1719 // For resource records created using multicast, the InterfaceIDs have to match
1720 if (rr->InterfaceID &&
1721 q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1722
1723 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
1724 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1725
1726 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
1727 if (enables_dnssec_validation(q) && record_type_answers_dnssec_question(rr, q->qtype)) checkType = mDNSfalse;
1728 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
1729
1730 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1731 if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1732
1733 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1734
1735 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1736 }
1737
GetRDLength(const ResourceRecord * const rr,mDNSBool estimate)1738 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1739 {
1740 const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data;
1741 const domainname *const name = estimate ? rr->name : mDNSNULL;
1742 if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1743 else switch (rr->rrtype)
1744 {
1745 case kDNSType_A: return(sizeof(rd->ipv4));
1746
1747 case kDNSType_NS:
1748 case kDNSType_CNAME:
1749 case kDNSType_PTR:
1750 case kDNSType_DNAME: return(CompressedDomainNameLength(&rd->name, name));
1751
1752 case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
1753 CompressedDomainNameLength(&rd->soa.rname, name) +
1754 5 * sizeof(mDNSOpaque32));
1755
1756 case kDNSType_NULL:
1757 case kDNSType_TSIG:
1758 case kDNSType_TXT:
1759 case kDNSType_X25:
1760 case kDNSType_ISDN:
1761 case kDNSType_LOC:
1762 case kDNSType_DHCID: return(rr->rdlength); // Not self-describing, so have to just trust rdlength
1763
1764 case kDNSType_HINFO: return (mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
1765
1766 case kDNSType_MX:
1767 case kDNSType_AFSDB:
1768 case kDNSType_RT:
1769 case kDNSType_KX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
1770
1771 case kDNSType_MINFO:
1772 case kDNSType_RP: return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
1773 CompressedDomainNameLength(&rd->rp.txt, name));
1774
1775 case kDNSType_PX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
1776 CompressedDomainNameLength(&rd->px.mapx400, name));
1777
1778 case kDNSType_AAAA: return(sizeof(rd->ipv6));
1779
1780 case kDNSType_SRV: return (mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
1781
1782 case kDNSType_OPT: return(rr->rdlength);
1783
1784 case kDNSType_NSEC: {
1785 domainname *next = (domainname *)rd->data;
1786 int dlen = DomainNameLength(next);
1787 //
1788 if (UNICAST_NSEC(rr))
1789 return (mDNSu16)(CompressedDomainNameLength(next, name) + rr->rdlength - dlen);
1790 else
1791 return (mDNSu16)((estimate ? 2 : dlen) + rr->rdlength - dlen);
1792 }
1793
1794 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
1795 return(rr->rdlength);
1796 }
1797 }
1798
1799 // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
1800 // to help reduce the risk of bogus malformed data on the network
ValidateRData(const mDNSu16 rrtype,const mDNSu16 rdlength,const RData * const rd)1801 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1802 {
1803 mDNSu16 len;
1804
1805 switch(rrtype)
1806 {
1807 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1808
1809 case kDNSType_NS: // Same as PTR
1810 case kDNSType_MD: // Same as PTR
1811 case kDNSType_MF: // Same as PTR
1812 case kDNSType_CNAME: // Same as PTR
1813 //case kDNSType_SOA not checked
1814 case kDNSType_MB: // Same as PTR
1815 case kDNSType_MG: // Same as PTR
1816 case kDNSType_MR: // Same as PTR
1817 //case kDNSType_NULL not checked (no specified format, so always valid)
1818 //case kDNSType_WKS not checked
1819 case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
1820 return(len <= MAX_DOMAIN_NAME && rdlength == len);
1821
1822 case kDNSType_HINFO: // Same as TXT (roughly)
1823 case kDNSType_MINFO: // Same as TXT (roughly)
1824 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
1825 {
1826 const mDNSu8 *ptr = rd->u.txt.c;
1827 const mDNSu8 *end = rd->u.txt.c + rdlength;
1828 while (ptr < end) ptr += 1 + ptr[0];
1829 return (ptr == end);
1830 }
1831
1832 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1833
1834 case kDNSType_MX: // Must be at least two-byte preference, plus domainname
1835 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1836 len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
1837 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1838
1839 case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname
1840 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1841 len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
1842 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1843
1844 //case kDNSType_NSEC not checked
1845
1846 default: return(mDNStrue); // Allow all other types without checking
1847 }
1848 }
1849
1850 // ***************************************************************************
1851 #if COMPILER_LIKES_PRAGMA_MARK
1852 #pragma mark -
1853 #pragma mark - DNS Message Creation Functions
1854 #endif
1855
InitializeDNSMessage(DNSMessageHeader * h,mDNSOpaque16 id,mDNSOpaque16 flags)1856 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1857 {
1858 h->id = id;
1859 h->flags = flags;
1860 h->numQuestions = 0;
1861 h->numAnswers = 0;
1862 h->numAuthorities = 0;
1863 h->numAdditionals = 0;
1864 }
1865
1866 #endif // !STANDALONE
1867
FindCompressionPointer(const mDNSu8 * const base,const mDNSu8 * const end,const mDNSu8 * const domname)1868 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1869 {
1870 const mDNSu8 *result = end - *domname - 1;
1871
1872 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1873
1874 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1875 while (result >= base)
1876 {
1877 // If the length byte and first character of the label match, then check further to see
1878 // if this location in the packet will yield a useful name compression pointer.
1879 if (result[0] == domname[0] && result[1] == domname[1])
1880 {
1881 const mDNSu8 *name = domname;
1882 const mDNSu8 *targ = result;
1883 while (targ + *name < end)
1884 {
1885 // First see if this label matches
1886 int i;
1887 const mDNSu8 *pointertarget;
1888 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
1889 if (i <= *name) break; // If label did not match, bail out
1890 targ += 1 + *name; // Else, did match, so advance target pointer
1891 name += 1 + *name; // and proceed to check next label
1892 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
1893 if (*name == 0) break; // If no more labels to match, we failed, so bail out
1894
1895 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1896 if (targ[0] < 0x40) continue; // If length value, continue to check next label
1897 if (targ[0] < 0xC0) break; // If 40-BF, not valid
1898 if (targ+1 >= end) break; // Second byte not present!
1899 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
1900 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
1901 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1902 targ = pointertarget;
1903 }
1904 }
1905 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1906 }
1907 return(mDNSNULL);
1908 }
1909
1910 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1911 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1912 // end points to the end of the message so far
1913 // ptr points to where we want to put the name
1914 // limit points to one byte past the end of the buffer that we must not overrun
1915 // domainname is the name to put
putDomainNameAsLabels(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name)1916 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1917 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1918 {
1919 const mDNSu8 *const base = (const mDNSu8 *)msg;
1920 const mDNSu8 * np = name->c;
1921 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1922 const mDNSu8 * pointer = mDNSNULL;
1923 const mDNSu8 *const searchlimit = ptr;
1924
1925 if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); }
1926
1927 if (!*np) // If just writing one-byte root label, make sure we have space for that
1928 {
1929 if (ptr >= limit) return(mDNSNULL);
1930 }
1931 else // else, loop through writing labels and/or a compression offset
1932 {
1933 do {
1934 if (*np > MAX_DOMAIN_LABEL)
1935 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1936
1937 // This check correctly allows for the final trailing root label:
1938 // e.g.
1939 // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
1940 // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
1941 // We know that max will be at name->c[256]
1942 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1943 // six bytes, then exit the loop, write the final terminating root label, and the domain
1944 // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
1945 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1946 if (np + 1 + *np >= max)
1947 { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); }
1948
1949 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1950 if (pointer) // Use a compression pointer if we can
1951 {
1952 const mDNSu16 offset = (mDNSu16)(pointer - base);
1953 if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up
1954 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1955 *ptr++ = (mDNSu8)( offset & 0xFF);
1956 return(ptr);
1957 }
1958 else // Else copy one label and try again
1959 {
1960 int i;
1961 mDNSu8 len = *np++;
1962 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
1963 if (ptr + 1 + len >= limit) return(mDNSNULL);
1964 *ptr++ = len;
1965 for (i=0; i<len; i++) *ptr++ = *np++;
1966 }
1967 } while (*np); // While we've got characters remaining in the name, continue
1968 }
1969
1970 *ptr++ = 0; // Put the final root label
1971 return(ptr);
1972 }
1973
1974 #ifndef STANDALONE
1975
putVal16(mDNSu8 * ptr,mDNSu16 val)1976 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1977 {
1978 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1979 ptr[1] = (mDNSu8)((val ) & 0xFF);
1980 return ptr + sizeof(mDNSOpaque16);
1981 }
1982
putVal32(mDNSu8 * ptr,mDNSu32 val)1983 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
1984 {
1985 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
1986 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
1987 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
1988 ptr[3] = (mDNSu8)((val ) & 0xFF);
1989 return ptr + sizeof(mDNSu32);
1990 }
1991
1992 // Copy the RDATA information. The actual in memory storage for the data might be bigger than what the rdlength
1993 // says. Hence, the only way to copy out the data from a resource record is to use putRData.
1994 // msg points to the message we're building (pass mDNSNULL for "msg" if we don't want to use compression pointers)
putRData(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const ResourceRecord * const rr)1995 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
1996 {
1997 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1998 switch (rr->rrtype)
1999 {
2000 case kDNSType_A: if (rr->rdlength != 4)
2001 { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); }
2002 if (ptr + 4 > limit) return(mDNSNULL);
2003 *ptr++ = rdb->ipv4.b[0];
2004 *ptr++ = rdb->ipv4.b[1];
2005 *ptr++ = rdb->ipv4.b[2];
2006 *ptr++ = rdb->ipv4.b[3];
2007 return(ptr);
2008
2009 case kDNSType_NS:
2010 case kDNSType_CNAME:
2011 case kDNSType_PTR:
2012 case kDNSType_DNAME: return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name));
2013
2014 case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname);
2015 if (!ptr) return(mDNSNULL);
2016 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname);
2017 if (!ptr || ptr + 20 > limit) return(mDNSNULL);
2018 ptr = putVal32(ptr, rdb->soa.serial);
2019 ptr = putVal32(ptr, rdb->soa.refresh);
2020 ptr = putVal32(ptr, rdb->soa.retry);
2021 ptr = putVal32(ptr, rdb->soa.expire);
2022 ptr = putVal32(ptr, rdb->soa.min);
2023 return(ptr);
2024
2025 case kDNSType_NULL:
2026 case kDNSType_HINFO:
2027 case kDNSType_TSIG:
2028 case kDNSType_TXT:
2029 case kDNSType_X25:
2030 case kDNSType_ISDN:
2031 case kDNSType_LOC:
2032 case kDNSType_DHCID: if (ptr + rr->rdlength > limit) return(mDNSNULL);
2033 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2034 return(ptr + rr->rdlength);
2035
2036 case kDNSType_MX:
2037 case kDNSType_AFSDB:
2038 case kDNSType_RT:
2039 case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL);
2040 ptr = putVal16(ptr, rdb->mx.preference);
2041 return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange));
2042
2043 case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox);
2044 if (!ptr) return(mDNSNULL);
2045 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt);
2046 return(ptr);
2047
2048 case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL);
2049 ptr = putVal16(ptr, rdb->px.preference);
2050 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822);
2051 if (!ptr) return(mDNSNULL);
2052 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400);
2053 return(ptr);
2054
2055 case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6))
2056 { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); }
2057 if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL);
2058 mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6));
2059 return(ptr + sizeof(rdb->ipv6));
2060
2061 case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL);
2062 *ptr++ = (mDNSu8)(rdb->srv.priority >> 8);
2063 *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF);
2064 *ptr++ = (mDNSu8)(rdb->srv.weight >> 8);
2065 *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF);
2066 *ptr++ = rdb->srv.port.b[0];
2067 *ptr++ = rdb->srv.port.b[1];
2068 return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target));
2069
2070 case kDNSType_OPT: {
2071 int len = 0;
2072 const rdataOPT *opt;
2073 const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
2074 for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
2075 len += DNSOpt_Data_Space(opt);
2076 if (ptr + len > limit)
2077 {
2078 LogMsg("ERROR: putOptRData - out of space");
2079 return mDNSNULL;
2080 }
2081 for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
2082 {
2083 const int space = DNSOpt_Data_Space(opt);
2084 ptr = putVal16(ptr, opt->opt);
2085 ptr = putVal16(ptr, (mDNSu16)space - 4);
2086 switch (opt->opt)
2087 {
2088 case kDNSOpt_LLQ:
2089 ptr = putVal16(ptr, opt->u.llq.vers);
2090 ptr = putVal16(ptr, opt->u.llq.llqOp);
2091 ptr = putVal16(ptr, opt->u.llq.err);
2092 mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id
2093 ptr += 8;
2094 ptr = putVal32(ptr, opt->u.llq.llqlease);
2095 break;
2096 case kDNSOpt_Lease:
2097 ptr = putVal32(ptr, opt->u.updatelease);
2098 break;
2099 case kDNSOpt_Owner:
2100 *ptr++ = opt->u.owner.vers;
2101 *ptr++ = opt->u.owner.seq;
2102 mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier
2103 ptr += 6;
2104 if (space >= DNSOpt_OwnerData_ID_Wake_Space)
2105 {
2106 mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC
2107 ptr += 6;
2108 if (space > DNSOpt_OwnerData_ID_Wake_Space)
2109 {
2110 mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space);
2111 ptr += space - DNSOpt_OwnerData_ID_Wake_Space;
2112 }
2113 }
2114 break;
2115 case kDNSOpt_Trace:
2116 *ptr++ = opt->u.tracer.platf;
2117 ptr = putVal32(ptr, opt->u.tracer.mDNSv);
2118 break;
2119 }
2120 }
2121 return ptr;
2122 }
2123
2124 case kDNSType_NSEC: {
2125 // For NSEC records, rdlength represents the exact number of bytes
2126 // of in memory storage.
2127 mDNSu8 *nsec = (mDNSu8 *)rdb->data;
2128 domainname *name = (domainname *)nsec;
2129 const int dlen = DomainNameLength(name);
2130 nsec += dlen;
2131 // This function is called when we are sending a NSEC record as part of mDNS,
2132 // or to copy the data to any other buffer needed which could be a mDNS or uDNS
2133 // NSEC record. The only time compression is used that when we are sending it
2134 // in mDNS (indicated by non-NULL "msg") and hence we handle mDNS case
2135 // separately.
2136 if (!UNICAST_NSEC(rr))
2137 {
2138 mDNSu8 *save = ptr;
2139 int i, j, wlen;
2140 wlen = *(nsec + 1);
2141 nsec += 2; // Skip the window number and len
2142
2143 // For our simplified use of NSEC synthetic records:
2144 //
2145 // nextname is always the record's own name,
2146 // the block number is always 0,
2147 // the count byte is a value in the range 1-32,
2148 // followed by the 1-32 data bytes
2149 //
2150 // Note: When we send the NSEC record in mDNS, the window size is set to 32.
2151 // We need to find out what the last non-NULL byte is. If we are copying out
2152 // from an RDATA, we have the right length. As we need to handle both the case,
2153 // we loop to find the right value instead of blindly using len to copy.
2154
2155 for (i=wlen; i>0; i--) if (nsec[i-1]) break;
2156
2157 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2158 if (!ptr) { LogInfo("putRData: Can't put name, Length %d, record %##s", limit - save, rr->name->c); return(mDNSNULL); }
2159 if (i) // Only put a block if at least one type exists for this name
2160 {
2161 if (ptr + 2 + i > limit) { LogInfo("putRData: Can't put window, Length %d, i %d, record %##s", limit - ptr, i, rr->name->c); return(mDNSNULL); }
2162 *ptr++ = 0;
2163 *ptr++ = (mDNSu8)i;
2164 for (j=0; j<i; j++) *ptr++ = nsec[j];
2165 }
2166 return ptr;
2167 }
2168 else
2169 {
2170 int win, wlen;
2171 int len = rr->rdlength - dlen;
2172
2173 // Sanity check whether the bitmap is good
2174 while (len)
2175 {
2176 if (len < 3)
2177 { LogMsg("putRData: invalid length %d", len); return mDNSNULL; }
2178
2179 win = *nsec++;
2180 wlen = *nsec++;
2181 len -= 2;
2182 if (len < wlen || wlen < 1 || wlen > 32)
2183 { LogMsg("putRData: invalid window length %d", wlen); return mDNSNULL; }
2184 if (win < 0 || win >= 256)
2185 { LogMsg("putRData: invalid window %d", win); return mDNSNULL; }
2186
2187 nsec += wlen;
2188 len -= wlen;
2189 }
2190 if (ptr + rr->rdlength > limit) { LogMsg("putRData: NSEC rdlength beyond limit %##s (%s), ptr %p, rdlength %d, limit %p", rr->name->c, DNSTypeName(rr->rrtype), ptr, rr->rdlength, limit); return(mDNSNULL);}
2191
2192 // No compression allowed for "nxt", just copy the data.
2193 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2194 return(ptr + rr->rdlength);
2195 }
2196 }
2197
2198 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
2199 if (ptr + rr->rdlength > limit) return(mDNSNULL);
2200 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2201 return(ptr + rr->rdlength);
2202 }
2203 }
2204
2205 #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2206
PutResourceRecordTTLWithLimit(DNSMessage * const msg,mDNSu8 * ptr,mDNSu16 * count,const ResourceRecord * rr,mDNSu32 ttl,const mDNSu8 * limit)2207 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count,
2208 const ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
2209 {
2210 mDNSu8 *endofrdata;
2211 mDNSu16 actualLength;
2212 // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2213 const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg;
2214
2215 if (rr->RecordType == kDNSRecordTypeUnregistered)
2216 {
2217 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
2218 "Attempt to put kDNSRecordTypeUnregistered " PRI_DM_NAME " (" PUB_S ")",
2219 DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype));
2220 return(ptr);
2221 }
2222
2223 if (!ptr)
2224 {
2225 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
2226 "Pointer to message is NULL while filling resource record " PRI_DM_NAME " (" PUB_S ")",
2227 DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype));
2228 return(mDNSNULL);
2229 }
2230
2231 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2232 // If we're out-of-space, return mDNSNULL
2233 if (!ptr || ptr + 10 >= limit)
2234 {
2235 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2236 "Can't put more names into current message, will possibly put it into the next message - "
2237 "name: " PRI_DM_NAME " (" PUB_S "), remaining space: %ld",
2238 DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype), (long)(limit - ptr));
2239 return(mDNSNULL);
2240 }
2241 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
2242 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
2243 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
2244 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
2245 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
2246 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
2247 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
2248 ptr[7] = (mDNSu8)( ttl & 0xFF);
2249 // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
2250
2251 endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
2252 if (!endofrdata)
2253 {
2254 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2255 "Can't put more rdata into current message, will possibly put it into the next message - "
2256 "name: " PRI_DM_NAME " (" PUB_S "), remaining space: %ld",
2257 DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype), (long)(limit - ptr - 10));
2258 return(mDNSNULL);
2259 }
2260
2261 // Go back and fill in the actual number of data bytes we wrote
2262 // (actualLength can be less than rdlength when domain name compression is used)
2263 actualLength = (mDNSu16)(endofrdata - ptr - 10);
2264 ptr[8] = (mDNSu8)(actualLength >> 8);
2265 ptr[9] = (mDNSu8)(actualLength & 0xFF);
2266
2267 if (count)
2268 {
2269 (*count)++;
2270 }
2271 else
2272 {
2273 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
2274 "No target count to update for " PRI_DM_NAME " (" PUB_S ")",
2275 DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype));
2276 }
2277 return(endofrdata);
2278 }
2279
putEmptyResourceRecord(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,mDNSu16 * count,const AuthRecord * rr)2280 mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr)
2281 {
2282 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
2283 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2284 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
2285 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
2286 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
2287 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
2288 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
2289 ptr[8] = ptr[9] = 0; // RDATA length is zero
2290 (*count)++;
2291 return(ptr + 10);
2292 }
2293
putQuestion(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name,mDNSu16 rrtype,mDNSu16 rrclass)2294 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
2295 {
2296 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2297 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2298 ptr[0] = (mDNSu8)(rrtype >> 8);
2299 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2300 ptr[2] = (mDNSu8)(rrclass >> 8);
2301 ptr[3] = (mDNSu8)(rrclass & 0xFF);
2302 msg->h.numQuestions++;
2303 return(ptr+4);
2304 }
2305
2306 // for dynamic updates
putZone(DNSMessage * const msg,mDNSu8 * ptr,mDNSu8 * limit,const domainname * zone,mDNSOpaque16 zoneClass)2307 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
2308 {
2309 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
2310 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
2311 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
2312 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
2313 *ptr++ = zoneClass.b[0];
2314 *ptr++ = zoneClass.b[1];
2315 msg->h.mDNS_numZones++;
2316 return ptr;
2317 }
2318
2319 // for dynamic updates
putPrereqNameNotInUse(const domainname * const name,DNSMessage * const msg,mDNSu8 * const ptr,mDNSu8 * const end)2320 mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
2321 {
2322 AuthRecord prereq;
2323 mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
2324 AssignDomainName(&prereq.namestorage, name);
2325 prereq.resrec.rrtype = kDNSQType_ANY;
2326 prereq.resrec.rrclass = kDNSClass_NONE;
2327 return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
2328 }
2329
2330 // for dynamic updates
putDeletionRecord(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr)2331 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
2332 {
2333 // deletion: specify record w/ TTL 0, class NONE
2334 const mDNSu16 origclass = rr->rrclass;
2335 rr->rrclass = kDNSClass_NONE;
2336 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
2337 rr->rrclass = origclass;
2338 return ptr;
2339 }
2340
2341 // for dynamic updates
putDeletionRecordWithLimit(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr,mDNSu8 * limit)2342 mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit)
2343 {
2344 // deletion: specify record w/ TTL 0, class NONE
2345 const mDNSu16 origclass = rr->rrclass;
2346 rr->rrclass = kDNSClass_NONE;
2347 ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit);
2348 rr->rrclass = origclass;
2349 return ptr;
2350 }
2351
putDeleteRRSetWithLimit(DNSMessage * msg,mDNSu8 * ptr,const domainname * name,mDNSu16 rrtype,mDNSu8 * limit)2352 mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit)
2353 {
2354 mDNSu16 class = kDNSQClass_ANY;
2355
2356 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2357 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2358 ptr[0] = (mDNSu8)(rrtype >> 8);
2359 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2360 ptr[2] = (mDNSu8)(class >> 8);
2361 ptr[3] = (mDNSu8)(class & 0xFF);
2362 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2363 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2364
2365 msg->h.mDNS_numUpdates++;
2366 return ptr + 10;
2367 }
2368
2369 // for dynamic updates
putDeleteAllRRSets(DNSMessage * msg,mDNSu8 * ptr,const domainname * name)2370 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
2371 {
2372 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
2373 mDNSu16 class = kDNSQClass_ANY;
2374 mDNSu16 rrtype = kDNSQType_ANY;
2375
2376 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2377 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2378 ptr[0] = (mDNSu8)(rrtype >> 8);
2379 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2380 ptr[2] = (mDNSu8)(class >> 8);
2381 ptr[3] = (mDNSu8)(class & 0xFF);
2382 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2383 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2384
2385 msg->h.mDNS_numUpdates++;
2386 return ptr + 10;
2387 }
2388
2389 // for dynamic updates
putUpdateLease(DNSMessage * msg,mDNSu8 * ptr,mDNSu32 lease)2390 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease)
2391 {
2392 AuthRecord rr;
2393 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2394 rr.resrec.rrclass = NormalMaxDNSMessageData;
2395 rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2396 rr.resrec.rdestimate = sizeof(rdataOPT);
2397 rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2398 rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2399 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, &rr.resrec, 0);
2400 if (!ptr) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
2401 return ptr;
2402 }
2403
2404 // for dynamic updates
putUpdateLeaseWithLimit(DNSMessage * msg,mDNSu8 * ptr,mDNSu32 lease,mDNSu8 * limit)2405 mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit)
2406 {
2407 AuthRecord rr;
2408 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2409 rr.resrec.rrclass = NormalMaxDNSMessageData;
2410 rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2411 rr.resrec.rdestimate = sizeof(rdataOPT);
2412 rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2413 rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2414 ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.numAdditionals, &rr.resrec, 0, limit);
2415 if (!ptr) { LogMsg("ERROR: putUpdateLeaseWithLimit - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
2416 return ptr;
2417 }
2418
2419 // ***************************************************************************
2420 #if COMPILER_LIKES_PRAGMA_MARK
2421 #pragma mark -
2422 #pragma mark - DNS Message Parsing Functions
2423 #endif
2424
DomainNameHashValue(const domainname * const name)2425 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
2426 {
2427 mDNSu32 sum = 0;
2428 const mDNSu8 *c;
2429
2430 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
2431 {
2432 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
2433 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
2434 sum = (sum<<3) | (sum>>29);
2435 }
2436 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
2437 return(sum);
2438 }
2439
SetNewRData(ResourceRecord * const rr,RData * NewRData,mDNSu16 rdlength)2440 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
2441 {
2442 domainname *target;
2443 if (NewRData)
2444 {
2445 rr->rdata = NewRData;
2446 rr->rdlength = rdlength;
2447 }
2448 // Must not try to get target pointer until after updating rr->rdata
2449 target = GetRRDomainNameTarget(rr);
2450 rr->rdlength = GetRDLength(rr, mDNSfalse);
2451 rr->rdestimate = GetRDLength(rr, mDNStrue);
2452 rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr);
2453 }
2454
skipDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end)2455 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
2456 {
2457 mDNSu16 total = 0;
2458
2459 if (ptr < (mDNSu8*)msg || ptr >= end)
2460 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2461
2462 while (1) // Read sequence of labels
2463 {
2464 const mDNSu8 len = *ptr++; // Read length of this label
2465 if (len == 0) return(ptr); // If length is zero, that means this name is complete
2466 switch (len & 0xC0)
2467 {
2468 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2469 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2470 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
2471 { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
2472 ptr += len;
2473 total += 1 + len;
2474 break;
2475
2476 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
2477 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
2478 case 0xC0: return(ptr+1);
2479 }
2480 }
2481 }
2482
2483 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
getDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end,domainname * const name)2484 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
2485 domainname *const name)
2486 {
2487 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
2488 mDNSu8 *np = name->c; // Name pointer
2489 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
2490
2491 if (ptr < (mDNSu8*)msg || ptr >= end)
2492 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2493
2494 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2495
2496 while (1) // Read sequence of labels
2497 {
2498 int i;
2499 mDNSu16 offset;
2500 const mDNSu8 len = *ptr++; // Read length of this label
2501 if (len == 0) break; // If length is zero, that means this name is complete
2502 switch (len & 0xC0)
2503 {
2504
2505 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2506 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2507 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
2508 { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
2509 *np++ = len;
2510 for (i=0; i<len; i++) *np++ = *ptr++;
2511 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2512 break;
2513
2514 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
2515 return(mDNSNULL);
2516
2517 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
2518
2519 case 0xC0: if (ptr >= end)
2520 { debugf("getDomainName: Malformed compression label (overruns packet end)"); return(mDNSNULL); }
2521 offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
2522 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
2523 ptr = (mDNSu8 *)msg + offset;
2524 if (ptr < (mDNSu8*)msg || ptr >= end)
2525 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
2526 if (*ptr & 0xC0)
2527 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
2528 break;
2529 }
2530 }
2531
2532 if (nextbyte) return(nextbyte);
2533 else return(ptr);
2534 }
2535
skipResourceRecord(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)2536 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2537 {
2538 mDNSu16 pktrdlength;
2539
2540 ptr = skipDomainName(msg, ptr, end);
2541 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
2542
2543 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2544 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
2545 ptr += 10;
2546 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2547
2548 return(ptr + pktrdlength);
2549 }
2550
2551 // Sanity check whether the NSEC/NSEC3 bitmap is good
SanityCheckBitMap(const mDNSu8 * bmap,const mDNSu8 * end,int len)2552 mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len)
2553 {
2554 int win, wlen;
2555
2556 while (bmap < end)
2557 {
2558 if (len < 3)
2559 {
2560 LogInfo("SanityCheckBitMap: invalid length %d", len);
2561 return mDNSNULL;
2562 }
2563
2564 win = *bmap++;
2565 wlen = *bmap++;
2566 len -= 2;
2567 if (len < wlen || wlen < 1 || wlen > 32)
2568 {
2569 LogInfo("SanityCheckBitMap: invalid window length %d", wlen);
2570 return mDNSNULL;
2571 }
2572 if (win < 0 || win >= 256)
2573 {
2574 LogInfo("SanityCheckBitMap: invalid window %d", win);
2575 return mDNSNULL;
2576 }
2577
2578 bmap += wlen;
2579 len -= wlen;
2580 }
2581 return (mDNSu8 *)bmap;
2582 }
2583
AssignDomainNameWithLimit(domainname * const dst,const domainname * src,const mDNSu8 * const end)2584 mDNSlocal mDNSBool AssignDomainNameWithLimit(domainname *const dst, const domainname *src, const mDNSu8 *const end)
2585 {
2586 const mDNSu32 len = DomainNameLengthLimit(src, end);
2587 if ((len >= 1) && (len <= MAX_DOMAIN_NAME))
2588 {
2589 mDNSPlatformMemCopy(dst->c, src->c, len);
2590 return mDNStrue;
2591 }
2592 else
2593 {
2594 dst->c[0] = 0;
2595 return mDNSfalse;
2596 }
2597 }
2598
2599 // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
2600 // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
2601 // (domainnames are expanded to 256 bytes) when stored in memory.
2602 //
2603 // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr.
2604 // The caller can do this only if the names in the resource records are not compressed and validity of the
2605 // resource record has already been done before.
SetRData(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,ResourceRecord * const rr,const mDNSu16 rdlength)2606 mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, ResourceRecord *const rr,
2607 const mDNSu16 rdlength)
2608 {
2609 RDataBody2 *const rdb = (RDataBody2 *)&rr->rdata->u;
2610
2611 switch (rr->rrtype)
2612 {
2613 case kDNSType_A:
2614 if (rdlength != sizeof(mDNSv4Addr))
2615 goto fail;
2616 rdb->ipv4.b[0] = ptr[0];
2617 rdb->ipv4.b[1] = ptr[1];
2618 rdb->ipv4.b[2] = ptr[2];
2619 rdb->ipv4.b[3] = ptr[3];
2620 break;
2621
2622 case kDNSType_NS:
2623 case kDNSType_MD:
2624 case kDNSType_MF:
2625 case kDNSType_CNAME:
2626 case kDNSType_MB:
2627 case kDNSType_MG:
2628 case kDNSType_MR:
2629 case kDNSType_PTR:
2630 case kDNSType_NSAP_PTR:
2631 case kDNSType_DNAME:
2632 if (msg)
2633 {
2634 ptr = getDomainName(msg, ptr, end, &rdb->name);
2635 }
2636 else
2637 {
2638 if (!AssignDomainNameWithLimit(&rdb->name, (domainname *)ptr, end))
2639 {
2640 goto fail;
2641 }
2642 ptr += DomainNameLength(&rdb->name);
2643 }
2644 if (ptr != end)
2645 {
2646 debugf("SetRData: Malformed CNAME/PTR RDATA name");
2647 goto fail;
2648 }
2649 break;
2650
2651 case kDNSType_SOA:
2652 if (msg)
2653 {
2654 ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
2655 }
2656 else
2657 {
2658 if (!AssignDomainNameWithLimit(&rdb->soa.mname, (domainname *)ptr, end))
2659 {
2660 goto fail;
2661 }
2662 ptr += DomainNameLength(&rdb->soa.mname);
2663 }
2664 if (!ptr)
2665 {
2666 debugf("SetRData: Malformed SOA RDATA mname");
2667 goto fail;
2668 }
2669 if (msg)
2670 {
2671 ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
2672 }
2673 else
2674 {
2675 if (!AssignDomainNameWithLimit(&rdb->soa.rname, (domainname *)ptr, end))
2676 {
2677 goto fail;
2678 }
2679 ptr += DomainNameLength(&rdb->soa.rname);
2680 }
2681 if (!ptr)
2682 {
2683 debugf("SetRData: Malformed SOA RDATA rname");
2684 goto fail;
2685 }
2686 if (ptr + 0x14 != end)
2687 {
2688 debugf("SetRData: Malformed SOA RDATA");
2689 goto fail;
2690 }
2691 rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
2692 rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
2693 rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
2694 rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
2695 rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
2696 break;
2697
2698 case kDNSType_HINFO:
2699 // See https://tools.ietf.org/html/rfc1035#section-3.3.2 for HINFO RDATA format.
2700 {
2701 // HINFO should contain RDATA.
2702 if (end <= ptr || rdlength != (mDNSu32)(end - ptr))
2703 {
2704 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2705 "SetRData: Malformed HINFO RDATA - invalid RDATA length: %u", rdlength);
2706 goto fail;
2707 }
2708
2709 const mDNSu8 *currentPtr = ptr;
2710 // CPU character string length should be less than the RDATA length.
2711 mDNSu32 cpuCharacterStrLength = currentPtr[0];
2712 if (1 + cpuCharacterStrLength >= (mDNSu32)(end - currentPtr))
2713 {
2714 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2715 "SetRData: Malformed HINFO RDATA - CPU character string goes out of boundary");
2716 goto fail;
2717 }
2718 currentPtr += 1 + cpuCharacterStrLength;
2719
2720 // OS character string should end at the RDATA ending.
2721 mDNSu32 osCharacterStrLength = currentPtr[0];
2722 if (1 + osCharacterStrLength != (mDNSu32)(end - currentPtr))
2723 {
2724 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2725 "SetRData: Malformed HINFO RDATA - OS character string does not end at the RDATA ending");
2726 goto fail;
2727 }
2728
2729 // Copy the validated RDATA.
2730 rr->rdlength = rdlength;
2731 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
2732 break;
2733 }
2734 case kDNSType_NULL:
2735 case kDNSType_TXT:
2736 case kDNSType_X25:
2737 case kDNSType_ISDN:
2738 case kDNSType_LOC:
2739 case kDNSType_DHCID:
2740 case kDNSType_SVCB:
2741 case kDNSType_HTTPS:
2742 rr->rdlength = rdlength;
2743 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
2744 break;
2745
2746 case kDNSType_MX:
2747 case kDNSType_AFSDB:
2748 case kDNSType_RT:
2749 case kDNSType_KX:
2750 // Preference + domainname
2751 if (rdlength < 3)
2752 goto fail;
2753 rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2754 ptr += 2;
2755 if (msg)
2756 {
2757 ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange);
2758 }
2759 else
2760 {
2761 if (!AssignDomainNameWithLimit(&rdb->mx.exchange, (domainname *)ptr, end))
2762 {
2763 goto fail;
2764 }
2765 ptr += DomainNameLength(&rdb->mx.exchange);
2766 }
2767 if (ptr != end)
2768 {
2769 debugf("SetRData: Malformed MX name");
2770 goto fail;
2771 }
2772 break;
2773
2774 case kDNSType_MINFO:
2775 case kDNSType_RP:
2776 // Domainname + domainname
2777 if (msg)
2778 {
2779 ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox);
2780 }
2781 else
2782 {
2783 if (!AssignDomainNameWithLimit(&rdb->rp.mbox, (domainname *)ptr, end))
2784 {
2785 goto fail;
2786 }
2787 ptr += DomainNameLength(&rdb->rp.mbox);
2788 }
2789 if (!ptr)
2790 {
2791 debugf("SetRData: Malformed RP mbox");
2792 goto fail;
2793 }
2794 if (msg)
2795 {
2796 ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
2797 }
2798 else
2799 {
2800 if (!AssignDomainNameWithLimit(&rdb->rp.txt, (domainname *)ptr, end))
2801 {
2802 goto fail;
2803 }
2804 ptr += DomainNameLength(&rdb->rp.txt);
2805 }
2806 if (ptr != end)
2807 {
2808 debugf("SetRData: Malformed RP txt");
2809 goto fail;
2810 }
2811 break;
2812
2813 case kDNSType_PX:
2814 // Preference + domainname + domainname
2815 if (rdlength < 4)
2816 goto fail;
2817 rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2818 ptr += 2;
2819 if (msg)
2820 {
2821 ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
2822 }
2823 else
2824 {
2825 if (!AssignDomainNameWithLimit(&rdb->px.map822, (domainname *)ptr, end))
2826 {
2827 goto fail;
2828 }
2829 ptr += DomainNameLength(&rdb->px.map822);
2830 }
2831 if (!ptr)
2832 {
2833 debugf("SetRData: Malformed PX map822");
2834 goto fail;
2835 }
2836 if (msg)
2837 {
2838 ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
2839 }
2840 else
2841 {
2842 if (!AssignDomainNameWithLimit(&rdb->px.mapx400, (domainname *)ptr, end))
2843 {
2844 goto fail;
2845 }
2846 ptr += DomainNameLength(&rdb->px.mapx400);
2847 }
2848 if (ptr != end)
2849 {
2850 debugf("SetRData: Malformed PX mapx400");
2851 goto fail;
2852 }
2853 break;
2854
2855 case kDNSType_AAAA:
2856 if (rdlength != sizeof(mDNSv6Addr))
2857 goto fail;
2858 mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
2859 break;
2860
2861 case kDNSType_SRV:
2862 // Priority + weight + port + domainname
2863 if (rdlength < 7)
2864 goto fail;
2865 rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2866 rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2867 rdb->srv.port.b[0] = ptr[4];
2868 rdb->srv.port.b[1] = ptr[5];
2869 ptr += 6;
2870 if (msg)
2871 {
2872 ptr = getDomainName(msg, ptr, end, &rdb->srv.target);
2873 }
2874 else
2875 {
2876 if (!AssignDomainNameWithLimit(&rdb->srv.target, (domainname *)ptr, end))
2877 {
2878 goto fail;
2879 }
2880 ptr += DomainNameLength(&rdb->srv.target);
2881 }
2882 if (ptr != end)
2883 {
2884 debugf("SetRData: Malformed SRV RDATA name");
2885 goto fail;
2886 }
2887 break;
2888
2889 case kDNSType_NAPTR:
2890 {
2891 int savelen, len;
2892 domainname name;
2893 const mDNSu8 *orig = ptr;
2894
2895 // Make sure the data is parseable and within the limits.
2896 //
2897 // Fixed length: Order, preference (4 bytes)
2898 // Variable length: flags, service, regexp, domainname
2899
2900 if (rdlength < 8)
2901 goto fail;
2902 // Order, preference.
2903 ptr += 4;
2904 // Parse flags, Service and Regexp
2905 // length in the first byte does not include the length byte itself
2906 len = *ptr + 1;
2907 ptr += len;
2908 if (ptr >= end)
2909 {
2910 LogInfo("SetRData: Malformed NAPTR flags");
2911 goto fail;
2912 }
2913
2914 // Service
2915 len = *ptr + 1;
2916 ptr += len;
2917 if (ptr >= end)
2918 {
2919 LogInfo("SetRData: Malformed NAPTR service");
2920 goto fail;
2921 }
2922
2923 // Regexp
2924 len = *ptr + 1;
2925 ptr += len;
2926 if (ptr >= end)
2927 {
2928 LogInfo("SetRData: Malformed NAPTR regexp");
2929 goto fail;
2930 }
2931
2932 savelen = (int)(ptr - orig);
2933
2934 // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
2935 // states that for NAPTR we should decompress. We make sure that we store the full
2936 // name rather than the compressed name
2937 if (msg)
2938 {
2939 ptr = getDomainName(msg, ptr, end, &name);
2940 }
2941 else
2942 {
2943 if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end))
2944 {
2945 goto fail;
2946 }
2947 ptr += DomainNameLength(&name);
2948 }
2949 if (ptr != end)
2950 {
2951 LogInfo("SetRData: Malformed NAPTR RDATA name");
2952 goto fail;
2953 }
2954
2955 rr->rdlength = savelen + DomainNameLength(&name);
2956 // The uncompressed size should not exceed the limits
2957 if (rr->rdlength > MaximumRDSize)
2958 {
2959 LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->rdlength %d, "
2960 "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c);
2961 goto fail;
2962 }
2963 mDNSPlatformMemCopy(rdb->data, orig, savelen);
2964 AssignDomainName((domainname *)(rdb->data + savelen), &name);
2965 break;
2966 }
2967 case kDNSType_OPT: {
2968 const mDNSu8 * const dataend = &rr->rdata->u.data[rr->rdata->MaxRDLength];
2969 rdataOPT *opt = rr->rdata->u.opt;
2970 rr->rdlength = 0;
2971 while ((ptr < end) && ((dataend - ((const mDNSu8 *)opt)) >= ((mDNSs32)sizeof(*opt))))
2972 {
2973 const rdataOPT *const currentopt = opt;
2974 if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; }
2975 opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2976 opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2977 ptr += 4;
2978 if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; }
2979 switch (opt->opt)
2980 {
2981 case kDNSOpt_LLQ:
2982 if (opt->optlen == DNSOpt_LLQData_Space - 4)
2983 {
2984 opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2985 opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2986 opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
2987 mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8);
2988 opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]);
2989 if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
2990 opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
2991 opt++;
2992 }
2993 break;
2994 case kDNSOpt_Lease:
2995 if (opt->optlen == DNSOpt_LeaseData_Space - 4)
2996 {
2997 opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
2998 if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
2999 opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
3000 opt++;
3001 }
3002 break;
3003 case kDNSOpt_Owner:
3004 if (ValidOwnerLength(opt->optlen))
3005 {
3006 opt->u.owner.vers = ptr[0];
3007 opt->u.owner.seq = ptr[1];
3008 mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address
3009 mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address
3010 opt->u.owner.password = zeroEthAddr;
3011 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
3012 {
3013 mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address
3014 // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above
3015 // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4
3016 if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
3017 mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
3018 }
3019 opt++;
3020 }
3021 break;
3022 case kDNSOpt_Trace:
3023 if (opt->optlen == DNSOpt_TraceData_Space - 4)
3024 {
3025 opt->u.tracer.platf = ptr[0];
3026 opt->u.tracer.mDNSv = (mDNSu32) ((mDNSu32)ptr[1] << 24 | (mDNSu32)ptr[2] << 16 | (mDNSu32)ptr[3] << 8 | ptr[4]);
3027 opt++;
3028 }
3029 else
3030 {
3031 opt->u.tracer.platf = 0xFF;
3032 opt->u.tracer.mDNSv = 0xFFFFFFFF;
3033 opt++;
3034 }
3035 break;
3036 }
3037 ptr += currentopt->optlen;
3038 }
3039 rr->rdlength = (mDNSu16)((mDNSu8*)opt - rr->rdata->u.data);
3040 if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; }
3041 break;
3042 }
3043
3044 case kDNSType_NSEC: {
3045 domainname name;
3046 int len = rdlength;
3047 int bmaplen, dlen;
3048 const mDNSu8 *orig = ptr;
3049 const mDNSu8 *bmap;
3050
3051 if (msg)
3052 {
3053 ptr = getDomainName(msg, ptr, end, &name);
3054 }
3055 else
3056 {
3057 if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end))
3058 {
3059 goto fail;
3060 }
3061 ptr += DomainNameLength(&name);
3062 }
3063 if (!ptr)
3064 {
3065 LogInfo("SetRData: Malformed NSEC nextname");
3066 goto fail;
3067 }
3068
3069 dlen = DomainNameLength(&name);
3070
3071 // Multicast NSECs use name compression for this field unlike the unicast case which
3072 // does not use compression. And multicast case always succeeds in compression. So,
3073 // the rdlength includes only the compressed space in that case. So, can't
3074 // use the DomainNameLength of name to reduce the length here.
3075 len -= (ptr - orig);
3076 bmaplen = len; // Save the length of the bitmap
3077 bmap = ptr;
3078 ptr = SanityCheckBitMap(bmap, end, len);
3079 if (!ptr)
3080 goto fail;
3081 if (ptr != end)
3082 {
3083 LogInfo("SetRData: Malformed NSEC length not right");
3084 goto fail;
3085 }
3086
3087 // Initialize the right length here. When we call SetNewRData below which in turn calls
3088 // GetRDLength and for NSEC case, it assumes that rdlength is intitialized
3089 rr->rdlength = DomainNameLength(&name) + bmaplen;
3090
3091 // Do we have space after the name expansion ?
3092 if (rr->rdlength > MaximumRDSize)
3093 {
3094 LogInfo("SetRData: Malformed NSEC rdlength %d, rr->rdlength %d, "
3095 "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c);
3096 goto fail;
3097 }
3098 AssignDomainName((domainname *)rdb->data, &name);
3099 mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
3100 break;
3101 }
3102 case kDNSType_TKEY:
3103 case kDNSType_TSIG:
3104 {
3105 domainname name;
3106 int dlen, rlen;
3107
3108 // The name should not be compressed. But we take the conservative approach
3109 // and uncompress the name before we store it.
3110 if (msg)
3111 {
3112 ptr = getDomainName(msg, ptr, end, &name);
3113 }
3114 else
3115 {
3116 if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end))
3117 {
3118 goto fail;
3119 }
3120 ptr += DomainNameLength(&name);
3121 }
3122 if (!ptr || ptr >= end)
3123 {
3124 LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->rrtype);
3125 goto fail;
3126 }
3127 dlen = DomainNameLength(&name);
3128 rlen = (int)(end - ptr);
3129 rr->rdlength = dlen + rlen;
3130 if (rr->rdlength > MaximumRDSize)
3131 {
3132 LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->rdlength %d, "
3133 "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c);
3134 goto fail;
3135 }
3136 AssignDomainName((domainname *)rdb->data, &name);
3137 mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen);
3138 break;
3139 }
3140 default:
3141 debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
3142 rr->rrtype, DNSTypeName(rr->rrtype));
3143 // Note: Just because we don't understand the record type, that doesn't
3144 // mean we fail. The DNS protocol specifies rdlength, so we can
3145 // safely skip over unknown records and ignore them.
3146 // We also grab a binary copy of the rdata anyway, since the caller
3147 // might know how to interpret it even if we don't.
3148 rr->rdlength = rdlength;
3149 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3150 break;
3151 }
3152 return mDNStrue;
3153 fail:
3154 return mDNSfalse;
3155 }
3156
GetLargeResourceRecord(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,mDNSu8 RecordType,LargeCacheRecord * const largecr)3157 mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
3158 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
3159 {
3160 CacheRecord *const rr = &largecr->r;
3161 mDNSu16 pktrdlength;
3162 mDNSu32 maxttl = (!InterfaceID) ? mDNSMaximumUnicastTTLSeconds : mDNSMaximumMulticastTTLSeconds;
3163
3164 if (largecr == &m->rec && m->rec.r.resrec.RecordType)
3165 LogFatalError("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
3166
3167 rr->next = mDNSNULL;
3168 rr->resrec.name = &largecr->namestorage;
3169
3170 rr->NextInKAList = mDNSNULL;
3171 rr->TimeRcvd = m ? m->timenow : 0;
3172 rr->DelayDelivery = 0;
3173 rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
3174 #if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS)
3175 rr->LastCachedAnswerTime = 0;
3176 #endif
3177 rr->CRActiveQuestion = mDNSNULL;
3178 rr->UnansweredQueries = 0;
3179 rr->LastUnansweredTime= 0;
3180 rr->NextInCFList = mDNSNULL;
3181
3182 rr->resrec.InterfaceID = InterfaceID;
3183 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
3184 mdns_forget(&rr->resrec.dnsservice);
3185 #else
3186 rr->resrec.rDNSServer = mDNSNULL;
3187 #endif
3188
3189 ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL
3190 if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
3191 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
3192
3193 if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
3194
3195 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
3196 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
3197 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
3198 if (rr->resrec.rroriginalttl > maxttl && (mDNSs32)rr->resrec.rroriginalttl != -1)
3199 rr->resrec.rroriginalttl = maxttl;
3200 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
3201 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
3202 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
3203
3204 // If mDNS record has cache-flush bit set, we mark it unique
3205 // For uDNS records, all are implicitly deemed unique (a single DNS server is always authoritative for the entire RRSet)
3206 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || !InterfaceID)
3207 RecordType |= kDNSRecordTypePacketUniqueMask;
3208 ptr += 10;
3209 if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
3210 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
3211
3212 rr->resrec.rdata = (RData*)&rr->smallrdatastorage;
3213 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
3214
3215 if (pktrdlength > MaximumRDSize)
3216 {
3217 LogInfo("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
3218 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
3219 goto fail;
3220 }
3221
3222 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
3223
3224 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
3225 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
3226 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
3227 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
3228 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
3229 if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
3230 rr->resrec.rdlength = 0;
3231 else if (!SetRData(msg, ptr, end, &rr->resrec, pktrdlength))
3232 {
3233 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
3234 "GetLargeResourceRecord: SetRData failed for " PRI_DM_NAME " (" PUB_S ")",
3235 DM_NAME_PARAM(rr->resrec.name), DNSTypeName(rr->resrec.rrtype));
3236 goto fail;
3237 }
3238
3239 SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us
3240
3241 // Success! Now fill in RecordType to show this record contains valid data
3242 rr->resrec.RecordType = RecordType;
3243 return(end);
3244
3245 fail:
3246 // If we were unable to parse the rdata in this record, we indicate that by
3247 // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero
3248 rr->resrec.RecordType = kDNSRecordTypePacketNegative;
3249 rr->resrec.rdlength = 0;
3250 rr->resrec.rdestimate = 0;
3251 rr->resrec.rdatahash = 0;
3252 return(end);
3253 }
3254
skipQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)3255 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
3256 {
3257 ptr = skipDomainName(msg, ptr, end);
3258 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
3259 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
3260 return(ptr+4);
3261 }
3262
getQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,DNSQuestion * question)3263 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
3264 DNSQuestion *question)
3265 {
3266 mDNSPlatformMemZero(question, sizeof(*question));
3267 question->InterfaceID = InterfaceID;
3268 if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
3269 ptr = getDomainName(msg, ptr, end, &question->qname);
3270 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
3271 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
3272
3273 question->qnamehash = DomainNameHashValue(&question->qname);
3274 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
3275 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
3276 return(ptr+4);
3277 }
3278
LocateAnswers(const DNSMessage * const msg,const mDNSu8 * const end)3279 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
3280 {
3281 int i;
3282 const mDNSu8 *ptr = msg->data;
3283 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
3284 return(ptr);
3285 }
3286
LocateAuthorities(const DNSMessage * const msg,const mDNSu8 * const end)3287 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
3288 {
3289 int i;
3290 const mDNSu8 *ptr = LocateAnswers(msg, end);
3291 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
3292 return(ptr);
3293 }
3294
LocateAdditionals(const DNSMessage * const msg,const mDNSu8 * const end)3295 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
3296 {
3297 int i;
3298 const mDNSu8 *ptr = LocateAuthorities(msg, end);
3299 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
3300 return (ptr);
3301 }
3302
LocateOptRR(const DNSMessage * const msg,const mDNSu8 * const end,int minsize)3303 mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize)
3304 {
3305 int i;
3306 const mDNSu8 *ptr = LocateAdditionals(msg, end);
3307
3308 // Locate the OPT record.
3309 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
3310 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
3311 // but not necessarily the *last* entry in the Additional Section.
3312 for (i = 0; ptr && i < msg->h.numAdditionals; i++)
3313 {
3314 if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data
3315 ptr[0] == 0 && // Name must be root label
3316 ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT
3317 ptr[2] == (kDNSType_OPT & 0xFF) &&
3318 ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize)
3319 return(ptr);
3320 else
3321 ptr = skipResourceRecord(msg, ptr, end);
3322 }
3323 return(mDNSNULL);
3324 }
3325
3326 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
3327 // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
3328 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
3329 // The code that currently calls this assumes there's only one, instead of iterating through the set
GetLLQOptData(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * const end)3330 mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
3331 {
3332 const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space);
3333 if (ptr)
3334 {
3335 ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3336 if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]);
3337 }
3338 return(mDNSNULL);
3339 }
3340
3341 // Get the lease life of records in a dynamic update
GetPktLease(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * const end,mDNSu32 * const lease)3342 mDNSexport mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSu32 *const lease)
3343 {
3344 const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
3345 if (ptr)
3346 {
3347 ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3348 if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
3349 {
3350 const rdataOPT *o;
3351 const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
3352 for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
3353 if (o->opt == kDNSOpt_Lease)
3354 {
3355 *lease = o->u.updatelease;
3356 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
3357 return mDNStrue;
3358 }
3359 }
3360 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
3361 }
3362 return mDNSfalse;
3363 }
3364
3365 #define DNS_OP_Name(X) ( \
3366 (X) == kDNSFlag0_OP_StdQuery ? "" : \
3367 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
3368 (X) == kDNSFlag0_OP_Status ? "Status " : \
3369 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
3370 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
3371 (X) == kDNSFlag0_OP_Update ? "Update " : \
3372 (X) == kDNSFlag0_OP_DSO ? "DSO " : "?? " )
3373
3374 #define DNS_RC_Name(X) ( \
3375 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
3376 (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
3377 (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
3378 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
3379 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
3380 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
3381 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
3382 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
3383 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
3384 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
3385 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : \
3386 (X) == kDNSFlag1_RC_DSOTypeNI ? "DSOTypeNI" : "??" )
3387
mDNS_snprintf_add(char ** ptr,const char * lim,const char * fmt,...)3388 mDNSexport void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...)
3389 {
3390 va_list args;
3391 mDNSu32 buflen, n;
3392 char *const dst = *ptr;
3393
3394 buflen = (mDNSu32)(lim - dst);
3395 if (buflen > 0)
3396 {
3397 va_start(args, fmt);
3398 n = mDNS_vsnprintf(dst, buflen, fmt, args);
3399 va_end(args);
3400 *ptr = dst + n;
3401 }
3402 }
3403
3404 #define DNSTypeString(X) (((X) == kDNSType_A) ? "A" : DNSTypeName(X))
3405
3406 #define ReadField16(PTR) ((mDNSu16)((((mDNSu16)((mDNSu8 *)(PTR))[0]) << 8) | ((mDNSu16)((mDNSu8 *)(PTR))[1])))
3407 #define ReadField32(PTR) \
3408 ((mDNSu32)( \
3409 (((mDNSu32)((mDNSu8 *)(PTR))[0]) << 24) | \
3410 (((mDNSu32)((mDNSu8 *)(PTR))[1]) << 16) | \
3411 (((mDNSu32)((mDNSu8 *)(PTR))[2]) << 8) | \
3412 ((mDNSu32)((mDNSu8 *)(PTR))[3])))
3413
DNSMessageDumpToLog(const DNSMessage * const msg,const mDNSu8 * const end)3414 mDNSlocal void DNSMessageDumpToLog(const DNSMessage *const msg, const mDNSu8 *const end)
3415 {
3416 domainname *name = mDNSNULL;
3417 const mDNSu8 *ptr = msg->data;
3418 domainname nameStorage[2];
3419
3420 char questions[512];
3421 questions[0] = '\0';
3422 char *questions_dst = questions;
3423 const char *const questions_lim = &questions[512];
3424 for (mDNSu32 i = 0; i < msg->h.numQuestions; i++)
3425 {
3426 mDNSu16 qtype, qclass;
3427
3428 name = &nameStorage[0];
3429 ptr = getDomainName(msg, ptr, end, name);
3430 if (!ptr) goto exit;
3431
3432 if ((end - ptr) < 4) goto exit;
3433 qtype = ReadField16(&ptr[0]);
3434 qclass = ReadField16(&ptr[2]);
3435 ptr += 4;
3436
3437 mDNS_snprintf_add(&questions_dst, questions_lim, " %##s %s", name->c, DNSTypeString(qtype));
3438 if (qclass != kDNSClass_IN) mDNS_snprintf_add(&questions_dst, questions_lim, "/%u", qclass);
3439 mDNS_snprintf_add(&questions_dst, questions_lim, "?");
3440 }
3441
3442 char rrs[512];
3443 rrs[0] = '\0';
3444 char *rrs_dst = rrs;
3445 const char *const rrs_lim = &rrs[512];
3446 const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals;
3447 for (mDNSu32 i = 0; i < rrcount; i++)
3448 {
3449 mDNSu16 rrtype, rrclass, rdlength;
3450 mDNSu32 ttl;
3451 int handled;
3452 const mDNSu8 *rdata;
3453 const domainname *const previousName = name;
3454
3455 name = &nameStorage[(name == &nameStorage[0]) ? 1 : 0];
3456 ptr = getDomainName(msg, ptr, end, name);
3457 if (!ptr) goto exit;
3458
3459 if ((end - ptr) < 10) goto exit;
3460 rrtype = ReadField16(&ptr[0]);
3461 rrclass = ReadField16(&ptr[2]);
3462 ttl = ReadField32(&ptr[4]);
3463 rdlength = ReadField16(&ptr[8]);
3464 ptr += 10;
3465
3466 if ((end - ptr) < rdlength) goto exit;
3467 rdata = ptr;
3468
3469 if (i > 0) mDNS_snprintf_add(&rrs_dst, rrs_lim, ",");
3470 if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&rrs_dst, rrs_lim, " %##s", name);
3471
3472 mDNS_snprintf_add(&rrs_dst, rrs_lim, " %s", DNSTypeString(rrtype));
3473 if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&rrs_dst, rrs_lim, "/%u", rrclass);
3474 mDNS_snprintf_add(&rrs_dst, rrs_lim, " ");
3475
3476 handled = mDNSfalse;
3477 switch (rrtype)
3478 {
3479 case kDNSType_A:
3480 if (rdlength == 4)
3481 {
3482 mDNS_snprintf_add(&rrs_dst, rrs_lim, "%.4a", rdata);
3483 handled = mDNStrue;
3484 }
3485 break;
3486
3487 case kDNSType_AAAA:
3488 if (rdlength == 16)
3489 {
3490 mDNS_snprintf_add(&rrs_dst, rrs_lim, "%.16a", rdata);
3491 handled = mDNStrue;
3492 }
3493 break;
3494
3495 case kDNSType_CNAME:
3496 ptr = getDomainName(msg, rdata, end, name);
3497 if (!ptr) goto exit;
3498
3499 mDNS_snprintf_add(&rrs_dst, rrs_lim, "%##s", name);
3500 handled = mDNStrue;
3501 break;
3502
3503 case kDNSType_SOA:
3504 {
3505 mDNSu32 serial, refresh, retry, expire, minimum;
3506 domainname *const mname = &nameStorage[0];
3507 domainname *const rname = &nameStorage[1];
3508 name = mDNSNULL;
3509
3510 ptr = getDomainName(msg, rdata, end, mname);
3511 if (!ptr) goto exit;
3512
3513 ptr = getDomainName(msg, ptr, end, rname);
3514 if (!ptr) goto exit;
3515
3516 if ((end - ptr) < 20) goto exit;
3517 serial = ReadField32(&ptr[0]);
3518 refresh = ReadField32(&ptr[4]);
3519 retry = ReadField32(&ptr[8]);
3520 expire = ReadField32(&ptr[12]);
3521 minimum = ReadField32(&ptr[16]);
3522
3523 mDNS_snprintf_add(&rrs_dst, rrs_lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial,
3524 (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum);
3525
3526 handled = mDNStrue;
3527 break;
3528 }
3529
3530 default:
3531 break;
3532 }
3533 if (!handled) mDNS_snprintf_add(&rrs_dst, rrs_lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata);
3534 mDNS_snprintf_add(&rrs_dst, rrs_lim, " (%lu)", (unsigned long)ttl);
3535 ptr = rdata + rdlength;
3536 }
3537
3538 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO,
3539 "[Q%u] DNS " PUB_S PUB_S " (%lu) (flags %02X%02X) RCODE: " PUB_S " (%d)" PUB_S PUB_S PUB_S PUB_S PUB_S PUB_S ":"
3540 PRI_S " %u/%u/%u " PRI_S,
3541 mDNSVal16(msg->h.id),
3542 DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
3543 (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query",
3544 (unsigned long)(end - (const mDNSu8 *)msg),
3545 msg->h.flags.b[0], msg->h.flags.b[1],
3546 DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
3547 msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
3548 (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "",
3549 (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "",
3550 (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "",
3551 (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "",
3552 (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "",
3553 (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "",
3554 questions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, rrs);
3555
3556 exit:
3557 return;
3558 }
3559
3560 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
DumpPacket(mStatus status,mDNSBool sent,const char * transport,const mDNSAddr * srcaddr,mDNSIPPort srcport,const mDNSAddr * dstaddr,mDNSIPPort dstport,const DNSMessage * const msg,const mDNSu8 * const end,mDNSInterfaceID interfaceID)3561 mDNSexport void DumpPacket(mStatus status, mDNSBool sent, const char *transport,
3562 const mDNSAddr *srcaddr, mDNSIPPort srcport,const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg,
3563 const mDNSu8 *const end, mDNSInterfaceID interfaceID)
3564 {
3565 const mDNSAddr zeroIPv4Addr = { mDNSAddrType_IPv4, {{{ 0 }}} };
3566 char action[32];
3567 const char* interfaceName = "interface";
3568
3569 if (!status) mDNS_snprintf(action, sizeof(action), sent ? "Sent" : "Received");
3570 else mDNS_snprintf(action, sizeof(action), "ERROR %d %sing", status, sent ? "Send" : "Receiv");
3571
3572 #if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
3573 interfaceName = InterfaceNameForID(&mDNSStorage, interfaceID);
3574 #endif
3575
3576 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO,
3577 "[Q%u] " PUB_S " " PUB_S " DNS Message %lu bytes from " PRI_IP_ADDR ":%d to " PRI_IP_ADDR ":%d via " PUB_S " (%p)",
3578 mDNSVal16(msg->h.id), action, transport, (unsigned long)(end - (const mDNSu8 *)msg),
3579 srcaddr ? srcaddr : &zeroIPv4Addr, mDNSVal16(srcport), dstaddr ? dstaddr : &zeroIPv4Addr, mDNSVal16(dstport),
3580 interfaceName, interfaceID);
3581 DNSMessageDumpToLog(msg, end);
3582 }
3583
3584 // ***************************************************************************
3585 #if COMPILER_LIKES_PRAGMA_MARK
3586 #pragma mark -
3587 #pragma mark - Packet Sending Functions
3588 #endif
3589
3590 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
3591 struct TCPSocket_struct { mDNSIPPort port; TCPSocketFlags flags; /* ... */ };
3592 // Stub definition of UDPSocket_struct so we can access port field. (Rest of UDPSocket_struct is platform-dependent.)
3593 struct UDPSocket_struct { mDNSIPPort port; /* ... */ };
3594
3595 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
3596 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
mDNSSendDNSMessage(mDNS * const m,DNSMessage * const msg,mDNSu8 * end,mDNSInterfaceID InterfaceID,TCPSocket * tcpSrc,UDPSocket * udpSrc,const mDNSAddr * dst,mDNSIPPort dstport,DomainAuthInfo * authInfo,mDNSBool useBackgroundTrafficClass)3597 mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
3598 mDNSInterfaceID InterfaceID, TCPSocket *tcpSrc, UDPSocket *udpSrc, const mDNSAddr *dst,
3599 mDNSIPPort dstport, DomainAuthInfo *authInfo, mDNSBool useBackgroundTrafficClass)
3600 {
3601 mStatus status = mStatus_NoError;
3602 const mDNSu16 numAdditionals = msg->h.numAdditionals;
3603
3604 #if APPLE_OSX_mDNSResponder
3605 // maintain outbound packet statistics
3606 if (mDNSOpaque16IsZero(msg->h.id))
3607 m->MulticastPacketsSent++;
3608 else
3609 m->UnicastPacketsSent++;
3610 #endif // APPLE_OSX_mDNSResponder
3611
3612 // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
3613 if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
3614 {
3615 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
3616 return mStatus_BadParamErr;
3617 }
3618
3619 // Put all the integer values in IETF byte-order (MSB first, LSB second)
3620 SwapDNSHeaderBytes(msg);
3621
3622 if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order
3623 if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
3624 else
3625 {
3626 // Send the packet on the wire
3627 if (!tcpSrc)
3628 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, udpSrc, dst, dstport, useBackgroundTrafficClass);
3629 else
3630 {
3631 mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
3632 mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
3633 char *buf;
3634 long nsent;
3635
3636 // Try to send them in one packet if we can allocate enough memory
3637 buf = (char *) mDNSPlatformMemAllocate(msglen + 2);
3638 if (buf)
3639 {
3640 buf[0] = lenbuf[0];
3641 buf[1] = lenbuf[1];
3642 mDNSPlatformMemCopy(buf+2, msg, msglen);
3643 nsent = mDNSPlatformWriteTCP(tcpSrc, buf, msglen+2);
3644 if (nsent != (msglen + 2))
3645 {
3646 LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen);
3647 status = mStatus_ConnFailed;
3648 }
3649 mDNSPlatformMemFree(buf);
3650 }
3651 else
3652 {
3653 nsent = mDNSPlatformWriteTCP(tcpSrc, (char*)lenbuf, 2);
3654 if (nsent != 2)
3655 {
3656 LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2);
3657 status = mStatus_ConnFailed;
3658 }
3659 else
3660 {
3661 nsent = mDNSPlatformWriteTCP(tcpSrc, (char *)msg, msglen);
3662 if (nsent != msglen)
3663 {
3664 LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen);
3665 status = mStatus_ConnFailed;
3666 }
3667 }
3668 }
3669 }
3670 }
3671
3672 // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
3673 SwapDNSHeaderBytes(msg);
3674
3675 // Dump the packet with the HINFO and TSIG
3676 if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
3677 {
3678 char *transport = "UDP";
3679 mDNSIPPort portNumber = udpSrc ? udpSrc->port : MulticastDNSPort;
3680 if (tcpSrc)
3681 {
3682 if (tcpSrc->flags)
3683 transport = "TLS";
3684 else
3685 transport = "TCP";
3686 portNumber = tcpSrc->port;
3687 }
3688 DumpPacket(status, mDNStrue, transport, mDNSNULL, portNumber, dst, dstport, msg, end, InterfaceID);
3689 }
3690
3691 // put the number of additionals back the way it was
3692 msg->h.numAdditionals = numAdditionals;
3693
3694 return(status);
3695 }
3696
3697 // ***************************************************************************
3698 #if COMPILER_LIKES_PRAGMA_MARK
3699 #pragma mark -
3700 #pragma mark - RR List Management & Task Management
3701 #endif
3702
mDNS_Lock_(mDNS * const m,const char * const functionname)3703 mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname)
3704 {
3705 // MUST grab the platform lock FIRST!
3706 mDNSPlatformLock(m);
3707
3708 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
3709 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
3710 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
3711 // If mDNS_busy != mDNS_reentrancy that's a bad sign
3712 if (m->mDNS_busy != m->mDNS_reentrancy)
3713 LogFatalError("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
3714
3715 // If this is an initial entry into the mDNSCore code, set m->timenow
3716 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3717 if (m->mDNS_busy == 0)
3718 {
3719 if (m->timenow)
3720 LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m));
3721 m->timenow = mDNS_TimeNow_NoLock(m);
3722 if (m->timenow == 0) m->timenow = 1;
3723 }
3724 else if (m->timenow == 0)
3725 {
3726 LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy);
3727 m->timenow = mDNS_TimeNow_NoLock(m);
3728 if (m->timenow == 0) m->timenow = 1;
3729 }
3730
3731 if (m->timenow_last - m->timenow > 0)
3732 {
3733 m->timenow_adjust += m->timenow_last - m->timenow;
3734 LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust);
3735 m->timenow = m->timenow_last;
3736 }
3737 m->timenow_last = m->timenow;
3738
3739 // Increment mDNS_busy so we'll recognise re-entrant calls
3740 m->mDNS_busy++;
3741 }
3742
AnyLocalRecordReady(const mDNS * const m)3743 mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m)
3744 {
3745 AuthRecord *rr;
3746 for (rr = m->NewLocalRecords; rr; rr = rr->next)
3747 if (LocalRecordReady(rr)) return rr;
3748 return mDNSNULL;
3749 }
3750
GetNextScheduledEvent(const mDNS * const m)3751 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
3752 {
3753 mDNSs32 e = m->timenow + FutureTime;
3754 if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
3755 if (m->NewQuestions)
3756 {
3757 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
3758 else return(m->timenow);
3759 }
3760 if (m->NewLocalOnlyQuestions) return(m->timenow);
3761 if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
3762 if (m->NewLocalOnlyRecords) return(m->timenow);
3763 if (m->SPSProxyListChanged) return(m->timenow);
3764 if (m->LocalRemoveEvents) return(m->timenow);
3765
3766 #ifndef UNICAST_DISABLED
3767 if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent;
3768 if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp;
3769 if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate;
3770 #endif
3771
3772 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
3773 if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS;
3774 if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA;
3775
3776 #if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND)
3777 if (m->NextBonjourDisableTime && (e - m->NextBonjourDisableTime > 0)) e = m->NextBonjourDisableTime;
3778 #endif
3779
3780 // NextScheduledSPRetry only valid when DelaySleep not set
3781 if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
3782 if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
3783
3784 if (m->SuppressSending)
3785 {
3786 if (e - m->SuppressSending > 0) e = m->SuppressSending;
3787 }
3788 else
3789 {
3790 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
3791 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
3792 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
3793 }
3794 if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
3795
3796 if (m->NextBLEServiceTime && (e - m->NextBLEServiceTime > 0)) e = m->NextBLEServiceTime;
3797
3798 return(e);
3799 }
3800
3801 #define LogTSE TSE++,LogMsg
3802
ShowTaskSchedulingError(mDNS * const m)3803 mDNSexport void ShowTaskSchedulingError(mDNS *const m)
3804 {
3805 int TSE = 0;
3806 AuthRecord *rr;
3807 mDNS_Lock(m);
3808
3809 LogMsg("Task Scheduling Error: *** Continuously busy for more than a second");
3810
3811 // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3812
3813 if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
3814 LogTSE("Task Scheduling Error: NewQuestion %##s (%s)",
3815 m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
3816
3817 if (m->NewLocalOnlyQuestions)
3818 LogTSE("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3819 m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
3820
3821 if (m->NewLocalRecords)
3822 {
3823 rr = AnyLocalRecordReady(m);
3824 if (rr) LogTSE("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
3825 }
3826
3827 if (m->NewLocalOnlyRecords) LogTSE("Task Scheduling Error: NewLocalOnlyRecords");
3828
3829 if (m->SPSProxyListChanged) LogTSE("Task Scheduling Error: SPSProxyListChanged");
3830
3831 if (m->LocalRemoveEvents) LogTSE("Task Scheduling Error: LocalRemoveEvents");
3832
3833 #ifndef UNICAST_DISABLED
3834 if (m->timenow - m->NextuDNSEvent >= 0)
3835 LogTSE("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent);
3836 if (m->timenow - m->NextScheduledNATOp >= 0)
3837 LogTSE("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp);
3838 if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0)
3839 LogTSE("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate);
3840 #endif
3841
3842 if (m->timenow - m->NextCacheCheck >= 0)
3843 LogTSE("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck);
3844 if (m->timenow - m->NextScheduledSPS >= 0)
3845 LogTSE("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS);
3846 if (m->timenow - m->NextScheduledKA >= 0)
3847 LogTSE("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA);
3848 if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
3849 LogTSE("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry);
3850 if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
3851 LogTSE("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep);
3852
3853 if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
3854 LogTSE("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending);
3855 if (m->timenow - m->NextScheduledQuery >= 0)
3856 LogTSE("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery);
3857 if (m->timenow - m->NextScheduledProbe >= 0)
3858 LogTSE("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe);
3859 if (m->timenow - m->NextScheduledResponse >= 0)
3860 LogTSE("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
3861 if (m->timenow - m->NextScheduledStopTime >= 0)
3862 LogTSE("Task Scheduling Error: m->NextScheduledStopTime %d", m->timenow - m->NextScheduledStopTime);
3863
3864 if (m->timenow - m->NextScheduledEvent >= 0)
3865 LogTSE("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent);
3866
3867 if (m->NetworkChanged && m->timenow - m->NetworkChanged >= 0)
3868 LogTSE("Task Scheduling Error: NetworkChanged %d", m->timenow - m->NetworkChanged);
3869
3870 if (!TSE) LogMsg("Task Scheduling Error: *** No likely causes identified");
3871 else LogMsg("Task Scheduling Error: *** %d potential cause%s identified (significant only if the same cause consistently appears)", TSE, TSE > 1 ? "s" : "");
3872
3873 mDNS_Unlock(m);
3874 }
3875
mDNS_Unlock_(mDNS * const m,const char * const functionname)3876 mDNSexport void mDNS_Unlock_(mDNS *const m, const char *const functionname)
3877 {
3878 // Decrement mDNS_busy
3879 m->mDNS_busy--;
3880
3881 // Check for locking failures
3882 if (m->mDNS_busy != m->mDNS_reentrancy)
3883 LogFatalError("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
3884
3885 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
3886 if (m->mDNS_busy == 0)
3887 {
3888 m->NextScheduledEvent = GetNextScheduledEvent(m);
3889 if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname);
3890 m->timenow = 0;
3891 }
3892
3893 // MUST release the platform lock LAST!
3894 mDNSPlatformUnlock(m);
3895 }
3896
3897 // ***************************************************************************
3898 #if COMPILER_LIKES_PRAGMA_MARK
3899 #pragma mark -
3900 #pragma mark - Specialized mDNS version of vsnprintf
3901 #endif
3902
3903 static const struct mDNSprintf_format
3904 {
3905 unsigned leftJustify : 1;
3906 unsigned forceSign : 1;
3907 unsigned zeroPad : 1;
3908 unsigned havePrecision : 1;
3909 unsigned hSize : 1;
3910 unsigned lSize : 1;
3911 char altForm;
3912 char sign; // +, - or space
3913 unsigned int fieldWidth;
3914 unsigned int precision;
3915 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3916
3917 #define kHexDigitsLowercase "0123456789abcdef"
3918 #define kHexDigitsUppercase "0123456789ABCDEF";
3919
mDNS_vsnprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,va_list arg)3920 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
3921 {
3922 mDNSu32 nwritten = 0;
3923 int c;
3924 if (buflen == 0) return(0);
3925 buflen--; // Pre-reserve one space in the buffer for the terminating null
3926 if (buflen == 0) goto exit;
3927
3928 for (c = *fmt; c != 0; c = *++fmt)
3929 {
3930 unsigned long n;
3931 int hexdump = mDNSfalse;
3932 if (c != '%')
3933 {
3934 *sbuffer++ = (char)c;
3935 if (++nwritten >= buflen) goto exit;
3936 }
3937 else
3938 {
3939 unsigned int i=0, j;
3940 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
3941 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
3942 // The size needs to be enough for a 256-byte domain name plus some error text.
3943 #define mDNS_VACB_Size 300
3944 char mDNS_VACB[mDNS_VACB_Size];
3945 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
3946 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
3947 char *s = mDNS_VACB_Lim, *digits;
3948 struct mDNSprintf_format F = mDNSprintf_format_default;
3949
3950 while (1) // decode flags
3951 {
3952 c = *++fmt;
3953 if (c == '-') F.leftJustify = 1;
3954 else if (c == '+') F.forceSign = 1;
3955 else if (c == ' ') F.sign = ' ';
3956 else if (c == '#') F.altForm++;
3957 else if (c == '0') F.zeroPad = 1;
3958 else break;
3959 }
3960
3961 if (c == '*') // decode field width
3962 {
3963 int f = va_arg(arg, int);
3964 if (f < 0) { f = -f; F.leftJustify = 1; }
3965 F.fieldWidth = (unsigned int)f;
3966 c = *++fmt;
3967 }
3968 else
3969 {
3970 for (; c >= '0' && c <= '9'; c = *++fmt)
3971 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
3972 }
3973
3974 if (c == '.') // decode precision
3975 {
3976 if ((c = *++fmt) == '*')
3977 { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
3978 else for (; c >= '0' && c <= '9'; c = *++fmt)
3979 F.precision = (10 * F.precision) + (c - '0');
3980 F.havePrecision = 1;
3981 }
3982
3983 if (F.leftJustify) F.zeroPad = 0;
3984
3985 conv:
3986 switch (c) // perform appropriate conversion
3987 {
3988 case 'h': F.hSize = 1; c = *++fmt; goto conv;
3989 case 'l': // fall through
3990 case 'L': F.lSize = 1; c = *++fmt; goto conv;
3991 case 'd':
3992 case 'i': if (F.lSize) n = (unsigned long)va_arg(arg, long);
3993 else n = (unsigned long)va_arg(arg, int);
3994 if (F.hSize) n = (short) n;
3995 if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
3996 else if (F.forceSign) F.sign = '+';
3997 goto decimal;
3998 case 'u': if (F.lSize) n = va_arg(arg, unsigned long);
3999 else n = va_arg(arg, unsigned int);
4000 if (F.hSize) n = (unsigned short) n;
4001 F.sign = 0;
4002 goto decimal;
4003 decimal: if (!F.havePrecision)
4004 {
4005 if (F.zeroPad)
4006 {
4007 F.precision = F.fieldWidth;
4008 if (F.sign) --F.precision;
4009 }
4010 if (F.precision < 1) F.precision = 1;
4011 }
4012 if (F.precision > mDNS_VACB_Size - 1)
4013 F.precision = mDNS_VACB_Size - 1;
4014 for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
4015 for (; i < F.precision; i++) *--s = '0';
4016 if (F.sign) { *--s = F.sign; i++; }
4017 break;
4018
4019 case 'o': if (F.lSize) n = va_arg(arg, unsigned long);
4020 else n = va_arg(arg, unsigned int);
4021 if (F.hSize) n = (unsigned short) n;
4022 if (!F.havePrecision)
4023 {
4024 if (F.zeroPad) F.precision = F.fieldWidth;
4025 if (F.precision < 1) F.precision = 1;
4026 }
4027 if (F.precision > mDNS_VACB_Size - 1)
4028 F.precision = mDNS_VACB_Size - 1;
4029 for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
4030 if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
4031 for (; i < F.precision; i++) *--s = '0';
4032 break;
4033
4034 case 'a': {
4035 unsigned char *a = va_arg(arg, unsigned char *);
4036 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
4037 else
4038 {
4039 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
4040 if (F.altForm)
4041 {
4042 mDNSAddr *ip = (mDNSAddr*)a;
4043 switch (ip->type)
4044 {
4045 case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
4046 case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
4047 default: F.precision = 0; break;
4048 }
4049 }
4050 if (F.altForm && !F.precision)
4051 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
4052 else switch (F.precision)
4053 {
4054 case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
4055 a[0], a[1], a[2], a[3]); break;
4056 case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
4057 a[0], a[1], a[2], a[3], a[4], a[5]); break;
4058 case 16: {
4059 // Print IPv6 addresses according to RFC 5952, A Recommendation for IPv6 Address Text
4060 // Representation. See <https://tools.ietf.org/html/rfc5952>.
4061
4062 int idx, runLen = 0, runStart = 0, maxRunLen = 0, maxRunStart = 0, maxRunEnd;
4063
4064 // Find the leftmost longest run of consecutive zero hextets.
4065 for (idx = 0; idx < 8; ++idx)
4066 {
4067 const unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1];
4068 if (hextet == 0)
4069 {
4070 if (runLen++ == 0) runStart = idx;
4071 if (runLen > maxRunLen)
4072 {
4073 maxRunStart = runStart;
4074 maxRunLen = runLen;
4075 }
4076 }
4077 else
4078 {
4079 // If the number of remaining hextets is less than or equal to the length of the longest
4080 // run so far, then we've found the leftmost longest run.
4081 if ((8 - (idx + 1)) <= maxRunLen) break;
4082 runLen = 0;
4083 }
4084 }
4085
4086 // Compress the leftmost longest run of two or more consecutive zero hextets as "::".
4087 // For each reminaing hextet, suppress zeros leading up to the least-significant nibble, which
4088 // is always written, even if it's zero. Because of this requirement, it's easier to write the
4089 // IPv6 address in reverse. Also, write a colon separator before each hextet except for the
4090 // first one.
4091 s = mDNS_VACB_Lim;
4092 maxRunEnd = (maxRunLen >= 2) ? (maxRunStart + maxRunLen - 1) : -1;
4093 for (idx = 7; idx >= 0; --idx)
4094 {
4095 if (idx == maxRunEnd)
4096 {
4097 if (idx == 7) *--s = ':';
4098 idx = maxRunStart;
4099 *--s = ':';
4100 }
4101 else
4102 {
4103 unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1];
4104 do {
4105 *--s = kHexDigitsLowercase[hextet % 16];
4106 hextet /= 16;
4107 } while (hextet);
4108 if (idx > 0) *--s = ':';
4109 }
4110 }
4111 i = (unsigned int)(mDNS_VACB_Lim - s);
4112 }
4113 break;
4114
4115 default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
4116 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
4117 }
4118 }
4119 }
4120 break;
4121
4122 case 'p': F.havePrecision = F.lSize = 1;
4123 F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit
4124 /* FALLTHROUGH */
4125 case 'X': digits = kHexDigitsUppercase;
4126 goto hexadecimal;
4127 case 'x': digits = kHexDigitsLowercase;
4128 hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
4129 else n = va_arg(arg, unsigned int);
4130 if (F.hSize) n = (unsigned short) n;
4131 if (!F.havePrecision)
4132 {
4133 if (F.zeroPad)
4134 {
4135 F.precision = F.fieldWidth;
4136 if (F.altForm) F.precision -= 2;
4137 }
4138 if (F.precision < 1) F.precision = 1;
4139 }
4140 if (F.precision > mDNS_VACB_Size - 1)
4141 F.precision = mDNS_VACB_Size - 1;
4142 for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
4143 for (; i < F.precision; i++) *--s = '0';
4144 if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
4145 break;
4146
4147 case 'c': *--s = (char)va_arg(arg, int); i = 1; break;
4148
4149 case 's': s = va_arg(arg, char *);
4150 if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
4151 else switch (F.altForm)
4152 {
4153 case 0: i=0;
4154 if (!F.havePrecision) // C string
4155 while (s[i]) i++;
4156 else
4157 {
4158 while ((i < F.precision) && s[i]) i++;
4159 // Make sure we don't truncate in the middle of a UTF-8 character
4160 // If last character we got was any kind of UTF-8 multi-byte character,
4161 // then see if we have to back up.
4162 // This is not as easy as the similar checks below, because
4163 // here we can't assume it's safe to examine the *next* byte, so we
4164 // have to confine ourselves to working only backwards in the string.
4165 j = i; // Record where we got to
4166 // Now, back up until we find first non-continuation-char
4167 while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
4168 // Now s[i-1] is the first non-continuation-char
4169 // and (j-i) is the number of continuation-chars we found
4170 if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char
4171 {
4172 i--; // Tentatively eliminate this start-char as well
4173 // Now (j-i) is the number of characters we're considering eliminating.
4174 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
4175 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
4176 // (with sign extension) then the result has to be 0xFE.
4177 // If this is right, then we reinstate the tentatively eliminated bytes.
4178 if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
4179 }
4180 }
4181 break;
4182 case 1: i = (unsigned char) *s++; break; // Pascal string
4183 case 2: { // DNS label-sequence name
4184 unsigned char *a = (unsigned char *)s;
4185 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
4186 if (*a == 0) *s++ = '.'; // Special case for root DNS name
4187 while (*a)
4188 {
4189 char buf[63*4+1];
4190 if (*a > 63)
4191 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
4192 if (s + *a >= &mDNS_VACB[254])
4193 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
4194 // Need to use ConvertDomainLabelToCString to do proper escaping here,
4195 // so it's clear what's a literal dot and what's a label separator
4196 ConvertDomainLabelToCString((domainlabel*)a, buf);
4197 s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
4198 a += 1 + *a;
4199 }
4200 i = (mDNSu32)(s - mDNS_VACB);
4201 s = mDNS_VACB; // Reset s back to the start of the buffer
4202 break;
4203 }
4204 }
4205 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
4206 if (F.havePrecision && i > F.precision)
4207 { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
4208 break;
4209
4210 case 'H': {
4211 s = va_arg(arg, char *);
4212 hexdump = mDNStrue;
4213 }
4214 break;
4215
4216 case 'n': s = va_arg(arg, char *);
4217 if (F.hSize) *(short *) s = (short)nwritten;
4218 else if (F.lSize) *(long *) s = (long)nwritten;
4219 else *(int *) s = (int)nwritten;
4220 continue;
4221
4222 default: s = mDNS_VACB;
4223 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
4224 break;
4225
4226 case '%': *sbuffer++ = (char)c;
4227 if (++nwritten >= buflen) goto exit;
4228 break;
4229 }
4230
4231 if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
4232 do {
4233 *sbuffer++ = ' ';
4234 if (++nwritten >= buflen) goto exit;
4235 } while (i < --F.fieldWidth);
4236
4237 if (hexdump)
4238 {
4239 char *dst = sbuffer;
4240 const char *const lim = &sbuffer[buflen - nwritten];
4241 if (F.havePrecision)
4242 {
4243 for (i = 0; (i < F.precision) && (dst < lim); i++)
4244 {
4245 const unsigned int b = (unsigned int) *s++;
4246 if (i > 0) *dst++ = ' ';
4247 if (dst < lim) *dst++ = kHexDigitsLowercase[(b >> 4) & 0xF];
4248 if (dst < lim) *dst++ = kHexDigitsLowercase[ b & 0xF];
4249 }
4250 }
4251 i = (unsigned int)(dst - sbuffer);
4252 sbuffer = dst;
4253 }
4254 else
4255 {
4256 // Make sure we don't truncate in the middle of a UTF-8 character.
4257 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
4258 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
4259 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
4260 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
4261 if (i > buflen - nwritten)
4262 { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
4263 for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
4264 }
4265 nwritten += i;
4266 if (nwritten >= buflen) goto exit;
4267
4268 for (; i < F.fieldWidth; i++) // Pad on the right
4269 {
4270 *sbuffer++ = ' ';
4271 if (++nwritten >= buflen) goto exit;
4272 }
4273 }
4274 }
4275 exit:
4276 *sbuffer++ = 0;
4277 return(nwritten);
4278 }
4279
mDNS_snprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,...)4280 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
4281 {
4282 mDNSu32 length;
4283
4284 va_list ptr;
4285 va_start(ptr,fmt);
4286 length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
4287 va_end(ptr);
4288
4289 return(length);
4290 }
4291
4292 #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
mDNS_GetNextResolverGroupID(void)4293 mDNSexport mDNSu32 mDNS_GetNextResolverGroupID(void)
4294 {
4295 static mDNSu32 lastID = 0;
4296 if (++lastID == 0) lastID = 1; // Valid resolver group IDs are non-zero.
4297 return(lastID);
4298 }
4299 #endif
4300
4301 #define kReverseIPv6Domain ((const domainname *) "\x3" "ip6" "\x4" "arpa")
4302
GetReverseIPv6Addr(const domainname * name,mDNSu8 outIPv6[16])4303 mDNSexport mDNSBool GetReverseIPv6Addr(const domainname *name, mDNSu8 outIPv6[16])
4304 {
4305 const mDNSu8 * ptr;
4306 int i;
4307 mDNSu8 ipv6[16];
4308
4309 // If the name is of the form "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa.", where each x
4310 // is a hex digit, then the sequence of 32 hex digit labels represents the nibbles of an IPv6 address in reverse order.
4311 // See <https://tools.ietf.org/html/rfc3596#section-2.5>.
4312
4313 ptr = name->c;
4314 for (i = 0; i < 32; i++)
4315 {
4316 unsigned int c, nibble;
4317 const int j = 15 - (i / 2);
4318 if (*ptr++ != 1) return (mDNSfalse); // If this label's length is not 1, then fail.
4319 c = *ptr++; // Get label byte.
4320 if ( (c >= '0') && (c <= '9')) nibble = c - '0'; // If it's a hex digit, get its numeric value.
4321 else if ((c >= 'a') && (c <= 'f')) nibble = (c - 'a') + 10;
4322 else if ((c >= 'A') && (c <= 'F')) nibble = (c - 'A') + 10;
4323 else return (mDNSfalse); // Otherwise, fail.
4324 if ((i % 2) == 0)
4325 {
4326 ipv6[j] = (mDNSu8)nibble;
4327 }
4328 else
4329 {
4330 ipv6[j] |= (mDNSu8)(nibble << 4);
4331 }
4332 }
4333
4334 // The rest of the name needs to be "ip6.arpa.". If it isn't, fail.
4335
4336 if (!SameDomainName((const domainname *)ptr, kReverseIPv6Domain)) return (mDNSfalse);
4337 if (outIPv6) mDNSPlatformMemCopy(outIPv6, ipv6, 16);
4338 return (mDNStrue);
4339 }
4340 #endif // !STANDALONE
4341