1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2015 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        /* FALLTHROUGH */
1536    }
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:  offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
2774            if (!nextbyte) nextbyte = ptr;              // Record where we got to before we started following pointers
2775            ptr = (mDNSu8 *)msg + offset;
2776            if (ptr < (mDNSu8*)msg || ptr >= end)
2777            { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
2778            if (*ptr & 0xC0)
2779            { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
2780            break;
2781        }
2782    }
2783
2784    if (nextbyte) return(nextbyte);
2785    else return(ptr);
2786}
2787
2788mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2789{
2790    mDNSu16 pktrdlength;
2791
2792    ptr = skipDomainName(msg, ptr, end);
2793    if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
2794
2795    if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2796    pktrdlength = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
2797    ptr += 10;
2798    if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2799
2800    return(ptr + pktrdlength);
2801}
2802
2803// Sanity check whether the NSEC/NSEC3 bitmap is good
2804mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len)
2805{
2806    int win, wlen;
2807
2808    while (bmap < end)
2809    {
2810        if (len < 3)
2811        {
2812            LogInfo("SanityCheckBitMap: invalid length %d", len);
2813            return mDNSNULL;
2814        }
2815
2816        win = *bmap++;
2817        wlen = *bmap++;
2818        len -= 2;
2819        if (len < wlen || wlen < 1 || wlen > 32)
2820        {
2821            LogInfo("SanityCheckBitMap: invalid window length %d", wlen);
2822            return mDNSNULL;
2823        }
2824        if (win < 0 || win >= 256)
2825        {
2826            LogInfo("SanityCheckBitMap: invalid window %d", win);
2827            return mDNSNULL;
2828        }
2829
2830        bmap += wlen;
2831        len -= wlen;
2832    }
2833    return (mDNSu8 *)bmap;
2834}
2835
2836// This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
2837// pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
2838// (domainnames are expanded to 255 bytes) when stored in memory.
2839//
2840// This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr.
2841// The caller can do this only if the names in the resource records are not compressed and validity of the
2842// resource record has already been done before. DNSSEC currently uses it this way.
2843mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end,
2844    LargeCacheRecord *const largecr, mDNSu16 rdlength)
2845{
2846    CacheRecord *const rr = &largecr->r;
2847    RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
2848
2849    switch (rr->resrec.rrtype)
2850    {
2851    case kDNSType_A:
2852        if (rdlength != sizeof(mDNSv4Addr))
2853            goto fail;
2854        rdb->ipv4.b[0] = ptr[0];
2855        rdb->ipv4.b[1] = ptr[1];
2856        rdb->ipv4.b[2] = ptr[2];
2857        rdb->ipv4.b[3] = ptr[3];
2858        break;
2859
2860    case kDNSType_NS:
2861    case kDNSType_MD:
2862    case kDNSType_MF:
2863    case kDNSType_CNAME:
2864    case kDNSType_MB:
2865    case kDNSType_MG:
2866    case kDNSType_MR:
2867    case kDNSType_PTR:
2868    case kDNSType_NSAP_PTR:
2869    case kDNSType_DNAME:
2870        if (msg)
2871        {
2872            ptr = getDomainName(msg, ptr, end, &rdb->name);
2873        }
2874        else
2875        {
2876            AssignDomainName(&rdb->name, (domainname *)ptr);
2877            ptr += DomainNameLength(&rdb->name);
2878        }
2879        if (ptr != end)
2880        {
2881            debugf("SetRData: Malformed CNAME/PTR RDATA name");
2882            goto fail;
2883        }
2884        break;
2885
2886    case kDNSType_SOA:
2887        if (msg)
2888        {
2889            ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
2890        }
2891        else
2892        {
2893            AssignDomainName(&rdb->soa.mname, (domainname *)ptr);
2894            ptr += DomainNameLength(&rdb->soa.mname);
2895        }
2896        if (!ptr)
2897        {
2898            debugf("SetRData: Malformed SOA RDATA mname");
2899            goto fail;
2900        }
2901        if (msg)
2902        {
2903            ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
2904        }
2905        else
2906        {
2907            AssignDomainName(&rdb->soa.rname, (domainname *)ptr);
2908            ptr += DomainNameLength(&rdb->soa.rname);
2909        }
2910        if (!ptr)
2911        {
2912            debugf("SetRData: Malformed SOA RDATA rname");
2913            goto fail;
2914        }
2915        if (ptr + 0x14 != end)
2916        {
2917            debugf("SetRData: Malformed SOA RDATA");
2918            goto fail;
2919        }
2920        rdb->soa.serial  = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
2921        rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
2922        rdb->soa.retry   = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
2923        rdb->soa.expire  = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
2924        rdb->soa.min     = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
2925        break;
2926
2927    case kDNSType_NULL:
2928    case kDNSType_HINFO:
2929    case kDNSType_TXT:
2930    case kDNSType_X25:
2931    case kDNSType_ISDN:
2932    case kDNSType_LOC:
2933    case kDNSType_DHCID:
2934        rr->resrec.rdlength = rdlength;
2935        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
2936        break;
2937
2938    case kDNSType_MX:
2939    case kDNSType_AFSDB:
2940    case kDNSType_RT:
2941    case kDNSType_KX:
2942        // Preference + domainname
2943        if (rdlength < 3)
2944            goto fail;
2945        rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
2946        ptr += 2;
2947        if (msg)
2948        {
2949            ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange);
2950        }
2951        else
2952        {
2953            AssignDomainName(&rdb->mx.exchange, (domainname *)ptr);
2954            ptr += DomainNameLength(&rdb->mx.exchange);
2955        }
2956        if (ptr != end)
2957        {
2958            debugf("SetRData: Malformed MX name");
2959            goto fail;
2960        }
2961        break;
2962
2963    case kDNSType_MINFO:
2964    case kDNSType_RP:
2965        // Domainname + domainname
2966        if (msg)
2967        {
2968            ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox);
2969        }
2970        else
2971        {
2972            AssignDomainName(&rdb->rp.mbox, (domainname *)ptr);
2973            ptr += DomainNameLength(&rdb->rp.mbox);
2974        }
2975        if (!ptr)
2976        {
2977            debugf("SetRData: Malformed RP mbox");
2978            goto fail;
2979        }
2980        if (msg)
2981        {
2982            ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
2983        }
2984        else
2985        {
2986            AssignDomainName(&rdb->rp.txt, (domainname *)ptr);
2987            ptr += DomainNameLength(&rdb->rp.txt);
2988        }
2989        if (ptr != end)
2990        {
2991            debugf("SetRData: Malformed RP txt");
2992            goto fail;
2993        }
2994        break;
2995
2996    case kDNSType_PX:
2997        // Preference + domainname + domainname
2998        if (rdlength < 4)
2999            goto fail;
3000        rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
3001        ptr += 2;
3002        if (msg)
3003        {
3004            ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
3005        }
3006        else
3007        {
3008            AssignDomainName(&rdb->px.map822, (domainname *)ptr);
3009            ptr += DomainNameLength(&rdb->px.map822);
3010        }
3011        if (!ptr)
3012        {
3013            debugf("SetRData: Malformed PX map822");
3014            goto fail;
3015        }
3016        if (msg)
3017        {
3018            ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
3019        }
3020        else
3021        {
3022            AssignDomainName(&rdb->px.mapx400, (domainname *)ptr);
3023            ptr += DomainNameLength(&rdb->px.mapx400);
3024        }
3025        if (ptr != end)
3026        {
3027            debugf("SetRData: Malformed PX mapx400");
3028            goto fail;
3029        }
3030        break;
3031
3032    case kDNSType_AAAA:
3033        if (rdlength != sizeof(mDNSv6Addr))
3034            goto fail;
3035        mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
3036        break;
3037
3038    case kDNSType_SRV:
3039        // Priority + weight + port + domainname
3040        if (rdlength < 7)
3041            goto fail;
3042        rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
3043        rdb->srv.weight   = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
3044        rdb->srv.port.b[0] = ptr[4];
3045        rdb->srv.port.b[1] = ptr[5];
3046        ptr += 6;
3047        if (msg)
3048        {
3049            ptr = getDomainName(msg, ptr, end, &rdb->srv.target);
3050        }
3051        else
3052        {
3053            AssignDomainName(&rdb->srv.target, (domainname *)ptr);
3054            ptr += DomainNameLength(&rdb->srv.target);
3055        }
3056        if (ptr != end)
3057        {
3058            debugf("SetRData: Malformed SRV RDATA name");
3059            goto fail;
3060        }
3061        break;
3062
3063    case kDNSType_NAPTR:
3064    {
3065        int savelen, len;
3066        domainname name;
3067        const mDNSu8 *orig = ptr;
3068
3069        // Make sure the data is parseable and within the limits. DNSSEC code looks at
3070        // the domain name in the end for a valid domainname.
3071        //
3072        // Fixed length: Order, preference (4 bytes)
3073        // Variable length: flags, service, regexp, domainname
3074
3075        if (rdlength < 8)
3076            goto fail;
3077        // Order, preference.
3078        ptr += 4;
3079        // Parse flags, Service and Regexp
3080        // length in the first byte does not include the length byte itself
3081        len = *ptr + 1;
3082        ptr += len;
3083        if (ptr >= end)
3084        {
3085            LogInfo("SetRData: Malformed NAPTR flags");
3086            goto fail;
3087        }
3088
3089        // Service
3090        len = *ptr + 1;
3091        ptr += len;
3092        if (ptr >= end)
3093        {
3094            LogInfo("SetRData: Malformed NAPTR service");
3095            goto fail;
3096        }
3097
3098        // Regexp
3099        len = *ptr + 1;
3100        ptr += len;
3101        if (ptr >= end)
3102        {
3103            LogInfo("SetRData: Malformed NAPTR regexp");
3104            goto fail;
3105        }
3106
3107        savelen = ptr - orig;
3108
3109        // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
3110        // states that for NAPTR we should decompress. We make sure that we store the full
3111        // name rather than the compressed name
3112        if (msg)
3113        {
3114            ptr = getDomainName(msg, ptr, end, &name);
3115        }
3116        else
3117        {
3118            AssignDomainName(&name, (domainname *)ptr);
3119            ptr += DomainNameLength(&name);
3120        }
3121        if (ptr != end)
3122        {
3123            LogInfo("SetRData: Malformed NAPTR RDATA name");
3124            goto fail;
3125        }
3126
3127        rr->resrec.rdlength = savelen + DomainNameLength(&name);
3128        // The uncompressed size should not exceed the limits
3129        if (rr->resrec.rdlength > MaximumRDSize)
3130        {
3131            LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->resrec.rdlength %d, "
3132                    "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
3133            goto fail;
3134        }
3135        mDNSPlatformMemCopy(rdb->data, orig, savelen);
3136        AssignDomainName((domainname *)(rdb->data + savelen), &name);
3137        break;
3138    }
3139    case kDNSType_OPT:  {
3140        mDNSu8 *dataend     = rr->resrec.rdata->u.data;
3141        rdataOPT *opt = rr->resrec.rdata->u.opt;
3142        rr->resrec.rdlength = 0;
3143        while (ptr < end && (mDNSu8 *)(opt+1) < &dataend[MaximumRDSize])
3144        {
3145            const rdataOPT *const currentopt = opt;
3146            if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; }
3147            opt->opt    = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
3148            opt->optlen = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
3149            ptr += 4;
3150            if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; }
3151            switch (opt->opt)
3152            {
3153            case kDNSOpt_LLQ:
3154                if (opt->optlen == DNSOpt_LLQData_Space - 4)
3155                {
3156                    opt->u.llq.vers  = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
3157                    opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
3158                    opt->u.llq.err   = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
3159                    mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8);
3160                    opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]);
3161                    if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
3162                        opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
3163                    opt++;
3164                }
3165                break;
3166            case kDNSOpt_Lease:
3167                if (opt->optlen == DNSOpt_LeaseData_Space - 4)
3168                {
3169                    opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
3170                    if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
3171                        opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
3172                    opt++;
3173                }
3174                break;
3175            case kDNSOpt_Owner:
3176                if (ValidOwnerLength(opt->optlen))
3177                {
3178                    opt->u.owner.vers = ptr[0];
3179                    opt->u.owner.seq  = ptr[1];
3180                    mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6);                         // 6-byte MAC address
3181                    mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6);                         // 6-byte MAC address
3182                    opt->u.owner.password = zeroEthAddr;
3183                    if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
3184                    {
3185                        mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6);                     // 6-byte MAC address
3186                        // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above
3187                        // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4
3188                        if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
3189                            mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
3190                    }
3191                    opt++;
3192                }
3193                break;
3194            case kDNSOpt_Trace:
3195                if (opt->optlen == DNSOpt_TraceData_Space - 4)
3196                {
3197                    opt->u.tracer.platf   = ptr[0];
3198                    opt->u.tracer.mDNSv   = (mDNSu32) ((mDNSu32)ptr[1] << 24 | (mDNSu32)ptr[2] << 16 | (mDNSu32)ptr[3] << 8 | ptr[4]);
3199                    opt++;
3200                }
3201                else
3202                {
3203                    opt->u.tracer.platf   = 0xFF;
3204                    opt->u.tracer.mDNSv   = 0xFFFFFFFF;
3205                    opt++;
3206                }
3207                break;
3208            }
3209            ptr += currentopt->optlen;
3210        }
3211        rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data);
3212        if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; }
3213        break;
3214    }
3215
3216    case kDNSType_NSEC: {
3217        domainname name;
3218        int len = rdlength;
3219        int bmaplen, dlen;
3220        const mDNSu8 *orig = ptr;
3221        const mDNSu8 *bmap;
3222
3223        if (msg)
3224        {
3225            ptr = getDomainName(msg, ptr, end, &name);
3226        }
3227        else
3228        {
3229            AssignDomainName(&name, (domainname *)ptr);
3230            ptr += DomainNameLength(&name);
3231        }
3232        if (!ptr)
3233        {
3234            LogInfo("SetRData: Malformed NSEC nextname");
3235            goto fail;
3236        }
3237
3238        dlen = DomainNameLength(&name);
3239
3240        // Multicast NSECs use name compression for this field unlike the unicast case which
3241        // does not use compression. And multicast case always succeeds in compression. So,
3242        // the rdlength includes only the compressed space in that case. So, can't
3243        // use the DomainNameLength of name to reduce the length here.
3244        len -= (ptr - orig);
3245        bmaplen = len;                  // Save the length of the bitmap
3246        bmap = ptr;
3247        ptr = SanityCheckBitMap(bmap, end, len);
3248        if (!ptr)
3249            goto fail;
3250        if (ptr != end)
3251        {
3252            LogInfo("SetRData: Malformed NSEC length not right");
3253            goto fail;
3254        }
3255
3256        // Initialize the right length here. When we call SetNewRData below which in turn calls
3257        // GetRDLength and for NSEC case, it assumes that rdlength is intitialized
3258        rr->resrec.rdlength = DomainNameLength(&name) + bmaplen;
3259
3260        // Do we have space after the name expansion ?
3261        if (rr->resrec.rdlength > MaximumRDSize)
3262        {
3263            LogInfo("SetRData: Malformed NSEC rdlength %d, rr->resrec.rdlength %d, "
3264                    "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
3265            goto fail;
3266        }
3267        AssignDomainName((domainname *)rdb->data, &name);
3268        mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
3269        break;
3270    }
3271    case kDNSType_NSEC3:
3272    {
3273        rdataNSEC3 *nsec3 = (rdataNSEC3 *)ptr;
3274        mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
3275        int hashLength, bitmaplen;
3276
3277        if (rdlength < NSEC3_FIXED_SIZE + 1)
3278        {
3279            LogInfo("SetRData: NSEC3 too small length %d", rdlength);
3280            goto fail;
3281        }
3282        if (nsec3->alg != SHA1_DIGEST_TYPE)
3283        {
3284            LogInfo("SetRData: nsec3 alg %d not supported", nsec3->alg);
3285            goto fail;
3286        }
3287        if (swap16(nsec3->iterations) > NSEC3_MAX_ITERATIONS)
3288        {
3289            LogInfo("SetRData: nsec3 iteration count %d too big", swap16(nsec3->iterations));
3290            goto fail;
3291        }
3292        p += nsec3->saltLength;
3293        // There should at least be one byte beyond saltLength
3294        if (p >= end)
3295        {
3296            LogInfo("SetRData: nsec3 too small, at saltlength %d, p %p, end %p", nsec3->saltLength, p, end);
3297            goto fail;
3298        }
3299        // p is pointing at hashLength
3300        hashLength = (int)*p++;
3301        if (!hashLength)
3302        {
3303            LogInfo("SetRData: hashLength zero");
3304            goto fail;
3305        }
3306        p += hashLength;
3307        if (p > end)
3308        {
3309            LogInfo("SetRData: nsec3 too small, at hashLength %d, p %p, end %p", hashLength, p, end);
3310            goto fail;
3311        }
3312
3313        bitmaplen = rdlength - (int)(p - ptr);
3314        p = SanityCheckBitMap(p, end, bitmaplen);
3315        if (!p)
3316            goto fail;
3317        rr->resrec.rdlength = rdlength;
3318        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3319        break;
3320    }
3321    case kDNSType_TKEY:
3322    case kDNSType_TSIG:
3323    {
3324        domainname name;
3325        int dlen, rlen;
3326
3327        // The name should not be compressed. But we take the conservative approach
3328        // and uncompress the name before we store it.
3329        if (msg)
3330        {
3331            ptr = getDomainName(msg, ptr, end, &name);
3332        }
3333        else
3334        {
3335            AssignDomainName(&name, (domainname *)ptr);
3336            ptr += DomainNameLength(&name);
3337        }
3338        if (!ptr)
3339        {
3340            LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->resrec.rrtype);
3341            goto fail;
3342        }
3343        dlen = DomainNameLength(&name);
3344        rlen = end - ptr;
3345        rr->resrec.rdlength = dlen + rlen;
3346        if (rr->resrec.rdlength > MaximumRDSize)
3347        {
3348            LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->resrec.rdlength %d, "
3349                    "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
3350            goto fail;
3351        }
3352        AssignDomainName((domainname *)rdb->data, &name);
3353        mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen);
3354        break;
3355    }
3356    case kDNSType_RRSIG:
3357    {
3358        const mDNSu8 *sig = ptr + RRSIG_FIXED_SIZE;
3359        const mDNSu8 *orig = sig;
3360        domainname name;
3361        if (rdlength < RRSIG_FIXED_SIZE + 1)
3362        {
3363            LogInfo("SetRData: RRSIG too small length %d", rdlength);
3364            goto fail;
3365        }
3366        if (msg)
3367        {
3368            sig = getDomainName(msg, sig, end, &name);
3369        }
3370        else
3371        {
3372            AssignDomainName(&name, (domainname *)sig);
3373            sig += DomainNameLength(&name);
3374        }
3375        if (!sig)
3376        {
3377            LogInfo("SetRData: Malformed RRSIG record");
3378            goto fail;
3379        }
3380
3381        if ((sig - orig) != DomainNameLength(&name))
3382        {
3383            LogInfo("SetRData: Malformed RRSIG record, signer name compression");
3384            goto fail;
3385        }
3386        // Just ensure that we have at least one byte of the signature
3387        if (sig + 1 >= end)
3388        {
3389            LogInfo("SetRData: Not enough bytes for signature type %d", rr->resrec.rrtype);
3390            goto fail;
3391        }
3392        rr->resrec.rdlength = rdlength;
3393        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3394        break;
3395    }
3396    case kDNSType_DNSKEY:
3397    {
3398        if (rdlength < DNSKEY_FIXED_SIZE + 1)
3399        {
3400            LogInfo("SetRData: DNSKEY too small length %d", rdlength);
3401            goto fail;
3402        }
3403        rr->resrec.rdlength = rdlength;
3404        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3405        break;
3406    }
3407    case kDNSType_DS:
3408    {
3409        if (rdlength < DS_FIXED_SIZE + 1)
3410        {
3411            LogInfo("SetRData: DS too small length %d", rdlength);
3412            goto fail;
3413        }
3414        rr->resrec.rdlength = rdlength;
3415        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3416        break;
3417    }
3418    default:
3419        debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
3420               rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
3421        // Note: Just because we don't understand the record type, that doesn't
3422        // mean we fail. The DNS protocol specifies rdlength, so we can
3423        // safely skip over unknown records and ignore them.
3424        // We also grab a binary copy of the rdata anyway, since the caller
3425        // might know how to interpret it even if we don't.
3426        rr->resrec.rdlength = rdlength;
3427        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3428        break;
3429    }
3430    return mDNStrue;
3431fail:
3432    return mDNSfalse;
3433}
3434
3435mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
3436                                                const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
3437{
3438    CacheRecord *const rr = &largecr->r;
3439    mDNSu16 pktrdlength;
3440
3441    if (largecr == &m->rec && m->rec.r.resrec.RecordType)
3442        LogFatalError("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
3443
3444    rr->next              = mDNSNULL;
3445    rr->resrec.name       = &largecr->namestorage;
3446
3447    rr->NextInKAList      = mDNSNULL;
3448    rr->TimeRcvd          = m ? m->timenow : 0;
3449    rr->DelayDelivery     = 0;
3450    rr->NextRequiredQuery = m ? m->timenow : 0;     // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
3451    rr->LastUsed          = m ? m->timenow : 0;
3452    rr->CRActiveQuestion  = mDNSNULL;
3453    rr->UnansweredQueries = 0;
3454    rr->LastUnansweredTime= 0;
3455    rr->NextInCFList      = mDNSNULL;
3456
3457    rr->resrec.InterfaceID       = InterfaceID;
3458    rr->resrec.rDNSServer = mDNSNULL;
3459
3460    ptr = getDomainName(msg, ptr, end, &largecr->namestorage);      // Will bail out correctly if ptr is NULL
3461    if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
3462    rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
3463
3464    if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
3465
3466    rr->resrec.rrtype            = (mDNSu16) ((mDNSu16)ptr[0] <<  8 | ptr[1]);
3467    rr->resrec.rrclass           = (mDNSu16)(((mDNSu16)ptr[2] <<  8 | ptr[3]) & kDNSClass_Mask);
3468    rr->resrec.rroriginalttl     = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
3469    if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
3470        rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
3471    // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
3472    // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
3473    pktrdlength           = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
3474
3475    // If mDNS record has cache-flush bit set, we mark it unique
3476    // For uDNS records, all are implicitly deemed unique (a single DNS server is always
3477    // authoritative for the entire RRSet), unless this is a truncated response
3478    if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC)))
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
3632mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
3633{
3634    int i;
3635    LogInfo("%2d %s", count, label);
3636    for (i = 0; i < count && ptr; i++)
3637    {
3638        // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
3639        // but since it's only used for debugging (and probably only on OS X, not on
3640        // embedded systems) putting a 9kB object on the stack isn't a big problem.
3641        LargeCacheRecord largecr;
3642        ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
3643        if (ptr)
3644            LogInfo("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
3645    }
3646    if (!ptr)
3647        LogInfo("DumpRecords: ERROR: Premature end of packet data");
3648    return(ptr);
3649}
3650
3651#define DNS_OP_Name(X) (                              \
3652        (X) == kDNSFlag0_OP_StdQuery ? ""         :       \
3653        (X) == kDNSFlag0_OP_Iquery   ? "Iquery "  :       \
3654        (X) == kDNSFlag0_OP_Status   ? "Status "  :       \
3655        (X) == kDNSFlag0_OP_Unused3  ? "Unused3 " :       \
3656        (X) == kDNSFlag0_OP_Notify   ? "Notify "  :       \
3657        (X) == kDNSFlag0_OP_Update   ? "Update "  :       \
3658        (X) == kDNSFlag0_OP_Subscribe? "Subscribe":       \
3659        (X) == kDNSFlag0_OP_UnSubscribe? "UnSubscribe" : "?? " )
3660
3661#define DNS_RC_Name(X) (                             \
3662        (X) == kDNSFlag1_RC_NoErr    ? "NoErr"    :      \
3663        (X) == kDNSFlag1_RC_FormErr  ? "FormErr"  :      \
3664        (X) == kDNSFlag1_RC_ServFail ? "ServFail" :      \
3665        (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" :      \
3666        (X) == kDNSFlag1_RC_NotImpl  ? "NotImpl"  :      \
3667        (X) == kDNSFlag1_RC_Refused  ? "Refused"  :      \
3668        (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" :      \
3669        (X) == kDNSFlag1_RC_YXRRSet  ? "YXRRSet"  :      \
3670        (X) == kDNSFlag1_RC_NXRRSet  ? "NXRRSet"  :      \
3671        (X) == kDNSFlag1_RC_NotAuth  ? "NotAuth"  :      \
3672        (X) == kDNSFlag1_RC_NotZone  ? "NotZone"  : "??" )
3673
3674// Note: DumpPacket expects the packet header fields in host byte order, not network byte order
3675mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
3676                           const mDNSAddr *srcaddr, mDNSIPPort srcport,
3677                           const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
3678{
3679    mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
3680    const mDNSu8 *ptr = msg->data;
3681    int i;
3682    DNSQuestion q;
3683    char tbuffer[64], sbuffer[64], dbuffer[64] = "";
3684    if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received"                        )] = 0;
3685    else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0;
3686    if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port "        )] = 0;
3687    else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
3688    if (dstaddr || !mDNSIPPortIsZero(dstport))
3689        dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0;
3690
3691    LogInfo("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
3692           tbuffer, transport,
3693           DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
3694           msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
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           end - msg->data,
3706           sbuffer, mDNSVal16(srcport), dbuffer,
3707           (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
3708           );
3709
3710    LogInfo("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
3711    for (i = 0; i < msg->h.numQuestions && ptr; i++)
3712    {
3713        ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
3714        if (ptr) LogInfo("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
3715    }
3716    ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers,     IsUpdate ? "Prerequisites" : "Answers");
3717    ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates"       : "Authorities");
3718          DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
3719    LogInfo("--------------");
3720}
3721
3722// ***************************************************************************
3723#if COMPILER_LIKES_PRAGMA_MARK
3724#pragma mark -
3725#pragma mark - Packet Sending Functions
3726#endif
3727
3728#ifdef UNIT_TEST
3729// Run the unit test of mDNSSendDNSMessage
3730UNITTEST_SENDDNSMESSAGE
3731#else
3732// Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
3733struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
3734// Stub definition of UDPSocket_struct so we can access port field. (Rest of UDPSocket_struct is platform-dependent.)
3735struct UDPSocket_struct { mDNSIPPort     port;  /* ... */ };
3736
3737// Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
3738// is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
3739mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
3740                                      mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
3741                                      mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo,
3742                                      mDNSBool useBackgroundTrafficClass)
3743{
3744    mStatus status = mStatus_NoError;
3745    const mDNSu16 numAdditionals = msg->h.numAdditionals;
3746    mDNSu8 *newend;
3747    mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
3748
3749#if APPLE_OSX_mDNSResponder
3750    // maintain outbound packet statistics
3751    if (mDNSOpaque16IsZero(msg->h.id))
3752        m->MulticastPacketsSent++;
3753    else
3754        m->UnicastPacketsSent++;
3755#endif // APPLE_OSX_mDNSResponder
3756
3757    // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code)
3758    if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
3759    {
3760        LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
3761        return mStatus_BadParamErr;
3762    }
3763
3764    newend = putHINFO(m, msg, end, authInfo, limit);
3765    if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal
3766    else end = newend;
3767
3768    // Put all the integer values in IETF byte-order (MSB first, LSB second)
3769    SwapDNSHeaderBytes(msg);
3770
3771    if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0);    // DNSDigest_SignMessage operates on message in network byte order
3772    if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
3773    else
3774    {
3775        // Send the packet on the wire
3776        if (!sock)
3777            status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport, useBackgroundTrafficClass);
3778        else
3779        {
3780            mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
3781            mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
3782            char *buf;
3783            long nsent;
3784
3785            // Try to send them in one packet if we can allocate enough memory
3786            buf = mDNSPlatformMemAllocate(msglen + 2);
3787            if (buf)
3788            {
3789                buf[0] = lenbuf[0];
3790                buf[1] = lenbuf[1];
3791                mDNSPlatformMemCopy(buf+2, msg, msglen);
3792                nsent = mDNSPlatformWriteTCP(sock, buf, msglen+2);
3793                if (nsent != (msglen + 2))
3794                {
3795                    LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen);
3796                    status = mStatus_ConnFailed;
3797                }
3798                mDNSPlatformMemFree(buf);
3799            }
3800            else
3801            {
3802                nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2);
3803                if (nsent != 2)
3804                {
3805                    LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2);
3806                    status = mStatus_ConnFailed;
3807                }
3808                else
3809                {
3810                    nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
3811                    if (nsent != msglen)
3812                    {
3813                        LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen);
3814                        status = mStatus_ConnFailed;
3815                    }
3816                }
3817            }
3818        }
3819    }
3820
3821    // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
3822    SwapDNSHeaderBytes(msg);
3823
3824    // Dump the packet with the HINFO and TSIG
3825    if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
3826        DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end);
3827
3828    // put the number of additionals back the way it was
3829    msg->h.numAdditionals = numAdditionals;
3830
3831    return(status);
3832}
3833#endif // UNIT_TEST
3834
3835// ***************************************************************************
3836#if COMPILER_LIKES_PRAGMA_MARK
3837#pragma mark -
3838#pragma mark - RR List Management & Task Management
3839#endif
3840
3841mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname, int lineno)
3842{
3843    // MUST grab the platform lock FIRST!
3844    mDNSPlatformLock(m);
3845
3846    // Normally, mDNS_reentrancy is zero and so is mDNS_busy
3847    // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
3848    // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
3849    // If mDNS_busy != mDNS_reentrancy that's a bad sign
3850    if (m->mDNS_busy != m->mDNS_reentrancy)
3851        LogFatalError("%s,%d: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld) (last %s,%d)", functionname, lineno, m->mDNS_busy, m->mDNS_reentrancy, m->mDNS_Lock_functionname, m->mDNS_Lock_lineno);
3852
3853    // If this is an initial entry into the mDNSCore code, set m->timenow
3854    // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3855    if (m->mDNS_busy == 0)
3856    {
3857        if (m->timenow)
3858            LogMsg("%s,%d: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, lineno, m->timenow, mDNS_TimeNow_NoLock(m));
3859        m->timenow = mDNS_TimeNow_NoLock(m);
3860        if (m->timenow == 0) m->timenow = 1;
3861    }
3862    else if (m->timenow == 0)
3863    {
3864        LogMsg("%s,%d: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, lineno, m->mDNS_busy);
3865        m->timenow = mDNS_TimeNow_NoLock(m);
3866        if (m->timenow == 0) m->timenow = 1;
3867    }
3868
3869    if (m->timenow_last - m->timenow > 0)
3870    {
3871        m->timenow_adjust += m->timenow_last - m->timenow;
3872        LogMsg("%s,%d: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, lineno, m->timenow_last - m->timenow, m->timenow_adjust);
3873        m->timenow = m->timenow_last;
3874    }
3875    m->timenow_last = m->timenow;
3876
3877    // Increment mDNS_busy so we'll recognise re-entrant calls
3878    m->mDNS_busy++;
3879    m->mDNS_Lock_functionname = functionname;
3880    m->mDNS_Lock_lineno = lineno;
3881}
3882
3883mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m)
3884{
3885    AuthRecord *rr;
3886    for (rr = m->NewLocalRecords; rr; rr = rr->next)
3887        if (LocalRecordReady(rr)) return rr;
3888    return mDNSNULL;
3889}
3890
3891mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
3892{
3893    mDNSs32 e = m->timenow + FutureTime;
3894    if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
3895    if (m->NewQuestions)
3896    {
3897        if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
3898        else return(m->timenow);
3899    }
3900    if (m->NewLocalOnlyQuestions) return(m->timenow);
3901    if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
3902    if (m->NewLocalOnlyRecords) return(m->timenow);
3903    if (m->SPSProxyListChanged) return(m->timenow);
3904    if (m->LocalRemoveEvents) return(m->timenow);
3905
3906#ifndef UNICAST_DISABLED
3907    if (e - m->NextuDNSEvent         > 0) e = m->NextuDNSEvent;
3908    if (e - m->NextScheduledNATOp    > 0) e = m->NextScheduledNATOp;
3909    if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate;
3910#endif
3911
3912    if (e - m->NextCacheCheck        > 0) e = m->NextCacheCheck;
3913    if (e - m->NextScheduledSPS      > 0) e = m->NextScheduledSPS;
3914    if (e - m->NextScheduledKA       > 0) e = m->NextScheduledKA;
3915
3916#if BONJOUR_ON_DEMAND
3917    if (m->NextBonjourDisableTime && (e - m->NextBonjourDisableTime > 0)) e = m->NextBonjourDisableTime;
3918#endif // BONJOUR_ON_DEMAND
3919
3920    // NextScheduledSPRetry only valid when DelaySleep not set
3921    if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
3922    if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
3923
3924    if (m->SuppressSending)
3925    {
3926        if (e - m->SuppressSending       > 0) e = m->SuppressSending;
3927    }
3928    else
3929    {
3930        if (e - m->NextScheduledQuery    > 0) e = m->NextScheduledQuery;
3931        if (e - m->NextScheduledProbe    > 0) e = m->NextScheduledProbe;
3932        if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
3933    }
3934    if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
3935
3936    if (m->NextBLEServiceTime && (e - m->NextBLEServiceTime > 0)) e = m->NextBLEServiceTime;
3937
3938    return(e);
3939}
3940
3941#define LogTSE TSE++,LogMsg
3942
3943mDNSexport void ShowTaskSchedulingError(mDNS *const m)
3944{
3945    int TSE = 0;
3946    AuthRecord *rr;
3947    mDNS_Lock(m);
3948
3949    LogMsg("Task Scheduling Error: *** Continuously busy for more than a second");
3950
3951    // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3952
3953    if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
3954        LogTSE("Task Scheduling Error: NewQuestion %##s (%s)",
3955               m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
3956
3957    if (m->NewLocalOnlyQuestions)
3958        LogTSE("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3959               m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
3960
3961    if (m->NewLocalRecords)
3962    {
3963        rr = AnyLocalRecordReady(m);
3964        if (rr) LogTSE("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
3965    }
3966
3967    if (m->NewLocalOnlyRecords) LogTSE("Task Scheduling Error: NewLocalOnlyRecords");
3968
3969    if (m->SPSProxyListChanged) LogTSE("Task Scheduling Error: SPSProxyListChanged");
3970
3971    if (m->LocalRemoveEvents) LogTSE("Task Scheduling Error: LocalRemoveEvents");
3972
3973#ifndef UNICAST_DISABLED
3974    if (m->timenow - m->NextuDNSEvent         >= 0)
3975        LogTSE("Task Scheduling Error: m->NextuDNSEvent %d",         m->timenow - m->NextuDNSEvent);
3976    if (m->timenow - m->NextScheduledNATOp    >= 0)
3977        LogTSE("Task Scheduling Error: m->NextScheduledNATOp %d",    m->timenow - m->NextScheduledNATOp);
3978    if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0)
3979        LogTSE("Task Scheduling Error: m->NextSRVUpdate %d",         m->timenow - m->NextSRVUpdate);
3980#endif
3981
3982    if (m->timenow - m->NextCacheCheck        >= 0)
3983        LogTSE("Task Scheduling Error: m->NextCacheCheck %d",        m->timenow - m->NextCacheCheck);
3984    if (m->timenow - m->NextScheduledSPS      >= 0)
3985        LogTSE("Task Scheduling Error: m->NextScheduledSPS %d",      m->timenow - m->NextScheduledSPS);
3986    if (m->timenow - m->NextScheduledKA       >= 0)
3987        LogTSE("Task Scheduling Error: m->NextScheduledKA %d",      m->timenow - m->NextScheduledKA);
3988    if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
3989        LogTSE("Task Scheduling Error: m->NextScheduledSPRetry %d",  m->timenow - m->NextScheduledSPRetry);
3990    if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
3991        LogTSE("Task Scheduling Error: m->DelaySleep %d",            m->timenow - m->DelaySleep);
3992
3993    if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
3994        LogTSE("Task Scheduling Error: m->SuppressSending %d",       m->timenow - m->SuppressSending);
3995    if (m->timenow - m->NextScheduledQuery    >= 0)
3996        LogTSE("Task Scheduling Error: m->NextScheduledQuery %d",    m->timenow - m->NextScheduledQuery);
3997    if (m->timenow - m->NextScheduledProbe    >= 0)
3998        LogTSE("Task Scheduling Error: m->NextScheduledProbe %d",    m->timenow - m->NextScheduledProbe);
3999    if (m->timenow - m->NextScheduledResponse >= 0)
4000        LogTSE("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
4001    if (m->timenow - m->NextScheduledStopTime >= 0)
4002        LogTSE("Task Scheduling Error: m->NextScheduledStopTime %d", m->timenow - m->NextScheduledStopTime);
4003
4004    if (m->timenow - m->NextScheduledEvent    >= 0)
4005        LogTSE("Task Scheduling Error: m->NextScheduledEvent %d",    m->timenow - m->NextScheduledEvent);
4006
4007    if (m->NetworkChanged && m->timenow - m->NetworkChanged >= 0)
4008        LogTSE("Task Scheduling Error: NetworkChanged %d",           m->timenow - m->NetworkChanged);
4009
4010    if (!TSE) LogMsg("Task Scheduling Error: *** No likely causes identified");
4011    else LogMsg("Task Scheduling Error: *** %d potential cause%s identified (significant only if the same cause consistently appears)", TSE, TSE > 1 ? "s" : "");
4012
4013    mDNS_Unlock(m);
4014}
4015
4016mDNSexport void mDNS_Unlock_(mDNS *const m, const char *const functionname, int lineno)
4017{
4018    // Decrement mDNS_busy
4019    m->mDNS_busy--;
4020
4021    // Check for locking failures
4022    if (m->mDNS_busy != m->mDNS_reentrancy)
4023        LogFatalError("%s,%d: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld) (last %s,%d)", functionname, lineno, m->mDNS_busy,
4024	    m->mDNS_reentrancy, m->mDNS_Lock_functionname, m->mDNS_Lock_lineno);
4025
4026    // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
4027    if (m->mDNS_busy == 0)
4028    {
4029        m->NextScheduledEvent = GetNextScheduledEvent(m);
4030        if (m->timenow == 0) LogMsg("%s,%d: mDNS_Unlock: ERROR! m->timenow aready zero", functionname, lineno);
4031        m->timenow = 0;
4032    }
4033
4034    // MUST release the platform lock LAST!
4035    mDNSPlatformUnlock(m);
4036}
4037
4038// ***************************************************************************
4039#if COMPILER_LIKES_PRAGMA_MARK
4040#pragma mark -
4041#pragma mark - Specialized mDNS version of vsnprintf
4042#endif
4043
4044static const struct mDNSprintf_format
4045{
4046    unsigned leftJustify : 1;
4047    unsigned forceSign : 1;
4048    unsigned zeroPad : 1;
4049    unsigned havePrecision : 1;
4050    unsigned hSize : 1;
4051    unsigned lSize : 1;
4052    char altForm;
4053    char sign;              // +, - or space
4054    unsigned int fieldWidth;
4055    unsigned int precision;
4056} mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
4057
4058mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
4059{
4060    mDNSu32 nwritten = 0;
4061    int c;
4062    if (buflen == 0) return(0);
4063    buflen--;       // Pre-reserve one space in the buffer for the terminating null
4064    if (buflen == 0) goto exit;
4065
4066    for (c = *fmt; c != 0; c = *++fmt)
4067    {
4068        unsigned long n;
4069		if (c != '%')
4070        {
4071            *sbuffer++ = (char)c;
4072            if (++nwritten >= buflen) goto exit;
4073        }
4074        else
4075        {
4076            unsigned int i=0, j;
4077            // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
4078            // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
4079            // The size needs to be enough for a 256-byte domain name plus some error text.
4080            #define mDNS_VACB_Size 300
4081            char mDNS_VACB[mDNS_VACB_Size];
4082            #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
4083            #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
4084            char *s = mDNS_VACB_Lim, *digits;
4085            struct mDNSprintf_format F = mDNSprintf_format_default;
4086
4087            while (1)   //  decode flags
4088            {
4089                c = *++fmt;
4090                if      (c == '-') F.leftJustify = 1;
4091                else if (c == '+') F.forceSign = 1;
4092                else if (c == ' ') F.sign = ' ';
4093                else if (c == '#') F.altForm++;
4094                else if (c == '0') F.zeroPad = 1;
4095                else break;
4096            }
4097
4098            if (c == '*')   //  decode field width
4099            {
4100                int f = va_arg(arg, int);
4101                if (f < 0) { f = -f; F.leftJustify = 1; }
4102                F.fieldWidth = (unsigned int)f;
4103                c = *++fmt;
4104            }
4105            else
4106            {
4107                for (; c >= '0' && c <= '9'; c = *++fmt)
4108                    F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
4109            }
4110
4111            if (c == '.')   //  decode precision
4112            {
4113                if ((c = *++fmt) == '*')
4114                { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
4115                else for (; c >= '0' && c <= '9'; c = *++fmt)
4116                        F.precision = (10 * F.precision) + (c - '0');
4117                F.havePrecision = 1;
4118            }
4119
4120            if (F.leftJustify) F.zeroPad = 0;
4121
4122conv:
4123            switch (c)  //  perform appropriate conversion
4124            {
4125            case 'h':  F.hSize = 1; c = *++fmt; goto conv;
4126            case 'l':       // fall through
4127            case 'L':  F.lSize = 1; c = *++fmt; goto conv;
4128            case 'd':
4129            case 'i':  if (F.lSize) n = (unsigned long)va_arg(arg, long);
4130                else n = (unsigned long)va_arg(arg, int);
4131                if (F.hSize) n = (short) n;
4132                if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
4133                else if (F.forceSign) F.sign = '+';
4134                goto decimal;
4135            case 'u':  if (F.lSize) n = va_arg(arg, unsigned long);
4136                else n = va_arg(arg, unsigned int);
4137                if (F.hSize) n = (unsigned short) n;
4138                F.sign = 0;
4139                goto decimal;
4140decimal:    if (!F.havePrecision)
4141                {
4142                    if (F.zeroPad)
4143                    {
4144                        F.precision = F.fieldWidth;
4145                        if (F.sign) --F.precision;
4146                    }
4147                    if (F.precision < 1) F.precision = 1;
4148                }
4149                if (F.precision > mDNS_VACB_Size - 1)
4150                    F.precision = mDNS_VACB_Size - 1;
4151                for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
4152                for (; i < F.precision; i++) *--s = '0';
4153                if (F.sign) { *--s = F.sign; i++; }
4154                break;
4155
4156            case 'o':  if (F.lSize) n = va_arg(arg, unsigned long);
4157                else n = va_arg(arg, unsigned int);
4158                if (F.hSize) n = (unsigned short) n;
4159                if (!F.havePrecision)
4160                {
4161                    if (F.zeroPad) F.precision = F.fieldWidth;
4162                    if (F.precision < 1) F.precision = 1;
4163                }
4164                if (F.precision > mDNS_VACB_Size - 1)
4165                    F.precision = mDNS_VACB_Size - 1;
4166                for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
4167                if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
4168                for (; i < F.precision; i++) *--s = '0';
4169                break;
4170
4171            case 'a':  {
4172                unsigned char *a = va_arg(arg, unsigned char *);
4173                if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
4174                else
4175                {
4176                    s = mDNS_VACB;              // Adjust s to point to the start of the buffer, not the end
4177                    if (F.altForm)
4178                    {
4179                        mDNSAddr *ip = (mDNSAddr*)a;
4180                        switch (ip->type)
4181                        {
4182                        case mDNSAddrType_IPv4: F.precision =  4; a = (unsigned char *)&ip->ip.v4; break;
4183                        case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
4184                        default:                F.precision =  0; break;
4185                        }
4186                    }
4187                    if (F.altForm && !F.precision)
4188                        i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "��ZERO ADDRESS��");
4189                    else switch (F.precision)
4190                        {
4191                        case  4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
4192                                                   a[0], a[1], a[2], a[3]); break;
4193                        case  6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
4194                                                   a[0], a[1], a[2], a[3], a[4], a[5]); break;
4195                        case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
4196                                                   "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
4197                                                   a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
4198                                                   a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
4199                        default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
4200                                                   " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
4201                        }
4202                }
4203            }
4204            break;
4205
4206            case 'p':  F.havePrecision = F.lSize = 1;
4207                F.precision = sizeof(void*) * 2;                // 8 characters on 32-bit; 16 characters on 64-bit
4208            case 'X':  digits = "0123456789ABCDEF";
4209                goto hexadecimal;
4210            case 'x':  digits = "0123456789abcdef";
4211hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
4212                else n = va_arg(arg, unsigned int);
4213                if (F.hSize) n = (unsigned short) n;
4214                if (!F.havePrecision)
4215                {
4216                    if (F.zeroPad)
4217                    {
4218                        F.precision = F.fieldWidth;
4219                        if (F.altForm) F.precision -= 2;
4220                    }
4221                    if (F.precision < 1) F.precision = 1;
4222                }
4223                if (F.precision > mDNS_VACB_Size - 1)
4224                    F.precision = mDNS_VACB_Size - 1;
4225                for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
4226                for (; i < F.precision; i++) *--s = '0';
4227                if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
4228                break;
4229
4230            case 'c':  *--s = (char)va_arg(arg, int); i = 1; break;
4231
4232            case 's':  s = va_arg(arg, char *);
4233                if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
4234                else switch (F.altForm)
4235                    {
4236                    case 0: i=0;
4237                        if (!F.havePrecision)                               // C string
4238                            while (s[i]) i++;
4239                        else
4240                        {
4241                            while ((i < F.precision) && s[i]) i++;
4242                            // Make sure we don't truncate in the middle of a UTF-8 character
4243                            // If last character we got was any kind of UTF-8 multi-byte character,
4244                            // then see if we have to back up.
4245                            // This is not as easy as the similar checks below, because
4246                            // here we can't assume it's safe to examine the *next* byte, so we
4247                            // have to confine ourselves to working only backwards in the string.
4248                            j = i;                      // Record where we got to
4249                            // Now, back up until we find first non-continuation-char
4250                            while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
4251                            // Now s[i-1] is the first non-continuation-char
4252                            // and (j-i) is the number of continuation-chars we found
4253                            if (i>0 && (s[i-1] & 0xC0) == 0xC0)                 // If we found a start-char
4254                            {
4255                                i--;                        // Tentatively eliminate this start-char as well
4256                                // Now (j-i) is the number of characters we're considering eliminating.
4257                                // To be legal UTF-8, the start-char must contain (j-i) one-bits,
4258                                // followed by a zero bit. If we shift it right by (7-(j-i)) bits
4259                                // (with sign extension) then the result has to be 0xFE.
4260                                // If this is right, then we reinstate the tentatively eliminated bytes.
4261                                if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
4262                            }
4263                        }
4264                        break;
4265                    case 1: i = (unsigned char) *s++; break;                // Pascal string
4266                    case 2: {                                               // DNS label-sequence name
4267                        unsigned char *a = (unsigned char *)s;
4268                        s = mDNS_VACB;                  // Adjust s to point to the start of the buffer, not the end
4269                        if (*a == 0) *s++ = '.';                    // Special case for root DNS name
4270                        while (*a)
4271                        {
4272                            char buf[63*4+1];
4273                            if (*a > 63)
4274                            { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
4275                            if (s + *a >= &mDNS_VACB[254])
4276                            { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
4277                            // Need to use ConvertDomainLabelToCString to do proper escaping here,
4278                            // so it's clear what's a literal dot and what's a label separator
4279                            ConvertDomainLabelToCString((domainlabel*)a, buf);
4280                            s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
4281                            a += 1 + *a;
4282                        }
4283                        i = (mDNSu32)(s - mDNS_VACB);
4284                        s = mDNS_VACB;                  // Reset s back to the start of the buffer
4285                        break;
4286                    }
4287                    }
4288                // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
4289                if (F.havePrecision && i > F.precision)
4290                { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
4291                break;
4292
4293            case 'n':  s = va_arg(arg, char *);
4294                if      (F.hSize) *(short *) s = (short)nwritten;
4295                else if (F.lSize) *(long  *) s = (long)nwritten;
4296                else *(int   *) s = (int)nwritten;
4297                continue;
4298
4299            default:    s = mDNS_VACB;
4300                i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
4301
4302            case '%':  *sbuffer++ = (char)c;
4303                if (++nwritten >= buflen) goto exit;
4304                break;
4305            }
4306
4307            if (i < F.fieldWidth && !F.leftJustify)         // Pad on the left
4308                do  {
4309                    *sbuffer++ = ' ';
4310                    if (++nwritten >= buflen) goto exit;
4311                } while (i < --F.fieldWidth);
4312
4313            // Make sure we don't truncate in the middle of a UTF-8 character.
4314            // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
4315            // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
4316            // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
4317            // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
4318            if (i > buflen - nwritten)
4319            { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
4320            for (j=0; j<i; j++) *sbuffer++ = *s++;          // Write the converted result
4321            nwritten += i;
4322            if (nwritten >= buflen) goto exit;
4323
4324            for (; i < F.fieldWidth; i++)                   // Pad on the right
4325            {
4326                *sbuffer++ = ' ';
4327                if (++nwritten >= buflen) goto exit;
4328            }
4329        }
4330    }
4331exit:
4332    *sbuffer++ = 0;
4333    return(nwritten);
4334}
4335
4336mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
4337{
4338    mDNSu32 length;
4339
4340    va_list ptr;
4341    va_start(ptr,fmt);
4342    length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
4343    va_end(ptr);
4344
4345    return(length);
4346}
4347