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.

830 lines
22 KiB

  1. /*++
  2. Copyright (c) 1999, Microsoft Corporation
  3. Module Name:
  4. sample\networkmanager.c
  5. Abstract:
  6. The file contains network configuration related functions,
  7. implementing the network manager.
  8. NOTE: The network manager should never take the configuration entry
  9. lock (g_ce.rwlLock).
  10. . the protocol manager never modifies any g_ce field protected by it
  11. . the network manager never modifies any g_ce field protected by it
  12. . the configuration manager never cleans up any g_ce field as long as
  13. there are active threads
  14. --*/
  15. #include "pchsample.h"
  16. #pragma hdrstop
  17. BOOL
  18. ValidateInterfaceConfig (
  19. IN PIPSAMPLE_IF_CONFIG piic)
  20. /*++
  21. Routine Description
  22. Checks to see if the interface configuration is OK. It is good practice
  23. to do this because a corrupt registry can change configuration causing
  24. all sorts of debugging headaches if it is not found early
  25. Locks
  26. None
  27. Arguments
  28. piic pointer to ip sample interface's configuration
  29. Return Value
  30. TRUE if the configuration is good
  31. FALSE o/w
  32. --*/
  33. {
  34. DWORD dwErr = NO_ERROR;
  35. do // breakout loop
  36. {
  37. if (piic is NULL)
  38. {
  39. dwErr = ERROR_INVALID_PARAMETER;
  40. TRACE0(NETWORK, "Error null interface config");
  41. break;
  42. }
  43. //
  44. // check range of each field
  45. //
  46. // ensure that the metric is within bounds
  47. if (piic->ulMetric > IPSAMPLE_METRIC_INFINITE)
  48. {
  49. dwErr = ERROR_INVALID_PARAMETER;
  50. TRACE0(NETWORK, "Error metric out of range");
  51. break;
  52. }
  53. // ensure that protocol flags are fine, for now they'll always be
  54. // add more here...
  55. } while (FALSE);
  56. if (!(dwErr is NO_ERROR))
  57. {
  58. TRACE0(NETWORK, "Error corrupt interface config");
  59. LOGERR0(CORRUPT_INTERFACE_CONFIG, dwErr);
  60. return FALSE;
  61. }
  62. return TRUE;
  63. }
  64. ////////////////////////////////////////
  65. // CALLBACKFUNCTIONS
  66. ////////////////////////////////////////
  67. VOID
  68. WINAPI
  69. NM_CallbackNetworkEvent (
  70. IN PVOID pvContext,
  71. IN BOOLEAN bTimerOrWaitFired)
  72. /*++
  73. Routine Description
  74. Processes a network event on the specified interface.
  75. NOTE: The interface might have been deleted.
  76. Locks
  77. Acquires shared (g_ce.pneNetworkEntry)->rwlLock
  78. Releases (g_ce.pneNetworkEntry)->rwlLock
  79. Arguments
  80. pvContext dwIfIndex
  81. Return Value
  82. None
  83. --*/
  84. {
  85. DWORD dwErr = NO_ERROR;
  86. DWORD dwIfIndex = 0;
  87. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  88. PACKET Packet;
  89. dwIfIndex = (DWORD) pvContext;
  90. TRACE1(ENTER, "Entering NM_CallbackNetworkEvent: %u", dwIfIndex);
  91. if (!ENTER_SAMPLE_API()) { return; } // cannot return anything
  92. ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  93. do // breakout loop
  94. {
  95. // fail if the interface does not exist
  96. dwErr = IE_Get(dwIfIndex, &pieInterfaceEntry);
  97. if (dwErr != NO_ERROR)
  98. break;
  99. // fail if interface is inactive
  100. if (!INTERFACE_IS_ACTIVE(pieInterfaceEntry))
  101. {
  102. TRACE1(NETWORK, "Error interface %u is inactive", dwIfIndex);
  103. break;
  104. }
  105. RTASSERT(pieInterfaceEntry->sRawSocket != INVALID_SOCKET);
  106. if (SocketReceiveEvent(pieInterfaceEntry->sRawSocket))
  107. {
  108. if (SocketReceive(pieInterfaceEntry->sRawSocket,
  109. &Packet) is NO_ERROR)
  110. PacketDisplay(&Packet);
  111. }
  112. else
  113. {
  114. TRACE1(NETWORK, "Error interface %u false alarm", dwIfIndex);
  115. break;
  116. }
  117. } while (FALSE);
  118. // reregister ReceiveWait if the interface exists
  119. if (pieInterfaceEntry)
  120. {
  121. if (!RegisterWaitForSingleObject(&pieInterfaceEntry->hReceiveWait,
  122. pieInterfaceEntry->hReceiveEvent,
  123. NM_CallbackNetworkEvent,
  124. (PVOID) pieInterfaceEntry->dwIfIndex,
  125. INFINITE,
  126. WT_EXECUTEONLYONCE))
  127. {
  128. dwErr = GetLastError();
  129. TRACE2(NETWORK, "Error %u registering wait for %u, continuing",
  130. dwErr, pieInterfaceEntry->dwIfIndex);
  131. LOGERR0(REGISTER_WAIT_FAILED, dwErr);
  132. }
  133. }
  134. RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  135. LEAVE_SAMPLE_API();
  136. TRACE0(LEAVE, "Leaving NM_CallbackNetworkEvent");
  137. }
  138. VOID
  139. WINAPI
  140. NM_CallbackPeriodicTimer (
  141. IN PVOID pvContext,
  142. IN BOOLEAN bTimerOrWaitFired)
  143. /*++
  144. Routine Description
  145. Processes a periodic timeout event on the specified interface.
  146. NOTE: The interface might have been deleted.
  147. Locks
  148. Acquires shared (g_ce.pneNetworkEntry)->rwlLock
  149. Releases (g_ce.pneNetworkEntry)->rwlLock
  150. Arguments
  151. pvContext dwIfIndex
  152. Return Value
  153. None
  154. --*/
  155. {
  156. DWORD dwErr = NO_ERROR;
  157. DWORD dwIfIndex = 0;
  158. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  159. PPACKET pPacket;
  160. dwIfIndex = (DWORD) pvContext;
  161. TRACE1(ENTER, "Entering NM_CallbackPeriodicTimer: %u", dwIfIndex);
  162. if (!ENTER_SAMPLE_API()) { return; } // cannot return anything
  163. ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  164. do // breakout loop
  165. {
  166. // fail if the interface does not exist
  167. dwErr = IE_Get(dwIfIndex, &pieInterfaceEntry);
  168. if (dwErr != NO_ERROR)
  169. break;
  170. // fail if interface is inactive
  171. if (!INTERFACE_IS_ACTIVE(pieInterfaceEntry))
  172. break;
  173. RTASSERT(pieInterfaceEntry->sRawSocket != INVALID_SOCKET);
  174. // fail if packet cannot be created
  175. if (PacketCreate(&pPacket) != NO_ERROR)
  176. break;
  177. PacketDisplay(pPacket);
  178. dwErr = SocketSend(pieInterfaceEntry->sRawSocket,
  179. SAMPLE_PROTOCOL_MULTICAST_GROUP,
  180. pPacket);
  181. if (dwErr != NO_ERROR)
  182. {
  183. PacketDestroy(pPacket);
  184. break;
  185. }
  186. // update interface statistics
  187. InterlockedIncrement(&(pieInterfaceEntry->iisStats.ulNumPackets));
  188. } while (FALSE);
  189. // restart timer if the interface exists and is active
  190. if ((pieInterfaceEntry) and INTERFACE_IS_ACTIVE(pieInterfaceEntry))
  191. {
  192. RESTART_TIMER(pieInterfaceEntry->hPeriodicTimer,
  193. PERIODIC_INTERVAL,
  194. &dwErr);
  195. }
  196. RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  197. LEAVE_SAMPLE_API();
  198. TRACE0(LEAVE, "Leaving NM_CallbackPeriodicTimer");
  199. }
  200. ////////////////////////////////////////
  201. // APIFUNCTIONS
  202. ////////////////////////////////////////
  203. DWORD
  204. NM_AddInterface (
  205. IN LPWSTR pwszInterfaceName,
  206. IN DWORD dwInterfaceIndex,
  207. IN WORD wAccessType,
  208. IN PVOID pvInterfaceInfo)
  209. /*++
  210. Routine Description
  211. Add an interface with the given configuration to IPSAMPLE. Interface
  212. is created UNBOUND and DISABLED.
  213. Locks
  214. Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
  215. Releases (g_ce.pneNetworkEntry)->rwlLock
  216. Arguments
  217. pwszInterfaceName the name of the interface, used for logging.
  218. dwInterfaceIndex the positive integer used to refer to this interface.
  219. wAccessType access type... MULTIACCESS or POINTTOPOINT
  220. pvInterfaceInfo our config for this interface
  221. Return Value
  222. NO_ERROR if successfully initiailzed
  223. Failure code o/w
  224. --*/
  225. {
  226. DWORD dwErr = NO_ERROR;
  227. PIPSAMPLE_IF_CONFIG piic = NULL;
  228. PINTERFACE_ENTRY pieEntry = NULL;
  229. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  230. ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  231. do // breakout loop
  232. {
  233. piic = (PIPSAMPLE_IF_CONFIG) pvInterfaceInfo;
  234. // validate the configuration parameters
  235. if (!ValidateInterfaceConfig(piic))
  236. {
  237. dwErr = ERROR_INVALID_PARAMETER;
  238. break;
  239. }
  240. // fail if the interface exists
  241. if (IE_IsPresent(dwInterfaceIndex))
  242. {
  243. dwErr = ERROR_INVALID_PARAMETER;
  244. TRACE2(NETWORK, "Error interface %S (%u) already exists",
  245. pwszInterfaceName, dwInterfaceIndex);
  246. LOGERR0(INTERFACE_PRESENT, dwErr);
  247. break;
  248. }
  249. // create an interface entry
  250. dwErr = IE_Create(pwszInterfaceName,
  251. dwInterfaceIndex,
  252. wAccessType,
  253. &pieEntry);
  254. if (dwErr != NO_ERROR)
  255. break;
  256. // initialize interface configuration fields
  257. pieEntry->ulMetric = piic->ulMetric;
  258. // insert the interface in all access structures
  259. dwErr = IE_Insert(pieEntry);
  260. RTASSERT(dwErr is NO_ERROR); // no reason to fail!
  261. // update global statistics
  262. InterlockedIncrement(&(g_ce.igsStats.ulNumInterfaces));
  263. } while (FALSE);
  264. RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  265. LEAVE_SAMPLE_API();
  266. return dwErr;
  267. }
  268. DWORD
  269. NM_DeleteInterface (
  270. IN DWORD dwInterfaceIndex)
  271. /*++
  272. Routine Description
  273. Remove an interface with the given index, deactivating it if required.
  274. Locks
  275. Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
  276. Releases (g_ce.pneNetworkEntry)->rwlLock
  277. Arguments
  278. dwInterfaceIndex the positive integer used to identify the interface.
  279. Return Value
  280. NO_ERROR if successfully initiailzed
  281. Failure code o/w
  282. --*/
  283. {
  284. DWORD dwErr = NO_ERROR;
  285. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  286. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  287. do // breakout loop
  288. {
  289. // remove from all tables, lists...
  290. ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  291. dwErr = IE_Delete(dwInterfaceIndex, &pieInterfaceEntry);
  292. RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  293. // fail if the interface does not exist
  294. if (dwErr != NO_ERROR)
  295. {
  296. TRACE1(NETWORK, "Error interface %u does not exist",
  297. dwInterfaceIndex);
  298. break;
  299. }
  300. // destroy the interface entry, deregisters ReceiveWait
  301. // hence best not to hold any locks to prevent deadlocks.
  302. IE_Destroy(pieInterfaceEntry);
  303. // update global statistics
  304. InterlockedDecrement(&(g_ce.igsStats.ulNumInterfaces));
  305. } while (FALSE);
  306. LEAVE_SAMPLE_API();
  307. return dwErr;
  308. }
  309. DWORD
  310. NM_InterfaceStatus (
  311. IN DWORD dwInterfaceIndex,
  312. IN BOOL bInterfaceActive,
  313. IN DWORD dwStatusType,
  314. IN PVOID pvStatusInfo)
  315. /*++
  316. Routine Description
  317. Locks
  318. Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
  319. Releases (g_ce.pneNetworkEntry)->rwlLock
  320. Arguments
  321. dwInterfaceIndex The index of the interface in question
  322. bInterfaceActive Whether the interface can send and receive data
  323. dwStatusType RIS_INTERFACE_[ADDRESS_CHANGED|ENABLED|DISABLED]
  324. pvStatusInfo Pointer to IP_ADAPTER_BINDING_INFO containing info
  325. about the addresses on the interface
  326. Return Value
  327. NO_ERROR if successfully initiailzed
  328. Failure code o/w
  329. --*/
  330. {
  331. DWORD dwErr = NO_ERROR;
  332. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  333. PIP_ADAPTER_BINDING_INFO pBinding = NULL;
  334. BOOL bBindingChanged = FALSE;
  335. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  336. ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  337. do // breakout loop
  338. {
  339. // fail if the interface does not exist
  340. dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
  341. if (dwErr != NO_ERROR)
  342. break;
  343. // the only status we care about is a change in interface binding
  344. if (dwStatusType is RIS_INTERFACE_ADDRESS_CHANGE)
  345. {
  346. // destroy existing binding
  347. if (INTERFACE_IS_BOUND(pieInterfaceEntry))
  348. {
  349. bBindingChanged = TRUE;
  350. dwErr = IE_UnBindInterface(pieInterfaceEntry);
  351. RTASSERT(dwErr is NO_ERROR);
  352. }
  353. // create new binding
  354. pBinding = (PIP_ADAPTER_BINDING_INFO) pvStatusInfo;
  355. if(pBinding->AddressCount)
  356. {
  357. bBindingChanged = TRUE;
  358. dwErr = IE_BindInterface(pieInterfaceEntry, pBinding);
  359. if (dwErr != NO_ERROR)
  360. break;
  361. }
  362. }
  363. // interface needs to be deactivated even when the binding changes!
  364. // this restriction is due to the fact that the socket is bound to
  365. // the interface address and not the interface index...
  366. if (INTERFACE_IS_ACTIVE(pieInterfaceEntry) and
  367. (bBindingChanged or !bInterfaceActive))
  368. {
  369. dwErr = IE_DeactivateInterface(pieInterfaceEntry);
  370. if (dwErr != NO_ERROR)
  371. break;
  372. }
  373. // activate interface only when a binding exists!
  374. // i.e. we do not support unnumbered interfaces for now...
  375. if (INTERFACE_IS_INACTIVE(pieInterfaceEntry) and
  376. INTERFACE_IS_BOUND(pieInterfaceEntry) and
  377. bInterfaceActive)
  378. {
  379. dwErr = IE_ActivateInterface(pieInterfaceEntry);
  380. if (dwErr != NO_ERROR)
  381. break;
  382. }
  383. } while (FALSE);
  384. RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  385. LEAVE_SAMPLE_API();
  386. return dwErr;
  387. }
  388. DWORD
  389. NM_GetInterfaceInfo (
  390. IN DWORD dwInterfaceIndex,
  391. IN PVOID pvInterfaceInfo,
  392. IN OUT PULONG pulBufferSize,
  393. OUT PULONG pulStructureVersion,
  394. OUT PULONG pulStructureSize,
  395. OUT PULONG pulStructureCount)
  396. /*++
  397. Routine Description
  398. See if there's space enough to return ip sample interface config. If
  399. yes, we return it, otherwise return the size needed.
  400. Locks
  401. Acquires shared (g_ce.pneNetworkEntry)->rwlLock
  402. Releases (g_ce.pneNetworkEntry)->rwlLock
  403. Arguments
  404. dwInterfaceIndex the interface whose configuration is needed
  405. pvInterfaceInfo pointer to allocated buffer to store our config
  406. pulBufferSize IN size of buffer received
  407. OUT size of our interface config
  408. Return Value
  409. NO_ERROR if success
  410. Failure code o/w
  411. --*/
  412. {
  413. DWORD dwErr = NO_ERROR;
  414. PIPSAMPLE_IF_CONFIG piic;
  415. ULONG ulSize = sizeof(IPSAMPLE_IF_CONFIG);
  416. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  417. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  418. ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  419. do // breakout loop
  420. {
  421. // fail if the interface does not exist
  422. dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
  423. if (dwErr != NO_ERROR)
  424. break;
  425. // fail if the size was too small or there was no storage
  426. if((*pulBufferSize < ulSize) or (pvInterfaceInfo is NULL))
  427. {
  428. TRACE1(NETWORK, "NM_GetInterfaceInfo: *ulBufferSize %u",
  429. *pulBufferSize);
  430. *pulBufferSize = ulSize;
  431. dwErr = ERROR_INSUFFICIENT_BUFFER;
  432. break;
  433. }
  434. // set the OUT parameters
  435. *pulBufferSize = ulSize;
  436. if (pulStructureVersion) *pulStructureVersion = 1;
  437. if (pulStructureSize) *pulStructureSize = ulSize;
  438. if (pulStructureCount) *pulStructureCount = 1;
  439. // copy out the interface configuration
  440. piic = (PIPSAMPLE_IF_CONFIG) pvInterfaceInfo;
  441. piic->ulMetric = pieInterfaceEntry->ulMetric;
  442. } while (FALSE);
  443. RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  444. LEAVE_SAMPLE_API();
  445. return dwErr;
  446. }
  447. DWORD
  448. NM_SetInterfaceInfo (
  449. IN DWORD dwInterfaceIndex,
  450. IN PVOID pvInterfaceInfo)
  451. /*++
  452. Routine Description
  453. Set ip sample interface's configuration.
  454. Locks
  455. Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
  456. Releases (g_ce.pneNetworkEntry)->rwlLock
  457. Arguments
  458. dwInterfaceIndex the interface whose configuration is to be set
  459. pvInterfaceInfo buffer with new interface config
  460. Return Value
  461. NO_ERROR if success
  462. Failure code o/w
  463. --*/
  464. {
  465. DWORD dwErr = NO_ERROR;
  466. PIPSAMPLE_IF_CONFIG piic;
  467. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  468. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  469. ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  470. do // breakout loop
  471. {
  472. // fail if the interface does not exist
  473. dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
  474. if (dwErr != NO_ERROR)
  475. break;
  476. // fail if the configuration is invalid
  477. piic = (PIPSAMPLE_IF_CONFIG) pvInterfaceInfo;
  478. if(!ValidateInterfaceConfig(piic))
  479. {
  480. dwErr = ERROR_INVALID_PARAMETER;
  481. break;
  482. }
  483. // update our configuration
  484. pieInterfaceEntry->ulMetric = piic->ulMetric;
  485. // might need additional processing depending on the state change
  486. // caused by the updated interface configuration and the protocol
  487. // behavior. for instance, sockets may need to be created/shutdown
  488. } while (FALSE);
  489. RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  490. LEAVE_SAMPLE_API();
  491. return dwErr;
  492. }
  493. DWORD
  494. NM_DoUpdateRoutes (
  495. IN DWORD dwInterfaceIndex
  496. )
  497. /*++
  498. Routine Description
  499. Updates routes over a demand dial interface.
  500. Locks
  501. Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
  502. Releases (g_ce.pneNetworkEntry)->rwlLock
  503. Arguments
  504. dwInterfaceIndex the relevant interface index
  505. Return Value
  506. NO_ERROR if success
  507. Failure code o/w
  508. --*/
  509. {
  510. DWORD dwErr = NO_ERROR;
  511. PINTERFACE_ENTRY pieInterfaceEntry = NULL;
  512. MESSAGE mMessage;
  513. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  514. ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  515. do // breakout loop
  516. {
  517. // fail if the interface does not exist
  518. dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
  519. if (dwErr != NO_ERROR)
  520. break;
  521. // ensure interface is active
  522. if (INTERFACE_IS_INACTIVE(pieInterfaceEntry))
  523. {
  524. dwErr = ERROR_CAN_NOT_COMPLETE;
  525. TRACE1(NETWORK, "Error, interface %u inactive", dwInterfaceIndex);
  526. break;
  527. }
  528. // here we do protocol specific processing,
  529. // for sample nothing :)
  530. } while (FALSE);
  531. RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
  532. mMessage.UpdateCompleteMessage.InterfaceIndex = dwInterfaceIndex;
  533. mMessage.UpdateCompleteMessage.UpdateType = RF_DEMAND_UPDATE_ROUTES;
  534. mMessage.UpdateCompleteMessage.UpdateStatus = dwErr;
  535. if (EnqueueEvent(UPDATE_COMPLETE, mMessage) is NO_ERROR)
  536. SetEvent(g_ce.hMgrNotificationEvent);
  537. LEAVE_SAMPLE_API();
  538. return dwErr;
  539. }
  540. DWORD
  541. NM_ProcessRouteChange (
  542. VOID)
  543. /*++
  544. Routine Description
  545. Handle messages from RTM about route changes.
  546. Locks
  547. None
  548. Arguments
  549. None
  550. Return Value
  551. NO_ERROR success
  552. Error Code o/w
  553. --*/
  554. {
  555. DWORD dwErr = NO_ERROR;
  556. RTM_DEST_INFO rdiDestination; // 1 view registered for change
  557. BOOL bDone = FALSE;
  558. UINT uiNumDests;
  559. if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
  560. // loop dequeueing messages until RTM says there are no more left
  561. while (!bDone)
  562. {
  563. // retrieve route changes
  564. uiNumDests = 1;
  565. dwErr = RTM_GetChangedDests(
  566. g_ce.hRtmHandle, // my RTMv2 handle
  567. g_ce.hRtmNotificationHandle, // my notification handle
  568. &uiNumDests, // IN # dest info's required
  569. // OUT # dest info's supplied
  570. &rdiDestination); // OUT buffer for dest info's
  571. switch (dwErr)
  572. {
  573. case ERROR_NO_MORE_ITEMS:
  574. bDone = TRUE;
  575. dwErr = NO_ERROR;
  576. if (uiNumDests < 1)
  577. break;
  578. // else continue below to process the last destination
  579. case NO_ERROR:
  580. RTASSERT(uiNumDests is 1);
  581. RTM_DisplayDestInfo(&rdiDestination);
  582. // release the destination info
  583. if (RTM_ReleaseChangedDests(
  584. g_ce.hRtmHandle, // my RTMv2 handle
  585. g_ce.hRtmNotificationHandle,// my notif handle
  586. uiNumDests, // 1
  587. &rdiDestination // released dest info
  588. ) != NO_ERROR)
  589. TRACE0(NETWORK, "Error releasing changed dests");
  590. break;
  591. default:
  592. bDone = TRUE;
  593. TRACE1(NETWORK, "Error %u RtmGetChangedDests", dwErr);
  594. break;
  595. }
  596. } // while
  597. LEAVE_SAMPLE_API();
  598. return dwErr;
  599. }