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.

502 lines
18 KiB

  1. /*
  2. * File: notification.cpp
  3. * Description: Support for connection notification.
  4. * Author: shouse 4.30.01
  5. */
  6. #include <windows.h>
  7. #include <stdio.h>
  8. #include <devguid.h>
  9. #include <iphlpapi.h>
  10. #include "wlbsiocl.h"
  11. #include "debug.h"
  12. #include "wlbsctrl.h"
  13. #if defined (NLB_SESSION_SUPPORT)
  14. /* The length of the IP to GUID hash table. */
  15. #define IP_TO_GUID_HASH 19
  16. /* Loopback IP address. (127.0.0.1) */
  17. #define IP_LOOPBACK_ADDRESS 0x0100007f
  18. /* An Ip to GUID table entry. */
  19. typedef struct IPToGUIDEntry {
  20. DWORD dwIPAddress;
  21. WCHAR szAdapterGUID[CVY_MAX_DEVNAME_LEN];
  22. IPToGUIDEntry * pNext;
  23. } IPToGUIDEntry;
  24. /* The WLBS device - necessary for IOCTLs. */
  25. WCHAR szDevice[CVY_STR_SIZE];
  26. /* The IP to GUID map is an array of linked lists hashed on IP address. */
  27. IPToGUIDEntry * IPToGUIDMap[IP_TO_GUID_HASH];
  28. /* An overlapped structure for IP address change notifications. */
  29. OVERLAPPED AddrChangeOverlapped;
  30. /* A handle for IP address change notifications. */
  31. HANDLE hAddrChangeHandle;
  32. /* A handle for an IP address change event. */
  33. HANDLE hAddrChangeEvent;
  34. /* A boolean to indicate whether or not connection notification has been initialized.
  35. Initialization is performed upon the first call to either WlbsConnectionUp or WlbsConnectionDown. */
  36. static BOOL fInitialized = FALSE;
  37. /*
  38. * Function: GetGUIDFromIP
  39. * Description: Gets the GUID from the IPToGUID table corresponding to the
  40. * the given IP address.
  41. * Returns: If the call succeeds, returns a pointer to the unicode string
  42. * containing the CLSID (GUID). Upon failure, returns NULL.
  43. * Author: shouse 6.15.00
  44. */
  45. WCHAR * GetGUIDFromIP (DWORD IPAddress) {
  46. IPToGUIDEntry * entry = NULL;
  47. /* Loop through the linked list at the hashed index and return the GUID from the entry
  48. corresponding to the given IP address. */
  49. for (entry = IPToGUIDMap[IPAddress % IP_TO_GUID_HASH]; entry; entry = entry->pNext)
  50. if (entry->dwIPAddress == IPAddress)
  51. return entry->szAdapterGUID;
  52. /* At this point, we can't find the IP address in the table, so bail. */
  53. return NULL;
  54. }
  55. /*
  56. * Function: GetGUIDFromIndex
  57. * Description: Gets the GUID from the AdaptersInfo table corresponding
  58. * to the given IP address.
  59. * Returns: If the call succeeds, returns a pointer to the string containing
  60. * the adapter name (GUID). Upon failure, returns NULL.
  61. * Author: shouse 6.15.00
  62. */
  63. CHAR * GetGUIDFromIndex (PIP_ADAPTER_INFO pAdapterTable, DWORD dwIndex) {
  64. PIP_ADAPTER_INFO pAdapterInfo = NULL;
  65. /* Loop through the adapter table looking for the given index. Return the adapter
  66. name for the corresponding index. */
  67. for (pAdapterInfo = pAdapterTable; pAdapterInfo; pAdapterInfo = pAdapterInfo->Next)
  68. if (pAdapterInfo->Index == dwIndex)
  69. return pAdapterInfo->AdapterName;
  70. /* If we get this far, we can't find it, so bail. */
  71. return NULL;
  72. }
  73. /*
  74. * Function: PrintIPAddress
  75. * Description: Prints an IP address in dot notation.
  76. * Returns:
  77. * Author: shouse 6.15.00
  78. */
  79. void PrintIPAddress (DWORD IPAddress) {
  80. CHAR szIPAddress[16];
  81. sprintf(szIPAddress, "%d.%d.%d.%d", IPAddress & 0x000000ff, (IPAddress & 0x0000ff00) >> 8,
  82. (IPAddress & 0x00ff0000) >> 16, (IPAddress & 0xff000000) >> 24);
  83. TRACE1("%-15s", szIPAddress);
  84. }
  85. /*
  86. * Function: PrintIPToGUIDMap
  87. * Description: Traverses and prints the IPToGUID map.
  88. * Returns:
  89. * Author: shouse 6.15.00
  90. */
  91. void PrintIPToGUIDMap (void) {
  92. IPToGUIDEntry * entry = NULL;
  93. DWORD dwHash;
  94. /* Loop through the linked list at each hashed index and print the IP to GUID mapping. */
  95. for (dwHash = 0; dwHash < IP_TO_GUID_HASH; dwHash++) {
  96. for (entry = IPToGUIDMap[dwHash]; entry; entry = entry->pNext) {
  97. PrintIPAddress(entry->dwIPAddress);
  98. TRACE1(" -> GUID %ws\n", entry->szAdapterGUID);
  99. }
  100. }
  101. }
  102. /*
  103. * Function: DestroyIPToGUIDMap
  104. * Description: Destroys the IPToGUID map.
  105. * Returns: Returns ERROR_SUCCESS if successful. Returns an error code otherwise.
  106. * Author: shouse 6.15.00
  107. */
  108. DWORD DestroyIPToGUIDMap (void) {
  109. DWORD dwError = ERROR_SUCCESS;
  110. IPToGUIDEntry * next = NULL;
  111. DWORD dwHash;
  112. /* Loop through all hash indexes. */
  113. for (dwHash = 0; dwHash < IP_TO_GUID_HASH; dwHash++) {
  114. next = IPToGUIDMap[dwHash];
  115. /* Loop through the linked list and free each entry. */
  116. while (next) {
  117. IPToGUIDEntry * entry = NULL;
  118. entry = next;
  119. next = next->pNext;
  120. if (!HeapFree(GetProcessHeap(), 0, entry)) {
  121. dwError = GetLastError();
  122. TRACE_ERROR1("HeapFree failed: %d\n", dwError);
  123. return dwError;
  124. }
  125. }
  126. /* Reset the pointer to the head of the list in the array. */
  127. IPToGUIDMap[dwHash] = NULL;
  128. }
  129. return dwError;
  130. }
  131. /*
  132. * Function: BuildIPToGUIDMap
  133. * Description: Builds the IPToGUID map by first getting information on all adapters and
  134. * then retrieving the map of IP addresses to adapters. Using those tables,
  135. * this constructs a mapping of IP addresses to adapter GUIDs.
  136. * Returns: Returns ERROR_SUCCESS if successful. Returns an error code otherwise.
  137. * Author: shouse 6.14.00
  138. */
  139. DWORD BuildIPToGUIDMap (void) {
  140. DWORD dwError = ERROR_SUCCESS;
  141. PMIB_IPADDRTABLE pAddressTable = NULL;
  142. PIP_ADAPTER_INFO pAdapterTable = NULL;
  143. DWORD dwAddressSize = 0;
  144. DWORD dwAdapterSize = 0;
  145. DWORD dwEntry;
  146. /* Destroy the IP to GUID map first. */
  147. if ((dwError = DestroyIPToGUIDMap()) != NO_ERROR) {
  148. TRACE_ERROR1("DestroyIPToGUIDMap failed: %d\n", dwError);
  149. return dwError;
  150. }
  151. /* Query the necessary length of a buffer to hold the adapter info. */
  152. if ((dwError = GetAdaptersInfo(pAdapterTable, &dwAdapterSize)) != ERROR_BUFFER_OVERFLOW) {
  153. TRACE_ERROR1("GetAdaptersInfo failed: %d\n", dwError);
  154. return dwError;
  155. }
  156. /* Allocate a buffer of the indicated size. */
  157. if (!(pAdapterTable = (PIP_ADAPTER_INFO)HeapAlloc(GetProcessHeap(), 0, dwAdapterSize))) {
  158. dwError = ERROR_NOT_ENOUGH_MEMORY;
  159. TRACE_ERROR1("HeapAlloc failed: %d\n", dwError);
  160. return dwError;
  161. }
  162. /* Fill the buffer with the adapter info. */
  163. if ((dwError = GetAdaptersInfo(pAdapterTable, &dwAdapterSize)) != NO_ERROR) {
  164. TRACE_ERROR1("GetAdaptersInfo failed: %d\n", dwError);
  165. return dwError;
  166. }
  167. /* Query the necessary length of a buffer to hold the IP address table. */
  168. if ((dwError = GetIpAddrTable(pAddressTable, &dwAddressSize, TRUE)) != ERROR_INSUFFICIENT_BUFFER) {
  169. TRACE_ERROR1("GetIpAddrTable failed: %d\n", dwError);
  170. return dwError;
  171. }
  172. /* Allocate a buffer of the indicated size. */
  173. if (!(pAddressTable = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(), 0, dwAddressSize))) {
  174. dwError = ERROR_NOT_ENOUGH_MEMORY;
  175. TRACE_ERROR1("HeapAlloc failed: %d\n", dwError);
  176. return dwError;
  177. }
  178. /* Fill the buffer with the IP address table. */
  179. if ((dwError = GetIpAddrTable(pAddressTable, &dwAddressSize, TRUE)) != NO_ERROR) {
  180. TRACE_ERROR1("GetIpAddrTable failed: %d\n", dwError);
  181. return dwError;
  182. }
  183. /* For each entry in the IP address to adapter table, create an entry for our IP address to GUID table. */
  184. for (dwEntry = 0; dwEntry < pAddressTable->dwNumEntries; dwEntry++) {
  185. PCHAR pszDeviceName = NULL;
  186. IPToGUIDEntry * entry = NULL;
  187. /* Only create an entry if the IP address is nonzero and is not the IP loopback address. */
  188. if ((pAddressTable->table[dwEntry].dwAddr != 0UL) && (pAddressTable->table[dwEntry].dwAddr != IP_LOOPBACK_ADDRESS)) {
  189. WCHAR szAdapterGUID[CVY_MAX_DEVNAME_LEN];
  190. /* Retrieve the GUID from the interface index. */
  191. if (!(pszDeviceName = GetGUIDFromIndex(pAdapterTable, pAddressTable->table[dwEntry].dwIndex))) {
  192. dwError = ERROR_INCORRECT_ADDRESS;
  193. TRACE_ERROR1("GetGUIDFromIndex failed: %d\n", dwError);
  194. return dwError;
  195. }
  196. /* Allocate a buffer for the IP to GUID entry. */
  197. if (!(entry = (IPToGUIDEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(IPToGUIDEntry)))) {
  198. dwError = ERROR_NOT_ENOUGH_MEMORY;
  199. TRACE_ERROR1("HeapAlloc failed: %d\n", dwError);
  200. return dwError;
  201. }
  202. /* Zero the entry contents. */
  203. ZeroMemory((VOID *)entry, sizeof(entry));
  204. /* Insert the entry at the head of the linked list indexed by the IP address % HASH. */
  205. entry->pNext = IPToGUIDMap[pAddressTable->table[dwEntry].dwAddr % IP_TO_GUID_HASH];
  206. IPToGUIDMap[pAddressTable->table[dwEntry].dwAddr % IP_TO_GUID_HASH] = entry;
  207. /* Fill in the IP address. */
  208. entry->dwIPAddress = pAddressTable->table[dwEntry].dwAddr;
  209. /* GUIDS in NLB multi-NIC are expected to be prefixed with "\DEVICE\". */
  210. lstrcpy(entry->szAdapterGUID, L"\\DEVICE\\");
  211. /* Convert the adapter name ASCII string to a GUID unicode string and place it in the table entry. */
  212. if (!MultiByteToWideChar(CP_ACP, 0, pszDeviceName, -1, szAdapterGUID, CVY_MAX_DEVNAME_LEN)) {
  213. dwError = GetLastError();
  214. TRACE_ERROR1("MultiByteToWideChar failed: %d\n", dwError);
  215. return dwError;
  216. }
  217. /* Cat the GUID onto the "\DEVICE\". */
  218. lstrcat(entry->szAdapterGUID, szAdapterGUID);
  219. }
  220. }
  221. /* Free the buffers used to query the IP stack. */
  222. if (pAddressTable) HeapFree(GetProcessHeap(), 0, pAddressTable);
  223. if (pAdapterTable) HeapFree(GetProcessHeap(), 0, pAdapterTable);
  224. PrintIPToGUIDMap();
  225. return dwError;
  226. }
  227. /*
  228. * Function: WlbsConnectionNotificationInit
  229. * Description: Initialize connection notification by retrieving the device driver
  230. * information for later use by IOCTLs and build the IPToGUID map.
  231. * Returns: Returns ERROR_SUCCESS if successful. Returns an error code otherwise.
  232. * Author: shouse 6.15.00
  233. */
  234. DWORD WlbsConnectionNotificationInit () {
  235. DWORD dwError = ERROR_SUCCESS;
  236. WCHAR szDriver[CVY_STR_SIZE];
  237. swprintf(szDevice, L"\\\\.\\%ls", CVY_NAME);
  238. /* Query for the existence of the WLBS driver. */
  239. if (!QueryDosDevice(szDevice + 4, szDriver, CVY_STR_SIZE)) {
  240. dwError = GetLastError();
  241. TRACE_ERROR1("QueryDosDevice failed: %d\n", dwError);
  242. return dwError;
  243. }
  244. /* Build the IP to GUID mapping. */
  245. if ((dwError = BuildIPToGUIDMap()) != ERROR_SUCCESS) {
  246. TRACE_ERROR1("BuildIPToGUIDMap failed: %d\n", dwError);
  247. return dwError;
  248. }
  249. /* Create an IP address change event. */
  250. if (!(hAddrChangeEvent = CreateEvent(NULL, FALSE, FALSE, L"NLB Connection Notification IP Address Change Event"))) {
  251. dwError = GetLastError();
  252. TRACE_ERROR1("CreateEvent failed: %d\n", dwError);
  253. return dwError;
  254. }
  255. /* Clear the overlapped structure. */
  256. ZeroMemory(&AddrChangeOverlapped, sizeof(OVERLAPPED));
  257. /* Place the event handle in the overlapped structure. */
  258. AddrChangeOverlapped.hEvent = hAddrChangeEvent;
  259. /* Tell IP to notify us of any changes to the IP address to interface mapping. */
  260. dwError = NotifyAddrChange(&hAddrChangeHandle, &AddrChangeOverlapped);
  261. if ((dwError != NO_ERROR) && (dwError != ERROR_IO_PENDING)) {
  262. TRACE_ERROR1("NotifyAddrChange failed: %d\n", dwError);
  263. return dwError;
  264. }
  265. return ERROR_SUCCESS;
  266. }
  267. /*
  268. * Function: ResolveAddressTableChanges
  269. * Description: Checks for changes in the IP address to adapter mapping and rebuilds the
  270. * IPToGUID map if necessary.
  271. * Returns: Returns ERROR_SUCCESS upon success. Returns an error code otherwise.
  272. * Author: shouse 6.20.00
  273. */
  274. DWORD ResolveAddressTableChanges () {
  275. DWORD dwError = ERROR_SUCCESS;
  276. DWORD dwLength = 0;
  277. /* Check to see if the IP address to adapter table has been modified. */
  278. if (GetOverlappedResult(hAddrChangeHandle, &AddrChangeOverlapped, &dwLength, FALSE)) {
  279. TRACE("IP address to adapter table modified... Rebuilding IP to GUID map...\n");
  280. /* If so, rebuild the IP address to GUID mapping. */
  281. if ((dwError = BuildIPToGUIDMap()) != ERROR_SUCCESS) {
  282. TRACE_ERROR1("BuildIPToGUIDMap failed: %d\n", dwError);
  283. return dwError;
  284. }
  285. /* Tell IP to notify us of any changes to the IP address to interface mapping. */
  286. dwError = NotifyAddrChange(&hAddrChangeHandle, &AddrChangeOverlapped);
  287. if ((dwError != NO_ERROR) && (dwError != ERROR_IO_PENDING)) {
  288. TRACE_ERROR1("NotifyAddrChange failed: %d\n", dwError);
  289. return dwError;
  290. }
  291. }
  292. return ERROR_SUCCESS;
  293. }
  294. /*
  295. * Function: WlbsConnectionNotify
  296. * Description: Used to notify the WLBS load module that a connection has been established, reset or closed.
  297. * Returns: Returns ERROR_SUCCESS if successful. Returns an error code otherwise.
  298. * Author: shouse 6.13.00
  299. */
  300. DWORD WINAPI WlbsConnectionNotify (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, NLB_CONN_NOTIFICATION_OPERATION Operation, PULONG NLBStatusEx) {
  301. IOCTL_LOCAL_HDR Header;
  302. DWORD dwError = ERROR_SUCCESS;
  303. PWCHAR pszAdapterGUID = NULL;
  304. HANDLE hDescriptor;
  305. DWORD dwLength = 0;
  306. /* By default, the extended NLB status is success. */
  307. *NLBStatusEx = NLB_ERROR_SUCCESS;
  308. /* If not done so already, initialize connection notification support. */
  309. if (!fInitialized) {
  310. if ((dwError = WlbsConnectionNotificationInit()) != ERROR_SUCCESS) {
  311. TRACE_ERROR1("WlbsConnectionNotificationInit failed: %d\n", dwError);
  312. return dwError;
  313. }
  314. fInitialized = TRUE;
  315. }
  316. /* Zero the IOCTL input and output buffers. */
  317. ZeroMemory((VOID *)&Header, sizeof(IOCTL_LOCAL_HDR));
  318. /* Resolve any changes to the IP address table before we map this IP address. */
  319. if ((dwError = ResolveAddressTableChanges()) != ERROR_SUCCESS) {
  320. TRACE_ERROR1("CheckForAddressTableChanges failed: %d\n", dwError);
  321. return dwError;
  322. }
  323. /* Retrieve the GUID corresponding to the adapter on which this IP address is configured. */
  324. if (!(pszAdapterGUID = GetGUIDFromIP(ServerIp))) {
  325. dwError = ERROR_INCORRECT_ADDRESS;
  326. TRACE_ERROR1("GetGUIDFromIP failed: %d\n", dwError);
  327. return dwError;
  328. }
  329. /* Copy the GUID into the IOCTL input buffer. */
  330. lstrcpy(Header.device_name, pszAdapterGUID);
  331. /* Copy the function parameters into the IOCTL input buffer. */
  332. Header.options.notify.flags = 0;
  333. Header.options.notify.conn.Operation = Operation;
  334. Header.options.notify.conn.ServerIPAddress = ServerIp;
  335. Header.options.notify.conn.ServerPort = ServerPort;
  336. Header.options.notify.conn.ClientIPAddress = ClientIp;
  337. Header.options.notify.conn.ClientPort = ClientPort;
  338. Header.options.notify.conn.Protocol = Protocol;
  339. PrintIPAddress(ServerIp);
  340. TRACE1(" maps to GUID %ws\n", Header.device_name);
  341. /* Open the device driver. */
  342. if ((hDescriptor = CreateFile(szDevice, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
  343. dwError = GetLastError();
  344. *NLBStatusEx = NLB_ERROR_NOT_BOUND;
  345. TRACE_ERROR1("CreateFile failed: %d\n", dwError);
  346. return dwError;
  347. }
  348. /* Use an IOCTL to notify the WLBS driver that a connection has gone up. */
  349. if (!DeviceIoControl(hDescriptor, IOCTL_CVY_CONNECTION_NOTIFY, &Header, sizeof(IOCTL_LOCAL_HDR), &Header, sizeof(IOCTL_LOCAL_HDR), &dwLength, NULL)) {
  350. dwError = GetLastError();
  351. CloseHandle(hDescriptor);
  352. TRACE_ERROR1("DeviceIoControl failed: %d\n", dwError);
  353. return dwError;
  354. }
  355. /* Make sure the expected number of bytes was returned by the IOCTL. */
  356. if (dwLength != sizeof(IOCTL_LOCAL_HDR)) {
  357. dwError = ERROR_GEN_FAILURE;
  358. CloseHandle(hDescriptor);
  359. TRACE_ERROR1("DeviceIoControl failed: %d\n", dwError);
  360. return dwError;
  361. }
  362. /*
  363. Return code can be one of:
  364. NLB_ERROR_SUCCESS, if the notification is accepted.
  365. NLB_ERROR_REQUEST_REFUSED, if the notification is rejected
  366. NLB_ERROR_INVALID_PARAMETER, if the arguments are invalid.
  367. NLB_ERROR_NOT_FOUND, if NLB was not bound to the specified adapter..
  368. NLB_ERROR_GENERIC_FAILURE, if a non-specific error occurred.
  369. */
  370. /* Pass the return code from the driver back to the caller. */
  371. *NLBStatusEx = Header.options.notify.conn.ReturnCode;
  372. /* Close the device driver. */
  373. CloseHandle(hDescriptor);
  374. return dwError;
  375. }
  376. /*
  377. * Function:
  378. * Description:
  379. * Returns:
  380. * Author: shouse 6.13.00
  381. */
  382. DWORD WINAPI WlbsConnectionUp (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, PULONG NLBStatusEx) {
  383. return WlbsConnectionNotify(ServerIp, ServerPort, ClientIp, ClientPort, Protocol, NLB_CONN_UP, NLBStatusEx);
  384. }
  385. /*
  386. * Function:
  387. * Description:
  388. * Returns:
  389. * Author: shouse 6.13.00
  390. */
  391. DWORD WINAPI WlbsConnectionDown (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, PULONG NLBStatusEx) {
  392. return WlbsConnectionNotify(ServerIp, ServerPort, ClientIp, ClientPort, Protocol, NLB_CONN_DOWN, NLBStatusEx);
  393. }
  394. /*
  395. * Function:
  396. * Description:
  397. * Returns:
  398. * Author: shouse 6.13.00
  399. */
  400. DWORD WINAPI WlbsConnectionReset (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, PULONG NLBStatusEx) {
  401. return WlbsConnectionNotify(ServerIp, ServerPort, ClientIp, ClientPort, Protocol, NLB_CONN_RESET, NLBStatusEx);
  402. }
  403. #else
  404. DWORD WINAPI WlbsConnectionUp (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, PULONG NLBStatusEx) { return ERROR_SUCCESS; }
  405. DWORD WINAPI WlbsConnectionDown (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, PULONG NLBStatusEx) { return ERROR_SUCCESS; }
  406. DWORD WINAPI WlbsConnectionReset (DWORD ServerIp, WORD ServerPort, DWORD ClientIp, WORD ClientPort, USHORT Protocol, PULONG NLBStatusEx) { return ERROR_SUCCESS; }
  407. #endif /* NLB_SESSION_SUPPORT */