source: rtems-libbsd/mDNSResponder/mDNSCore/DNSCommon.c @ 9449f15

4.1155-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since 9449f15 was 9449f15, checked in by Sebastian Huber <sebastian.huber@…>, on 01/30/14 at 12:52:13

mDNS: Import

The sources can be obtained via:

http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz

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