Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
5.3 KiB

  1. #include "precomp.h"
  2. #include "bestintf.h"
  3. typedef DWORD (WINAPI * PFNGetBestInterface) (
  4. IN IPAddr dwDestAddr,
  5. OUT PDWORD pdwBestIfIndex
  6. );
  7. typedef DWORD (WINAPI * PFNGetIpAddrTable) (
  8. OUT PMIB_IPADDRTABLE pIpAddrTable,
  9. IN OUT PULONG pdwSize,
  10. IN BOOL bOrder
  11. );
  12. static HINSTANCE s_hIPHLPAPI = NULL;
  13. const TCHAR g_cszIPHLPAPIDllName[] = _TEXT("iphlpapi.dll");
  14. static PFNGetBestInterface s_pfnGetBestInterface;
  15. static PFNGetIpAddrTable s_pfnGetIpAddrTable;
  16. //forward references
  17. static DWORD IpAddrTable(PMIB_IPADDRTABLE& pIpAddrTable, BOOL fOrder = FALSE);
  18. static DWORD InterfaceIdxToInterfaceIp(PMIB_IPADDRTABLE
  19. pIpAddrTable, DWORD dwIndex, in_addr* s);
  20. DWORD NMINTERNAL NMGetBestInterface ( SOCKADDR_IN* srem, SOCKADDR_IN* sloc )
  21. {
  22. SOCKET hSock;
  23. int nAddrSize = sizeof(SOCKADDR);
  24. const int maxhostname = 1024;
  25. char hostname[maxhostname + 1];
  26. hostent *ha;
  27. char** c;
  28. int cIpAddr; // count of IpAddresses
  29. in_addr* in;
  30. DWORD BestIFIndex;
  31. PMIB_IPADDRTABLE pIpAddrTable = NULL;
  32. int dwStatus = ERROR_SUCCESS;
  33. ASSERT(srem);
  34. ASSERT(sloc);
  35. // This function tries to find the best IP interface to return when given a remote address
  36. // Three different ways are tried.
  37. //(1) statically get the list of interfaces, this will work when there's only one IP address
  38. dwStatus = gethostname(hostname, maxhostname);
  39. if (dwStatus != 0)
  40. {
  41. return WSAGetLastError();
  42. }
  43. ha = gethostbyname(hostname);
  44. if (ha == NULL)
  45. {
  46. return WSAGetLastError();
  47. }
  48. cIpAddr = 0; // count the interfaces, if there's only one this is easy
  49. for (c = ha->h_addr_list; *c != NULL; ++c)
  50. {
  51. cIpAddr++;
  52. in = (in_addr*)*c;
  53. }
  54. if (cIpAddr == 1) //just a single IP Address
  55. {
  56. sloc->sin_family = 0;
  57. sloc->sin_port = 0;
  58. sloc->sin_addr = *in;
  59. return dwStatus;
  60. }
  61. // (2) This computer has multiple IP interfaces, try the functions
  62. // in IPHLPAPI.DLL
  63. // As of this writing - Win98, NT4SP4, Windows 2000 contain these functions.
  64. //
  65. // This is a win because the information we need can be looked up statically.
  66. if (NULL == s_hIPHLPAPI)
  67. {
  68. s_hIPHLPAPI = ::LoadLibrary(g_cszIPHLPAPIDllName);
  69. }
  70. if (NULL != s_hIPHLPAPI)
  71. {
  72. s_pfnGetBestInterface = (PFNGetBestInterface)
  73. ::GetProcAddress(s_hIPHLPAPI, "GetBestInterface");
  74. s_pfnGetIpAddrTable = (PFNGetIpAddrTable)
  75. ::GetProcAddress(s_hIPHLPAPI, "GetIpAddrTable");
  76. if ((NULL != s_pfnGetBestInterface) &&
  77. (NULL != s_pfnGetIpAddrTable))
  78. {
  79. dwStatus = s_pfnGetBestInterface( (IPAddr)((ULONG_PTR)srem), &BestIFIndex);
  80. if (dwStatus != ERROR_SUCCESS)
  81. {
  82. FreeLibrary(s_hIPHLPAPI);
  83. s_hIPHLPAPI = NULL;
  84. return dwStatus;
  85. }
  86. // get IP Address Table for mapping interface index number to ip address
  87. dwStatus = IpAddrTable(pIpAddrTable);
  88. if (dwStatus != ERROR_SUCCESS)
  89. {
  90. if (pIpAddrTable)
  91. MemFree(pIpAddrTable);
  92. FreeLibrary(s_hIPHLPAPI);
  93. s_hIPHLPAPI = NULL;
  94. return dwStatus;
  95. }
  96. dwStatus = InterfaceIdxToInterfaceIp(pIpAddrTable,
  97. BestIFIndex, &(sloc->sin_addr));
  98. MemFree(pIpAddrTable);
  99. if (dwStatus == ERROR_SUCCESS)
  100. {
  101. FreeLibrary(s_hIPHLPAPI);
  102. s_hIPHLPAPI = NULL;
  103. return dwStatus;
  104. }
  105. }
  106. }
  107. // (3) As a last resort, try and connect on the stream socket that was passed in
  108. // This will work for NetMeeting when connecting to an LDAP server, for example.
  109. //
  110. hSock = socket(AF_INET, SOCK_STREAM, 0); // must be a STREAM socket for MS stack
  111. if (hSock != INVALID_SOCKET)
  112. {
  113. dwStatus = connect(hSock, (LPSOCKADDR)&srem, sizeof (SOCKADDR));
  114. if (dwStatus != SOCKET_ERROR)
  115. {
  116. getsockname(hSock, (LPSOCKADDR)&sloc, (int *) &nAddrSize);
  117. }
  118. closesocket(hSock);
  119. return ERROR_SUCCESS;
  120. }
  121. return SOCKET_ERROR;
  122. }
  123. //----------------------------------------------------------------------------
  124. // Inputs: pIpAddrTable is the IP address table
  125. // dwIndex is the Interface Number
  126. // Output: returns ERROR_SUCCESS when a match is found, s contains the IpAddr
  127. //----------------------------------------------------------------------------
  128. DWORD InterfaceIdxToInterfaceIp(PMIB_IPADDRTABLE pIpAddrTable, DWORD dwIndex, in_addr* s)
  129. {
  130. for (DWORD dwIdx = 0; dwIdx < pIpAddrTable->dwNumEntries; dwIdx++)
  131. {
  132. if (dwIndex == pIpAddrTable->table[dwIdx].dwIndex)
  133. {
  134. s->S_un.S_addr = pIpAddrTable->table[dwIdx].dwAddr;
  135. return ERROR_SUCCESS;
  136. }
  137. }
  138. return 1;
  139. }
  140. //----------------------------------------------------------------------------
  141. // If returned status is ERROR_SUCCESS, then pIpAddrTable points to a Ip Address
  142. // table.
  143. //----------------------------------------------------------------------------
  144. DWORD IpAddrTable(PMIB_IPADDRTABLE& pIpAddrTable, BOOL fOrder)
  145. {
  146. DWORD status = ERROR_SUCCESS;
  147. DWORD statusRetry = ERROR_SUCCESS;
  148. DWORD dwActualSize = 0;
  149. // query for buffer size needed
  150. status = s_pfnGetIpAddrTable(pIpAddrTable, &dwActualSize, fOrder);
  151. if (status == ERROR_SUCCESS)
  152. {
  153. return status;
  154. }
  155. else if (status == ERROR_INSUFFICIENT_BUFFER)
  156. {
  157. // need more space
  158. pIpAddrTable = (PMIB_IPADDRTABLE) MemAlloc(dwActualSize);
  159. statusRetry = s_pfnGetIpAddrTable(pIpAddrTable, &dwActualSize, fOrder);
  160. return statusRetry;
  161. }
  162. else
  163. {
  164. return status;
  165. }
  166. }
  167.