]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSCore/DNSCommon.c
mDNSResponder-108.4.tar.gz
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
index 4116c1cdb1339ffcbe4418964a274b6779ee18d9..e16b4aefc51cdb751f47e06e9d126011d46841e1 100644 (file)
@@ -1,4 +1,5 @@
-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
  * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
     Change History (most recent first):
 
 $Log: DNSCommon.c,v $
+Revision 1.96.2.1  2006/10/31 02:50:16  cheshire
+<rdar://problem/4683163> mDNSResponder insufficiently defensive against malformed browsing PTR responses
+
+Revision 1.96  2006/03/10 21:51:42  cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Split out SameRDataBody() into a separate routine so it can be called from other code
+
+Revision 1.95  2006/03/08 22:43:11  cheshire
+Use "localdomain" symbol instead of literal string
+
+Revision 1.94  2006/03/02 21:59:55  cheshire
+<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
+Improve sanity checks & debugging support in GetLargeResourceRecord()
+
+Revision 1.93  2006/03/02 20:30:47  cheshire
+Improved GetRRDisplayString to also show priority, weight, and port for SRV records
+
+Revision 1.92  2005/09/16 21:06:49  cheshire
+Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
+
+Revision 1.91  2005/07/10 22:10:37  cheshire
+The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to
+hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that
+large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires.
+
+Revision 1.90  2005/03/21 00:33:51  shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.89  2005/03/17 18:59:38  ksekar
+<rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows
+
+Revision 1.88  2005/03/16 00:42:32  ksekar
+<rdar://problem/4012279> Long-lived queries not working on Windows
+
+Revision 1.87  2005/02/25 04:21:00  cheshire
+<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+
+Revision 1.86  2005/02/18 00:43:12  cheshire
+<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
+
+Revision 1.85  2005/02/10 22:35:17  cheshire
+<rdar://problem/3727944> Update name
+
+Revision 1.84  2005/02/03 00:44:38  cheshire
+<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
+
 Revision 1.83  2005/01/27 22:57:55  cheshire
 Fix compile errors on gcc4
 
@@ -55,7 +102,7 @@ Revision 1.74  2004/12/09 22:49:15  ksekar
 <rdar://problem/3913653> Wide-Area Goodbyes broken
 
 Revision 1.73  2004/12/07 22:49:06  cheshire
-<rdar://problem/3908850> BIND doesn't like zero-length rdata
+<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
 
 Revision 1.72  2004/12/06 21:15:20  ksekar
 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
@@ -156,7 +203,7 @@ Fix param order error moving putPrereqNameNotInUse from uDNS.c using
 ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
 
 Revision 1.42  2004/08/10 23:19:14  ksekar
-<rdar://problem/3722542>: DNS Extension daemon for Wide Area Rendezvous
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
 Moved routines/constants to allow extern access for garbage collection daemon
 
 Revision 1.41  2004/08/10 01:10:01  cheshire
@@ -410,6 +457,9 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype)
                }
        }
 
+// Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
+// the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
+// long as this routine is only used for debugging messages, it probably isn't a big problem.
 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer)
        {
        char *ptr = buffer;
@@ -426,7 +476,8 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd,
                case kDNSType_TXT:  mDNS_snprintf(buffer+length, 79-length, "%#s", rd->txt.c);         break;
 
                case kDNSType_AAAA:     mDNS_snprintf(buffer+length, 79-length, "%.16a", &rd->ipv6);       break;
-               case kDNSType_SRV:      mDNS_snprintf(buffer+length, 79-length, "%##s", rd->srv.target.c); break;
+               case kDNSType_SRV:      mDNS_snprintf(buffer+length, 79-length, "%u %u %u %##s",
+                                                               rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
                default:                        mDNS_snprintf(buffer+length, 79-length, "RDLen %d: %s", rr->rdlength, rd->data);  break;
                }
        for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
@@ -817,7 +868,7 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
                                        for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
                                        type = (domainname *)s1;
                                        
-                                       // Special support for queries done by older versions of "Rendezvous Browser"
+                                       // Special support for queries done by some third-party network monitoring software
                                        // For these queries, we retract the "._sub" we just added between the subtype and the main type
                                        if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") ||
                                                SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp"))
@@ -839,7 +890,7 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
 
        src = type->c;                                                                          // Put the service type into the domain name
        len = *src;
-       if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, (domainname*)"\x05" "local")))
+       if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain)))
                {
                errormsg="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
                goto fail;
@@ -872,6 +923,11 @@ fail:
        return(mDNSNULL);
        }
 
+// A service name has the form: instance.application-protocol.transport-protocol.domain
+// DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
+// set or length limits for the protocol names, and the final domain is allowed to be empty.
+// However, if the given FQDN doesn't contain at least three labels,
+// DeconstructServiceName will reject it and return mDNSfalse.
 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
        domainlabel *const name, domainname *const type, domainname *const domain)
        {
@@ -880,29 +936,32 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
        const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
        mDNSu8 *dst;
 
-       dst = name->c;                                                                          // Extract the service name from the domain name
+       dst = name->c;                                                                          // Extract the service name
        len = *src;
-       if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); }
+       if (!len)        { debugf("DeconstructServiceName: FQDN empty!");            return(mDNSfalse); }
+       if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
        for (i=0; i<=len; i++) *dst++ = *src++;
 
-       dst = type->c;                                                                          // Extract the service type from the domain name
+       dst = type->c;                                                                          // Extract the service type
        len = *src;
-       if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
+       if (!len)        { debugf("DeconstructServiceName: FQDN contains only one label!");      return(mDNSfalse); }
+       if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
        for (i=0; i<=len; i++) *dst++ = *src++;
 
        len = *src;
-       if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
+       if (!len)        { debugf("DeconstructServiceName: FQDN contains only two labels!");   return(mDNSfalse); }
+       if (len >= 0x40) { debugf("DeconstructServiceName: Transport protocol name too long"); return(mDNSfalse); }
        for (i=0; i<=len; i++) *dst++ = *src++;
-       *dst++ = 0;             // Put the null root label on the end of the service type
+       *dst++ = 0;                                                                                     // Put terminator on the end of service type
 
-       dst = domain->c;                                                                        // Extract the service domain from the domain name
+       dst = domain->c;                                                                        // Extract the service domain
        while (*src)
                {
                len = *src;
                if (len >= 0x40)
-                       { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); }
+                       { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
                if (src + 1 + len + 1 >= max)
-                       { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse); }
+                       { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
                for (i=0; i<=len; i++) *dst++ = *src++;
                }
        *dst++ = 0;             // Put the null root label on the end
@@ -910,6 +969,45 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
        return(mDNStrue);
        }
 
+// Notes on UTF-8:
+// 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
+// 10xxxxxx is a continuation byte of a multi-byte character
+// 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x     80 - 0x     800-1)
+// 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x    800 - 0x   10000-1)
+// 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x  10000 - 0x  200000-1)
+// 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
+// 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
+//
+// UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
+// Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
+// about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
+// The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
+// and the second    is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
+
+mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
+       {
+       if (length > max)
+               {
+               mDNSu8 c1 = string[max];                                                                // First byte after cut point
+               mDNSu8 c2 = (max+1 < length) ? string[max+1] : 0xB0;    // Second byte after cut point
+               length = max;   // Trim length down
+               while (length > 0)
+                       {
+                       // Check if the byte right after the chop point is a UTF-8 continuation byte,
+                       // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
+                       // If so, then we continue to chop more bytes until we get to a legal chop point.
+                       mDNSBool continuation    = ((c1 & 0xC0) == 0x80);
+                       mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
+                       if (!continuation && !secondsurrogate) break;
+                       c2 = c1;
+                       c1 = string[--length];
+                       }
+               // Having truncated characters off the end of our string, also cut off any residual white space
+               while (length > 0 && string[length-1] <= ' ') length--;
+               }
+       return(length);
+       }
+
 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
 // name ends in "-nnn", where n is some decimal number.
 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
@@ -974,13 +1072,7 @@ mDNSexport void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichT
 
        while (val >= divisor * 10) { divisor *= 10; chars++; }
 
-       if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - chars))
-               {
-               name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - chars);
-               // If the following character is a UTF-8 continuation character,
-               // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point
-               while (name->c[0] > 0 && (name->c[name->c[0]+1] & 0xC0) == 0x80) name->c[0]--;
-               }
+       name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
 
        if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
        else          { name->c[++name->c[0]] = '-'; }
@@ -1035,26 +1127,32 @@ mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const
        return(sum);
        }
 
-mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
+// r1 has to be a full ResourceRecord including rrtype and rdlength
+// r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
+mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2)
        {
-       if (r1->rrtype     != r2->rrtype)   return(mDNSfalse);
-       if (r1->rdlength   != r2->rdlength) return(mDNSfalse);
-       if (r1->rdatahash  != r2->rdatahash) return(mDNSfalse);
-       if (r1->rdnamehash != r2->rdnamehash) return(mDNSfalse);
        switch(r1->rrtype)
                {
                case kDNSType_CNAME:// Same as PTR
-               case kDNSType_PTR:      return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name));
+               case kDNSType_PTR:      return(SameDomainName(&r1->rdata->u.name, &r2->name));
 
-               case kDNSType_SRV:      return(mDNSBool)(       r1->rdata->u.srv.priority          == r2->rdata->u.srv.priority          &&
-                                                                                               r1->rdata->u.srv.weight            == r2->rdata->u.srv.weight            &&
-                                                                                               r1->rdata->u.srv.port.NotAnInteger == r2->rdata->u.srv.port.NotAnInteger &&
-                                                                                               SameDomainName(&r1->rdata->u.srv.target, &r2->rdata->u.srv.target)       );
+               case kDNSType_SRV:      return(mDNSBool)(       r1->rdata->u.srv.priority          == r2->srv.priority          &&
+                                                                                               r1->rdata->u.srv.weight            == r2->srv.weight            &&
+                                                                                               r1->rdata->u.srv.port.NotAnInteger == r2->srv.port.NotAnInteger &&
+                                                                                               SameDomainName(&r1->rdata->u.srv.target, &r2->srv.target)       );
 
-               default:                        return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength));
+               default:                        return(mDNSPlatformMemSame(r1->rdata->u.data, r2->data, r1->rdlength));
                }
        }
 
+mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
+       {
+       if (r1->rrtype     != r2->rrtype)     return(mDNSfalse);
+       if (r1->rdlength   != r2->rdlength)   return(mDNSfalse);
+       if (r1->rdatahash  != r2->rdatahash)  return(mDNSfalse);
+       return(SameRDataBody(r1, &r2->rdata->u));
+       }
+
 mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2)
        {
        return (r1->namehash == r2->namehash &&
@@ -1102,10 +1200,6 @@ mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate
 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
        {
        mDNSu16 len;
-       // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates
-       // with zero-length rdata, so for consistency we don't allow them for mDNS either.
-       // Otherwise we risk having applications that work with mDNS but not with uDNS.
-       if (!rdlength) return(mDNSfalse);
 
        switch(rrtype)
                {
@@ -1121,12 +1215,14 @@ mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength,
                case kDNSType_MR:       // Same as PTR
                //case kDNSType_NULL not checked (no specified format, so always valid)
                //case kDNSType_WKS not checked
-               case kDNSType_PTR:      len = DomainNameLength(&rd->u.name);
+               case kDNSType_PTR:      if (!rdlength) return(mDNSfalse);
+                                                       len = DomainNameLength(&rd->u.name);
                                                        return(len <= MAX_DOMAIN_NAME && rdlength == len);
 
                case kDNSType_HINFO:// Same as TXT (roughly)
                case kDNSType_MINFO:// Same as TXT (roughly)
-               case kDNSType_TXT:  {
+               case kDNSType_TXT:  if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
+                                                       {
                                                        const mDNSu8 *ptr = rd->u.txt.c;
                                                        const mDNSu8 *end = rd->u.txt.c + rdlength;
                                                        while (ptr < end) ptr += 1 + ptr[0];
@@ -1135,10 +1231,12 @@ mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength,
 
                case kDNSType_AAAA:     return(rdlength == sizeof(mDNSv6Addr));
 
-               case kDNSType_MX:   len = DomainNameLength(&rd->u.mx.exchange);
+               case kDNSType_MX:   if (!rdlength) return(mDNSfalse);
+                                                       len = DomainNameLength(&rd->u.mx.exchange);
                                                        return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
 
-               case kDNSType_SRV:      len = DomainNameLength(&rd->u.srv.target);
+               case kDNSType_SRV:      if (!rdlength) return(mDNSfalse);
+                                                       len = DomainNameLength(&rd->u.srv.target);
                                                        return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
 
                default:                        return(mDNStrue);       // Allow all other types without checking
@@ -1295,14 +1393,14 @@ mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *
                nput += 2 * sizeof(mDNSu16);
                if (opt->opt == kDNSOpt_LLQ)
                        {
-                       if (ptr + sizeof(LLQOptData) > limit) goto space_err;
+                       if (ptr + LLQ_OPTLEN > limit) goto space_err;
                        ptr = putVal16(ptr, opt->OptData.llq.vers);
                        ptr = putVal16(ptr, opt->OptData.llq.llqOp);
                        ptr = putVal16(ptr, opt->OptData.llq.err);
                        mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8);  // 8-byte id
                        ptr += 8;
                        ptr = putVal32(ptr, opt->OptData.llq.lease);
-                       nput += sizeof(LLQOptData);
+                       nput += LLQ_OPTLEN;
                        }
                else if (opt->opt == kDNSOpt_Lease)
                        {
@@ -1327,14 +1425,14 @@ mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
        return val;
        }
 
-mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr, mDNSu16 pktRDLen)
+mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen)
        {
        int nread = 0;
-       rdataOpt *opt;
-       
-       while (nread < pktRDLen)
+       ResourceRecord *const rr = &cr->r.resrec;
+       rdataOpt *opt = (rdataOpt *)rr->rdata->u.data;
+
+       while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOpt))
                {
-               opt = (rdataOpt *)(rr->rdata->u.data + nread);
                // space for opt + optlen
                if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err;
                opt->opt = getVal16(&ptr);
@@ -1342,7 +1440,7 @@ mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, Reso
                nread += 2 * sizeof(mDNSu16);
                if (opt->opt == kDNSOpt_LLQ)
                        {
-                       if ((unsigned)(limit - ptr) < sizeof(LLQOptData)) goto space_err;
+                       if ((unsigned)(limit - ptr) < LLQ_OPTLEN) goto space_err;
                        opt->OptData.llq.vers = getVal16(&ptr);
                        opt->OptData.llq.llqOp = getVal16(&ptr);
                        opt->OptData.llq.err = getVal16(&ptr);
@@ -1352,7 +1450,7 @@ mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, Reso
                        if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond)
                                opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond;
                        ptr += sizeof(mDNSOpaque32);
-                       nread += sizeof(LLQOptData);
+                       nread += LLQ_OPTLEN;
                        }
                else if (opt->opt == kDNSOpt_Lease)
                        {
@@ -1365,6 +1463,7 @@ mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, Reso
                        nread += sizeof(mDNSs32);
                        }
                else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
+               opt++;  // increment pointer into rdatabody
                }
        
        rr->rdlength = pktRDLen;
@@ -1582,8 +1681,8 @@ mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
        
        opt->RecordType = kDNSRecordTypeKnownUnique;  // to avoid warnings in other layers
        opt->rrtype = kDNSType_OPT;
-       opt->rdlength = LEASE_OPT_SIZE;
-       opt->rdestimate = LEASE_OPT_SIZE;
+       opt->rdlength = LEASE_OPT_RDLEN;
+       opt->rdestimate = LEASE_OPT_RDLEN;
 
        optRD = &rr.resrec.rdata->u.opt;
        optRD->opt = kDNSOpt_Lease;
@@ -1628,8 +1727,7 @@ mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 r
        target = GetRRDomainNameTarget(rr);
        rr->rdlength   = GetRDLength(rr, mDNSfalse);
        rr->rdestimate = GetRDLength(rr, mDNStrue);
-       rr->rdatahash  = RDataHashValue(rr->rdlength, &rr->rdata->u);
-       rr->rdnamehash = target ? DomainNameHashValue(target) : 0;
+       rr->rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(rr->rdlength, &rr->rdata->u);
        }
 
 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
@@ -1732,11 +1830,10 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage
        CacheRecord *rr = &largecr->r;
        mDNSu16 pktrdlength;
        
-       if (largecr == &m->rec && rr->resrec.RecordType)
-               LogMsg("GetLargeResourceRecord: m->rec appears to be already in use");
+       if (largecr == &m->rec && largecr->r.resrec.RecordType)
+               LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &largecr->r));
 
        rr->next              = mDNSNULL;
-       rr->resrec.RecordType = RecordType;
        rr->resrec.name       = &largecr->namestorage;
 
        rr->NextInKAList      = mDNSNULL;
@@ -1768,7 +1865,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage
        // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
        pktrdlength           = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
        if (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
-               rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask;
+               RecordType |= kDNSRecordTypePacketUniqueMask;
        ptr += 10;
        if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
        end = ptr + pktrdlength;                // Adjust end to indicate the end of the rdata for this resource record
@@ -1829,7 +1926,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage
                                        rr->resrec.rdata->u.soa.min     = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
                                        break;
 
-               case kDNSType_OPT:  getOptRdata(ptr, end, &rr->resrec, pktrdlength); break;
+               case kDNSType_OPT:  getOptRdata(ptr, end, largecr, pktrdlength); break;
 
                default:                        if (pktrdlength > rr->resrec.rdata->MaxRDLength)
                                                                {
@@ -1852,6 +1949,8 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage
        rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
        SetNewRData(&rr->resrec, mDNSNULL, 0);
 
+       // Success! Now fill in RecordType to show this record contains valid data
+       rr->resrec.RecordType = RecordType;
        return(ptr + pktrdlength);
        }
 
@@ -1992,14 +2091,14 @@ mDNSexport void mDNS_Lock(mDNS *const m)
        if (m->mDNS_busy == 0)
                {
                if (m->timenow)
-                       LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformRawTime() + m->timenow_adjust);
-               m->timenow = mDNSPlatformRawTime() + m->timenow_adjust;
+                       LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m));
+               m->timenow = mDNS_TimeNow_NoLock(m);
                if (m->timenow == 0) m->timenow = 1;
                }
        else if (m->timenow == 0)
                {
                LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
-               m->timenow = mDNSPlatformRawTime() + m->timenow_adjust;
+               m->timenow = mDNS_TimeNow_NoLock(m);
                if (m->timenow == 0) m->timenow = 1;
                }