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.