Leaked source code of windows server 2003
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.

3033 lines
92 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. srvenum.c
  5. Abstract:
  6. This module contains the worker routine for the NetServerEnum API
  7. implemented by the Workstation service.
  8. Author:
  9. Rita Wong (ritaw) 25-Mar-1991
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. NET_API_STATUS NET_API_FUNCTION
  15. BrNetServerEnum(
  16. IN PNETWORK Network OPTIONAL,
  17. IN LPCWSTR ClientName OPTIONAL,
  18. IN ULONG Level,
  19. IN DWORD PreferedMaximumLength,
  20. OUT PVOID *Buffer,
  21. OUT LPDWORD EntriesRead,
  22. OUT LPDWORD TotalEntries,
  23. IN DWORD ServerType,
  24. IN LPCWSTR Domain,
  25. IN LPCWSTR FirstNameToReturn OPTIONAL
  26. );
  27. NET_API_STATUS
  28. BrRetrieveServerListForMaster(
  29. IN PNETWORK Network,
  30. IN OUT PVOID *Buffer,
  31. OUT PDWORD EntriesRead,
  32. OUT PDWORD TotalEntries,
  33. IN DWORD Level,
  34. IN DWORD ServerType,
  35. IN DWORD PreferedMaximumLength,
  36. IN BOOLEAN LocalListOnly,
  37. IN LPTSTR ClientName,
  38. IN LPTSTR DomainName,
  39. IN LPCWSTR FirstNameToReturn
  40. );
  41. NET_API_STATUS
  42. BrRetrieveServerListForBackup(
  43. IN PNETWORK Network,
  44. IN OUT PVOID *Buffer,
  45. OUT PDWORD EntriesRead,
  46. OUT PDWORD TotalEntries,
  47. IN DWORD Level,
  48. IN DWORD ServerType,
  49. IN DWORD PreferedMaximumLength,
  50. IN LPCWSTR FirstNameToReturn
  51. );
  52. //
  53. // This points to XsConvertServerEnumBuffer, which is dynamically loaded from
  54. // xactsrv.dll when required
  55. //
  56. XS_CONVERT_SERVER_ENUM_BUFFER_FUNCTION *pXsConvertServerEnumBuffer = NULL;
  57. NET_API_STATUS NET_API_FUNCTION
  58. I_BrowserrServerEnum(
  59. IN LPTSTR ServerName OPTIONAL,
  60. IN LPTSTR TransportName OPTIONAL,
  61. IN LPTSTR ClientName OPTIONAL,
  62. IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
  63. IN DWORD PreferedMaximumLength,
  64. OUT LPDWORD TotalEntries,
  65. IN DWORD ServerType,
  66. IN LPTSTR Domain,
  67. IN OUT LPDWORD ResumeHandle OPTIONAL
  68. )
  69. /*++
  70. Routine Description:
  71. This function is the NetServerEnum entry point in the Workstation service.
  72. Arguments:
  73. ServerName - Supplies the name of server to execute this function
  74. TransportName - Supplies the name of xport on which to enumerate servers
  75. InfoStruct - This structure supplies the level of information requested,
  76. returns a pointer to the buffer allocated by the Workstation service
  77. which contains a sequence of information structure of the specified
  78. information level, and returns the number of entries read. The buffer
  79. pointer is set to NULL if return code is not NERR_Success or
  80. ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
  81. value is only valid if the return code is NERR_Success or
  82. ERROR_MORE_DATA.
  83. PreferedMaximumLength - Supplies the number of bytes of information
  84. to return in the buffer. If this value is MAXULONG, we will try
  85. to return all available information if there is enough memory
  86. resource.
  87. TotalEntries - Returns the total number of entries available. This value
  88. is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
  89. ServerType - Supplies the type of server to enumerate.
  90. Domain - Supplies the name of one of the active domains to enumerate the
  91. servers from. If NULL, servers from the primary domain, logon domain
  92. and other domains are enumerated.
  93. ResumeHandle - Supplies and returns the point to continue with enumeration.
  94. Return Value:
  95. NET_API_STATUS - NERR_Success or reason for failure.
  96. --*/
  97. {
  98. NET_API_STATUS NetStatus;
  99. NetStatus = I_BrowserrServerEnumEx(
  100. ServerName,
  101. TransportName,
  102. ClientName,
  103. InfoStruct,
  104. PreferedMaximumLength,
  105. TotalEntries,
  106. ServerType,
  107. Domain,
  108. NULL ); // NULL FirstNameToReturn
  109. if (ARGUMENT_PRESENT(ResumeHandle)) {
  110. *ResumeHandle = 0;
  111. }
  112. return NetStatus;
  113. }
  114. NET_API_STATUS NET_API_FUNCTION
  115. I_BrowserrServerEnumEx(
  116. IN LPTSTR ServerName OPTIONAL,
  117. IN LPTSTR TransportName OPTIONAL,
  118. IN LPTSTR ClientName OPTIONAL,
  119. IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
  120. IN DWORD PreferedMaximumLength,
  121. OUT LPDWORD TotalEntries,
  122. IN DWORD ServerType,
  123. IN LPTSTR Domain,
  124. IN LPTSTR FirstNameToReturnArg
  125. )
  126. /*++
  127. Routine Description:
  128. This function is the NetServerEnum entry point in the Workstation service.
  129. Arguments:
  130. ServerName - Supplies the name of server to execute this function
  131. TransportName - Supplies the name of xport on which to enumerate servers
  132. InfoStruct - This structure supplies the level of information requested,
  133. returns a pointer to the buffer allocated by the Workstation service
  134. which contains a sequence of information structure of the specified
  135. information level, and returns the number of entries read. The buffer
  136. pointer is set to NULL if return code is not NERR_Success or
  137. ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
  138. value is only valid if the return code is NERR_Success or
  139. ERROR_MORE_DATA.
  140. PreferedMaximumLength - Supplies the number of bytes of information
  141. to return in the buffer. If this value is MAXULONG, we will try
  142. to return all available information if there is enough memory
  143. resource.
  144. TotalEntries - Returns the total number of entries available. This value
  145. is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
  146. ServerType - Supplies the type of server to enumerate.
  147. Domain - Supplies the name of one of the active domains to enumerate the
  148. servers from. If NULL, servers from the primary domain, logon domain
  149. and other domains are enumerated.
  150. FirstNameToReturnArg - Supplies the name of the first domain or server entry to return.
  151. The caller can use this parameter to implement a resume handle of sorts by passing
  152. the name of the last entry returned on a previous call. (Notice that the specified
  153. entry will, also, be returned on this call unless it has since been deleted.)
  154. Pass NULL to start with the first entry available.
  155. Return Value:
  156. NET_API_STATUS - NERR_Success or reason for failure.
  157. --*/
  158. {
  159. NET_API_STATUS status;
  160. PVOID Buffer = NULL;
  161. ULONG EntriesRead;
  162. BOOLEAN NetworkLocked = FALSE;
  163. PNETWORK Network = NULL;
  164. UNICODE_STRING NetworkName;
  165. WCHAR FirstNameToReturn[DNLEN+1];
  166. PDOMAIN_INFO DomainInfo = NULL;
  167. #if DBG
  168. DWORD StartTickCount, EndTickCount;
  169. #endif
  170. UNREFERENCED_PARAMETER(ServerName);
  171. #if DBG
  172. StartTickCount = GetTickCount();
  173. #endif
  174. BrPrint( (BR_CLIENT_OP,
  175. "I_BrowserrServerEnumEx: called by client <%ws>, for domain <%ws>, type <%lx>, firstNameToReturn <%ws>, on Transport <%ws>\n",
  176. ClientName, Domain, ServerType, FirstNameToReturnArg, TransportName ));
  177. if (!ARGUMENT_PRESENT(TransportName)) {
  178. status = ERROR_INVALID_PARAMETER;
  179. goto Cleanup;
  180. }
  181. if (!ARGUMENT_PRESENT(InfoStruct)) {
  182. status = ERROR_INVALID_PARAMETER;
  183. goto Cleanup;
  184. }
  185. if ( InfoStruct->Level == 101 &&
  186. !InfoStruct->ServerInfo.Level101 ) {
  187. status = ERROR_INVALID_PARAMETER;
  188. goto Cleanup;
  189. } else if ( InfoStruct->Level == 100 &&
  190. !InfoStruct->ServerInfo.Level100 ) {
  191. status = ERROR_INVALID_PARAMETER;
  192. goto Cleanup;
  193. }
  194. #ifdef ENABLE_PSEUDO_BROWSER
  195. //
  196. // Pseudo Server returns nothing
  197. //
  198. if (BrInfo.PseudoServerLevel == BROWSER_PSEUDO) {
  199. if (InfoStruct->Level == 101) {
  200. InfoStruct->ServerInfo.Level101->Buffer = NULL;
  201. InfoStruct->ServerInfo.Level101->EntriesRead = 0;
  202. } else {
  203. InfoStruct->ServerInfo.Level100->Buffer = NULL;
  204. InfoStruct->ServerInfo.Level100->EntriesRead = 0;
  205. }
  206. status = ERROR_SUCCESS;
  207. goto Cleanup;
  208. }
  209. #endif
  210. //
  211. // Find the requested domain.
  212. //
  213. DomainInfo = BrFindDomain( Domain, TRUE );
  214. if ( DomainInfo == NULL) {
  215. status = ERROR_NO_SUCH_DOMAIN;
  216. goto Cleanup;
  217. }
  218. //
  219. // Find the requested network
  220. //
  221. RtlInitUnicodeString(&NetworkName, TransportName);
  222. Network = BrFindNetwork( DomainInfo, &NetworkName);
  223. if (Network == NULL) {
  224. BrPrint(( BR_CRITICAL,
  225. "%ws: %ws: BrowserrServerEnum: Network not found.\n",
  226. Domain,
  227. TransportName));
  228. status = ERROR_FILE_NOT_FOUND;
  229. goto Cleanup;
  230. }
  231. //
  232. // If the caller has asked us to use the alternate transport,
  233. // do so.
  234. //
  235. if ((ServerType != SV_TYPE_ALL) &&
  236. (ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
  237. //
  238. // If this transport has an alternate network, then actually
  239. // query the alternate name, not the real one.
  240. //
  241. if (Network->AlternateNetwork != NULL) {
  242. PNETWORK TempNetwork = Network;
  243. Network = Network->AlternateNetwork;
  244. if ( !BrReferenceNetwork( Network )) {
  245. Network = TempNetwork;
  246. status = ERROR_INVALID_PARAMETER;
  247. goto Cleanup;
  248. }
  249. BrDereferenceNetwork( TempNetwork );
  250. BrPrint((BR_CLIENT_OP,
  251. "I_BrowserrServerEnumEx: for client <%ws>, querying alternate network <%ws> instead of <%ws>\n",
  252. ClientName, Network->NetworkName.Buffer, Network->AlternateNetwork->NetworkName.Buffer ));
  253. } else {
  254. status = ERROR_INVALID_PARAMETER;
  255. goto Cleanup;
  256. }
  257. ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
  258. if (ServerType == 0) {
  259. ServerType = SV_TYPE_ALL;
  260. }
  261. }
  262. if (!LOCK_NETWORK_SHARED(Network)) {
  263. status = NERR_InternalError;
  264. goto Cleanup;
  265. }
  266. NetworkLocked = TRUE;
  267. if (!(Network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
  268. //
  269. // If this is a wannish transport,
  270. // and the caller is asking for "local list",
  271. // try to find another wannish transport that is a master browser.
  272. //
  273. // The domain master browser doesn't have any control over which
  274. // transport he comes in on. If he picks a disabled transport,
  275. // this code will find the enabled transport.
  276. //
  277. // There are cases where there is more than one wannish master browser
  278. // transport on this machine. In that case, BrNetServerEnum merges the
  279. // local lists for all wannish transports to ensure that all list are returned
  280. // to the domain master browser
  281. //
  282. if ( (Network->Flags & NETWORK_WANNISH) != 0 &&
  283. (ServerType == SV_TYPE_LOCAL_LIST_ONLY ||
  284. ServerType == (SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM) ) ) {
  285. PNETWORK TempNetwork;
  286. TempNetwork = BrFindWannishMasterBrowserNetwork( DomainInfo );
  287. if ( TempNetwork == NULL ) {
  288. BrPrint(( BR_CRITICAL,
  289. "%ws: %ws: Browse request received from %ws, but not backup or master\n",
  290. Network->DomainInfo->DomUnicodeDomainName,
  291. Network->NetworkName.Buffer,
  292. ClientName));
  293. status = ERROR_REQ_NOT_ACCEP;
  294. goto Cleanup;
  295. }
  296. //
  297. // Ditch the old network.
  298. //
  299. UNLOCK_NETWORK(Network);
  300. NetworkLocked = FALSE;
  301. BrDereferenceNetwork( Network );
  302. Network = TempNetwork;
  303. //
  304. // Use the new network
  305. //
  306. if (!LOCK_NETWORK_SHARED(Network)) {
  307. status = NERR_InternalError;
  308. goto Cleanup;
  309. }
  310. NetworkLocked = TRUE;
  311. BrPrint(( BR_SERVER_ENUM,
  312. "%ws: %ws: Is wannish IP Network found for %ws\n",
  313. Network->DomainInfo->DomUnicodeDomainName,
  314. Network->NetworkName.Buffer,
  315. ClientName));
  316. } else {
  317. BrPrint(( BR_CRITICAL,
  318. "%ws: %ws: Browse request received from %ws, but not backup or master\n",
  319. Network->DomainInfo->DomUnicodeDomainName,
  320. Network->NetworkName.Buffer,
  321. ClientName));
  322. status = ERROR_REQ_NOT_ACCEP;
  323. goto Cleanup;
  324. }
  325. }
  326. //
  327. // Canonicalize the FirstNameToReturn.
  328. //
  329. if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
  330. if ( I_NetNameCanonicalize(
  331. NULL,
  332. (LPWSTR) FirstNameToReturnArg,
  333. FirstNameToReturn,
  334. sizeof(FirstNameToReturn),
  335. NAMETYPE_WORKGROUP,
  336. LM2X_COMPATIBLE
  337. ) != NERR_Success) {
  338. status = ERROR_INVALID_PARAMETER;
  339. goto Cleanup;
  340. }
  341. } else {
  342. FirstNameToReturn[0] = L'\0';
  343. }
  344. status = BrNetServerEnum(Network,
  345. ClientName,
  346. InfoStruct->Level,
  347. PreferedMaximumLength,
  348. &Buffer,
  349. &EntriesRead,
  350. TotalEntries,
  351. ServerType,
  352. Domain,
  353. FirstNameToReturn );
  354. //
  355. // Return output parameters other than output buffer from request packet.
  356. //
  357. if (status == NERR_Success || status == ERROR_MORE_DATA) {
  358. if (InfoStruct->Level == 101) {
  359. InfoStruct->ServerInfo.Level101->Buffer = (PSERVER_INFO_101) Buffer;
  360. InfoStruct->ServerInfo.Level101->EntriesRead = EntriesRead;
  361. } else {
  362. InfoStruct->ServerInfo.Level100->Buffer = (PSERVER_INFO_100) Buffer;
  363. InfoStruct->ServerInfo.Level100->EntriesRead = EntriesRead;
  364. }
  365. }
  366. Cleanup:
  367. if (NetworkLocked) {
  368. UNLOCK_NETWORK(Network);
  369. }
  370. if ( Network != NULL ) {
  371. #if DBG
  372. EndTickCount = GetTickCount();
  373. BrPrint(( BR_SERVER_ENUM,
  374. "%ws: %ws: Browse request for %ws took %ld milliseconds\n",
  375. Network->DomainInfo->DomUnicodeDomainName,
  376. Network->NetworkName.Buffer,
  377. ClientName,
  378. EndTickCount - StartTickCount));
  379. #endif
  380. BrDereferenceNetwork( Network );
  381. }
  382. if ( DomainInfo != NULL ) {
  383. BrDereferenceDomain( DomainInfo );
  384. }
  385. return status;
  386. }
  387. WORD
  388. I_BrowserServerEnumForXactsrv(
  389. IN LPCWSTR TransportName OPTIONAL,
  390. IN LPCWSTR ClientName OPTIONAL,
  391. IN ULONG Level,
  392. IN USHORT ClientLevel,
  393. IN PVOID ClientBuffer,
  394. IN WORD BufferLength,
  395. IN DWORD PreferedMaximumLength,
  396. OUT LPDWORD EntriesFilled,
  397. OUT LPDWORD TotalEntries,
  398. IN DWORD ServerType,
  399. IN LPCWSTR Domain,
  400. IN LPCWSTR FirstNameToReturnArg OPTIONAL,
  401. OUT PWORD Converter
  402. )
  403. /*++
  404. Routine Description:
  405. This function is a private entrypoint for Xactsrv that bypasses RPC
  406. entirely.
  407. Arguments:
  408. TransportName - Supplies the name of xport on which to enumerate servers
  409. ClientName - Supplies the name of the client that requested the data
  410. Level - Level of data requested.
  411. ClientLevel - Level requested by the client.
  412. ClientBuffer - Output buffer allocated to hold the buffer.
  413. BufferLength - Size of ClientBuffer
  414. PreferedMaximumLength - Prefered maximum size of Client buffer if we are
  415. going to use the NT form of the buffer
  416. OUT LPDWORD EntriesFilled - The entries packed into ClientBuffer
  417. OUT LPDWORD TotalEntries - The total # of entries available.
  418. IN DWORD ServerType - Server type mask.
  419. IN LPTSTR Domain - Domain to query
  420. OUT PWORD Converter - Magic constant from Xactsrv that allows the client
  421. to convert the response buffer.
  422. Return Value:
  423. WORD - NERR_Success or reason for failure.
  424. --*/
  425. {
  426. NET_API_STATUS status;
  427. BOOLEAN networkLocked = FALSE;
  428. PNETWORK network = NULL;
  429. UNICODE_STRING networkName;
  430. PDOMAIN_INFO DomainInfo = NULL;
  431. PVOID buffer = NULL;
  432. DWORD entriesRead;
  433. PCACHED_BROWSE_RESPONSE response = NULL;
  434. WCHAR FirstNameToReturn[DNLEN+1];
  435. #if DBG
  436. DWORD startTickCount, endTickCount;
  437. startTickCount = GetTickCount();
  438. #endif
  439. try {
  440. BrPrint( (BR_CLIENT_OP,
  441. "I_BrowserServerEnunForXactsrv: called by client <%ws>, domain <%ws>, type <%lx>, firstNameToReturn <%ws>, on Transport <%ws>\n",
  442. ClientName, Domain, ServerType, FirstNameToReturnArg, TransportName ));
  443. //
  444. // If the browser isn't up and we get one of these calls, fail the API
  445. // immediately.
  446. //
  447. if (BrGlobalData.Status.dwCurrentState != SERVICE_RUNNING) {
  448. BrPrint(( BR_CRITICAL,
  449. "Browse request from %ws received, but browser not running\n", ClientName));
  450. try_return(status = NERR_ServiceNotInstalled);
  451. }
  452. if (!ARGUMENT_PRESENT(TransportName)) {
  453. try_return(status = ERROR_INVALID_PARAMETER);
  454. }
  455. //
  456. // Find the requested domain.
  457. //
  458. DomainInfo = BrFindDomain( (LPWSTR) Domain, TRUE );
  459. if ( DomainInfo == NULL) {
  460. try_return(status = ERROR_NO_SUCH_DOMAIN);
  461. }
  462. //
  463. // Look up the transport.
  464. //
  465. RtlInitUnicodeString(&networkName, TransportName);
  466. BrPrint(( BR_SERVER_ENUM,
  467. "%ws: %ws: NetServerEnum: Look up network for %ws\n",
  468. Domain,
  469. TransportName,
  470. ClientName));
  471. network = BrFindNetwork( DomainInfo, &networkName);
  472. //
  473. // If it returns NULL, the network was not found.
  474. // In that case just pick a network randomly and enumerate for that network
  475. // This is a workaround for RAID bug 614688.
  476. // The situation is that the request comes over NetbiosSMB and so
  477. // networkName points to that, and browser is not registered on that transport.
  478. //
  479. if ( network == NULL) {
  480. EnterCriticalSection(&NetworkCritSect);
  481. if ( ServicedNetworks.Flink != &ServicedNetworks ) {
  482. // If the list is not empty, just pick the first network
  483. network = CONTAINING_RECORD(ServicedNetworks.Flink, NETWORK, NextNet);
  484. network->ReferenceCount ++;
  485. }
  486. LeaveCriticalSection(&NetworkCritSect);
  487. }
  488. if (network == NULL) {
  489. BrPrint(( BR_CRITICAL,
  490. "%ws: %ws: Network not found.\n",
  491. Domain,
  492. TransportName));
  493. try_return(status = ERROR_FILE_NOT_FOUND);
  494. }
  495. //
  496. // If the caller has asked us to use the alternate transport,
  497. // do so.
  498. //
  499. if ((ServerType != SV_TYPE_ALL) &&
  500. (ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
  501. //
  502. // If this transport has an alternate network, then actually
  503. // query the alternate name, not the real one.
  504. //
  505. if (network->AlternateNetwork != NULL) {
  506. PNETWORK TempNetwork = network;
  507. network = network->AlternateNetwork;
  508. if ( !BrReferenceNetwork( network )) {
  509. network = TempNetwork;
  510. try_return(status = ERROR_INVALID_PARAMETER);
  511. }
  512. BrDereferenceNetwork( TempNetwork );
  513. } else {
  514. try_return(status = ERROR_INVALID_PARAMETER);
  515. }
  516. ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
  517. if (ServerType == 0) {
  518. ServerType = SV_TYPE_ALL;
  519. }
  520. }
  521. if (!LOCK_NETWORK_SHARED(network)) {
  522. try_return(status = NERR_InternalError);
  523. }
  524. networkLocked = TRUE;
  525. BrPrint(( BR_SERVER_ENUM,
  526. "%ws: %ws: Network found for %ws\n",
  527. network->DomainInfo->DomUnicodeDomainName,
  528. network->NetworkName.Buffer,
  529. ClientName));
  530. if (!(network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
  531. //
  532. // If this is a wannish transport,
  533. // and the caller is asking for "local list",
  534. // try to find another wannish transport that is a master browser.
  535. //
  536. // The domain master browser doesn't have any control over which
  537. // transport he comes in on. If he picks a disabled transport,
  538. // this code will find the enabled transport.
  539. //
  540. // There are cases where there is more than one wannish master browser
  541. // transport on this machine. In that case, BrNetServerEnum merges the
  542. // local lists for all wannish transports to ensure that all list are returned
  543. // to the domain master browser
  544. //
  545. if ( (network->Flags & NETWORK_WANNISH) != 0 &&
  546. (ServerType == SV_TYPE_LOCAL_LIST_ONLY ||
  547. ServerType == (SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM) ) ) {
  548. PNETWORK TempNetwork;
  549. TempNetwork = BrFindWannishMasterBrowserNetwork( DomainInfo );
  550. if ( TempNetwork == NULL ) {
  551. BrPrint(( BR_CRITICAL,
  552. "%ws: %ws: Browse request received from %ws, but not backup or master\n",
  553. network->DomainInfo->DomUnicodeDomainName,
  554. network->NetworkName.Buffer,
  555. ClientName));
  556. try_return(status = ERROR_REQ_NOT_ACCEP);
  557. }
  558. //
  559. // Ditch the old network.
  560. //
  561. UNLOCK_NETWORK(network);
  562. networkLocked = FALSE;
  563. BrDereferenceNetwork( network );
  564. network = TempNetwork;
  565. //
  566. // Use the new network
  567. //
  568. if (!LOCK_NETWORK_SHARED(network)) {
  569. try_return(status = NERR_InternalError);
  570. }
  571. networkLocked = TRUE;
  572. BrPrint(( BR_SERVER_ENUM,
  573. "%ws: %ws: Is wannish IP Network found for %ws\n",
  574. network->DomainInfo->DomUnicodeDomainName,
  575. network->NetworkName.Buffer,
  576. ClientName));
  577. } else {
  578. BrPrint(( BR_CRITICAL,
  579. "%ws: %ws: Browse request received from %ws, but not backup or master\n",
  580. network->DomainInfo->DomUnicodeDomainName,
  581. network->NetworkName.Buffer,
  582. ClientName));
  583. try_return(status = ERROR_REQ_NOT_ACCEP);
  584. }
  585. }
  586. //
  587. // If we weren't able to find a cached response buffer, or
  588. // if the cached response didn't have a buffer allocated for it,
  589. // we need to process the browse request.
  590. //
  591. if (network->Role & ROLE_MASTER) {
  592. //
  593. // Check to see if we should flush the cache at this time. If
  594. // we should, we need to release the network and re-acquire it
  595. // exclusively, because we use the network lock to protect
  596. // enumerations from flushes.
  597. //
  598. if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
  599. UNLOCK_NETWORK(network);
  600. networkLocked = FALSE;
  601. if (!LOCK_NETWORK(network)) {
  602. try_return(status = NERR_InternalError);
  603. }
  604. networkLocked = TRUE;
  605. //
  606. // We're running on a master browser, and we found a cached browse
  607. // request. See if we can safely use this cached value, or if we
  608. // should go to the driver for it.
  609. //
  610. EnterCriticalSection(&network->ResponseCacheLock);
  611. if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
  612. BrPrint(( BR_SERVER_ENUM,
  613. "%ws: %ws: Flushing cache\n",
  614. network->DomainInfo->DomUnicodeDomainName,
  615. network->NetworkName.Buffer ));
  616. network->TimeCacheFlushed = BrCurrentSystemTime();
  617. BrAgeResponseCache( network );
  618. }
  619. LeaveCriticalSection(&network->ResponseCacheLock);
  620. }
  621. }
  622. //
  623. // Canonicalize the FirstNameToReturn.
  624. //
  625. if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
  626. if ( I_NetNameCanonicalize(
  627. NULL,
  628. (LPWSTR) FirstNameToReturnArg,
  629. FirstNameToReturn,
  630. sizeof(FirstNameToReturn),
  631. NAMETYPE_WORKGROUP,
  632. LM2X_COMPATIBLE
  633. ) != NERR_Success) {
  634. try_return(status = ERROR_INVALID_PARAMETER);
  635. }
  636. } else {
  637. FirstNameToReturn[0] = L'\0';
  638. }
  639. if (!ARGUMENT_PRESENT(Domain) ||
  640. !STRICMP(Domain, network->DomainInfo->DomUnicodeDomainName)) {
  641. BrPrint(( BR_SERVER_ENUM,
  642. "%ws: %ws: Look up 0x%x/%d/%x.\n",
  643. Domain, TransportName, ServerType, ClientLevel, BufferLength));
  644. //
  645. // This request is for our primary domain. Look up a cached response
  646. // entry. If none is found for this request, allocate a new one and
  647. // return it.
  648. //
  649. BrPrint( (BR_CLIENT_OP,
  650. "I_BrowserServerEnunForXactsrv: try to look up a cached response for our primary domain <%ws> on network <%ws>\n",
  651. Domain, network->NetworkName.Buffer ));
  652. response = BrLookupAndAllocateCachedEntry(
  653. network,
  654. ServerType,
  655. BufferLength,
  656. ClientLevel,
  657. FirstNameToReturn
  658. );
  659. }
  660. EnterCriticalSection(&network->ResponseCacheLock);
  661. if ((response == NULL)
  662. ||
  663. (response->Buffer == NULL)) {
  664. LeaveCriticalSection(&network->ResponseCacheLock);
  665. BrPrint(( BR_SERVER_ENUM,
  666. "%ws: %ws: Cached entry not found, or hit count too low. Retrieve actual list for %ws\n",
  667. Domain, TransportName, ClientName));
  668. BrPrint( (BR_CLIENT_OP,
  669. "I_BrowserServerEnunForXactsrv: retrieving actual list for client <%ws>, domain <%ws> on network <%ws>\n",
  670. ClientName, Domain, network->NetworkName.Buffer ));
  671. status = BrNetServerEnum(network,
  672. ClientName,
  673. Level,
  674. PreferedMaximumLength,
  675. &buffer,
  676. &entriesRead,
  677. TotalEntries,
  678. ServerType,
  679. Domain,
  680. FirstNameToReturn
  681. );
  682. if (status == NERR_Success || status == ERROR_MORE_DATA) {
  683. BrPrint(( BR_SERVER_ENUM,
  684. "%ws: %ws: Convert NT buffer to Xactsrv buffer for %ws\n",
  685. Domain,
  686. TransportName,
  687. ClientName ));
  688. if( pXsConvertServerEnumBuffer == NULL ) {
  689. //
  690. // It doesn't really matter if several threads do this simultaneously.
  691. // We are never going to unload this library anyway. But we are delaying
  692. // the load of the library until now to speed up system boot and init.
  693. //
  694. HMODULE hLibrary = LoadLibrary( L"xactsrv.dll" );
  695. if( hLibrary != NULL ) {
  696. pXsConvertServerEnumBuffer = (XS_CONVERT_SERVER_ENUM_BUFFER_FUNCTION *)GetProcAddress(
  697. hLibrary, "XsConvertServerEnumBuffer" );
  698. }
  699. }
  700. if( pXsConvertServerEnumBuffer != NULL ) {
  701. status = pXsConvertServerEnumBuffer(
  702. buffer,
  703. entriesRead,
  704. TotalEntries,
  705. ClientLevel,
  706. ClientBuffer,
  707. BufferLength,
  708. EntriesFilled,
  709. Converter);
  710. } else {
  711. status = GetLastError();
  712. }
  713. if (status == NERR_Success || status == ERROR_MORE_DATA) {
  714. BrPrint(( BR_SERVER_ENUM,
  715. "%ws: %ws: Conversion done for %ws\n",
  716. Domain,
  717. TransportName,
  718. ClientName ));
  719. EnterCriticalSection(&network->ResponseCacheLock);
  720. if ((response != NULL) &&
  721. (response->Buffer == NULL) &&
  722. (response->TotalHitCount >= BrInfo.CacheHitLimit)) {
  723. BrPrint(( BR_SERVER_ENUM,
  724. "%ws: %ws: Save contents of server list for 0x%x/%d/%x.\n",
  725. Domain,
  726. TransportName,
  727. ServerType, ClientLevel, BufferLength));
  728. response->Buffer = MIDL_user_allocate(BufferLength);
  729. if ( response->Buffer != NULL ) {
  730. //
  731. // We successfully allocated the buffer, now copy
  732. // our response into the buffer and save away the
  733. // other useful stuff about the request.
  734. //
  735. RtlCopyMemory(response->Buffer, ClientBuffer, BufferLength);
  736. response->EntriesRead = *EntriesFilled;
  737. response->TotalEntries = *TotalEntries;
  738. response->Converter = *Converter;
  739. response->Status = (WORD)status;
  740. }
  741. }
  742. LeaveCriticalSection(&network->ResponseCacheLock);
  743. }
  744. }
  745. } else {
  746. ASSERT (response);
  747. ASSERT (response->Buffer);
  748. ASSERT (response->Size == BufferLength);
  749. BrPrint(( BR_SERVER_ENUM,
  750. "Cache hit. Use contents of server list for 0x%x/%d/%x.\n",
  751. Domain,
  752. TransportName,
  753. ServerType, ClientLevel, BufferLength));
  754. //
  755. // Copy over the cached response from the response cache into the
  756. // users response buffer.
  757. //
  758. RtlCopyMemory(ClientBuffer, response->Buffer, BufferLength);
  759. //
  760. // Also copy over the other useful stuff that the client will
  761. // want to know about.
  762. //
  763. *EntriesFilled = response->EntriesRead;
  764. *TotalEntries = response->TotalEntries;
  765. *Converter = response->Converter;
  766. status = response->Status;
  767. LeaveCriticalSection(&network->ResponseCacheLock);
  768. }
  769. try_exit:NOTHING;
  770. } finally {
  771. if (networkLocked) {
  772. UNLOCK_NETWORK(network);
  773. }
  774. //
  775. // If a buffer was allocated, free it.
  776. //
  777. if (buffer != NULL) {
  778. MIDL_user_free(buffer);
  779. }
  780. if ( network != NULL ) {
  781. #if DBG
  782. endTickCount = GetTickCount();
  783. BrPrint(( BR_SERVER_ENUM,
  784. "%ws: %ws: Browse request for %ws took %ld milliseconds\n",
  785. network->DomainInfo->DomUnicodeDomainName,
  786. network->NetworkName.Buffer,
  787. ClientName,
  788. endTickCount - startTickCount));
  789. #endif
  790. BrDereferenceNetwork( network );
  791. }
  792. if ( DomainInfo != NULL ) {
  793. BrDereferenceDomain( DomainInfo );
  794. }
  795. }
  796. return (WORD)status;
  797. }
  798. NET_API_STATUS NET_API_FUNCTION
  799. BrNetServerEnum(
  800. IN PNETWORK Network OPTIONAL,
  801. IN LPCWSTR ClientName OPTIONAL,
  802. IN ULONG Level,
  803. IN DWORD PreferedMaximumLength,
  804. OUT PVOID *Buffer,
  805. OUT LPDWORD EntriesRead,
  806. OUT LPDWORD TotalEntries,
  807. IN DWORD ServerType,
  808. IN LPCWSTR Domain,
  809. IN LPCWSTR FirstNameToReturn OPTIONAL
  810. )
  811. /*++
  812. Routine Description:
  813. This function is the real worker for the NetServerEnum entry point in the
  814. Workstation service.
  815. Arguments:
  816. ServerName - Supplies the name of server to execute this function
  817. TransportName - Supplies the name of xport on which to enumerate servers
  818. InfoStruct - This structure supplies the level of information requested,
  819. returns a pointer to the buffer allocated by the Workstation service
  820. which contains a sequence of information structure of the specified
  821. information level, and returns the number of entries read. The buffer
  822. pointer is set to NULL if return code is not NERR_Success or
  823. ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
  824. value is only valid if the return code is NERR_Success or
  825. ERROR_MORE_DATA.
  826. PreferedMaximumLength - Supplies the number of bytes of information
  827. to return in the buffer. If this value is MAXULONG, we will try
  828. to return all available information if there is enough memory
  829. resource.
  830. TotalEntries - Returns the total number of entries available. This value
  831. is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
  832. ServerType - Supplies the type of server to enumerate.
  833. Domain - Supplies the name of one of the active domains to enumerate the
  834. servers from. If NULL, servers from the primary domain, logon domain
  835. and other domains are enumerated.
  836. FirstNameToReturn - Supplies the name of the first server or domain to return
  837. to the caller. If NULL, enumeration begins with the very first entry.
  838. Passed name must be the canonical form of the name.
  839. Return Value:
  840. NET_API_STATUS - NERR_Success or reason for failure.
  841. --*/
  842. {
  843. NET_API_STATUS status;
  844. DWORD DomainNameSize = 0;
  845. TCHAR DomainName[DNLEN + 1];
  846. BOOLEAN NetworkLocked = TRUE;
  847. BOOLEAN LocalListOnly = FALSE;
  848. PVOID DoubleHopLocalList = NULL;
  849. BrPrint(( BR_SERVER_ENUM,
  850. "%ws: %ws: Retrieve browse list for %lx for client %ws\n",
  851. Domain,
  852. Network->NetworkName.Buffer,
  853. ServerType,
  854. ClientName));
  855. EnterCriticalSection(&BrowserStatisticsLock);
  856. if (ServerType == SV_TYPE_DOMAIN_ENUM) {
  857. NumberOfDomainEnumerations += 1;
  858. } else if (ServerType == SV_TYPE_ALL) {
  859. NumberOfServerEnumerations += 1;
  860. } else {
  861. NumberOfOtherEnumerations += 1;
  862. }
  863. LeaveCriticalSection(&BrowserStatisticsLock);
  864. //
  865. // Only levels 100 and 101 are valid
  866. //
  867. if ((Level != 100) && (Level != 101)) {
  868. return ERROR_INVALID_LEVEL;
  869. }
  870. #ifdef ENABLE_PSEUDO_BROWSER
  871. //
  872. // Pseudo Server shortcut
  873. //
  874. if ( BrInfo.PseudoServerLevel == BROWSER_PSEUDO ) {
  875. *Buffer = NULL;
  876. *EntriesRead = 0;
  877. *TotalEntries = 0;
  878. return NERR_Success;
  879. }
  880. #endif
  881. if (ARGUMENT_PRESENT(Domain)) {
  882. //
  883. // NAMETYPE_WORKGROUP allows spaces.
  884. //
  885. if ( I_NetNameCanonicalize(
  886. NULL,
  887. (LPWSTR) Domain,
  888. DomainName,
  889. (DNLEN + 1) * sizeof(TCHAR),
  890. NAMETYPE_WORKGROUP,
  891. 0
  892. ) != NERR_Success) {
  893. return ERROR_INVALID_DOMAINNAME;
  894. }
  895. DomainNameSize = STRLEN(DomainName) * sizeof(WCHAR);
  896. }
  897. try {
  898. if (ServerType != SV_TYPE_ALL) {
  899. //
  900. // If the user has specified SV_TYPE_LOCAL_LIST_ONLY, we
  901. // will only accept SV_TYPE_DOMAIN_ENUM or 0 as legal bits
  902. // otherwise, we return an error.
  903. //
  904. if (ServerType & SV_TYPE_LOCAL_LIST_ONLY) {
  905. LocalListOnly = TRUE;
  906. // BrPrint(( BR_SERVER_ENUM, "Retrieve local list for %ws\n", ClientName));
  907. //
  908. // Turn off the LOCAL_LIST_ONLY bit.
  909. //
  910. ServerType &= ~SV_TYPE_LOCAL_LIST_ONLY;
  911. //
  912. // Check the remaining bits. The only two legal values
  913. // are SV_TYPE_DOMAIN_ENUM and 0
  914. //
  915. if (ServerType != SV_TYPE_DOMAIN_ENUM) {
  916. if (ServerType == 0) {
  917. ServerType = SV_TYPE_ALL;
  918. } else {
  919. try_return(status = ERROR_INVALID_FUNCTION);
  920. }
  921. }
  922. //
  923. // If we aren't a master browser, blow this request
  924. // off, since we don't have a local list.
  925. //
  926. if (!(Network->Role & ROLE_MASTER)) {
  927. BrPrint(( BR_CRITICAL,
  928. "%ws: %ws: Local list request received from %ws, but not master\n",
  929. Domain,
  930. Network->NetworkName.Buffer,
  931. ClientName));
  932. try_return(status = ERROR_REQ_NOT_ACCEP);
  933. }
  934. } else if (ServerType & SV_TYPE_DOMAIN_ENUM) {
  935. if (ServerType != SV_TYPE_DOMAIN_ENUM) {
  936. try_return(status = ERROR_INVALID_FUNCTION);
  937. }
  938. }
  939. }
  940. if (ARGUMENT_PRESENT(Domain) &&
  941. STRICMP(Domain, Network->DomainInfo->DomUnicodeDomainName)) {
  942. PINTERIM_ELEMENT DomainEntry;
  943. LPWSTR MasterName = NULL;
  944. BrPrint(( BR_SERVER_ENUM,
  945. "Non local domain %ws - Check for double hop\n",
  946. Domain));
  947. if ( Network->Role & ROLE_MASTER ) {
  948. if ( Network->DomainList.EntriesRead != 0 ) {
  949. DomainEntry = LookupInterimServerList(&Network->DomainList, (LPWSTR) Domain);
  950. if (DomainEntry != NULL) {
  951. MasterName = DomainEntry->Comment;
  952. }
  953. } else {
  954. ULONG i;
  955. PSERVER_INFO_101 DomainInfo;
  956. DWORD DoubleHopEntriesRead = 0;
  957. DWORD DoubleHopTotalEntries = 0;
  958. status = BrGetLocalBrowseList(Network,
  959. NULL,
  960. 101,
  961. SV_TYPE_DOMAIN_ENUM,
  962. &DoubleHopLocalList,
  963. &DoubleHopEntriesRead,
  964. &DoubleHopTotalEntries);
  965. for (i = 0 , DomainInfo = DoubleHopLocalList ;
  966. i < DoubleHopEntriesRead ;
  967. i += 1) {
  968. if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
  969. MasterName = DomainInfo->sv101_comment;
  970. break;
  971. }
  972. DomainInfo += 1;
  973. }
  974. }
  975. } else {
  976. ULONG i;
  977. PSERVER_INFO_101 DomainInfo;
  978. //
  979. // We're running on a backup browser. We want to find the
  980. // name of the master browser by looking in the backup
  981. // server list for this network.
  982. //
  983. for (i = 0 , DomainInfo = Network->BackupDomainList ;
  984. i < Network->TotalBackupDomainListEntries ;
  985. i += 1) {
  986. if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
  987. MasterName = DomainInfo->sv101_comment;
  988. break;
  989. }
  990. DomainInfo += 1;
  991. }
  992. }
  993. //
  994. // If we couldn't find a master name, bail out right now.
  995. //
  996. if (MasterName == NULL || *MasterName == UNICODE_NULL) {
  997. try_return(status = ERROR_NO_BROWSER_SERVERS_FOUND);
  998. }
  999. // We also check if the MasterName is actually our current domain name
  1000. // There is a deadlock scenario that is being hit that is causing this
  1001. // to be the case which results in stalled SRV threads
  1002. if ( 0 == STRICMP(MasterName, Network->DomainInfo->DomUnicodeDomainName)) {
  1003. Domain = NULL;
  1004. goto get_local_list;
  1005. }
  1006. //
  1007. // If the master for this domain isn't listed as our
  1008. // current machine, remote the API to that server.
  1009. //
  1010. if (STRICMP(MasterName, Network->DomainInfo->DomUnicodeComputerName))
  1011. {
  1012. WCHAR RemoteComputerName[UNLEN+1];
  1013. //
  1014. // Build the name of the remote computer.
  1015. //
  1016. STRCPY(RemoteComputerName, TEXT("\\\\"));
  1017. STRCAT(RemoteComputerName, MasterName);
  1018. ASSERT (NetworkLocked);
  1019. UNLOCK_NETWORK(Network);
  1020. NetworkLocked = FALSE;
  1021. BrPrint(( BR_SERVER_ENUM,
  1022. "Double hop to %ws on %ws\n",
  1023. RemoteComputerName,
  1024. Network->NetworkName.Buffer));
  1025. BrPrint(( BR_CLIENT_OP,
  1026. "BrNetServerEnum: for client <%ws>, remoting the call to computer <%ws> on network <%ws>\n",
  1027. ClientName, RemoteComputerName, Network->NetworkName.Buffer ));
  1028. status = RxNetServerEnum(RemoteComputerName,
  1029. Network->NetworkName.Buffer,
  1030. Level,
  1031. (LPBYTE *)Buffer,
  1032. PreferedMaximumLength,
  1033. EntriesRead,
  1034. TotalEntries,
  1035. ServerType,
  1036. Domain,
  1037. FirstNameToReturn );
  1038. BrPrint(( BR_SERVER_ENUM, "Double hop done\n"));
  1039. if (!LOCK_NETWORK_SHARED (Network)) {
  1040. try_return(status = NERR_InternalError);
  1041. }
  1042. NetworkLocked = TRUE;
  1043. try_return(status);
  1044. }
  1045. }
  1046. get_local_list:
  1047. ASSERT (NetworkLocked);
  1048. if (!ARGUMENT_PRESENT(Domain)) {
  1049. STRCPY(DomainName, Network->DomainInfo->DomUnicodeDomainName);
  1050. }
  1051. //
  1052. // If we are running on the master browser, we want to retrieve the
  1053. // local list, merge it with our interim server list, and
  1054. // return that to the user.
  1055. //
  1056. if (Network->Role & ROLE_MASTER) {
  1057. BrPrint(( BR_CLIENT_OP,
  1058. "BrNetServerEnum: retrieving local server list as master browser for client <%ws> on network <%ws>\n",
  1059. ClientName, Network->NetworkName.Buffer ));
  1060. status = BrRetrieveServerListForMaster(Network,
  1061. Buffer,
  1062. EntriesRead,
  1063. TotalEntries,
  1064. Level,
  1065. ServerType,
  1066. PreferedMaximumLength,
  1067. LocalListOnly,
  1068. (LPWSTR) ClientName,
  1069. DomainName,
  1070. FirstNameToReturn );
  1071. } else {
  1072. BrPrint(( BR_CLIENT_OP,
  1073. "BrNetServerEnum: retrieving local server list as backup browser for client <%ws> on network <%ws>\n",
  1074. ClientName, Network->NetworkName.Buffer ));
  1075. status = BrRetrieveServerListForBackup(Network,
  1076. Buffer,
  1077. EntriesRead,
  1078. TotalEntries,
  1079. Level,
  1080. ServerType,
  1081. PreferedMaximumLength,
  1082. FirstNameToReturn );
  1083. }
  1084. try_return(status);
  1085. try_exit:NOTHING;
  1086. } finally {
  1087. #if DBG
  1088. if (status == NERR_Success || status == ERROR_MORE_DATA) {
  1089. BrPrint(( BR_SERVER_ENUM,
  1090. "%ws: %ws: Returning Browse list for %lx with %ld entries for %ws\n",
  1091. Domain,
  1092. Network->NetworkName.Buffer,
  1093. ServerType,
  1094. *EntriesRead,
  1095. ClientName));
  1096. } else {
  1097. BrPrint(( BR_CRITICAL,
  1098. "%ws: %ws: Failing I_BrowserServerEnum: %ld for client %ws\n",
  1099. Domain,
  1100. Network->NetworkName.Buffer,
  1101. status, ClientName));
  1102. }
  1103. #endif
  1104. //
  1105. // If we used the local driver's list to retrieve the list of
  1106. // domains, free up the buffer.
  1107. //
  1108. if (DoubleHopLocalList != NULL) {
  1109. MIDL_user_free(DoubleHopLocalList);
  1110. }
  1111. ASSERT (NetworkLocked);
  1112. }
  1113. return status;
  1114. }
  1115. VOID
  1116. TrimServerList(
  1117. IN DWORD Level,
  1118. IN OUT LPBYTE *Buffer,
  1119. IN OUT LPDWORD EntriesRead,
  1120. IN OUT LPDWORD TotalEntries,
  1121. IN LPCWSTR FirstNameToReturn
  1122. )
  1123. /*++
  1124. Routine Description:
  1125. This routine trims any name from Buffer that's less than FirstNameToReturn.
  1126. The routine is implemented by moving the fixed part of the kept entries to the beginning
  1127. of the allocated buffer. Thay way, the pointer contained in the entries need not be
  1128. relocated and the original buffer can still be used.
  1129. Arguments:
  1130. Level - Level of information in the buffer.
  1131. Buffer - Pointer to address of buffer to trim.
  1132. Returns null (and deallocates the buffer) if all the entries are trimmed.
  1133. EntriesRead - Pointer to number of entries in the buffer.
  1134. Returns the number of entries remaining in the buffer after triming.
  1135. TotalEntries - Pointer to number of entries available from server.
  1136. Returns a number reduced by the number of trimmed entries.
  1137. FirstNameToReturn - Supplies the name of the first server or domain to return
  1138. to the caller. If NULL, enumeration begins with the very first entry.
  1139. Passed name must be the canonical form of the name.
  1140. Return Value:
  1141. Returns the error code for the operation.
  1142. --*/
  1143. {
  1144. DWORD EntryCount;
  1145. DWORD EntryNumber;
  1146. DWORD FixedSize;
  1147. LPBYTE CurrentEntry;
  1148. //
  1149. // Early out if there's nothing to do.
  1150. //
  1151. if ( FirstNameToReturn == NULL || *FirstNameToReturn == L'\0' || *EntriesRead == 0 ) {
  1152. return;
  1153. }
  1154. //
  1155. // Compute the size of each entry.
  1156. //
  1157. switch (Level) {
  1158. case 100:
  1159. FixedSize = sizeof(SERVER_INFO_100);
  1160. break;
  1161. case 101:
  1162. FixedSize = sizeof(SERVER_INFO_101);
  1163. break;
  1164. default:
  1165. NetpAssert( FALSE );
  1166. return;
  1167. }
  1168. //
  1169. // Finding the first entry to return
  1170. //
  1171. EntryCount = *EntriesRead;
  1172. for ( EntryNumber=0; EntryNumber< EntryCount; EntryNumber++ ) {
  1173. LPSERVER_INFO_100 ServerEntry =
  1174. (LPSERVER_INFO_100)( *Buffer + FixedSize * EntryNumber);
  1175. //
  1176. // Found the first entry to return.
  1177. //
  1178. if ( STRCMP( ServerEntry->sv100_name, FirstNameToReturn ) >= 0 ) {
  1179. //
  1180. // If we're returning the whole list,
  1181. // simply return.
  1182. //
  1183. if ( ServerEntry == (LPSERVER_INFO_100)(*Buffer) ) {
  1184. return;
  1185. }
  1186. //
  1187. // Copy the remaining entries to the beginning of the buffer.
  1188. // (Yes, this is an overlapping copy)
  1189. //
  1190. RtlMoveMemory( *Buffer, ServerEntry, (*EntriesRead) * FixedSize );
  1191. return;
  1192. }
  1193. //
  1194. // Account for skipped entries.
  1195. //
  1196. *EntriesRead -= 1;
  1197. *TotalEntries -= 1;
  1198. }
  1199. //
  1200. // If no entries should be returned,
  1201. // deallocate the buffer.
  1202. //
  1203. NetApiBufferFree( *Buffer );
  1204. *Buffer = NULL;
  1205. ASSERT ( *EntriesRead == 0 );
  1206. return;
  1207. } // TrimServerList
  1208. //
  1209. // Context for building a local list of all the Wannish transports.
  1210. //
  1211. typedef struct _BR_LOCAL_LIST_CONTEXT {
  1212. LPWSTR DomainName;
  1213. DWORD Level;
  1214. DWORD ServerType;
  1215. PVOID Buffer;
  1216. NET_API_STATUS NetStatus;
  1217. DWORD EntriesRead;
  1218. DWORD TotalEntries;
  1219. BOOLEAN AtLeastOneWorked;
  1220. INTERIM_SERVER_LIST InterimServerList;
  1221. } BR_LOCAL_LIST_CONTEXT, *PBR_LOCAL_LIST_CONTEXT;
  1222. NET_API_STATUS
  1223. BrGetWannishLocalList(
  1224. IN PNETWORK Network,
  1225. IN PVOID Context
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. Worker function to get the local list for all WANNISH networks and
  1230. merge them together.
  1231. Arguments:
  1232. Network - The current network to do.
  1233. Context - Context that the accumulated list is built into.
  1234. Return Value:
  1235. NET_API_STATUS - NERR_Success or reason for failure.
  1236. --*/
  1237. {
  1238. NET_API_STATUS NetStatus;
  1239. PBR_LOCAL_LIST_CONTEXT LocalListContext = (PBR_LOCAL_LIST_CONTEXT) Context;
  1240. PVOID Buffer = NULL;
  1241. DWORD EntriesRead;
  1242. DWORD TotalEntries;
  1243. //
  1244. // If this isn't a wannish network,
  1245. // ignore it.
  1246. if ((Network->Flags & NETWORK_WANNISH) == 0 ) {
  1247. BrPrint(( BR_SERVER_ENUM,
  1248. "%ws: %ws: isn't a wannish network. (ignored)\n",
  1249. Network->DomainInfo->DomUnicodeDomainName,
  1250. Network->NetworkName.Buffer ));
  1251. return NO_ERROR;
  1252. }
  1253. //
  1254. // Get the local list from this transport.
  1255. //
  1256. BrPrint(( BR_SERVER_ENUM,
  1257. "%ws: %ws: Get local list from wannish network.\n",
  1258. Network->DomainInfo->DomUnicodeDomainName,
  1259. Network->NetworkName.Buffer ));
  1260. NetStatus = BrGetLocalBrowseList(
  1261. Network,
  1262. LocalListContext->DomainName,
  1263. LocalListContext->Level,
  1264. LocalListContext->ServerType,
  1265. &Buffer,
  1266. &EntriesRead,
  1267. &TotalEntries);
  1268. if ( NetStatus != NO_ERROR && NetStatus != ERROR_MORE_DATA ) {
  1269. BrPrint(( BR_CRITICAL,
  1270. "%ws: %ws: Get local list from wannish network failed: %ld.\n",
  1271. Network->DomainInfo->DomUnicodeDomainName,
  1272. Network->NetworkName.Buffer,
  1273. NetStatus ));
  1274. //
  1275. // If no transport has worked yet, save this as the new default status.
  1276. // (But keep on going)
  1277. //
  1278. if ( !LocalListContext->AtLeastOneWorked ) {
  1279. LocalListContext->NetStatus = NetStatus;
  1280. }
  1281. NetStatus = NO_ERROR;
  1282. goto Cleanup;
  1283. }
  1284. LocalListContext->AtLeastOneWorked = TRUE;
  1285. LocalListContext->NetStatus = NetStatus;
  1286. //
  1287. // If no data was returned,
  1288. // we're done.
  1289. //
  1290. if ( EntriesRead == 0 ) {
  1291. BrPrint(( BR_SERVER_ENUM,
  1292. "%ws: %ws: Get local list from wannish network got zero entries.\n",
  1293. Network->DomainInfo->DomUnicodeDomainName,
  1294. Network->NetworkName.Buffer ));
  1295. NetStatus = NO_ERROR;
  1296. goto Cleanup;
  1297. }
  1298. //
  1299. // If a prior network already returned some entries,
  1300. // build an interim list to merge both lists into.
  1301. //
  1302. if ( LocalListContext->Buffer != NULL ) {
  1303. NetStatus = MergeServerList(
  1304. &LocalListContext->InterimServerList,
  1305. LocalListContext->Level,
  1306. LocalListContext->Buffer,
  1307. LocalListContext->EntriesRead,
  1308. LocalListContext->TotalEntries );
  1309. MIDL_user_free(LocalListContext->Buffer);
  1310. LocalListContext->Buffer = NULL;
  1311. LocalListContext->EntriesRead = 0;
  1312. LocalListContext->TotalEntries = 0;;
  1313. if ( NetStatus != NO_ERROR ) {
  1314. BrPrint(( BR_CRITICAL,
  1315. "%ws: %ws: merge local list from wannish network failed: %ld.\n",
  1316. Network->DomainInfo->DomUnicodeDomainName,
  1317. Network->NetworkName.Buffer,
  1318. NetStatus ));
  1319. goto Cleanup;
  1320. }
  1321. }
  1322. //
  1323. // If there is an interim server list,
  1324. // add the new entries to it.
  1325. //
  1326. if ( LocalListContext->InterimServerList.TotalEntries != 0 ) {
  1327. NetStatus = MergeServerList(
  1328. &LocalListContext->InterimServerList,
  1329. LocalListContext->Level,
  1330. Buffer,
  1331. EntriesRead,
  1332. TotalEntries );
  1333. if ( NetStatus != NO_ERROR ) {
  1334. BrPrint(( BR_CRITICAL,
  1335. "%ws: %ws: merge local list from wannish network failed: %ld.\n",
  1336. Network->DomainInfo->DomUnicodeDomainName,
  1337. Network->NetworkName.Buffer,
  1338. NetStatus ));
  1339. goto Cleanup;
  1340. }
  1341. //
  1342. // If this is the first network to return entries,
  1343. // simply save the list.
  1344. //
  1345. // This saves us from having to convert a list to an interim list just
  1346. // to have to convert it back in those cases where we have a single wannish
  1347. // network.
  1348. //
  1349. } else {
  1350. LocalListContext->Buffer = Buffer;
  1351. LocalListContext->EntriesRead = EntriesRead;
  1352. LocalListContext->TotalEntries = TotalEntries;
  1353. Buffer = NULL;
  1354. }
  1355. NetStatus = NO_ERROR;
  1356. Cleanup:
  1357. if ( Buffer != NULL ) {
  1358. MIDL_user_free(Buffer);
  1359. }
  1360. return NetStatus;
  1361. }
  1362. NET_API_STATUS
  1363. BrRetrieveServerListForMaster(
  1364. IN PNETWORK Network,
  1365. IN OUT PVOID *Buffer,
  1366. OUT PDWORD EntriesRead,
  1367. OUT PDWORD TotalEntries,
  1368. IN DWORD Level,
  1369. IN DWORD ServerType,
  1370. IN DWORD PreferedMaximumLength,
  1371. IN BOOLEAN LocalListOnly,
  1372. IN LPTSTR ClientName,
  1373. IN LPTSTR DomainName,
  1374. IN LPCWSTR FirstNameToReturn
  1375. )
  1376. {
  1377. BOOLEAN GetLocalList = FALSE;
  1378. NET_API_STATUS status;
  1379. BOOLEAN EarlyOut = FALSE;
  1380. //
  1381. // Determine if it is appropriate that we should early out after retrieving
  1382. // the list of servers (or domains) from the bowser.
  1383. //
  1384. // We will early out on the following critieria:
  1385. //
  1386. // 1) If we are requested to retrieve the local list on a Wannish xport
  1387. // 2) If the transport isn't a wannish transport, or
  1388. // 3) If this domain isn't our primary domain (in which case it's an
  1389. // "otherdomain" request).
  1390. //
  1391. if (LocalListOnly || STRICMP(DomainName, Network->DomainInfo->DomUnicodeDomainName)) {
  1392. EarlyOut = TRUE;
  1393. } else if (!(Network->Flags & NETWORK_WANNISH)) {
  1394. //
  1395. // This isn't a wannish transport. We need to see if we've been
  1396. // master long enough for the driver to have retrieved a reasonable
  1397. // list.
  1398. //
  1399. // The server and domain table will be emptied when the first master
  1400. // browser timer is run. This will happen 15 minutes after we
  1401. // become the master, so we will use our services list for the
  1402. // first 15 minutes the browser is master.
  1403. //
  1404. if (ServerType == SV_TYPE_DOMAIN_ENUM) {
  1405. if (Network->DomainList.EntriesRead == 0) {
  1406. EarlyOut = TRUE;
  1407. }
  1408. } else {
  1409. if (Network->BrowseTable.EntriesRead == 0) {
  1410. EarlyOut = TRUE;
  1411. }
  1412. }
  1413. }
  1414. if (EarlyOut) {
  1415. GetLocalList = TRUE;
  1416. } else if (ServerType == SV_TYPE_ALL) {
  1417. if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
  1418. GetLocalList = TRUE;
  1419. }
  1420. } else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
  1421. if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
  1422. GetLocalList = TRUE;
  1423. }
  1424. } else {
  1425. GetLocalList = TRUE;
  1426. }
  1427. if (GetLocalList && !EarlyOut) {
  1428. //
  1429. // If we're retriving the list from the driver, and can't early out
  1430. // this request, this means that we will be merging this server list
  1431. // with an interim server list. This means that we will be modifying
  1432. // the network structure, and thus that we need to re-lock the network
  1433. // structure.
  1434. //
  1435. UNLOCK_NETWORK(Network);
  1436. if (!LOCK_NETWORK(Network)) {
  1437. return NERR_InternalError;
  1438. }
  1439. //
  1440. // Now re-test to see if another thread came in and retrieved the
  1441. // list from the driver while we had the network unlocked. If so,
  1442. // we don't want to get the local list any more.
  1443. //
  1444. // Otherwise, we want to update the last query time.
  1445. //
  1446. if (ServerType == SV_TYPE_ALL) {
  1447. if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
  1448. Network->LastBowserServerQueried = BrCurrentSystemTime();
  1449. } else {
  1450. GetLocalList = FALSE;
  1451. }
  1452. } else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
  1453. if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
  1454. Network->LastBowserDomainQueried = BrCurrentSystemTime();
  1455. } else {
  1456. GetLocalList = FALSE;
  1457. }
  1458. } else {
  1459. Network->LastBowserServerQueried = BrCurrentSystemTime();
  1460. }
  1461. }
  1462. //
  1463. // If we're supposed to retrieve the local server list, retrieve it.
  1464. //
  1465. if (GetLocalList) {
  1466. DWORD ServerTypeForLocalList;
  1467. //
  1468. // Calculate the server type to use when retrieving the list from
  1469. // the driver.
  1470. //
  1471. if (LocalListOnly ||
  1472. (ServerType == SV_TYPE_DOMAIN_ENUM) ||
  1473. !(Network->Flags & NETWORK_WANNISH)) {
  1474. //
  1475. // If we are retrieving the local list, or this is a non-wannish
  1476. // transport, or we are asking for domains, we want to
  1477. // keep the callers server type.
  1478. //
  1479. ServerTypeForLocalList = ServerType;
  1480. } else {
  1481. //
  1482. // This must be a wannish transport, ask for all servers (we know
  1483. // that we're not asking for domains, since we checked that
  1484. // above).
  1485. //
  1486. // We ask for all the servers because it's possible that a servers
  1487. // role has changed.
  1488. //
  1489. ASSERT (Network->Flags & NETWORK_WANNISH);
  1490. ServerTypeForLocalList = SV_TYPE_ALL;
  1491. }
  1492. BrPrint(( BR_SERVER_ENUM,
  1493. "%ws: %ws: Get local browse list for %ws\n",
  1494. Network->DomainInfo->DomUnicodeDomainName,
  1495. Network->NetworkName.Buffer,
  1496. ClientName));
  1497. //
  1498. // If this is a call from the Domain Master Browser to get the local list,
  1499. // and this is a wannish transport,
  1500. // get the local list on all wannish transports.
  1501. //
  1502. if ( LocalListOnly &&
  1503. (Network->Flags & NETWORK_WANNISH) != 0 ) {
  1504. BR_LOCAL_LIST_CONTEXT LocalListContext;
  1505. //
  1506. // Build a context to use to gather the server lists from all
  1507. // wannish transports
  1508. //
  1509. LocalListContext.DomainName = DomainName;
  1510. LocalListContext.Level = Level;
  1511. LocalListContext.ServerType = ServerTypeForLocalList;
  1512. LocalListContext.Buffer = NULL;
  1513. LocalListContext.NetStatus = NO_ERROR;
  1514. LocalListContext.EntriesRead = 0;
  1515. LocalListContext.TotalEntries = 0;
  1516. LocalListContext.AtLeastOneWorked = FALSE;
  1517. (VOID) InitializeInterimServerList(&LocalListContext.InterimServerList, NULL, NULL, NULL, NULL);
  1518. //
  1519. // Gather the lists
  1520. //
  1521. status = BrEnumerateNetworks( BrGetWannishLocalList, &LocalListContext );
  1522. if ( status == NO_ERROR ) {
  1523. //
  1524. // If multiple networks were found,
  1525. // pack the interim list.
  1526. //
  1527. if ( LocalListContext.InterimServerList.TotalEntries != 0 ) {
  1528. status = PackServerList( &LocalListContext.InterimServerList,
  1529. LocalListContext.Level,
  1530. LocalListContext.ServerType,
  1531. PreferedMaximumLength,
  1532. Buffer,
  1533. EntriesRead,
  1534. TotalEntries,
  1535. (LPWSTR)FirstNameToReturn );
  1536. //
  1537. // If just a single network was found,
  1538. // just use it.
  1539. //
  1540. } else {
  1541. status = LocalListContext.NetStatus;
  1542. *Buffer = LocalListContext.Buffer;
  1543. *EntriesRead = LocalListContext.EntriesRead;
  1544. *TotalEntries = LocalListContext.TotalEntries;
  1545. }
  1546. }
  1547. (VOID) UninitializeInterimServerList( &LocalListContext.InterimServerList );
  1548. } else {
  1549. status = BrGetLocalBrowseList(
  1550. Network,
  1551. DomainName,
  1552. ( EarlyOut ? Level : 101 ),
  1553. ServerTypeForLocalList,
  1554. Buffer,
  1555. EntriesRead,
  1556. TotalEntries);
  1557. }
  1558. // BrPrint(( BR_SERVER_ENUM, "List retrieved. %ld entries, %ld total\n", *EntriesRead, *TotalEntries));
  1559. //
  1560. // If we're supposed to early-out this request (or if there was
  1561. // an error), do so now.
  1562. //
  1563. if (EarlyOut ||
  1564. (status != NERR_Success && status != ERROR_MORE_DATA)) {
  1565. //
  1566. // If we're returning an early out list,
  1567. // truncate the complete list returned from the kernel.
  1568. //
  1569. // This saves us from having to modify the kernel interface and untangle
  1570. // the code above.
  1571. //
  1572. if ( status == NERR_Success || status == ERROR_MORE_DATA ) {
  1573. TrimServerList( Level,
  1574. (LPBYTE *)Buffer,
  1575. EntriesRead,
  1576. TotalEntries,
  1577. FirstNameToReturn );
  1578. }
  1579. BrPrint(( BR_SERVER_ENUM, "Early out for %ws with %ld servers. Don't merge server list.\n", ClientName, *EntriesRead));
  1580. return status;
  1581. }
  1582. if (status == NERR_Success || status == ERROR_MORE_DATA) {
  1583. if (*EntriesRead != 0) {
  1584. //
  1585. // Merge the local list with the list we got from the
  1586. // master or from the domain master.
  1587. //
  1588. BrPrint(( BR_SERVER_ENUM,
  1589. "%ws: %ws: Merge %d entries in server list for %ws \n",
  1590. Network->DomainInfo->DomUnicodeDomainName,
  1591. Network->NetworkName.Buffer,
  1592. *EntriesRead,
  1593. ClientName));
  1594. status = MergeServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
  1595. &Network->DomainList :
  1596. &Network->BrowseTable),
  1597. 101,
  1598. *Buffer,
  1599. *EntriesRead,
  1600. *TotalEntries
  1601. );
  1602. }
  1603. }
  1604. }
  1605. //
  1606. // We've merged the local list into the appropriate interim table,
  1607. // now free it up.
  1608. //
  1609. if (*EntriesRead != 0) {
  1610. MIDL_user_free(*Buffer);
  1611. }
  1612. status = PackServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
  1613. &Network->DomainList :
  1614. &Network->BrowseTable),
  1615. Level,
  1616. ServerType,
  1617. PreferedMaximumLength,
  1618. Buffer,
  1619. EntriesRead,
  1620. TotalEntries,
  1621. FirstNameToReturn
  1622. );
  1623. return status;
  1624. }
  1625. NET_API_STATUS
  1626. BrRetrieveServerListForBackup(
  1627. IN PNETWORK Network,
  1628. IN OUT PVOID *Buffer,
  1629. OUT PDWORD EntriesRead,
  1630. OUT PDWORD TotalEntries,
  1631. IN DWORD Level,
  1632. IN DWORD ServerType,
  1633. IN DWORD PreferedMaximumLength,
  1634. IN LPCWSTR FirstNameToReturn
  1635. )
  1636. {
  1637. PSERVER_INFO_101 ServerList, ClientServerInfo;
  1638. ULONG EntriesInList;
  1639. ULONG TotalEntriesInList;
  1640. ULONG EntrySize;
  1641. ULONG BufferSize;
  1642. LPTSTR BufferEnd;
  1643. BOOLEAN ReturnWholeList = FALSE;
  1644. BOOLEAN TrimmingNames;
  1645. BOOLEAN BufferFull = FALSE; // see bug 427656
  1646. //
  1647. // If we are not running as a master, we want to use our stored
  1648. // server list to figure out what the client gets.
  1649. //
  1650. if (ServerType == SV_TYPE_DOMAIN_ENUM) {
  1651. ServerList = Network->BackupDomainList;
  1652. TotalEntriesInList = EntriesInList = Network->TotalBackupDomainListEntries;
  1653. ReturnWholeList = TRUE;
  1654. } else {
  1655. ServerList = Network->BackupServerList;
  1656. TotalEntriesInList = EntriesInList = Network->TotalBackupServerListEntries;
  1657. if (ServerType == SV_TYPE_ALL) {
  1658. ReturnWholeList = TRUE;
  1659. }
  1660. }
  1661. //
  1662. // Figure out the largest buffer we have to allocate to hold this
  1663. // server info.
  1664. //
  1665. if (Level == 101) {
  1666. if (PreferedMaximumLength == MAXULONG) {
  1667. if (ServerType == SV_TYPE_DOMAIN_ENUM) {
  1668. BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
  1669. } else {
  1670. BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + LM20_MAXCOMMENTSZ+1)*sizeof(TCHAR)) * EntriesInList;
  1671. }
  1672. } else {
  1673. BufferSize = PreferedMaximumLength;
  1674. }
  1675. EntrySize = sizeof(SERVER_INFO_101);
  1676. } else {
  1677. if (PreferedMaximumLength == MAXULONG) {
  1678. BufferSize = (sizeof(SERVER_INFO_100) + (CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
  1679. } else {
  1680. BufferSize = PreferedMaximumLength;
  1681. }
  1682. EntrySize = sizeof(SERVER_INFO_100);
  1683. }
  1684. *Buffer = MIDL_user_allocate(BufferSize);
  1685. if (*Buffer == NULL) {
  1686. return ERROR_NOT_ENOUGH_MEMORY;
  1687. }
  1688. BufferEnd = (LPTSTR)((ULONG_PTR)*Buffer+BufferSize);
  1689. ClientServerInfo = *Buffer;
  1690. *TotalEntries = 0;
  1691. *EntriesRead = 0;
  1692. //
  1693. // While there are still entries to process....
  1694. //
  1695. TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
  1696. while (EntriesInList) {
  1697. EntriesInList -= 1;
  1698. //
  1699. // If this entry is appropriate to be packed,
  1700. //
  1701. if ( (ServerList->sv101_type & ServerType) &&
  1702. (!TrimmingNames ||
  1703. STRCMP( ServerList->sv101_name, FirstNameToReturn ) >= 0 ) ) {
  1704. TrimmingNames = FALSE;
  1705. //
  1706. // Indicate one more entry in the list.
  1707. //
  1708. *TotalEntries += 1;
  1709. //
  1710. // If we can fit this entry before the buffer end,
  1711. // pack in the information into the buffer.
  1712. //
  1713. if ( !BufferFull &&
  1714. (ULONG_PTR)ClientServerInfo+EntrySize <= (ULONG_PTR)BufferEnd) {
  1715. //
  1716. // Copy over the platform ID and computer name.
  1717. //
  1718. ClientServerInfo->sv101_platform_id = ServerList->sv101_platform_id;
  1719. ClientServerInfo->sv101_name = ServerList->sv101_name;
  1720. if (NetpPackString(&ClientServerInfo->sv101_name,
  1721. (LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
  1722. &BufferEnd)) {
  1723. if (Level == 101) {
  1724. ClientServerInfo->sv101_version_major = ServerList->sv101_version_major;
  1725. ClientServerInfo->sv101_version_minor = ServerList->sv101_version_minor;
  1726. ClientServerInfo->sv101_type = ServerList->sv101_type;
  1727. ClientServerInfo->sv101_comment = ServerList->sv101_comment;
  1728. if (NetpPackString(&ClientServerInfo->sv101_comment,
  1729. (LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
  1730. &BufferEnd)) {
  1731. *EntriesRead += 1;
  1732. }
  1733. else {
  1734. BufferFull = TRUE;
  1735. }
  1736. } else {
  1737. *EntriesRead += 1;
  1738. }
  1739. }
  1740. else {
  1741. BufferFull = TRUE;
  1742. }
  1743. ClientServerInfo = (PSERVER_INFO_101)((PCHAR)ClientServerInfo+EntrySize);
  1744. } else {
  1745. //
  1746. // If we're returning the entire list, we can
  1747. // early out now, since there's no point in continuing.
  1748. //
  1749. if (ReturnWholeList) {
  1750. *TotalEntries = TotalEntriesInList;
  1751. break;
  1752. }
  1753. BufferFull = TRUE;
  1754. }
  1755. }
  1756. ServerList += 1;
  1757. }
  1758. //
  1759. // If we weren't able to pack all the entries into the list,
  1760. // return ERROR_MORE_DATA
  1761. //
  1762. BrPrint( (BR_CLIENT_OP,
  1763. "BrRetrieveServerListForBackup: returning from the stored server list for network <%ws>\n",
  1764. Network->NetworkName.Buffer ));
  1765. if (*EntriesRead != *TotalEntries) {
  1766. return ERROR_MORE_DATA;
  1767. } else {
  1768. return NERR_Success;
  1769. }
  1770. }
  1771. NET_API_STATUS
  1772. I_BrowserrResetStatistics (
  1773. IN LPTSTR servername OPTIONAL
  1774. )
  1775. {
  1776. NET_API_STATUS Status = NERR_Success;
  1777. ULONG BufferSize;
  1778. //
  1779. // Perform access validation on the caller.
  1780. //
  1781. Status = NetpAccessCheck(
  1782. BrGlobalBrowserSecurityDescriptor, // Security descriptor
  1783. BROWSER_CONTROL_ACCESS, // Desired access
  1784. &BrGlobalBrowserInfoMapping ); // Generic mapping
  1785. if ( Status != NERR_Success) {
  1786. BrPrint((BR_CRITICAL,
  1787. "I_BrowserrResetStatistics failed NetpAccessCheck\n" ));
  1788. return ERROR_ACCESS_DENIED;
  1789. }
  1790. EnterCriticalSection(&BrowserStatisticsLock);
  1791. NumberOfServerEnumerations = 0;
  1792. NumberOfDomainEnumerations = 0;
  1793. NumberOfOtherEnumerations = 0;
  1794. NumberOfMissedGetBrowserListRequests = 0;
  1795. //
  1796. // Reset the driver's statistics as well.
  1797. //
  1798. if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_RESET_STATISTICS, NULL, 0, NULL, 0, &BufferSize, NULL)) {
  1799. //
  1800. // The API failed, return the error.
  1801. //
  1802. Status = GetLastError();
  1803. }
  1804. LeaveCriticalSection(&BrowserStatisticsLock);
  1805. return Status;
  1806. }
  1807. NET_API_STATUS
  1808. I_BrowserrQueryStatistics (
  1809. IN LPTSTR servername OPTIONAL,
  1810. OUT LPBROWSER_STATISTICS *Statistics
  1811. )
  1812. {
  1813. NET_API_STATUS Status = NERR_Success;
  1814. BOWSER_STATISTICS BowserStatistics;
  1815. ULONG BufferSize;
  1816. //
  1817. // Perform access validation on the caller.
  1818. //
  1819. Status = NetpAccessCheck(
  1820. BrGlobalBrowserSecurityDescriptor, // Security descriptor
  1821. BROWSER_QUERY_ACCESS, // Desired access
  1822. &BrGlobalBrowserInfoMapping ); // Generic mapping
  1823. if ( Status != NERR_Success) {
  1824. BrPrint((BR_CRITICAL,
  1825. "I_BrowserrQueryStatistics failed NetpAccessCheck\n" ));
  1826. return ERROR_ACCESS_DENIED;
  1827. }
  1828. *Statistics = MIDL_user_allocate(sizeof(BROWSER_STATISTICS));
  1829. if (*Statistics == NULL) {
  1830. return ERROR_NOT_ENOUGH_MEMORY;
  1831. }
  1832. EnterCriticalSection(&BrowserStatisticsLock);
  1833. if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_QUERY_STATISTICS, NULL, 0, &BowserStatistics, sizeof(BowserStatistics), &BufferSize, NULL)) {
  1834. //
  1835. // The API failed, return the error.
  1836. //
  1837. Status = GetLastError();
  1838. } else {
  1839. if (BufferSize != sizeof(BOWSER_STATISTICS)) {
  1840. Status = ERROR_INSUFFICIENT_BUFFER;
  1841. } else {
  1842. (*Statistics)->StatisticsStartTime = BowserStatistics.StartTime;
  1843. (*Statistics)->NumberOfServerAnnouncements = BowserStatistics.NumberOfServerAnnouncements;
  1844. (*Statistics)->NumberOfDomainAnnouncements = BowserStatistics.NumberOfDomainAnnouncements;
  1845. (*Statistics)->NumberOfElectionPackets = BowserStatistics.NumberOfElectionPackets;
  1846. (*Statistics)->NumberOfMailslotWrites = BowserStatistics.NumberOfMailslotWrites;
  1847. (*Statistics)->NumberOfGetBrowserServerListRequests = BowserStatistics.NumberOfGetBrowserServerListRequests;
  1848. (*Statistics)->NumberOfMissedServerAnnouncements = BowserStatistics.NumberOfMissedServerAnnouncements;
  1849. (*Statistics)->NumberOfMissedMailslotDatagrams = BowserStatistics.NumberOfMissedMailslotDatagrams;
  1850. (*Statistics)->NumberOfMissedGetBrowserServerListRequests = BowserStatistics.NumberOfMissedGetBrowserServerListRequests +
  1851. NumberOfMissedGetBrowserListRequests;
  1852. (*Statistics)->NumberOfFailedServerAnnounceAllocations = BowserStatistics.NumberOfFailedServerAnnounceAllocations;
  1853. (*Statistics)->NumberOfFailedMailslotAllocations = BowserStatistics.NumberOfFailedMailslotAllocations;
  1854. (*Statistics)->NumberOfFailedMailslotReceives = BowserStatistics.NumberOfFailedMailslotReceives;
  1855. (*Statistics)->NumberOfFailedMailslotWrites = BowserStatistics.NumberOfFailedMailslotWrites;
  1856. (*Statistics)->NumberOfFailedMailslotOpens = BowserStatistics.NumberOfFailedMailslotOpens;
  1857. (*Statistics)->NumberOfDuplicateMasterAnnouncements = BowserStatistics.NumberOfDuplicateMasterAnnouncements;
  1858. (*Statistics)->NumberOfIllegalDatagrams = BowserStatistics.NumberOfIllegalDatagrams;
  1859. //
  1860. // Now fill in the local statistics.
  1861. //
  1862. (*Statistics)->NumberOfServerEnumerations = NumberOfServerEnumerations;
  1863. (*Statistics)->NumberOfDomainEnumerations = NumberOfDomainEnumerations;
  1864. (*Statistics)->NumberOfOtherEnumerations = NumberOfOtherEnumerations;
  1865. }
  1866. }
  1867. LeaveCriticalSection(&BrowserStatisticsLock);
  1868. return Status;
  1869. }
  1870. //
  1871. // Browser request response cache management logic.
  1872. //
  1873. PCACHED_BROWSE_RESPONSE
  1874. BrLookupAndAllocateCachedEntry(
  1875. IN PNETWORK Network,
  1876. IN DWORD ServerType,
  1877. IN WORD Size,
  1878. IN DWORD Level,
  1879. IN LPCWSTR FirstNameToReturn
  1880. )
  1881. /*++
  1882. Routine Description:
  1883. This function will look up (and allocate if appropriate) a cached
  1884. browse response for this browse.
  1885. Enter with the Network Locked shared or exclusive.
  1886. Arguments:
  1887. IN PNETWORK Network - Network to allocate entry on.
  1888. IN DWORD ServerType - Server type bits for request.
  1889. IN WORD Size, - Users buffer size for request.
  1890. IN WORD Level - Level of request.
  1891. FirstNameToReturn - Supplies the name of the first domain or server entry to return.
  1892. The caller can use this parameter to implement a resume handle of sorts by passing
  1893. the name of the last entry returned on a previous call. (Notice that the specified
  1894. entry will, also, be returned on this call unless it has since been deleted.)
  1895. Passed name must be the canonical form of the name.
  1896. This entry is never NULL. It may be a pointer to an empty string to indicate
  1897. the enumeration starts at the beginning of the list.
  1898. Return Value:
  1899. PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
  1900. --*/
  1901. {
  1902. PLIST_ENTRY entry;
  1903. PCACHED_BROWSE_RESPONSE response;
  1904. //
  1905. // If we have more cached responses than we are allowed,
  1906. // remove the last entry from the list and free it.
  1907. //
  1908. if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
  1909. //
  1910. // We need to release the network and re-acquire it
  1911. // exclusively, because we use the network lock to protect
  1912. // enumerations from deletions.
  1913. //
  1914. UNLOCK_NETWORK(Network);
  1915. if (LOCK_NETWORK(Network)) {
  1916. EnterCriticalSection(&Network->ResponseCacheLock);
  1917. if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
  1918. PLIST_ENTRY LastEntry = RemoveTailList(&Network->ResponseCache);
  1919. response = CONTAINING_RECORD(LastEntry, CACHED_BROWSE_RESPONSE, Next);
  1920. Network->NumberOfCachedResponses -= 1;
  1921. response->Next.Flink = NULL;
  1922. response->Next.Blink = NULL;
  1923. //
  1924. // Free the last cached entry.
  1925. //
  1926. BrDestroyCacheEntry( response );
  1927. response = NULL;
  1928. }
  1929. LeaveCriticalSection(&Network->ResponseCacheLock);
  1930. }
  1931. }
  1932. //
  1933. // Search the list of responses for this one.
  1934. //
  1935. EnterCriticalSection(&Network->ResponseCacheLock);
  1936. for (entry = Network->ResponseCache.Flink ;
  1937. entry != &Network->ResponseCache ;
  1938. entry = entry->Flink ) {
  1939. response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
  1940. //
  1941. // If this response cache entry matches the incoming request,
  1942. // we can increment the hit count for this entry and return it.
  1943. //
  1944. if (response->Level == Level
  1945. &&
  1946. response->ServerType == ServerType
  1947. &&
  1948. response->Size == Size
  1949. &&
  1950. wcscmp( response->FirstNameToReturn, FirstNameToReturn ) == 0) {
  1951. //
  1952. // This response exactly matches the request.
  1953. //
  1954. // Bump its hit count and move it to the head of the cache.
  1955. //
  1956. response->HitCount += 1;
  1957. response->TotalHitCount += 1;
  1958. //
  1959. // Remove this entry from its current location in the list and
  1960. // move it to the head of the list.
  1961. //
  1962. RemoveEntryList(&response->Next);
  1963. InsertHeadList(&Network->ResponseCache, &response->Next);
  1964. BrPrint(( BR_SERVER_ENUM,
  1965. "%ws: %ws: Found cache entry 0x%x/%d/%x H:%d T:%d\n",
  1966. Network->DomainInfo->DomUnicodeDomainName,
  1967. Network->NetworkName.Buffer,
  1968. response->ServerType,
  1969. response->Level,
  1970. response->Size,
  1971. response->HitCount,
  1972. response->TotalHitCount ));
  1973. BrPrint( (BR_CLIENT_OP,
  1974. "BrLookupAndAllocateCachedEntry: returning cached entry for domain <%ws>, network <%ws>\n",
  1975. Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer ));
  1976. LeaveCriticalSection(&Network->ResponseCacheLock);
  1977. return response;
  1978. }
  1979. }
  1980. //
  1981. // We've walked our entire cache and have been unable to find
  1982. // a response that matches our request.
  1983. //
  1984. // Allocate a new response cache entry and hook it into the cache.
  1985. //
  1986. response = BrAllocateResponseCacheEntry(Network, ServerType, Size, Level, FirstNameToReturn );
  1987. LeaveCriticalSection(&Network->ResponseCacheLock);
  1988. return response;
  1989. }
  1990. VOID
  1991. BrAgeResponseCache(
  1992. IN PNETWORK Network
  1993. )
  1994. /*++
  1995. Routine Description:
  1996. This function will age response cache entries for a network.
  1997. We scan the response cache, and every entry that has a cached response
  1998. will be tossed. In addition, any entry that has had less than the
  1999. cache hit limit number of hits since the past scan will also be removed.
  2000. Arguments:
  2001. IN PNETWORK Network - Network to age entries on.
  2002. Return Value:
  2003. None.
  2004. --*/
  2005. {
  2006. PLIST_ENTRY entry;
  2007. EnterCriticalSection(&Network->ResponseCacheLock);
  2008. try {
  2009. for (entry = Network->ResponseCache.Flink ;
  2010. entry != &Network->ResponseCache ;
  2011. entry = entry->Flink ) {
  2012. PCACHED_BROWSE_RESPONSE response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
  2013. //
  2014. // If this response didn't have a hit count high enough during
  2015. // the previous run to justify keeping it around, blow it away.
  2016. //
  2017. if (response->HitCount < BrInfo.CacheHitLimit) {
  2018. response->LowHitCount += 1;
  2019. }
  2020. //
  2021. // If we have CacheHitLimit iterations of low hits, then
  2022. // flush the entry from the cache.
  2023. //
  2024. if (response->LowHitCount > BrInfo.CacheHitLimit) {
  2025. PLIST_ENTRY nextentry = entry->Blink;
  2026. BrPrint(( BR_SERVER_ENUM,
  2027. "%ws: %ws: Flush cache entry for 0x%x/%d/%x H:%d T:%d\n",
  2028. Network->DomainInfo->DomUnicodeDomainName,
  2029. Network->NetworkName.Buffer,
  2030. response->ServerType,
  2031. response->Level,
  2032. response->Size,
  2033. response->HitCount,
  2034. response->TotalHitCount ));
  2035. Network->NumberOfCachedResponses -= 1;
  2036. RemoveEntryList(entry);
  2037. entry->Flink = NULL;
  2038. entry->Blink = NULL;
  2039. BrDestroyCacheEntry(response);
  2040. entry = nextentry;
  2041. //
  2042. // Null out the pointer to make sure we don't use it again.
  2043. //
  2044. response = NULL;
  2045. } else {
  2046. BrPrint(( BR_SERVER_ENUM,
  2047. "%ws: %ws: Retain cache entry 0x%x/%d/%x H:%d T:%d\n",
  2048. Network->DomainInfo->DomUnicodeDomainName,
  2049. Network->NetworkName.Buffer,
  2050. response->ServerType, response->Level, response->Size, response->HitCount, response->TotalHitCount ));
  2051. //
  2052. // We ALWAYS blow away the response buffer during an age pass.
  2053. //
  2054. MIDL_user_free( response->Buffer );
  2055. response->Buffer = NULL;
  2056. //
  2057. // Reset the hit count for this entry for this pass.
  2058. //
  2059. response->HitCount = 0;
  2060. }
  2061. }
  2062. } finally {
  2063. LeaveCriticalSection(&Network->ResponseCacheLock);
  2064. }
  2065. }
  2066. PCACHED_BROWSE_RESPONSE
  2067. BrAllocateResponseCacheEntry(
  2068. IN PNETWORK Network,
  2069. IN DWORD ServerType,
  2070. IN WORD Size,
  2071. IN DWORD Level,
  2072. IN LPCWSTR FirstNameToReturn
  2073. )
  2074. /*++
  2075. Routine Description:
  2076. This function will allocate a new browse response cache entry.
  2077. Arguments:
  2078. IN PNETWORK Network - Network to allocate entry on.
  2079. IN DWORD ServerType - Server type bits for request.
  2080. IN WORD Size, - Users buffer size for request.
  2081. IN WORD Level - Level of request.
  2082. FirstNameToReturn - FirstNameCached
  2083. Return Value:
  2084. PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
  2085. NOTE: This is called with the network response cache locked.
  2086. --*/
  2087. {
  2088. PCACHED_BROWSE_RESPONSE response;
  2089. response = MIDL_user_allocate( sizeof( CACHED_BROWSE_RESPONSE ) );
  2090. if ( response == NULL ) {
  2091. return NULL;
  2092. }
  2093. //
  2094. // Flag the information for this response.
  2095. //
  2096. response->ServerType = ServerType;
  2097. response->Size = Size;
  2098. response->Level = Level;
  2099. //
  2100. // Initialize the other fields in the response.
  2101. //
  2102. response->Buffer = NULL;
  2103. response->HitCount = 0;
  2104. response->TotalHitCount = 0;
  2105. response->LowHitCount = 0;
  2106. response->Status = NERR_Success;
  2107. wcscpy( response->FirstNameToReturn, FirstNameToReturn );
  2108. Network->NumberOfCachedResponses += 1;
  2109. //
  2110. // We hook this response into the tail of the cache. We do this
  2111. // because we assume that this request won't be used frequently. If
  2112. // it is, it will move to the head of the cache naturally.
  2113. //
  2114. InsertTailList(&Network->ResponseCache, &response->Next);
  2115. return response;
  2116. }
  2117. NET_API_STATUS
  2118. BrDestroyCacheEntry(
  2119. IN PCACHED_BROWSE_RESPONSE CacheEntry
  2120. )
  2121. /*++
  2122. Routine Description:
  2123. This routine destroys an individual response cache entry.
  2124. Arguments:
  2125. IN PCACHED_BROWSE_RESPONSE CacheEntry - Entry to destroy.
  2126. Return Value:
  2127. NET_API_STATUS - NERR_Success
  2128. --*/
  2129. {
  2130. ASSERT (CacheEntry->Next.Flink == NULL);
  2131. ASSERT (CacheEntry->Next.Blink == NULL);
  2132. if (CacheEntry->Buffer != NULL) {
  2133. MIDL_user_free(CacheEntry->Buffer);
  2134. }
  2135. MIDL_user_free(CacheEntry);
  2136. return NERR_Success;
  2137. }
  2138. NET_API_STATUS
  2139. BrDestroyResponseCache(
  2140. IN PNETWORK Network
  2141. )
  2142. /*++
  2143. Routine Description:
  2144. This routine destroys the entire response cache for a supplied network.
  2145. Arguments:
  2146. IN PNETWORK Network - Network to allocate entry on.
  2147. Return Value:
  2148. NET_API_STATUS - NERR_Success
  2149. --*/
  2150. {
  2151. while (!IsListEmpty(&Network->ResponseCache)) {
  2152. PCACHED_BROWSE_RESPONSE cacheEntry;
  2153. PLIST_ENTRY entry = RemoveHeadList(&Network->ResponseCache);
  2154. entry->Flink = NULL;
  2155. entry->Blink = NULL;
  2156. cacheEntry = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
  2157. Network->NumberOfCachedResponses -= 1;
  2158. BrDestroyCacheEntry(cacheEntry);
  2159. }
  2160. ASSERT (Network->NumberOfCachedResponses == 0);
  2161. return NERR_Success;
  2162. }
  2163. NET_API_STATUS
  2164. NetrBrowserStatisticsGet (
  2165. IN LPTSTR servername OPTIONAL,
  2166. IN DWORD Level,
  2167. IN OUT LPBROWSER_STATISTICS_STRUCT InfoStruct
  2168. )
  2169. {
  2170. //
  2171. // And return success.
  2172. //
  2173. return(NERR_Success);
  2174. }
  2175. NET_API_STATUS
  2176. NetrBrowserStatisticsClear (
  2177. IN LPTSTR servername OPTIONAL
  2178. )
  2179. {
  2180. //
  2181. // And return success.
  2182. //
  2183. return(NERR_Success);
  2184. }
  2185. #if DBG
  2186. NET_API_STATUS
  2187. I_BrowserrDebugCall (
  2188. IN LPTSTR servername OPTIONAL,
  2189. IN DWORD DebugCode,
  2190. IN DWORD OptionalValue
  2191. )
  2192. {
  2193. NET_API_STATUS Status = STATUS_SUCCESS;
  2194. //
  2195. // Perform access validation on the caller.
  2196. //
  2197. Status = NetpAccessCheck(
  2198. BrGlobalBrowserSecurityDescriptor, // Security descriptor
  2199. BROWSER_CONTROL_ACCESS, // Desired access
  2200. &BrGlobalBrowserInfoMapping ); // Generic mapping
  2201. if ( Status != NERR_Success) {
  2202. BrPrint((BR_CRITICAL,
  2203. "I_BrowserrDebugCall failed NetpAccessCheck\n" ));
  2204. return ERROR_ACCESS_DENIED;
  2205. }
  2206. switch (DebugCode) {
  2207. case BROWSER_DEBUG_BREAK_POINT:
  2208. DbgBreakPoint();
  2209. break;
  2210. case BROWSER_DEBUG_DUMP_NETWORKS:
  2211. BrDumpNetworks();
  2212. break;
  2213. case BROWSER_DEBUG_SET_DEBUG:
  2214. BrInfo.BrowserDebug |= OptionalValue;
  2215. BrPrint(( BR_INIT, "Setting browser trace to %lx\n", BrInfo.BrowserDebug));
  2216. break;
  2217. case BROWSER_DEBUG_CLEAR_DEBUG:
  2218. BrInfo.BrowserDebug &= ~OptionalValue;
  2219. BrPrint(( BR_INIT, "Setting browser trace to %lx\n", BrInfo.BrowserDebug));
  2220. break;
  2221. case BROWSER_DEBUG_TRUNCATE_LOG:
  2222. Status = BrTruncateLog();
  2223. break;
  2224. default:
  2225. BrPrint(( BR_CRITICAL, "Unknown debug callout %lx\n", DebugCode));
  2226. DbgBreakPoint();
  2227. break;
  2228. }
  2229. return Status;
  2230. }
  2231. NET_API_STATUS
  2232. I_BrowserrDebugTrace (
  2233. IN LPTSTR servername OPTIONAL,
  2234. IN LPSTR String
  2235. )
  2236. {
  2237. NET_API_STATUS Status;
  2238. //
  2239. // Perform access validation on the caller.
  2240. //
  2241. Status = NetpAccessCheck(
  2242. BrGlobalBrowserSecurityDescriptor, // Security descriptor
  2243. BROWSER_CONTROL_ACCESS, // Desired access
  2244. &BrGlobalBrowserInfoMapping ); // Generic mapping
  2245. if ( Status != NERR_Success) {
  2246. BrPrint((BR_CRITICAL,
  2247. "I_BrowserrDebugTrace failed NetpAccessCheck\n" ));
  2248. return ERROR_ACCESS_DENIED;
  2249. }
  2250. //
  2251. // Stick the string parameter into the browser log.
  2252. //
  2253. BrowserTrace( BR_UTIL, "%s", String);
  2254. //
  2255. // And return success.
  2256. //
  2257. return(NERR_Success);
  2258. }
  2259. #else
  2260. NET_API_STATUS
  2261. I_BrowserrDebugCall (
  2262. IN LPTSTR servername OPTIONAL,
  2263. IN DWORD DebugCode,
  2264. IN DWORD OptionalValue
  2265. )
  2266. {
  2267. return(ERROR_NOT_SUPPORTED);
  2268. }
  2269. NET_API_STATUS
  2270. I_BrowserrDebugTrace (
  2271. IN LPTSTR servername OPTIONAL,
  2272. IN LPSTR String
  2273. )
  2274. {
  2275. return(ERROR_NOT_SUPPORTED);
  2276. }
  2277. #endif