/*
 * win_if_list - Display network interfaces with description (for Windows)
 * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Alternatively, this software may be distributed under the terms of BSD
 * license.
 *
 * See README and COPYING for more details.
 *
 * This small tool is for the Windows build to provide an easy way of fetching
 * a list of available network interfaces.
 */

#include "includes.h"
#include <stdio.h>
#ifdef CONFIG_USE_NDISUIO
#include <winsock2.h>
#include <ntddndis.h>
#else /* CONFIG_USE_NDISUIO */
#include "pcap.h"
#include <winsock.h>
#endif /* CONFIG_USE_NDISUIO */

#ifdef CONFIG_USE_NDISUIO

/* from nuiouser.h */
#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK

#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)

#define IOCTL_NDISUIO_QUERY_BINDING \
	_NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define IOCTL_NDISUIO_BIND_WAIT \
	_NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)

typedef struct _NDISUIO_QUERY_BINDING
{
	ULONG BindingIndex;
	ULONG DeviceNameOffset;
	ULONG DeviceNameLength;
	ULONG DeviceDescrOffset;
	ULONG DeviceDescrLength;
} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;


static HANDLE ndisuio_open(void)
{
	DWORD written;
	HANDLE h;

	h = CreateFile(TEXT("\\\\.\\\\Ndisuio"),
		       GENERIC_READ | GENERIC_WRITE, 0, NULL,
		       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
		       INVALID_HANDLE_VALUE);
	if (h == INVALID_HANDLE_VALUE)
		return h;

#ifndef _WIN32_WCE
	if (!DeviceIoControl(h, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0,
			     &written, NULL)) {
		printf("IOCTL_NDISUIO_BIND_WAIT failed: %d",
		       (int) GetLastError());
		CloseHandle(h);
		return INVALID_HANDLE_VALUE;
	}
#endif /* _WIN32_WCE */

	return h;
}


static void ndisuio_query_bindings(HANDLE ndisuio)
{
	NDISUIO_QUERY_BINDING *b;
	size_t blen = sizeof(*b) + 1024;
	int i, error;
	DWORD written;
	char name[256], desc[256];
	WCHAR *pos;
	size_t j, len;

	b = malloc(blen);
	if (b == NULL)
		return;

	for (i = 0; ; i++) {
		memset(b, 0, blen);
		b->BindingIndex = i;
		if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
				     b, sizeof(NDISUIO_QUERY_BINDING), b,
				     (DWORD) blen, &written, NULL)) {
			error = (int) GetLastError();
			if (error == ERROR_NO_MORE_ITEMS)
				break;
			printf("IOCTL_NDISUIO_QUERY_BINDING failed: %d",
			       error);
			break;
		}

		pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
		len = b->DeviceNameLength;
		if (len >= sizeof(name))
			len = sizeof(name) - 1;
		for (j = 0; j < len; j++)
			name[j] = (char) pos[j];
		name[len] = '\0';

		pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
		len = b->DeviceDescrLength;
		if (len >= sizeof(desc))
			len = sizeof(desc) - 1;
		for (j = 0; j < len; j++)
			desc[j] = (char) pos[j];
		desc[len] = '\0';

		printf("ifname: %s\ndescription: %s\n\n", name, desc);
	}

	free(b);
}


static void ndisuio_enum_bindings(void)
{
	HANDLE ndisuio = ndisuio_open();
	if (ndisuio == INVALID_HANDLE_VALUE)
		return;

	ndisuio_query_bindings(ndisuio);
	CloseHandle(ndisuio);
}

#else /* CONFIG_USE_NDISUIO */

static void show_dev(pcap_if_t *dev)
{
	printf("ifname: %s\ndescription: %s\n\n",
	       dev->name, dev->description);
}


static void pcap_enum_devs(void)
{
	pcap_if_t *devs, *dev;
	char err[PCAP_ERRBUF_SIZE + 1];

	if (pcap_findalldevs(&devs, err) < 0) {
		fprintf(stderr, "Error - pcap_findalldevs: %s\n", err);
		return;
	}

	for (dev = devs; dev; dev = dev->next) {
		show_dev(dev);
	}

	pcap_freealldevs(devs);
}

#endif /* CONFIG_USE_NDISUIO */


int main(int argc, char *argv[])
{
#ifdef CONFIG_USE_NDISUIO
	ndisuio_enum_bindings();
#else /* CONFIG_USE_NDISUIO */
	pcap_enum_devs();
#endif /* CONFIG_USE_NDISUIO */

	return 0;
}
