#include "network.hpp"

#include <cstring>
#include <ifaddrs.h>
#include <stdexcept>
#include <netinet/in.h>


namespace drp::util::network {


bool is_localhost(const sockaddr_storage& address, const socklen_t addressLength) {
    // iterate through all the interfaces
    ifaddrs* interfaces;
    if (getifaddrs(&interfaces) == -1)
        throw std::runtime_error("Could not get network interfaces.");

    bool result = false;
    for (const ifaddrs *interface = interfaces; interface; interface = interface->ifa_next) {
        // if the interface family does not correspond to the server candidate, ignore it
        if (interface->ifa_addr->sa_family != address.ss_family)
            continue;

        switch (address.ss_family) {
            case AF_INET: {
                const auto interfaceIpv4 = reinterpret_cast<const sockaddr_in*>(interface->ifa_addr);
                const auto addressIpv4 = reinterpret_cast<const sockaddr_in*>(&address);

                // the family have already been checked.
                if (std::memcmp(&interfaceIpv4->sin_addr, &addressIpv4->sin_addr, sizeof(in_addr)) == 0)
                    result = true;

                break;
            }

            case AF_INET6: {
                const auto interfaceIpv6 = reinterpret_cast<const sockaddr_in6*>(interface->ifa_addr);
                const auto addressIpv6 = reinterpret_cast<const sockaddr_in6*>(&address);

                // the interface and the family have already been checked.
                if (std::memcmp(&interfaceIpv6->sin6_addr, &addressIpv6->sin6_addr, sizeof(in6_addr)) == 0)
                    result = true;

                break;
            }

            default:
                throw std::runtime_error("Unknown address family.");
        }

        if (result == true)
            break;
    }

    freeifaddrs(interfaces);
    return result;
}


}
