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