Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  DNSPacket.cpp   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "DNSPacket.h"

#include "DNS.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_network.h"
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
#include "DNSLogging.h"

#include "nsIInputStream.h"

namespace mozilla {
namespace net {

static uint16_t get16bit(const unsigned char* aData, unsigned int index) {
  return ((aData[index] << 8) | aData[index + 1]);
}

static uint32_t get32bit(const unsigned char* aData, unsigned int index) {
  return (aData[index] << 24) | (aData[index + 1] << 16) |
         (aData[index + 2] << 8) | aData[index + 3];
}

// https://datatracker.ietf.org/doc/html/rfc8914#name-defined-extended-dns-errors
// This is a list of errors for which we should not fallback to Do53.
// These are normally explicit filtering performed by the recursive resolver.
bool hardFail(uint16_t code) {
  const uint16_t noFallbackErrors[] = {
      4,   // Forged answer (malware filtering)
      17,  // Filtered
  };

  for (const auto& err : noFallbackErrors) {
    if (code == err) {
      return true;
    }
  }
  return false;
}

nsresult DNSPacket::FillBuffer(
    std::function<int(unsigned char response[MAX_SIZE])>&& aPredicate) {
  int response_length = aPredicate(mResponse);
  if (response_length < 0) {
    LOG(("FillBuffer response len < 0"));
    mBodySize = 0;
    mStatus = NS_ERROR_UNEXPECTED;
    return mStatus;
  }

  mBodySize = response_length;
  return NS_OK;
}
// static
nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key,
                                  SvcFieldValue& field, uint16_t length,
                                  const unsigned char* aBuffer) {
  switch (key) {
    case SvcParamKeyMandatory: {
      if (length % 2 != 0) {
        // This key should encode a list of uint16_t
        return NS_ERROR_UNEXPECTED;
      }
      while (length > 0) {
        uint16_t mandatoryKey = get16bit(aBuffer, svcbIndex);
        length -= 2;
        svcbIndex += 2;

        if (!IsValidSvcParamKey(mandatoryKey)) {
          LOG(("The mandatory field includes a key we don't support %u",
               mandatoryKey));
          return NS_ERROR_UNEXPECTED;
        }
      }
      break;
    }
    case SvcParamKeyAlpn: {
      field.mValue = AsVariant(SvcParamAlpn());
      auto& alpnArray = field.mValue.as<SvcParamAlpn>().mValue;
      while (length > 0) {
        uint8_t alpnIdLength = aBuffer[svcbIndex++];
        length -= 1;
        if (alpnIdLength > length) {
          return NS_ERROR_UNEXPECTED;
        }

        alpnArray.AppendElement(
            nsCString((const char*)&aBuffer[svcbIndex], alpnIdLength));
        length -= alpnIdLength;
        svcbIndex += alpnIdLength;
      }
      break;
    }
    case SvcParamKeyNoDefaultAlpn: {
      if (length != 0) {
        // This key should not contain a value
        return NS_ERROR_UNEXPECTED;
      }
      field.mValue = AsVariant(SvcParamNoDefaultAlpn{});
      break;
    }
    case SvcParamKeyPort: {
      if (length != 2) {
        // This key should only encode a uint16_t
        return NS_ERROR_UNEXPECTED;
      }
      field.mValue =
          AsVariant(SvcParamPort{.mValue = get16bit(aBuffer, svcbIndex)});
      break;
    }
    case SvcParamKeyIpv4Hint: {
      if (length % 4 != 0) {
        // This key should only encode IPv4 addresses
        return NS_ERROR_UNEXPECTED;
      }

      field.mValue = AsVariant(SvcParamIpv4Hint());
      auto& ipv4array = field.mValue.as<SvcParamIpv4Hint>().mValue;
      while (length > 0) {
        NetAddr addr;
        addr.inet.family = AF_INET;
        addr.inet.port = 0;
        addr.inet.ip = ntohl(get32bit(aBuffer, svcbIndex));
        ipv4array.AppendElement(addr);
        length -= 4;
        svcbIndex += 4;
      }
      break;
    }
    case SvcParamKeyEchConfig: {
      field.mValue = AsVariant(SvcParamEchConfig{
          .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
      break;
    }
    case SvcParamKeyIpv6Hint: {
      if (length % 16 != 0) {
        // This key should only encode IPv6 addresses
        return NS_ERROR_UNEXPECTED;
      }

      field.mValue = AsVariant(SvcParamIpv6Hint());
      auto& ipv6array = field.mValue.as<SvcParamIpv6Hint>().mValue;
      while (length > 0) {
        NetAddr addr;
        addr.inet6.family = AF_INET6;
        addr.inet6.port = 0;      // unknown
        addr.inet6.flowinfo = 0;  // unknown
        addr.inet6.scope_id = 0;  // unknown
        for (int i = 0; i < 16; i++, svcbIndex++) {
          addr.inet6.ip.u8[i] = aBuffer[svcbIndex];
        }
        ipv6array.AppendElement(addr);
        length -= 16;
        // no need to increase svcbIndex - we did it in the for above.
      }
      break;
    }
    case SvcParamKeyODoHConfig: {
      field.mValue = AsVariant(SvcParamODoHConfig{
          .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
      break;
    }
    default: {
      // Unespected type. We'll just ignore it.
      return NS_OK;
      break;
    }
  }
  return NS_OK;
}

nsresult DNSPacket::PassQName(unsigned int& index,
                              const unsigned char* aBuffer) {
  uint8_t length;
  do {
    if (mBodySize < (index + 1)) {
      LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    length = static_cast<uint8_t>(aBuffer[index]);
    if ((length & 0xc0) == 0xc0) {
      // name pointer, advance over it and be done
      if (mBodySize < (index + 2)) {
        return NS_ERROR_ILLEGAL_VALUE;
      }
      index += 2;
      break;
    }
    if (length & 0xc0) {
      LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    // pass label
    if (mBodySize < (index + 1 + length)) {
      LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    index += 1 + length;
  } while (length);
  return NS_OK;
}

// GetQname: retrieves the qname (stores in 'aQname') and stores the index
// after qname was parsed into the 'aIndex'.
// static
nsresult DNSPacket::GetQname(nsACString& aQname, unsigned int& aIndex,
                             const unsigned char* aBuffer,
                             unsigned int aBodySize) {
  uint8_t clength = 0;
  unsigned int cindex = aIndex;
  unsigned int loop = 128;    // a valid DNS name can never loop this much
  unsigned int endindex = 0;  // index position after this data
  do {
    if (cindex >= aBodySize) {
      LOG(("TRR: bad Qname packet\n"));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    clength = static_cast<uint8_t>(aBuffer[cindex]);
    if ((clength & 0xc0) == 0xc0) {
      // name pointer, get the new offset (14 bits)
      if ((cindex + 1) >= aBodySize) {
        return NS_ERROR_ILLEGAL_VALUE;
      }
      // extract the new index position for the next label
      uint16_t newpos = (clength & 0x3f) << 8 | aBuffer[cindex + 1];
      if (!endindex) {
        // only update on the first "jump"
        endindex = cindex + 2;
      }
      cindex = newpos;
      continue;
    }
    if (clength & 0xc0) {
      // any of those bits set individually is an error
      LOG(("TRR: bad Qname packet\n"));
      return NS_ERROR_ILLEGAL_VALUE;
    }

    cindex++;

    if (clength) {
      if (!aQname.IsEmpty()) {
        aQname.Append(".");
      }
      if ((cindex + clength) > aBodySize) {
        return NS_ERROR_ILLEGAL_VALUE;
      }
      aQname.Append((const char*)(&aBuffer[cindex]), clength);
      cindex += clength;  // skip label
    }
  } while (clength && --loop);

  if (!loop) {
    LOG(("DNSPacket::DohDecode pointer loop error\n"));
    return NS_ERROR_ILLEGAL_VALUE;
  }
  if (!endindex) {
    // there was no "jump"
    endindex = cindex;
  }
  aIndex = endindex;
  return NS_OK;
}

nsresult DOHresp::Add(uint32_t TTL, unsigned char const* dns,
                      unsigned int index, uint16_t len, bool aLocalAllowed) {
  NetAddr addr;
  if (4 == len) {
    // IPv4
    addr.inet.family = AF_INET;
    addr.inet.port = 0;  // unknown
    addr.inet.ip = ntohl(get32bit(dns, index));
  } else if (16 == len) {
    // IPv6
    addr.inet6.family = AF_INET6;
    addr.inet6.port = 0;      // unknown
    addr.inet6.flowinfo = 0;  // unknown
    addr.inet6.scope_id = 0;  // unknown
    for (int i = 0; i < 16; i++, index++) {
      addr.inet6.ip.u8[i] = dns[index];
    }
  } else {
    return NS_ERROR_UNEXPECTED;
  }

  if (addr.IsIPAddrLocal() && !aLocalAllowed) {
    return NS_ERROR_FAILURE;
  }

  // While the DNS packet might return individual TTLs for each address,
  // we can only return one value in the AddrInfo class so pick the
  // lowest number.
  if (mTtl < TTL) {
    mTtl = TTL;
  }

  if (LOG_ENABLED()) {
    char buf[128];
    addr.ToStringBuffer(buf, sizeof(buf));
    LOG(("DOHresp:Add %s\n", buf));
  }
  mAddresses.AppendElement(addr);
  return NS_OK;
}

nsresult DNSPacket::OnDataAvailable(nsIRequest* aRequest,
                                    nsIInputStream* aInputStream,
                                    uint64_t aOffset, const uint32_t aCount) {
  if (aCount + mBodySize > MAX_SIZE) {
    LOG(("DNSPacket::OnDataAvailable:%d fail\n", __LINE__));
    return NS_ERROR_FAILURE;
  }
  uint32_t count;
  nsresult rv =
      aInputStream->Read((char*)mResponse + mBodySize, aCount, &count);
  if (NS_FAILED(rv)) {
    return rv;
  }
  MOZ_ASSERT(count == aCount);
  mBodySize += aCount;
  return NS_OK;
}

const uint8_t kDNS_CLASS_IN = 1;

// static
nsresult DNSPacket::EncodeHost(nsCString& aBody, const nsACString& aHost) {
  // The input host name should be converted to a sequence of labels, where
  // each label consists of a length octet followed by that number of
  // octets.  The domain name terminates with the zero length octet for the
  // null label of the root.

  int32_t index = 0;
  int32_t offset = 0;
  do {
    bool dotFound = false;
    int32_t labelLength;
    index = aHost.FindChar('.', offset);
    if (kNotFound != index) {
      dotFound = true;
      labelLength = index - offset;
    } else {
      labelLength = aHost.Length() - offset;
    }
    if (labelLength > 63) {
      // too long label!
      return NS_ERROR_ILLEGAL_VALUE;
    }
    if (labelLength > 0) {
      aBody += static_cast<unsigned char>(labelLength);
      nsDependentCSubstring label = Substring(aHost, offset, labelLength);
      aBody.Append(label);
    }
    if (!dotFound) {
      aBody += '\0';  // terminate with a final zero
      break;
    }
    offset += labelLength + 1;  // move over label and dot
  } while (true);

  return NS_OK;
}

nsresult DNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost,
                                  uint16_t aType, bool aDisableECS) {
  aBody.Truncate();
  // Header
  aBody += '\0';
  aBody += '\0';  // 16 bit id
  aBody += 0x01;  // |QR|   Opcode  |AA|TC|RD| Set the RD bit
  aBody += '\0';  // |RA|   Z    |   RCODE   |
  aBody += '\0';
  aBody += 1;  // QDCOUNT (number of entries in the question section)
  aBody += '\0';
  aBody += '\0';  // ANCOUNT
  aBody += '\0';
  aBody += '\0';  // NSCOUNT

  char additionalRecords =
      (aDisableECS || StaticPrefs::network_trr_padding()) ? 1 : 0;
  aBody += '\0';               // ARCOUNT
  aBody += additionalRecords;  // ARCOUNT low byte for EDNS(0)

  // Question

  nsresult rv = EncodeHost(aBody, aHost);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Followed by 16 bit QTYPE and 16 bit QCLASS
  aBody += static_cast<uint8_t>(aType >> 8);  // upper 8 bit TYPE
  aBody += static_cast<uint8_t>(aType);
  aBody += '\0';           // upper 8 bit CLASS
  aBody += kDNS_CLASS_IN;  // IN - "the Internet"

  if (additionalRecords) {
    // EDNS(0) is RFC 6891, ECS is RFC 7871
    aBody += '\0';  // NAME       | domain name  | MUST be 0 (root domain) |
    aBody += '\0';
    aBody += 41;  // TYPE       | u_int16_t    | OPT (41)                     |
    aBody += 16;  // CLASS      | u_int16_t    | requestor's UDP payload size |
    aBody +=
        '\0';  // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
    aBody += '\0';  // TTL        | u_int32_t    | extended RCODE and flags |
    aBody += '\0';
    aBody += '\0';
    aBody += '\0';

    // calculate padding length
    unsigned int paddingLen = 0;
    unsigned int rdlen = 0;
    bool padding = StaticPrefs::network_trr_padding();
    if (padding) {
      // always add padding specified in rfc 7830 when this config is enabled
      // to allow the reponse to be padded as well

      // two bytes RDLEN, 4 bytes padding header
      unsigned int packetLen = aBody.Length() + 2 + 4;
      if (aDisableECS) {
        // 8 bytes for disabling ecs
        packetLen += 8;
      }

      // clamp the padding length, because the padding extension only allows up
      // to 2^16 - 1 bytes padding and adding too much padding wastes resources
      uint32_t padTo = std::clamp<uint32_t>(
          StaticPrefs::network_trr_padding_length(), 0, 1024);

      // Calculate number of padding bytes. The second '%'-operator is necessary
      // because we prefer to add 0 bytes padding rather than padTo bytes
      if (padTo > 0) {
        paddingLen = (padTo - (packetLen % padTo)) % padTo;
      }
      // padding header + padding length
      rdlen += 4 + paddingLen;
    }
    if (aDisableECS) {
      rdlen += 8;
    }

    // RDLEN      | u_int16_t    | length of all RDATA          |
    aBody += (char)((rdlen >> 8) & 0xff);  // upper 8 bit RDLEN
    aBody += (char)(rdlen & 0xff);

    // RDATA      | octet stream | {attribute,value} pairs      |
    // The RDATA is just the ECS option setting zero subnet prefix

    if (aDisableECS) {
      aBody += '\0';  // upper 8 bit OPTION-CODE ECS
      aBody += 8;     // OPTION-CODE, 2 octets, for ECS is 8

      aBody += '\0';  // upper 8 bit OPTION-LENGTH
      aBody += 4;     // OPTION-LENGTH, 2 octets, contains the length of the
                      // payload after OPTION-LENGTH
      aBody += '\0';  // upper 8 bit FAMILY. IANA Address Family Numbers
                      // registry, not the AF_* constants!
      aBody += 1;     // FAMILY (Ipv4), 2 octets

      aBody += '\0';  // SOURCE PREFIX-LENGTH      |     SCOPE PREFIX-LENGTH |
      aBody += '\0';

      // ADDRESS, minimum number of octets == nothing because zero bits
    }

    if (padding) {
      aBody += '\0';  // upper 8 bit option OPTION-CODE PADDING
      aBody += 12;    // OPTION-CODE, 2 octets, for PADDING is 12

      // OPTION-LENGTH, 2 octets
      aBody += (char)((paddingLen >> 8) & 0xff);
      aBody += (char)(paddingLen & 0xff);
      for (unsigned int i = 0; i < paddingLen; i++) {
        aBody += '\0';
      }
    }
  }

  return NS_OK;
}

// static
nsresult DNSPacket::ParseHTTPS(uint16_t aRDLen, struct SVCB& aParsed,
                               unsigned int aIndex,
                               const unsigned char* aBuffer,
                               unsigned int aBodySize,
                               const nsACString& aOriginHost) {
  int32_t lastSvcParamKey = -1;
  nsresult rv = NS_OK;
  unsigned int svcbIndex = aIndex;
  CheckedInt<uint16_t> available = aRDLen;

  // Should have at least 2 bytes for the priority and one for the
  // qname length.
  if (available.value() < 3) {
    return NS_ERROR_UNEXPECTED;
  }

  aParsed.mSvcFieldPriority = get16bit(aBuffer, svcbIndex);
  svcbIndex += 2;

  rv = GetQname(aParsed.mSvcDomainName, svcbIndex, aBuffer, aBodySize);
  if (NS_FAILED(rv)) {
    return rv;
  }

  if (aParsed.mSvcDomainName.IsEmpty()) {
    if (aParsed.mSvcFieldPriority == 0) {
      // For AliasMode SVCB RRs, a TargetName of "." indicates that
      // the service is not available or does not exist.
      return NS_OK;
    }

    // For ServiceMode SVCB RRs, if TargetName has the value ".",
    // then the owner name of this record MUST be used as
    // the effective TargetName.
    // When the qname is port prefix name, we need to use the
    // original host name as TargetName.
    aParsed.mSvcDomainName = aOriginHost;
  }

  available -= (svcbIndex - aIndex);
  if (!available.isValid()) {
    return NS_ERROR_UNEXPECTED;
  }
  while (available.value() >= 4) {
    // Every SvcFieldValues must have at least 4 bytes for the
    // SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes)
    // If the length ever goes above the available data, meaning if
    // available ever underflows, then that is an error.
    struct SvcFieldValue value;
    uint16_t key = get16bit(aBuffer, svcbIndex);
    svcbIndex += 2;

    // 2.2 Clients MUST consider an RR malformed if SvcParamKeys are
    // not in strictly increasing numeric order.
    if (key <= lastSvcParamKey) {
      LOG(("SvcParamKeys not in increasing order"));
      return NS_ERROR_UNEXPECTED;
    }
    lastSvcParamKey = key;

    uint16_t len = get16bit(aBuffer, svcbIndex);
    svcbIndex += 2;

    available -= 4 + len;
    if (!available.isValid()) {
      return NS_ERROR_UNEXPECTED;
    }

    rv = ParseSvcParam(svcbIndex, key, value, len, aBuffer);
    if (NS_FAILED(rv)) {
      return rv;
    }
    svcbIndex += len;

    // If this is an unknown key, we will simply ignore it.
    // We also don't need to record SvcParamKeyMandatory
    if (key == SvcParamKeyMandatory || !IsValidSvcParamKey(key)) {
      continue;
    }

    if (value.mValue.is<SvcParamIpv4Hint>() ||
        value.mValue.is<SvcParamIpv6Hint>()) {
      aParsed.mHasIPHints = true;
    }
    if (value.mValue.is<SvcParamEchConfig>()) {
      aParsed.mHasEchConfig = true;
      aParsed.mEchConfig = value.mValue.as<SvcParamEchConfig>().mValue;
    }
    if (value.mValue.is<SvcParamODoHConfig>()) {
      aParsed.mODoHConfig = value.mValue.as<SvcParamODoHConfig>().mValue;
    }
    aParsed.mSvcFieldValue.AppendElement(value);
  }

  return NS_OK;
}

Result<uint8_t, nsresult> DNSPacket::GetRCode() const {
  if (mBodySize < 12) {
    LOG(("DNSPacket::GetRCode - packet too small"));
    return Err(NS_ERROR_ILLEGAL_VALUE);
  }

  return mResponse[3] & 0x0F;
}

Result<bool, nsresult> DNSPacket::RecursionAvailable() const {
  if (mBodySize < 12) {
    LOG(("DNSPacket::GetRCode - packet too small"));
    return Err(NS_ERROR_ILLEGAL_VALUE);
  }

  return mResponse[3] & 0x80;
}

nsresult DNSPacket::DecodeInternal(
    nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
    DOHresp& aResp, TypeRecordResultType& aTypeResult,
    nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
    uint32_t& aTTL, const unsigned char* aBuffer, uint32_t aLen) {
  // The response has a 12 byte header followed by the question (returned)
  // and then the answer. The answer section itself contains the name, type
  // and class again and THEN the record data.

  // www.example.com response:
  // header:
  // abcd 8180 0001 0001 0000 0000
  // the question:
  // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
  // the answer:
  // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
  // 0000 0080 0004 5db8 d822

  unsigned int index = 12;
  uint8_t length;
  nsAutoCString host;
  nsresult rv;
  uint16_t extendedError = UINT16_MAX;

  LOG(("doh decode %s %d bytes\n", aHost.get(), aLen));

  aCname.Truncate();

  if (aLen < 12) {
    LOG(("TRR bad incoming DOH, eject!\n"));
    return NS_ERROR_ILLEGAL_VALUE;
  }

  if (!mNativePacket && (aBuffer[0] || aBuffer[1])) {
    LOG(("Packet ID is unexpectedly non-zero"));
    return NS_ERROR_ILLEGAL_VALUE;
  }

  uint8_t rcode = mResponse[3] & 0x0F;
  LOG(("TRR Decode %s RCODE %d\n", PromiseFlatCString(aHost).get(), rcode));

  uint16_t questionRecords = get16bit(aBuffer, 4);  // qdcount
  // iterate over the single(?) host name in question
  while (questionRecords) {
    do {
      if (aLen < (index + 1)) {
        LOG(("TRR Decode 1 index: %u size: %u", index, aLen));
        return NS_ERROR_ILLEGAL_VALUE;
      }
      length = static_cast<uint8_t>(aBuffer[index]);
      if (length) {
        if (host.Length()) {
          host.Append(".");
        }
        if (aLen < (index + 1 + length)) {
          LOG(("TRR Decode 2 index: %u size: %u len: %u", index, aLen, length));
          return NS_ERROR_ILLEGAL_VALUE;
        }
        host.Append(((char*)aBuffer) + index + 1, length);
      }
      index += 1 + length;  // skip length byte + label
    } while (length);
    if (aLen < (index + 4)) {
      LOG(("TRR Decode 3 index: %u size: %u", index, aLen));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    index += 4;  // skip question's type, class
    questionRecords--;
  }

  // Figure out the number of answer records from ANCOUNT
  uint16_t answerRecords = get16bit(aBuffer, 6);

  LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n",
       answerRecords, aLen, host.get(), index));

  while (answerRecords) {
    nsAutoCString qname;
    rv = GetQname(qname, index, aBuffer, mBodySize);
    if (NS_FAILED(rv)) {
      return rv;
    }
    // 16 bit TYPE
    if (aLen < (index + 2)) {
      LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    uint16_t TYPE = get16bit(aBuffer, index);

    index += 2;

    // 16 bit class
    if (aLen < (index + 2)) {
      LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    uint16_t CLASS = get16bit(aBuffer, index);
    if (kDNS_CLASS_IN != CLASS) {
      LOG(("TRR bad CLASS (%u) at index %d\n"CLASS, index));
      return NS_ERROR_UNEXPECTED;
    }
    index += 2;

    // 32 bit TTL (seconds)
    if (aLen < (index + 4)) {
      LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    uint32_t TTL = get32bit(aBuffer, index);
    index += 4;

    // 16 bit RDLENGTH
    if (aLen < (index + 2)) {
      LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
      return NS_ERROR_ILLEGAL_VALUE;
    }
    uint16_t RDLENGTH = get16bit(aBuffer, index);
    index += 2;

    if (aLen < (index + RDLENGTH)) {
      LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__,
           RDLENGTH, index));
      return NS_ERROR_ILLEGAL_VALUE;
    }

    if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) &&
        (TYPE != static_cast<uint16_t>(aType))) {
      // Not the same type as was asked for nor CNAME
      LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, aType,
           TYPE));
      index += RDLENGTH;
      answerRecords--;
      continue;
    }

    // We check if the qname is a case-insensitive match for the host or the
    // FQDN version of the host
    bool responseMatchesQuestion =
        (qname.Length() == aHost.Length() ||
         (aHost.Length() == qname.Length() + 1 && aHost.Last() == '.')) &&
        StringBeginsWith(aHost, qname, nsCaseInsensitiveCStringComparator);

    if (responseMatchesQuestion) {
      // RDATA
      // - A (TYPE 1):  4 bytes
      // - AAAA (TYPE 28): 16 bytes
      // - NS (TYPE 2): N bytes

      switch (TYPE) {
        case TRRTYPE_A:
          if (RDLENGTH != 4) {
            LOG(("TRR bad length for A (%u)\n", RDLENGTH));
            return NS_ERROR_UNEXPECTED;
          }
          rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
          if (NS_FAILED(rv)) {
            LOG(
                ("TRR:DohDecode failed: local IP addresses or unknown IP "
                 "family\n"));
            return rv;
          }
          break;
        case TRRTYPE_AAAA:
          if (RDLENGTH != 16) {
            LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH));
            return NS_ERROR_UNEXPECTED;
          }
          rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
          if (NS_FAILED(rv)) {
            LOG(("TRR got unique/local IPv6 address!\n"));
            return rv;
          }
          break;

        case TRRTYPE_NS:
          break;
        case TRRTYPE_CNAME:
          if (aCname.IsEmpty()) {
            nsAutoCString qname;
            unsigned int qnameindex = index;
            rv = GetQname(qname, qnameindex, aBuffer, mBodySize);
            if (NS_FAILED(rv)) {
              return rv;
            }
            if (!qname.IsEmpty()) {
              ToLowerCase(qname);
              aCname = qname;
              LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host.get(),
                   aCname.get()));
            } else {
              LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n",
                   host.get()));
            }
          } else {
            LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n"));
          }
          break;
        case TRRTYPE_TXT: {
          // TXT record RRDATA sections are a series of character-strings
          // each character string is a length byte followed by that many data
          // bytes
          nsAutoCString txt;
          unsigned int txtIndex = index;
          uint16_t available = RDLENGTH;

          while (available > 0) {
            uint8_t characterStringLen = aBuffer[txtIndex++];
            available--;
            if (characterStringLen > available) {
              LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
              break;
            }
            txt.Append((const char*)(&aBuffer[txtIndex]), characterStringLen);
            txtIndex += characterStringLen;
            available -= characterStringLen;
          }

          if (!aTypeResult.is<TypeRecordTxt>()) {
            aTypeResult = AsVariant(CopyableTArray<nsCString>());
          }

          {
            auto& results = aTypeResult.as<TypeRecordTxt>();
            results.AppendElement(txt);
          }
          if (aTTL > TTL) {
            aTTL = TTL;
          }
          LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host.get(),
               txt.get()));

          break;
        }
        case TRRTYPE_HTTPSSVC: {
          struct SVCB parsed;

          if (aType != TRRTYPE_HTTPSSVC) {
            // Ignore the entry that we just parsed if we didn't ask for it.
            break;
          }

          rv = ParseHTTPS(RDLENGTH, parsed, index, aBuffer, mBodySize,
                          mOriginHost ? *mOriginHost : qname);
          if (NS_FAILED(rv)) {
            return rv;
          }

          if (parsed.mSvcDomainName.IsEmpty() &&
              parsed.mSvcFieldPriority == 0) {
            // For AliasMode SVCB RRs, a TargetName of "." indicates that the
            // service is not available or does not exist.
            continue;
          }

          // Check for AliasForm
          if (aCname.IsEmpty() && parsed.mSvcFieldPriority == 0) {
            // Alias form SvcDomainName must not have the "." value (empty)
            if (parsed.mSvcDomainName.IsEmpty()) {
              return NS_ERROR_UNEXPECTED;
            }
            aCname = parsed.mSvcDomainName;
            // If aliasForm is present, Service form must be ignored.
            aTypeResult = mozilla::AsVariant(Nothing());
            ToLowerCase(aCname);
            LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
                 host.get(), aCname.get()));
            break;
          }

          aTTL = TTL;

          if (!aTypeResult.is<TypeRecordHTTPSSVC>()) {
            aTypeResult = mozilla::AsVariant(CopyableTArray<SVCB>());
          }
          {
            auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
            results.AppendElement(parsed);
          }

          break;
        }
        default:
          // skip unknown record types
          LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH));
          break;
      }
    } else {
      LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get()));
    }

    index += RDLENGTH;
    LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH,
         index, aLen));
    answerRecords--;
  }

  // NSCOUNT
  uint16_t nsRecords = get16bit(aBuffer, 8);
  LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, aLen));
  while (nsRecords) {
    rv = PassQName(index, aBuffer);
    if (NS_FAILED(rv)) {
      return rv;
    }

    if (aLen < (index + 8)) {
      return NS_ERROR_ILLEGAL_VALUE;
    }
    index += 2;  // type
    index += 2;  // class
    index += 4;  // ttl

    // 16 bit RDLENGTH
    if (aLen < (index + 2)) {
      return NS_ERROR_ILLEGAL_VALUE;
    }
    uint16_t RDLENGTH = get16bit(aBuffer, index);
    index += 2;
    if (aLen < (index + RDLENGTH)) {
      return NS_ERROR_ILLEGAL_VALUE;
    }
    index += RDLENGTH;
    LOG(("done with nsRecord now %u of %u\n", index, aLen));
    nsRecords--;
  }

  // additional resource records
  uint16_t arRecords = get16bit(aBuffer, 10);
  LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
       arRecords, aLen));

  while (arRecords) {
    nsAutoCString qname;
    rv = GetQname(qname, index, aBuffer, mBodySize);
    if (NS_FAILED(rv)) {
      LOG(("Bad qname for additional record"));
      return rv;
    }

    if (aLen < (index + 8)) {
      return NS_ERROR_ILLEGAL_VALUE;
    }
    uint16_t type = get16bit(aBuffer, index);
    index += 2;
    // The next two bytes encode class
    // (or udpPayloadSize when type is TRRTYPE_OPT)
    uint16_t cls = get16bit(aBuffer, index);
    index += 2;
    // The next 4 bytes encode TTL
    // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
    uint32_t ttl = get32bit(aBuffer, index);
    index += 4;
    // cls and ttl are unused when type is TRRTYPE_OPT

    // 16 bit RDLENGTH
    if (aLen < (index + 2)) {
      LOG(("Record too small"));
      return NS_ERROR_ILLEGAL_VALUE;
    }

    uint16_t rdlength = get16bit(aBuffer, index);
    index += 2;
    if (aLen < (index + rdlength)) {
      LOG(("rdlength too big"));
      return NS_ERROR_ILLEGAL_VALUE;
    }

    auto parseRecord = [&]() {
      LOG(("Parsing additional record type: %u", type));
      auto* entry = aAdditionalRecords.GetOrInsertNew(qname);

      switch (type) {
        case TRRTYPE_A:
          if (kDNS_CLASS_IN != cls) {
            LOG(("NOT IN - returning"));
            return;
          }
          if (rdlength != 4) {
            LOG(("TRR bad length for A (%u)\n", rdlength));
            return;
          }
          rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
          if (NS_FAILED(rv)) {
            LOG(
                ("TRR:DohDecode failed: local IP addresses or unknown IP "
                 "family\n"));
            return;
          }
          break;
        case TRRTYPE_AAAA:
          if (kDNS_CLASS_IN != cls) {
            LOG(("NOT IN - returning"));
            return;
          }
          if (rdlength != 16) {
            LOG(("TRR bad length for AAAA (%u)\n", rdlength));
            return;
          }
          rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
          if (NS_FAILED(rv)) {
            LOG(("TRR got unique/local IPv6 address!\n"));
            return;
          }
          break;
        case TRRTYPE_OPT: {  // OPT
          LOG(("Parsing opt rdlen: %u", rdlength));
          unsigned int offset = 0;
          while (offset + 2 <= rdlength) {
            uint16_t optCode = get16bit(aBuffer, index + offset);
            LOG(("optCode: %u", optCode));
            offset += 2;
            if (offset + 2 > rdlength) {
              break;
            }
            uint16_t optLen = get16bit(aBuffer, index + offset);
            LOG(("optLen: %u", optLen));
            offset += 2;
            if (offset + optLen > rdlength) {
              LOG(("offset: %u, optLen: %u, rdlen: %u", offset, optLen,
                   rdlength));
              break;
            }

            LOG(("OPT: code: %u len:%u", optCode, optLen));

            if (optCode != 15) {
              offset += optLen;
              continue;
            }

            // optCode == 15; Extended DNS error

            if (offset + 2 > rdlength || optLen < 2) {
              break;
            }
            extendedError = get16bit(aBuffer, index + offset);

            LOG(("Extended error code: %u message: %s", extendedError,
                 nsAutoCString((char*)aBuffer + index + offset + 2, optLen - 2)
                     .get()));
            offset += optLen;
          }
          break;
        }
        default:
          break;
      }
    };

    parseRecord();

    index += rdlength;
    LOG(("done with additional rr now %u of %u\n", index, aLen));
    arRecords--;
  }

  if (index != aLen) {
    LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
         index, aLen));
    // failed to parse 100%, do not continue
    return NS_ERROR_ILLEGAL_VALUE;
  }

  if (aType == TRRTYPE_NS && rcode != 0) {
    return NS_ERROR_UNKNOWN_HOST;
  }

  if ((aType != TRRTYPE_NS) && aCname.IsEmpty() && aResp.mAddresses.IsEmpty() &&
      aTypeResult.is<TypeRecordEmpty>()) {
    // no entries were stored!
    LOG(("TRR: No entries were stored!\n"));

    if (extendedError != UINT16_MAX &&
        StaticPrefs::network_trr_hard_fail_on_extended_error() &&
        hardFail(extendedError)) {
      return NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
    }
    return NS_ERROR_UNKNOWN_HOST;
  }

  // https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03#page-14
  // If one or more SVCB records of ServiceForm SvcRecordType are returned for
  // HOST, clients should select the highest-priority option with acceptable
  // parameters.
  if (aTypeResult.is<TypeRecordHTTPSSVC>()) {
    auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
    results.Sort();
  }

  return NS_OK;
}

//
// DohDecode() collects the TTL and the IP addresses in the response
//
nsresult DNSPacket::Decode(
    nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
    DOHresp& aResp, TypeRecordResultType& aTypeResult,
    nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
    uint32_t& aTTL) {
  nsresult rv =
      DecodeInternal(aHost, aType, aCname, aAllowRFC1918, aResp, aTypeResult,
                     aAdditionalRecords, aTTL, mResponse, mBodySize);
  mStatus = rv;
  return rv;
}

}  // namespace net
}  // namespace mozilla

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.47 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge