/*

snmp95.c: win95-safe versions of IpHlpApi calls
Copyright (C) 2001  Andy Lutomirski

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License, version 2.1, as published by the Free Software
Foundation, with the exception that if this copy of the library
is distributed under the Lesser GNU Public License (as opposed
to the ordinary GPL), you may ignore section 6b, and that all
copies distributed without exercising section 3 must retain this
paragraph in its entirety.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

These functions are equivalent to the IpHlpApi calls of the same name
except that they work on windows 95.


*/

//	Side note: on GCC, this code is pointless :)

#include "..\tcpip.h"
#include "winip.h"
#include "iphlpapi.h"
#include "MibAccess.h"

#ifdef _MSC_VER
#include "delayimp.h"
#endif

#define MakeAOI(name) {sizeof(name) / sizeof(UINT), name}


//	This is ridiculous...
#undef errno	//	safe for now
#undef read		//	for GCC
#include <memory>

#define DLI_ERROR VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND)

#ifndef _MSC_VER
//	sigh
#define min(x, y) ( (x) < (y) ? (x) : (y) )
#endif


//	MIB descriptors

//	ifTable
UINT OID_ifNumber[] = {1, 3, 6, 1, 2, 1, 2, 1, 0};	//	includes instance
UINT OID_ifIndex[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 1};
UINT OID_ifType[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 3};
UINT OID_ifPhysAddress[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 6};
UINT OID_ifOperStatus[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 8};

AsnObjectIdentifier AOI_ifNumber = MakeAOI(OID_ifNumber);

AsnObjectIdentifier ifTable[] =
{
	MakeAOI(OID_ifIndex),		// 0
	MakeAOI(OID_ifType),		// 1
	MakeAOI(OID_ifPhysAddress),	// 2
	MakeAOI(OID_ifOperStatus)	// 3
};

//	ipAddrTable
UINT OID_ipAdEntAddr[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 1};
UINT OID_ipAdEntIfIndex[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 2};
UINT OID_ipAdEntNetMask[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 3};
UINT OID_ipAdEntBcastAddr[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 4};
UINT OID_ipAdEntReasmMaxSize[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 5};

AsnObjectIdentifier ipAddrTable[] =
{
	MakeAOI(OID_ipAdEntAddr),			// 0
	MakeAOI(OID_ipAdEntIfIndex),		// 1
	MakeAOI(OID_ipAdEntNetMask),		// 2
	MakeAOI(OID_ipAdEntBcastAddr),		// 3 (int)
	MakeAOI(OID_ipAdEntReasmMaxSize)	// 4
};

//	ipRouteTable
UINT OID_ipRouteDest[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 1};
UINT OID_ipRouteIfIndex[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 2};
UINT OID_ipRouteMetric1[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 3};
UINT OID_ipRouteNextHop[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 7};
UINT OID_ipRouteType[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 8};
UINT OID_ipRouteMask[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 11};

AsnObjectIdentifier ipRouteTable[] =
{
	MakeAOI(OID_ipRouteDest),		// 0
	MakeAOI(OID_ipRouteIfIndex),	// 1
	MakeAOI(OID_ipRouteMetric1),	// 2
	MakeAOI(OID_ipRouteNextHop),	// 3
	MakeAOI(OID_ipRouteType),		// 4
	MakeAOI(OID_ipRouteMask)		// 5
};

//	ipNetTable
UINT OID_ipNetToMediaIfIndex[] = {1, 3, 6, 1, 2, 1, 4, 22, 1, 1};
UINT OID_ipNetToMediaPhysAddress[] = {1, 3, 6, 1, 2, 1, 4, 22, 1, 2};
UINT OID_ipNetToMediaNetAddress[] = {1, 3, 6, 1, 2, 1, 4, 22, 1, 3};
UINT OID_ipNetToMediaType[] = {1, 3, 6, 1, 2, 1, 4, 22, 1, 4};

AsnObjectIdentifier ipNetToMediaTable[] =
{
	MakeAOI(OID_ipNetToMediaIfIndex),		// 0
	MakeAOI(OID_ipNetToMediaPhysAddress),	// 1
	MakeAOI(OID_ipNetToMediaNetAddress),	// 2
	MakeAOI(OID_ipNetToMediaType)			// 3
};

static std::auto_ptr<MibII> m;

static bool populated = false;

static PMIB_IPADDRTABLE pAddrtable = 0;
static DWORD szAddrtable = 0;

static PMIB_IPFORWARDTABLE pRoutetable = 0;
static DWORD szRoutetable = 0;

int iphlp_avail = 1;	//	Is the iphlpapi dll present?
int net_avail = 1;	//	Is some method of access present?

static int __cdecl compip(const void *e1, const void *e2)
{
	return ((const MIB_IPADDRROW*)(e1))->dwAddr - ((const MIB_IPADDRROW*)(e2))->dwAddr;
}

static bool Populate()
{
#ifdef _MSC_VER
	if(populated) return szAddrtable != 0;
	populated = true;

	if(wo.trace) printf("***WinIP***  initializing inetmib1 tables...");

	//	Allocate
	m = std::auto_ptr<MibII>(new MibII);
	MIBTraverser::m = m.get();

	m->Init();
	if(!m->GetDLLStatus())
	{
		if(wo.trace) printf("\n***WinIP***  no inetmib1.dll\n");
		net_avail = 0;
		return false;
	}

	MIBTraverser mt;

	//	Populate the address table
	mt.Init(ipAddrTable, sizeof(ipAddrTable) / sizeof(ipAddrTable[0]));

	szAddrtable = sizeof(UINT) + 10 * sizeof(MIB_IPADDRROW);
	pAddrtable = (PMIB_IPADDRTABLE)malloc(szAddrtable);
	pAddrtable->dwNumEntries = 0;

	while(mt.Next())
	{
		if(sizeof(UINT) + (pAddrtable->dwNumEntries + 1) * sizeof(MIB_IPADDRROW)
			> szAddrtable)
		{
			szAddrtable += 10 * sizeof(MIB_IPADDRROW);
			pAddrtable = (PMIB_IPADDRTABLE)realloc(pAddrtable, szAddrtable);
		}

		MIB_IPADDRROW *r = pAddrtable->table + pAddrtable->dwNumEntries;
		pAddrtable->dwNumEntries++;
		ZeroMemory(r, sizeof(MIB_IPADDRROW));
		r->dwAddr = ASN_IP(mt[0].value.asnValue);
		r->dwIndex = mt[1].value.asnValue.unsigned32;
		r->dwMask = ASN_IP(mt[2].value.asnValue);
		r->dwBCastAddr = (r->dwAddr & r->dwMask)
			| ( (mt[3].value.asnValue.unsigned32 & 1) * ~r->dwMask );
		r->dwReasmSize = mt[4].value.asnValue.unsigned32;
	}

	szAddrtable = sizeof(UINT) * pAddrtable->dwNumEntries * sizeof(MIB_IPADDRROW);

	//	Populate the route table
	mt.Init(ipRouteTable, sizeof(ipRouteTable) / sizeof(ipRouteTable[0]));

	szRoutetable = sizeof(UINT) + 10 * sizeof(MIB_IPFORWARDROW);
	pRoutetable = (PMIB_IPFORWARDTABLE)malloc(szRoutetable);
	pRoutetable->dwNumEntries = 0;

	while(mt.Next())
	{
		if(sizeof(UINT) + (pRoutetable->dwNumEntries + 1) * sizeof(MIB_IPFORWARDROW)
			> szRoutetable)
		{
			szRoutetable += 10 * sizeof(MIB_IPFORWARDROW);
			pRoutetable = (PMIB_IPFORWARDTABLE)realloc(pRoutetable, szRoutetable);
		}

		MIB_IPFORWARDROW *r = pRoutetable->table + pRoutetable->dwNumEntries;
		pRoutetable->dwNumEntries++;
		ZeroMemory(r, sizeof(MIB_IPFORWARDROW));
		r->dwForwardIfIndex = mt[1].value.asnValue.unsigned32;
		r->dwForwardDest = ASN_IP(mt[0].value.asnValue);
		r->dwForwardMetric1 = mt[2].value.asnValue.unsigned32;
		r->dwForwardNextHop = ASN_IP(mt[3].value.asnValue);
		r->dwForwardType = mt[4].value.asnValue.unsigned32;
		r->dwForwardMask = ASN_IP(mt[5].value.asnValue);
	}

	szRoutetable = sizeof(UINT) * pRoutetable->dwNumEntries * sizeof(MIB_IPFORWARDROW);

	if(wo.trace) printf(" Done\n");

	return true;
#else
	return false;	//	won't get here anyway
#endif
}

//	we can ignore the sort option because the table is pre-sorted
extern "C" DWORD GetIfTableSafe(PMIB_IFTABLE pOut, DWORD* size, BOOL bSort)
{
	if(wo.noiphlpapi) iphlp_avail = 0;

	if(iphlp_avail)
	{
#ifdef _MSC_VER
		__try {
#endif
			return GetIfTable(pOut, size, bSort);
#ifdef _MSC_VER
		}
		__except(GetExceptionCode() == DLI_ERROR)
		{
			iphlp_avail = 0;
		}
#endif
	}

	if(!Populate()) return -1;

	MIBTraverser mt;

	//	Initialize for single-object read
	mt.Init(&AOI_ifNumber, 1);

	if(!mt.Get())
		return 0xFFFFFFFF;

	UINT numnic = mt[0].value.asnValue.unsigned32;
	DWORD sz = sizeof(UINT) + numnic * sizeof(MIB_IFROW);

	if(!pOut)
	{
		*size = sz;
		return 0;
	}
	else
	{
		if(*size < sz)
		{
			*size = sz;
			return ERROR_INSUFFICIENT_BUFFER;
		}

		//	Populate the table
		mt.Init(ifTable, sizeof(ifTable) / sizeof(ifTable[0]));
		pOut->dwNumEntries = 0;
		while(mt.Next())
		{
			MIB_IFROW *r = &pOut->table[pOut->dwNumEntries];
			pOut->dwNumEntries++;

			ZeroMemory(r, sizeof(MIB_IFROW));
			r->dwIndex = mt[0].value.asnValue.unsigned32;
			r->dwType = mt[1].value.asnValue.unsigned32;
			r->dwPhysAddrLen = min(MAXLEN_PHYSADDR,
				mt[2].value.asnValue.string.length);
			memcpy(r->bPhysAddr, mt[2].value.asnValue.string.stream,
				r->dwPhysAddrLen);
			r->dwOperStatus = mt[3].value.asnValue.unsigned32;
		}

		return 0;
	}
}


extern "C" DWORD GetIpAddrTableSafe(PMIB_IPADDRTABLE pOut, DWORD* size, BOOL bSort)
{
	if(wo.noiphlpapi) iphlp_avail = 0;

	if(iphlp_avail)
	{
#ifdef _MSC_VER
		__try {
#endif
			return GetIpAddrTable(pOut, size, bSort);
#ifdef _MSC_VER
		}
		__except(GetExceptionCode() == DLI_ERROR)
		{
			iphlp_avail = 0;
		}
#endif
	}

	if(!Populate()) return 0xFFFFFFFF;

	if(!pOut)
	{
		*size = szAddrtable;
		return 0;
	}
	else
	{
		if(*size < szAddrtable)
		{
			*size = szAddrtable;
			return ERROR_INSUFFICIENT_BUFFER;
		}

		memcpy(pOut, pAddrtable, szAddrtable);
		return 0;
	}
}


extern "C" DWORD GetIpNetTableSafe(PMIB_IPNETTABLE pOut, DWORD* size, BOOL bSort)
{
	if(wo.noiphlpapi) iphlp_avail = 0;

	if(iphlp_avail)
	{
#ifdef _MSC_VER
		__try {
#endif
			return GetIpNetTable(pOut, size, bSort);
#ifdef _MSC_VER
		}
		__except(GetExceptionCode() == DLI_ERROR)
		{
			iphlp_avail = 0;
		}
#endif
	}

	if(!Populate()) return -1;

	int sz = sizeof(UINT);	//	Space used so far

	DWORD temp;
	if(*size < 4) pOut = (PMIB_IPNETTABLE)&temp;
	pOut->dwNumEntries = 0;

	//	Initialize the traverser
	MIBTraverser mt;
	mt.Init(ipNetToMediaTable,
		sizeof(ipNetToMediaTable) / sizeof(ipNetToMediaTable[0]));

	//	Begin the traversal
	while(mt.Next())
	{
		sz += sizeof(MIB_IPNETROW);
		if(sz <= *size)
		{
			//	Fill in the row
			MIB_IPNETROW *r = pOut->table + pOut->dwNumEntries;
			pOut->dwNumEntries++;
			r->dwIndex = mt[0].value.asnValue.unsigned32;
			r->dwPhysAddrLen = mt[1].value.asnValue.string.length;
			memcpy(r->bPhysAddr, mt[1].value.asnValue.string.stream,
				r->dwPhysAddrLen);
			r->dwAddr = ASN_IP(mt[2].value.asnValue);
			r->dwType = mt[3].value.asnValue.unsigned32;
		}
	}

	if(sz > *size)
	{
		*size = sz;
		return ERROR_INSUFFICIENT_BUFFER;
	}
	else return 0;
}

extern "C" DWORD GetIpForwardTableSafe(PMIB_IPFORWARDTABLE pOut, DWORD* size, BOOL bSort)
{
	if(wo.noiphlpapi) iphlp_avail = 0;

	if(iphlp_avail)
	{
#ifdef _MSC_VER
		__try {
#endif
			return GetIpForwardTable(pOut, size, bSort);
#ifdef _MSC_VER
		}
		__except(GetExceptionCode() == DLI_ERROR)
		{
			iphlp_avail = 0;
		}
#endif
	}

	if(!Populate()) return -1;

	if(!pOut)
	{
		*size = szRoutetable;
		return 0;
	}
	else
	{
		if(*size < szRoutetable)
		{
			*size = szRoutetable;
			return ERROR_INSUFFICIENT_BUFFER;
		}

		memcpy(pOut, pRoutetable, szRoutetable);
		return 0;
	}
}
