/*
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 * 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 "mdns_protocol_impl.h"

#include <arpa/inet.h>
#include <cstddef>
#include <iostream>
#include <random>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

#include "mdns_manager.h"
#include "mdns_packet_parser.h"
#include "net_conn_client.h"
#include "netmgr_ext_log_wrapper.h"

#include "securec.h"

namespace OHOS {
namespace NetManagerStandard {

constexpr uint32_t DEFAULT_INTEVAL_MS = 2000;
constexpr uint32_t DEFAULT_LOST_MS = 10000;
constexpr uint32_t DEFAULT_TTL = 120;
constexpr uint16_t MDNS_FLUSH_CACHE_BIT = 0x8000;

constexpr int PHASE_PTR = 1;
constexpr int PHASE_SRV = 2;
constexpr int PHASE_DOMAIN = 3;

std::string AddrToString(const std::any &addr)
{
    char buf[INET6_ADDRSTRLEN] = {0};
    if (std::any_cast<in_addr>(&addr)) {
        if (inet_ntop(AF_INET, std::any_cast<in_addr>(&addr), buf, sizeof(buf)) == nullptr) {
            return std::string{};
        }
    } else if (std::any_cast<in6_addr>(&addr)) {
        if (inet_ntop(AF_INET6, std::any_cast<in6_addr>(&addr), buf, sizeof(buf)) == nullptr) {
            return std::string{};
        }
    }
    return std::string(buf);
}

int64_t MilliSecondsSinceEpoch()
{
    return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
        .count();
}

MDnsProtocolImpl::MDnsProtocolImpl()
{
    Init();
}

void MDnsProtocolImpl::Init()
{
    NETMGR_EXT_LOG_D("mdns_log MDnsProtocolImpl init");
    listener_.Stop();
    listener_.CloseAllSocket();

    if (config_.configAllIface) {
        listener_.OpenSocketForEachIface(config_.ipv6Support, config_.configLo);
    } else {
        listener_.OpenSocketForDefault(config_.ipv6Support);
    }
    listener_.SetReceiveHandler(
        [this](int sock, const MDnsPayload &payload) { return this->ReceivePacket(sock, payload); });
    listener_.SetFinishedHandler([this](int sock) {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        RunTaskQueue(taskQueue_);
    });
    listener_.Start();

    taskQueue_.clear();
    taskOnChange_.clear();
    AddTask([this]() { return Browse(); }, false);
}

bool MDnsProtocolImpl::Browse()
{
    if (lastRunTime != -1 && MilliSecondsSinceEpoch() - lastRunTime < DEFAULT_INTEVAL_MS) {
        return false;
    }
    lastRunTime = MilliSecondsSinceEpoch();
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    for (auto &&[key, res] : browserMap_) {
        NETMGR_EXT_LOG_D("mdns_log Browse browserMap_ key[%{public}s] res.size[%{public}zu]", key.c_str(), res.size());
        if (nameCbMap_.find(key) != nameCbMap_.end() &&
            !MDnsManager::GetInstance().IsAvailableCallback(nameCbMap_[key])) {
            continue;
        }
        handleOfflineService(key, res);
        MDnsPayloadParser parser;
        MDnsMessage msg{};
        msg.questions.emplace_back(DNSProto::Question{
            .name = key,
            .qtype = DNSProto::RRTYPE_PTR,
            .qclass = DNSProto::RRCLASS_IN,
        });
        listener_.MulticastAll(parser.ToBytes(msg));
    }
    return false;
}

int32_t MDnsProtocolImpl::ConnectControl(int32_t sockfd, sockaddr* serverAddr)
{
    uint32_t flags = static_cast<uint32_t>(fcntl(sockfd, F_GETFL, 0));
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    int32_t ret = connect(sockfd, serverAddr, sizeof(sockaddr));
    if ((ret < 0) && (errno != EINPROGRESS)) {
        NETMGR_EXT_LOG_E("connect error: %{public}d", errno);
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    if (ret == 0) {
        fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
        NETMGR_EXT_LOG_I("connect success.");
        return NETMANAGER_EXT_SUCCESS;
    }

    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(sockfd, &rset);
    fd_set wset = rset;
    timeval tval {1, 0};
    ret = select(sockfd + 1, &rset, &wset, NULL, &tval);
    if (ret < 0) { // select error.
        NETMGR_EXT_LOG_E("select error: %{public}d", errno);
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    if (ret == 0) { // timeout
        NETMGR_EXT_LOG_E("connect timeout...");
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    if (!FD_ISSET(sockfd, &rset) && !FD_ISSET(sockfd, &wset)) {
        NETMGR_EXT_LOG_E("select error: sockfd not set");
        return NETMANAGER_EXT_ERR_INTERNAL;
    }

    int32_t result = NETMANAGER_EXT_ERR_INTERNAL;
    socklen_t len = sizeof(result);
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &result, &len) < 0) {
        NETMGR_EXT_LOG_E("getsockopt error: %{public}d", errno);
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    if (result != 0) { // connect failed.
        NETMGR_EXT_LOG_E("connect failed. error: %{public}d", result);
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
    NETMGR_EXT_LOG_I("lost but connect success.");
    return NETMANAGER_EXT_SUCCESS;
}

bool MDnsProtocolImpl::IsConnectivity(const std::string &ip, int32_t port)
{
    if (ip.empty()) {
        NETMGR_EXT_LOG_E("ip is empty");
        return false;
    }

    int32_t sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        NETMGR_EXT_LOG_E("create socket error: %{public}d", errno);
        return false;
    }

    struct sockaddr_in serverAddr;
    if (memset_s(&serverAddr, sizeof(serverAddr), 0, sizeof(serverAddr)) != EOK) {
        NETMGR_EXT_LOG_E("memset_s serverAddr failed!");
        close(sockfd);
        return false;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
    serverAddr.sin_port = htons(port);
    if (ConnectControl(sockfd, (struct sockaddr*)&serverAddr) != NETMANAGER_EXT_SUCCESS) {
        NETMGR_EXT_LOG_I("connect error: %{public}d", errno);
        close(sockfd);
        return false;
    }

    close(sockfd);
    return true;
}

void MDnsProtocolImpl::handleOfflineService(const std::string &key, std::vector<Result> &res)
{
    NETMGR_EXT_LOG_D("mdns_log handleOfflineService key:[%{public}s]", key.c_str());
    for (auto it = res.begin(); it != res.end();) {
        if (lastRunTime - it->refrehTime > DEFAULT_LOST_MS && it->state == State::LIVE) {
            std::string fullName = Decorated(it->serviceName + MDNS_DOMAIN_SPLITER_STR + it->serviceType);
            if ((cacheMap_.find(fullName) != cacheMap_.end()) &&
                IsConnectivity(cacheMap_[fullName].addr, cacheMap_[fullName].port)) {
                it++;
                continue;
            }

            it->state = State::DEAD;
            if (nameCbMap_.find(key) != nameCbMap_.end() && nameCbMap_[key] != nullptr) {
                NETMGR_EXT_LOG_W("mdns_log HandleServiceLost");
                nameCbMap_[key]->HandleServiceLost(ConvertResultToInfo(*it), NETMANAGER_EXT_SUCCESS);
            }
            it = res.erase(it);
            cacheMap_.erase(fullName);
        } else {
            it++;
        }
    }
}

void MDnsProtocolImpl::SetConfig(const MDnsConfig &config)
{
    config_ = config;
}

const MDnsConfig &MDnsProtocolImpl::GetConfig() const
{
    return config_;
}

std::string MDnsProtocolImpl::Decorated(const std::string &name) const
{
    return name + config_.topDomain;
}

int32_t MDnsProtocolImpl::Register(const Result &info)
{
    NETMGR_EXT_LOG_D("mdns_log Register");
    if (!(IsNameValid(info.serviceName) && IsTypeValid(info.serviceType) && IsPortValid(info.port))) {
        return NET_MDNS_ERR_ILLEGAL_ARGUMENT;
    }
    std::string name = Decorated(info.serviceName + MDNS_DOMAIN_SPLITER_STR + info.serviceType);
    if (!IsDomainValid(name)) {
        return NET_MDNS_ERR_ILLEGAL_ARGUMENT;
    }
    {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        if (srvMap_.find(name) != srvMap_.end()) {
            return NET_MDNS_ERR_SERVICE_INSTANCE_DUPLICATE;
        }
        srvMap_.emplace(name, info);
    }
    return Announce(info, false);
}

int32_t MDnsProtocolImpl::UnRegister(const std::string &key)
{
    NETMGR_EXT_LOG_D("mdns_log UnRegister");
    std::string name = Decorated(key);
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    if (srvMap_.find(name) != srvMap_.end()) {
        Announce(srvMap_[name], true);
        srvMap_.erase(name);
        return NETMANAGER_EXT_SUCCESS;
    }
    return NET_MDNS_ERR_SERVICE_INSTANCE_NOT_FOUND;
}

bool MDnsProtocolImpl::DiscoveryFromCache(const std::string &serviceType, const sptr<IDiscoveryCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log DiscoveryFromCache");
    std::string name = Decorated(serviceType);
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    if (!IsBrowserAvailable(name)) {
        return false;
    }

    if (browserMap_.find(name) == browserMap_.end()) {
        NETMGR_EXT_LOG_D("mdns_log DiscoveryFromCache browserMap_ not find name");
        return false;
    }

    for (auto &res : browserMap_[name]) {
        if (res.state == State::REMOVE || res.state == State::DEAD) {
            continue;
        }
        AddTask([cb, info = ConvertResultToInfo(res)]() {
            NETMGR_EXT_LOG_W("mdns_log DiscoveryFromCache ConvertResultToInfo HandleServiceFound");
            if (MDnsManager::GetInstance().IsAvailableCallback(cb)) {
                cb->HandleServiceFound(info, NETMANAGER_EXT_SUCCESS);
            }
            return true;
        });
    }
    return true;
}

bool MDnsProtocolImpl::DiscoveryFromNet(const std::string &serviceType, const sptr<IDiscoveryCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log DiscoveryFromNet");
    std::string name = Decorated(serviceType);
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    browserMap_.insert({name, std::vector<Result>{}});
    nameCbMap_[name] = cb;
    // key is serviceTYpe
    AddEvent(name, [this, name, cb]() {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        if (!IsBrowserAvailable(name)) {
            return false;
        }
        if (!MDnsManager::GetInstance().IsAvailableCallback(cb)) {
            return true;
        }
        for (auto &res : browserMap_[name]) {
            std::string fullName = Decorated(res.serviceName + MDNS_DOMAIN_SPLITER_STR + res.serviceType);
            NETMGR_EXT_LOG_W("mdns_log DiscoveryFromNet name:[%{public}s] fullName:[%{public}s]", name.c_str(),
                             fullName.c_str());
            if (cacheMap_.find(fullName) == cacheMap_.end() ||
                (res.state == State::ADD || res.state == State::REFRESH)) {
                NETMGR_EXT_LOG_W("mdns_log HandleServiceFound");
                cb->HandleServiceFound(ConvertResultToInfo(res), NETMANAGER_EXT_SUCCESS);
                res.state = State::LIVE;
            }
            if (res.state == State::REMOVE) {
                res.state = State::DEAD;
                NETMGR_EXT_LOG_D("mdns_log HandleServiceLost");
                cb->HandleServiceLost(ConvertResultToInfo(res), NETMANAGER_EXT_SUCCESS);
                if (cacheMap_.find(fullName) != cacheMap_.end()) {
                    res.state = State::ADD;
                    cacheMap_.erase(fullName);
                }
            }
        }
        return false;
    });

    AddTask([=]() {
            MDnsPayloadParser parser;
            MDnsMessage msg{};
            msg.questions.emplace_back(DNSProto::Question{
                .name = name,
                .qtype = DNSProto::RRTYPE_PTR,
                .qclass = DNSProto::RRCLASS_IN,
            });
            listener_.MulticastAll(parser.ToBytes(msg));
            return true;
        }, false);
    return true;
}

int32_t MDnsProtocolImpl::Discovery(const std::string &serviceType, const sptr<IDiscoveryCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log Discovery");
    DiscoveryFromCache(serviceType, cb);
    DiscoveryFromNet(serviceType, cb);
    return NETMANAGER_EXT_SUCCESS;
}

bool MDnsProtocolImpl::ResolveInstanceFromCache(const std::string &name, const sptr<IResolveCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log ResolveInstanceFromCache");
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    if (!IsInstanceCacheAvailable(name)) {
        NETMGR_EXT_LOG_W("mdns_log ResolveInstanceFromCache cacheMap_ has no element [%{public}s]", name.c_str());
        return false;
    }

    NETMGR_EXT_LOG_I("mdns_log rr.name : [%{public}s]", name.c_str());
    Result r = cacheMap_[name];
    if (IsDomainCacheAvailable(r.domain)) {
        r.ipv6 = cacheMap_[r.domain].ipv6;
        r.addr = cacheMap_[r.domain].addr;

        NETMGR_EXT_LOG_D("mdns_log Add Task DomainCache Available, [%{public}s]", r.domain.c_str());
        AddTask([cb, info = ConvertResultToInfo(r)]() {
            if (nullptr != cb) {
                cb->HandleResolveResult(info, NETMANAGER_EXT_SUCCESS);
            }
            return true;
        });
    } else {
        ResolveFromNet(r.domain, nullptr);
        NETMGR_EXT_LOG_D("mdns_log Add Event DomainCache UnAvailable, [%{public}s]", r.domain.c_str());
        AddEvent(r.domain, [this, cb, r]() mutable {
            if (!IsDomainCacheAvailable(r.domain)) {
                return false;
            }
            r.ipv6 = cacheMap_[r.domain].ipv6;
            r.addr = cacheMap_[r.domain].addr;
            if (nullptr != cb) {
                cb->HandleResolveResult(ConvertResultToInfo(r), NETMANAGER_EXT_SUCCESS);
            }
            return true;
        });
    }
    return true;
}

bool MDnsProtocolImpl::ResolveInstanceFromNet(const std::string &name, const sptr<IResolveCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log ResolveInstanceFromNet");
    {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        cacheMap_[name].state = State::ADD;
        ExtractNameAndType(name, cacheMap_[name].serviceName, cacheMap_[name].serviceType);
    }
    MDnsPayloadParser parser;
    MDnsMessage msg{};
    msg.questions.emplace_back(DNSProto::Question{
        .name = name,
        .qtype = DNSProto::RRTYPE_SRV,
        .qclass = DNSProto::RRCLASS_IN,
    });
    msg.questions.emplace_back(DNSProto::Question{
        .name = name,
        .qtype = DNSProto::RRTYPE_TXT,
        .qclass = DNSProto::RRCLASS_IN,
    });
    msg.header.qdcount = msg.questions.size();
    AddEvent(name, [this, name, cb]() { return ResolveInstanceFromCache(name, cb); });
    ssize_t size = listener_.MulticastAll(parser.ToBytes(msg));
    return size > 0;
}

bool MDnsProtocolImpl::ResolveFromCache(const std::string &domain, const sptr<IResolveCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log ResolveFromCache");
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    if (!IsDomainCacheAvailable(domain)) {
        return false;
    }
    AddTask([this, cb, info = ConvertResultToInfo(cacheMap_[domain])]() {
        if (nullptr != cb) {
            cb->HandleResolveResult(info, NETMANAGER_EXT_SUCCESS);
        }
        return true;
    });
    return true;
}

bool MDnsProtocolImpl::ResolveFromNet(const std::string &domain, const sptr<IResolveCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log ResolveFromNet");
    {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        cacheMap_[domain];
        cacheMap_[domain].domain = domain;
    }
    MDnsPayloadParser parser;
    MDnsMessage msg{};
    msg.questions.emplace_back(DNSProto::Question{
        .name = domain,
        .qtype = DNSProto::RRTYPE_A,
        .qclass = DNSProto::RRCLASS_IN,
    });
    msg.questions.emplace_back(DNSProto::Question{
        .name = domain,
        .qtype = DNSProto::RRTYPE_AAAA,
        .qclass = DNSProto::RRCLASS_IN,
    });
    // key is serviceName
    AddEvent(domain, [this, cb, domain]() { return ResolveFromCache(domain, cb); });
    ssize_t size = listener_.MulticastAll(parser.ToBytes(msg));
    return size > 0;
}

int32_t MDnsProtocolImpl::ResolveInstance(const std::string &instance, const sptr<IResolveCallback> &cb)
{
    NETMGR_EXT_LOG_D("mdns_log execute ResolveInstance");
    if (!IsInstanceValid(instance)) {
        return NET_MDNS_ERR_ILLEGAL_ARGUMENT;
    }
    std::string name = Decorated(instance);
    if (!IsDomainValid(name)) {
        return NET_MDNS_ERR_ILLEGAL_ARGUMENT;
    }
    if (ResolveInstanceFromCache(name, cb)) {
        return NETMANAGER_EXT_SUCCESS;
    }
    return ResolveInstanceFromNet(name, cb) ? NETMANAGER_EXT_SUCCESS : NET_MDNS_ERR_SEND;
}

int32_t MDnsProtocolImpl::Announce(const Result &info, bool off)
{
    NETMGR_EXT_LOG_I("mdns_log Announce message");
    MDnsMessage response{};
    response.header.flags = DNSProto::MDNS_ANSWER_FLAGS;
    std::string name = Decorated(info.serviceName + MDNS_DOMAIN_SPLITER_STR + info.serviceType);
    response.answers.emplace_back(DNSProto::ResourceRecord{.name = Decorated(info.serviceType),
                                                           .rtype = static_cast<uint16_t>(DNSProto::RRTYPE_PTR),
                                                           .rclass = DNSProto::RRCLASS_IN | MDNS_FLUSH_CACHE_BIT,
                                                           .ttl = off ? 0U : DEFAULT_TTL,
                                                           .rdata = name});
    response.answers.emplace_back(DNSProto::ResourceRecord{.name = name,
                                                           .rtype = static_cast<uint16_t>(DNSProto::RRTYPE_SRV),
                                                           .rclass = DNSProto::RRCLASS_IN | MDNS_FLUSH_CACHE_BIT,
                                                           .ttl = off ? 0U : DEFAULT_TTL,
                                                           .rdata = DNSProto::RDataSrv{
                                                               .priority = 0,
                                                               .weight = 0,
                                                               .port = static_cast<uint16_t>(info.port),
                                                               .name = GetHostDomain(),
                                                           }});
    response.answers.emplace_back(DNSProto::ResourceRecord{.name = name,
                                                           .rtype = static_cast<uint16_t>(DNSProto::RRTYPE_TXT),
                                                           .rclass = DNSProto::RRCLASS_IN | MDNS_FLUSH_CACHE_BIT,
                                                           .ttl = off ? 0U : DEFAULT_TTL,
                                                           .rdata = info.txt});
    MDnsPayloadParser parser;
    ssize_t size = listener_.MulticastAll(parser.ToBytes(response));
    return size > 0 ? NETMANAGER_EXT_SUCCESS : NET_MDNS_ERR_SEND;
}

void MDnsProtocolImpl::ReceivePacket(int sock, const MDnsPayload &payload)
{
    if (payload.size() == 0) {
        return;
    }
    MDnsPayloadParser parser;
    MDnsMessage msg = parser.FromBytes(payload);
    if (parser.GetError() != 0) {
        NETMGR_EXT_LOG_E("parser payload failed");
        return;
    }
    if ((msg.header.flags & DNSProto::HEADER_FLAGS_QR_MASK) == 0) {
        ProcessQuestion(sock, msg);
    } else {
        ProcessAnswer(sock, msg);
    }
}

void MDnsProtocolImpl::AppendRecord(std::vector<DNSProto::ResourceRecord> &rrlist, DNSProto::RRType type,
                                    const std::string &name, const std::any &rdata)
{
    rrlist.emplace_back(DNSProto::ResourceRecord{.name = name,
                                                 .rtype = static_cast<uint16_t>(type),
                                                 .rclass = DNSProto::RRCLASS_IN | MDNS_FLUSH_CACHE_BIT,
                                                 .ttl = DEFAULT_TTL,
                                                 .rdata = rdata});
}

void MDnsProtocolImpl::ProcessQuestion(int sock, const MDnsMessage &msg)
{
    const sockaddr *saddrIf = listener_.GetSockAddr(sock);
    if (saddrIf == nullptr) {
        NETMGR_EXT_LOG_W("mdns_log ProcessQuestion saddrIf is null");
        return;
    }
    std::any anyAddr;
    DNSProto::RRType anyAddrType;
    if (saddrIf->sa_family == AF_INET6) {
        anyAddr = reinterpret_cast<const sockaddr_in6 *>(saddrIf)->sin6_addr;
        anyAddrType = DNSProto::RRTYPE_AAAA;
    } else {
        anyAddr = reinterpret_cast<const sockaddr_in *>(saddrIf)->sin_addr;
        anyAddrType = DNSProto::RRTYPE_A;
    }
    int phase = 0;
    MDnsMessage response{};
    response.header.flags = DNSProto::MDNS_ANSWER_FLAGS;
    for (size_t i = 0; i < msg.header.qdcount; ++i) {
        ProcessQuestionRecord(anyAddr, anyAddrType, msg.questions[i], phase, response);
    }
    if (phase < PHASE_DOMAIN) {
        AppendRecord(response.additional, anyAddrType, GetHostDomain(), anyAddr);
    }

    if (phase != 0 && response.answers.size() > 0) {
        listener_.Multicast(sock, MDnsPayloadParser().ToBytes(response));
    }
}

void MDnsProtocolImpl::ProcessQuestionRecord(const std::any &anyAddr, const DNSProto::RRType &anyAddrType,
                                             const DNSProto::Question &qu, int &phase, MDnsMessage &response)
{
    NETMGR_EXT_LOG_D("mdns_log ProcessQuestionRecord");
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    std::string name = qu.name;
    if (qu.qtype == DNSProto::RRTYPE_ANY || qu.qtype == DNSProto::RRTYPE_PTR) {
        std::for_each(srvMap_.begin(), srvMap_.end(), [&](const auto &elem) -> void {
            if (EndsWith(elem.first, name)) {
                AppendRecord(response.answers, DNSProto::RRTYPE_PTR, name, elem.first);
                AppendRecord(response.additional, DNSProto::RRTYPE_SRV, elem.first,
                             DNSProto::RDataSrv{
                                 .priority = 0,
                                 .weight = 0,
                                 .port = static_cast<uint16_t>(elem.second.port),
                                 .name = GetHostDomain(),
                             });
                AppendRecord(response.additional, DNSProto::RRTYPE_TXT, elem.first, elem.second.txt);
            }
        });
        phase = std::max(phase, PHASE_PTR);
    }
    if (qu.qtype == DNSProto::RRTYPE_ANY || qu.qtype == DNSProto::RRTYPE_SRV) {
        auto iter = srvMap_.find(name);
        if (iter == srvMap_.end()) {
            return;
        }
        AppendRecord(response.answers, DNSProto::RRTYPE_SRV, name,
                     DNSProto::RDataSrv{
                         .priority = 0,
                         .weight = 0,
                         .port = static_cast<uint16_t>(iter->second.port),
                         .name = GetHostDomain(),
                     });
        phase = std::max(phase, PHASE_SRV);
    }
    if (qu.qtype == DNSProto::RRTYPE_ANY || qu.qtype == DNSProto::RRTYPE_TXT) {
        auto iter = srvMap_.find(name);
        if (iter == srvMap_.end()) {
            return;
        }
        AppendRecord(response.answers, DNSProto::RRTYPE_TXT, name, iter->second.txt);
        phase = std::max(phase, PHASE_SRV);
    }
    if (qu.qtype == DNSProto::RRTYPE_ANY || qu.qtype == DNSProto::RRTYPE_A || qu.qtype == DNSProto::RRTYPE_AAAA) {
        if (name != GetHostDomain() || (qu.qtype != DNSProto::RRTYPE_ANY && anyAddrType != qu.qtype)) {
            return;
        }
        AppendRecord(response.answers, anyAddrType, name, anyAddr);
        phase = std::max(phase, PHASE_DOMAIN);
    }
}

void MDnsProtocolImpl::ProcessAnswer(int sock, const MDnsMessage &msg)
{
    const sockaddr *saddrIf = listener_.GetSockAddr(sock);
    if (saddrIf == nullptr) {
        return;
    }
    bool v6 = (saddrIf->sa_family == AF_INET6);
    std::set<std::string> changed;
    for (const auto &answer : msg.answers) {
        ProcessAnswerRecord(v6, answer, changed);
    }
    for (const auto &i : msg.additional) {
        ProcessAnswerRecord(v6, i, changed);
    }
    for (const auto &i : changed) {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        RunTaskQueue(taskOnChange_[i]);
        KillCache(i);
    }
}

void MDnsProtocolImpl::UpdatePtr(bool v6, const DNSProto::ResourceRecord &rr, std::set<std::string> &changed)
{
    const std::string *data = std::any_cast<std::string>(&rr.rdata);
    if (data == nullptr) {
        return;
    }

    std::string name = rr.name;
    if (browserMap_.find(name) == browserMap_.end()) {
        return;
    }
    auto &results = browserMap_[name];
    std::string srvName;
    std::string srvType;
    ExtractNameAndType(*data, srvName, srvType);
    if (srvName.empty() || srvType.empty()) {
        return;
    }
    auto res =
        std::find_if(results.begin(), results.end(), [&](const auto &elem) { return elem.serviceName == srvName; });
    if (res == results.end()) {
        results.emplace_back(Result{
            .serviceName = srvName,
            .serviceType = srvType,
            .state = State::ADD,
        });
    }
    res = std::find_if(results.begin(), results.end(), [&](const auto &elem) { return elem.serviceName == srvName; });
    if (res->serviceName != srvName || res->state == State::DEAD) {
        res->state = State::REFRESH;
        res->serviceName = srvName;
    }
    if (rr.ttl == 0) {
        res->state = State::REMOVE;
    }
    if (res->state != State::LIVE && res->state != State::DEAD) {
        changed.emplace(name);
    }
    res->ttl = rr.ttl;
    res->refrehTime = MilliSecondsSinceEpoch();
}

void MDnsProtocolImpl::UpdateSrv(bool v6, const DNSProto::ResourceRecord &rr, std::set<std::string> &changed)
{
    const DNSProto::RDataSrv *srv = std::any_cast<DNSProto::RDataSrv>(&rr.rdata);
    if (srv == nullptr) {
        return;
    }
    std::string name = rr.name;
    if (cacheMap_.find(name) == cacheMap_.end()) {
        ExtractNameAndType(name, cacheMap_[name].serviceName, cacheMap_[name].serviceType);
        cacheMap_[name].state = State::ADD;
        cacheMap_[name].domain = srv->name;
        cacheMap_[name].port = srv->port;
    }
    Result &result = cacheMap_[name];
    if (result.domain != srv->name || result.port != srv->port || result.state == State::DEAD) {
        if (result.state != State::ADD) {
            result.state = State::REFRESH;
        }
        result.domain = srv->name;
        result.port = srv->port;
    }
    if (rr.ttl == 0) {
        result.state = State::REMOVE;
    }
    if (result.state != State::LIVE && result.state != State::DEAD) {
        changed.emplace(name);
    }
    result.ttl = rr.ttl;
    result.refrehTime = MilliSecondsSinceEpoch();
}

void MDnsProtocolImpl::UpdateTxt(bool v6, const DNSProto::ResourceRecord &rr, std::set<std::string> &changed)
{
    const TxtRecordEncoded *txt = std::any_cast<TxtRecordEncoded>(&rr.rdata);
    if (txt == nullptr) {
        return;
    }
    std::string name = rr.name;
    if (cacheMap_.find(name) == cacheMap_.end()) {
        ExtractNameAndType(name, cacheMap_[name].serviceName, cacheMap_[name].serviceType);
        cacheMap_[name].state = State::ADD;
        cacheMap_[name].txt = *txt;
    }
    Result &result = cacheMap_[name];
    if (result.txt != *txt || result.state == State::DEAD) {
        if (result.state != State::ADD) {
            result.state = State::REFRESH;
        }
        result.txt = *txt;
    }
    if (rr.ttl == 0) {
        result.state = State::REMOVE;
    }
    if (result.state != State::LIVE && result.state != State::DEAD) {
        changed.emplace(name);
    }
    result.ttl = rr.ttl;
    result.refrehTime = MilliSecondsSinceEpoch();
}

void MDnsProtocolImpl::UpdateAddr(bool v6, const DNSProto::ResourceRecord &rr, std::set<std::string> &changed)
{
    if (v6 != (rr.rtype == DNSProto::RRTYPE_AAAA)) {
        return;
    }
    const std::string addr = AddrToString(rr.rdata);
    bool v6rr = (rr.rtype == DNSProto::RRTYPE_AAAA);
    if (addr.empty()) {
        return;
    }
    std::string name = rr.name;
    if (cacheMap_.find(name) == cacheMap_.end()) {
        ExtractNameAndType(name, cacheMap_[name].serviceName, cacheMap_[name].serviceType);
        cacheMap_[name].state = State::ADD;
        cacheMap_[name].ipv6 = v6rr;
        cacheMap_[name].addr = addr;
    }
    Result &result = cacheMap_[name];
    if (result.addr != addr || result.ipv6 != v6rr || result.state == State::DEAD) {
        result.state = State::REFRESH;
        result.addr = addr;
        result.ipv6 = v6rr;
    }
    if (rr.ttl == 0) {
        result.state = State::REMOVE;
    }
    if (result.state != State::LIVE && result.state != State::DEAD) {
        changed.emplace(name);
    }
    result.ttl = rr.ttl;
    result.refrehTime = MilliSecondsSinceEpoch();
}

void MDnsProtocolImpl::ProcessAnswerRecord(bool v6, const DNSProto::ResourceRecord &rr, std::set<std::string> &changed)
{
    NETMGR_EXT_LOG_D("mdns_log ProcessAnswerRecord, type=[%{public}d]", rr.rtype);
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    std::string name = rr.name;
    if (cacheMap_.find(name) == cacheMap_.end() && browserMap_.find(name) == browserMap_.end() &&
        srvMap_.find(name) != srvMap_.end()) {
        return;
    }
    if (rr.rtype == DNSProto::RRTYPE_PTR) {
        UpdatePtr(v6, rr, changed);
    } else if (rr.rtype == DNSProto::RRTYPE_SRV) {
        UpdateSrv(v6, rr, changed);
    } else if (rr.rtype == DNSProto::RRTYPE_TXT) {
        UpdateTxt(v6, rr, changed);
    } else if (rr.rtype == DNSProto::RRTYPE_A || rr.rtype == DNSProto::RRTYPE_AAAA) {
        UpdateAddr(v6, rr, changed);
    } else {
        NETMGR_EXT_LOG_D("mdns_log Unknown packet received, type=[%{public}d]", rr.rtype);
    }
}

std::string MDnsProtocolImpl::GetHostDomain()
{
    if (config_.hostname.empty()) {
        char buffer[MDNS_MAX_DOMAIN_LABEL];
        if (gethostname(buffer, sizeof(buffer)) == 0) {
            config_.hostname = buffer;
            static auto uid = []() {
                std::random_device rd;
                return rd();
            }();
            config_.hostname += std::to_string(uid);
        }
    }
    return Decorated(config_.hostname);
}

void MDnsProtocolImpl::AddTask(const Task &task, bool atonce)
{
    {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        taskQueue_.emplace_back(task);
    }
    if (atonce) {
        listener_.TriggerRefresh();
    }
}

MDnsServiceInfo MDnsProtocolImpl::ConvertResultToInfo(const MDnsProtocolImpl::Result &result)
{
    MDnsServiceInfo info;
    info.name = result.serviceName;
    info.type = result.serviceType;
    if (!result.addr.empty()) {
        info.family = result.ipv6 ? MDnsServiceInfo::IPV6 : MDnsServiceInfo::IPV4;
    }
    info.addr = result.addr;
    info.port = result.port;
    info.txtRecord = result.txt;
    return info;
}

bool MDnsProtocolImpl::IsCacheAvailable(const std::string &key)
{
    constexpr int64_t ms2S = 1000LL;
    NETMGR_EXT_LOG_D("mdns_log IsCacheAvailable, ttl=[%{public}u]", cacheMap_[key].ttl);
    return cacheMap_.find(key) != cacheMap_.end() &&
           (ms2S * cacheMap_[key].ttl) > static_cast<uint32_t>(MilliSecondsSinceEpoch() - cacheMap_[key].refrehTime);
}

bool MDnsProtocolImpl::IsDomainCacheAvailable(const std::string &key)
{
    return IsCacheAvailable(key) && !cacheMap_[key].addr.empty();
}

bool MDnsProtocolImpl::IsInstanceCacheAvailable(const std::string &key)
{
    return IsCacheAvailable(key) && !cacheMap_[key].domain.empty();
}

bool MDnsProtocolImpl::IsBrowserAvailable(const std::string &key)
{
    return browserMap_.find(key) != browserMap_.end() && !browserMap_[key].empty();
}

void MDnsProtocolImpl::AddEvent(const std::string &key, const Task &task)
{
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    taskOnChange_[key].emplace_back(task);
}

void MDnsProtocolImpl::RunTaskQueue(std::list<Task> &queue)
{
    std::list<Task> tmp;
    for (auto &&func : queue) {
        if (!func()) {
            tmp.emplace_back(func);
        }
    }
    tmp.swap(queue);
}

void MDnsProtocolImpl::KillCache(const std::string &key)
{
    NETMGR_EXT_LOG_D("mdns_log KillCache");
    if (IsBrowserAvailable(key) && browserMap_.find(key) != browserMap_.end()) {
        for (auto it = browserMap_[key].begin(); it != browserMap_[key].end();) {
            KillBrowseCache(key, it);
        }
    }
    if (IsCacheAvailable(key)) {
        std::lock_guard<std::recursive_mutex> guard(mutex_);
        auto &elem = cacheMap_[key];
        if (elem.state == State::REMOVE) {
            elem.state = State::DEAD;
            cacheMap_.erase(key);
        } else if (elem.state == State::ADD || elem.state == State::REFRESH) {
            elem.state = State::LIVE;
        }
    }
}

void MDnsProtocolImpl::KillBrowseCache(const std::string &key, std::vector<Result>::iterator &it)
{
    NETMGR_EXT_LOG_D("mdns_log KillBrowseCache");
    if (it->state == State::REMOVE) {
        it->state = State::DEAD;
        if (nameCbMap_.find(key) != nameCbMap_.end()) {
            NETMGR_EXT_LOG_D("mdns_log HandleServiceLost");
            nameCbMap_[key]->HandleServiceLost(ConvertResultToInfo(*it), NETMANAGER_EXT_SUCCESS);
        }
        std::string fullName = Decorated(it->serviceName + MDNS_DOMAIN_SPLITER_STR + it->serviceType);
        cacheMap_.erase(fullName);
        it = browserMap_[key].erase(it);
    } else if (it->state == State::ADD || it->state == State::REFRESH) {
        it->state = State::LIVE;
        it++;
    } else {
        it++;
    }
}

int32_t MDnsProtocolImpl::StopCbMap(const std::string &serviceType)
{
    NETMGR_EXT_LOG_D("mdns_log StopCbMap");
    std::lock_guard<std::recursive_mutex> guard(mutex_);
    std::string name = Decorated(serviceType);
    sptr<IDiscoveryCallback> cb = nullptr;
    if (nameCbMap_.find(name) != nameCbMap_.end()) {
        cb = nameCbMap_[name];
        nameCbMap_.erase(name);
    }
    taskOnChange_.erase(name);
    auto it = browserMap_.find(name);
    if (it != browserMap_.end()) {
        if (cb != nullptr) {
            NETMGR_EXT_LOG_I("mdns_log StopCbMap res size:[%{public}zu]", it->second.size());
            for (auto &&res : it->second) {
                NETMGR_EXT_LOG_W("mdns_log HandleServiceLost");
                cb->HandleServiceLost(ConvertResultToInfo(res), NETMANAGER_EXT_SUCCESS);
            }
        }
        browserMap_.erase(name);
    }
    return NETMANAGER_SUCCESS;
}
} // namespace NetManagerStandard
} // namespace OHOS