/**
 * @file wif.c  Windows network interface code
 *
 * Copyright (C) 2010 Creytiv.com
 */
#include <winsock2.h>
#include <iphlpapi.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_net.h>
#include <re_sa.h>


#define DEBUG_MODULE "wif"
#define DEBUG_LEVEL 5
#include <re_dbg.h>


/**
 * List interfaces using GetAdaptersAddresses, which handles both
 * IPv4 and IPv6 address families.
 *
 * This is available from Windows XP and Windows Server 2003
 */
static int if_list_gaa(net_ifaddr_h *ifh, void *arg)
{
	IP_ADAPTER_ADDRESSES addrv[64], *cur;
	ULONG ret, len = sizeof(addrv);
	const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;
	HANDLE hLib;
	union {
		FARPROC proc;
		ULONG (WINAPI *gaa)(ULONG, ULONG, PVOID,
				    PIP_ADAPTER_ADDRESSES, PULONG);
	} u;
	bool stop = false;
	int err = 0;

	hLib = LoadLibrary(TEXT("iphlpapi.dll"));
	if (!hLib)
		return ENOSYS;

	u.proc = GetProcAddress(hLib, TEXT("GetAdaptersAddresses"));
	if (!u.proc) {
		err = ENOSYS;
		goto out;
	}

	ret = (*u.gaa)(AF_UNSPEC, flags, NULL, addrv, &len);
	if (ret != ERROR_SUCCESS) {
		DEBUG_WARNING("if_list: GetAdaptersAddresses ret=%u\n", ret);
		err = ENODEV;
		goto out;
	}

	for (cur = addrv; cur && !stop; cur = cur->Next) {
		PIP_ADAPTER_UNICAST_ADDRESS ip;

		/* an interface can have many IP-addresses */
		for (ip = cur->FirstUnicastAddress; ip; ip = ip->Next) {
			struct sa sa;

			sa_set_sa(&sa, ip->Address.lpSockaddr);

			if (ifh && ifh(cur->AdapterName, &sa, arg)) {
				stop = true;
				break;
			}
		}
	}

 out:
	FreeLibrary(hLib);

	return err;
}


/**
 * List interfaces using GetAdaptersInfo, which handles only IPv4 family.
 *
 * This is available from Windows 2000, and also works under Wine.
 */
static int if_list_gai(net_ifaddr_h *ifh, void *arg)
{
	IP_ADAPTER_INFO info[32];
	PIP_ADAPTER_INFO p = info;
	ULONG ulOutBufLen = sizeof(info);
	DWORD ret;

	ret = GetAdaptersInfo(info, &ulOutBufLen);
	if (ret != ERROR_SUCCESS) {
		DEBUG_WARNING("if_list: GetAdaptersInfo ret=%u\n", ret);
		return ENODEV;
	}

	for (p = info; p; p = p->Next) {
		struct sa sa;

		if (sa_set_str(&sa, p->IpAddressList.IpAddress.String, 0))
			continue;

		if (ifh && ifh(p->AdapterName, &sa, arg))
			break;
	}

	return 0;
}


/**
 * Enumerate all network interfaces
 *
 * @param ifh Interface handler
 * @param arg Handler argument
 *
 * @return 0 if success, otherwise errorcode
 */
int net_if_list(net_ifaddr_h *ifh, void *arg)
{
	/* Try both methods .. */

	if (!if_list_gaa(ifh, arg))
		return 0;

	return if_list_gai(ifh, arg);
}
