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.

3197 lines
83 KiB

  1. /*++
  2. Copyright (c) 1991-1993 Microsoft Corporation
  3. Module Name:
  4. device.c
  5. Abstract:
  6. This module contains the support routines for the APIs that call
  7. into the NetWare redirector
  8. Author:
  9. Rita Wong (ritaw) 20-Feb-1991
  10. Colin Watson (colinw) 30-Dec-1992
  11. Revision History:
  12. --*/
  13. #include <nw.h>
  14. #include <nwcons.h>
  15. #include <nwxchg.h>
  16. #include <nwapi32.h>
  17. #include <nwstatus.h>
  18. #include <nwmisc.h>
  19. #include <nwcons.h>
  20. #include <nds.h>
  21. #include <svcguid.h>
  22. #include <tdi.h>
  23. #include <nwreg.h>
  24. #define NW_LINKAGE_REGISTRY_PATH L"NWCWorkstation\\Linkage"
  25. #define NW_BIND_VALUENAME L"Bind"
  26. #define TWO_KB 2048
  27. #define EIGHT_KB 8192
  28. #define EXTRA_BYTES 256
  29. #define TREECHAR L'*'
  30. #define BUFFSIZE 1024
  31. //-------------------------------------------------------------------//
  32. // //
  33. // Local Function Prototypes //
  34. // //
  35. //-------------------------------------------------------------------//
  36. STATIC
  37. NTSTATUS
  38. BindToEachTransport(
  39. IN PWSTR ValueName,
  40. IN ULONG ValueType,
  41. IN PVOID ValueData,
  42. IN ULONG ValueLength,
  43. IN PVOID Context,
  44. IN PVOID EntryContext
  45. );
  46. DWORD
  47. NwBindTransport(
  48. IN LPWSTR TransportName,
  49. IN DWORD QualityOfService
  50. );
  51. DWORD
  52. GetConnectedBinderyServers(
  53. OUT LPNW_ENUM_CONTEXT ContextHandle
  54. );
  55. DWORD
  56. GetTreeEntriesFromBindery(
  57. OUT LPNW_ENUM_CONTEXT ContextHandle
  58. );
  59. DWORD
  60. NwGetConnectionStatus(
  61. IN LPWSTR pszServerName,
  62. IN OUT PDWORD_PTR ResumeKey,
  63. OUT LPBYTE *Buffer,
  64. OUT PDWORD EntriesRead
  65. );
  66. VOID
  67. GetLuid(
  68. IN OUT PLUID plogonid
  69. );
  70. VOID
  71. GetNearestDirServer(
  72. IN LPWSTR TreeName,
  73. OUT LPDWORD lpdwReplicaAddressSize,
  74. OUT LPBYTE lpReplicaAddress
  75. );
  76. VOID
  77. GetPreferredServerAddress(
  78. IN LPWSTR PreferredServerName,
  79. OUT LPDWORD lpdwReplicaAddressSize,
  80. OUT LPBYTE lpReplicaAddress
  81. );
  82. BOOL
  83. NwpCompareTreeNames(
  84. LPWSTR lpServiceInstanceName,
  85. LPWSTR lpTreeName
  86. );
  87. //-------------------------------------------------------------------//
  88. // //
  89. // Global variables //
  90. // //
  91. //-------------------------------------------------------------------//
  92. //
  93. // Handle to the Redirector FSD
  94. //
  95. STATIC HANDLE RedirDeviceHandle = NULL;
  96. //
  97. // Redirector name in NT string format
  98. //
  99. STATIC UNICODE_STRING RedirDeviceName;
  100. extern BOOL NwLUIDDeviceMapsEnabled;
  101. DWORD
  102. NwInitializeRedirector(
  103. VOID
  104. )
  105. /*++
  106. Routine Description:
  107. This routine initializes the NetWare redirector FSD.
  108. Arguments:
  109. None.
  110. Return Value:
  111. NO_ERROR or reason for failure.
  112. --*/
  113. {
  114. DWORD error;
  115. NWR_REQUEST_PACKET Rrp;
  116. //
  117. // Initialize global handles
  118. //
  119. RedirDeviceHandle = NULL;
  120. //
  121. // Initialize the global NT-style redirector device name string.
  122. //
  123. RtlInitUnicodeString(&RedirDeviceName, DD_NWFS_DEVICE_NAME_U);
  124. //
  125. // Load driver
  126. //
  127. error = NwLoadOrUnloadDriver(TRUE);
  128. if (error != NO_ERROR && error != ERROR_SERVICE_ALREADY_RUNNING) {
  129. return error;
  130. }
  131. if ((error = NwOpenRedirector()) != NO_ERROR) {
  132. //
  133. // Unload the redirector driver
  134. //
  135. (void) NwLoadOrUnloadDriver(FALSE);
  136. return error;
  137. }
  138. //
  139. // Send the start FSCTL to the redirector
  140. //
  141. Rrp.Version = REQUEST_PACKET_VERSION;
  142. return NwRedirFsControl(
  143. RedirDeviceHandle,
  144. FSCTL_NWR_START,
  145. &Rrp,
  146. sizeof(NWR_REQUEST_PACKET),
  147. NULL,
  148. 0,
  149. NULL
  150. );
  151. }
  152. DWORD
  153. NwOpenRedirector(
  154. VOID
  155. )
  156. /*++
  157. Routine Description:
  158. This routine opens the NT NetWare redirector FSD.
  159. Arguments:
  160. None.
  161. Return Value:
  162. NO_ERROR or reason for failure.
  163. --*/
  164. {
  165. return RtlNtStatusToDosError(
  166. NwOpenHandle(&RedirDeviceName, FALSE, &RedirDeviceHandle)
  167. );
  168. }
  169. DWORD
  170. NwShutdownRedirector(
  171. VOID
  172. )
  173. /*++
  174. Routine Description:
  175. This routine stops the NetWare Redirector FSD and unloads it if
  176. possible.
  177. Arguments:
  178. None.
  179. Return Value:
  180. NO_ERROR or ERROR_REDIRECTOR_HAS_OPEN_HANDLES
  181. --*/
  182. {
  183. NWR_REQUEST_PACKET Rrp;
  184. DWORD error;
  185. Rrp.Version = REQUEST_PACKET_VERSION;
  186. error = NwRedirFsControl(
  187. RedirDeviceHandle,
  188. FSCTL_NWR_STOP,
  189. &Rrp,
  190. sizeof(NWR_REQUEST_PACKET),
  191. NULL,
  192. 0,
  193. NULL
  194. );
  195. (void) NtClose(RedirDeviceHandle);
  196. RedirDeviceHandle = NULL;
  197. if (error != ERROR_REDIRECTOR_HAS_OPEN_HANDLES) {
  198. //
  199. // Unload the redirector only if all its open handles are closed.
  200. //
  201. (void) NwLoadOrUnloadDriver(FALSE);
  202. }
  203. return error;
  204. }
  205. DWORD
  206. NwLoadOrUnloadDriver(
  207. BOOL Load
  208. )
  209. /*++
  210. Routine Description:
  211. This routine loads or unloads the NetWare redirector driver.
  212. Arguments:
  213. Load - Supplies the flag which if TRUE load the driver; otherwise
  214. unloads the driver.
  215. Return Value:
  216. NO_ERROR or reason for failure.
  217. --*/
  218. {
  219. LPWSTR DriverRegistryName;
  220. UNICODE_STRING DriverRegistryString;
  221. NTSTATUS ntstatus;
  222. BOOLEAN WasEnabled;
  223. DriverRegistryName = (LPWSTR) LocalAlloc(
  224. LMEM_FIXED,
  225. (UINT) (sizeof(SERVICE_REGISTRY_KEY) +
  226. (wcslen(NW_DRIVER_NAME) *
  227. sizeof(WCHAR)))
  228. );
  229. if (DriverRegistryName == NULL) {
  230. return ERROR_NOT_ENOUGH_MEMORY;
  231. }
  232. ntstatus = RtlAdjustPrivilege(
  233. SE_LOAD_DRIVER_PRIVILEGE,
  234. TRUE,
  235. FALSE,
  236. &WasEnabled
  237. );
  238. if (! NT_SUCCESS(ntstatus)) {
  239. (void) LocalFree(DriverRegistryName);
  240. return RtlNtStatusToDosError(ntstatus);
  241. }
  242. wcscpy(DriverRegistryName, SERVICE_REGISTRY_KEY);
  243. wcscat(DriverRegistryName, NW_DRIVER_NAME);
  244. RtlInitUnicodeString(&DriverRegistryString, DriverRegistryName);
  245. if (Load) {
  246. ntstatus = NtLoadDriver(&DriverRegistryString);
  247. }
  248. else {
  249. ntstatus = NtUnloadDriver(&DriverRegistryString);
  250. }
  251. (void) RtlAdjustPrivilege(
  252. SE_LOAD_DRIVER_PRIVILEGE,
  253. WasEnabled,
  254. FALSE,
  255. &WasEnabled
  256. );
  257. (void) LocalFree(DriverRegistryName);
  258. if (Load) {
  259. if (ntstatus != STATUS_SUCCESS && ntstatus != STATUS_IMAGE_ALREADY_LOADED) {
  260. LPWSTR SubString[1];
  261. KdPrint(("NWWORKSTATION: NtLoadDriver returned %08lx\n", ntstatus));
  262. SubString[0] = NW_DRIVER_NAME;
  263. NwLogEvent(
  264. EVENT_NWWKSTA_CANT_CREATE_REDIRECTOR,
  265. 1,
  266. SubString,
  267. ntstatus
  268. );
  269. }
  270. }
  271. if (ntstatus == STATUS_OBJECT_NAME_NOT_FOUND) {
  272. return ERROR_FILE_NOT_FOUND;
  273. }
  274. return NwMapStatus(ntstatus);
  275. }
  276. DWORD
  277. NwRedirFsControl(
  278. IN HANDLE FileHandle,
  279. IN ULONG RedirControlCode,
  280. IN PNWR_REQUEST_PACKET Rrp,
  281. IN ULONG RrpLength,
  282. IN PVOID SecondBuffer OPTIONAL,
  283. IN ULONG SecondBufferLength,
  284. OUT PULONG Information OPTIONAL
  285. )
  286. /*++
  287. Routine Description:
  288. Arguments:
  289. FileHandle - Supplies a handle to the file or device on which the service
  290. is being performed.
  291. RedirControlCode - Supplies the NtFsControlFile function code given to
  292. the redirector.
  293. Rrp - Supplies the redirector request packet.
  294. RrpLength - Supplies the length of the redirector request packet.
  295. SecondBuffer - Supplies the second buffer in call to NtFsControlFile.
  296. SecondBufferLength - Supplies the length of the second buffer.
  297. Information - Returns the information field of the I/O status block.
  298. Return Value:
  299. NO_ERROR or reason for failure.
  300. --*/
  301. {
  302. NTSTATUS ntstatus;
  303. IO_STATUS_BLOCK IoStatusBlock;
  304. //
  305. // Send the request to the Redirector FSD.
  306. //
  307. ntstatus = NtFsControlFile(
  308. FileHandle,
  309. NULL,
  310. NULL,
  311. NULL,
  312. &IoStatusBlock,
  313. RedirControlCode,
  314. (PVOID) Rrp,
  315. RrpLength,
  316. SecondBuffer,
  317. SecondBufferLength
  318. );
  319. if (ntstatus == STATUS_SUCCESS) {
  320. ntstatus = IoStatusBlock.Status;
  321. }
  322. if (ARGUMENT_PRESENT(Information)) {
  323. *Information = (ULONG) IoStatusBlock.Information;
  324. }
  325. #if DBG
  326. if (ntstatus != STATUS_SUCCESS) {
  327. IF_DEBUG(DEVICE) {
  328. KdPrint(("NWWORKSTATION: fsctl to redir returns %08lx\n", ntstatus));
  329. }
  330. }
  331. #endif
  332. return NwMapStatus(ntstatus);
  333. }
  334. DWORD
  335. NwBindToTransports(
  336. VOID
  337. )
  338. /*++
  339. Routine Description:
  340. This routine binds to every transport specified under the linkage
  341. key of the NetWare Workstation service.
  342. Arguments:
  343. None.
  344. Return Value:
  345. NET_API_STATUS - success/failure of the operation.
  346. --*/
  347. {
  348. NTSTATUS ntstatus;
  349. PRTL_QUERY_REGISTRY_TABLE QueryTable;
  350. ULONG NumberOfBindings = 0;
  351. //
  352. // Ask the RTL to call us back for each subvalue in the MULTI_SZ
  353. // value \NWCWorkstation\Linkage\Bind.
  354. //
  355. if ((QueryTable = (PVOID) LocalAlloc(
  356. LMEM_ZEROINIT,
  357. sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
  358. )) == NULL) {
  359. return ERROR_NOT_ENOUGH_MEMORY;
  360. }
  361. QueryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) BindToEachTransport;
  362. QueryTable[0].Flags = 0;
  363. QueryTable[0].Name = NW_BIND_VALUENAME;
  364. QueryTable[0].EntryContext = NULL;
  365. QueryTable[0].DefaultType = REG_NONE;
  366. QueryTable[0].DefaultData = NULL;
  367. QueryTable[0].DefaultLength = 0;
  368. QueryTable[1].QueryRoutine = NULL;
  369. QueryTable[1].Flags = 0;
  370. QueryTable[1].Name = NULL;
  371. ntstatus = RtlQueryRegistryValues(
  372. RTL_REGISTRY_SERVICES,
  373. NW_LINKAGE_REGISTRY_PATH,
  374. QueryTable,
  375. &NumberOfBindings,
  376. NULL
  377. );
  378. (void) LocalFree((HLOCAL) QueryTable);
  379. //
  380. // If failed to bind to any transports, the workstation will
  381. // not start.
  382. //
  383. if (! NT_SUCCESS(ntstatus)) {
  384. #if DBG
  385. IF_DEBUG(INIT) {
  386. KdPrint(("NwBindToTransports: RtlQueryRegistryValues failed: "
  387. "%lx\n", ntstatus));
  388. }
  389. #endif
  390. return RtlNtStatusToDosError(ntstatus);
  391. }
  392. if (NumberOfBindings == 0) {
  393. #if 0
  394. //
  395. // tommye - MS 24187 / MCS 255
  396. //
  397. //
  398. // We don't want to log an event unnecessarily and panic the user that
  399. // G/CSNW could not bind. This could have been caused by the user unbinding
  400. // G/CSNW and rebooting.
  401. //
  402. NwLogEvent(
  403. EVENT_NWWKSTA_NO_TRANSPORTS,
  404. 0,
  405. NULL,
  406. NO_ERROR
  407. );
  408. #endif
  409. KdPrint(("NWWORKSTATION: NwBindToTransports: could not bind "
  410. "to any transport\n"));
  411. return ERROR_INVALID_PARAMETER;
  412. }
  413. return NO_ERROR;
  414. }
  415. STATIC
  416. NTSTATUS
  417. BindToEachTransport(
  418. IN PWSTR ValueName,
  419. IN ULONG ValueType,
  420. IN PVOID ValueData,
  421. IN ULONG ValueLength,
  422. IN PVOID Context,
  423. IN PVOID EntryContext
  424. )
  425. {
  426. DWORD error;
  427. LPDWORD NumberOfBindings = Context;
  428. LPWSTR SubStrings[2];
  429. static DWORD QualityOfService = 65536;
  430. UNREFERENCED_PARAMETER(ValueName);
  431. UNREFERENCED_PARAMETER(ValueLength);
  432. UNREFERENCED_PARAMETER(EntryContext);
  433. //
  434. // The value type must be REG_SZ (translated from REG_MULTI_SZ by
  435. // the RTL).
  436. //
  437. if (ValueType != REG_SZ) {
  438. SubStrings[0] = ValueName;
  439. SubStrings[1] = NW_LINKAGE_REGISTRY_PATH;
  440. NwLogEvent(
  441. EVENT_NWWKSTA_INVALID_REGISTRY_VALUE,
  442. 2,
  443. SubStrings,
  444. NO_ERROR
  445. );
  446. KdPrint(("NWWORKSTATION: Skipping invalid value %ws\n", ValueName));
  447. return STATUS_SUCCESS;
  448. }
  449. //
  450. // The value data is the name of the transport device object.
  451. //
  452. //
  453. // Bind to the transport.
  454. //
  455. #if DBG
  456. IF_DEBUG(INIT) {
  457. KdPrint(("NWWORKSTATION: Binding to transport %ws with QOS %lu\n",
  458. ValueData, QualityOfService));
  459. }
  460. #endif
  461. error = NwBindTransport(ValueData, QualityOfService--);
  462. if (error != NO_ERROR) {
  463. //
  464. // If failed to bind to one transport, don't fail starting yet.
  465. // Try other transports.
  466. //
  467. SubStrings[0] = ValueData;
  468. NwLogEvent(
  469. EVENT_NWWKSTA_CANT_BIND_TO_TRANSPORT,
  470. 1,
  471. SubStrings,
  472. error
  473. );
  474. }
  475. else {
  476. (*NumberOfBindings)++;
  477. }
  478. return STATUS_SUCCESS;
  479. }
  480. DWORD
  481. NwBindTransport(
  482. IN LPWSTR TransportName,
  483. IN DWORD QualityOfService
  484. )
  485. /*++
  486. Routine Description:
  487. This function binds the specified transport to the redirector
  488. and the datagram receiver.
  489. NOTE: The transport name length pass to the redirector and
  490. datagram receiver is the number of bytes.
  491. Arguments:
  492. TransportName - Supplies the name of the transport to bind to.
  493. QualityOfService - Supplies a value which specifies the search
  494. order of the transport with respect to other transports. The
  495. highest value is searched first.
  496. Return Value:
  497. NO_ERROR or reason for failure.
  498. --*/
  499. {
  500. DWORD status;
  501. DWORD RequestPacketSize;
  502. DWORD TransportNameSize = wcslen(TransportName) * sizeof(WCHAR);
  503. PNWR_REQUEST_PACKET Rrp;
  504. //
  505. // Size of request packet buffer
  506. //
  507. RequestPacketSize = TransportNameSize + sizeof(NWR_REQUEST_PACKET);
  508. //
  509. // Allocate memory for redirector/datagram receiver request packet
  510. //
  511. if ((Rrp = (PVOID) LocalAlloc(LMEM_ZEROINIT, (UINT) RequestPacketSize)) == NULL) {
  512. return ERROR_NOT_ENOUGH_MEMORY;
  513. }
  514. //
  515. // Get redirector to bind to transport
  516. //
  517. Rrp->Version = REQUEST_PACKET_VERSION;
  518. Rrp->Parameters.Bind.QualityOfService = QualityOfService;
  519. Rrp->Parameters.Bind.TransportNameLength = TransportNameSize;
  520. wcscpy((LPWSTR) Rrp->Parameters.Bind.TransportName, TransportName);
  521. if ((status = NwRedirFsControl(
  522. RedirDeviceHandle,
  523. FSCTL_NWR_BIND_TO_TRANSPORT,
  524. Rrp,
  525. RequestPacketSize,
  526. NULL,
  527. 0,
  528. NULL
  529. )) != NO_ERROR) {
  530. KdPrint(("NWWORKSTATION: NwBindTransport fsctl to bind to transport %ws failed\n",
  531. TransportName));
  532. }
  533. (void) LocalFree((HLOCAL) Rrp);
  534. return status;
  535. }
  536. DWORD
  537. NwGetCallerLuid (
  538. IN OUT PLUID pLuid
  539. )
  540. /*++
  541. Routine Description:
  542. Retrieves the caller's LUID from the effective access_token
  543. The effective access_token will be the thread's token if
  544. impersonating, else the process' token
  545. Arguments:
  546. pLuid [IN OUT] - pointer to a buffer to hold the LUID
  547. Return Value:
  548. STATUS_SUCCESS - operations successful, did not encounter any errors
  549. STATUS_INVALID_PARAMETER - pLuid is NULL
  550. STATUS_NO_TOKEN - could not find a token for the user
  551. appropriate NTSTATUS code - an unexpected error encountered
  552. --*/
  553. {
  554. TOKEN_STATISTICS TokenStats;
  555. HANDLE hToken = NULL;
  556. DWORD dwLength = 0;
  557. NTSTATUS Status;
  558. ULONG DosError;
  559. if( (pLuid == NULL) || (sizeof(*pLuid) != sizeof(LUID)) ) {
  560. return( STATUS_INVALID_PARAMETER );
  561. }
  562. //
  563. // Get the access token
  564. // Try to get the impersonation token, else the primary token
  565. //
  566. Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_READ, TRUE, &hToken );
  567. if( Status == STATUS_NO_TOKEN ) {
  568. Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_READ, &hToken );
  569. }
  570. if( NT_SUCCESS(Status) ) {
  571. //
  572. // Query the LUID for the user.
  573. //
  574. Status = NtQueryInformationToken( hToken,
  575. TokenStatistics,
  576. &TokenStats,
  577. sizeof(TokenStats),
  578. &dwLength );
  579. if( NT_SUCCESS(Status) ) {
  580. RtlCopyLuid( pLuid, &(TokenStats.AuthenticationId) );
  581. }
  582. }
  583. if( hToken != NULL ) {
  584. NtClose( hToken );
  585. }
  586. DosError = RtlNtStatusToDosError(Status);
  587. return( (DWORD)DosError );
  588. }
  589. DWORD
  590. NwCreateTreeConnectName(
  591. IN LPWSTR UncName,
  592. IN LPWSTR LocalName OPTIONAL,
  593. OUT PUNICODE_STRING TreeConnectStr
  594. )
  595. /*++
  596. Routine Description:
  597. This function replaces \\ with \Device\NwRdr\LocalName:\ in the
  598. UncName to form the NT-style tree connection name. LocalName:\ is part
  599. of the tree connection name only if LocalName is specified. A buffer
  600. is allocated by this function and returned as the output string.
  601. Arguments:
  602. UncName - Supplies the UNC name of the shared resource.
  603. LocalName - Supplies the local device name for the redirection.
  604. TreeConnectStr - Returns a string with a newly allocated buffer that
  605. contains the NT-style tree connection name.
  606. Return Value:
  607. NO_ERROR - the operation was successful.
  608. ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
  609. --*/
  610. {
  611. WCHAR LUIDBuffer[NW_MAX_LOGON_ID_LEN];
  612. DWORD UncNameLength = wcslen(UncName);
  613. LUID CallerLuid;
  614. BOOLEAN UseLUID;
  615. //UseLUID = (ARGUMENT_PRESENT(LocalName) && NwLUIDDeviceMapsEnabled);
  616. //
  617. // Temporary disable passing the LUID until LUID support is added in
  618. // the nwrdr.sys for parsing the device name
  619. //
  620. UseLUID = FALSE;
  621. //
  622. // Initialize tree connect string maximum length to hold
  623. // If LUID DosDevices enabled && LocalName Specified,
  624. // \Device\NwRdr\LocalName:XXXXXXXXxxxxxxxx\Server\Volume\Path
  625. // XXXXXXXX - LUID.HighPart
  626. // xxxxxxxx - LUID.LowPart
  627. // else
  628. // \Device\NwRdr\LocalName:\Server\Volume\Path
  629. //
  630. if( UseLUID ) {
  631. DWORD DosError;
  632. DosError = NwGetCallerLuid(&CallerLuid);
  633. if( DosError != NO_ERROR) {
  634. return DosError;
  635. }
  636. }
  637. TreeConnectStr->MaximumLength = RedirDeviceName.Length +
  638. sizeof(WCHAR) + // For '\'
  639. (ARGUMENT_PRESENT(LocalName) ? (wcslen(LocalName) * sizeof(WCHAR)) : 0) +
  640. (UseLUID ? NW_MAX_LOGON_ID_LEN * sizeof(WCHAR): 0) +
  641. (USHORT) (UncNameLength * sizeof(WCHAR)); // Includes '\' and
  642. // term char
  643. if ((TreeConnectStr->Buffer = (PWSTR) LocalAlloc(
  644. LMEM_ZEROINIT,
  645. (UINT) TreeConnectStr->MaximumLength
  646. )) == NULL) {
  647. KdPrint(("NWWORKSTATION: NwCreateTreeConnectName LocalAlloc failed %lu\n",
  648. GetLastError()));
  649. return ERROR_NOT_ENOUGH_MEMORY;
  650. }
  651. //
  652. // Copy \Device\NwRdr
  653. //
  654. RtlCopyUnicodeString(TreeConnectStr, &RedirDeviceName);
  655. //
  656. // Concatenate \LocalName:
  657. //
  658. if (ARGUMENT_PRESENT(LocalName)) {
  659. wcscat(TreeConnectStr->Buffer, L"\\");
  660. TreeConnectStr->Length += sizeof(WCHAR);
  661. wcscat(TreeConnectStr->Buffer, LocalName);
  662. TreeConnectStr->Length += (USHORT) (wcslen(LocalName) * sizeof(WCHAR));
  663. //
  664. // Concatenate the caller's LUID
  665. //
  666. if( UseLUID ) {
  667. _snwprintf( LUIDBuffer,
  668. sizeof(LUIDBuffer)/sizeof(WCHAR),
  669. L"%08x%08x",
  670. CallerLuid.HighPart,
  671. CallerLuid.LowPart );
  672. wcscat(TreeConnectStr->Buffer, LUIDBuffer);
  673. TreeConnectStr->Length += (USHORT) (wcslen(LUIDBuffer) * sizeof(WCHAR));
  674. }
  675. }
  676. //
  677. // Concatenate \Server\Volume\Path
  678. //
  679. wcscat(TreeConnectStr->Buffer, &UncName[1]);
  680. TreeConnectStr->Length += (USHORT) ((UncNameLength - 1) * sizeof(WCHAR));
  681. #if DBG
  682. IF_DEBUG(CONNECT) {
  683. KdPrint(("NWWORKSTATION: NwCreateTreeConnectName %ws, maxlength %u, length %u\n",
  684. TreeConnectStr->Buffer, TreeConnectStr->MaximumLength,
  685. TreeConnectStr->Length));
  686. }
  687. #endif
  688. return NO_ERROR;
  689. }
  690. DWORD
  691. NwOpenCreateConnection(
  692. IN PUNICODE_STRING TreeConnectionName,
  693. IN LPWSTR UserName OPTIONAL,
  694. IN LPWSTR Password OPTIONAL,
  695. IN LPWSTR UncName,
  696. IN ACCESS_MASK DesiredAccess,
  697. IN ULONG CreateDisposition,
  698. IN ULONG CreateOptions,
  699. IN ULONG ConnectionType,
  700. OUT PHANDLE TreeConnectionHandle,
  701. OUT PULONG_PTR Information OPTIONAL
  702. )
  703. /*++
  704. Routine Description:
  705. This function asks the redirector to either open an existing tree
  706. connection (CreateDisposition == FILE_OPEN), or create a new tree
  707. connection if one does not exist (CreateDisposition == FILE_CREATE).
  708. The password and user name passed to the redirector via the EA buffer
  709. in the NtCreateFile call. The EA buffer is NULL if neither password
  710. or user name is specified.
  711. The redirector expects the EA descriptor strings to be in ANSI
  712. but the password and username themselves are in Unicode.
  713. Arguments:
  714. TreeConnectionName - Supplies the name of the tree connection in NT-style
  715. file name format: \Device\NwRdr\Server\Volume\Directory
  716. UserName - Supplies the user name to create the tree connection with.
  717. Password - Supplies the password to create the tree connection with.
  718. DesiredAccess - Supplies the access need on the connection handle.
  719. CreateDisposition - Supplies the create disposition value to either
  720. open or create the tree connection.
  721. CreateOptions - Supplies the options used when creating or opening
  722. the tree connection.
  723. ConnectionType - Supplies the type of the connection (DISK, PRINT,
  724. or ANY).
  725. TreeConnectionHandle - Returns the handle to the tree connection
  726. created/opened by the redirector.
  727. Information - Returns the information field of the I/O status block.
  728. Return Value:
  729. NO_ERROR or reason for failure.
  730. --*/
  731. {
  732. DWORD status;
  733. NTSTATUS ntstatus;
  734. OBJECT_ATTRIBUTES UncNameAttributes;
  735. IO_STATUS_BLOCK IoStatusBlock;
  736. PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
  737. PFILE_FULL_EA_INFORMATION Ea;
  738. ULONG EaBufferSize = 0;
  739. UCHAR EaNamePasswordSize = (UCHAR) (ROUND_UP_COUNT(
  740. strlen(EA_NAME_PASSWORD) + sizeof(CHAR),
  741. ALIGN_WCHAR
  742. ) - sizeof(CHAR));
  743. UCHAR EaNameUserNameSize = (UCHAR) (ROUND_UP_COUNT(
  744. strlen(EA_NAME_USERNAME) + sizeof(CHAR),
  745. ALIGN_WCHAR
  746. ) - sizeof(CHAR));
  747. UCHAR EaNameTypeSize = (UCHAR) (ROUND_UP_COUNT(
  748. strlen(EA_NAME_TYPE) + sizeof(CHAR),
  749. ALIGN_DWORD
  750. ) - sizeof(CHAR));
  751. USHORT PasswordSize = 0;
  752. USHORT UserNameSize = 0;
  753. USHORT TypeSize = sizeof(ULONG);
  754. InitializeObjectAttributes(
  755. &UncNameAttributes,
  756. TreeConnectionName,
  757. OBJ_CASE_INSENSITIVE,
  758. NULL,
  759. NULL
  760. );
  761. //
  762. // Calculate the number of bytes needed for the EA buffer to put the
  763. // password or user name.
  764. //
  765. if (ARGUMENT_PRESENT(Password)) {
  766. #if DBG
  767. IF_DEBUG(CONNECT) {
  768. KdPrint(("NWWORKSTATION: NwOpenCreateConnection password is %ws\n",
  769. Password));
  770. }
  771. #endif
  772. PasswordSize = (USHORT) (wcslen(Password) * sizeof(WCHAR));
  773. EaBufferSize = ROUND_UP_COUNT(
  774. FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
  775. EaNamePasswordSize + sizeof(CHAR) +
  776. PasswordSize,
  777. ALIGN_DWORD
  778. );
  779. }
  780. if (ARGUMENT_PRESENT(UserName)) {
  781. #if DBG
  782. IF_DEBUG(CONNECT) {
  783. KdPrint(("NWWORKSTATION: NwOpenCreateConnection username is %ws\n",
  784. UserName));
  785. }
  786. #endif
  787. UserNameSize = (USHORT) (wcslen(UserName) * sizeof(WCHAR));
  788. EaBufferSize += ROUND_UP_COUNT(
  789. FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
  790. EaNameUserNameSize + sizeof(CHAR) +
  791. UserNameSize,
  792. ALIGN_DWORD
  793. );
  794. }
  795. EaBufferSize += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
  796. EaNameTypeSize + sizeof(CHAR) +
  797. TypeSize;
  798. //
  799. // Allocate the EA buffer
  800. //
  801. if ((EaBuffer = (PFILE_FULL_EA_INFORMATION) LocalAlloc(
  802. LMEM_ZEROINIT,
  803. (UINT) EaBufferSize
  804. )) == NULL) {
  805. status = GetLastError();
  806. goto FreeMemory;
  807. }
  808. Ea = EaBuffer;
  809. if (ARGUMENT_PRESENT(Password)) {
  810. //
  811. // Copy the EA name into EA buffer. EA name length does not
  812. // include the zero terminator.
  813. //
  814. strcpy((LPSTR) Ea->EaName, EA_NAME_PASSWORD);
  815. Ea->EaNameLength = EaNamePasswordSize;
  816. //
  817. // Copy the EA value into EA buffer. EA value length does not
  818. // include the zero terminator.
  819. //
  820. wcscpy(
  821. (LPWSTR) &(Ea->EaName[EaNamePasswordSize + sizeof(CHAR)]),
  822. Password
  823. );
  824. Ea->EaValueLength = PasswordSize;
  825. Ea->NextEntryOffset = ROUND_UP_COUNT(
  826. FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
  827. EaNamePasswordSize + sizeof(CHAR) +
  828. PasswordSize,
  829. ALIGN_DWORD
  830. );
  831. Ea->Flags = 0;
  832. (ULONG_PTR) Ea += Ea->NextEntryOffset;
  833. }
  834. if (ARGUMENT_PRESENT(UserName)) {
  835. //
  836. // Copy the EA name into EA buffer. EA name length does not
  837. // include the zero terminator.
  838. //
  839. strcpy((LPSTR) Ea->EaName, EA_NAME_USERNAME);
  840. Ea->EaNameLength = EaNameUserNameSize;
  841. //
  842. // Copy the EA value into EA buffer. EA value length does not
  843. // include the zero terminator.
  844. //
  845. wcscpy(
  846. (LPWSTR) &(Ea->EaName[EaNameUserNameSize + sizeof(CHAR)]),
  847. UserName
  848. );
  849. Ea->EaValueLength = UserNameSize;
  850. Ea->NextEntryOffset = ROUND_UP_COUNT(
  851. FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
  852. EaNameUserNameSize + sizeof(CHAR) +
  853. UserNameSize,
  854. ALIGN_DWORD
  855. );
  856. Ea->Flags = 0;
  857. (ULONG_PTR) Ea += Ea->NextEntryOffset;
  858. }
  859. //
  860. // Copy the connection type name into EA buffer. EA name length
  861. // does not include the zero terminator.
  862. //
  863. strcpy((LPSTR) Ea->EaName, EA_NAME_TYPE);
  864. Ea->EaNameLength = EaNameTypeSize;
  865. *((PULONG) &(Ea->EaName[EaNameTypeSize + sizeof(CHAR)])) = ConnectionType;
  866. Ea->EaValueLength = TypeSize;
  867. //
  868. // Terminate the EA.
  869. //
  870. Ea->NextEntryOffset = 0;
  871. Ea->Flags = 0;
  872. //
  873. // Create or open a tree connection
  874. //
  875. ntstatus = NtCreateFile(
  876. TreeConnectionHandle,
  877. DesiredAccess,
  878. &UncNameAttributes,
  879. &IoStatusBlock,
  880. NULL,
  881. FILE_ATTRIBUTE_NORMAL,
  882. FILE_SHARE_VALID_FLAGS,
  883. CreateDisposition,
  884. CreateOptions,
  885. (PVOID) EaBuffer,
  886. EaBufferSize
  887. );
  888. if (ntstatus == NWRDR_PASSWORD_HAS_EXPIRED) {
  889. //
  890. // wait till other thread is not using the popup data struct.
  891. // if we timeout, then we just lose the popup.
  892. //
  893. switch (WaitForSingleObject(NwPopupDoneEvent, 3000))
  894. {
  895. case WAIT_OBJECT_0:
  896. {
  897. LPWSTR lpServerStart, lpServerEnd ;
  898. WCHAR UserNameW[NW_MAX_USERNAME_LEN+1] ;
  899. DWORD dwUserNameWSize = sizeof(UserNameW)/sizeof(UserNameW[0]) ;
  900. DWORD dwServerLength, dwGraceLogins ;
  901. DWORD dwMessageId = NW_PASSWORD_HAS_EXPIRED ;
  902. //
  903. // get the current username
  904. //
  905. if (UserName)
  906. {
  907. wcscpy(UserNameW, UserName) ;
  908. }
  909. else
  910. {
  911. if (!GetUserNameW(UserNameW, &dwUserNameWSize))
  912. {
  913. SetEvent(NwPopupDoneEvent) ;
  914. break ;
  915. }
  916. }
  917. //
  918. // allocate string and fill in the username
  919. //
  920. if (!(PopupData.InsertStrings[0] =
  921. (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  922. sizeof(WCHAR) * (wcslen(UserNameW)+1))))
  923. {
  924. SetEvent(NwPopupDoneEvent) ;
  925. break ;
  926. }
  927. wcscpy(PopupData.InsertStrings[0], UserNameW) ;
  928. //
  929. // find the server name from unc name
  930. //
  931. lpServerStart = (*UncName == L'\\') ? UncName+2 : UncName ;
  932. lpServerEnd = wcschr(lpServerStart,L'\\') ;
  933. dwServerLength = lpServerEnd ? (DWORD) (lpServerEnd-lpServerStart) :
  934. wcslen(lpServerStart) ;
  935. //
  936. // allocate string and fill in the server insert string
  937. //
  938. if (!(PopupData.InsertStrings[1] =
  939. (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  940. sizeof(WCHAR) * (dwServerLength+1))))
  941. {
  942. (void) LocalFree((HLOCAL) PopupData.InsertStrings[0]);
  943. SetEvent(NwPopupDoneEvent) ;
  944. break ;
  945. }
  946. wcsncpy(PopupData.InsertStrings[1],
  947. lpServerStart,
  948. dwServerLength) ;
  949. //
  950. // now call the NCP. if an error occurs while getting
  951. // the grace login count, dont use it.
  952. //
  953. if (NwGetGraceLoginCount(
  954. PopupData.InsertStrings[1],
  955. UserNameW,
  956. &dwGraceLogins) != NO_ERROR)
  957. {
  958. dwMessageId = NW_PASSWORD_HAS_EXPIRED1 ;
  959. dwGraceLogins = 0 ;
  960. }
  961. //
  962. // stick the number of grace logins in second insert string.
  963. //
  964. if (!(PopupData.InsertStrings[2] =
  965. (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  966. sizeof(WCHAR) * 16)))
  967. {
  968. (void) LocalFree((HLOCAL) PopupData.InsertStrings[0]);
  969. (void) LocalFree((HLOCAL) PopupData.InsertStrings[1]);
  970. SetEvent(NwPopupDoneEvent) ;
  971. break ;
  972. }
  973. wsprintfW(PopupData.InsertStrings[2], L"%d", dwGraceLogins);
  974. PopupData.InsertCount = 3 ;
  975. PopupData.MessageId = dwMessageId ;
  976. //--Mutl-user change ----
  977. GetLuid( &PopupData.LogonId );
  978. //
  979. // all done at last, trigger the other thread do the popup
  980. //
  981. SetEvent(NwPopupEvent) ;
  982. break ;
  983. }
  984. default:
  985. break ; // dont bother if we cannot
  986. }
  987. }
  988. if (NT_SUCCESS(ntstatus)) {
  989. ntstatus = IoStatusBlock.Status;
  990. }
  991. if (ntstatus == NWRDR_PASSWORD_HAS_EXPIRED) {
  992. ntstatus = STATUS_SUCCESS ;
  993. }
  994. if (ARGUMENT_PRESENT(Information)) {
  995. *Information = IoStatusBlock.Information;
  996. }
  997. #if DBG
  998. IF_DEBUG(CONNECT) {
  999. KdPrint(("NWWORKSTATION: NtCreateFile returns %lx\n", ntstatus));
  1000. }
  1001. #endif
  1002. status = NwMapStatus(ntstatus);
  1003. FreeMemory:
  1004. if (EaBuffer != NULL) {
  1005. RtlZeroMemory( EaBuffer, EaBufferSize ); // Clear the password
  1006. (void) LocalFree((HLOCAL) EaBuffer);
  1007. }
  1008. return status;
  1009. }
  1010. DWORD
  1011. NwNukeConnection(
  1012. IN HANDLE TreeConnection,
  1013. IN DWORD UseForce
  1014. )
  1015. /*++
  1016. Routine Description:
  1017. This function asks the redirector to delete an existing tree
  1018. connection.
  1019. Arguments:
  1020. TreeConnection - Supplies the handle to an existing tree connection.
  1021. UseForce - Supplies the force flag to delete the tree connection.
  1022. Return Value:
  1023. NO_ERROR or reason for failure.
  1024. --*/
  1025. {
  1026. DWORD status;
  1027. NWR_REQUEST_PACKET Rrp; // Redirector request packet
  1028. //
  1029. // Tell the redirector to delete the tree connection
  1030. //
  1031. Rrp.Version = REQUEST_PACKET_VERSION;
  1032. Rrp.Parameters.DeleteConn.UseForce = (BOOLEAN) UseForce;
  1033. status = NwRedirFsControl(
  1034. TreeConnection,
  1035. FSCTL_NWR_DELETE_CONNECTION,
  1036. &Rrp,
  1037. sizeof(NWR_REQUEST_PACKET),
  1038. NULL,
  1039. 0,
  1040. NULL
  1041. );
  1042. return status;
  1043. }
  1044. DWORD
  1045. NwGetServerResource(
  1046. IN LPWSTR LocalName,
  1047. IN DWORD LocalNameLength,
  1048. OUT LPWSTR RemoteName,
  1049. IN DWORD RemoteNameLen,
  1050. OUT LPDWORD CharsRequired
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. This function
  1055. Arguments:
  1056. Return Value:
  1057. --*/
  1058. {
  1059. DWORD status = NO_ERROR;
  1060. BYTE Buffer[sizeof(NWR_REQUEST_PACKET) + 2 * sizeof(WCHAR)];
  1061. PNWR_REQUEST_PACKET Rrp = (PNWR_REQUEST_PACKET) Buffer;
  1062. //
  1063. // local device name should not be longer than 4 characters e.g. LPTx, X:
  1064. //
  1065. if ( LocalNameLength > 4 )
  1066. return ERROR_INVALID_PARAMETER;
  1067. Rrp->Version = REQUEST_PACKET_VERSION;
  1068. wcsncpy(Rrp->Parameters.GetConn.DeviceName, LocalName, LocalNameLength);
  1069. Rrp->Parameters.GetConn.DeviceNameLength = LocalNameLength * sizeof(WCHAR);
  1070. status = NwRedirFsControl(
  1071. RedirDeviceHandle,
  1072. FSCTL_NWR_GET_CONNECTION,
  1073. Rrp,
  1074. sizeof(NWR_REQUEST_PACKET) +
  1075. Rrp->Parameters.GetConn.DeviceNameLength,
  1076. RemoteName,
  1077. RemoteNameLen * sizeof(WCHAR),
  1078. NULL
  1079. );
  1080. if (status == ERROR_INSUFFICIENT_BUFFER) {
  1081. *CharsRequired = Rrp->Parameters.GetConn.BytesNeeded / sizeof(WCHAR);
  1082. }
  1083. else if (status == ERROR_FILE_NOT_FOUND) {
  1084. //
  1085. // Redirector could not find the specified LocalName
  1086. //
  1087. status = WN_NOT_CONNECTED;
  1088. }
  1089. return status;
  1090. }
  1091. DWORD
  1092. NwEnumerateConnections(
  1093. IN OUT PDWORD_PTR ResumeId,
  1094. IN DWORD_PTR EntriesRequested,
  1095. IN LPBYTE Buffer,
  1096. IN DWORD BufferSize,
  1097. OUT LPDWORD BytesNeeded,
  1098. OUT LPDWORD EntriesRead,
  1099. IN DWORD ConnectionType,
  1100. IN PLUID LogonId
  1101. )
  1102. /*++
  1103. Routine Description:
  1104. This function asks the redirector to enumerate all existing
  1105. connections.
  1106. Arguments:
  1107. ResumeId - On input, supplies the resume ID of the next entry
  1108. to begin the enumeration. This ID is an integer value that
  1109. is either the smaller or the same value as the ID of the
  1110. next entry to return. On output, this ID indicates the next
  1111. entry to start resuming from for the subsequent call.
  1112. EntriesRequested - Supplies the number of entries to return. If
  1113. this value is -1, return all available entries.
  1114. Buffer - Receives the entries we are listing.
  1115. BufferSize - Supplies the size of the output buffer.
  1116. BytesNeeded - Receives the number of bytes required to get the
  1117. first entry. This value is returned iff ERROR_MORE_DATA is
  1118. the return code, and Buffer is too small to even fit one
  1119. entry.
  1120. EntriesRead - Receives the number of entries returned in Buffer.
  1121. This value is only returned iff NO_ERROR is the return code.
  1122. NO_ERROR is returned as long as at least one entry was written
  1123. into Buffer but does not necessarily mean that it's the number
  1124. of EntriesRequested.
  1125. ConnectionType - The type of connected resource wanted ( DISK, PRINT, ...)
  1126. Return Value:
  1127. NO_ERROR or reason for failure.
  1128. --*/
  1129. {
  1130. DWORD status;
  1131. NWR_REQUEST_PACKET Rrp; // Redirector request packet
  1132. //
  1133. // Tell the redirector to enumerate all connections.
  1134. //
  1135. Rrp.Version = REQUEST_PACKET_VERSION;
  1136. Rrp.Parameters.EnumConn.ResumeKey = *ResumeId;
  1137. Rrp.Parameters.EnumConn.EntriesRequested = (ULONG) EntriesRequested;
  1138. Rrp.Parameters.EnumConn.ConnectionType = ConnectionType;
  1139. //Multi-user change
  1140. if (LogonId != NULL ) {
  1141. Rrp.Parameters.EnumConn.Uid = *LogonId;
  1142. }
  1143. //
  1144. // This is good to do, the the fix below is also needed.
  1145. //
  1146. Rrp.Parameters.EnumConn.EntriesReturned = 0;
  1147. status = NwRedirFsControl(
  1148. RedirDeviceHandle,
  1149. FSCTL_NWR_ENUMERATE_CONNECTIONS,
  1150. &Rrp,
  1151. sizeof(NWR_REQUEST_PACKET),
  1152. Buffer, // User output buffer
  1153. BufferSize,
  1154. NULL
  1155. );
  1156. *EntriesRead = Rrp.Parameters.EnumConn.EntriesReturned;
  1157. //
  1158. // Strange bug on shutdown
  1159. // WinLogon was clearing connections after the shutdown
  1160. //
  1161. if (status == ERROR_INVALID_HANDLE ) {
  1162. KdPrint(("NWWORKSTATION: NwEnumerateConnections Invalid Handle!\n"));
  1163. *EntriesRead = 0;
  1164. }
  1165. else if (status == WN_MORE_DATA) {
  1166. *BytesNeeded = Rrp.Parameters.EnumConn.BytesNeeded;
  1167. //
  1168. // NP specs expect WN_SUCCESS in this case.
  1169. //
  1170. if (*EntriesRead)
  1171. status = WN_SUCCESS ;
  1172. }
  1173. *ResumeId = Rrp.Parameters.EnumConn.ResumeKey;
  1174. return status;
  1175. }
  1176. DWORD
  1177. NwGetNextServerEntry(
  1178. IN HANDLE PreferredServer,
  1179. IN OUT LPDWORD LastObjectId,
  1180. OUT LPSTR ServerName
  1181. )
  1182. /*++
  1183. Routine Description:
  1184. This function uses an opened handle to the preferred server to
  1185. scan it bindery for all file server objects.
  1186. Arguments:
  1187. PreferredServer - Supplies the handle to the preferred server on
  1188. which to scan the bindery.
  1189. LastObjectId - On input, supplies the object ID to the last file
  1190. server object returned, which is the resume handle to get the
  1191. next file server object. On output, receives the object ID
  1192. of the file server object returned.
  1193. ServerName - Receives the name of the returned file server object.
  1194. Return Value:
  1195. NO_ERROR - Successfully gotten a file server name.
  1196. WN_NO_MORE_ENTRIES - No other file server object past the one
  1197. specified by LastObjectId.
  1198. --*/
  1199. {
  1200. NTSTATUS ntstatus;
  1201. WORD ObjectType;
  1202. #if DBG
  1203. IF_DEBUG(ENUM) {
  1204. KdPrint(("NWWORKSTATION: NwGetNextServerEntry LastObjectId %lu\n",
  1205. *LastObjectId));
  1206. }
  1207. #endif
  1208. ntstatus = NwlibMakeNcp(
  1209. PreferredServer,
  1210. FSCTL_NWR_NCP_E3H, // Bindery function
  1211. 58, // Max request packet size
  1212. 59, // Max response packet size
  1213. "bdwp|dwc", // Format string
  1214. 0x37, // Scan bindery object
  1215. *LastObjectId, // Previous ID
  1216. 0x4, // File server object
  1217. "*", // Wildcard to match all
  1218. LastObjectId, // Current ID
  1219. &ObjectType, // Ignore
  1220. ServerName // Currently returned server
  1221. );
  1222. #if DBG
  1223. if (ntstatus == STATUS_SUCCESS) {
  1224. IF_DEBUG(ENUM) {
  1225. KdPrint(("NWWORKSTATION: NwGetNextServerEntry NewObjectId %08lx, ServerName %s\n",
  1226. *LastObjectId, ServerName));
  1227. }
  1228. }
  1229. #endif
  1230. return NwMapBinderyCompletionCode(ntstatus);
  1231. }
  1232. DWORD
  1233. GetConnectedBinderyServers(
  1234. OUT LPNW_ENUM_CONTEXT ContextHandle
  1235. )
  1236. /*++
  1237. Routine Description:
  1238. This function is a helper routine for the function
  1239. NwGetNextServerConnection. It allocates a buffer to cache
  1240. bindery server names returned from calls to the redirector. Since the
  1241. redirector may return duplicate bindery server names, this
  1242. function checks to see if the server name already exist in the buffer
  1243. before adding it.
  1244. Arguments:
  1245. ContextHandle - Used to track cached bindery information and the
  1246. current server name pointer in the cache buffer.
  1247. Return Value:
  1248. NO_ERROR - Successfully returned a server name and cache buffer.
  1249. WN_NO_MORE_ENTRIES - No other server object past the one
  1250. specified by CH->ResumeId.
  1251. ERROR_NOT_ENOUGH_MEMORY - Function was unable to allocate a buffer.
  1252. ++*/
  1253. {
  1254. DWORD_PTR ResumeKey = 0;
  1255. LPBYTE pBuffer = NULL;
  1256. DWORD EntriesRead = 0;
  1257. BYTE tokenIter;
  1258. LPWSTR tokenPtr;
  1259. BOOL fAddToList;
  1260. DWORD status = NwGetConnectionStatus( NULL,
  1261. &ResumeKey,
  1262. &pBuffer,
  1263. &EntriesRead );
  1264. if ( status == NO_ERROR && EntriesRead > 0 )
  1265. {
  1266. DWORD i;
  1267. PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
  1268. ContextHandle->ResumeId = 0;
  1269. ContextHandle->NdsRawDataCount = 0;
  1270. ContextHandle->NdsRawDataSize = (NW_MAX_SERVER_LEN + 2) * EntriesRead;
  1271. ContextHandle->NdsRawDataBuffer =
  1272. (DWORD_PTR) LocalAlloc( LMEM_ZEROINIT,
  1273. ContextHandle->NdsRawDataSize );
  1274. if ( ContextHandle->NdsRawDataBuffer == 0 )
  1275. {
  1276. KdPrint(("NWWORKSTATION: GetConnectedBinderyServers LocalAlloc failed %lu\n",
  1277. GetLastError()));
  1278. ContextHandle->NdsRawDataSize = 0;
  1279. return ERROR_NOT_ENOUGH_MEMORY;
  1280. }
  1281. for ( i = 0; i < EntriesRead ; i++ )
  1282. {
  1283. fAddToList = FALSE;
  1284. if ( pConnStatus->fNds == 0 &&
  1285. ( pConnStatus->dwConnType == NW_CONN_BINDERY_LOGIN ||
  1286. pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE ||
  1287. pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED ||
  1288. pConnStatus->dwConnType == NW_CONN_DISCONNECTED ) )
  1289. {
  1290. fAddToList = TRUE;
  1291. tokenPtr = (LPWSTR) ContextHandle->NdsRawDataBuffer;
  1292. tokenIter = 0;
  1293. //
  1294. // Walk through buffer to see if the tree name already exists.
  1295. //
  1296. while ( tokenIter < ContextHandle->NdsRawDataCount )
  1297. {
  1298. if ( !wcscmp( tokenPtr, pConnStatus->pszServerName ) )
  1299. {
  1300. fAddToList = FALSE;
  1301. }
  1302. tokenPtr = tokenPtr + wcslen( tokenPtr ) + 1;
  1303. tokenIter++;
  1304. }
  1305. }
  1306. //
  1307. // Add the new tree name to end of buffer if needed.
  1308. //
  1309. if ( fAddToList )
  1310. {
  1311. wcscpy( tokenPtr, pConnStatus->pszServerName );
  1312. _wcsupr( tokenPtr );
  1313. ContextHandle->NdsRawDataCount += 1;
  1314. }
  1315. pConnStatus = (PCONN_STATUS) ( pConnStatus +
  1316. pConnStatus->dwTotalLength );
  1317. }
  1318. if ( pBuffer != NULL )
  1319. {
  1320. LocalFree( pBuffer );
  1321. pBuffer = NULL;
  1322. }
  1323. if ( ContextHandle->NdsRawDataCount > 0 )
  1324. {
  1325. //
  1326. // Set ResumeId to point to the first entry in buffer
  1327. // and have NdsRawDataCount set to the number
  1328. // of tree entries left in buffer (ie. substract 1)
  1329. //
  1330. ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
  1331. ContextHandle->NdsRawDataCount -= 1;
  1332. }
  1333. return NO_ERROR;
  1334. }
  1335. return WN_NO_MORE_ENTRIES;
  1336. }
  1337. DWORD
  1338. NwGetNextServerConnection(
  1339. OUT LPNW_ENUM_CONTEXT ContextHandle
  1340. )
  1341. /*++
  1342. Routine Description:
  1343. This function queries the redirector for bindery server connections
  1344. Arguments:
  1345. ContextHandle - Receives the name of the returned bindery server.
  1346. Return Value:
  1347. NO_ERROR - Successfully returned a server name.
  1348. WN_NO_MORE_ENTRIES - No other server objects past the one
  1349. specified by CH->ResumeId exist.
  1350. --*/
  1351. {
  1352. #if DBG
  1353. IF_DEBUG(ENUM) {
  1354. KdPrint(("NWWORKSTATION: NwGetNextServerConnection ResumeId %lu\n",
  1355. ContextHandle->ResumeId));
  1356. }
  1357. #endif
  1358. if ( ContextHandle->ResumeId == (DWORD_PTR) -1 &&
  1359. ContextHandle->NdsRawDataBuffer == 0 &&
  1360. ContextHandle->NdsRawDataCount == 0 )
  1361. {
  1362. //
  1363. // Fill the buffer and point ResumeId to the last
  1364. // server entry name in it. NdsRawDataCount will be
  1365. // set to one less than the number of server names in buffer.
  1366. //
  1367. return GetConnectedBinderyServers( ContextHandle );
  1368. }
  1369. if ( ContextHandle->NdsRawDataBuffer != 0 &&
  1370. ContextHandle->NdsRawDataCount > 0 )
  1371. {
  1372. //
  1373. // Move ResumeId to point to the next entry in the buffer
  1374. // and decrement the NdsRawDataCount by one. Watch for case
  1375. // where we backed up to -1.
  1376. //
  1377. if (ContextHandle->ResumeId == (DWORD_PTR) -1) {
  1378. //
  1379. // Reset to start of buffer.
  1380. //
  1381. ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
  1382. }
  1383. else {
  1384. //
  1385. // Treat as pointer and advance as need.
  1386. //
  1387. ContextHandle->ResumeId =
  1388. ContextHandle->ResumeId +
  1389. ( ( wcslen( (LPWSTR) ContextHandle->ResumeId ) + 1 ) *
  1390. sizeof(WCHAR) );
  1391. }
  1392. ContextHandle->NdsRawDataCount -= 1;
  1393. return NO_ERROR;
  1394. }
  1395. if ( ContextHandle->NdsRawDataBuffer != 0 &&
  1396. ContextHandle->NdsRawDataCount == 0 )
  1397. {
  1398. //
  1399. // We already have a buffer and processed all server names
  1400. // in it, and there is no more data to get.
  1401. // So free the memory used for the buffer and return
  1402. // WN_NO_MORE_ENTRIES to tell WinFile that we are done.
  1403. //
  1404. (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
  1405. ContextHandle->NdsRawDataBuffer = 0;
  1406. ContextHandle->NdsRawDataSize = 0;
  1407. return WN_NO_MORE_ENTRIES;
  1408. }
  1409. //
  1410. // Were done
  1411. //
  1412. return WN_NO_MORE_ENTRIES;
  1413. }
  1414. DWORD
  1415. GetTreeEntriesFromBindery(
  1416. OUT LPNW_ENUM_CONTEXT ContextHandle
  1417. )
  1418. /*++
  1419. Routine Description:
  1420. This function is a helper routine for the function NwGetNextNdsTreeEntry.
  1421. It allocates a buffer (if needed) to cache NDS tree names returned from
  1422. calls to the bindery. Since the bindery often returns duplicates of a
  1423. NDS tree name, this function checks to see if the tree name already
  1424. exist in the buffer before adding it to it if not present.
  1425. Arguments:
  1426. ContextHandle - Used to track cached bindery information and the
  1427. current tree name pointer in the cache buffer.
  1428. Return Value:
  1429. NO_ERROR - Successfully returned a NDS tree name and cache buffer.
  1430. WN_NO_MORE_ENTRIES - No other NDS tree object past the one
  1431. specified by CH->ResumeId.
  1432. ERROR_NOT_ENOUGH_MEMORY - Function was unable to allocate a buffer.
  1433. ++*/
  1434. {
  1435. NTSTATUS ntstatus = STATUS_SUCCESS;
  1436. SERVERNAME TreeName;
  1437. LPWSTR UTreeName = NULL; //Unicode tree name
  1438. DWORD tempDataId;
  1439. WORD ObjectType;
  1440. BYTE iter;
  1441. BYTE tokenIter;
  1442. LPWSTR tokenPtr;
  1443. BOOL fAddToList;
  1444. //
  1445. // Check to see if we need to allocate a buffer for use
  1446. //
  1447. if ( ContextHandle->NdsRawDataBuffer == 0x00000000 )
  1448. {
  1449. ContextHandle->NdsRawDataId = (DWORD) ContextHandle->ResumeId;
  1450. ContextHandle->NdsRawDataSize = EIGHT_KB;
  1451. ContextHandle->NdsRawDataBuffer =
  1452. (DWORD_PTR) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
  1453. ContextHandle->NdsRawDataSize );
  1454. if ( ContextHandle->NdsRawDataBuffer == 0 )
  1455. {
  1456. KdPrint(("NWWORKSTATION: GetTreeEntriesFromBindery LocalAlloc failed %lu\n",
  1457. GetLastError()));
  1458. ContextHandle->NdsRawDataSize = 0;
  1459. ContextHandle->NdsRawDataId = (DWORD) -1;
  1460. return ERROR_NOT_ENOUGH_MEMORY;
  1461. }
  1462. }
  1463. //
  1464. // Repeatedly call bindery to fill buffer with NDS tree names until
  1465. // buffer is full.
  1466. //
  1467. while ( ntstatus == STATUS_SUCCESS )
  1468. {
  1469. RtlZeroMemory( TreeName, sizeof( TreeName ) );
  1470. tempDataId = ContextHandle->NdsRawDataId;
  1471. ntstatus = NwlibMakeNcp(
  1472. ContextHandle->TreeConnectionHandle,
  1473. FSCTL_NWR_NCP_E3H, // Bindery function
  1474. 58, // Max request packet size
  1475. 59, // Max response packet size
  1476. "bdwp|dwc", // Format string
  1477. 0x37, // Scan bindery object
  1478. ContextHandle->NdsRawDataId, // Previous ID
  1479. 0x278, // Directory server object
  1480. "*", // Wildcard to match all
  1481. &ContextHandle->NdsRawDataId, // Current ID
  1482. &ObjectType, // Ignore
  1483. TreeName // Currently returned NDS tree
  1484. );
  1485. //
  1486. // We got a tree name, clean it up (i.e. get rid of underscores ),
  1487. // and add it to buffer if unique.
  1488. //
  1489. if ( ntstatus == STATUS_SUCCESS )
  1490. {
  1491. iter = 31;
  1492. while ( TreeName[iter] == '_' && iter > 0 )
  1493. {
  1494. iter--;
  1495. }
  1496. TreeName[iter + 1] = '\0';
  1497. //
  1498. // Convert tree name to a UNICODE string and proccess it,
  1499. // else just skip it and move on to the next tree name.
  1500. //
  1501. if ( NwConvertToUnicode( &UTreeName, TreeName ) )
  1502. {
  1503. tokenPtr = (LPWSTR) ContextHandle->NdsRawDataBuffer;
  1504. tokenIter = 0;
  1505. fAddToList = TRUE;
  1506. //
  1507. // Walk through buffer to see if the tree name already exists.
  1508. //
  1509. while ( tokenIter < ContextHandle->NdsRawDataCount )
  1510. {
  1511. if ( !wcscmp( tokenPtr, UTreeName ) )
  1512. {
  1513. fAddToList = FALSE;
  1514. }
  1515. tokenPtr = tokenPtr + wcslen( tokenPtr ) + 1;
  1516. tokenIter++;
  1517. }
  1518. //
  1519. // Add the new tree name to end of buffer if needed.
  1520. //
  1521. if ( fAddToList )
  1522. {
  1523. DWORD BytesNeededToAddTreeName = (wcslen(UTreeName)+1) * sizeof(WCHAR);
  1524. DWORD NumberOfBytesAvailable =(DWORD) ( ContextHandle->NdsRawDataBuffer +
  1525. ContextHandle->NdsRawDataSize -
  1526. (DWORD_PTR) tokenPtr );
  1527. if ( BytesNeededToAddTreeName < NumberOfBytesAvailable )
  1528. {
  1529. wcscpy( tokenPtr, UTreeName );
  1530. ContextHandle->NdsRawDataCount += 1;
  1531. }
  1532. else
  1533. {
  1534. ContextHandle->NdsRawDataId = tempDataId;
  1535. ntstatus = ERROR_NOT_ENOUGH_MEMORY;
  1536. }
  1537. }
  1538. (void) LocalFree((HLOCAL) UTreeName);
  1539. }
  1540. }
  1541. }
  1542. //
  1543. // We are done filling buffer, and there are no more tree names
  1544. // to request. Set id to indicate last value.
  1545. //
  1546. if ( ntstatus == STATUS_NO_MORE_ENTRIES )
  1547. {
  1548. ContextHandle->NdsRawDataId = (DWORD) -1;
  1549. ntstatus = STATUS_SUCCESS;
  1550. }
  1551. //
  1552. // We are done because the buffer is full. So we return NO_ERROR to
  1553. // indicate completion, and leave ContextHandle->NdsRawDataId as is
  1554. // to indicate where we left off.
  1555. //
  1556. if ( ntstatus == ERROR_NOT_ENOUGH_MEMORY )
  1557. {
  1558. ntstatus = STATUS_SUCCESS;
  1559. }
  1560. if ( ContextHandle->NdsRawDataCount == 0 )
  1561. {
  1562. if ( ContextHandle->NdsRawDataBuffer )
  1563. (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
  1564. ContextHandle->NdsRawDataBuffer = 0;
  1565. ContextHandle->NdsRawDataSize = 0;
  1566. ContextHandle->NdsRawDataId = (DWORD) -1;
  1567. return WN_NO_MORE_ENTRIES;
  1568. }
  1569. if ( ContextHandle->NdsRawDataCount > 0 )
  1570. {
  1571. //
  1572. // Set ResumeId to point to the first entry in buffer
  1573. // and have NdsRawDataCount set to the number
  1574. // of tree entries left in buffer (ie. substract 1)
  1575. //
  1576. ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
  1577. ContextHandle->NdsRawDataCount -= 1;
  1578. return NO_ERROR;
  1579. }
  1580. if ( ContextHandle->NdsRawDataBuffer )
  1581. (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
  1582. ContextHandle->NdsRawDataBuffer = 0;
  1583. ContextHandle->NdsRawDataSize = 0;
  1584. ContextHandle->NdsRawDataId = (DWORD) -1;
  1585. return NwMapStatus( ntstatus );
  1586. }
  1587. DWORD
  1588. NwGetNextNdsTreeEntry(
  1589. OUT LPNW_ENUM_CONTEXT ContextHandle
  1590. )
  1591. /*++
  1592. Routine Description:
  1593. This function uses an opened handle to the preferred server to
  1594. scan it bindery for all NDS tree objects.
  1595. Arguments:
  1596. ContextHandle - Receives the name of the returned NDS tree object
  1597. given the current preferred server connection and CH->ResumeId.
  1598. Return Value:
  1599. NO_ERROR - Successfully returned a NDS tree name.
  1600. WN_NO_MORE_ENTRIES - No other NDS tree objects past the one
  1601. specified by CH->ResumeId exist.
  1602. --*/
  1603. {
  1604. #if DBG
  1605. IF_DEBUG(ENUM) {
  1606. KdPrint(("NWWORKSTATION: NwGetNextNdsTreeEntry ResumeId %lu\n",
  1607. ContextHandle->ResumeId));
  1608. }
  1609. #endif
  1610. if ( ContextHandle->ResumeId == (DWORD_PTR) -1 &&
  1611. ContextHandle->NdsRawDataBuffer == 0 &&
  1612. ContextHandle->NdsRawDataCount == 0 )
  1613. {
  1614. //
  1615. // Fill the buffer and point ResumeId to the last
  1616. // tree entry name in it. NdsRawDataCount will be
  1617. // set to one less than the number of tree names in buffer.
  1618. //
  1619. return GetTreeEntriesFromBindery( ContextHandle );
  1620. }
  1621. if ( ContextHandle->NdsRawDataBuffer != 0 &&
  1622. ContextHandle->NdsRawDataCount > 0 )
  1623. {
  1624. //
  1625. // Move ResumeId to point to the next entry in the buffer
  1626. // and decrement the NdsRawDataCount by one. Watch for case
  1627. // where we backed up to -1.
  1628. //
  1629. if (ContextHandle->ResumeId == (DWORD_PTR) -1) {
  1630. //
  1631. // Reset to start of buffer.
  1632. //
  1633. ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
  1634. }
  1635. else {
  1636. //
  1637. // Move ResumeId to point to the next entry in the buffer
  1638. // and decrement the NdsRawDataCount by one
  1639. //
  1640. ContextHandle->ResumeId =
  1641. ContextHandle->ResumeId +
  1642. ( ( wcslen( (LPWSTR) ContextHandle->ResumeId ) + 1 ) *
  1643. sizeof(WCHAR) );
  1644. }
  1645. ContextHandle->NdsRawDataCount -= 1;
  1646. return NO_ERROR;
  1647. }
  1648. if ( ContextHandle->NdsRawDataBuffer != 0 &&
  1649. ContextHandle->NdsRawDataCount == 0 &&
  1650. ContextHandle->NdsRawDataId != (DWORD) -1 )
  1651. {
  1652. //
  1653. // We already have a buffer and processed all tree names
  1654. // in it, and there is more data in the bindery to get.
  1655. // So go get it and point ResumeId to the last tree
  1656. // entry name in the buffer and set NdsRawDataCount to
  1657. // one less than the number of tree names in buffer.
  1658. //
  1659. return GetTreeEntriesFromBindery( ContextHandle );
  1660. }
  1661. if ( ContextHandle->NdsRawDataBuffer != 0 &&
  1662. ContextHandle->NdsRawDataCount == 0 &&
  1663. ContextHandle->NdsRawDataId == (DWORD) -1 )
  1664. {
  1665. //
  1666. // We already have a buffer and processed all tree names
  1667. // in it, and there is no more data in the bindery to get.
  1668. // So free the memory used for the buffer and return
  1669. // WN_NO_MORE_ENTRIES to tell WinFile that we are done.
  1670. //
  1671. (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
  1672. ContextHandle->NdsRawDataBuffer = 0;
  1673. ContextHandle->NdsRawDataSize = 0;
  1674. return WN_NO_MORE_ENTRIES;
  1675. }
  1676. //
  1677. // We should never hit this area!
  1678. //
  1679. return WN_NO_MORE_ENTRIES;
  1680. }
  1681. DWORD
  1682. NwGetNextVolumeEntry(
  1683. IN HANDLE ServerConnection,
  1684. IN DWORD NextVolumeNumber,
  1685. OUT LPSTR VolumeName
  1686. )
  1687. /*++
  1688. Routine Description:
  1689. This function lists the volumes on the server specified by
  1690. an opened tree connection handle to the server.
  1691. Arguments:
  1692. ServerConnection - Supplies the tree connection handle to the
  1693. server to enumerate volumes from.
  1694. NextVolumeNumber - Supplies the volume number which to look
  1695. up the name.
  1696. VolumeName - Receives the name of the volume associated with
  1697. NextVolumeNumber.
  1698. Return Value:
  1699. NO_ERROR - Successfully gotten the volume name.
  1700. WN_NO_MORE_ENTRIES - No other volume name associated with the
  1701. specified volume number.
  1702. --*/
  1703. {
  1704. NTSTATUS ntstatus;
  1705. #if DBG
  1706. IF_DEBUG(ENUM) {
  1707. KdPrint(("NWWORKSTATION: NwGetNextVolumeEntry volume number %lu\n",
  1708. NextVolumeNumber));
  1709. }
  1710. #endif
  1711. ntstatus = NwlibMakeNcp(
  1712. ServerConnection,
  1713. FSCTL_NWR_NCP_E2H, // Directory function
  1714. 4, // Max request packet size
  1715. 19, // Max response packet size
  1716. "bb|p", // Format string
  1717. 0x6, // Get volume name
  1718. (BYTE) NextVolumeNumber, // Previous ID
  1719. VolumeName // Currently returned server
  1720. );
  1721. return NwMapStatus(ntstatus);
  1722. }
  1723. DWORD
  1724. NwRdrLogonUser(
  1725. IN PLUID LogonId,
  1726. IN LPWSTR UserName,
  1727. IN DWORD UserNameSize,
  1728. IN LPWSTR Password OPTIONAL,
  1729. IN DWORD PasswordSize,
  1730. IN LPWSTR PreferredServer OPTIONAL,
  1731. IN DWORD PreferredServerSize,
  1732. IN LPWSTR NdsPreferredServer OPTIONAL,
  1733. IN DWORD NdsPreferredServerSize,
  1734. IN DWORD PrintOption
  1735. )
  1736. /*++
  1737. Routine Description:
  1738. This function tells the redirector the user logon credential.
  1739. Arguments:
  1740. UserName - Supplies the user name.
  1741. UserNameSize - Supplies the size in bytes of the user name string without
  1742. the NULL terminator.
  1743. Password - Supplies the password.
  1744. PasswordSize - Supplies the size in bytes of the password string without
  1745. the NULL terminator.
  1746. PreferredServer - Supplies the preferred server name.
  1747. PreferredServerSize - Supplies the size in bytes of the preferred server
  1748. string without the NULL terminator.
  1749. Return Value:
  1750. NO_ERROR or reason for failure.
  1751. --*/
  1752. {
  1753. DWORD status;
  1754. PNWR_REQUEST_PACKET Rrp; // Redirector request packet
  1755. DWORD RrpSize = sizeof(NWR_REQUEST_PACKET) +
  1756. UserNameSize +
  1757. PasswordSize +
  1758. PreferredServerSize;
  1759. LPBYTE Dest;
  1760. BYTE lpReplicaAddress[sizeof(TDI_ADDRESS_IPX)];
  1761. DWORD ReplicaAddressSize = 0;
  1762. #if DBG
  1763. IF_DEBUG(LOGON) {
  1764. BYTE PW[128];
  1765. RtlZeroMemory(PW, sizeof(PW));
  1766. if (PasswordSize > (sizeof(PW) - 1)) {
  1767. memcpy(PW, Password, sizeof(PW) - 1);
  1768. }
  1769. else {
  1770. memcpy(PW, Password, PasswordSize);
  1771. }
  1772. KdPrint(("NWWORKSTATION: NwRdrLogonUser: UserName %ws\n", UserName));
  1773. KdPrint((" Password %ws\n", PW));
  1774. if ( PreferredServer )
  1775. KdPrint((" Server %ws\n", PreferredServer ));
  1776. }
  1777. #endif
  1778. if ( PreferredServer &&
  1779. PreferredServer[0] == TREECHAR &&
  1780. PreferredServer[1] )
  1781. {
  1782. WCHAR TreeName[MAX_NDS_NAME_CHARS + 1];
  1783. LPWSTR lpTemp;
  1784. //
  1785. // Find the nearest dir server for the tree that the user wants to
  1786. // connect to.
  1787. //
  1788. // Citrix Terminal Server Merge
  1789. // 12/09/96 cjc PreferredServer also includes organizational units -
  1790. // not just the tree name so the size of it can be
  1791. // > MAX_NDS_TREE_NAME_LEN and when it is, the wcscpy
  1792. // below overwrites other stack data and causes errors
  1793. // during NW logins.
  1794. if ( PreferredServerSize > (MAX_NDS_TREE_NAME_LEN*sizeof(WCHAR)) ) {
  1795. memcpy(TreeName, PreferredServer+1,
  1796. (MAX_NDS_TREE_NAME_LEN*sizeof(WCHAR)) );
  1797. TreeName[MAX_NDS_TREE_NAME_LEN] = L'\0';
  1798. }
  1799. else {
  1800. wcscpy( TreeName, PreferredServer + 1 );
  1801. }
  1802. lpTemp = wcschr( TreeName, L'\\' );
  1803. if (lpTemp) {
  1804. lpTemp[0] = L'\0';
  1805. }
  1806. if (NdsPreferredServer != NULL) {
  1807. KdPrint(("NWWORKSTATION: NdsPreferredServer: %ws\n", PreferredServer));
  1808. GetPreferredServerAddress( NdsPreferredServer/*L"red_41b"*/,
  1809. &ReplicaAddressSize,
  1810. lpReplicaAddress );
  1811. } else {
  1812. GetNearestDirServer( TreeName,
  1813. &ReplicaAddressSize,
  1814. lpReplicaAddress );
  1815. }
  1816. RrpSize += ReplicaAddressSize;
  1817. }
  1818. if ( PreferredServer &&
  1819. PreferredServer[0] == TREECHAR &&
  1820. !PreferredServer[1] )
  1821. {
  1822. PreferredServerSize = 0;
  1823. }
  1824. //
  1825. // Allocate the request packet
  1826. //
  1827. if ((Rrp = (PVOID) LocalAlloc(
  1828. LMEM_ZEROINIT,
  1829. RrpSize
  1830. )) == NULL) {
  1831. KdPrint(("NWWORKSTATION: NwRdrLogonUser LocalAlloc failed %lu\n",
  1832. GetLastError()));
  1833. return ERROR_NOT_ENOUGH_MEMORY;
  1834. }
  1835. //
  1836. // Tell the redirector the user logon credential.
  1837. //
  1838. Rrp->Version = REQUEST_PACKET_VERSION;
  1839. RtlCopyLuid(&(Rrp->Parameters.Logon.LogonId), LogonId);
  1840. #if DBG
  1841. IF_DEBUG(LOGON) {
  1842. KdPrint(("NWWORKSTATION: NwRdrLogonUser passing to Rdr logon ID %lu %lu\n",
  1843. *LogonId, *((PULONG) ((DWORD_PTR) LogonId + sizeof(ULONG)))));
  1844. }
  1845. #endif
  1846. Rrp->Parameters.Logon.UserNameLength = UserNameSize;
  1847. Rrp->Parameters.Logon.PasswordLength = PasswordSize;
  1848. Rrp->Parameters.Logon.ServerNameLength = PreferredServerSize;
  1849. Rrp->Parameters.Logon.ReplicaAddrLength = ReplicaAddressSize;
  1850. Rrp->Parameters.Logon.PrintOption = PrintOption;
  1851. memcpy(Rrp->Parameters.Logon.UserName, UserName, UserNameSize);
  1852. Dest = (LPBYTE) ((DWORD_PTR) Rrp->Parameters.Logon.UserName + UserNameSize);
  1853. if (PasswordSize > 0)
  1854. {
  1855. memcpy(Dest, Password, PasswordSize);
  1856. Dest = (LPBYTE) ((DWORD_PTR) Dest + PasswordSize);
  1857. }
  1858. if (PreferredServerSize > 0)
  1859. {
  1860. memcpy(Dest, PreferredServer, PreferredServerSize);
  1861. if (ReplicaAddressSize > 0)
  1862. {
  1863. Dest = (LPBYTE) ((DWORD_PTR) Dest + PreferredServerSize);
  1864. memcpy(Dest, lpReplicaAddress, ReplicaAddressSize);
  1865. }
  1866. }
  1867. status = NwRedirFsControl(
  1868. RedirDeviceHandle,
  1869. FSCTL_NWR_LOGON,
  1870. Rrp,
  1871. RrpSize,
  1872. NULL, // No logon script in this release
  1873. 0,
  1874. NULL
  1875. );
  1876. RtlZeroMemory(Rrp, RrpSize); // Clear the password
  1877. (void) LocalFree((HLOCAL) Rrp);
  1878. return status;
  1879. }
  1880. VOID
  1881. NwRdrChangePassword(
  1882. IN PNWR_REQUEST_PACKET Rrp
  1883. )
  1884. /*++
  1885. Routine Description:
  1886. This function tells the redirector the new password for a user on
  1887. a particular server.
  1888. Arguments:
  1889. Rrp - Supplies the username, new password and servername.
  1890. RrpSize - Supplies the size of the request packet.
  1891. Return Value:
  1892. None.
  1893. --*/
  1894. {
  1895. //
  1896. // Tell the redirector the user new password.
  1897. //
  1898. Rrp->Version = REQUEST_PACKET_VERSION;
  1899. (void) NwRedirFsControl(
  1900. RedirDeviceHandle,
  1901. FSCTL_NWR_CHANGE_PASS,
  1902. Rrp,
  1903. sizeof(NWR_REQUEST_PACKET) +
  1904. Rrp->Parameters.ChangePass.UserNameLength +
  1905. Rrp->Parameters.ChangePass.PasswordLength +
  1906. Rrp->Parameters.ChangePass.ServerNameLength,
  1907. NULL,
  1908. 0,
  1909. NULL
  1910. );
  1911. }
  1912. DWORD
  1913. NwRdrSetInfo(
  1914. IN DWORD PrintOption,
  1915. IN DWORD PacketBurstSize,
  1916. IN LPWSTR PreferredServer OPTIONAL,
  1917. IN DWORD PreferredServerSize,
  1918. IN LPWSTR ProviderName OPTIONAL,
  1919. IN DWORD ProviderNameSize
  1920. )
  1921. /*++
  1922. Routine Description:
  1923. This function passes some workstation configuration and current user's
  1924. preference to the redirector. This includes the network provider name, the
  1925. packet burst size, the user's selected preferred server and print option.
  1926. Arguments:
  1927. PrintOption - The current user's print option
  1928. PacketBurstSize - The packet burst size stored in the registry
  1929. PreferredServer - The preferred server the current user selected
  1930. PreferredServerSize - Supplies the size in bytes of the preferred server
  1931. string without the NULL terminator.
  1932. ProviderName - Supplies the provider name.
  1933. ProviderNameSize - Supplies the size in bytes of the provider name
  1934. string without the NULL terminator.
  1935. Return Value:
  1936. NO_ERROR or reason for failure.
  1937. --*/
  1938. {
  1939. DWORD status;
  1940. PNWR_REQUEST_PACKET Rrp; // Redirector request packet
  1941. DWORD RrpSize = sizeof(NWR_REQUEST_PACKET) +
  1942. PreferredServerSize +
  1943. ProviderNameSize;
  1944. LPBYTE Dest;
  1945. BOOL Impersonate = FALSE;
  1946. //
  1947. // Allocate the request packet
  1948. //
  1949. if ((Rrp = (PVOID) LocalAlloc(
  1950. LMEM_ZEROINIT,
  1951. RrpSize
  1952. )) == NULL) {
  1953. KdPrint(("NWWORKSTATION: NwRdrSetInfo LocalAlloc failed %lu\n",
  1954. GetLastError()));
  1955. return ERROR_NOT_ENOUGH_MEMORY;
  1956. }
  1957. Rrp->Version = REQUEST_PACKET_VERSION;
  1958. Rrp->Parameters.SetInfo.PrintOption = PrintOption;
  1959. Rrp->Parameters.SetInfo.MaximumBurstSize = PacketBurstSize;
  1960. Rrp->Parameters.SetInfo.PreferredServerLength = PreferredServerSize;
  1961. Rrp->Parameters.SetInfo.ProviderNameLength = ProviderNameSize;
  1962. if (ProviderNameSize > 0) {
  1963. memcpy( Rrp->Parameters.SetInfo.PreferredServer,
  1964. PreferredServer, PreferredServerSize);
  1965. }
  1966. Dest = (LPBYTE) ((DWORD_PTR) Rrp->Parameters.SetInfo.PreferredServer
  1967. + PreferredServerSize);
  1968. if (ProviderNameSize > 0) {
  1969. memcpy(Dest, ProviderName, ProviderNameSize);
  1970. }
  1971. /* --- Multi-user change
  1972. * For print options
  1973. * It's OK if it doesn't work
  1974. */
  1975. if ((status = NwImpersonateClient()) == NO_ERROR)
  1976. {
  1977. Impersonate = TRUE;
  1978. }
  1979. status = NwRedirFsControl(
  1980. RedirDeviceHandle,
  1981. FSCTL_NWR_SET_INFO,
  1982. Rrp,
  1983. RrpSize,
  1984. NULL,
  1985. 0,
  1986. NULL
  1987. );
  1988. if ( Impersonate ) {
  1989. (void) NwRevertToSelf() ;
  1990. }
  1991. (void) LocalFree((HLOCAL) Rrp);
  1992. if ( status != NO_ERROR )
  1993. {
  1994. KdPrint(("NwRedirFsControl: FSCTL_NWR_SET_INFO failed with %d\n",
  1995. status ));
  1996. }
  1997. return status;
  1998. }
  1999. DWORD
  2000. NwRdrLogoffUser(
  2001. IN PLUID LogonId
  2002. )
  2003. /*++
  2004. Routine Description:
  2005. This function asks the redirector to log off the interactive user.
  2006. Arguments:
  2007. None.
  2008. Return Value:
  2009. NO_ERROR or reason for failure.
  2010. --*/
  2011. {
  2012. DWORD status;
  2013. NWR_REQUEST_PACKET Rrp; // Redirector request packet
  2014. //
  2015. // Tell the redirector to logoff user.
  2016. //
  2017. Rrp.Version = REQUEST_PACKET_VERSION;
  2018. RtlCopyLuid(&Rrp.Parameters.Logoff.LogonId, LogonId);
  2019. status = NwRedirFsControl(
  2020. RedirDeviceHandle,
  2021. FSCTL_NWR_LOGOFF,
  2022. &Rrp,
  2023. sizeof(NWR_REQUEST_PACKET),
  2024. NULL,
  2025. 0,
  2026. NULL
  2027. );
  2028. return status;
  2029. }
  2030. DWORD
  2031. NwConnectToServer(
  2032. IN LPWSTR ServerName
  2033. )
  2034. /*++
  2035. Routine Description:
  2036. This function opens a handle to \Device\Nwrdr\ServerName, given
  2037. ServerName, and then closes the handle if the open was successful.
  2038. It is to validate that the current user credential can access
  2039. the server.
  2040. Arguments:
  2041. ServerName - Supplies the name of the server to validate the
  2042. user credential.
  2043. Return Value:
  2044. NO_ERROR or reason for failure.
  2045. --*/
  2046. {
  2047. DWORD status;
  2048. UNICODE_STRING ServerStr;
  2049. HANDLE ServerHandle;
  2050. ServerStr.MaximumLength = (wcslen(ServerName) + 2) *
  2051. sizeof(WCHAR) + // \ServerName0
  2052. RedirDeviceName.Length; // \Device\Nwrdr
  2053. if ((ServerStr.Buffer = (PWSTR) LocalAlloc(
  2054. LMEM_ZEROINIT,
  2055. (UINT) ServerStr.MaximumLength
  2056. )) == NULL) {
  2057. return ERROR_NOT_ENOUGH_MEMORY;
  2058. }
  2059. //
  2060. // Copy \Device\NwRdr
  2061. //
  2062. RtlCopyUnicodeString(&ServerStr, &RedirDeviceName);
  2063. //
  2064. // Concatenate \ServerName
  2065. //
  2066. wcscat(ServerStr.Buffer, L"\\");
  2067. ServerStr.Length += sizeof(WCHAR);
  2068. wcscat(ServerStr.Buffer, ServerName);
  2069. ServerStr.Length += (USHORT) (wcslen(ServerName) * sizeof(WCHAR));
  2070. status = NwOpenCreateConnection(
  2071. &ServerStr,
  2072. NULL,
  2073. NULL,
  2074. ServerName,
  2075. SYNCHRONIZE | FILE_WRITE_DATA,
  2076. FILE_OPEN,
  2077. FILE_SYNCHRONOUS_IO_NONALERT,
  2078. RESOURCETYPE_DISK,
  2079. &ServerHandle,
  2080. NULL
  2081. );
  2082. if (status == ERROR_FILE_NOT_FOUND) {
  2083. status = ERROR_BAD_NETPATH;
  2084. }
  2085. (void) LocalFree((HLOCAL) ServerStr.Buffer);
  2086. if (status == NO_ERROR || status == NW_PASSWORD_HAS_EXPIRED) {
  2087. (void) NtClose(ServerHandle);
  2088. }
  2089. return status;
  2090. }
  2091. DWORD
  2092. NWPGetConnectionStatus(
  2093. IN LPWSTR pszRemoteName,
  2094. IN OUT PDWORD_PTR ResumeKey,
  2095. OUT LPBYTE Buffer,
  2096. IN DWORD BufferSize,
  2097. OUT PDWORD BytesNeeded,
  2098. OUT PDWORD EntriesRead
  2099. )
  2100. {
  2101. NTSTATUS ntstatus = STATUS_SUCCESS;
  2102. HANDLE handleRdr = NULL;
  2103. OBJECT_ATTRIBUTES ObjectAttributes;
  2104. IO_STATUS_BLOCK IoStatusBlock;
  2105. UNICODE_STRING uRdrName;
  2106. WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
  2107. PNWR_REQUEST_PACKET RequestPacket = NULL;
  2108. DWORD RequestPacketSize = 0;
  2109. DWORD dwRemoteNameLen = 0;
  2110. //
  2111. // Set up the object attributes.
  2112. //
  2113. RtlInitUnicodeString( &uRdrName, RdrPrefix );
  2114. InitializeObjectAttributes( &ObjectAttributes,
  2115. &uRdrName,
  2116. OBJ_CASE_INSENSITIVE,
  2117. NULL,
  2118. NULL );
  2119. ntstatus = NtOpenFile( &handleRdr,
  2120. SYNCHRONIZE | FILE_LIST_DIRECTORY,
  2121. &ObjectAttributes,
  2122. &IoStatusBlock,
  2123. FILE_SHARE_VALID_FLAGS,
  2124. FILE_SYNCHRONOUS_IO_NONALERT );
  2125. if ( !NT_SUCCESS(ntstatus) )
  2126. goto CleanExit;
  2127. dwRemoteNameLen = pszRemoteName? wcslen(pszRemoteName)*sizeof(WCHAR) : 0;
  2128. RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwRemoteNameLen;
  2129. RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT,
  2130. RequestPacketSize );
  2131. if ( RequestPacket == NULL )
  2132. {
  2133. ntstatus = STATUS_NO_MEMORY;
  2134. goto CleanExit;
  2135. }
  2136. //
  2137. // Fill out the request packet for FSCTL_NWR_GET_CONN_STATUS.
  2138. //
  2139. RequestPacket->Parameters.GetConnStatus.ResumeKey = *ResumeKey;
  2140. RequestPacket->Version = REQUEST_PACKET_VERSION;
  2141. RequestPacket->Parameters.GetConnStatus.ConnectionNameLength = dwRemoteNameLen;
  2142. RtlCopyMemory( &(RequestPacket->Parameters.GetConnStatus.ConnectionName[0]),
  2143. pszRemoteName,
  2144. dwRemoteNameLen );
  2145. ntstatus = NtFsControlFile( handleRdr,
  2146. NULL,
  2147. NULL,
  2148. NULL,
  2149. &IoStatusBlock,
  2150. FSCTL_NWR_GET_CONN_STATUS,
  2151. (PVOID) RequestPacket,
  2152. RequestPacketSize,
  2153. (PVOID) Buffer,
  2154. BufferSize );
  2155. if ( NT_SUCCESS( ntstatus ))
  2156. ntstatus = IoStatusBlock.Status;
  2157. *EntriesRead = RequestPacket->Parameters.GetConnStatus.EntriesReturned;
  2158. *ResumeKey = RequestPacket->Parameters.GetConnStatus.ResumeKey;
  2159. *BytesNeeded = RequestPacket->Parameters.GetConnStatus.BytesNeeded;
  2160. CleanExit:
  2161. if ( handleRdr != NULL )
  2162. NtClose( handleRdr );
  2163. if ( RequestPacket != NULL )
  2164. LocalFree( RequestPacket );
  2165. return RtlNtStatusToDosError( ntstatus );
  2166. }
  2167. DWORD
  2168. NwGetConnectionStatus(
  2169. IN LPWSTR pszRemoteName,
  2170. OUT PDWORD_PTR ResumeKey,
  2171. OUT LPBYTE *Buffer,
  2172. OUT PDWORD EntriesRead
  2173. )
  2174. {
  2175. DWORD err = NO_ERROR;
  2176. DWORD dwBytesNeeded = 0;
  2177. DWORD dwBufferSize = TWO_KB;
  2178. *Buffer = NULL;
  2179. *EntriesRead = 0;
  2180. do {
  2181. *Buffer = (LPBYTE) LocalAlloc( LMEM_ZEROINIT, dwBufferSize );
  2182. if ( *Buffer == NULL )
  2183. return ERROR_NOT_ENOUGH_MEMORY;
  2184. err = NWPGetConnectionStatus( pszRemoteName,
  2185. ResumeKey,
  2186. *Buffer,
  2187. dwBufferSize,
  2188. &dwBytesNeeded,
  2189. EntriesRead );
  2190. if ( err == ERROR_INSUFFICIENT_BUFFER )
  2191. {
  2192. dwBufferSize = dwBytesNeeded + EXTRA_BYTES;
  2193. LocalFree( *Buffer );
  2194. *Buffer = NULL;
  2195. }
  2196. } while ( err == ERROR_INSUFFICIENT_BUFFER );
  2197. if ( err == ERROR_INVALID_PARAMETER ) // not attached
  2198. {
  2199. err = NO_ERROR;
  2200. *EntriesRead = 0;
  2201. }
  2202. return err;
  2203. }
  2204. VOID
  2205. GetNearestDirServer(
  2206. IN LPWSTR TreeName,
  2207. OUT LPDWORD lpdwReplicaAddressSize,
  2208. OUT LPBYTE lpReplicaAddress
  2209. )
  2210. {
  2211. WCHAR Buffer[BUFFSIZE];
  2212. PWSAQUERYSETW Query = (PWSAQUERYSETW)Buffer;
  2213. HANDLE hRnr;
  2214. DWORD dwQuerySize = BUFFSIZE;
  2215. GUID gdService = SVCID_NETWARE(0x278);
  2216. WSADATA wsaData;
  2217. WCHAR ServiceInstanceName[] = L"*";
  2218. WSAStartup(MAKEWORD(1, 1), &wsaData);
  2219. memset(Query, 0, sizeof(*Query));
  2220. //
  2221. // putting a "*" in the lpszServiceInstanceName causes
  2222. // the query to look for all server instances. Putting a
  2223. // specific name in here will search only for instance of
  2224. // that name. If you have a specific name to look for,
  2225. // put a pointer to the name here.
  2226. //
  2227. Query->lpszServiceInstanceName = ServiceInstanceName;
  2228. Query->dwNameSpace = NS_SAP;
  2229. Query->dwSize = sizeof(*Query);
  2230. Query->lpServiceClassId = &gdService;
  2231. //
  2232. // Find the servers. The flags indicate:
  2233. // LUP_NEAREST: look for nearest servers
  2234. // LUP_DEEP : if none are found on the local segement look
  2235. // for server using a general query
  2236. // LUP_RETURN_NAME: return the name
  2237. // LUP_RETURN_ADDR: return the server address
  2238. //
  2239. // if only servers on the local segment are acceptable, omit
  2240. // setting LUP_DEEP
  2241. //
  2242. if( WSALookupServiceBegin( Query,
  2243. LUP_NEAREST |
  2244. LUP_DEEP |
  2245. LUP_RETURN_NAME |
  2246. LUP_RETURN_ADDR,
  2247. &hRnr ) == SOCKET_ERROR )
  2248. {
  2249. //
  2250. // Something went wrong, return no address. The redirector will
  2251. // have to come up with a dir server on its own.
  2252. //
  2253. *lpdwReplicaAddressSize = 0;
  2254. return ;
  2255. }
  2256. else
  2257. {
  2258. //
  2259. // Ready to look for one of them ...
  2260. //
  2261. Query->dwSize = BUFFSIZE;
  2262. while( WSALookupServiceNext( hRnr,
  2263. 0,
  2264. &dwQuerySize,
  2265. Query ) == NO_ERROR )
  2266. {
  2267. //
  2268. // Found a dir server, now see if it is a server for the NDS tree
  2269. // TreeName.
  2270. //
  2271. if ( NwpCompareTreeNames( Query->lpszServiceInstanceName,
  2272. TreeName ) )
  2273. {
  2274. *lpdwReplicaAddressSize = sizeof(TDI_ADDRESS_IPX);
  2275. memcpy( lpReplicaAddress,
  2276. Query->lpcsaBuffer->RemoteAddr.lpSockaddr->sa_data,
  2277. sizeof(TDI_ADDRESS_IPX) );
  2278. WSALookupServiceEnd(hRnr);
  2279. return ;
  2280. }
  2281. }
  2282. //
  2283. // Could not find a dir server, return no address. The redirector will
  2284. // have to come up with a dir server on its own.
  2285. //
  2286. *lpdwReplicaAddressSize = 0;
  2287. WSALookupServiceEnd(hRnr);
  2288. }
  2289. }
  2290. BOOL
  2291. NwpCompareTreeNames(
  2292. LPWSTR lpServiceInstanceName,
  2293. LPWSTR lpTreeName
  2294. )
  2295. {
  2296. DWORD iter = 31;
  2297. while ( lpServiceInstanceName[iter] == '_' && iter > 0 )
  2298. {
  2299. iter--;
  2300. }
  2301. lpServiceInstanceName[iter + 1] = '\0';
  2302. if ( !_wcsicmp( lpServiceInstanceName, lpTreeName ) )
  2303. {
  2304. return TRUE;
  2305. }
  2306. return FALSE;
  2307. }
  2308. #define SIZE_OF_STATISTICS_TOKEN_INFORMATION \
  2309. sizeof( TOKEN_STATISTICS )
  2310. VOID
  2311. GetLuid(
  2312. IN OUT PLUID plogonid
  2313. )
  2314. /*++
  2315. Routine Description:
  2316. Returns an LUID
  2317. Arguments:
  2318. none
  2319. Return Value:
  2320. LUID
  2321. --*/
  2322. {
  2323. HANDLE TokenHandle;
  2324. UCHAR TokenInformation[ SIZE_OF_STATISTICS_TOKEN_INFORMATION ];
  2325. ULONG ReturnLength;
  2326. LUID NullId = { 0, 0 };
  2327. // We can use OpenThreadToken because this server thread
  2328. // is impersonating a client
  2329. if ( !OpenThreadToken( GetCurrentThread(),
  2330. TOKEN_READ,
  2331. TRUE, /* Open as self */
  2332. &TokenHandle ))
  2333. {
  2334. #if DBG
  2335. KdPrint(("GetLuid: OpenThreadToken failed: Error %d\n",
  2336. GetLastError()));
  2337. #endif
  2338. *plogonid = NullId;
  2339. return;
  2340. }
  2341. // notice that we've allocated enough space for the
  2342. // TokenInformation structure. so if we fail, we
  2343. // return a NULL pointer indicating failure
  2344. if ( !GetTokenInformation( TokenHandle,
  2345. TokenStatistics,
  2346. TokenInformation,
  2347. sizeof( TokenInformation ),
  2348. &ReturnLength ))
  2349. {
  2350. #if DBG
  2351. KdPrint(("GetLuid: GetTokenInformation failed: Error %d\n",
  2352. GetLastError()));
  2353. #endif
  2354. *plogonid = NullId;
  2355. return;
  2356. }
  2357. CloseHandle( TokenHandle );
  2358. *plogonid = ( ((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId );
  2359. return;
  2360. }
  2361. DWORD
  2362. NwCloseAllConnections(
  2363. VOID
  2364. )
  2365. /*++
  2366. Routine Description:
  2367. This routine closes all connections. It is used when stopping the
  2368. redirector.
  2369. Arguments:
  2370. None.
  2371. Return Value:
  2372. NO_ERROR or error
  2373. --*/
  2374. {
  2375. NWR_REQUEST_PACKET Rrp;
  2376. DWORD error;
  2377. Rrp.Version = REQUEST_PACKET_VERSION;
  2378. error = NwRedirFsControl(
  2379. RedirDeviceHandle,
  2380. FSCTL_NWR_CLOSEALL,
  2381. &Rrp,
  2382. sizeof(NWR_REQUEST_PACKET),
  2383. NULL,
  2384. 0,
  2385. NULL
  2386. );
  2387. return error;
  2388. }
  2389. VOID
  2390. GetPreferredServerAddress(
  2391. IN LPWSTR PreferredServerName,
  2392. OUT LPDWORD lpdwReplicaAddressSize,
  2393. OUT LPBYTE lpReplicaAddress
  2394. )
  2395. {
  2396. WCHAR Buffer[1024];
  2397. PWSAQUERYSETW Query = (PWSAQUERYSETW)Buffer;
  2398. HANDLE hRnr;
  2399. DWORD dwQuerySize = 1024;
  2400. GUID gdService = SVCID_NETWARE( 0x4 );
  2401. WSADATA wsaData;
  2402. PWCHAR ServiceInstanceName = PreferredServerName;
  2403. WSAStartup(MAKEWORD(1, 1), &wsaData);
  2404. memset(Query, 0, sizeof(*Query));
  2405. //
  2406. // putting a "*" in the lpszServiceInstanceName causes
  2407. // the query to look for all server instances. Putting a
  2408. // specific name in here will search only for instance of
  2409. // that name. If you have a specific name to look for,
  2410. // put a pointer to the name here.
  2411. //
  2412. Query->lpszServiceInstanceName = ServiceInstanceName;
  2413. Query->dwNameSpace = NS_SAP;
  2414. Query->dwSize = sizeof(*Query);
  2415. Query->lpServiceClassId = &gdService;
  2416. //
  2417. // Find the servers. The flags indicate:
  2418. // LUP_NEAREST: look for nearest servers
  2419. // LUP_DEEP : if none are found on the local segement look
  2420. // for server using a general query
  2421. // LUP_RETURN_NAME: return the name
  2422. // LUP_RETURN_ADDR: return the server address
  2423. //
  2424. // if only servers on the local segment are acceptable, omit
  2425. // setting LUP_DEEP
  2426. //
  2427. if( WSALookupServiceBeginW( Query,
  2428. // LUP_NEAREST |
  2429. LUP_DEEP |
  2430. LUP_RETURN_NAME |
  2431. LUP_RETURN_ADDR,
  2432. &hRnr ) == SOCKET_ERROR )
  2433. {
  2434. //
  2435. // Something went wrong, return no address. The redirector will
  2436. // have to come up with a dir server on its own.
  2437. //
  2438. *lpdwReplicaAddressSize = 0;
  2439. return ;
  2440. }
  2441. else
  2442. {
  2443. //
  2444. // Ready to look for one of them ...
  2445. //
  2446. Query->dwSize = 1024;
  2447. while( WSALookupServiceNextW( hRnr,
  2448. 0,
  2449. &dwQuerySize,
  2450. Query ) == NO_ERROR )
  2451. {
  2452. //
  2453. // Found a dir server, now see if it is a server for the NDS tree
  2454. // TreeName.
  2455. //
  2456. // if ( NwpCompareTreeNames( Query->lpszServiceInstanceName,
  2457. // TreeName ) )
  2458. {
  2459. *lpdwReplicaAddressSize = sizeof(TDI_ADDRESS_IPX);
  2460. memcpy( lpReplicaAddress,
  2461. Query->lpcsaBuffer->RemoteAddr.lpSockaddr->sa_data,
  2462. sizeof(TDI_ADDRESS_IPX) );
  2463. WSALookupServiceEnd(hRnr);
  2464. return ;
  2465. }
  2466. }
  2467. //
  2468. // Could not find a dir server, return no address. The redirector will
  2469. // have to come up with a dir server on its own.
  2470. //
  2471. *lpdwReplicaAddressSize = 0;
  2472. WSALookupServiceEnd(hRnr);
  2473. }
  2474. }