diff options
Diffstat (limited to 'mDNSResponder/mDNSCore/DNSCommon.c')
-rw-r--r-- | mDNSResponder/mDNSCore/DNSCommon.c | 359 |
1 files changed, 284 insertions, 75 deletions
diff --git a/mDNSResponder/mDNSCore/DNSCommon.c b/mDNSResponder/mDNSCore/DNSCommon.c index 597c4cc0..a249b967 100644 --- a/mDNSResponder/mDNSCore/DNSCommon.c +++ b/mDNSResponder/mDNSCore/DNSCommon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -3450,7 +3450,6 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->TimeRcvd = m ? m->timenow : 0; rr->DelayDelivery = 0; rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord() - rr->LastUsed = m ? m->timenow : 0; rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; rr->LastUnansweredTime= 0; @@ -3630,25 +3629,6 @@ mDNSexport mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, cons return mDNSfalse; } -mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) -{ - int i; - LogInfo("%2d %s", count, label); - for (i = 0; i < count && ptr; i++) - { - // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage, - // but since it's only used for debugging (and probably only on OS X, not on - // embedded systems) putting a 9kB object on the stack isn't a big problem. - LargeCacheRecord largecr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); - if (ptr) - LogInfo("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); - } - if (!ptr) - LogInfo("DumpRecords: ERROR: Premature end of packet data"); - return(ptr); -} - #define DNS_OP_Name(X) ( \ (X) == kDNSFlag0_OP_StdQuery ? "" : \ (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \ @@ -3672,52 +3652,198 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) -// Note: DumpPacket expects the packet header fields in host byte order, not network byte order -mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, - const mDNSAddr *srcaddr, mDNSIPPort srcport, - const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +mDNSlocal void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...) { - mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update); - const mDNSu8 *ptr = msg->data; - int i; - DNSQuestion q; - char tbuffer[64], sbuffer[64], dbuffer[64] = ""; - if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0; - else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0; - if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0; - else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0; - if (dstaddr || !mDNSIPPortIsZero(dstport)) - dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; - - LogInfo("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --", - tbuffer, transport, + va_list args; + mDNSu32 buflen, n; + char *const dst = *ptr; + + buflen = (mDNSu32)(lim - dst); + if (buflen > 0) + { + va_start(args, fmt); + n = mDNS_vsnprintf(dst, buflen, fmt, args); + va_end(args); + *ptr = dst + n; + } +} + +#define DNSTypeString(X) (((X) == kDNSType_A) ? "A" : DNSTypeName(X)) + +#define ReadField16(PTR) ((mDNSu16)((((mDNSu16)((mDNSu8 *)(PTR))[0]) << 8) | ((mDNSu16)((mDNSu8 *)(PTR))[1]))) +#define ReadField32(PTR) \ + ((mDNSu32)( \ + (((mDNSu32)((mDNSu8 *)(PTR))[0]) << 24) | \ + (((mDNSu32)((mDNSu8 *)(PTR))[1]) << 16) | \ + (((mDNSu32)((mDNSu8 *)(PTR))[2]) << 8) | \ + ((mDNSu32)((mDNSu8 *)(PTR))[3]))) + +mDNSlocal void DNSMessageDump(const DNSMessage *const msg, const mDNSu8 *const end, char *buffer, mDNSu32 buflen) +{ + domainname *name; + const mDNSu8 *ptr; + domainname nameStorage[2]; + char *dst = buffer; + const char *const lim = &buffer[buflen]; + mDNSu32 i; + const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; + + mDNS_snprintf_add(&dst, lim, "DNS %s%s (%lu) (flags %02X%02X) RCODE: %s (%d)%s%s%s%s%s%s ID: %u:", DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), - msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", + (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query", + (unsigned long)(end - (const mDNSu8 *)msg), msg->h.flags.b[0], msg->h.flags.b[1], DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), msg->h.flags.b[1] & kDNSFlag1_RC_Mask, - msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "", - msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "", - msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "", - msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "", - msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "", - msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "", - mDNSVal16(msg->h.id), - end - msg->data, - sbuffer, mDNSVal16(srcport), dbuffer, - (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" - ); - - LogInfo("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); - for (i = 0; i < msg->h.numQuestions && ptr; i++) + (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "", + (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "", + (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "", + (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "", + (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "", + (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "", + mDNSVal16(msg->h.id)); + + name = mDNSNULL; + ptr = msg->data; + for (i = 0; i < msg->h.numQuestions; i++) { - ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); - if (ptr) LogInfo("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); + mDNSu16 qtype, qclass; + + name = &nameStorage[0]; + ptr = getDomainName(msg, ptr, end, name); + if (!ptr) goto exit; + + if ((end - ptr) < 4) goto exit; + qtype = ReadField16(&ptr[0]); + qclass = ReadField16(&ptr[2]); + ptr += 4; + + mDNS_snprintf_add(&dst, lim, " %##s %s", name->c, DNSTypeString(qtype)); + if (qclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", qclass); + mDNS_snprintf_add(&dst, lim, "?"); } - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); - DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); - LogInfo("--------------"); + + mDNS_snprintf_add(&dst, lim, " %u/%u/%u", msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals); + for (i = 0; i < rrcount; i++) + { + mDNSu16 rrtype, rrclass, rdlength; + mDNSu32 ttl; + int handled; + const mDNSu8 *rdata; + const domainname *const previousName = name; + + name = &nameStorage[(name == &nameStorage[0]) ? 1 : 0]; + ptr = getDomainName(msg, ptr, end, name); + if (!ptr) goto exit; + + if ((end - ptr) < 10) goto exit; + rrtype = ReadField16(&ptr[0]); + rrclass = ReadField16(&ptr[2]); + ttl = ReadField32(&ptr[4]); + rdlength = ReadField16(&ptr[8]); + ptr += 10; + + if ((end - ptr) < rdlength) goto exit; + rdata = ptr; + + if (i > 0) mDNS_snprintf_add(&dst, lim, ","); + if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&dst, lim, " %##s", name); + + mDNS_snprintf_add(&dst, lim, " %s", DNSTypeString(rrtype)); + if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", rrclass); + mDNS_snprintf_add(&dst, lim, " "); + + handled = mDNSfalse; + switch (rrtype) + { + case kDNSType_A: + if (rdlength == 4) + { + mDNS_snprintf_add(&dst, lim, "%.4a", rdata); + handled = mDNStrue; + } + break; + + case kDNSType_AAAA: + if (rdlength == 16) + { + mDNS_snprintf_add(&dst, lim, "%.16a", rdata); + handled = mDNStrue; + } + break; + + case kDNSType_CNAME: + ptr = getDomainName(msg, rdata, end, name); + if (!ptr) goto exit; + + mDNS_snprintf_add(&dst, lim, "%##s", name); + handled = mDNStrue; + break; + + case kDNSType_SOA: + { + mDNSu32 serial, refresh, retry, expire, minimum; + domainname *const mname = &nameStorage[0]; + domainname *const rname = &nameStorage[1]; + name = mDNSNULL; + + ptr = getDomainName(msg, rdata, end, mname); + if (!ptr) goto exit; + + ptr = getDomainName(msg, ptr, end, rname); + if (!ptr) goto exit; + + if ((end - ptr) < 20) goto exit; + serial = ReadField32(&ptr[0]); + refresh = ReadField32(&ptr[4]); + retry = ReadField32(&ptr[8]); + expire = ReadField32(&ptr[12]); + minimum = ReadField32(&ptr[16]); + + mDNS_snprintf_add(&dst, lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial, + (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum); + + handled = mDNStrue; + break; + } + + default: + break; + } + if (!handled) mDNS_snprintf_add(&dst, lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata); + mDNS_snprintf_add(&dst, lim, " (%lu)", (unsigned long)ttl); + ptr = rdata + rdlength; + } + +exit: + return; +} + +// Note: DumpPacket expects the packet header fields in host byte order, not network byte order +mDNSexport void DumpPacket(mStatus status, mDNSBool sent, char *transport, + const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +{ + char buffer[512]; + char *dst = buffer; + const char *const lim = &buffer[512]; + + buffer[0] = '\0'; + if (!status) mDNS_snprintf_add(&dst, lim, sent ? "Sent" : "Received"); + else mDNS_snprintf_add(&dst, lim, "ERROR %d %sing", status, sent ? "Send" : "Receiv"); + + mDNS_snprintf_add(&dst, lim, " %s DNS Message %u bytes from ", transport, (unsigned long)(end - (const mDNSu8 *)msg)); + + if (sent) mDNS_snprintf_add(&dst, lim, "port %d", mDNSVal16(srcport)); + else mDNS_snprintf_add(&dst, lim, "%#a:%d", srcaddr, mDNSVal16(srcport)); + + if (dstaddr || !mDNSIPPortIsZero(dstport)) mDNS_snprintf_add(&dst, lim, " to %#a:%d", dstaddr, mDNSVal16(dstport)); + + LogInfo("%s", buffer); + + buffer[0] = '\0'; + DNSMessageDump(msg, end, buffer, (mDNSu32)sizeof(buffer)); + LogInfo("%s", buffer); } // *************************************************************************** @@ -3824,7 +3950,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS // Dump the packet with the HINFO and TSIG if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) - DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); + DumpPacket(status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); // put the number of additionals back the way it was msg->h.numAdditionals = numAdditionals; @@ -4053,6 +4179,9 @@ static const struct mDNSprintf_format unsigned int precision; } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#define kHexDigitsLowercase "0123456789abcdef" +#define kHexDigitsUppercase "0123456789ABCDEF"; + mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) { mDNSu32 nwritten = 0; @@ -4064,6 +4193,7 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt for (c = *fmt; c != 0; c = *++fmt) { unsigned long n; + int hexdump = mDNSfalse; if (c != '%') { *sbuffer++ = (char)c; @@ -4190,10 +4320,63 @@ decimal: if (!F.havePrecision) a[0], a[1], a[2], a[3]); break; case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]); break; - case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), - "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], - a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; + case 16: { + // Print IPv6 addresses according to RFC 5952, A Recommendation for IPv6 Address Text + // Representation. See <https://tools.ietf.org/html/rfc5952>. + + int idx, runLen = 0, runStart = 0, maxRunLen = 0, maxRunStart = 0, maxRunEnd; + + // Find the leftmost longest run of consecutive zero hextets. + for (idx = 0; idx < 8; ++idx) + { + const unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1]; + if (hextet == 0) + { + if (runLen++ == 0) runStart = idx; + if (runLen > maxRunLen) + { + maxRunStart = runStart; + maxRunLen = runLen; + } + } + else + { + // If the number of remaining hextets is less than or equal to the length of the longest + // run so far, then we've found the leftmost longest run. + if ((8 - (idx + 1)) <= maxRunLen) break; + runLen = 0; + } + } + + // Compress the leftmost longest run of two or more consecutive zero hextets as "::". + // For each reminaing hextet, suppress zeros leading up to the least-significant nibble, which + // is always written, even if it's zero. Because of this requirement, it's easier to write the + // IPv6 address in reverse. Also, write a colon separator before each hextet except for the + // first one. + s = mDNS_VACB_Lim; + maxRunEnd = (maxRunLen >= 2) ? (maxRunStart + maxRunLen - 1) : -1; + for (idx = 7; idx >= 0; --idx) + { + if (idx == maxRunEnd) + { + if (idx == 7) *--s = ':'; + idx = maxRunStart; + *--s = ':'; + } + else + { + unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1]; + do { + *--s = kHexDigitsLowercase[hextet % 16]; + hextet /= 16; + } while (hextet); + if (idx > 0) *--s = ':'; + } + } + i = (unsigned int)(mDNS_VACB_Lim - s); + } + break; + default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; } @@ -4203,9 +4386,9 @@ decimal: if (!F.havePrecision) case 'p': F.havePrecision = F.lSize = 1; F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit - case 'X': digits = "0123456789ABCDEF"; + case 'X': digits = kHexDigitsUppercase; goto hexadecimal; - case 'x': digits = "0123456789abcdef"; + case 'x': digits = kHexDigitsLowercase; hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); else n = va_arg(arg, unsigned int); if (F.hSize) n = (unsigned short) n; @@ -4288,6 +4471,12 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} break; + case 'H': { + s = va_arg(arg, char *); + hexdump = mDNStrue; + } + break; + case 'n': s = va_arg(arg, char *); if (F.hSize) *(short *) s = (short)nwritten; else if (F.lSize) *(long *) s = (long)nwritten; @@ -4308,14 +4497,34 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); if (++nwritten >= buflen) goto exit; } while (i < --F.fieldWidth); - // Make sure we don't truncate in the middle of a UTF-8 character. - // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the - // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, - // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly - // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). - if (i > buflen - nwritten) - { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} - for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result + if (hexdump) + { + char *dst = sbuffer; + const char *const lim = &sbuffer[buflen - nwritten]; + if (F.havePrecision) + { + for (i = 0; (i < F.precision) && (dst < lim); i++) + { + const unsigned int b = (unsigned int) *s++; + if (i > 0) *dst++ = ' '; + if (dst < lim) *dst++ = kHexDigitsLowercase[(b >> 4) & 0xF]; + if (dst < lim) *dst++ = kHexDigitsLowercase[ b & 0xF]; + } + } + i = (unsigned int)(dst - sbuffer); + sbuffer = dst; + } + else + { + // Make sure we don't truncate in the middle of a UTF-8 character. + // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the + // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, + // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly + // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). + if (i > buflen - nwritten) + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result + } nwritten += i; if (nwritten >= buflen) goto exit; |