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