|
|
#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 = NmLoadLibrary(g_cszIPHLPAPIDllName, TRUE); } 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; } }
|