//
// IPAddress.cpp
//
// Library: Net
// Package: NetCore
// Module:  IPAddress
//
// Copyright (c) 2005-2011, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/Net/IPAddress.h"
#include "Poco/Net/NetException.h"
#include "Poco/RefCountedObject.h"
#include "Poco/NumberFormatter.h"
#include "Poco/BinaryReader.h"
#include "Poco/BinaryWriter.h"
#include "Poco/String.h"
#include "Poco/Format.h"
#include "Poco/Types.h"


using Poco::RefCountedObject;
using Poco::NumberFormatter;
using Poco::BinaryReader;
using Poco::BinaryWriter;
using Poco::toLower;
using Poco::trim;
using Poco::UInt8;
using Poco::UInt16;
using Poco::UInt32;
using Poco::Net::Impl::IPAddressImpl;
using Poco::Net::Impl::IPv4AddressImpl;
#if defined(POCO_HAVE_IPv6)
using Poco::Net::Impl::IPv6AddressImpl;
#endif


namespace Poco {
namespace Net {


#if !defined(_MSC_VER) || defined(__STDC__)
// Go home MSVC, you're drunk...
// See http://stackoverflow.com/questions/5899857/multiple-definition-error-for-static-const-class-members
const IPAddress::Family IPAddress::IPv4;
#if defined(POCO_HAVE_IPv6)
const IPAddress::Family IPAddress::IPv6;
#endif
#endif


IPAddress::IPAddress()
{
	newIPv4();
}


IPAddress::IPAddress(const IPAddress& addr)
{
	if (addr.family() == IPv4)
		newIPv4(addr.addr());
#if defined(POCO_HAVE_IPv6)
	else
		newIPv6(addr.addr(), addr.scope());
#endif
}


IPAddress::IPAddress(IPAddress&& addr): _pImpl(std::move(addr._pImpl))
{
}


IPAddress::IPAddress(Family family)
{
	if (family == IPv4)
		newIPv4();
#if defined(POCO_HAVE_IPv6)
	else if (family == IPv6)
		newIPv6();
#endif
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


IPAddress::IPAddress(const std::string& addr)
{
	IPv4AddressImpl empty4 = IPv4AddressImpl();
	if (addr.empty() || trim(addr) == "0.0.0.0")
	{
		newIPv4(empty4.addr());
		return;
	}

	IPv4AddressImpl addr4(IPv4AddressImpl::parse(addr));
	if (addr4 != empty4)
	{
		newIPv4(addr4.addr());
		return;
	}

#if defined(POCO_HAVE_IPv6)
	IPv6AddressImpl empty6 = IPv6AddressImpl();
	if (addr.empty() || trimIPv6(addr) == "::")
	{
		newIPv6(empty6.addr());
		return;
	}

	IPv6AddressImpl addr6(IPv6AddressImpl::parse(addr));
	if (addr6 != IPv6AddressImpl())
	{
		newIPv6(addr6.addr(), addr6.scope());
		return;
	}
#endif

	throw InvalidAddressException(addr);
}


IPAddress::IPAddress(const std::string& addr, Family family)
{
	if (family == IPv4)
	{
		IPv4AddressImpl addr4(IPv4AddressImpl::parse(addr));
		newIPv4(addr4.addr());
		return;
	}
#if defined(POCO_HAVE_IPv6)
	else if (family == IPv6)
	{
		IPv6AddressImpl addr6(IPv6AddressImpl::parse(addr));
		newIPv6(addr6.addr(), addr6.scope());
		return;
	}
#endif
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


IPAddress::IPAddress(const void* addr, poco_socklen_t length)
	: _pImpl(nullptr)
{
	if (length == sizeof(struct in_addr))
		newIPv4(addr);
#if defined(POCO_HAVE_IPv6)
	else if (length == sizeof(struct in6_addr))
		newIPv6(addr);
#endif
	else throw Poco::InvalidArgumentException("Invalid address length passed to IPAddress()");
}


IPAddress::IPAddress(const void* addr, poco_socklen_t length, Poco::UInt32 scope)
{
	if (length == sizeof(struct in_addr))
		newIPv4(addr);
#if defined(POCO_HAVE_IPv6)
	else if (length == sizeof(struct in6_addr))
		newIPv6(addr, scope);
#endif
	else throw Poco::InvalidArgumentException("Invalid address length passed to IPAddress()");
}


IPAddress::IPAddress(unsigned prefix, Family family)
{
	if (family == IPv4)
	{
		if (prefix <= 32)
			newIPv4(prefix);
		else
			throw Poco::InvalidArgumentException("Invalid prefix length passed to IPAddress()");
	}
#if defined(POCO_HAVE_IPv6)
	else if (family == IPv6)
	{
		if (prefix <= 128)
			newIPv6(prefix);
		else
			throw Poco::InvalidArgumentException("Invalid prefix length passed to IPAddress()");
	}
#endif
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


#if defined(_WIN32)
IPAddress::IPAddress(const SOCKET_ADDRESS& socket_address)
	: _pImpl(nullptr)
{
	ADDRESS_FAMILY family = socket_address.lpSockaddr->sa_family;
	if (family == AF_INET)
		newIPv4(&reinterpret_cast<const struct sockaddr_in*>(socket_address.lpSockaddr)->sin_addr);
#if defined(POCO_HAVE_IPv6)
	else if (family == AF_INET6)
		newIPv6(&reinterpret_cast<const struct sockaddr_in6*>(socket_address.lpSockaddr)->sin6_addr,
			reinterpret_cast<const struct sockaddr_in6*>(socket_address.lpSockaddr)->sin6_scope_id);
#endif
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}
#endif


IPAddress::IPAddress(const struct sockaddr& sockaddr)
{
	unsigned short family = sockaddr.sa_family;
	if (family == AF_INET)
		newIPv4(&reinterpret_cast<const struct sockaddr_in*>(&sockaddr)->sin_addr);
#if defined(POCO_HAVE_IPv6)
	else if (family == AF_INET6)
		newIPv6(&reinterpret_cast<const struct sockaddr_in6*>(&sockaddr)->sin6_addr,
			reinterpret_cast<const struct sockaddr_in6*>(&sockaddr)->sin6_scope_id);
#endif
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


IPAddress::~IPAddress()
{
}


IPAddress& IPAddress::operator = (const IPAddress& addr)
{
	if (&addr != this)
	{
		if (addr.family() == IPAddress::IPv4)
			newIPv4(addr.addr());
#if defined(POCO_HAVE_IPv6)
		else if (addr.family() == IPAddress::IPv6)
			newIPv6(addr.addr(), addr.scope());
#endif
		else
			throw Poco::InvalidArgumentException("Invalid or unsupported address family");
	}
	return *this;
}


IPAddress& IPAddress::operator = (IPAddress&& addr)
{
	_pImpl = std::move(addr._pImpl);
	return *this;
}


IPAddress::Family IPAddress::family() const
{
	return pImpl()->family();
}


Poco::UInt32 IPAddress::scope() const
{
	return pImpl()->scope();
}


std::string IPAddress::toString() const
{
	return pImpl()->toString();
}


bool IPAddress::isWildcard() const
{
	return pImpl()->isWildcard();
}


bool IPAddress::isBroadcast() const
{
	return pImpl()->isBroadcast();
}


bool IPAddress::isLoopback() const
{
	return pImpl()->isLoopback();
}


bool IPAddress::isMulticast() const
{
	return pImpl()->isMulticast();
}


bool IPAddress::isUnicast() const
{
	return !isWildcard() && !isBroadcast() && !isMulticast();
}


bool IPAddress::isLinkLocal() const
{
	return pImpl()->isLinkLocal();
}


bool IPAddress::isSiteLocal() const
{
	return pImpl()->isSiteLocal();
}


bool IPAddress::isIPv4Compatible() const
{
	return pImpl()->isIPv4Compatible();
}


bool IPAddress::isIPv4Mapped() const
{
	return pImpl()->isIPv4Mapped();
}


bool IPAddress::isWellKnownMC() const
{
	return pImpl()->isWellKnownMC();
}


bool IPAddress::isNodeLocalMC() const
{
	return pImpl()->isNodeLocalMC();
}


bool IPAddress::isLinkLocalMC() const
{
	return pImpl()->isLinkLocalMC();
}


bool IPAddress::isSiteLocalMC() const
{
	return pImpl()->isSiteLocalMC();
}


bool IPAddress::isOrgLocalMC() const
{
	return pImpl()->isOrgLocalMC();
}


bool IPAddress::isGlobalMC() const
{
	return pImpl()->isGlobalMC();
}


bool IPAddress::operator == (const IPAddress& a) const
{
	poco_socklen_t l1 = length();
	poco_socklen_t l2 = a.length();
	if (l1 == l2)
	{
#if defined(POCO_HAVE_IPv6)
		if ( scope() != a.scope() )
			return false;
#endif
		return std::memcmp(addr(), a.addr(), l1) == 0;
	}
	else return false;
}


bool IPAddress::operator != (const IPAddress& a) const
{
	return !(*this == a);
}


bool IPAddress::operator < (const IPAddress& a) const
{
	poco_socklen_t l1 = length();
	poco_socklen_t l2 = a.length();
	if (l1 == l2)
	{
#if defined(POCO_HAVE_IPv6)
		if ( scope() != a.scope() )
			return scope() < a.scope();
#endif
		return std::memcmp(addr(), a.addr(), l1) < 0;
	}
	else return l1 < l2;
}


bool IPAddress::operator <= (const IPAddress& a) const
{
	return !(a < *this);
}


bool IPAddress::operator > (const IPAddress& a) const
{
	return a < *this;
}


bool IPAddress::operator >= (const IPAddress& a) const
{
	return !(*this < a);
}


IPAddress IPAddress::operator & (const IPAddress& other) const
{
	if (family() == other.family())
	{
		if (family() == IPv4)
		{
			IPv4AddressImpl t(pImpl()->addr());
			IPv4AddressImpl o(other.pImpl()->addr());
			return IPAddress((t & o).addr(), sizeof(struct in_addr));
		}
#if defined(POCO_HAVE_IPv6)
		else if (family() == IPv6)
		{
			const IPv6AddressImpl t(pImpl()->addr(), pImpl()->scope());
			const IPv6AddressImpl o(other.pImpl()->addr(), other.pImpl()->scope());
			const IPv6AddressImpl r = t & o;
			return IPAddress(r.addr(), sizeof(struct in6_addr), r.scope());
		}
#endif
		else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
	}
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


IPAddress IPAddress::operator | (const IPAddress& other) const
{
	if (family() == other.family())
	{
		if (family() == IPv4)
		{
			IPv4AddressImpl t(pImpl()->addr());
			IPv4AddressImpl o(other.pImpl()->addr());
			return IPAddress((t | o).addr(), sizeof(struct in_addr));
		}
#if defined(POCO_HAVE_IPv6)
		else if (family() == IPv6)
		{
			const IPv6AddressImpl t(pImpl()->addr(), pImpl()->scope());
			const IPv6AddressImpl o(other.pImpl()->addr(), other.pImpl()->scope());
			const IPv6AddressImpl r = t | o;
			return IPAddress(r.addr(), sizeof(struct in6_addr), r.scope());
		}
#endif
		else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
	}
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


IPAddress IPAddress::operator ^ (const IPAddress& other) const
{
	if (family() == other.family())
	{
		if (family() == IPv4)
		{
			IPv4AddressImpl t(pImpl()->addr());
			IPv4AddressImpl o(other.pImpl()->addr());
			return IPAddress((t ^ o).addr(), sizeof(struct in_addr));
		}
#if defined(POCO_HAVE_IPv6)
		else if (family() == IPv6)
		{
			const IPv6AddressImpl t(pImpl()->addr(), pImpl()->scope());
			const IPv6AddressImpl o(other.pImpl()->addr(), other.pImpl()->scope());
			const IPv6AddressImpl r = t ^ o;
			return IPAddress(r.addr(), sizeof(struct in6_addr), r.scope());
		}
#endif
		else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
	}
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


IPAddress IPAddress::operator ~ () const
{
	if (family() == IPv4)
	{
		IPv4AddressImpl self(this->pImpl()->addr());
		return IPAddress((~self).addr(), sizeof(struct in_addr));
	}
#if defined(POCO_HAVE_IPv6)
	else if (family() == IPv6)
	{
		const IPv6AddressImpl self(pImpl()->addr(), pImpl()->scope());
		const IPv6AddressImpl r = ~self;
		return IPAddress(r.addr(), sizeof(struct in6_addr), r.scope());
	}
#endif
	else throw Poco::InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}


poco_socklen_t IPAddress::length() const
{
	return pImpl()->length();
}


const void* IPAddress::addr() const
{
	return pImpl()->addr();
}


int IPAddress::af() const
{
	return pImpl()->af();
}


unsigned IPAddress::prefixLength() const
{
	return pImpl()->prefixLength();
}


std::string& IPAddress::compressV6(std::string& v6addr)
{
	// get rid of leading zeros at the beginning
	while (v6addr.size() && v6addr[0] == '0') v6addr.erase(v6addr.begin());

	// get rid of leading zeros in the middle
	while (v6addr.find(":0") != std::string::npos)
		Poco::replaceInPlace(v6addr, ":0", ":");

	// get rid of extraneous colons
	while (v6addr.find(":::") != std::string::npos)
		Poco::replaceInPlace(v6addr, ":::", "::");

	return v6addr;
}


std::string IPAddress::trimIPv6(const std::string& v6Addr)
{
	std::string v6addr(v6Addr);
	std::string::size_type len = v6addr.length();
	int dblColOcc = 0;
	auto pos = v6addr.find("::");
	while ((pos <= len-2) && (pos != std::string::npos))
	{
		++dblColOcc;
		pos = v6addr.find("::", pos + 2);
	}

	if ((dblColOcc > 1) ||
		(std::count(v6addr.begin(), v6addr.end(), ':') > 8) ||
		(v6addr.find(":::") != std::string::npos) ||
		((len >= 2) && ((v6addr[len-1] == ':') && v6addr[len-2] != ':')))
	{
		return v6addr;
	}

	return compressV6(v6addr);
}


IPAddress IPAddress::parse(const std::string& addr)
{
	return IPAddress(addr);
}


bool IPAddress::tryParse(const std::string& addr, IPAddress& result)
{
	IPv4AddressImpl impl4(IPv4AddressImpl::parse(addr));
	if (impl4 != IPv4AddressImpl() || trim(addr) == "0.0.0.0")
	{
		result.newIPv4(impl4.addr());
		return true;
	}
#if defined(POCO_HAVE_IPv6)
	IPv6AddressImpl impl6(IPv6AddressImpl::parse(addr));
	if (impl6 != IPv6AddressImpl() || trimIPv6(addr) == "::")
	{
		result.newIPv6(impl6.addr(), impl6.scope());
		return true;
	}
#endif
	return false;
}


void IPAddress::mask(const IPAddress& mask)
{
	IPAddress null;
	pImpl()->mask(mask.pImpl(), null.pImpl());
}


void IPAddress::mask(const IPAddress& mask, const IPAddress& set)
{
	pImpl()->mask(mask.pImpl(), set.pImpl());
}


IPAddress IPAddress::wildcard(Family family)
{
	return IPAddress(family);
}


IPAddress IPAddress::broadcast()
{
	struct in_addr ia;
	ia.s_addr = INADDR_NONE;
	return IPAddress(&ia, sizeof(ia));
}


IPAddress::RawIPv4 IPAddress::toV4Bytes() const
{
	if (family() != IPv4)
		throw Poco::InvalidAccessException(Poco::format("IPAddress::toV4Bytes(%d)", (int)family()));

	RawIPv4 bytes;
	std::memcpy(&bytes[0], addr(), IPv4Size);
	return bytes;
}


IPAddress::RawIPv6 IPAddress::toV6Bytes() const
{
	if (family() != IPv6)
		throw Poco::InvalidAccessException(Poco::format("IPAddress::toV6Bytes(%d)", (int)family()));

	RawIPv6 bytes;
	std::memcpy(&bytes[0], addr(), IPv6Size);
	return bytes;
}


std::vector<unsigned char> IPAddress::toBytes() const
{
	std::size_t sz = 0;
	std::vector<unsigned char> bytes;
	const void* ptr = nullptr;
	switch (family())
	{
		case IPv4:
			sz = sizeof(in_addr);
			ptr = addr();
			break;
#if defined(POCO_HAVE_IPv6)
		case IPv6:
			sz = sizeof(in6_addr);
			ptr = addr();
			break;
#endif
		default:
			throw Poco::IllegalStateException(Poco::format("IPAddress::toBytes(%d)", (int)family()));
	}
	bytes.resize(sz);
	std::memcpy(&bytes[0], ptr, sz);
	return bytes;
}


Poco::BinaryWriter& operator << (Poco::BinaryWriter& writer, const Poco::Net::IPAddress& value)
{
	writer << static_cast<Poco::UInt8>(value.length());
	writer.writeRaw(reinterpret_cast<const char*>(value.addr()), value.length());
	return writer;
}


Poco::BinaryReader& operator >> (Poco::BinaryReader& reader, Poco::Net::IPAddress& value)
{
	char buf[Poco::Net::IPAddress::MAX_ADDRESS_LENGTH];
	Poco::UInt8 length;
	reader >> length;
	reader.readRaw(buf, length);
	value = Poco::Net::IPAddress(buf, length);
	return reader;
}


std::ostream& operator << (std::ostream& ostr, const Poco::Net::IPAddress& addr)
{
	ostr << addr.toString();
	return ostr;
}


} } // namespace Poco::Net

