202 lines
5.3 KiB
C++
202 lines
5.3 KiB
C++
|
#include "precomp.h"
|
|||
|
#include "bestintf.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
typedef DWORD (WINAPI * PFNGetBestInterface) (
|
|||
|
IN IPAddr dwDestAddr,
|
|||
|
OUT PDWORD pdwBestIfIndex
|
|||
|
);
|
|||
|
|
|||
|
typedef DWORD (WINAPI * PFNGetIpAddrTable) (
|
|||
|
OUT PMIB_IPADDRTABLE pIpAddrTable,
|
|||
|
IN OUT PULONG pdwSize,
|
|||
|
IN BOOL bOrder
|
|||
|
);
|
|||
|
|
|||
|
static HINSTANCE s_hIPHLPAPI = NULL;
|
|||
|
const TCHAR g_cszIPHLPAPIDllName[] = _TEXT("iphlpapi.dll");
|
|||
|
static PFNGetBestInterface s_pfnGetBestInterface;
|
|||
|
static PFNGetIpAddrTable s_pfnGetIpAddrTable;
|
|||
|
|
|||
|
|
|||
|
//forward references
|
|||
|
static DWORD IpAddrTable(PMIB_IPADDRTABLE& pIpAddrTable, BOOL fOrder = FALSE);
|
|||
|
static DWORD InterfaceIdxToInterfaceIp(PMIB_IPADDRTABLE
|
|||
|
pIpAddrTable, DWORD dwIndex, in_addr* s);
|
|||
|
|
|||
|
|
|||
|
DWORD NMINTERNAL NMGetBestInterface ( SOCKADDR_IN* srem, SOCKADDR_IN* sloc )
|
|||
|
{
|
|||
|
SOCKET hSock;
|
|||
|
int nAddrSize = sizeof(SOCKADDR);
|
|||
|
|
|||
|
const int maxhostname = 1024;
|
|||
|
char hostname[maxhostname + 1];
|
|||
|
hostent *ha;
|
|||
|
char** c;
|
|||
|
int cIpAddr; // count of IpAddresses
|
|||
|
in_addr* in;
|
|||
|
|
|||
|
DWORD BestIFIndex;
|
|||
|
PMIB_IPADDRTABLE pIpAddrTable = NULL;
|
|||
|
|
|||
|
int dwStatus = ERROR_SUCCESS;
|
|||
|
|
|||
|
ASSERT(srem);
|
|||
|
ASSERT(sloc);
|
|||
|
|
|||
|
// This function tries to find the best IP interface to return when given a remote address
|
|||
|
// Three different ways are tried.
|
|||
|
|
|||
|
//(1) statically get the list of interfaces, this will work when there's only one IP address
|
|||
|
dwStatus = gethostname(hostname, maxhostname);
|
|||
|
if (dwStatus != 0)
|
|||
|
{
|
|||
|
return WSAGetLastError();
|
|||
|
}
|
|||
|
|
|||
|
ha = gethostbyname(hostname);
|
|||
|
if (ha == NULL)
|
|||
|
{
|
|||
|
return WSAGetLastError();
|
|||
|
}
|
|||
|
|
|||
|
cIpAddr = 0; // count the interfaces, if there's only one this is easy
|
|||
|
for (c = ha->h_addr_list; *c != NULL; ++c)
|
|||
|
{
|
|||
|
cIpAddr++;
|
|||
|
in = (in_addr*)*c;
|
|||
|
}
|
|||
|
|
|||
|
if (cIpAddr == 1) //just a single IP Address
|
|||
|
{
|
|||
|
sloc->sin_family = 0;
|
|||
|
sloc->sin_port = 0;
|
|||
|
sloc->sin_addr = *in;
|
|||
|
return dwStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// (2) This computer has multiple IP interfaces, try the functions
|
|||
|
// in IPHLPAPI.DLL
|
|||
|
// As of this writing - Win98, NT4SP4, Windows 2000 contain these functions.
|
|||
|
//
|
|||
|
// This is a win because the information we need can be looked up statically.
|
|||
|
|
|||
|
if (NULL == s_hIPHLPAPI)
|
|||
|
{
|
|||
|
s_hIPHLPAPI = ::LoadLibrary(g_cszIPHLPAPIDllName);
|
|||
|
}
|
|||
|
if (NULL != s_hIPHLPAPI)
|
|||
|
{
|
|||
|
s_pfnGetBestInterface = (PFNGetBestInterface)
|
|||
|
::GetProcAddress(s_hIPHLPAPI, "GetBestInterface");
|
|||
|
s_pfnGetIpAddrTable = (PFNGetIpAddrTable)
|
|||
|
::GetProcAddress(s_hIPHLPAPI, "GetIpAddrTable");
|
|||
|
if ((NULL != s_pfnGetBestInterface) &&
|
|||
|
(NULL != s_pfnGetIpAddrTable))
|
|||
|
{
|
|||
|
dwStatus = s_pfnGetBestInterface( (IPAddr)((ULONG_PTR)srem), &BestIFIndex);
|
|||
|
if (dwStatus != ERROR_SUCCESS)
|
|||
|
{
|
|||
|
FreeLibrary(s_hIPHLPAPI);
|
|||
|
s_hIPHLPAPI = NULL;
|
|||
|
return dwStatus;
|
|||
|
}
|
|||
|
|
|||
|
// get IP Address Table for mapping interface index number to ip address
|
|||
|
dwStatus = IpAddrTable(pIpAddrTable);
|
|||
|
if (dwStatus != ERROR_SUCCESS)
|
|||
|
{
|
|||
|
if (pIpAddrTable)
|
|||
|
MemFree(pIpAddrTable);
|
|||
|
FreeLibrary(s_hIPHLPAPI);
|
|||
|
s_hIPHLPAPI = NULL;
|
|||
|
return dwStatus;
|
|||
|
}
|
|||
|
|
|||
|
dwStatus = InterfaceIdxToInterfaceIp(pIpAddrTable,
|
|||
|
BestIFIndex, &(sloc->sin_addr));
|
|||
|
|
|||
|
MemFree(pIpAddrTable);
|
|||
|
if (dwStatus == ERROR_SUCCESS)
|
|||
|
{
|
|||
|
FreeLibrary(s_hIPHLPAPI);
|
|||
|
s_hIPHLPAPI = NULL;
|
|||
|
return dwStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// (3) As a last resort, try and connect on the stream socket that was passed in
|
|||
|
// This will work for NetMeeting when connecting to an LDAP server, for example.
|
|||
|
//
|
|||
|
hSock = socket(AF_INET, SOCK_STREAM, 0); // must be a STREAM socket for MS stack
|
|||
|
if (hSock != INVALID_SOCKET)
|
|||
|
{
|
|||
|
dwStatus = connect(hSock, (LPSOCKADDR)&srem, sizeof (SOCKADDR));
|
|||
|
if (dwStatus != SOCKET_ERROR)
|
|||
|
{
|
|||
|
getsockname(hSock, (LPSOCKADDR)&sloc, (int *) &nAddrSize);
|
|||
|
}
|
|||
|
closesocket(hSock);
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
return SOCKET_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
// Inputs: pIpAddrTable is the IP address table
|
|||
|
// dwIndex is the Interface Number
|
|||
|
// Output: returns ERROR_SUCCESS when a match is found, s contains the IpAddr
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
DWORD InterfaceIdxToInterfaceIp(PMIB_IPADDRTABLE pIpAddrTable, DWORD dwIndex, in_addr* s)
|
|||
|
{
|
|||
|
for (DWORD dwIdx = 0; dwIdx < pIpAddrTable->dwNumEntries; dwIdx++)
|
|||
|
{
|
|||
|
if (dwIndex == pIpAddrTable->table[dwIdx].dwIndex)
|
|||
|
{
|
|||
|
s->S_un.S_addr = pIpAddrTable->table[dwIdx].dwAddr;
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
return 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
// If returned status is ERROR_SUCCESS, then pIpAddrTable points to a Ip Address
|
|||
|
// table.
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
DWORD IpAddrTable(PMIB_IPADDRTABLE& pIpAddrTable, BOOL fOrder)
|
|||
|
{
|
|||
|
DWORD status = ERROR_SUCCESS;
|
|||
|
DWORD statusRetry = ERROR_SUCCESS;
|
|||
|
DWORD dwActualSize = 0;
|
|||
|
|
|||
|
// query for buffer size needed
|
|||
|
status = s_pfnGetIpAddrTable(pIpAddrTable, &dwActualSize, fOrder);
|
|||
|
|
|||
|
if (status == ERROR_SUCCESS)
|
|||
|
{
|
|||
|
return status;
|
|||
|
}
|
|||
|
else if (status == ERROR_INSUFFICIENT_BUFFER)
|
|||
|
{
|
|||
|
// need more space
|
|||
|
pIpAddrTable = (PMIB_IPADDRTABLE) MemAlloc(dwActualSize);
|
|||
|
|
|||
|
statusRetry = s_pfnGetIpAddrTable(pIpAddrTable, &dwActualSize, fOrder);
|
|||
|
return statusRetry;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|