// Copyright 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "aemu/base/network/Dns.h"

#include "aemu/base/network/IpAddress.h"

#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

#include <errno.h>

namespace android {
namespace base {

// Convenience class used to mock the system DNS resolver during unit-testing.
//
// Usage:
//     1) Create TestDnsResolver instance.
//
//     2) Call addEntry(), addEntryIpv4() or addEntryIpv6() on it as many times
//        as needed to populate its table. One can add several addresses per
//        server name.
//
//     3) Run your test, calls to Dns::resolveName() will use the
//        TestDnsResolver() content automatically.
//
class TestDnsResolver : public Dns::Resolver {
public:
    using AddressList = Dns::AddressList;

    // Default constructor injects this instance into the process' global
    // state.
    TestDnsResolver() { mPrevResolver = Dns::Resolver::setForTesting(this); }

    // Destructor restores the previous global resolver instance.
    ~TestDnsResolver() { Dns::Resolver::setForTesting(mPrevResolver); }

    // Clear the internal table.
    void reset() { mMap.clear(); }

    // Add new entry to the internal table. |server_name| is a server name,
    // and |address| is a string corresponding to either a dotted IPv4 address
    // or column-separated IPv6 one.
    void addEntry(std::string_view server_name, std::string_view address) {
        AddressList list = {IpAddress(address.data())};
        addEntryList(server_name, list);
    }

    // Add new entry to the internal table. |server_nam| is a server name,
    // and |ipv4| is the host-order 32-bit IPv4 address for it.
    void addEntryIpv4(std::string_view server_name, uint32_t ipv4) {
        AddressList list = {IpAddress(ipv4)};
        addEntryList(server_name, list);
    }

    // Add a new entry to the internal table. |server_name| is a server name,
    // and |ipv6| is a 26-byte IPv6 address for it.
    void addEntryIpv6(std::string_view server_name, const uint8_t ipv6[16]) {
        addEntryList(server_name, AddressList({IpAddress(ipv6)}));
    }

    // Add a |list| of IP addresses to the internal table, associated with
    // |server_name|.
    void addEntryList(std::string_view server_name, const AddressList& list) {
        std::string key(server_name);
        auto it = mMap.find(key);
        if (it == mMap.end()) {
            mMap.insert(std::make_pair(key, list));
            return;
        }
        for (const auto& item : list) {
            it->second.push_back(item);
        }
    }

    // Return the list of IpAddress instances associated with |server_name|
    // through previous addEntryXXX() calls.
    virtual int resolveName(std::string_view server_name,
                            AddressList* out) override {
        std::string key(server_name);
        // First try to parse it as a numerical IP address.
        IpAddress ip(key);
        if (ip.valid()) {
            out->push_back(std::move(ip));
            return 0;
        }
        auto it = mMap.find(key);
        if (it == mMap.end()) {
            return -ENOENT;
        }
        *out = it->second;
        return 0;
    }

    // Return the list of DNS servers used by the system currently.
    virtual int getSystemServerList(AddressList* out) override {
        out->emplace_back(IpAddress(0x7f000001));
        return 0;
    }

private:
    Dns::Resolver* mPrevResolver = nullptr;
    std::unordered_map<std::string, AddressList> mMap;
};

}  // namespace base
}  // namespace android
