#include "GetIfAddrs.h"

#if defined(PLATFORM_POSIX)

	#define HAVE_GETIFADDRS
	#include <sys/types.h>
	#include <ifaddrs.h>

#elif defined(PLATFORM_WIN32)

	#include <iptypes.h>
	#include <iphlpapi.h>

	// Link with Iphlpapi.lib
	#pragma comment(lib, "IPHLPAPI.lib")

#endif

#ifndef IF_NAMESIZE
#	ifdef IFNAMSIZ
#		define IF_NAMESIZE	IFNAMSIZ
#	elif defined(MAX_INTERFACE_NAME_LEN)
#		define IF_NAMESIZE	MAX_INTERFACE_NAME_LEN
#	elif defined(_WIN32)
/* 40 for UUID, 256 for device path */
#		define IF_NAMESIZE	256
#	else
#		define IF_NAMESIZE	16
#	endif
#endif

struct pgm_ifaddrs_t
{
	struct pgm_ifaddrs_t*	ifa_next;	/* Pointer to the next structure.  */

	char*					ifa_name;	/* Name of this network interface.  */
	unsigned int			ifa_flags;	/* Flags as from SIOCGIFFLAGS ioctl.  */

#ifdef ifa_addr
#	undef ifa_addr
#endif
	struct sockaddr*	ifa_addr;		/* Network address of this interface.  */
	struct sockaddr*	ifa_netmask;	/* Netmask of this interface.  */
};

socklen_t pgm_sockaddr_len (const struct sockaddr* sa)
{
	socklen_t sa_len;
	switch (sa->sa_family) 
	{
		case AF_INET:
			sa_len = sizeof(struct sockaddr_in);
			break;
		case AF_INET6:
			sa_len = sizeof(struct sockaddr_in6);
			break;
		default:
			sa_len = 0;
			break;
	}
	return sa_len;
}

#ifndef _TRUNCATE
#	define _TRUNCATE	(size_t)-1
#endif

static inline int pgm_strncpy_s (char *dest, size_t size, const char *src, size_t count)
{
#ifndef CONFIG_HAVE_SECURITY_ENHANCED_CRT
	if (_TRUNCATE == count) {
		strncpy (dest, src, size);
		if (size > 0)
			dest[size - 1] = 0;
		return 0;
	}
	strncpy (dest, src, count + 1);
	dest[count] = 0;
	return 0;
#else
	return strncpy_s (dest, size, src, count);
#endif
}

/* locals */
struct _pgm_ifaddrs_t
{
	struct pgm_ifaddrs_t		_ifa;
	char						_name[IF_NAMESIZE];
	struct sockaddr_storage		_addr;
	struct sockaddr_storage		_netmask;
};

/* Number of attempts to try enumerating the interface list */
#define MAX_TRIES		3
#ifdef _WIN32
/* MSDN(GetAdaptersAddresses Function) recommends pre-allocating a 15KB
 * working buffer to reduce chances of a buffer overflow.
 * NB: The article actually recommends 15,000 and not 15,360 bytes.
 */
#	define DEFAULT_BUFFER_SIZE	15000
#else
#	define DEFAULT_BUFFER_SIZE	4096
#endif

#if defined(_WIN32)

/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes
 * 4-byte datatype whilst compiler uses an 8-byte datatype.  Size can be forced
 * with -D_USE_32BIT_TIME_T with side effects to everything else.
 *
 * Only supports IPv4 addressing similar to SIOCGIFCONF socket option.
 *
 * Interfaces that are not "operationally up" will return the address 0.0.0.0,
 * this includes adapters with static IP addresses but with disconnected cable.
 * This is documented under the GetIpAddrTable API.  Interface status can only
 * be determined by the address, a separate flag is introduced with the
 * GetAdapterAddresses API.
 *
 * The IPv4 loopback interface is not included.
 *
 * Available in Windows 2000 and Wine 1.0.
 */
static bool _pgm_getadaptersinfo(struct pgm_ifaddrs_t** ifap)
{
	DWORD dwRet;
	ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE;
	PIP_ADAPTER_INFO pAdapterInfo = NULL;
	PIP_ADAPTER_INFO pAdapter = NULL;

	/* loop to handle interfaces coming online causing a buffer overflow
	 * between first call to list buffer length and second call to enumerate.
	 */
	for (unsigned i = MAX_TRIES; i; i--)
	{
		pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
		dwRet = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
		if (ERROR_BUFFER_OVERFLOW == dwRet)
		{
			free(pAdapterInfo);
			pAdapterInfo = NULL;
		}
		else
		{
			break;
		}
	}

	switch (dwRet)
	{
		case ERROR_SUCCESS:	/* NO_ERROR */
			break;
		case ERROR_BUFFER_OVERFLOW:
			if (pAdapterInfo)
				free(pAdapterInfo);
			return false;
		default:
			if (pAdapterInfo)
				free(pAdapterInfo);
			return false;
	}

	/* count valid adapters */
	int n = 0;
	for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next)
	{
		for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; pIPAddr; pIPAddr = pIPAddr->Next)
		{
			/* skip null adapters */
			if (strlen(pIPAddr->IpAddress.String) == 0)
				continue;
			++n;
		}
	}

	/* contiguous block for adapter list */
	struct _pgm_ifaddrs_t* ifa = (struct _pgm_ifaddrs_t*)malloc(n * sizeof(struct _pgm_ifaddrs_t));
	memset(ifa, 0, n * sizeof(struct _pgm_ifaddrs_t*));
	struct _pgm_ifaddrs_t* ift = ifa;

	/* now populate list */
	int k = 0;
	for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next)
	{
		for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; pIPAddr; pIPAddr = pIPAddr->Next)
		{
			/* skip null adapters */
			if (strlen(pIPAddr->IpAddress.String) == 0)
				continue;

			/* address */
			ift->_ifa.ifa_addr = (sockaddr *) & ift->_addr;
//			ASSERT(1 == pgm_sockaddr_pton(pIPAddr->IpAddress.String, ift->_ifa.ifa_addr));

			/* name */
			ift->_ifa.ifa_name = ift->_name;
			pgm_strncpy_s(ift->_ifa.ifa_name, IF_NAMESIZE, pAdapter->AdapterName, _TRUNCATE);

			/* flags: assume up, broadcast and multicast */
			ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST;
			if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK)
				ift->_ifa.ifa_flags |= IFF_LOOPBACK;

			/* netmask */
			ift->_ifa.ifa_netmask = (sockaddr *) & ift->_netmask;
//			ASSERT(1 == pgm_sockaddr_pton(pIPAddr->IpMask.String, ift->_ifa.ifa_netmask));

			/* next */
			ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1);
			ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next);
			
			k++;
		}
	}
	if(k)
		ifa[k - 1]._ifa.ifa_next = NULL;

	if (pAdapterInfo)
		free(pAdapterInfo);
	*ifap = (struct pgm_ifaddrs_t*)ifa;
	return TRUE;
}

/* Supports both IPv4 and IPv6 addressing.  The size of IP_ADAPTER_ADDRESSES
 * changes between Windows XP, XP SP1, and Vista with additional members.
 *
 * Interfaces that are not "operationally up" will typically return a host
 * IP address with the defined IPv4 link-local prefix 169.254.0.0/16.
 * Adapters with a static configured IP address but down will return both
 * the IPv4 link-local prefix and the static address.
 *
 * It is easier to say "not up" rather than "down" as this API returns six
 * effective down status values: down, testing, unknown, dormant, not present,
 * and lower layer down.
 *
 * Available in Windows XP and Wine 1.3.
 */
static bool _pgm_getadaptersaddresses(struct pgm_ifaddrs_t** ifap)
{
	DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet;
	IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter;

	/* loop to handle interfaces coming online causing a buffer overflow
	 * between first call to list buffer length and second call to enumerate.
	 */
	for (unsigned i = MAX_TRIES; i; i--)
	{
		pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)malloc(dwSize);
		dwRet = GetAdaptersAddresses(AF_UNSPEC,
				/* requires Windows XP SP1 */
				GAA_FLAG_INCLUDE_PREFIX |
				GAA_FLAG_SKIP_ANYCAST |
				GAA_FLAG_SKIP_DNS_SERVER |
				GAA_FLAG_SKIP_FRIENDLY_NAME |
				GAA_FLAG_SKIP_MULTICAST,
				NULL,
				pAdapterAddresses,
				&dwSize);
		if (ERROR_BUFFER_OVERFLOW == dwRet)
		{
			free(pAdapterAddresses);
			pAdapterAddresses = NULL;
		}
		else
		{
			break;
		}
	}

	switch (dwRet)
	{
		case ERROR_SUCCESS:
			break;
		case ERROR_BUFFER_OVERFLOW:
			if (pAdapterAddresses)
				free(pAdapterAddresses);
			return false;
		default:
			if (pAdapterAddresses)
				free(pAdapterAddresses);
			return false;
	}

	/* count valid adapters */
	int n = 0;
	for (adapter = pAdapterAddresses; adapter; adapter = adapter->Next)
	{
		for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next)
		{
			/* ensure IP adapter */
			if (AF_INET != unicast->Address.lpSockaddr->sa_family &&
				AF_INET6 != unicast->Address.lpSockaddr->sa_family)
			{
				continue;
			}
			++n;
		}
	}

	/* contiguous block for adapter list */
	struct _pgm_ifaddrs_t* ifa = (struct _pgm_ifaddrs_t*)malloc(n * sizeof(struct _pgm_ifaddrs_t));
	memset(ifa, 0, sizeof(struct _pgm_ifaddrs_t*));
	struct _pgm_ifaddrs_t* ift = ifa;

	/* now populate list */
	int k = 0;
	for (adapter = pAdapterAddresses; adapter; adapter = adapter->Next)
	{
		int unicastIndex = 0;
		for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress;
			 unicast;
			 unicast = unicast->Next, ++unicastIndex)
		{
			/* ensure IP adapter */
			if (AF_INET != unicast->Address.lpSockaddr->sa_family &&
				AF_INET6 != unicast->Address.lpSockaddr->sa_family)
			{
				continue;
			}

			/* address */
			ift->_ifa.ifa_addr = (sockaddr *) & ift->_addr;
			memcpy(ift->_ifa.ifa_addr, unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength);

			/* name */
			ift->_ifa.ifa_name = ift->_name;
			pgm_strncpy_s(ift->_ifa.ifa_name, IF_NAMESIZE, adapter->AdapterName, _TRUNCATE);

			/* flags */
			ift->_ifa.ifa_flags = 0;
			if (IfOperStatusUp == adapter->OperStatus)
				ift->_ifa.ifa_flags |= IFF_UP;
			if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
				ift->_ifa.ifa_flags |= IFF_LOOPBACK;
			if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST))
				ift->_ifa.ifa_flags |= IFF_MULTICAST;

			/* netmask */
			ift->_ifa.ifa_netmask = (sockaddr *) &ift->_netmask;

			/* pre-Vista must hunt for matching prefix in linked list, otherwise use
			 * OnLinkPrefixLength from IP_ADAPTER_UNICAST_ADDRESS structure.
			 * FirstPrefix requires Windows XP SP1, from SP1 to pre-Vista provides a
			 * single adapter prefix for each IP address.  Vista and later provides
			 * host IP address prefix, subnet IP address, and subnet broadcast IP
			 * address.  In addition there is a multicast and broadcast address prefix.
			 */
			ULONG prefixLength = 0;

#if defined( _WIN32 ) && ( _WIN32_WINNT >= 0x0600 )
			/* For a unicast IPv4 address, any value greater than 32 is an illegal
			 * value. For a unicast IPv6 address, any value greater than 128 is an
			 * illegal value. A value of 255 is commonly used to represent an illegal
			 * value.
			 *
			 * Windows 7 SP1 returns 64 for Teredo links which is incorrect.
			 */

#define IN6_IS_ADDR_TEREDO(addr) \
	(((const uint32_t *)(addr))[0] == ntohl (0x20010000))

			if (AF_INET6 == unicast->Address.lpSockaddr->sa_family &&
				/* TunnelType only applies to one interface on the adapter and no
				 * convenient method is provided to determine which.
				 */
				TUNNEL_TYPE_TEREDO == adapter->TunnelType &&
				/* Test the interface with the known Teredo network prefix.
				 */
				IN6_IS_ADDR_TEREDO(&((struct sockaddr_in6*)(unicast->Address.lpSockaddr))->sin6_addr) &&
				/* Test that this version is actually wrong, subsequent releases from Microsoft
				 * may resolve the issue.
				 */
				32 != unicast->OnLinkPrefixLength)
			{
				prefixLength = 32;
			}
			else
				prefixLength = unicast->OnLinkPrefixLength;
#else
			/* The order of linked IP_ADAPTER_UNICAST_ADDRESS structures pointed to by
			* the FirstUnicastAddress member does not have any relationship with the
			* order of linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix
			* member.
			*
			* Example enumeration:
			*    [ no subnet ]
			*   ::1/128            - address
			*   ff00::%1/8         - multicast (no IPv6 broadcast)
			*   127.0.0.0/8        - subnet
			*   127.0.0.1/32       - address
			*   127.255.255.255/32 - subnet broadcast
			*   224.0.0.0/4        - multicast
			*   255.255.255.255/32 - broadcast
			*
			* Which differs from most adapters listing three IPv6:
			*   fe80::%10/64       - subnet
			*   fe80::51e9:5fe5:4202:325a%10/128 - address
			*   ff00::%10/8        - multicast
			*
			* !IfOperStatusUp IPv4 addresses are skipped:
			*   fe80::%13/64       - subnet
			*   fe80::d530:946d:e8df:8c91%13/128 - address
			*   ff00::%13/8        - multicast
			*    [ no subnet  ]
			*    [ no address ]
			*   224.0.0.0/4        - multicast
			*   255.255.255.255/32 - broadcast
			*
			* On PTP links no multicast or broadcast addresses are returned:
			*    [ no subnet ]
			*   fe80::5efe:10.203.9.30/128 - address
			*    [ no multicast ]
			*    [ no multicast ]
			*    [ no broadcast ]
			*
			* Active primary IPv6 interfaces are a bit overloaded:
			*   ::/0               - default route
			*   2001::/32          - global subnet
			*   2001:0:4137:9e76:2443:d6:ba87:1a2a/128 - global address
			*   fe80::/64          - link-local subnet
			*   fe80::2443:d6:ba87:1a2a/128 - link-local address
			*   ff00::/8           - multicast
			*/

#define IN_LINKLOCAL(a)	((((uint32_t) (a)) & 0xaffff0000) == 0xa9fe0000)

			for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix;
				 prefix;
				 prefix = prefix->Next)
			{
				LPSOCKADDR lpSockaddr = prefix->Address.lpSockaddr;
				if (lpSockaddr->sa_family != unicast->Address.lpSockaddr->sa_family)
					continue;
				/* special cases */
				/* RFC2863: IPv4 interface not up */
				if (AF_INET == lpSockaddr->sa_family &&
					adapter->OperStatus != IfOperStatusUp)
				{
					/* RFC3927: link-local IPv4 always has 16-bit CIDR */
					if (IN_LINKLOCAL(ntohl(((struct sockaddr_in*)(unicast->Address.lpSockaddr))->sin_addr.s_addr)))
					{
						pgm_trace(PGM_LOG_ROLE_NETWORK, _("Assuming 16-bit prefix length for link-local IPv4 adapter %s."),
								  adapter->AdapterName);
						prefixLength = 16;
					}
					else
					{
						pgm_trace(PGM_LOG_ROLE_NETWORK, _("Prefix length unavailable for IPv4 adapter %s."),
								  adapter->AdapterName);
					}
					break;
				}
				/* default IPv6 route */
				if (AF_INET6 == lpSockaddr->sa_family &&
					0 == prefix->PrefixLength &&
					IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)(lpSockaddr))->sin6_addr))
				{
					pgm_trace(PGM_LOG_ROLE_NETWORK, _("Ingoring unspecified address prefix on IPv6 adapter %s."),
							  adapter->AdapterName);
					continue;
				}
				/* Assume unicast address for first prefix of operational adapter */
				if (AF_INET == lpSockaddr->sa_family)
					pgm_assert(!IN_MULTICAST(ntohl(((struct sockaddr_in*)(lpSockaddr))->sin_addr.s_addr)));
				if (AF_INET6 == lpSockaddr->sa_family)
					pgm_assert(!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)(lpSockaddr))->sin6_addr));
				/* Assume subnet or host IP address for XP backward compatibility */

				prefixLength = prefix->PrefixLength;
				break;
			}
#endif /* defined( _WIN32 ) && ( _WIN32_WINNT >= 0x0600 ) */

			/* map prefix to netmask */
			ift->_ifa.ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family;
			switch (unicast->Address.lpSockaddr->sa_family)
			{
				case AF_INET:
					if (0 == prefixLength || prefixLength > 32)
					{
						prefixLength = 32;
					}
#if defined( _WIN32) && ( _WIN32_WINNT >= 0x0600 )
					/* Added in Vista, but no IPv6 equivalent. */
					{
						ULONG Mask;
						ConvertLengthToIpv4Mask(prefixLength, &Mask);
						((struct sockaddr_in*)ift->_ifa.ifa_netmask)->sin_addr.s_addr = htonl(Mask);
					}
#else
					/* NB: left-shift of full bit-width is undefined in C standard. */
					((struct sockaddr_in*)ift->_ifa.ifa_netmask)->sin_addr.s_addr = htonl(0xffffffffU << (32 - prefixLength));
#endif
					break;

				case AF_INET6:
					if (0 == prefixLength || prefixLength > 128)
					{
						prefixLength = 128;
					}
					for (ULONG i = prefixLength, j = 0; i > 0; i -= 8, ++j)
					{
						((struct sockaddr_in6*)ift->_ifa.ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)((0xffU << (8 - i)) & 0xffU);
					}
					break;
			}

			/* next */
			ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1);
			ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next);
			
			k++;
		}
	}
	if(k)
		ifa[k - 1]._ifa.ifa_next = NULL;

	if (pAdapterAddresses)
		free(pAdapterAddresses);
	*ifap = (struct pgm_ifaddrs_t*)ifa;
	return TRUE;
}
#endif /* _WIN32 */

#if defined( HAVE_GETIFADDRS )
static bool _pgm_getifaddrs(struct pgm_ifaddrs_t** ifap)
{
	struct ifaddrs *_ifap, *_ifa;
	const int e = getifaddrs(&_ifap);
	if (-1 == e)
		return false;

	int n = 0, k = 0;
	for (_ifa = _ifap; _ifa; _ifa = _ifa->ifa_next)
		++n;

	struct _pgm_ifaddrs_t* ifa = (struct _pgm_ifaddrs_t *)malloc(n * sizeof(struct _pgm_ifaddrs_t));
	memset(ifa, 0, n * sizeof(struct _pgm_ifaddrs_t));
	struct _pgm_ifaddrs_t* ift = ifa;
	for (_ifa = _ifap; _ifa; _ifa = _ifa->ifa_next)
	{
		/* ensure IP adapter */
		if (NULL == _ifa->ifa_addr ||
			(_ifa->ifa_addr->sa_family != AF_INET &&
			 _ifa->ifa_addr->sa_family != AF_INET6))
		{
			n--;
			continue;
		}

		/* address */
		ift->_ifa.ifa_addr = (sockaddr *)&ift->_addr;
		memcpy(ift->_ifa.ifa_addr, _ifa->ifa_addr, pgm_sockaddr_len(_ifa->ifa_addr));

		/* name */
		ift->_ifa.ifa_name = ift->_name;
		pgm_strncpy_s(ift->_ifa.ifa_name, IF_NAMESIZE, _ifa->ifa_name, _TRUNCATE);

		/* flags */
		ift->_ifa.ifa_flags = _ifa->ifa_flags;

		/* netmask */
		ift->_ifa.ifa_netmask = (sockaddr*) & ift->_netmask;
		memcpy(ift->_ifa.ifa_netmask, _ifa->ifa_netmask, pgm_sockaddr_len(_ifa->ifa_netmask));

		/* next */
		if (k++ < (n - 1))
		{
			ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1);
			ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next);
		}
	}
	freeifaddrs(_ifap);
	*ifap = (struct pgm_ifaddrs_t*)ifa;
	return true;
}
#endif

/* returns TRUE on success setting ifap to a linked list of system interfaces,
 * returns FALSE on failure and sets error appropriately.
 */
bool pgm_getifaddrs(struct pgm_ifaddrs_t** ifap)
{
#if defined( HAVE_GETIFADDRS )
	return _pgm_getifaddrs(ifap);
#elif defined( _WIN32 )
	return _pgm_getadaptersaddresses(ifap);
#else
#	error "Unsupported interface enumeration on this platform."
#endif
}

void pgm_freeifaddrs(struct pgm_ifaddrs_t* ifa)
{
	if(!ifa)
		return;
	free(ifa);
}

// get all (useful) local interfaces names and addresses
VectorMap<String, IPV4Address> GetLocalIPV4Addresses(void)
{
	VectorMap<String, IPV4Address> res;

	struct pgm_ifaddrs_t* ifa;
	if(!pgm_getifaddrs(&ifa))
		return res;
	struct pgm_ifaddrs_t* ifap = ifa;
	while(ifap)
	{
		struct sockaddr* addr = ifap->ifa_addr;
		if(addr->sa_family == AF_INET)
		{
			struct sockaddr_in *v4addr = (struct sockaddr_in *)addr;
			res.Add(ifap->ifa_name, IPV4Address(v4addr->sin_addr.s_addr));
		}
		ifap = ifap->ifa_next;
	}
	
	pgm_freeifaddrs(ifa);
	return res;
}
