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.

1805 lines
48 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1997 - 1999
  3. Module Name:
  4. lan.cxx
  5. Abstract:
  6. This is the source file relating to the LAN-specific routines of the
  7. Connectivity APIs implementation.
  8. Author:
  9. Gopal Parupudi <GopalP>
  10. [Notes:]
  11. optional-notes
  12. Revision History:
  13. GopalP 10/11/1997 Start.
  14. --*/
  15. #include <precomp.hxx>
  16. //
  17. // Constants
  18. //
  19. #define GETIFTABLE GetIfTable
  20. #define GETIPADDRTABLE GetIpAddrTable
  21. #define GETIPFORWARDTABLE GetIpForwardTable
  22. #define GETRTTANDHOPCOUNT GetRTTAndHopCount
  23. #define GETIPSTATISTICS GetIpStatistics
  24. #define MAX_IFTABLE_ROWS 4
  25. #define MAX_IPADDRTABLE_ROWS 6
  26. #define MAX_IPNETTABLE_ROWS 8
  27. #define MAX_IPFORWARDTABLE_ROWS 8
  28. #define MAX_HOPS_COUNT 0xFFFF
  29. #define BROADCAST_ACTIVITY_THRESHOLD 2 // +2 thru -2
  30. #define MEDIASENSE_INITIALIZATION_DELAY 3*25*1000 // 1:15 minutes
  31. #define MEDIASENSE_EVALUATE_LAN_DELAY 5*1000 // 5 seconds
  32. #define SENS_WINSOCK_VERSION MAKEWORD( 2, 0 )
  33. //
  34. // Globals
  35. //
  36. BOOL gbIpInitSuccessful;
  37. long gdwLastLANTime;
  38. long gdwLANState;
  39. IF_STATE gIfState[MAX_IF_ENTRIES];
  40. MIB_IPSTATS gIpStats;
  41. extern CRITICAL_SECTION gSensLock;
  42. HANDLE ghMediaTimer;
  43. DWORD gdwMediaSenseState;
  44. //
  45. // Macros
  46. //
  47. /*++
  48. Macro Description:
  49. A macro to help in allocating tables when calling IP Helper APIs.
  50. Arguments:
  51. TABLE_TYPE - The type of the IP Table being queried.
  52. ROW_TYPE - The type of the Row corresponding to the TABLE_TYPE.
  53. FUNC_NAME - The IP API to be called to get the IP table.
  54. MAX_NUM_ROWS - The default number of rows for the table which is
  55. being retrieved. These rows are allocated on the stack.
  56. Notes:
  57. o lpdwLastError should be defined as an LPDWORD in the code
  58. fragment that uses this macro.
  59. --*/
  60. #define \
  61. BEGIN_GETTABLE( \
  62. TABLE_TYPE, \
  63. ROW_TYPE, \
  64. FUNC_NAME, \
  65. MAX_NUM_ROWS \
  66. ) \
  67. { \
  68. DWORD dwOldSize; \
  69. DWORD dwSize; \
  70. DWORD dwStatus; \
  71. \
  72. BOOL bOrder; \
  73. \
  74. TABLE_TYPE *pTable; \
  75. \
  76. bOrder = FALSE; \
  77. \
  78. dwSize = sizeof(DWORD) + MAX_NUM_ROWS * sizeof(ROW_TYPE); \
  79. pTable = (TABLE_TYPE *) new char[dwSize]; \
  80. if (pTable == NULL) \
  81. { \
  82. SensPrintA(SENS_MEM, (#FUNC_NAME "(): failed to new %d bytes\n", \
  83. dwSize)); \
  84. *lpdwLastError = ERROR_OUTOFMEMORY; \
  85. return FALSE; \
  86. } \
  87. \
  88. dwOldSize = dwSize; \
  89. \
  90. dwStatus = FUNC_NAME( \
  91. pTable, \
  92. &dwSize, \
  93. bOrder \
  94. ); \
  95. \
  96. if ( (dwStatus == ERROR_INSUFFICIENT_BUFFER) \
  97. || (dwStatus == ERROR_MORE_DATA)) \
  98. { \
  99. ASSERT(dwSize > dwOldSize); \
  100. SensPrintA(SENS_WARN, (#FUNC_NAME "(%d): reallocing buffer to be %d bytes\n", \
  101. dwOldSize, dwSize)); \
  102. delete (char *)pTable; \
  103. pTable = (TABLE_TYPE *) new char[dwSize]; \
  104. if (pTable != NULL) \
  105. { \
  106. dwStatus = FUNC_NAME( \
  107. pTable, \
  108. &dwSize, \
  109. bOrder \
  110. ); \
  111. } \
  112. else \
  113. { \
  114. SensPrintA(SENS_MEM, (#FUNC_NAME "(): failed to new (%d) bytes\n", \
  115. dwSize)); \
  116. *lpdwLastError = ERROR_OUTOFMEMORY; \
  117. return FALSE; \
  118. } \
  119. } \
  120. \
  121. if (dwStatus != 0) \
  122. { \
  123. ASSERT( (dwStatus != ERROR_INSUFFICIENT_BUFFER) \
  124. && (dwStatus != ERROR_MORE_DATA)); \
  125. \
  126. SensPrintA(SENS_ERR, (#FUNC_NAME "() returned %d\n", dwStatus));\
  127. *lpdwLastError = dwStatus; \
  128. /* P3 BUG: Might need to fire ConnectionLost() here */ \
  129. gdwLANState = FALSE; \
  130. UpdateSensCache(LAN); \
  131. delete pTable; \
  132. return FALSE; \
  133. }
  134. /*++
  135. Macro Description:
  136. This macro ends the BEGIN_GETTABLE() macro.
  137. Arguments:
  138. None.
  139. Notes:
  140. a. If we have a return between BEGIN_XXX and END_XXX, we need to make
  141. sure that we free pTable.
  142. --*/
  143. #define \
  144. END_GETTABLE() \
  145. \
  146. delete pTable; \
  147. \
  148. }
  149. BOOL
  150. DoLanSetup(
  151. void
  152. )
  153. /*++
  154. Routine Description:
  155. Arguments:
  156. None.
  157. Return Value:
  158. --*/
  159. {
  160. BOOL bRetValue;
  161. WORD wVersionRequested;
  162. WSADATA wsaData;
  163. int err;
  164. bRetValue = FALSE;
  165. //
  166. // NT5-specific stuff
  167. //
  168. #if defined(SENS_NT5)
  169. ghMediaTimer = NULL;
  170. // Register for Media-sense notifications
  171. if (FALSE == MediaSenseRegister())
  172. {
  173. SensPrintA(SENS_ERR, ("%s MediaSenseRegister() failed.\n", SERVICE_NAME));
  174. }
  175. #endif // SENS_NT5
  176. //
  177. // AOL-specific code
  178. //
  179. #if defined(AOL_PLATFORM)
  180. gdwAOLState = FALSE;
  181. #endif // AOL_PLATFORM
  182. //
  183. // Initialize Winsock.
  184. //
  185. wVersionRequested = SENS_WINSOCK_VERSION;
  186. err = WSAStartup(wVersionRequested, &wsaData);
  187. if (err != 0)
  188. {
  189. SensPrintA(SENS_ERR, ("WSAStartup() returned %d!\n", err));
  190. bRetValue = FALSE;
  191. goto Cleanup;
  192. }
  193. bRetValue = TRUE;
  194. Cleanup:
  195. //
  196. // Cleanup
  197. //
  198. #if defined(SENS_NT4)
  199. if ( (FALSE == bRetValue)
  200. && (NULL != hDLL))
  201. {
  202. FreeLibrary(hDLL);
  203. }
  204. #endif // SENS_NT4
  205. return bRetValue;
  206. }
  207. #ifdef DBG
  208. inline void
  209. PrintIfState(
  210. void
  211. )
  212. /*++
  213. Routine Description:
  214. Arguments:
  215. Return Value:
  216. --*/
  217. {
  218. int i;
  219. SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
  220. SensPrintA(SENS_INFO, ("| Valid Index UcastIN UcastOUT NUcastIN NUcastOUT ErrIN ErrOUT DiscIN DiscOUT |\n"));
  221. SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
  222. for (i = 0; i < MAX_IF_ENTRIES; i++)
  223. {
  224. SensPrintA(SENS_INFO, ("| %c %9d %7d %7d %9d %9d %5d %6d %6d %6d |\n",
  225. gIfState[i].fValid ? 'Y' : 'N',
  226. gIfState[i].dwIndex,
  227. gIfState[i].dwInUcastPkts,
  228. gIfState[i].dwOutUcastPkts,
  229. gIfState[i].dwInNUcastPkts,
  230. gIfState[i].dwOutNUcastPkts,
  231. gIfState[i].dwInErrors,
  232. gIfState[i].dwOutErrors,
  233. gIfState[i].dwInDiscards,
  234. gIfState[i].dwOutDiscards)
  235. );
  236. }
  237. SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
  238. }
  239. #else // DBG
  240. #define PrintIfState() // Nothing
  241. #endif // DBG
  242. #ifdef DETAIL_DEBUG
  243. void
  244. PrintIfTable(
  245. MIB_IFTABLE *pTable
  246. )
  247. {
  248. int i;
  249. SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
  250. SensPrintA(SENS_INFO, ("| Type Index Spd/1K UcastIN UcastOUT ErrorIN OUT DiscIN OUT Opr Adm |\n"));
  251. SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
  252. for (i = 0; i < pTable->dwNumEntries; i++)
  253. {
  254. SensPrintA(SENS_INFO, ("| %4d %7d %6d %7d %8d %7d %3d %6d %3d %3d %3d |\n",
  255. pTable->table[i].dwType,
  256. pTable->table[i].dwIndex,
  257. pTable->table[i].dwSpeed/1000,
  258. pTable->table[i].dwInUcastPkts,
  259. pTable->table[i].dwOutUcastPkts,
  260. pTable->table[i].dwInErrors,
  261. pTable->table[i].dwOutErrors,
  262. pTable->table[i].dwInDiscards,
  263. pTable->table[i].dwOutDiscards,
  264. pTable->table[i].dwOperStatus,
  265. pTable->table[i].dwAdminStatus
  266. )
  267. );
  268. }
  269. SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
  270. }
  271. void
  272. PrintIpStats(
  273. void
  274. )
  275. {
  276. SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
  277. SensPrintA(SENS_INFO, ("| IP_STATS InReceives OutRequests |\n"));
  278. SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
  279. SensPrintA(SENS_INFO, ("| %10d %10d |\n",
  280. gIpStats.dwInReceives,
  281. gIpStats.dwOutRequests)
  282. );
  283. SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
  284. }
  285. #else
  286. #define PrintIfTable(_X_) // Nothing
  287. #define PrintIpStats() // Nothing
  288. #endif // DETAIL_DEBUG
  289. BOOL WINAPI
  290. EvaluateLanConnectivityDelayed(
  291. LPDWORD
  292. )
  293. {
  294. for (int i = 0; i < 4; i++)
  295. {
  296. Sleep(MEDIASENSE_EVALUATE_LAN_DELAY*i); // First time waits 0 ms, no delay
  297. if (EvaluateLanConnectivity(NULL))
  298. {
  299. SensPrintA(SENS_INFO, ("EvaluateLanConnectivity: Delayed eval successful (%d)\n", i));
  300. return TRUE;
  301. }
  302. }
  303. return FALSE;
  304. }
  305. BOOL WINAPI
  306. EvaluateLanConnectivity(
  307. OUT LPDWORD lpdwLastError
  308. )
  309. /*++
  310. Routine Description:
  311. Evaluates LAN Connectivity.
  312. Arguments:
  313. lpdwLastError - if return value is FALSE, GetLastError is returned
  314. in this OUT parameter.
  315. Notes:
  316. a. This routine can be entered by multiple threads at the same time.
  317. Currently only very essential code is under a critical section.
  318. b. This routine can be executed as a threadpool work item.
  319. Return Value:
  320. TRUE, if LAN connectivity is present.
  321. FALSE, otherwise
  322. --*/
  323. {
  324. DWORD dwNow;
  325. DWORD dwLocalLastError;
  326. DWORD dwActiveInterfaceSpeed;
  327. WCHAR wszActiveInterfaceName[MAX_INTERFACE_NAME_LEN];
  328. BOOL bLanAlive;
  329. BOOL bSomeInterfaceActive;
  330. BOOL bCheckCache;
  331. dwNow = GetTickCount();
  332. dwLocalLastError = ERROR_NO_NETWORK;
  333. dwActiveInterfaceSpeed = 0x0;
  334. bLanAlive = FALSE;
  335. bSomeInterfaceActive = FALSE;
  336. bCheckCache = FALSE;
  337. if (lpdwLastError)
  338. {
  339. *lpdwLastError = dwLocalLastError;
  340. }
  341. else
  342. {
  343. lpdwLastError = &dwLocalLastError;
  344. }
  345. //
  346. // Get infomation about IP statistics.
  347. //
  348. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  349. //
  350. // PurgeStaleInterfaces
  351. //
  352. PurgeStaleInterfaces(pTable, lpdwLastError);
  353. //
  354. // Algorithm:
  355. //
  356. // o Create a record.
  357. // o See if this record exists.
  358. // o Save the record, if not and return success.
  359. // o If it does exist, compare. If greater, then save record.
  360. // o If not greater, try other entries.
  361. // o All entries? return failure.
  362. //
  363. IF_STATE ifEntry;
  364. DWORD i;
  365. SensPrintA(SENS_INFO, ("GetIfTable(): Number of entries - %d.\n", pTable->dwNumEntries));
  366. PrintIfTable(pTable);
  367. i = 0;
  368. while (i < pTable->dwNumEntries)
  369. {
  370. //
  371. // Calculate only if it is a non-WAN and non-Loopback interface.
  372. //
  373. if ( (pTable->table[i].dwType != MIB_IF_TYPE_PPP)
  374. && (pTable->table[i].dwType != MIB_IF_TYPE_SLIP)
  375. && (pTable->table[i].dwType != MIB_IF_TYPE_LOOPBACK))
  376. {
  377. BOOL bForceInvalid = FALSE;
  378. //
  379. // BOOT UP WITH NO NETWORK:
  380. //
  381. // Check to see if both UnicastIN and UnicastOUT are zero. If so,
  382. // this interface is considered as not active and we skip it.
  383. //
  384. if ( (pTable->table[i].dwInUcastPkts == 0)
  385. && (pTable->table[i].dwOutUcastPkts == 0))
  386. {
  387. bForceInvalid = TRUE;
  388. }
  389. //
  390. // Check if networking says it is connected, if not, skip it.
  391. //
  392. if (pTable->table[i].dwOperStatus < MIB_IF_OPER_STATUS_CONNECTING)
  393. {
  394. SensPrintA(SENS_INFO, ("GetIfTable: Found interface %d in < connecting state (%d), ignored\n",
  395. pTable->table[i].dwIndex, pTable->table[i].dwOperStatus) );
  396. bForceInvalid = TRUE;
  397. }
  398. //
  399. // At this stage, there is some Unicast activity on this interface.
  400. // So, we can skip the check for Unicast activity below.
  401. //
  402. // Fill the IF_STATE structure
  403. ifEntry.dwIndex = pTable->table[i].dwIndex;
  404. ifEntry.dwInUcastPkts = pTable->table[i].dwInUcastPkts;
  405. ifEntry.dwOutUcastPkts = pTable->table[i].dwOutUcastPkts;
  406. ifEntry.dwInNUcastPkts = pTable->table[i].dwInNUcastPkts;
  407. ifEntry.dwOutNUcastPkts = pTable->table[i].dwOutNUcastPkts;
  408. ifEntry.dwInErrors = pTable->table[i].dwInErrors;
  409. ifEntry.dwOutErrors = pTable->table[i].dwOutErrors;
  410. ifEntry.dwInDiscards = pTable->table[i].dwInDiscards;
  411. ifEntry.dwOutDiscards = pTable->table[i].dwOutDiscards;
  412. bSomeInterfaceActive = HasIfStateChanged(ifEntry, bForceInvalid);
  413. if (TRUE == bSomeInterfaceActive)
  414. {
  415. bLanAlive = TRUE;
  416. // Save info about interface for later use.
  417. dwActiveInterfaceSpeed = pTable->table[i].dwSpeed;
  418. wcscpy(wszActiveInterfaceName, pTable->table[i].wszName);
  419. }
  420. else
  421. {
  422. if (!bForceInvalid)
  423. {
  424. bCheckCache = TRUE; // Idle IF found but still valid (enable MAX_LAN_INTERNAL check below)
  425. }
  426. }
  427. }
  428. i++;
  429. } // while ()
  430. PrintIfState();
  431. END_GETTABLE()
  432. //
  433. // RACE Condition Fix:
  434. //
  435. // If there are 2 threads that are in EvaluateLanConnectivity() and one
  436. // of them updates the interface's packet cache, then there is a distinct
  437. // possibility that the second thread will compare with the updated cache
  438. // and wrongly conclude that there is no activity. We ignore any loss of
  439. // connectivity that was evaluated before MAX_LAN_INTERVAL (ie., we should
  440. // keep giving cached information for MAX_LAN_INTERVAL seconds).
  441. //
  442. if ( (TRUE == bCheckCache)
  443. && (FALSE == bLanAlive) )
  444. {
  445. dwNow = GetTickCount();
  446. if ( ((dwNow - gdwLastLANTime) <= MAX_LAN_INTERVAL)
  447. && (gdwLastLANTime != 0) )
  448. {
  449. SensPrintA(SENS_DBG, ("EvaluateLanConnectivity(): Returning TRUE "
  450. "(Now - %d sec, LastLANTime - %d sec)\n", dwNow/1000, gdwLastLANTime/1000));
  451. return TRUE;
  452. }
  453. }
  454. //
  455. // NOTE: If we are doing DWORD InterlockedExchange, then assignment
  456. // should suffice. Using InterlockedExchange is not a bug, though.
  457. //
  458. if (bLanAlive)
  459. {
  460. SensPrintA(SENS_DBG, ("**** EvaluateLanConnectivity(): Setting"
  461. " gdwLastLANTime to %d secs\n", dwNow/1000));
  462. InterlockedExchange(&gdwLastLANTime, dwNow);
  463. }
  464. else
  465. {
  466. SensPrintA(SENS_DBG, ("**** EvaluateLanConnectivity(): Setting"
  467. " gdwLastLANTime to 0 secs\n"));
  468. InterlockedExchange(&gdwLastLANTime, 0x0);
  469. }
  470. //
  471. // Adjust LAN state and fire an event, if necessary.
  472. //
  473. if (InterlockedExchange(&gdwLANState, bLanAlive) != bLanAlive)
  474. {
  475. //
  476. // LAN Connectivity state changed.
  477. //
  478. SENSEVENT_NETALIVE Data;
  479. Data.eType = SENS_EVENT_NETALIVE;
  480. Data.bAlive = bLanAlive;
  481. memset(&Data.QocInfo, 0x0, sizeof(QOCINFO));
  482. Data.QocInfo.dwSize = sizeof(QOCINFO);
  483. Data.QocInfo.dwFlags = NETWORK_ALIVE_LAN;
  484. Data.QocInfo.dwInSpeed = dwActiveInterfaceSpeed;
  485. Data.QocInfo.dwOutSpeed = dwActiveInterfaceSpeed;
  486. //
  487. // NOTE: When dwActiveInterfaceName gets the right value from
  488. // IPHLPAPIs we should use that name. Until then, we use a default.
  489. //
  490. Data.strConnection = DEFAULT_LAN_CONNECTION_NAME;
  491. UpdateSensCache(LAN);
  492. SensFireEvent((PVOID)&Data);
  493. }
  494. return bLanAlive;
  495. }
  496. BOOL
  497. HasIfStateChanged(
  498. IF_STATE ifEntry,
  499. BOOL bForceInvalid
  500. )
  501. /*++
  502. Routine Description:
  503. Compares the current state of a remote network IF with the cached history
  504. to determine if it is active or not.
  505. Arguments:
  506. ifEntry - An interface that appears to have changed state and is "valid" as a remote
  507. LAN if. (ie, loopback, pptp, etc should be filtered out)
  508. bForceInvalid - If TRUE, don't bother to look at the stats; this interface is NOT valid.
  509. Return Value:
  510. TRUE - ifEntry appears up and active
  511. FALSE - ifEntry inactive or down
  512. --*/
  513. {
  514. int i, j;
  515. static int iLastActiveIndex = -1;
  516. BOOL bActive;
  517. BOOL bSeenButInactive;
  518. DWORD dwInDiff;
  519. DWORD dwOutDiff;
  520. int iNUcastDiff;
  521. i = 0;
  522. bActive = FALSE;
  523. bSeenButInactive = FALSE;
  524. dwInDiff = 0;
  525. dwOutDiff = 0;
  526. iNUcastDiff = 0;
  527. RequestSensLock();
  528. //
  529. // Compare the current snapshot with the saved snapshot
  530. // for this interface.
  531. //
  532. while (i < MAX_IF_ENTRIES)
  533. {
  534. if ( (gIfState[i].fValid == TRUE)
  535. && (gIfState[i].dwIndex == ifEntry.dwIndex))
  536. {
  537. if (bForceInvalid)
  538. {
  539. gIfState[i].fValid = FALSE;
  540. break;
  541. }
  542. if ( (ifEntry.dwInUcastPkts > gIfState[i].dwInUcastPkts)
  543. || (ifEntry.dwOutUcastPkts > gIfState[i].dwOutUcastPkts)
  544. || (ifEntry.dwInNUcastPkts > gIfState[i].dwInNUcastPkts)
  545. || (ifEntry.dwOutNUcastPkts > gIfState[i].dwOutNUcastPkts)
  546. || (ifEntry.dwInErrors > gIfState[i].dwInErrors)
  547. || (ifEntry.dwOutErrors > gIfState[i].dwOutErrors)
  548. || (ifEntry.dwInDiscards > gIfState[i].dwInDiscards)
  549. || (ifEntry.dwOutDiscards > gIfState[i].dwOutDiscards))
  550. {
  551. //
  552. // HEURISTIC:
  553. //
  554. // a. When the net tap is pulled out, it has been observed that
  555. // the difference in the incoming non-Unicast packet count
  556. // is within +1 thru -1 of the difference in the outgoing
  557. // non-Unicast packet count. Most of the times the diff of
  558. // these differences is 0. We don't count this as LAN alive
  559. //
  560. // b. Also, there should be no change in the unicast IN packet
  561. // count. Unicast OUT packet count may change. This could be
  562. // problematic.
  563. //
  564. dwInDiff = ifEntry.dwInNUcastPkts - gIfState[i].dwInNUcastPkts;
  565. dwOutDiff = ifEntry.dwOutNUcastPkts - gIfState[i].dwOutNUcastPkts;
  566. iNUcastDiff = dwOutDiff - dwInDiff;
  567. SensPrintA(SENS_INFO, ("HasIfStateChanged(): dwInDiff = %d, "
  568. "dwOutDiff = %d, dwNUcastDiff = %d, UcastINDiff = "
  569. "%d, UcastOUTDiff = %d\n",
  570. dwInDiff, dwOutDiff, iNUcastDiff,
  571. ifEntry.dwInUcastPkts - gIfState[i].dwInUcastPkts,
  572. ifEntry.dwOutUcastPkts - gIfState[i].dwOutUcastPkts));
  573. if ( (ifEntry.dwInUcastPkts == gIfState[i].dwInUcastPkts)
  574. && (iNUcastDiff <= BROADCAST_ACTIVITY_THRESHOLD)
  575. && (iNUcastDiff >= -BROADCAST_ACTIVITY_THRESHOLD))
  576. {
  577. SensPrintA(SENS_INFO, ("HasIfStateChanged(): Interface %d"
  578. " has only Broadcast activity (Diff is %d)!\n",
  579. gIfState[i].dwIndex, iNUcastDiff));
  580. bSeenButInactive = TRUE;
  581. }
  582. else
  583. {
  584. //
  585. // Unicast IN packet counts have changed or Broadcast
  586. // activity is greater than the threshold.
  587. //
  588. iLastActiveIndex = i;
  589. bActive = TRUE;
  590. SensPrintA(SENS_INFO, ("HasStateChanged(): Interface %d "
  591. "has been active.\n", gIfState[i].dwIndex));
  592. gdwLastLANTime = GetTickCount();
  593. SensPrintA(SENS_DBG, ("**** HasIfStateChanged(): Setting "
  594. "gdwLastLANTime to %d secs\n", gdwLastLANTime/1000));
  595. }
  596. //
  597. // Save the new values.
  598. //
  599. memcpy(&gIfState[i], &ifEntry, sizeof(IF_STATE));
  600. gIfState[i].fValid = TRUE;
  601. }
  602. else
  603. {
  604. SensPrintA(SENS_INFO, ("HasStateChanged(): Interface %d has NO activity.\n",
  605. gIfState[i].dwIndex));
  606. bSeenButInactive = TRUE;
  607. }
  608. // Found the interface, so stop searching
  609. break;
  610. }
  611. i++;
  612. } // while ()
  613. ReleaseSensLock();
  614. if ( (bSeenButInactive == TRUE)
  615. || (bForceInvalid) )
  616. {
  617. return FALSE;
  618. }
  619. if (bActive == TRUE)
  620. {
  621. return TRUE;
  622. }
  623. //
  624. // We are seeing this interface for the first time. Go ahead and save it
  625. // in the global interface state array.
  626. //
  627. i = MAX_IF_ENTRIES;
  628. j = iLastActiveIndex;
  629. RequestSensLock();
  630. while (i > 0)
  631. {
  632. // Try to find a free slot starting from the last active slot.
  633. j = (j+1) % MAX_IF_ENTRIES;
  634. if (gIfState[j].fValid == FALSE)
  635. {
  636. // Found one!
  637. break;
  638. }
  639. i--;
  640. }
  641. //
  642. // NOTE: If there are more than MAX_IF_ENTRIES, we will start
  643. // start reusing valid interface elements in gIfState array. This,
  644. // I guess, is OK since we will have enough interfaces to figure
  645. // out connectivity.
  646. //
  647. memcpy(&gIfState[j], &ifEntry, sizeof(IF_STATE));
  648. gIfState[j].fValid = TRUE;
  649. ReleaseSensLock();
  650. SensPrintA(SENS_ERR, ("******** HasIfStateChanged(): Adding a new "
  651. "interface with index %d\n", gIfState[j].dwIndex));
  652. return TRUE;
  653. }
  654. BOOL
  655. MediaSenseRegister(
  656. void
  657. )
  658. /*++
  659. Routine Description:
  660. Schedule a workitem to register for Media-sense notifications from WMI.
  661. Arguments:
  662. None.
  663. Return Value:
  664. TRUE, if success.
  665. FALSE, otherwise.
  666. --*/
  667. {
  668. BOOL bRetVal;
  669. bRetVal = TRUE;
  670. ASSERT(gdwMediaSenseState == SENSSVC_START);
  671. //
  672. // Create a timer object to schedule (one-time only) Media-sense
  673. // registration.
  674. //
  675. SensSetTimerQueueTimer(
  676. bRetVal, // Bool return on NT5
  677. ghMediaTimer, // Handle return on IE5
  678. NULL, // Use default process timer queue
  679. MediaSenseRegisterHelper, // Callback
  680. NULL, // Parameter
  681. MEDIASENSE_INITIALIZATION_DELAY, // Time from now when timer should fire
  682. 0x0, // Time inbetween firings of this timer
  683. 0x0 // No Flags.
  684. );
  685. if (SENS_TIMER_CREATE_FAILED(bRetVal, ghMediaTimer))
  686. {
  687. SensPrintA(SENS_ERR, ("MediaSenseRegister(): SensSetTimerQueueTimer() failed with %d.\n",
  688. GetLastError()));
  689. bRetVal = FALSE;
  690. }
  691. return bRetVal;
  692. }
  693. SENS_TIMER_CALLBACK_RETURN
  694. MediaSenseRegisterHelper(
  695. PVOID pvIgnore,
  696. BOOLEAN bIgnore
  697. )
  698. /*++
  699. Routine Description:
  700. Helper routine that is scheduled to the WMI registration.
  701. Arguments:
  702. pvIgnore - Ignored.
  703. bIgnore - Ignored.
  704. Return Value:
  705. None (void).
  706. --*/
  707. {
  708. ULONG Status;
  709. GUID guid;
  710. RequestSensLock();
  711. if ( (SENSSVC_STOP == gdwMediaSenseState)
  712. || (UNREGISTERED == gdwMediaSenseState))
  713. {
  714. goto Cleanup;
  715. }
  716. //
  717. // Enable the media disconnect event.
  718. //
  719. guid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
  720. Status = WmiNotificationRegistrationW(
  721. &guid, // Event of interest
  722. TRUE, // Enable Notification?
  723. EventCallbackRoutine, // Callback function
  724. 0, // Callback context
  725. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  726. );
  727. if (ERROR_SUCCESS != Status)
  728. {
  729. SensPrintA(SENS_ERR, ("Unable to enable media disconnect event: 0x%x!\n", Status));
  730. goto Cleanup;
  731. }
  732. //
  733. // Enable the media connect event
  734. //
  735. guid = GUID_NDIS_STATUS_MEDIA_CONNECT;
  736. Status = WmiNotificationRegistrationW(
  737. &guid, // Event of interest
  738. TRUE, // Enable Notification?
  739. EventCallbackRoutine, // Callback function
  740. 0, // Callback context
  741. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  742. );
  743. if (ERROR_SUCCESS != Status)
  744. {
  745. SensPrintA(SENS_ERR, ("Unable to enable media connect event: 0x%x!\n", Status));
  746. ASSERT(0); // If we hit this then we need to unregister the first registration above.
  747. goto Cleanup;
  748. }
  749. SensPrintA(SENS_ERR, ("MediaSenseRegister(): Media-sense registration successful.\n"));
  750. gdwMediaSenseState = REGISTERED;
  751. Cleanup:
  752. //
  753. // Cleanup
  754. //
  755. ReleaseSensLock();
  756. return;
  757. }
  758. BOOL
  759. MediaSenseUnregister(
  760. void
  761. )
  762. /*++
  763. Routine Description:
  764. Unregister from Media-sense notifications from WMI.
  765. Arguments:
  766. None.
  767. Return Value:
  768. TRUE, if success.
  769. FALSE, otherwise.
  770. --*/
  771. {
  772. ULONG Status;
  773. GUID guid;
  774. BOOL bRetVal;
  775. BOOL bRegistered;
  776. bRetVal = TRUE;
  777. bRegistered = FALSE;
  778. RequestSensLock();
  779. ASSERT(gdwMediaSenseState == REGISTERED ||
  780. gdwMediaSenseState == SENSSVC_START);
  781. if (gdwMediaSenseState == REGISTERED)
  782. {
  783. bRegistered = TRUE;
  784. }
  785. gdwMediaSenseState = SENSSVC_STOP;
  786. if (NULL != ghMediaTimer)
  787. {
  788. bRetVal = SensCancelTimerQueueTimer(NULL, ghMediaTimer, NULL);
  789. ghMediaTimer = NULL;
  790. SensPrintA(SENS_INFO, ("[%d] MediaSensUnregister(): SensCancelTimer"
  791. "QueueTimer(Media) %s\n", GetTickCount(),
  792. bRetVal ? "succeeded" : "failed!"));
  793. }
  794. if (!bRegistered)
  795. {
  796. // Should not do unregistration.
  797. goto Cleanup;
  798. }
  799. //
  800. // Disable the media disconnect event.
  801. //
  802. guid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
  803. Status = WmiNotificationRegistrationW(
  804. &guid, // Event of interest
  805. FALSE, // Enable Notification?
  806. EventCallbackRoutine, // Callback function
  807. 0, // Callback context
  808. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  809. );
  810. if (ERROR_SUCCESS != Status)
  811. {
  812. SensPrintA(SENS_ERR, ("[%d] MediaSensUnregister(): Unable to disable "
  813. "media disconnect event: 0x%x!\n", GetTickCount(), Status));
  814. ASSERT(0); // If this fails analyze if we should still to the second unregister
  815. bRetVal = FALSE;
  816. }
  817. //
  818. // Disable the connect event
  819. //
  820. guid = GUID_NDIS_STATUS_MEDIA_CONNECT;
  821. Status = WmiNotificationRegistrationW(
  822. &guid, // Event of interest
  823. FALSE, // Enable Notification?
  824. EventCallbackRoutine, // Callback function
  825. 0, // Callback context
  826. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  827. );
  828. if (ERROR_SUCCESS != Status)
  829. {
  830. SensPrintA(SENS_ERR, ("[%d] MediaSensUnregister(): Unable to disable "
  831. "media disconnect event: 0x%x!\n", GetTickCount(), Status));
  832. bRetVal = FALSE;
  833. }
  834. Cleanup:
  835. //
  836. //
  837. //
  838. gdwMediaSenseState = UNREGISTERED;
  839. ReleaseSensLock();
  840. return bRetVal;
  841. }
  842. void
  843. EventCallbackRoutine(
  844. IN PWNODE_HEADER WnodeHeader,
  845. IN ULONG Context
  846. )
  847. /*++
  848. Routine Description:
  849. Arguments:
  850. Return Value:
  851. --*/
  852. {
  853. PULONG Data;
  854. PWNODE_SINGLE_INSTANCE Wnode = (PWNODE_SINGLE_INSTANCE)WnodeHeader;
  855. PWCHAR Name;
  856. DWORD dwIgnore;
  857. ULONG NameLen;
  858. int result;
  859. //
  860. // Get the information for the media disconnect.
  861. //
  862. result = memcmp(&WnodeHeader->Guid, &GUID_NDIS_STATUS_MEDIA_DISCONNECT, sizeof(GUID));
  863. if (0 == result)
  864. {
  865. SensPrintA(SENS_INFO, ("NDIS: received a media disconnect!\n"));
  866. EvaluateConnectivity(TYPE_LAN);
  867. }
  868. else
  869. {
  870. //
  871. // Get the information for the media connect.
  872. //
  873. result = memcmp(&WnodeHeader->Guid, &GUID_NDIS_STATUS_MEDIA_CONNECT, sizeof(GUID));
  874. if (0 == result)
  875. {
  876. SensPrintA(SENS_INFO, ("NDIS: received a media connect!\n"));
  877. EvaluateConnectivity(TYPE_DELAY_LAN);
  878. }
  879. else
  880. {
  881. SensPrintA(SENS_WARN, ("NDIS: Unknown event received!\n"));
  882. }
  883. }
  884. Name = (PWCHAR)RtlOffsetToPointer(Wnode, Wnode->OffsetInstanceName);
  885. SensPrintW(SENS_INFO, (L"NDIS: Instance: %ws\n", Name));
  886. }
  887. BOOL
  888. GetIfEntryStats(
  889. IN DWORD dwIfIndex,
  890. IN LPQOCINFO lpQOCInfo,
  891. OUT LPDWORD lpdwLastError,
  892. OUT LPBOOL lpbIsWanIf
  893. )
  894. /*++
  895. Routine Description:
  896. Get the Statistics field of the Interface entry which has the given
  897. index.
  898. Arguments:
  899. dwIfIndex - The interface of interest.
  900. lpQOCInfo - QOC Info structure whose fields are set when the interface
  901. entry is found in the interface table.
  902. lpdwLastError - The GLE, if any.
  903. lpbIsWanIf - Is the interface at this index a WAN interface or not.
  904. Return Value:
  905. TRUE, if we find the index.
  906. FALSE, otherwise.
  907. --*/
  908. {
  909. DWORD i;
  910. BOOL bFound;
  911. *lpdwLastError = ERROR_SUCCESS;
  912. *lpbIsWanIf = FALSE;
  913. bFound = FALSE;
  914. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  915. // Search the Interface table for the entry with the given index.
  916. for (i = 0; i < pTable->dwNumEntries; i++)
  917. {
  918. if (pTable->table[i].dwIndex == dwIfIndex)
  919. {
  920. bFound = TRUE;
  921. SensPrintA(SENS_INFO, ("GetIfEntryStats(): Interface %d is of "
  922. "type %d\n", dwIfIndex, pTable->table[i].dwType));
  923. if ( (pTable->table[i].dwType == MIB_IF_TYPE_PPP)
  924. || (pTable->table[i].dwType == MIB_IF_TYPE_SLIP))
  925. {
  926. *lpbIsWanIf = TRUE;
  927. }
  928. else
  929. {
  930. *lpbIsWanIf = FALSE;
  931. }
  932. if (lpQOCInfo != NULL)
  933. {
  934. lpQOCInfo->dwSize = sizeof(QOCINFO);
  935. lpQOCInfo->dwInSpeed = pTable->table[i].dwSpeed;
  936. lpQOCInfo->dwOutSpeed = pTable->table[i].dwSpeed;
  937. lpQOCInfo->dwFlags = (*lpbIsWanIf) ? CONNECTION_WAN : CONNECTION_LAN;
  938. }
  939. break;
  940. }
  941. }
  942. END_GETTABLE()
  943. return bFound;
  944. }
  945. BOOL
  946. CheckForReachability(
  947. IN IPAddr DestIpAddr,
  948. IN OUT LPQOCINFO lpQOCInfo,
  949. OUT LPDWORD lpdwLastError
  950. )
  951. /*++
  952. Routine Description:
  953. This helper function does all the dirty work in checking for Reachability
  954. of a particular destination.
  955. Arguments:
  956. DestIpAddr - The Destination of interest.
  957. lpQOCInfo - The QOC Info structure.
  958. lpdwLastError - Returns the GetLastError value when the destination is
  959. not reachable.
  960. Return Value:
  961. TRUE, if the destination IP Address is reachable
  962. FALSE, otherwise. GLE returned in lpdwLastError.
  963. --*/
  964. {
  965. DWORD i;
  966. BOOL bSuccess;
  967. BOOL bSameNetId;
  968. BOOL bReachable;
  969. BOOL bIsWanIf;
  970. DWORD dwNetId;
  971. DWORD dwSubnetMask;
  972. DWORD ifNum;
  973. DWORD dwHopCount;
  974. DWORD dwRtt;
  975. ifNum = -1;
  976. dwRtt = 0;
  977. bSuccess = FALSE;
  978. bIsWanIf = FALSE;
  979. bReachable = FALSE;
  980. bSameNetId = FALSE;
  981. //
  982. // On NT4, check to see if IPHLPAPI was present. If not, gracefully fail with
  983. // default values.
  984. //
  985. #if defined(SENS_NT4)
  986. if (FALSE == gbIpInitSuccessful)
  987. {
  988. lpdwLastError = 0x0;
  989. if (NULL != lpQOCInfo)
  990. {
  991. lpQOCInfo->dwSize = sizeof(QOCINFO);
  992. lpQOCInfo->dwFlags = CONNECTION_LAN;
  993. lpQOCInfo->dwInSpeed = DEFAULT_LAN_BANDWIDTH;
  994. lpQOCInfo->dwOutSpeed = DEFAULT_LAN_BANDWIDTH;
  995. }
  996. return TRUE;
  997. }
  998. #endif // SENS_NT4
  999. //
  1000. // Search the IP Address table for an entry with the same NetId as the
  1001. // Destination. If such an entry exists, the Destination is in the same
  1002. // sub-net and hence reachable.
  1003. //
  1004. BEGIN_GETTABLE(MIB_IPADDRTABLE, MIB_IPADDRROW, GETIPADDRTABLE, MAX_IPADDRTABLE_ROWS)
  1005. // Search for an entry with the same NetId
  1006. for (i = 0; i < pTable->dwNumEntries; i++)
  1007. {
  1008. // Compare NetIds.
  1009. dwSubnetMask = pTable->table[i].dwMask;
  1010. dwNetId = pTable->table[i].dwAddr & dwSubnetMask;
  1011. SensPrintA(SENS_INFO, ("IPADDRESS(%d) - Mask %8x, IP %8x, NETID %8x, COMP %8x\n", i,
  1012. pTable->table[i].dwMask,
  1013. pTable->table[i].dwAddr,
  1014. dwNetId,
  1015. (DestIpAddr & dwSubnetMask))
  1016. );
  1017. if ( (pTable->table[i].dwAddr != 0x0)
  1018. && ((DestIpAddr & dwSubnetMask) == dwNetId))
  1019. {
  1020. bSameNetId = TRUE;
  1021. ifNum = pTable->table[i].dwIndex;
  1022. SensPrintA(SENS_INFO, ("CheckForReachability(): Found entry in IPAddr Table with same NetId\n"));
  1023. break;
  1024. }
  1025. }
  1026. END_GETTABLE()
  1027. if (bSameNetId)
  1028. {
  1029. // Destination is in the same Subnet. Get stats from the IfTable.
  1030. bSuccess = GetIfEntryStats(ifNum, lpQOCInfo, lpdwLastError, &bIsWanIf);
  1031. ASSERT(bSuccess == TRUE);
  1032. if (bSuccess)
  1033. {
  1034. return TRUE;
  1035. }
  1036. }
  1037. //
  1038. // Entry is not in the IP AddrTable. We need to Ping. Search the Gateway
  1039. // table for default gateway and get it's interface statistics.
  1040. //
  1041. BEGIN_GETTABLE(MIB_IPFORWARDTABLE, MIB_IPFORWARDROW, GETIPFORWARDTABLE, MAX_IPFORWARDTABLE_ROWS)
  1042. for (i = 0; i < pTable->dwNumEntries; i++)
  1043. {
  1044. dwSubnetMask = pTable->table[i].dwForwardMask;
  1045. dwNetId = pTable->table[i].dwForwardDest & dwSubnetMask;
  1046. ifNum = pTable->table[i].dwForwardIfIndex;
  1047. SensPrintA(SENS_INFO, ("IPFORWARD(%d) - Mask %8x, IP %8x, NETID %8x, COMP %8x\n", i,
  1048. pTable->table[i].dwForwardMask,
  1049. pTable->table[i].dwForwardDest,
  1050. dwNetId,
  1051. (DestIpAddr & dwSubnetMask))
  1052. );
  1053. if (pTable->table[i].dwForwardDest == 0x0)
  1054. {
  1055. //
  1056. // Skip the default gateway 0.0.0.0. But, get the statistics
  1057. // anyways. The QOC of the default gateway is used if we have
  1058. // to Ping the destination.
  1059. //
  1060. bSuccess = GetIfEntryStats(ifNum, lpQOCInfo, lpdwLastError, &bIsWanIf);
  1061. SensPrintA(SENS_INFO, ("Default Gateway statistics (if = %d, "
  1062. "dwSpeed = %d, IsWanIf = %s)\n", ifNum, lpQOCInfo ?
  1063. lpQOCInfo->dwInSpeed : 0x0, bIsWanIf ? "TRUE" : "FALSE"));
  1064. ASSERT(bSuccess == TRUE);
  1065. break;
  1066. }
  1067. }
  1068. END_GETTABLE()
  1069. //
  1070. // Resort to a Ping
  1071. //
  1072. bReachable = GETRTTANDHOPCOUNT(
  1073. DestIpAddr,
  1074. &dwHopCount,
  1075. MAX_HOPS_COUNT,
  1076. &dwRtt
  1077. );
  1078. //
  1079. // If we got around to doing a Ping, QOC information will have been
  1080. // retrieved when we found the Default Gateway entry.
  1081. //
  1082. SensPrintA(SENS_INFO, ("CheckForReachability(): Ping returned %s with GLE of %d\n",
  1083. bReachable ? "TRUE" : "FALSE", GetLastError()));
  1084. if (bReachable == FALSE)
  1085. {
  1086. *lpdwLastError = ERROR_HOST_UNREACHABLE;
  1087. }
  1088. //
  1089. // P3 BUG:
  1090. //
  1091. // a. We determine whether the interface on which the Ping went is a LAN
  1092. // or WAN by checking the interface type of the default gateway. This
  1093. // is not TRUE!
  1094. //
  1095. return bReachable;
  1096. }
  1097. BOOL
  1098. GetActiveWanInterfaceStatistics(
  1099. OUT LPDWORD lpdwLastError,
  1100. OUT LPDWORD lpdwWanSpeed
  1101. )
  1102. /*++
  1103. Routine Description:
  1104. Get the Statistics field of the Interface entry
  1105. Arguments:
  1106. lpdwLastError - The GLE, if any.
  1107. lpdwWanSpeed - Speed of the WAN interface
  1108. Notes:
  1109. P3 BUG: Currently, this will return the speed of the first WAN interface
  1110. it finds. This won't work properly if there are multiple "active" WAN
  1111. interfaces.
  1112. Return Value:
  1113. TRUE, if statistics were successfully retrieved
  1114. FALSE, otherwise.
  1115. --*/
  1116. {
  1117. DWORD i;
  1118. BOOL bFound;
  1119. *lpdwLastError = ERROR_SUCCESS;
  1120. *lpdwWanSpeed = DEFAULT_WAN_BANDWIDTH;
  1121. bFound = FALSE;
  1122. #if defined(SENS_NT4)
  1123. if (FALSE == gbIpInitSuccessful)
  1124. {
  1125. return TRUE;
  1126. }
  1127. #endif // SENS_NT4
  1128. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  1129. // Search the Interface table for the first active WAN interface.
  1130. for (i = 0; i < pTable->dwNumEntries; i++)
  1131. {
  1132. if ( (pTable->table[i].dwType == MIB_IF_TYPE_PPP)
  1133. || (pTable->table[i].dwType == MIB_IF_TYPE_SLIP))
  1134. {
  1135. bFound = TRUE;
  1136. if ( (pTable->table[i].dwInNUcastPkts != 0)
  1137. || (pTable->table[i].dwOutNUcastPkts != 0)
  1138. || (pTable->table[i].dwInErrors != 0)
  1139. || (pTable->table[i].dwOutErrors != 0)
  1140. || (pTable->table[i].dwInDiscards != 0)
  1141. || (pTable->table[i].dwOutDiscards != 0))
  1142. {
  1143. *lpdwWanSpeed = pTable->table[i].dwSpeed;
  1144. break;
  1145. }
  1146. }
  1147. } // for
  1148. END_GETTABLE()
  1149. return bFound;
  1150. }
  1151. BOOL
  1152. PurgeStaleInterfaces(
  1153. IN MIB_IFTABLE *pTable,
  1154. OUT LPDWORD lpdwLastError
  1155. )
  1156. /*++
  1157. Routine Description:
  1158. Remove statistics from the interfaces that went away.
  1159. Arguments:
  1160. pTable - The current If table.
  1161. lpdwLastError - The GLE, if any.
  1162. Return Value:
  1163. TRUE, always.
  1164. --*/
  1165. {
  1166. DWORD i;
  1167. DWORD j;
  1168. BOOL bFound;
  1169. *lpdwLastError = ERROR_SUCCESS;
  1170. RequestSensLock();
  1171. // Check if each valid interface in the cache still exists.
  1172. for (j = 0; j < MAX_IF_ENTRIES; j++)
  1173. {
  1174. if (gIfState[j].fValid == FALSE)
  1175. {
  1176. continue;
  1177. }
  1178. bFound = FALSE;
  1179. // Search if the interface in the cache is present in the IF_TABLE.
  1180. for (i = 0; i < pTable->dwNumEntries; i++)
  1181. {
  1182. if (pTable->table[i].dwIndex == gIfState[j].dwIndex)
  1183. {
  1184. bFound = TRUE;
  1185. }
  1186. } // for (i)
  1187. if (FALSE == bFound)
  1188. {
  1189. SensPrintA(SENS_ERR, ("******** PurgeStaleInterfaces(): Purging"
  1190. "interface with index %d\n", gIfState[j].dwIndex));
  1191. // Interface went away. So remove from Cache.
  1192. memset(&gIfState[j], 0x0, sizeof(IF_STATE));
  1193. gIfState[j].fValid = FALSE;
  1194. }
  1195. } // for (j)
  1196. ReleaseSensLock();
  1197. return TRUE;
  1198. }
  1199. #if defined(AOL_PLATFORM)
  1200. BOOL
  1201. IsAOLInstalled(
  1202. void
  1203. )
  1204. /*++
  1205. Routine Description:
  1206. Try to determine if AOL is installed on this machine.
  1207. Arguments:
  1208. None.
  1209. Return Value:
  1210. TRUE, if AOL is installed.
  1211. FALSE, otherwise.
  1212. --*/
  1213. {
  1214. if (AOL_NOT_INSTALLED == gAOLInstallState)
  1215. {
  1216. SensPrintA(SENS_ERR, ("IsAOLInstalled(): NOT INSTALLED.\n"));
  1217. return FALSE;
  1218. }
  1219. if (AOL_INSTALLED == gAOLInstallState)
  1220. {
  1221. SensPrintA(SENS_ERR, ("IsAOLInstalled(): INSTALLED !\n"));
  1222. return TRUE;
  1223. }
  1224. ASSERT(AOL_DETECT_PENDING == gAOLInstallState);
  1225. gAOLInstallState = AOL_NOT_INSTALLED;
  1226. HKEY hKeyAOL;
  1227. LONG lResult;
  1228. //
  1229. // Open AOL Key under HKCU.
  1230. //
  1231. hKeyAOL = NULL;
  1232. lResult = RegOpenKeyEx(
  1233. HKEY_CURRENT_USER, // Handle to the Parent
  1234. REGSZ_AOLKEY, // Name of the child key
  1235. 0, // Reserved
  1236. KEY_ENUMERATE_SUB_KEYS, // Security Access Mask
  1237. &hKeyAOL // Handle to the opened key
  1238. );
  1239. if (lResult != ERROR_SUCCESS)
  1240. {
  1241. SensPrintA(SENS_ERR, ("IsAOLInstalled(): RegOpenKeyEx(HKCU\\AOL) "
  1242. "failed with %d\n,", lResult));
  1243. return FALSE;
  1244. }
  1245. //
  1246. // To make sure, open AOL Key under HKLM.
  1247. //
  1248. RegCloseKey(hKeyAOL);
  1249. hKeyAOL = NULL;
  1250. lResult = RegOpenKeyEx(
  1251. HKEY_LOCAL_MACHINE, // Handle to the Parent
  1252. REGSZ_AOLKEY, // Name of the child key
  1253. 0, // Reserved
  1254. KEY_ENUMERATE_SUB_KEYS, // Security Access Mask
  1255. &hKeyAOL // Handle to the opened key
  1256. );
  1257. if (lResult != ERROR_SUCCESS)
  1258. {
  1259. SensPrintA(SENS_ERR, ("IsAOLInstalled(): RegOpenKeyEx(HKLM\\AOL) "
  1260. "failed with %d\n,", lResult));
  1261. return FALSE;
  1262. }
  1263. RegCloseKey(hKeyAOL);
  1264. gAOLInstallState = AOL_INSTALLED;
  1265. SensPrintA(SENS_ERR, ("IsAOLInstalled(): Detected that AOL is installed"
  1266. " !\n"));
  1267. return TRUE;
  1268. }
  1269. BOOL WINAPI
  1270. EvaluateAOLConnectivity(
  1271. OUT LPDWORD lpdwLastError
  1272. )
  1273. /*++
  1274. Routine Description:
  1275. Evaluates AOL WAN Connectivity.
  1276. Arguments:
  1277. lpdwLastError - if return value is FALSE, GetLastError is returned
  1278. in this OUT parameter.
  1279. Notes:
  1280. a. This routine can be executed as a threadpool work item.
  1281. b. Currently, AOL can be installed only on Win9x platforms, not NTx.
  1282. This code needs to be updated to handle NTx.
  1283. Return Value:
  1284. TRUE, if AOL WAN connectivity is present.
  1285. FALSE, otherwise
  1286. --*/
  1287. {
  1288. DWORD i;
  1289. DWORD dwNow;
  1290. DWORD dwAolIfIndex;
  1291. DWORD dwLocalLastError;
  1292. BOOL bAolAlive;
  1293. dwNow = GetTickCount();
  1294. dwAolIfIndex = -1;
  1295. dwLocalLastError = ERROR_NO_NETWORK;
  1296. bAolAlive = FALSE;
  1297. if (lpdwLastError)
  1298. {
  1299. *lpdwLastError = dwLocalLastError;
  1300. }
  1301. else
  1302. {
  1303. lpdwLastError = &dwLocalLastError;
  1304. }
  1305. //
  1306. // Check if AOL is installed
  1307. //
  1308. if (FALSE == IsAOLInstalled())
  1309. {
  1310. goto Cleanup;
  1311. }
  1312. //
  1313. // Get IF_TABLE to retrieve the AOL Adapater's interface index.
  1314. //
  1315. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  1316. SensPrintA(SENS_INFO, ("GetIfTable(): Number of entries - %d.\n",
  1317. pTable->dwNumEntries));
  1318. PrintIfTable(pTable);
  1319. for (i = 0; i < pTable->dwNumEntries; i++)
  1320. {
  1321. //
  1322. // AOL Adapter's description is atleast 3 characters long
  1323. //
  1324. if (pTable->table[i].dwDescrLen > AOL_ADAPTER_PREFIX_LEN)
  1325. {
  1326. if ( (pTable->table[i].bDescr[0] == AOL_ADAPTER_PREFIX[0])
  1327. && (pTable->table[i].bDescr[1] == AOL_ADAPTER_PREFIX[1])
  1328. && (pTable->table[i].bDescr[2] == AOL_ADAPTER_PREFIX[2]))
  1329. {
  1330. dwAolIfIndex = pTable->table[i].dwIndex;
  1331. break;
  1332. }
  1333. }
  1334. } // for ()
  1335. END_GETTABLE()
  1336. if (-1 == dwAolIfIndex)
  1337. {
  1338. SensPrintA(SENS_INFO, ("EvaluateAOLConnectivity(): No AOL adapter"
  1339. " found!\n"));
  1340. goto Cleanup;
  1341. }
  1342. //
  1343. // Found an AOL Adapter. Now, see if this Adapter has a non-zero IP Address
  1344. //
  1345. BEGIN_GETTABLE(MIB_IPADDRTABLE, MIB_IPADDRROW, GETIPADDRTABLE, MAX_IPADDRTABLE_ROWS)
  1346. // Search for an entry for the AOL adapter
  1347. for (i = 0; i < pTable->dwNumEntries; i++)
  1348. {
  1349. if ( (pTable->table[i].dwIndex == dwAolIfIndex)
  1350. && (pTable->table[i].dwAddr != 0x0))
  1351. {
  1352. bAolAlive = TRUE;
  1353. SensPrintA(SENS_INFO, ("EvaluateAOLConnectivity(): AOL Adapter's "
  1354. "IP Address is 0x%x\n", pTable->table[i].dwAddr));
  1355. break;
  1356. }
  1357. } // for ()
  1358. END_GETTABLE()
  1359. Cleanup:
  1360. //
  1361. // Cleanup
  1362. //
  1363. if (bAolAlive)
  1364. {
  1365. SensPrintA(SENS_INFO, ("EvalutateAOLConnectivity(): Setting AOL to TRUE\n"));
  1366. *lpdwLastError = ERROR_SUCCESS;
  1367. gdwAOLState = TRUE;
  1368. }
  1369. else
  1370. {
  1371. SensPrintA(SENS_INFO, ("EvalutateAOLConnectivity(): Setting AOL to FALSE\n"));
  1372. gdwAOLState = FALSE;
  1373. }
  1374. SensPrintA(SENS_INFO, ("EvaluateAOLConnectivity() returning %s, GLE of %d\n",
  1375. bAolAlive ? "TRUE" : "FALSE", *lpdwLastError));
  1376. return bAolAlive;
  1377. }
  1378. #endif // AOL_PLATFORM