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.

1229 lines
37 KiB

  1. /*++
  2. Copyright (c) 1991-1993 Microsoft Corporation
  3. Module Name:
  4. SrvEnum.c
  5. Abstract:
  6. This module only contains RxNetServerEnum.
  7. Author:
  8. John Rogers (JohnRo) 03-May-1991
  9. Environment:
  10. Portable to any flat, 32-bit environment. (Uses Win32 typedefs.)
  11. Requires ANSI C extensions: slash-slash comments, long external names.
  12. Revision History:
  13. 03-May-1991 JohnRo
  14. Created.
  15. 14-May-1991 JohnRo
  16. Pass 3 aux descriptors to RxRemoteApi.
  17. 22-May-1991 JohnRo
  18. Made LINT-suggested changes. Got rid of tabs.
  19. 26-May-1991 JohnRo
  20. Added incomplete output parm to RxGetServerInfoLevelEquivalent.
  21. 17-Jul-1991 JohnRo
  22. Extracted RxpDebug.h from Rxp.h.
  23. 15-Oct-1991 JohnRo
  24. Be paranoid about possible infinite loop.
  25. 21-Nov-1991 JohnRo
  26. Removed NT dependencies to reduce recompiles.
  27. 07-Feb-1992 JohnRo
  28. Use NetApiBufferAllocate() instead of private version.
  29. 22-Sep-1992 JohnRo
  30. RAID 6739: Browser too slow when not logged into browsed domain.
  31. Use PREFIX_ equates.
  32. 14-Oct-1992 JohnRo
  33. RAID 8844: Assert in net/netlib/convsrv.c(563) converting srvinfo
  34. struct (caused by bug in RxNetServerEnum).
  35. 10-Dec-1992 JohnRo
  36. RAID 4999: RxNetServerEnum doesn't handle near 64K right.
  37. 02-Apr-1993 JohnRo
  38. RAID 5098: DOS app NetUserPasswordSet to downlevel gets NT return code.
  39. Clarify design limit debug message.
  40. 28-Apr-1993 JohnRo
  41. RAID 8072: Remoting NetServerEnum to WFW server hangs forever.
  42. 05-May-1993 JohnRo
  43. RAID 8720: bad data from WFW can cause RxNetServerEnum GP fault.
  44. 21-Jun-1993 JohnRo
  45. RAID 14180: NetServerEnum never returns (alignment bug in
  46. RxpConvertDataStructures).
  47. Also avoid infinite loop RxNetServerEnum.
  48. Made changes suggested by PC-LINT 5.0
  49. --*/
  50. // These must be included first:
  51. #include <nt.h> // DbgPrint prototype
  52. #include <ntrtl.h> // DbgPrint
  53. #include <nturtl.h> // Needed by winbase.h
  54. #include <windef.h> // DWORD
  55. #include <winbase.h> // LocalFree
  56. // #include <windows.h> // IN, LPBYTE, DWORD, etc.
  57. #include <lmcons.h> // NET_API_STATUS, etc.
  58. #include <rap.h> // LPDESC, etc. (Needed by <RxServer.h>)
  59. #include <lmerr.h> // NERR_ and ERROR_ equates. (Needed by rxp.h)
  60. // These may be included in any order:
  61. #include <apinums.h> // API_ equates.
  62. #include <dlserver.h> // NetpConvertServerInfo().
  63. #include <lmapibuf.h> // API buffer alloc & free routines.
  64. #include <lmremutl.h> // RxRemoteApi().
  65. #include <lmserver.h> // SV_TYPE_DOMAIN_ENUM.
  66. #include <netdebug.h> // NetpAssert(), NetpKdPrint(), FORMAT_ equates.
  67. #include <netlib.h> // NetpAdjustPreferedMaximum().
  68. #include <prefix.h> // PREFIX_ equates.
  69. #include <remdef.h> // REMSmb_ equates.
  70. #include <rxp.h> // MAX_TRANSACT_RET_DATA_SIZE, RxpFatalErrorCode().
  71. #include <rxpdebug.h> // IF_DEBUG().
  72. #include <rxserver.h> // My prototype, etc.
  73. #include <rpcutil.h> // MIDL_user_allocate
  74. #define OVERHEAD 0
  75. #define INITIAL_MAX_SIZE (1024 * 16)
  76. VOID
  77. ServerRelocationRoutine(
  78. IN DWORD Level,
  79. IN DWORD FixedSize,
  80. IN DWORD EntryCount,
  81. IN LPBYTE Buffer,
  82. IN PTRDIFF_T Offset
  83. )
  84. /*++
  85. Routine Description:
  86. Routine to relocate the pointers from the fixed portion of a NetServerEnum
  87. enumeration buffer to the string portion of an enumeration buffer.
  88. Arguments:
  89. Level - Level of information in the buffer.
  90. FixedSize - Size of each entry in Buffer.
  91. EntryCount - Number of entries in Buffer.
  92. Buffer - Array of SERVER_INFO_X structures.
  93. Offset - Offset to add to each pointer in the fixed portion.
  94. Return Value:
  95. Returns the error code for the operation.
  96. --*/
  97. {
  98. //
  99. // Local macro to add a byte offset to a pointer.
  100. //
  101. #define RELOCATE_ONE( _fieldname, _offset ) \
  102. if ( (_fieldname) != NULL ) { \
  103. _fieldname = (PVOID) ((LPBYTE)(_fieldname) + _offset); \
  104. }
  105. DWORD EntryNumber;
  106. //
  107. // Loop relocating each field in each fixed size structure
  108. //
  109. for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
  110. LPBYTE TheStruct = Buffer + FixedSize * EntryNumber;
  111. switch ( Level ) {
  112. case 101:
  113. RELOCATE_ONE( ((PSERVER_INFO_101)TheStruct)->sv101_comment, Offset );
  114. //
  115. // Drop through to case 100
  116. //
  117. case 100:
  118. RELOCATE_ONE( ((PSERVER_INFO_100)TheStruct)->sv100_name, Offset );
  119. break;
  120. default:
  121. return;
  122. }
  123. }
  124. return;
  125. }
  126. NET_API_STATUS
  127. AppendServerList(
  128. IN OUT LPBYTE *CurrentServerList,
  129. IN OUT LPDWORD CurrentEntriesRead,
  130. IN OUT LPDWORD CurrentTotalEntries,
  131. IN DWORD Level,
  132. IN DWORD FixedSize,
  133. IN LPBYTE *NewServerList,
  134. IN DWORD NewEntriesRead,
  135. IN DWORD NewTotalEntries
  136. )
  137. /*++
  138. Routine Description:
  139. Concatenates two ServerList Arrays.
  140. Arguments:
  141. CurrentServerList -- Pointer to the current server list. The resultant server list
  142. is returned here.
  143. Pass a pointer to NULL if there is no current list.
  144. The returned list should be free by MIDL_user_free().
  145. CurrentEntriesRead -- Pointer to the number of entries is CurrentServerList.
  146. Updated to reflect entries added.
  147. CurrentTotalEtnries -- Pointer to the total number of entries available at server.
  148. Updated to reflect new information.
  149. Level -- Info level of CurrentServerList and NewServerList.
  150. FixedSize -- Fixed size of each entry.
  151. NewServerList -- Pointer to the server list to append to CurrentServerList.
  152. NULL is returned if this routine deallocates the buffer.
  153. NewEntriesRead -- Number of entries in NewServerList.
  154. NewTotalEntries -- Total number of entries the server believes it has.
  155. Return Value:
  156. NERR_Success -- All OK
  157. ERROR_NO_MEMORY -- Can't reallocate the buffer.
  158. --*/
  159. {
  160. LPBYTE TempServerList;
  161. LPBYTE Where;
  162. LPBYTE LocalCurrentServerList;
  163. LPBYTE LocalNewServerList;
  164. DWORD CurrentFixedSize;
  165. DWORD NewFixedSize;
  166. DWORD CurrentAllocatedSize;
  167. DWORD NewAllocatedSize;
  168. //
  169. // If this is the first list to append,
  170. // simply capture this list and return it to the caller.
  171. //
  172. if ( *CurrentServerList == NULL ) {
  173. *CurrentServerList = *NewServerList;
  174. *NewServerList = NULL;
  175. *CurrentEntriesRead = NewEntriesRead;
  176. *CurrentTotalEntries = NewTotalEntries;
  177. return NERR_Success;
  178. }
  179. //
  180. // Early out if there's nothing to return.
  181. //
  182. if ( NewEntriesRead == 0 ) {
  183. return NERR_Success;
  184. }
  185. //
  186. // Handle the case where the first entry appended is equal to the current last entry.
  187. //
  188. CurrentAllocatedSize = MIDL_user_size( *CurrentServerList );
  189. NewAllocatedSize = MIDL_user_size( *NewServerList );
  190. TempServerList = *NewServerList;
  191. if ( NewEntriesRead != 0 &&
  192. *CurrentEntriesRead != 0 &&
  193. wcscmp( ((LPSERVER_INFO_100)(*NewServerList))->sv100_name,
  194. ((LPSERVER_INFO_100)((*CurrentServerList)+ ((*CurrentEntriesRead)-1) * FixedSize))->sv100_name ) == 0 ) {
  195. TempServerList += FixedSize;
  196. NewEntriesRead -= 1;
  197. NewAllocatedSize -= FixedSize;
  198. //
  199. // Early out if there's nothing to return.
  200. //
  201. if ( NewEntriesRead == 0 ) {
  202. return NERR_Success;
  203. }
  204. }
  205. //
  206. // Allocate a buffer for to return the combined data into.
  207. //
  208. LocalCurrentServerList = MIDL_user_allocate( CurrentAllocatedSize +
  209. NewAllocatedSize );
  210. if ( LocalCurrentServerList == NULL ) {
  211. return ERROR_NOT_ENOUGH_MEMORY;
  212. }
  213. Where = LocalCurrentServerList;
  214. //
  215. // Copy the fixed part of the current buffer into the result buffer.
  216. //
  217. CurrentFixedSize = (*CurrentEntriesRead) * FixedSize;
  218. RtlCopyMemory( LocalCurrentServerList, *CurrentServerList, CurrentFixedSize );
  219. Where += CurrentFixedSize;
  220. //
  221. // Copy the fixed part of the appended buffer into the result buffer.
  222. //
  223. LocalNewServerList = Where;
  224. NewFixedSize = NewEntriesRead * FixedSize;
  225. RtlCopyMemory( LocalNewServerList, TempServerList, NewFixedSize );
  226. Where += NewFixedSize;
  227. //
  228. // Copy the variable portion of current buffer into the result buffer.
  229. // Relocate the pointers from the fixed portion to the variable portion.
  230. //
  231. RtlCopyMemory( Where,
  232. (*CurrentServerList) + CurrentFixedSize,
  233. CurrentAllocatedSize - CurrentFixedSize );
  234. ServerRelocationRoutine( Level,
  235. FixedSize,
  236. *CurrentEntriesRead,
  237. LocalCurrentServerList,
  238. (PTRDIFF_T)(Where - ((*CurrentServerList) + CurrentFixedSize)) );
  239. Where += CurrentAllocatedSize - CurrentFixedSize;
  240. //
  241. // Copy the variable portion of appended buffer into the result buffer.
  242. // Relocate the pointers from the fixed portion to the variable portion.
  243. //
  244. RtlCopyMemory( Where,
  245. TempServerList + NewFixedSize,
  246. NewAllocatedSize - NewFixedSize );
  247. ServerRelocationRoutine( Level,
  248. FixedSize,
  249. NewEntriesRead,
  250. LocalNewServerList,
  251. (PTRDIFF_T)(Where - (TempServerList + NewFixedSize )));
  252. Where += NewAllocatedSize - NewFixedSize;
  253. ASSERT( ((ULONG)(Where - LocalCurrentServerList)) <= CurrentAllocatedSize + NewAllocatedSize );
  254. //
  255. // Free the Old buffer passed in.
  256. //
  257. MIDL_user_free( *CurrentServerList );
  258. *CurrentServerList = NULL;
  259. //
  260. // Pass out the new buffer.
  261. //
  262. *CurrentServerList = LocalCurrentServerList;
  263. //
  264. // Adjust the entry counts
  265. //
  266. *CurrentEntriesRead += NewEntriesRead;
  267. if ( *CurrentTotalEntries < NewTotalEntries ) {
  268. *CurrentTotalEntries = NewTotalEntries;
  269. }
  270. return NERR_Success;
  271. }
  272. NET_API_STATUS
  273. RxNetServerEnumWorker (
  274. IN LPCWSTR UncServerName,
  275. IN LPCWSTR TransportName,
  276. IN DWORD Level,
  277. OUT LPBYTE *BufPtr,
  278. IN DWORD PrefMaxSize,
  279. OUT LPDWORD EntriesRead,
  280. OUT LPDWORD TotalEntries,
  281. IN DWORD ServerType,
  282. IN LPCWSTR Domain OPTIONAL,
  283. IN LPCWSTR FirstNameToReturn OPTIONAL,
  284. IN BOOLEAN InternalContinuation
  285. )
  286. /*++
  287. Routine Description:
  288. RxNetServerEnumWorker performs a single RXACT-style API call to a specified server.
  289. It automatically determines whether to use the Enum2 (old) or Enum3 (new) RXACT API.
  290. It automatically determines whether to use a null session or an authenticated session.
  291. Since Enum2 is not resumable and is the only level implemented on some servers,
  292. this function ignores PrefMaxSize when using that level and returns ALL of the
  293. information available from Enum2.
  294. Since this routine was originally designed for Enum2 (with the above restriction), we
  295. always return the maximum available Enum3 also.
  296. Arguments:
  297. (Same as NetServerEnum, except UncServerName must not be null, and
  298. must not refer to the local computer.)
  299. FirstNameToReturn: Must be uppercase
  300. Passed name must be the canonical form of the name.
  301. InternalContinuation: TRUE if the caller has previously called RxNetServerEnumWorker
  302. to return all the possible entries using Enum2. This flag is used to prevent
  303. an automatic fallback to using Enum2.
  304. Return Value:
  305. (Same as NetServerEnum.)
  306. --*/
  307. {
  308. DWORD EntryCount; // entries (old & new: same).
  309. DWORD NewFixedSize;
  310. DWORD NewMaxSize;
  311. DWORD NewEntryStringSize;
  312. LPDESC OldDataDesc16;
  313. LPDESC OldDataDesc32;
  314. LPDESC OldDataDescSmb;
  315. DWORD OldEntriesRead;
  316. DWORD OldFixedSize;
  317. LPVOID OldInfoArray = NULL;
  318. DWORD OldInfoArraySize;
  319. DWORD OldLevel;
  320. DWORD OldMaxInfoSize;
  321. DWORD OldTotalAvail;
  322. NET_API_STATUS Status; // Status of this actual API.
  323. NET_API_STATUS TempStatus; // Short-term status of random stuff.
  324. BOOL TryNullSession = TRUE; // Try null session (OK for Winball).
  325. BOOL TryEnum3; // Use NetServerEnum3 to remote server
  326. LPVOID OldInfoEntry = OldInfoArray;
  327. LPVOID NewInfoArray = NULL;
  328. DWORD NewInfoArraySize;
  329. LPVOID NewInfoEntry;
  330. LPVOID NewStringArea;
  331. // Make sure caller didn't get confused.
  332. NetpAssert(UncServerName != NULL);
  333. if (BufPtr == NULL) {
  334. return (ERROR_INVALID_PARAMETER);
  335. }
  336. // Level for enum is a subset of all possible server info levels, so
  337. // we have to check that here.
  338. if ( (Level != 100) && (Level != 101) ) {
  339. Status = ERROR_INVALID_LEVEL;
  340. goto Cleanup;
  341. }
  342. Status = RxGetServerInfoLevelEquivalent(
  343. Level, // from level
  344. TRUE, // from native
  345. TRUE, // to native
  346. & OldLevel, // output level
  347. & OldDataDesc16,
  348. & OldDataDesc32,
  349. & OldDataDescSmb,
  350. & NewMaxSize, // "from" max length
  351. & NewFixedSize, // "from" fixed length
  352. & NewEntryStringSize, // "from" string length
  353. & OldMaxInfoSize, // "to" max length
  354. & OldFixedSize, // "to" fixed length
  355. NULL, // don't need "to" string length
  356. NULL); // don't need to know if this is incomplete
  357. if (Status != NO_ERROR) {
  358. NetpAssert(Status != ERROR_INVALID_LEVEL); // Already checked subset!
  359. goto Cleanup;
  360. }
  361. //
  362. // Because downlevel servers don't support resume handles, and we don't
  363. // have a way to say "close this resume handle" even if we wanted to
  364. // emulate them here, we have to do everthing in one shot. So, the first
  365. // time around, we'll try using the caller's prefered maximum, but we
  366. // will enlarge that until we can get everything in one buffer.
  367. //
  368. //
  369. // Some downlevel servers (Sparta/WinBALL) don't like it if we ask for
  370. // 64K of data at a time, so we limit our initial request to 16K or so
  371. // and increase it if the actual data amount is larger than 16K.
  372. //
  373. // First time: try at most a reasonable amount (16K or so's worth),
  374. // but at least enough for one entire entry.
  375. NetpAdjustPreferedMaximum (
  376. // caller's request (for "new" strucs):
  377. (PrefMaxSize > INITIAL_MAX_SIZE ? INITIAL_MAX_SIZE : PrefMaxSize),
  378. NewMaxSize, // byte count per array element
  379. OVERHEAD, // zero bytes overhead at end of array
  380. NULL, // we'll compute byte counts ourselves.
  381. & EntryCount); // num of entries we can get.
  382. NetpAssert( EntryCount > 0 ); // Code below assumes as least 1 entry.
  383. //
  384. // If a FirstNameToReturn was passed in,
  385. // use the new NetServerEnum3 API.
  386. //
  387. // The assumption is that this routine will typically be called with a FirstNameToReturn
  388. // only if the NetServerEnum2 list is exhausted. There's certainly no requirement
  389. // that's true. So, below we revert to NetServerEnum2 if NetServerEnum3 isn't supported.
  390. //
  391. // On the other hand, we always use NetServerEnum2 to pick up the first part of the list
  392. // since it's supported by all servers.
  393. //
  394. TryEnum3 = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0' );
  395. //
  396. // Loop until we have enough memory or we die for some other reason.
  397. // Also loop trying null session first (for speedy Winball access), then
  398. // non-null session (required by Lanman).
  399. //
  400. do {
  401. //
  402. // Figure out how much memory we need.
  403. //
  404. OldInfoArraySize = (EntryCount * OldMaxInfoSize) + OVERHEAD;
  405. //
  406. // adjust the size to the maximum amount a down-level server
  407. // can handle
  408. //
  409. if (OldInfoArraySize > MAX_TRANSACT_RET_DATA_SIZE) {
  410. OldInfoArraySize = MAX_TRANSACT_RET_DATA_SIZE;
  411. }
  412. TryTheApi:
  413. //
  414. // Remote the API.
  415. // We'll let RxRemoteApi allocate the old info array for us.
  416. //
  417. Status = RxRemoteApi(
  418. TryEnum3 ? API_NetServerEnum3 : API_NetServerEnum2 , // api number
  419. (LPWSTR)UncServerName, // \\servername
  420. TryEnum3 ? REMSmb_NetServerEnum3_P : REMSmb_NetServerEnum2_P, // parm desc (SMB version)
  421. OldDataDesc16,
  422. OldDataDesc32,
  423. OldDataDescSmb,
  424. NULL, // no aux desc 16
  425. NULL, // no aux desc 32
  426. NULL, // no aux desc SMB
  427. (TryNullSession ? NO_PERMISSION_REQUIRED : 0) |
  428. ALLOCATE_RESPONSE |
  429. USE_SPECIFIC_TRANSPORT, // Next param is Xport name.
  430. TransportName,
  431. // rest of API's arguments in LM 2.x format:
  432. OldLevel, // sLevel: info level (old)
  433. & OldInfoArray, // pbBuffer: old info lvl array
  434. TryEnum3 ? MAX_TRANSACT_RET_DATA_SIZE : OldInfoArraySize, // cbBuffer: old info lvl array len
  435. & OldEntriesRead, // pcEntriesRead
  436. & OldTotalAvail, // pcTotalAvail
  437. ServerType, // flServerType
  438. Domain, // pszDomain (may be null ptr).
  439. FirstNameToReturn ); // Used only for NetServerEnum3
  440. //
  441. // There are a couple of situations where null session might not
  442. // have worked, and where it is worth retrying with non-null session.
  443. //
  444. if (TryNullSession) {
  445. //
  446. // Null session wouldn't have worked to LanMan, so try again if it
  447. // failed. (Winball would succeed on null session.)
  448. //
  449. if (Status == ERROR_ACCESS_DENIED) {
  450. TryNullSession = FALSE;
  451. goto TryTheApi;
  452. //
  453. // Another situation where null session might have failed...
  454. // wrong credentials. (LarryO says that the null session might
  455. // exhibit this, so let's give it a shot with non-null session.)
  456. //
  457. } else if (Status == ERROR_SESSION_CREDENTIAL_CONFLICT) {
  458. TryNullSession = FALSE;
  459. goto TryTheApi;
  460. }
  461. }
  462. //
  463. // If the server doesn't support the new API,
  464. // Try the old API.
  465. //
  466. if ( TryEnum3 ) {
  467. //
  468. // Unfortunately, NT 3.5x servers return ERROR_ACCESS_DENIED for bogus
  469. // API Numbers since they have a NULL session check prior to their API
  470. // Number range check.
  471. //
  472. if ( Status == ERROR_ACCESS_DENIED || // NT 3.5x with NULL session checking
  473. Status == ERROR_NOT_SUPPORTED ) { // Windows 95
  474. //
  475. // If the original caller is asking for this continuation,
  476. // we need to oblige him.
  477. // Fall back to Enum2
  478. //
  479. if ( !InternalContinuation ) {
  480. TryNullSession = TRUE;
  481. TryEnum3 = FALSE;
  482. goto TryTheApi;
  483. //
  484. // Otherwise, we know we've gotten all the data this server has to give.
  485. //
  486. // Just tell the caller there is more data, but we can't return it.
  487. //
  488. } else {
  489. Status = ERROR_MORE_DATA;
  490. *EntriesRead = 0;
  491. *TotalEntries = 0;
  492. goto Cleanup;
  493. }
  494. }
  495. //
  496. // Set OldInfoArraySize to the actual value we used above.
  497. //
  498. // We couldn't set the variable before this because we wanted to use the
  499. // original value in the case that we had to fall back to Enum2.
  500. //
  501. OldInfoArraySize = MAX_TRANSACT_RET_DATA_SIZE;
  502. }
  503. // If there is still an error and it is ERROR_CONNECTION_ACTIVE,
  504. // try the call with any transport, instead of specifying the transport.
  505. // We are needing to do this because of a widely seen scenario where
  506. // there is an existing SMB connection with an outstanding exchange
  507. // and so the call fails over that transport
  508. //
  509. if ( Status == ERROR_CONNECTION_ACTIVE ) {
  510. Status = RxRemoteApi(
  511. TryEnum3 ? API_NetServerEnum3 : API_NetServerEnum2 , // api number
  512. (LPWSTR)UncServerName, // \\servername
  513. TryEnum3 ? REMSmb_NetServerEnum3_P : REMSmb_NetServerEnum2_P, // parm desc (SMB version)
  514. OldDataDesc16,
  515. OldDataDesc32,
  516. OldDataDescSmb,
  517. NULL, // no aux desc 16
  518. NULL, // no aux desc 32
  519. NULL, // no aux desc SMB
  520. (TryNullSession ? NO_PERMISSION_REQUIRED : 0) |
  521. ALLOCATE_RESPONSE,
  522. // rest of API's arguments in LM 2.x format:
  523. OldLevel, // sLevel: info level (old)
  524. & OldInfoArray, // pbBuffer: old info lvl array
  525. TryEnum3 ? MAX_TRANSACT_RET_DATA_SIZE : OldInfoArraySize, // cbBuffer: old info lvl array len
  526. & OldEntriesRead, // pcEntriesRead
  527. & OldTotalAvail, // pcTotalAvail
  528. ServerType, // flServerType
  529. Domain, // pszDomain (may be null ptr).
  530. FirstNameToReturn ); // Used only for NetServerEnum3
  531. }
  532. //
  533. // If we still have an error at this point,
  534. // return it to the caller.
  535. //
  536. if ( Status != NERR_Success && Status != ERROR_MORE_DATA ) {
  537. goto Cleanup;
  538. }
  539. if ((OldEntriesRead == EntryCount) && (Status==ERROR_MORE_DATA) ) {
  540. // Bug in loop, or lower level code, or remote system?
  541. NetpKdPrint(( PREFIX_NETAPI
  542. "RxNetServerEnum: **WARNING** Got same sizes twice in "
  543. "a row; returning internal error.\n" ));
  544. Status = NERR_InternalError;
  545. goto Cleanup;
  546. }
  547. EntryCount = OldEntriesRead;
  548. *EntriesRead = EntryCount;
  549. *TotalEntries = OldTotalAvail;
  550. //
  551. // If the server returned ERROR_MORE_DATA, free the buffer and try
  552. // again. (Actually, if we already tried 64K, then forget it.)
  553. //
  554. NetpAssert( OldInfoArraySize <= MAX_TRANSACT_RET_DATA_SIZE );
  555. if (Status != ERROR_MORE_DATA) {
  556. break;
  557. } else if (OldInfoArraySize == MAX_TRANSACT_RET_DATA_SIZE ) {
  558. // Let the calling code handle this problem.
  559. break;
  560. } else if (OldEntriesRead == 0) {
  561. // We ran into WFW bug (always says ERROR_MORE_DATA, but 0 read).
  562. NetpKdPrint(( PREFIX_NETAPI
  563. "RxNetServerEnum: Downlevel returns 0 entries and says "
  564. "ERROR_MORE_DATA! Returning NERR_InternalError.\n" ));
  565. Status = NERR_InternalError;
  566. goto Cleanup;
  567. }
  568. //
  569. // Various versions of Windows For Workgroups (WFW) get entry count,
  570. // total available, and whether or not an array is returned, confused.
  571. // Attempt to protect ourselves from that...
  572. //
  573. if (EntryCount >= OldTotalAvail) {
  574. NetpKdPrint(( PREFIX_NETAPI
  575. "RxNetServerEnum: Downlevel says ERROR_MORE_DATA but "
  576. "entry count (" FORMAT_DWORD ") >= total ("
  577. FORMAT_DWORD ").\n", EntryCount, OldTotalAvail ));
  578. *EntriesRead = EntryCount;
  579. *TotalEntries = EntryCount;
  580. Status = NO_ERROR;
  581. break;
  582. }
  583. NetpAssert( EntryCount < OldTotalAvail );
  584. //
  585. // Free array, as it is too small anyway.
  586. //
  587. (void) NetApiBufferFree(OldInfoArray);
  588. OldInfoArray = NULL;
  589. //
  590. // Try again, resizing array to total.
  591. //
  592. EntryCount = OldTotalAvail;
  593. } while (Status == ERROR_MORE_DATA);
  594. ASSERT( Status == NERR_Success || Status == ERROR_MORE_DATA );
  595. //
  596. // Some versions of Windows For Workgroups (WFW) lie about entries read,
  597. // total available, and what they actually return. If we didn't get an
  598. // array, then the counts are useless.
  599. //
  600. if (OldInfoArray == NULL) {
  601. *EntriesRead = 0;
  602. *TotalEntries = 0;
  603. goto Cleanup;
  604. }
  605. if (*EntriesRead == 0) {
  606. goto Cleanup;
  607. }
  608. //
  609. // Convert array of structures from old info level to new.
  610. //
  611. // Skip any returned entries that are before the ones we want.
  612. //
  613. OldInfoEntry = OldInfoArray;
  614. while (EntryCount > 0) {
  615. IF_DEBUG(SERVER) {
  616. NetpKdPrint(( PREFIX_NETAPI "RxNetServerEnum: " FORMAT_DWORD
  617. " entries left.\n", EntryCount ));
  618. }
  619. //
  620. // Break out of loop if we need to return this entry.
  621. //
  622. if ( wcscmp( FirstNameToReturn, ((LPSERVER_INFO_0)OldInfoEntry)->sv0_name) <= 0 ) {
  623. break;
  624. }
  625. *EntriesRead -= 1;
  626. *TotalEntries -= 1;
  627. OldInfoEntry = NetpPointerPlusSomeBytes(
  628. OldInfoEntry, OldFixedSize);
  629. --EntryCount;
  630. }
  631. //
  632. // If there were no entries we actually wanted,
  633. // indicate so.
  634. //
  635. if ( *EntriesRead == 0 ) {
  636. goto Cleanup;
  637. }
  638. //
  639. // Compute the largest possible size of buffer we'll return.
  640. //
  641. // It is never larger than the number of entries available times the largest
  642. // possible structure size.
  643. //
  644. // It is never larger than the number of entries available times the fixed structure
  645. // size PLUS the maximum possible text returned from the remote server. For the
  646. // latter case, we assume that every byte the remote server returned us is an OEM
  647. // character that we translate to UNICODE.
  648. //
  649. // The second limit prevents us from allocating mondo large structures
  650. // when a large number of entries with short strings are returned.
  651. NewInfoArraySize = min(
  652. EntryCount * NewMaxSize,
  653. (EntryCount * NewFixedSize) + (OldInfoArraySize * sizeof(WCHAR))) + OVERHEAD;
  654. //
  655. // Alloc memory for new info level arrays.
  656. //
  657. TempStatus = NetApiBufferAllocate( NewInfoArraySize, & NewInfoArray );
  658. if (TempStatus != NO_ERROR) {
  659. Status = TempStatus;
  660. goto Cleanup;
  661. }
  662. NewStringArea = NetpPointerPlusSomeBytes(NewInfoArray,NewInfoArraySize);
  663. NewInfoEntry = NewInfoArray;
  664. while (EntryCount > 0) {
  665. IF_DEBUG(SERVER) {
  666. NetpKdPrint(( PREFIX_NETAPI "RxNetServerEnum: " FORMAT_DWORD
  667. " entries left.\n", EntryCount ));
  668. }
  669. TempStatus = NetpConvertServerInfo (
  670. OldLevel, // from level
  671. OldInfoEntry, // from info (fixed part)
  672. TRUE, // from native format
  673. Level, // to level
  674. NewInfoEntry, // to info (fixed part)
  675. NewFixedSize,
  676. NewEntryStringSize,
  677. TRUE, // to native format
  678. (LPTSTR *)&NewStringArea); // to string area (ptr updated)
  679. if (TempStatus != NO_ERROR) {
  680. Status = TempStatus;
  681. if (NewInfoArray){
  682. // free NewInfoArray since allocated & returning error rather then buffer.
  683. (void) NetApiBufferFree(NewInfoArray);
  684. }
  685. goto Cleanup;
  686. }
  687. NewInfoEntry = NetpPointerPlusSomeBytes( NewInfoEntry, NewFixedSize);
  688. OldInfoEntry = NetpPointerPlusSomeBytes( OldInfoEntry, OldFixedSize);
  689. --EntryCount;
  690. }
  691. *BufPtr = NewInfoArray;
  692. //
  693. // Free locally used resources and exit.
  694. //
  695. Cleanup:
  696. //
  697. // Reset Output parameters on error
  698. if ( Status != NERR_Success && Status != ERROR_MORE_DATA ) {
  699. *EntriesRead = 0;
  700. *TotalEntries = 0;
  701. }
  702. if (*EntriesRead == 0) {
  703. *BufPtr = NULL;
  704. }
  705. // Free old array
  706. if (OldInfoArray != NULL) {
  707. (void) NetApiBufferFree(OldInfoArray);
  708. }
  709. return (Status);
  710. } // RxNetServerEnumWorker
  711. NET_API_STATUS
  712. RxNetServerEnum (
  713. IN LPCWSTR UncServerName,
  714. IN LPCWSTR TransportName,
  715. IN DWORD Level,
  716. OUT LPBYTE *BufPtr,
  717. IN DWORD PrefMaxSize,
  718. OUT LPDWORD EntriesRead,
  719. OUT LPDWORD TotalEntries,
  720. IN DWORD ServerType,
  721. IN LPCWSTR Domain OPTIONAL,
  722. IN LPCWSTR FirstNameToReturn OPTIONAL
  723. )
  724. /*++
  725. Routine Description:
  726. RxNetServerEnumIntoTree calls RxNetServerEnumWorker repeatedly until it returns
  727. all entries or until at least PrefMaxSize data has been returned.
  728. One of the callers is EnumServersForTransport (on behalf of NetServerEnumEx). It
  729. depends on the fact that we return at least PrefMaxSize entries for this transport.
  730. Otherwise, NetServerEnumEx might return a last entry from a different transport even
  731. though there are entries on this transport with names that are less than lexically
  732. smaller names. Such entries on this transport would never be returned.
  733. Arguments:
  734. (Same as NetServerEnum, except UncServerName must not be null, and
  735. must not refer to the local computer.)
  736. FirstNameToReturn: Must be uppercase
  737. Passed name must be the canonical form of the name.
  738. Return Value:
  739. (Same as NetServerEnum.)
  740. --*/
  741. {
  742. NET_API_STATUS NetStatus;
  743. NET_API_STATUS TempNetStatus;
  744. ULONG BytesGatheredSoFar = 0;
  745. ULONG BytesDuplicated = 0;
  746. LPBYTE LocalBuffer = NULL;
  747. DWORD LocalEntriesRead;
  748. DWORD LocalTotalEntries;
  749. WCHAR LocalFirstNameToReturn[CNLEN+1];
  750. BOOLEAN InternalContinuation = FALSE;
  751. // Variable to build the returned information into.
  752. LPBYTE CurrentServerList = NULL;
  753. DWORD CurrentEntriesRead = 0;
  754. DWORD CurrentTotalEntries = 0;
  755. DWORD MaxSize;
  756. DWORD FixedSize;
  757. //
  758. // Initialization.
  759. //
  760. *TotalEntries = 0;
  761. *EntriesRead = 0;
  762. if ( FirstNameToReturn == NULL) {
  763. LocalFirstNameToReturn[0] = L'\0';
  764. } else {
  765. wcsncpy( LocalFirstNameToReturn, FirstNameToReturn, CNLEN+1 );
  766. LocalFirstNameToReturn[CNLEN] = L'\0';
  767. }
  768. //
  769. // Get information about the array returned from RxNetServerEnumWorker.
  770. //
  771. NetStatus = RxGetServerInfoLevelEquivalent(
  772. Level, // from level
  773. TRUE, // from native
  774. TRUE, // to native
  775. NULL,
  776. NULL,
  777. NULL,
  778. NULL,
  779. &MaxSize, // "from" max length
  780. &FixedSize, // "from" fixed length
  781. NULL, // "from" string length
  782. NULL, // "to" max length
  783. NULL, // "to" fixed length
  784. NULL, // don't need "to" string length
  785. NULL); // don't need to know if this is incomplete
  786. if (NetStatus != NO_ERROR) {
  787. goto Cleanup;
  788. }
  789. //
  790. // Loop calling the server getting more entries on each call.
  791. //
  792. for (;;) {
  793. //
  794. // Get the next chunk of data from the server.
  795. // Return an extra entry to account for FirstNameToReturn being returned on the
  796. // previous call.
  797. //
  798. NetStatus = RxNetServerEnumWorker(
  799. UncServerName,
  800. TransportName,
  801. Level,
  802. &LocalBuffer,
  803. PrefMaxSize - BytesGatheredSoFar + BytesDuplicated,
  804. &LocalEntriesRead,
  805. &LocalTotalEntries,
  806. ServerType,
  807. Domain,
  808. LocalFirstNameToReturn,
  809. InternalContinuation );
  810. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  811. goto Cleanup;
  812. }
  813. //
  814. // If we have as many entries as the server has to give,
  815. // tell our caller.
  816. //
  817. // This is the case where the server doesn't support the ENUM3 protocol.
  818. //
  819. if ( NetStatus == ERROR_MORE_DATA && LocalEntriesRead == 0 ) {
  820. goto Cleanup;
  821. }
  822. //
  823. // If there is more data available,
  824. // and we only want a limited amount of data.
  825. // Compute the amount of data returned on this call.
  826. //
  827. // Determine the number of bytes to ask for on the next call.
  828. //
  829. // If our caller asked for all entries,
  830. // simply ask for all entries from the server.
  831. //
  832. if ( NetStatus == ERROR_MORE_DATA && PrefMaxSize != 0xFFFFFFFF ) {
  833. DWORD i;
  834. LPBYTE Current = LocalBuffer;
  835. //
  836. // Loop through the entries returned on the current call
  837. // computing the size returned.
  838. //
  839. for ( i=0; i<LocalEntriesRead; i++) {
  840. //
  841. // Add the size of the current entry.
  842. //
  843. BytesGatheredSoFar += FixedSize;
  844. if ( ((LPSERVER_INFO_100)Current)->sv100_name != NULL ) {
  845. BytesGatheredSoFar = (wcslen(((LPSERVER_INFO_100)Current)->sv100_name) + 1) * sizeof(WCHAR);
  846. }
  847. if ( Level == 101 &&
  848. ((LPSERVER_INFO_101)Current)->sv101_comment != NULL ) {
  849. BytesGatheredSoFar += (wcslen(((LPSERVER_INFO_101)Current)->sv101_comment) + 1) * sizeof(WCHAR);
  850. }
  851. //
  852. // Move to the next entry.
  853. //
  854. Current += FixedSize;
  855. }
  856. //
  857. // Account for the fact that the first entry returned is identical to the
  858. // last entry returned on the previous call.
  859. BytesDuplicated = MaxSize;
  860. }
  861. //
  862. // Append the new server list to the one we've been collecting
  863. //
  864. TempNetStatus = AppendServerList(
  865. &CurrentServerList,
  866. &CurrentEntriesRead,
  867. &CurrentTotalEntries,
  868. Level,
  869. FixedSize,
  870. &LocalBuffer,
  871. LocalEntriesRead,
  872. LocalTotalEntries );
  873. if ( TempNetStatus != NERR_Success ) {
  874. NetStatus = TempNetStatus;
  875. goto Cleanup;
  876. }
  877. //
  878. // Free the buffer if AppendServerList didn't already.
  879. //
  880. // Now free up the remaining parts of the list.
  881. //
  882. if (LocalBuffer != NULL) {
  883. NetApiBufferFree(LocalBuffer);
  884. LocalBuffer = NULL;
  885. }
  886. //
  887. // If we've returned everything from the server,
  888. // simply return now.
  889. //
  890. if ( NetStatus == NERR_Success ) {
  891. goto Cleanup;
  892. }
  893. //
  894. // Handle calling the server again to get the next several entries.
  895. //
  896. //
  897. // Pass the name of the next server to return
  898. //
  899. wcscpy( LocalFirstNameToReturn,
  900. ((LPSERVER_INFO_100)(CurrentServerList + (CurrentEntriesRead-1) * FixedSize))->sv100_name );
  901. InternalContinuation = TRUE;
  902. //
  903. // If we've already gathered all the bytes we need,
  904. // we're done.
  905. //
  906. // If the worker routine returned what we needed, it'll be a few bytes under
  907. // PrefMaxSize. So stop here if we're within one element of our goal.
  908. //
  909. if ( BytesGatheredSoFar + BytesDuplicated >= PrefMaxSize ) {
  910. NetStatus = ERROR_MORE_DATA;
  911. goto Cleanup;
  912. }
  913. }
  914. Cleanup:
  915. //
  916. // Return the collected data to the caller.
  917. //
  918. if ( NetStatus == NERR_Success || NetStatus == ERROR_MORE_DATA ) {
  919. //
  920. // Return the entries.
  921. //
  922. *BufPtr = CurrentServerList;
  923. CurrentServerList = NULL;
  924. *EntriesRead = CurrentEntriesRead;
  925. //
  926. // Adjust TotalEntries returned for reality.
  927. //
  928. if ( NetStatus == NERR_Success ) {
  929. *TotalEntries = *EntriesRead;
  930. } else {
  931. *TotalEntries = max( CurrentTotalEntries, CurrentEntriesRead + 1 );
  932. }
  933. }
  934. //
  935. // Free locally used resources
  936. //
  937. if (LocalBuffer != NULL) {
  938. NetApiBufferFree(LocalBuffer);
  939. LocalBuffer = NULL;
  940. }
  941. if ( CurrentServerList != NULL ) {
  942. NetApiBufferFree( CurrentServerList );
  943. }
  944. return NetStatus;
  945. }