// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.

#include <RB_INC_IFADDRS_H

#ifdef HAVE_IFADDRS_H
bool
ircd::net::addrs::has_usable_ipv6_interface()
try
{
	return !for_each([]
	(const addr &a) noexcept
	{
		if(a.family != AF_INET6)
			return true;

		if(a.scope_id != 0) // global scope
			return true;

		if(~a.flags & IFF_UP) // not up
			return true;

		if(a.flags & IFF_LOOPBACK) // not usable
			return true;

		// return false to break
		return false;
	});
}
catch(const std::exception &e)
{
	log::error
	{
		log, "Failed to check for usable IPv6 interfaces :%s",
		e.what()
	};

	return false;
}
#else
bool
ircd::net::addrs::has_usable_ipv6_interface()
{
	return false;
}
#endif

#ifdef HAVE_IFADDRS_H
[[GCC::optimize(0), clang::optnone]] //XXX: trouble
bool
ircd::net::addrs::for_each(const closure &closure)
{
	return for_each(raw_closure{[&closure]
	(const struct ::ifaddrs *const &ifa)
	{
		addr a;
		a.name = ifa->ifa_name;
		a.flags = ifa->ifa_flags;
		if(ifa->ifa_addr) switch(ifa->ifa_addr->sa_family)
		{
			case AF_INET6:
			{
				const auto sin(reinterpret_cast<const struct ::sockaddr_in6 *>(ifa->ifa_addr));
				const auto ip(reinterpret_cast<const uint128_t *>(sin->sin6_addr.s6_addr));
				a.family = sin->sin6_family;
				a.scope_id = sin->sin6_scope_id;
				a.flowinfo = sin->sin6_flowinfo;
				a.address =
				{
					ntoh(*ip), sin->sin6_port
				};
				break;
			}

			case AF_INET:
			{
				const auto &sin(reinterpret_cast<const struct ::sockaddr_in *>(ifa->ifa_addr));
				a.family = sin->sin_family;
				a.address =
				{
					ntoh(sin->sin_addr.s_addr), sin->sin_port
				};
				break;
			}

			default:
				return true;
		}

		return closure(a);
	}});
}
#else
bool
ircd::net::addrs::for_each(const closure &closure)
{
	return true;
}
#endif

#ifdef HAVE_IFADDRS_H
bool
ircd::net::addrs::for_each(const raw_closure &closure)
{
	struct ::ifaddrs *ifap_;
	syscall(::getifaddrs, &ifap_);
	const custom_ptr<struct ::ifaddrs> ifap
	{
		ifap_, ::freeifaddrs
	};

	for(auto ifa(ifap.get()); ifa; ifa = ifa->ifa_next)
		if(!closure(ifa))
			return false;

	return true;
}
#else
bool
ircd::net::addrs::for_each(const raw_closure &closure)
{
	return true;
}
#endif