//
// SocketAddress.h
//
// Library: Net
// Package: NetCore
// Module:  SocketAddress
//
// Definition of the SocketAddress class.
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#ifndef Net_SocketAddress_INCLUDED
#define Net_SocketAddress_INCLUDED


#include "Poco/Net/Net.h"
#include "Poco/Net/SocketAddressImpl.h"
#include <ostream>


namespace Poco {

class BinaryReader;
class BinaryWriter;

namespace Net {


class IPAddress;


class Net_API SocketAddress
	/// This class represents an internet (IP) endpoint/socket
	/// address. The address can belong either to the
	/// IPv4, IPv6 or Unix local family.
	/// IP addresses consist of a host address and a port number.
	/// An Unix local socket address consists of a path to socket file.
	/// Abstract local sockets, which operate without the need for
	/// interaction with the filesystem, are supported on Linux only.
{
public:
	// The following declarations keep the Family type
	// backwards compatible with the previously used
	// enum declaration.
	using Family = AddressFamily::Family;
	static const Family IPv4 = AddressFamily::IPv4;
#if defined(POCO_HAVE_IPv6)
	static const Family IPv6 = AddressFamily::IPv6;
#endif
#if defined(POCO_HAS_UNIX_SOCKET)
	static const Family UNIX_LOCAL = AddressFamily::UNIX_LOCAL;
#endif

	SocketAddress();
		/// Creates a wildcard (all zero) IPv4 SocketAddress.

	explicit SocketAddress(Family family);
		/// Creates a SocketAddress with unspecified (wildcard) IP address
		/// of the given family.

	SocketAddress(const IPAddress& hostAddress, Poco::UInt16 portNumber);
		/// Creates a SocketAddress from an IP address and given port number.

	explicit SocketAddress(Poco::UInt16 port);
		/// Creates a SocketAddress with unspecified (wildcard) IP address
		/// and given port number.

	SocketAddress(Family family, Poco::UInt16 port);
		/// Creates a SocketAddress with unspecified (wildcard) IP address
		/// of the given family, and given port number.

	SocketAddress(const std::string& hostAddress, Poco::UInt16 portNumber);
		/// Creates a SocketAddress from an IP address and given port number.
		///
		/// The IP address must either be a domain name, or it must
		/// be in dotted decimal (IPv4) or hex string (IPv6) format.

	SocketAddress(Family family, const std::string& hostAddress, Poco::UInt16 portNumber);
		/// Creates a SocketAddress from an IP address and given port number.
		///
		/// The IP address must either be a domain name, or it must
		/// be in dotted decimal (IPv4) or hex string (IPv6) format.
		///
		/// If a domain name is given in hostAddress, it is resolved and the address
		/// matching the given family is used. If no address matching the given family
		/// is found, or the IP address given in hostAddress does not match the given
		/// family, an AddressFamilyMismatchException is thrown.

	SocketAddress(const std::string& hostAddress, const std::string& portNumber);
		/// Creates a SocketAddress from an IP address and the
		/// service name or port number.
		///
		/// The IP address must either be a domain name, or it must
		/// be in dotted decimal (IPv4) or hex string (IPv6) format.
		///
		/// The given port must either be a decimal port number, or
		/// a service name.

	SocketAddress(Family family, const std::string& hostAddress, const std::string& portNumber);
		/// Creates a SocketAddress from an IP address and the
		/// service name or port number.
		///
		/// The IP address must either be a domain name, or it must
		/// be in dotted decimal (IPv4) or hex string (IPv6) format.
		///
		/// The given port must either be a decimal port number, or
		/// a service name.
		///
		/// If a domain name is given in hostAddress, it is resolved and the address
		/// matching the given family is used. If no address matching the given family
		/// is found, or the IP address given in hostAddress does not match the given
		/// family, an AddressFamilyMismatchException is thrown.

	explicit SocketAddress(const std::string& hostAndPort);
		/// Creates a SocketAddress from an IP address or host name and the
		/// port number/service name. Host name/address and port number must
		/// be separated by a colon. In case of an IPv6 address,
		/// the address part must be enclosed in brackets.
		///
		/// Examples:
		///     192.168.1.10:80
		///     [::ffff:192.168.1.120]:2040
		///     www.appinf.com:8080
		///
		/// On platforms supporting UNIX_LOCAL sockets, hostAndPort
		/// can also be a valid absolute local socket file path.
		///
		/// Examples:
		///     /tmp/local.sock
		///     C:\Temp\local.sock
		///
		/// On Linux, abstract local sockets are supported as well.
		/// Abstract local sockets operate in a namespace that has
		/// no need for a filesystem. They are identified by
		/// a null byte at the beginning of the path.
		///
		/// Example:
		///     \0abstract.sock
		///

	SocketAddress(Family family, const std::string& addr);
		/// Creates a SocketAddress of the given family from a
		/// string representation of the address, which is
		/// either (1) an IP address and port number, separated by
		/// a colon for IPv4 or IPv6 addresses, or (2) path for
		/// UNIX_LOCAL sockets.
		/// See `SocketAddress(const string&)` documentation
		/// for more details.

	SocketAddress(const SocketAddress& addr);
		/// Creates a SocketAddress by copying another one.

	SocketAddress(SocketAddress&& addr);
		/// Creates a SocketAddress by moving another one.

	SocketAddress(const struct sockaddr* addr, poco_socklen_t length);
		/// Creates a SocketAddress from a native socket address.

	~SocketAddress();
		/// Destroys the SocketAddress.

	SocketAddress& operator = (const SocketAddress& socketAddress);
		/// Assigns another SocketAddress.

	SocketAddress& operator = (SocketAddress&& socketAddress);
		/// Move-assigns another SocketAddress.

	IPAddress host() const;
		/// Returns the host IP address.

	Poco::UInt16 port() const;
		/// Returns the port number.

	poco_socklen_t length() const;
		/// Returns the length of the internal native socket address.

	const struct sockaddr* addr() const;
		/// Returns a pointer to the internal native socket address.

	int af() const;
		/// Returns the address family (AF_INET or AF_INET6) of the address.

	std::string toString() const;
		/// Returns a string representation of the address.

	Family family() const;
		/// Returns the address family of the host's address.

	bool operator < (const SocketAddress& socketAddress) const;
	bool operator == (const SocketAddress& socketAddress) const;
	bool operator != (const SocketAddress& socketAddress) const;

	enum
	{
		MAX_ADDRESS_LENGTH =
#if defined(POCO_HAS_UNIX_SOCKET)
			sizeof(struct sockaddr_un)
#elif defined(POCO_HAVE_IPv6)
			sizeof(struct sockaddr_in6)
#else
			sizeof(struct sockaddr_in)
#endif
			/// Maximum length in bytes of a socket address.
	};

	static bool isUnixLocal(const std::string& hostAndPort);
		/// Returns true iff `hostAndPort` is an absolute file path.

protected:
	void init(const IPAddress& hostAddress, Poco::UInt16 portNumber);
	void init(const std::string& hostAddress, Poco::UInt16 portNumber);
	void init(Family family, const std::string& hostAddress, Poco::UInt16 portNumber);
	void init(Family family, const std::string& address);
	void init(const std::string& hostAndPort);
	Poco::UInt16 resolveService(const std::string& service);

private:
	typedef Poco::Net::Impl::SocketAddressImpl Impl;
	typedef Poco::AutoPtr<Impl> Ptr;

	Ptr pImpl() const;

	void newIPv4();
	void newIPv4(const sockaddr_in*);
	void newIPv4(const IPAddress& hostAddress, Poco::UInt16 portNumber);

#if defined(POCO_HAVE_IPv6)
	void newIPv6(const sockaddr_in6*);
	void newIPv6(const IPAddress& hostAddress, Poco::UInt16 portNumber);
#endif

#if defined(POCO_HAS_UNIX_SOCKET)
	void newLocal(const sockaddr_un* sockAddr);
	void newLocal(const std::string& path);
#endif

	Ptr _pImpl;
};


//
// inlines
//
inline SocketAddress::Ptr SocketAddress::pImpl() const
{
	if (_pImpl) return _pImpl;
	throw Poco::NullPointerException("Pointer to SocketAddress implementation is NULL.");
}


inline void SocketAddress::newIPv4()
{
	_pImpl = new Poco::Net::Impl::IPv4SocketAddressImpl;
}


inline void SocketAddress::newIPv4(const sockaddr_in* sockAddr)
{
	_pImpl = new Poco::Net::Impl::IPv4SocketAddressImpl(sockAddr);
}


inline void SocketAddress::newIPv4(const IPAddress& hostAddress, Poco::UInt16 portNumber)
{
	_pImpl = new Poco::Net::Impl::IPv4SocketAddressImpl(hostAddress.addr(), htons(portNumber));
}


#if defined(POCO_HAVE_IPv6)
inline void SocketAddress::newIPv6(const sockaddr_in6* sockAddr)
{
	_pImpl = new Poco::Net::Impl::IPv6SocketAddressImpl(sockAddr);
}


inline void SocketAddress::newIPv6(const IPAddress& hostAddress, Poco::UInt16 portNumber)
{
	_pImpl = new Poco::Net::Impl::IPv6SocketAddressImpl(hostAddress.addr(), htons(portNumber), hostAddress.scope());
}
#endif // POCO_HAVE_IPv6


#if defined(POCO_HAS_UNIX_SOCKET)
inline void SocketAddress::newLocal(const sockaddr_un* sockAddr)
{
	_pImpl = new Poco::Net::Impl::LocalSocketAddressImpl(sockAddr);
}


inline void SocketAddress::newLocal(const std::string& path)
{
	_pImpl = new Poco::Net::Impl::LocalSocketAddressImpl(path.c_str(), path.size());
}
#endif // POCO_HAS_UNIX_SOCKET


inline 	bool SocketAddress::operator == (const SocketAddress& socketAddress) const
{
#if defined(POCO_HAS_UNIX_SOCKET)
	if (family() == UNIX_LOCAL)
		return toString() == socketAddress.toString();
	else
#endif
		return host() == socketAddress.host() && port() == socketAddress.port();
}


inline bool SocketAddress::operator != (const SocketAddress& socketAddress) const
{
	return !(operator == (socketAddress));
}


} } // namespace Poco::Net


Net_API Poco::BinaryWriter& operator << (Poco::BinaryWriter& writer, const Poco::Net::SocketAddress& value);
Net_API Poco::BinaryReader& operator >> (Poco::BinaryReader& reader, Poco::Net::SocketAddress& value);
Net_API std::ostream& operator << (std::ostream& ostr, const Poco::Net::SocketAddress& address);


#endif // Net_SocketAddress_INCLUDED
