void onDataInternal(Buffer::Instance& data) {
      buffer_.add(data);
      while (true) {
        if (size_ == 0) {
          uint16_t size_n;
          if (buffer_.length() < sizeof(size_n)) {
            // If we don't have enough bytes to determine size, wait until we do.
            return;
          }
          void* mem = buffer_.linearize(sizeof(size_n));
          std::memcpy(reinterpret_cast<void*>(&size_n), mem, sizeof(size_n));
          buffer_.drain(sizeof(size_n));
          size_ = ntohs(size_n);
        }

        if (buffer_.length() < size_) {
          // If we don't have enough bytes to read the complete query, wait until
          // we do.
          return;
        }

        // Expect requests to be small, so stack allocation is fine for test code.
        unsigned char* request = static_cast<unsigned char*>(buffer_.linearize(size_));
        // Only expecting a single question.
        ASSERT_EQ(1, DNS_HEADER_QDCOUNT(request));
        // Decode the question and perform lookup.
        const unsigned char* question = request + HFIXEDSZ;
        // The number of bytes the encoded question name takes up in the request.
        // Useful in the response when generating resource records containing the
        // name.
        long name_len;
        // Get host name from query and use the name to lookup a record
        // in a host map. If the query type is of type A, then perform the lookup in
        // the hosts_a_ host map. If the query type is of type AAAA, then perform the
        // lookup in the `hosts_aaaa_` host map.
        char* name;
        ASSERT_EQ(ARES_SUCCESS, ares_expand_name(question, request, size_, &name, &name_len));
        const std::list<std::string>* ips = nullptr;
        // We only expect resources of type A or AAAA.
        const int q_type = DNS_QUESTION_TYPE(question + name_len);
        std::string cname;
        // check if we have a cname. If so, we will need to send a response element with the cname
        // and lookup the ips of the cname and send back those ips (if any) too
        auto cit = parent_.cnames_.find(name);
        if (cit != parent_.cnames_.end()) {
          cname = cit->second;
        }
        const char* hostLookup = name;
        const unsigned char* ip_question = question;
        long ip_name_len = name_len;
        std::string encodedCname;
        if (!cname.empty()) {
          ASSERT_TRUE(cname.size() <= 253);
          hostLookup = cname.c_str();
          encodedCname = TestDnsServerQuery::encodeDnsName(cname);
          ip_question = reinterpret_cast<const unsigned char*>(encodedCname.c_str());
          ip_name_len =
              encodedCname.size() + 1; //+1 as we need to include the final null terminator
        }
        ASSERT_TRUE(q_type == T_A || q_type == T_AAAA);
        if (q_type == T_A) {
          auto it = parent_.hosts_a_.find(hostLookup);
          if (it != parent_.hosts_a_.end()) {
            ips = &it->second;
          }
        } else {
          auto it = parent_.hosts_aaaa_.find(hostLookup);
          if (it != parent_.hosts_aaaa_.end()) {
            ips = &it->second;
          }
        }
        ares_free_string(name);

        int answer_size = ips != nullptr ? ips->size() : 0;
        answer_size += !encodedCname.empty() ? 1 : 0;

        // The response begins with the initial part of the request
        // (including the question section).
        const size_t response_base_len = HFIXEDSZ + name_len + QFIXEDSZ;
        absl::FixedArray<unsigned char> response_buf(response_base_len);
        unsigned char* response_base = response_buf.begin();
        memcpy(response_base, request, response_base_len);
        DNS_HEADER_SET_QR(response_base, 1);
        DNS_HEADER_SET_AA(response_base, 0);
        if (parent_.refused_) {
          DNS_HEADER_SET_RCODE(response_base, REFUSED);
        } else {
          DNS_HEADER_SET_RCODE(response_base, answer_size > 0 ? NOERROR : NXDOMAIN);
        }
        DNS_HEADER_SET_ANCOUNT(response_base, answer_size);
        DNS_HEADER_SET_NSCOUNT(response_base, 0);
        DNS_HEADER_SET_ARCOUNT(response_base, 0);
        // Total response size will be computed according to cname response size + ip response sizes
        size_t response_ip_rest_len;
        if (q_type == T_A) {
          response_ip_rest_len =
              ips != nullptr ? ips->size() * (ip_name_len + RRFIXEDSZ + sizeof(in_addr)) : 0;
        } else {
          response_ip_rest_len =
              ips != nullptr ? ips->size() * (ip_name_len + RRFIXEDSZ + sizeof(in6_addr)) : 0;
        }
        size_t response_cname_len =
            !encodedCname.empty() ? name_len + RRFIXEDSZ + encodedCname.size() + 1 : 0;
        const uint16_t response_size_n =
            htons(response_base_len + response_ip_rest_len + response_cname_len);
        Buffer::OwnedImpl write_buffer;
        // Write response header
        write_buffer.add(&response_size_n, sizeof(response_size_n));
        write_buffer.add(response_base, response_base_len);

        // if we have a cname, create a resource record
        if (!encodedCname.empty()) {
          unsigned char cname_rr_fixed[RRFIXEDSZ];
          DNS_RR_SET_TYPE(cname_rr_fixed, T_CNAME);
          DNS_RR_SET_LEN(cname_rr_fixed, encodedCname.size() + 1);
          DNS_RR_SET_CLASS(cname_rr_fixed, C_IN);
          DNS_RR_SET_TTL(cname_rr_fixed, parent_.record_ttl_.count());
          write_buffer.add(question, name_len);
          write_buffer.add(cname_rr_fixed, RRFIXEDSZ);
          write_buffer.add(encodedCname.c_str(), encodedCname.size() + 1);
        }

        // Create a resource record for each IP found in the host map.
        unsigned char response_rr_fixed[RRFIXEDSZ];
        if (q_type == T_A) {
          DNS_RR_SET_TYPE(response_rr_fixed, T_A);
          DNS_RR_SET_LEN(response_rr_fixed, sizeof(in_addr));
        } else {
          DNS_RR_SET_TYPE(response_rr_fixed, T_AAAA);
          DNS_RR_SET_LEN(response_rr_fixed, sizeof(in6_addr));
        }
        DNS_RR_SET_CLASS(response_rr_fixed, C_IN);
        DNS_RR_SET_TTL(response_rr_fixed, parent_.record_ttl_.count());
        if (ips != nullptr) {
          for (const auto& it : *ips) {
            write_buffer.add(ip_question, ip_name_len);
            write_buffer.add(response_rr_fixed, RRFIXEDSZ);
            if (q_type == T_A) {
              in_addr addr;
              ASSERT_EQ(1, inet_pton(AF_INET, it.c_str(), &addr));
              write_buffer.add(&addr, sizeof(addr));
            } else {
              in6_addr addr;
              ASSERT_EQ(1, inet_pton(AF_INET6, it.c_str(), &addr));
              write_buffer.add(&addr, sizeof(addr));
            }
          }
        }
        parent_.connection_->write(write_buffer, false);

        // Reset query state, time for the next one.
        buffer_.drain(size_);
        size_ = 0;
      }
    }