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.

5654 lines
153 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. Attach.c
  5. Abstract:
  6. This module implements the routines for the NetWare
  7. redirector to connect and disconnect from a server.
  8. Author:
  9. Colin Watson [ColinW] 10-Jan-1992
  10. Revision History:
  11. --*/
  12. #include "Procs.h"
  13. #include <stdlib.h> // rand
  14. //
  15. // The number of bytes in the ipx host address, not
  16. // including the socket.
  17. //
  18. #define IPX_HOST_ADDR_LEN 10
  19. //
  20. // The debug trace level
  21. //
  22. #define Dbg (DEBUG_TRACE_CREATE)
  23. VOID
  24. ExtractNextComponentName (
  25. OUT PUNICODE_STRING Name,
  26. IN PUNICODE_STRING Path,
  27. IN BOOLEAN ColonSeparator
  28. );
  29. NTSTATUS
  30. ExtractPathAndFileName(
  31. IN PUNICODE_STRING EntryPath,
  32. OUT PUNICODE_STRING PathString,
  33. OUT PUNICODE_STRING FileName
  34. );
  35. NTSTATUS
  36. DoBinderyLogon(
  37. IN PIRP_CONTEXT pIrpContext,
  38. IN PUNICODE_STRING UserName,
  39. IN PUNICODE_STRING Password
  40. );
  41. NTSTATUS
  42. ConnectToServer(
  43. IN PIRP_CONTEXT pIrpContext,
  44. OUT PSCB *pScbCollision
  45. );
  46. BOOLEAN
  47. ProcessFindNearestEntry(
  48. PIRP_CONTEXT IrpContext,
  49. PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
  50. );
  51. NTSTATUS
  52. GetMaxPacketSize(
  53. PIRP_CONTEXT pIrpContext,
  54. PNONPAGED_SCB pNpScb
  55. );
  56. PNONPAGED_SCB
  57. FindServer(
  58. PIRP_CONTEXT pIrpContext,
  59. PNONPAGED_SCB pNpScb,
  60. PUNICODE_STRING ServerName
  61. );
  62. NTSTATUS
  63. NwAllocateAndInitScb(
  64. IN PIRP_CONTEXT pIrpContext,
  65. IN PUNICODE_STRING UidServerName OPTIONAL,
  66. IN PUNICODE_STRING ServerName OPTIONAL,
  67. OUT PSCB *ppScb
  68. );
  69. NTSTATUS
  70. IndirectToSeedServer(
  71. PIRP_CONTEXT pIrpContext,
  72. PUNICODE_STRING pServerName,
  73. PUNICODE_STRING pNewServer
  74. );
  75. #ifdef ALLOC_PRAGMA
  76. #pragma alloc_text( PAGE, ExtractNextComponentName )
  77. #pragma alloc_text( PAGE, ExtractPathAndFileName )
  78. #pragma alloc_text( PAGE, CrackPath )
  79. #pragma alloc_text( PAGE, CreateScb )
  80. #pragma alloc_text( PAGE, FindServer )
  81. #pragma alloc_text( PAGE, ProcessFindNearestEntry )
  82. #pragma alloc_text( PAGE, NegotiateBurstMode )
  83. #pragma alloc_text( PAGE, GetMaxPacketSize )
  84. #pragma alloc_text( PAGE, NwDeleteScb )
  85. #pragma alloc_text( PAGE, NwLogoffAndDisconnect )
  86. #pragma alloc_text( PAGE, InitializeAttach )
  87. #pragma alloc_text( PAGE, OpenScbSockets )
  88. #pragma alloc_text( PAGE, DoBinderyLogon )
  89. #pragma alloc_text( PAGE, QueryServersAddress )
  90. #pragma alloc_text( PAGE, TreeConnectScb )
  91. #pragma alloc_text( PAGE, TreeDisconnectScb )
  92. #ifndef QFE_BUILD
  93. #pragma alloc_text( PAGE1, ProcessFindNearest )
  94. #pragma alloc_text( PAGE1, NwLogoffAllServers )
  95. #pragma alloc_text( PAGE1, DestroyAllScb )
  96. #pragma alloc_text( PAGE1, SelectConnection )
  97. #pragma alloc_text( PAGE1, NwFindScb )
  98. #pragma alloc_text( PAGE1, ConnectToServer )
  99. #endif
  100. #endif
  101. #if 0 // Not pageable
  102. // see ifndef QFE_BUILD above
  103. #endif
  104. VOID
  105. ExtractNextComponentName (
  106. OUT PUNICODE_STRING Name,
  107. IN PUNICODE_STRING Path,
  108. IN BOOLEAN ColonSeparator
  109. )
  110. /*++
  111. Routine Description:
  112. This routine extracts a the "next" component from a path string.
  113. It assumes that
  114. Arguments:
  115. Name - Returns a pointer to the component.
  116. Path - Supplies a pointer to the backslash seperated pathname.
  117. ColonSeparator - A colon can be used to terminate this component
  118. name.
  119. Return Value:
  120. None
  121. --*/
  122. {
  123. register USHORT i; // Index into Name string.
  124. PAGED_CODE();
  125. if (Path->Length == 0) {
  126. RtlInitUnicodeString(Name, NULL);
  127. return;
  128. }
  129. //
  130. // Initialize the extracted name to the name passed in skipping the
  131. // leading backslash.
  132. //
  133. // DebugTrace(+0, Dbg, "NwExtractNextComponentName = %wZ\n", Path );
  134. Name->Buffer = Path->Buffer + 1;
  135. Name->Length = Path->Length - sizeof(WCHAR);
  136. Name->MaximumLength = Path->MaximumLength - sizeof(WCHAR);
  137. //
  138. // Scan forward finding the terminal "\" in the server name.
  139. //
  140. for (i=0;i<(USHORT)(Name->Length/sizeof(WCHAR));i++) {
  141. if ( Name->Buffer[i] == OBJ_NAME_PATH_SEPARATOR ||
  142. ( ColonSeparator && Name->Buffer[i] == L':' ) ) {
  143. break;
  144. }
  145. }
  146. //
  147. // Update the length and maximum length of the structure
  148. // to match the new length.
  149. //
  150. Name->Length = Name->MaximumLength = (USHORT)(i*sizeof(WCHAR));
  151. }
  152. NTSTATUS
  153. ExtractPathAndFileName (
  154. IN PUNICODE_STRING EntryPath,
  155. OUT PUNICODE_STRING PathString,
  156. OUT PUNICODE_STRING FileName
  157. )
  158. /*++
  159. Routine Description:
  160. This routine cracks the entry path into two pieces, the path and the file
  161. name component at the start of the name.
  162. Arguments:
  163. IN PUNICODE_STRING EntryPath - Supplies the path to disect.
  164. OUT PUNICODE_STRING PathString - Returns the directory containing the file.
  165. OUT PUNICODE_STRING FileName - Returns the file name specified.
  166. Return Value:
  167. NTSTATUS - SUCCESS
  168. --*/
  169. {
  170. UNICODE_STRING Component;
  171. UNICODE_STRING FilePath = *EntryPath;
  172. PAGED_CODE();
  173. // Strip trailing separators
  174. while ( (FilePath.Length != 0) &&
  175. FilePath.Buffer[(FilePath.Length-1)/sizeof(WCHAR)] ==
  176. OBJ_NAME_PATH_SEPARATOR ) {
  177. FilePath.Length -= sizeof(WCHAR);
  178. FilePath.MaximumLength -= sizeof(WCHAR);
  179. }
  180. // PathString will become EntryPath minus FileName and trailing separators
  181. *PathString = FilePath;
  182. // Initialize FileName just incase there are no components at all.
  183. RtlInitUnicodeString( FileName, NULL );
  184. //
  185. // Scan through the current file name to find the entire path
  186. // up to (but not including) the last component in the path.
  187. //
  188. do {
  189. //
  190. // Extract the next component from the name.
  191. //
  192. ExtractNextComponentName(&Component, &FilePath, FALSE);
  193. //
  194. // Bump the "remaining name" pointer by the length of this
  195. // component
  196. //
  197. if (Component.Length != 0) {
  198. FilePath.Length -= Component.Length+sizeof(WCHAR);
  199. FilePath.MaximumLength -= Component.MaximumLength+sizeof(WCHAR);
  200. FilePath.Buffer += (Component.Length/sizeof(WCHAR))+1;
  201. *FileName = Component;
  202. }
  203. } while (Component.Length != 0);
  204. //
  205. // Take the name, subtract the last component of the name
  206. // and concatenate the current path with the new path.
  207. //
  208. if ( FileName->Length != 0 ) {
  209. //
  210. // Set the path's name based on the original name, subtracting
  211. // the length of the name portion (including the "\")
  212. //
  213. PathString->Length -= (FileName->Length + sizeof(WCHAR));
  214. if ( PathString->Length != 0 ) {
  215. PathString->MaximumLength -= (FileName->MaximumLength + sizeof(WCHAR));
  216. } else{
  217. RtlInitUnicodeString( PathString, NULL );
  218. }
  219. } else {
  220. // There was no path or filename
  221. RtlInitUnicodeString( PathString, NULL );
  222. }
  223. return STATUS_SUCCESS;
  224. }
  225. NTSTATUS
  226. CrackPath (
  227. IN PUNICODE_STRING BaseName,
  228. OUT PUNICODE_STRING DriveName,
  229. OUT PWCHAR DriveLetter,
  230. OUT PUNICODE_STRING ServerName,
  231. OUT PUNICODE_STRING VolumeName,
  232. OUT PUNICODE_STRING PathName,
  233. OUT PUNICODE_STRING FileName,
  234. OUT PUNICODE_STRING FullName OPTIONAL
  235. )
  236. /*++
  237. Routine Description:
  238. This routine extracts the relevant portions from BaseName to extract
  239. the components of the user's string.
  240. Arguments:
  241. BaseName - Supplies the base user's path.
  242. DriveName - Supplies a string to hold the drive specifier.
  243. DriveLetter - Returns the drive letter. 0 for none, 'A'-'Z' for
  244. disk drives, '1'-'9' for LPT connections.
  245. ServerName - Supplies a string to hold the remote server name.
  246. VolumeName - Supplies a string to hold the volume name.
  247. PathName - Supplies a string to hold the remaining part of the path.
  248. FileName - Supplies a string to hold the final component of the path.
  249. FullName - Supplies a string to put the Path followed by FileName
  250. Return Value:
  251. NTSTATUS - Status of operation
  252. --*/
  253. {
  254. NTSTATUS Status;
  255. UNICODE_STRING BaseCopy = *BaseName;
  256. UNICODE_STRING ShareName;
  257. PAGED_CODE();
  258. RtlInitUnicodeString( DriveName, NULL);
  259. RtlInitUnicodeString( ServerName, NULL);
  260. RtlInitUnicodeString( VolumeName, NULL);
  261. RtlInitUnicodeString( PathName, NULL);
  262. RtlInitUnicodeString( FileName, NULL);
  263. *DriveLetter = 0;
  264. if (ARGUMENT_PRESENT(FullName)) {
  265. RtlInitUnicodeString( FullName, NULL);
  266. }
  267. //
  268. // If the name is "\", or empty, there is nothing to do.
  269. //
  270. if ( BaseName->Length <= sizeof( WCHAR ) ) {
  271. return STATUS_SUCCESS;
  272. }
  273. ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
  274. //
  275. // Skip over the server name.
  276. //
  277. BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
  278. BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
  279. BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
  280. if ((ServerName->Length == sizeof(L"X:") - sizeof(WCHAR) ) &&
  281. (ServerName->Buffer[(ServerName->Length / sizeof(WCHAR)) - 1] == L':'))
  282. {
  283. //
  284. // The file name is of the form x:\server\volume\foo\bar
  285. //
  286. *DriveName = *ServerName;
  287. *DriveLetter = DriveName->Buffer[0];
  288. RtlInitUnicodeString( ServerName, NULL );
  289. ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
  290. if ( ServerName->Length != 0 ) {
  291. //
  292. // Skip over the server name.
  293. //
  294. BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
  295. BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
  296. BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
  297. }
  298. }
  299. else if ( ( ServerName->Length == sizeof(L"LPTx") - sizeof(WCHAR) ) &&
  300. ( _wcsnicmp( ServerName->Buffer, L"LPT", 3 ) == 0) &&
  301. ( ServerName->Buffer[3] >= '0' && ServerName->Buffer[3] <= '9' ) )
  302. {
  303. //
  304. // The file name is of the form LPTx\server\printq
  305. //
  306. *DriveName = *ServerName;
  307. *DriveLetter = DriveName->Buffer[3];
  308. RtlInitUnicodeString( ServerName, NULL );
  309. ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
  310. if ( ServerName->Length != 0 ) {
  311. //
  312. // Skip over the server name.
  313. //
  314. BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
  315. BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
  316. BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
  317. }
  318. }
  319. if ( ServerName->Length != 0 ) {
  320. //
  321. // The file name is of the form \\server\volume\foo\bar
  322. // Set volume name to server\volume.
  323. //
  324. ExtractNextComponentName( &ShareName, &BaseCopy, TRUE );
  325. //
  326. // Set volume name = \drive:\server\share or \server\share if the
  327. // path is UNC.
  328. //
  329. VolumeName->Buffer = ServerName->Buffer - 1;
  330. if ( ShareName.Length != 0 ) {
  331. VolumeName->Length = ServerName->Length + ShareName.Length + 2 * sizeof( WCHAR );
  332. if ( DriveName->Buffer != NULL ) {
  333. VolumeName->Buffer = DriveName->Buffer - 1;
  334. VolumeName->Length += DriveName->Length + sizeof(WCHAR);
  335. }
  336. BaseCopy.Buffer += ShareName.Length / sizeof(WCHAR) + 1;
  337. BaseCopy.Length -= ShareName.Length + sizeof(WCHAR);
  338. BaseCopy.MaximumLength -= ShareName.MaximumLength + sizeof(WCHAR);
  339. } else {
  340. VolumeName->Length = ServerName->Length + sizeof( WCHAR );
  341. return( STATUS_SUCCESS );
  342. }
  343. VolumeName->MaximumLength = VolumeName->Length;
  344. }
  345. else
  346. {
  347. //
  348. // server name is empty. this should only happen if we are
  349. // opening the redirector itself. if there is volume or other
  350. // components left, fail it.
  351. //
  352. if (BaseCopy.Length > sizeof(WCHAR))
  353. {
  354. return STATUS_BAD_NETWORK_PATH ;
  355. }
  356. }
  357. Status = ExtractPathAndFileName ( &BaseCopy, PathName, FileName );
  358. if (NT_SUCCESS(Status) &&
  359. ARGUMENT_PRESENT(FullName)) {
  360. //
  361. // Use the feature that PathName and FileName are in the same buffer
  362. // to return <pathname>\<filename>
  363. //
  364. if ( PathName->Buffer == NULL ) {
  365. // return just <filename> or NULL
  366. *FullName = *FileName;
  367. } else {
  368. // Set FullFileName to <PathName>'\'<FileName>
  369. FullName->Buffer = PathName->Buffer;
  370. FullName->Length = PathName->Length +
  371. FileName->Length +
  372. sizeof(WCHAR);
  373. FullName->MaximumLength = PathName->MaximumLength +
  374. FileName->MaximumLength +
  375. sizeof(WCHAR);
  376. }
  377. }
  378. return( Status );
  379. }
  380. NTSTATUS
  381. GetServerByAddress(
  382. IN PIRP_CONTEXT pIrpContext,
  383. OUT PSCB *Scb,
  384. IN IPXaddress *pServerAddress
  385. )
  386. /*+++
  387. Description:
  388. This routine looks up a server by address. If it finds a server that
  389. has been connected, it returns it referenced. Otherwise, it returns no
  390. server.
  391. ---*/
  392. {
  393. NTSTATUS Status;
  394. PLIST_ENTRY ScbQueueEntry;
  395. KIRQL OldIrql;
  396. PNONPAGED_SCB pFirstNpScb, pNextNpScb;
  397. PNONPAGED_SCB pFoundNpScb = NULL;
  398. UNICODE_STRING CredentialName;
  399. //
  400. // Start at the head of the SCB list.
  401. //
  402. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  403. if ( ScbQueue.Flink == &ScbQueue ) {
  404. KeReleaseSpinLock( &ScbSpinLock, OldIrql);
  405. return STATUS_UNSUCCESSFUL;
  406. }
  407. ScbQueueEntry = ScbQueue.Flink;
  408. pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
  409. NONPAGED_SCB,
  410. ScbLinks );
  411. pNextNpScb = pFirstNpScb;
  412. //
  413. // Leave the first SCB referenced since we need it to
  414. // be there for when we walk all the way around the list.
  415. //
  416. NwReferenceScb( pFirstNpScb );
  417. NwReferenceScb( pNextNpScb );
  418. KeReleaseSpinLock( &ScbSpinLock, OldIrql);
  419. while ( TRUE ) {
  420. //
  421. // Check to see if the SCB address matches the address we have
  422. // and if the user uid matches the uid for this request. Skip
  423. // matches that are abandoned anonymous creates.
  424. //
  425. if ( pNextNpScb->pScb ) {
  426. if ( ( RtlCompareMemory( (BYTE *) pServerAddress,
  427. (BYTE *) &pNextNpScb->ServerAddress,
  428. IPX_HOST_ADDR_LEN ) == IPX_HOST_ADDR_LEN ) &&
  429. ( pIrpContext->Specific.Create.UserUid.QuadPart ==
  430. pNextNpScb->pScb->UserUid.QuadPart ) &&
  431. ( pNextNpScb->State != SCB_STATE_FLAG_SHUTDOWN ) &&
  432. ( !IS_ANONYMOUS_SCB( pNextNpScb->pScb ) ) ) {
  433. if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
  434. //
  435. // If this isn't an ex-create server, you can't use
  436. // it for this operation.
  437. //
  438. if ( !IsCredentialName( &(pNextNpScb->ServerName) ) ) {
  439. goto ContinueLoop;
  440. }
  441. //
  442. // On a credential create, the credential supplied has
  443. // to match the extended credential for the server.
  444. //
  445. Status = GetCredentialFromServerName( &pNextNpScb->ServerName,
  446. &CredentialName );
  447. if ( !NT_SUCCESS( Status ) ) {
  448. goto ContinueLoop;
  449. }
  450. if ( RtlCompareUnicodeString( &CredentialName,
  451. pIrpContext->Specific.Create.puCredentialName,
  452. TRUE ) ) {
  453. goto ContinueLoop;
  454. }
  455. } else {
  456. //
  457. // If this is an ex-create server, you can't use it for
  458. // this operation.
  459. //
  460. if ( IsCredentialName( &(pNextNpScb->ServerName) ) ) {
  461. goto ContinueLoop;
  462. }
  463. }
  464. pFoundNpScb = pNextNpScb;
  465. DebugTrace( 0, Dbg, "GetServerByAddress: %wZ\n", &pFoundNpScb->ServerName );
  466. break;
  467. }
  468. }
  469. ContinueLoop:
  470. //
  471. // Otherwise, get the next one in the list. Don't
  472. // forget to skip the list head.
  473. //
  474. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  475. ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
  476. if ( ScbQueueEntry == &ScbQueue ) {
  477. ScbQueueEntry = ScbQueue.Flink;
  478. }
  479. NwDereferenceScb( pNextNpScb );
  480. pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
  481. if ( pNextNpScb == pFirstNpScb ) {
  482. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  483. break;
  484. }
  485. //
  486. // Otherwise, reference this SCB and continue.
  487. //
  488. NwReferenceScb( pNextNpScb );
  489. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  490. }
  491. NwDereferenceScb( pFirstNpScb );
  492. if ( pFoundNpScb ) {
  493. *Scb = pFoundNpScb->pScb;
  494. return STATUS_SUCCESS;
  495. }
  496. return STATUS_UNSUCCESSFUL;
  497. }
  498. NTSTATUS
  499. CheckScbSecurity(
  500. IN PIRP_CONTEXT pIrpContext,
  501. IN PSCB pScb,
  502. IN PUNICODE_STRING puUserName,
  503. IN PUNICODE_STRING puPassword,
  504. IN BOOLEAN fDeferLogon
  505. )
  506. /*+++
  507. You must be at the head of the queue to call this function.
  508. This function makes sure that the Scb is valid for the user
  509. that requested it.
  510. ---*/
  511. {
  512. NTSTATUS Status;
  513. BOOLEAN SecurityConflict = FALSE;
  514. ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
  515. //
  516. // If there's no user name or password, there's no conflict.
  517. //
  518. if ( ( puUserName == NULL ) &&
  519. ( puPassword == NULL ) ) {
  520. return STATUS_SUCCESS;
  521. }
  522. if ( pScb->UserName.Length &&
  523. pScb->UserName.Buffer ) {
  524. //
  525. // Do a bindery security check if we were bindery
  526. // authenticated to this server.
  527. //
  528. if ( !fDeferLogon &&
  529. puUserName != NULL &&
  530. puUserName->Buffer != NULL ) {
  531. ASSERT( pScb->Password.Buffer != NULL );
  532. if ( !RtlEqualUnicodeString( &pScb->UserName, puUserName, TRUE ) ||
  533. ( puPassword &&
  534. puPassword->Buffer &&
  535. puPassword->Length &&
  536. !RtlEqualUnicodeString( &pScb->Password, puPassword, TRUE ) )) {
  537. SecurityConflict = TRUE;
  538. }
  539. }
  540. } else {
  541. //
  542. // Do an nds security check.
  543. //
  544. Status = NdsCheckCredentials( pIrpContext,
  545. puUserName,
  546. puPassword );
  547. if ( !NT_SUCCESS( Status )) {
  548. SecurityConflict = TRUE;
  549. }
  550. }
  551. //
  552. // If there was a security conflict, see if we can just
  553. // take this connection over (i.e. there are no open
  554. // files or open handles to the server).
  555. //
  556. if ( SecurityConflict ) {
  557. if ( ( pScb->OpenFileCount == 0 ) &&
  558. ( pScb->IcbCount == 0 ) ) {
  559. if ( pScb->UserName.Buffer ) {
  560. FREE_POOL( pScb->UserName.Buffer );
  561. }
  562. RtlInitUnicodeString( &pScb->UserName, NULL );
  563. RtlInitUnicodeString( &pScb->Password, NULL );
  564. pScb->pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
  565. } else {
  566. DebugTrace( 0, Dbg, "SCB security conflict.\n", 0 );
  567. return STATUS_NETWORK_CREDENTIAL_CONFLICT;
  568. }
  569. }
  570. DebugTrace( 0, Dbg, "SCB security check succeeded.\n", 0 );
  571. return STATUS_SUCCESS;
  572. }
  573. NTSTATUS
  574. GetScb(
  575. OUT PSCB *Scb,
  576. IN PIRP_CONTEXT pIrpContext,
  577. IN PUNICODE_STRING Server,
  578. IN IPXaddress *pServerAddress,
  579. IN PUNICODE_STRING UserName,
  580. IN PUNICODE_STRING Password,
  581. IN BOOLEAN DeferLogon,
  582. OUT PBOOLEAN Existing
  583. )
  584. /*+++
  585. Description:
  586. This routine locates an existing SCB or creates a new SCB.
  587. This is the first half of the original CreateScb routine.
  588. Locks:
  589. See the anonymous create information in CreateScb().
  590. ---*/
  591. {
  592. NTSTATUS Status;
  593. PSCB pScb = NULL;
  594. PNONPAGED_SCB pNpScb = NULL;
  595. BOOLEAN ExistingScb = TRUE;
  596. UNICODE_STRING UidServer;
  597. UNICODE_STRING ExCredName;
  598. PUNICODE_STRING puConnectName;
  599. KIRQL OldIrql;
  600. DebugTrace( 0, Dbg, "GetScb... %wZ\n", Server );
  601. if ( pServerAddress != NULL ) {
  602. DebugTrace( 0, Dbg, " ->Server Address = (provided)\n", 0 );
  603. } else {
  604. DebugTrace( 0, Dbg, " ->Server Address = NULL\n", 0 );
  605. }
  606. RtlInitUnicodeString( &UidServer, NULL );
  607. if ( ( Server == NULL ) ||
  608. ( Server->Length == 0 ) ) {
  609. //
  610. // No server name was provided. Either this is a connect by address,
  611. // or a connect to a nearby bindery server (defaulting to the preferred
  612. // server).
  613. //
  614. if ( pServerAddress == NULL ) {
  615. //
  616. // No server address was provided, so this is an attempt to open
  617. // a nearby bindery server.
  618. //
  619. while (TRUE) {
  620. //
  621. // The loop checks that after we get to the front, the SCB
  622. // is still in the state we wanted. If not, we need to
  623. // reselect another.
  624. //
  625. pNpScb = SelectConnection( NULL );
  626. //
  627. // Note: We'd like to call SelectConnection with the pNpScb
  628. // that we last tried, but if the scavenger runs before
  629. // this loop gets back to the select connection, we could
  630. // pass a bum pointer to SelectConnection, which is bad.
  631. //
  632. if ( pNpScb != NULL) {
  633. pScb = pNpScb->pScb;
  634. //
  635. // Queue ourselves to the SCB, wait to get to the front to
  636. // protect access to server State.
  637. //
  638. pIrpContext->pNpScb = pNpScb;
  639. pIrpContext->pScb = pScb;
  640. NwAppendToQueueAndWait( pIrpContext );
  641. //
  642. // These states have to match the conditions of the
  643. // SelectConnection to prevent an infinite loop.
  644. //
  645. if (!((pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
  646. (pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
  647. (pNpScb->State == SCB_STATE_IN_USE ))) {
  648. //
  649. // No good any more as default server, select another.
  650. //
  651. pScb = NULL ;
  652. NwDequeueIrpContext( pIrpContext, FALSE );
  653. NwDereferenceScb( pNpScb );
  654. continue ;
  655. }
  656. }
  657. //
  658. // otherwise, we're done
  659. //
  660. break ;
  661. }
  662. } else {
  663. //
  664. // An address was provided, so we are attempting to do a lookup
  665. // based on address. The server that we are looking for might
  666. // exist but not yet have its address recorded, so if we do an
  667. // anonymous create, we have to check at the end whether or not
  668. // someone else came in and successfully created while we were
  669. // looking up the name.
  670. //
  671. // We don't have to hold the RCB anymore since colliding creates
  672. // have to be handled gracefully anyway.
  673. //
  674. Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
  675. if ( !NT_SUCCESS( Status ) ) {
  676. PLIST_ENTRY pTemp;
  677. //
  678. // No anonymous creates are allowed if we are not allowed
  679. // to send packets to the net (because it's not possible for
  680. // us to resolve the address to a name).
  681. //
  682. if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
  683. return STATUS_BAD_NETWORK_PATH;
  684. }
  685. //
  686. // There's no connection to this server, so we'll
  687. // have to create one. Let's start with an anonymous
  688. // Scb.
  689. //
  690. Status = NwAllocateAndInitScb( pIrpContext,
  691. NULL,
  692. NULL,
  693. &pScb );
  694. if ( !NT_SUCCESS( Status )) {
  695. return Status;
  696. }
  697. //
  698. // We've made the anonymous create, so put it on the scb
  699. // list and get to the head of the queue.
  700. //
  701. SetFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
  702. pIrpContext->pScb = pScb;
  703. pIrpContext->pNpScb = pScb->pNpScb;
  704. ExInterlockedInsertHeadList( &pScb->pNpScb->Requests,
  705. &pIrpContext->NextRequest,
  706. &pScb->pNpScb->NpScbSpinLock );
  707. pTemp = &pScb->pNpScb->ScbLinks;
  708. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  709. InsertTailList(&ScbQueue, pTemp);
  710. KeReleaseSpinLock(&ScbSpinLock, OldIrql);
  711. DebugTrace( 0, Dbg, "GetScb started an anonymous create.\n", 0 );
  712. ExistingScb = FALSE;
  713. } else {
  714. //
  715. // Get to the head of the queue and see if this was
  716. // an abandoned anonymous create. If so, get the
  717. // right server and continue.
  718. //
  719. pIrpContext->pScb = pScb;
  720. pIrpContext->pNpScb = pScb->pNpScb;
  721. NwAppendToQueueAndWait( pIrpContext );
  722. if ( pScb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) {
  723. //
  724. // The create abandoned this scb, redoing a
  725. // GetServerByAddress() is guaranteed to get
  726. // us a good server if there is a server out
  727. // there.
  728. //
  729. NwDequeueIrpContext( pIrpContext, FALSE );
  730. NwDereferenceScb( pScb->pNpScb );
  731. Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
  732. if ( NT_SUCCESS( Status ) ) {
  733. ASSERT( pScb != NULL );
  734. ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
  735. }
  736. } else {
  737. ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
  738. }
  739. }
  740. ASSERT( pScb != NULL );
  741. pNpScb = pScb->pNpScb;
  742. }
  743. } else {
  744. //
  745. // A server name was provided, so we are doing a straight
  746. // lookup or create by name. Do we need to munge the name
  747. // for a supplemental credential connect?
  748. //
  749. RtlInitUnicodeString( &ExCredName, NULL );
  750. if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
  751. ( !IsCredentialName( Server ) ) ) {
  752. Status = BuildExCredentialServerName( Server,
  753. pIrpContext->Specific.Create.puCredentialName,
  754. &ExCredName );
  755. if ( !NT_SUCCESS( Status ) ) {
  756. return Status;
  757. }
  758. puConnectName = &ExCredName;
  759. } else {
  760. puConnectName = Server;
  761. }
  762. Status = MakeUidServer( &UidServer,
  763. &pIrpContext->Specific.Create.UserUid,
  764. puConnectName );
  765. if ( ExCredName.Buffer ) {
  766. FREE_POOL( ExCredName.Buffer );
  767. }
  768. if (!NT_SUCCESS(Status)) {
  769. return Status;
  770. }
  771. DebugTrace( 0, Dbg, " ->UidServer = %wZ\n", &UidServer );
  772. ExistingScb = NwFindScb( &pScb, pIrpContext, &UidServer, Server );
  773. ASSERT( pScb != NULL );
  774. pNpScb = pScb->pNpScb;
  775. pIrpContext->pNpScb = pNpScb;
  776. pIrpContext->pScb = pScb;
  777. NwAppendToQueueAndWait(pIrpContext);
  778. }
  779. //
  780. // 1) We may or may not have a server (evidenced by pScb).
  781. //
  782. // 2) If we have a server and ExistingScb is TRUE, we have
  783. // an existing server, possibly already connected.
  784. // Otherwise, we have a newly created server that
  785. // may or may not be anonymous.
  786. //
  787. // 3) If we are logged into this server make sure the supplied
  788. // username and password, match the username and password
  789. // that we logged in with.
  790. //
  791. if ( ( pScb ) && ( ExistingScb ) ) {
  792. if ( pNpScb->State == SCB_STATE_IN_USE ) {
  793. Status = CheckScbSecurity( pIrpContext,
  794. pScb,
  795. UserName,
  796. Password,
  797. DeferLogon );
  798. if ( !NT_SUCCESS( Status ) ) {
  799. if ( UidServer.Buffer != NULL ) {
  800. FREE_POOL( UidServer.Buffer );
  801. }
  802. NwDequeueIrpContext( pIrpContext, FALSE );
  803. NwDereferenceScb( pNpScb );
  804. return Status;
  805. }
  806. }
  807. }
  808. *Scb = pScb;
  809. *Existing = ExistingScb;
  810. #ifdef NWDBG
  811. if ( pScb != NULL ) {
  812. //
  813. // If we have a server, the SCB is referenced and we will
  814. // be at the head of the queue.
  815. //
  816. ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
  817. }
  818. #endif
  819. if ( UidServer.Buffer != NULL ) {
  820. FREE_POOL( UidServer.Buffer );
  821. }
  822. DebugTrace( 0, Dbg, "GetScb returned %08lx\n", pScb );
  823. return STATUS_SUCCESS;
  824. }
  825. NTSTATUS
  826. ConnectScb(
  827. IN PSCB *Scb,
  828. IN PIRP_CONTEXT pIrpContext,
  829. IN PUNICODE_STRING Server,
  830. IN IPXaddress *pServerAddress,
  831. IN PUNICODE_STRING UserName,
  832. IN PUNICODE_STRING Password,
  833. IN BOOLEAN DeferLogon,
  834. IN BOOLEAN DeleteConnection,
  835. IN BOOLEAN ExistingScb
  836. )
  837. /*+++
  838. Description:
  839. This routine puts the provided scb in the connected state.
  840. This is the second half of the original CreateScb routine.
  841. Arguments:
  842. Scb - The scb for the server we want to connect.
  843. pIrpContext - The context for this request.
  844. Server - The name of the server, or NULL.
  845. pServerAddress - The address of the server, or NULL,
  846. UserName - The name of the user to connect as, or NULL.
  847. Password - The password for the user, or NULL.
  848. DeferLogon - Should we defer the logon?
  849. DeleteConnection - Should we succeed even without the net so that
  850. the delete request will succeed?
  851. ExistingScb - Is this an existing SCB?
  852. If the SCB is anonymous, we need to safely check for colliding
  853. creates when we find out who the server is.
  854. If this is a reconnect attempt, this routine will not dequeue the
  855. irp context, which could cause a deadlock in the reconnect logic.
  856. ---*/
  857. {
  858. NTSTATUS Status = STATUS_SUCCESS;
  859. PSCB pScb = *Scb;
  860. PNONPAGED_SCB pNpScb = NULL;
  861. BOOLEAN AnonymousScb = FALSE;
  862. PSCB pCollisionScb = NULL;
  863. NTSTATUS LoginStatus;
  864. BOOLEAN TriedNdsLogin;
  865. PLOGON pLogon;
  866. BOOLEAN DeferredLogon = DeferLogon;
  867. PNDS_SECURITY_CONTEXT pNdsContext;
  868. NTSTATUS CredStatus;
  869. DebugTrace( 0, Dbg, "ConnectScb... %08lx\n", pScb );
  870. //
  871. // If we already have an SCB, find out where in the
  872. // connect chain we need to start off.
  873. //
  874. if ( pScb ) {
  875. pNpScb = pScb->pNpScb;
  876. AnonymousScb = IS_ANONYMOUS_SCB( pScb );
  877. if ( ExistingScb ) {
  878. ASSERT( !AnonymousScb );
  879. //
  880. // If this SCB is in STATE_ATTACHING, we need to check
  881. // the address in the SCB to make sure that it was at one
  882. // point a valid server. If it wasn't, then we shouldn't
  883. // honor this create because it's probably a tree create.
  884. //
  885. if ( DeleteConnection ) {
  886. ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) );
  887. if ( ( pNpScb->State == SCB_STATE_ATTACHING ) &&
  888. ( (pNpScb->ServerAddress).Socket == 0 ) ) {
  889. Status = STATUS_BAD_NETWORK_PATH;
  890. goto CleanupAndExit;
  891. } else {
  892. NwDequeueIrpContext( pIrpContext, FALSE );
  893. return STATUS_SUCCESS;
  894. }
  895. }
  896. RedoConnect:
  897. if ( pNpScb->State == SCB_STATE_ATTACHING ) {
  898. goto GetAddress;
  899. } else if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
  900. goto Connect;
  901. } else if ( pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
  902. goto Login;
  903. } else if ( pNpScb->State == SCB_STATE_IN_USE ) {
  904. goto InUse;
  905. } else {
  906. DebugTrace( 0, Dbg, "ConnectScb: Unknown Scb State %08lx\n", pNpScb->State );
  907. Status = STATUS_INVALID_PARAMETER;
  908. goto CleanupAndExit;
  909. }
  910. } else {
  911. //
  912. // This is a new SCB, we have to run through the whole routine.
  913. //
  914. pNpScb->State = SCB_STATE_ATTACHING;
  915. }
  916. }
  917. GetAddress:
  918. //
  919. // Set the reroute attempted bit so that we don't try
  920. // to reconnect during the connect.
  921. //
  922. SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
  923. if ( !pServerAddress ) {
  924. //
  925. // If we don't have an address, this SCB cannot be anonymous!!
  926. //
  927. ASSERT( !AnonymousScb );
  928. //
  929. // We have to cast an exception frame for this legacy routine
  930. // that still uses structured exceptions.
  931. //
  932. try {
  933. pNpScb = FindServer( pIrpContext, pNpScb, Server );
  934. ASSERT( pNpScb != NULL );
  935. //
  936. // This is redundant unless the starting server was NULL.
  937. // FindServer returns the same SCB we provided to it
  938. // unless we called it with NULL.
  939. //
  940. pScb = pNpScb->pScb;
  941. pIrpContext->pNpScb = pNpScb;
  942. pIrpContext->pScb = pScb;
  943. NwAppendToQueueAndWait( pIrpContext );
  944. } except ( EXCEPTION_EXECUTE_HANDLER ) {
  945. Status = GetExceptionCode();
  946. goto CleanupAndExit;
  947. }
  948. } else {
  949. //
  950. // Build the address into the NpScb since we already know it.
  951. //
  952. RtlCopyMemory( &pNpScb->ServerAddress,
  953. pServerAddress,
  954. sizeof( TDI_ADDRESS_IPX ) );
  955. if ( pNpScb->ServerAddress.Socket != NCP_SOCKET ) {
  956. DebugTrace( 0, DEBUG_TRACE_ALWAYS, "CreateScb supplied server socket is deviant.\n", 0 );
  957. }
  958. BuildIpxAddress( pNpScb->ServerAddress.Net,
  959. pNpScb->ServerAddress.Node,
  960. pNpScb->ServerAddress.Socket,
  961. &pNpScb->RemoteAddress );
  962. pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  963. }
  964. Connect:
  965. //
  966. // FindServer may have connected us to the server already,
  967. // so we may be able to skip the reconnect here.
  968. //
  969. if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
  970. //
  971. // If this is an anonymous scb, we have to be prepared
  972. // for ConnectToServer() to find that we've already connected
  973. // this server by name. In this case, we cancel the
  974. // anonymous create and use the server that was created
  975. // while we were looking up the name.
  976. //
  977. Status = ConnectToServer( pIrpContext, &pCollisionScb );
  978. if (!NT_SUCCESS(Status)) {
  979. goto CleanupAndExit;
  980. }
  981. //
  982. // We succeeded. If there's a collision scb, then we need to
  983. // abandon the anonymous scb and use the scb that we collided
  984. // with. Otherwise, we successfully completed an anonymous
  985. // connect and can go on with the create normally.
  986. //
  987. if ( pCollisionScb ) {
  988. ASSERT( AnonymousScb );
  989. //
  990. // Deref and dequeue from the abandoned server.
  991. //
  992. NwDequeueIrpContext( pIrpContext, FALSE );
  993. NwDereferenceScb( pIrpContext->pNpScb );
  994. //
  995. // Queue to the appropriate server.
  996. //
  997. pIrpContext->pScb = pCollisionScb;
  998. pIrpContext->pNpScb = pCollisionScb->pNpScb;
  999. NwAppendToQueueAndWait( pIrpContext );
  1000. pScb = pCollisionScb;
  1001. pNpScb = pCollisionScb->pNpScb;
  1002. *Scb = pCollisionScb;
  1003. //
  1004. // Re-start connecting the scb.
  1005. //
  1006. AnonymousScb = FALSE;
  1007. ExistingScb = TRUE;
  1008. pCollisionScb = NULL;
  1009. DebugTrace( 0, Dbg, "Re-doing connect on anonymous collision.\n", 0 );
  1010. goto RedoConnect;
  1011. }
  1012. DebugTrace( +0, Dbg, " Logout from server - just in case\n", 0);
  1013. Status = ExchangeWithWait (
  1014. pIrpContext,
  1015. SynchronousResponseCallback,
  1016. "F",
  1017. NCP_LOGOUT );
  1018. DebugTrace( +0, Dbg, " %X\n", Status);
  1019. if ( !NT_SUCCESS( Status ) ) {
  1020. goto CleanupAndExit;
  1021. }
  1022. DebugTrace( +0, Dbg, " Connect to real server = %X\n", Status);
  1023. pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
  1024. }
  1025. Login:
  1026. //
  1027. // If we have credentials for the tree and this server was named
  1028. // explicitly, we shouldn't defer the login or else the browse
  1029. // view of the tree may be wrong. For this reason, NdsServerAuthenticate
  1030. // has to be a straight shot call and can't remove us from the head
  1031. // of the queue.
  1032. //
  1033. if ( ( ( Server != NULL ) || ( pServerAddress != NULL ) ) &&
  1034. ( DeferredLogon ) &&
  1035. ( pScb->MajorVersion > 3 ) &&
  1036. ( pScb->UserName.Length == 0 ) ) {
  1037. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  1038. pLogon = FindUser( &pScb->UserUid, FALSE );
  1039. NwReleaseRcb( &NwRcb );
  1040. if ( pLogon ) {
  1041. CredStatus = NdsLookupCredentials( pIrpContext,
  1042. &pScb->NdsTreeName,
  1043. pLogon,
  1044. &pNdsContext,
  1045. CREDENTIAL_READ,
  1046. FALSE );
  1047. if ( NT_SUCCESS( CredStatus ) ) {
  1048. if ( ( pNdsContext->Credential != NULL ) &&
  1049. ( pNdsContext->CredentialLocked == FALSE ) ) {
  1050. DebugTrace( 0, Dbg, "Forcing authentication to %wZ.\n",
  1051. &pScb->UidServerName );
  1052. DeferredLogon = FALSE;
  1053. }
  1054. NwReleaseCredList( pLogon, pIrpContext );
  1055. }
  1056. }
  1057. }
  1058. if (pNpScb->State == SCB_STATE_LOGIN_REQUIRED && !DeferredLogon ) {
  1059. //
  1060. // NOTE: DoBinderyLogon() and DoNdsLogon() may return a
  1061. // warning status. If they do, we must return the
  1062. // warning status to the caller.
  1063. //
  1064. Status = STATUS_UNSUCCESSFUL;
  1065. TriedNdsLogin = FALSE;
  1066. //
  1067. // We force a bindery login for a non 4.x server. Otherwise, we
  1068. // allow a fall-back from NDS style authentication to bindery style
  1069. // authentication.
  1070. //
  1071. if ( pScb->MajorVersion >= 4 ) {
  1072. ASSERT( pScb->NdsTreeName.Length != 0 );
  1073. Status = DoNdsLogon( pIrpContext, UserName, Password );
  1074. if ( NT_SUCCESS( Status ) ) {
  1075. //
  1076. // Do we need to re-license the connection?
  1077. //
  1078. if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
  1079. Status = NdsLicenseConnection( pIrpContext );
  1080. if ( !NT_SUCCESS( Status ) ) {
  1081. Status = STATUS_REMOTE_SESSION_LIMIT;
  1082. }
  1083. }
  1084. }
  1085. TriedNdsLogin = TRUE;
  1086. LoginStatus = Status;
  1087. }
  1088. if ( !NT_SUCCESS( Status ) ) {
  1089. Status = DoBinderyLogon( pIrpContext, UserName, Password );
  1090. }
  1091. if ( !NT_SUCCESS( Status ) ) {
  1092. if ( TriedNdsLogin ) {
  1093. //
  1094. // Both login attempts have failed. We usually prefer
  1095. // the NDS status, but not always.
  1096. //
  1097. if ( ( Status != STATUS_WRONG_PASSWORD ) &&
  1098. ( Status != STATUS_ACCOUNT_DISABLED ) ) {
  1099. Status = LoginStatus;
  1100. }
  1101. }
  1102. //
  1103. // Couldn't log on, be good boys and disconnect.
  1104. //
  1105. ExchangeWithWait (
  1106. pIrpContext,
  1107. SynchronousResponseCallback,
  1108. "D-" ); // Disconnect
  1109. Stats.Sessions--;
  1110. if ( pScb->MajorVersion == 2 ) {
  1111. Stats.NW2xConnects--;
  1112. } else if ( pScb->MajorVersion == 3 ) {
  1113. Stats.NW3xConnects--;
  1114. } else if ( pScb->MajorVersion >= 4 ) {
  1115. Stats.NW4xConnects--;
  1116. }
  1117. //
  1118. // Demote this scb to reconnect required and exit.
  1119. //
  1120. pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  1121. goto CleanupAndExit;
  1122. }
  1123. pNpScb->State = SCB_STATE_IN_USE;
  1124. }
  1125. //
  1126. // We have to be at the head of the queue to do the reconnect.
  1127. //
  1128. if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
  1129. ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
  1130. } else {
  1131. NwAppendToQueueAndWait( pIrpContext );
  1132. }
  1133. ReconnectScb( pIrpContext, pScb );
  1134. InUse:
  1135. //
  1136. // Ok, we've completed the connect routine. Return this good server.
  1137. //
  1138. *Scb = pScb;
  1139. CleanupAndExit:
  1140. //
  1141. // The reconnect path must not do anything to remove the irp context from
  1142. // the head of the queue since it also owns the irp context in the second
  1143. // position on the queue and that irp context is running.
  1144. //
  1145. if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
  1146. NwDequeueIrpContext( pIrpContext, FALSE );
  1147. }
  1148. DebugTrace( 0, Dbg, "ConnectScb: Connected %08lx\n", pScb );
  1149. DebugTrace( 0, Dbg, "ConnectScb: Status was %08lx\n", Status );
  1150. return Status;
  1151. }
  1152. NTSTATUS
  1153. CreateScb(
  1154. OUT PSCB *Scb,
  1155. IN PIRP_CONTEXT pIrpContext,
  1156. IN PUNICODE_STRING Server,
  1157. IN IPXaddress *pServerAddress,
  1158. IN PUNICODE_STRING UserName,
  1159. IN PUNICODE_STRING Password,
  1160. IN BOOLEAN DeferLogon,
  1161. IN BOOLEAN DeleteConnection
  1162. )
  1163. /*++
  1164. Routine Description:
  1165. This routine connects to the requested server.
  1166. The following mix of parameters are valid:
  1167. Server Name, No Net Address - The routine will look up
  1168. up the SCB or create a new one if necessary, getting
  1169. the server address from a nearby bindery.
  1170. No Server Name, Valid Net Address - The routine will
  1171. look up the SCB by address or create a new one if
  1172. necessary. The name of the server will be set in
  1173. the SCB upon return.
  1174. Server Name, Valid Net Address - The routine will look
  1175. up the SCB by name or will create a new one if
  1176. necessary. The supplied server address will be used,
  1177. sparing a bindery query.
  1178. No Server Name, No Net Address - A connection to the
  1179. preferred server or a nearby server will be returned.
  1180. Arguments:
  1181. Scb - The pointer to the scb in question.
  1182. pIrpContext - The information for this request.
  1183. Server - The name of the server, or NULL.
  1184. pServerAddress - The address of the server, or NULL.
  1185. UserName - The username for the connect, or NULL.
  1186. Password - The password for the connect, or NULL.
  1187. DeferLogon - Should we defer the logon until later?
  1188. DeleteConnection - Should we allow this even when there's no
  1189. net response so that the connection can
  1190. be deleted?
  1191. Return Value:
  1192. NTSTATUS - Status of operation. If the return status is STATUS_SUCCESS,
  1193. then Scb must point to a valid Scb. The irp context pointers will also
  1194. be set, but the irp context will not be on the scb queue.
  1195. --*/
  1196. {
  1197. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  1198. PSCB pScb = NULL;
  1199. PNONPAGED_SCB pOriginalNpScb = pIrpContext->pNpScb;
  1200. PSCB pOriginalScb = pIrpContext->pScb;
  1201. BOOLEAN ExistingScb = FALSE;
  1202. BOOLEAN AnonymousScb = FALSE;
  1203. PLOGON pLogon;
  1204. PNDS_SECURITY_CONTEXT pNdsContext;
  1205. PAGED_CODE();
  1206. DebugTrace(+1, Dbg, "CreateScb....\n", 0);
  1207. //
  1208. // Do not allow any SCB opens unless the redirector is running
  1209. // unless they are no connect creates and we are waiting to bind.
  1210. //
  1211. if ( NwRcb.State != RCB_STATE_RUNNING ) {
  1212. if ( ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ||
  1213. ( NwRcb.State != RCB_STATE_NEED_BIND ) ) ) {
  1214. *Scb = NULL;
  1215. DebugTrace( -1, Dbg, "CreateScb -> %08lx\n", STATUS_REDIRECTOR_NOT_STARTED );
  1216. return STATUS_REDIRECTOR_NOT_STARTED;
  1217. }
  1218. }
  1219. if ( UserName != NULL ) {
  1220. DebugTrace( 0, Dbg, " ->UserName = %wZ\n", UserName );
  1221. } else {
  1222. DebugTrace( 0, Dbg, " ->UserName = NULL\n", 0 );
  1223. }
  1224. if ( Password != NULL ) {
  1225. DebugTrace( 0, Dbg, " ->Password = %wZ\n", Password );
  1226. } else {
  1227. DebugTrace( 0, Dbg, " ->Password = NULL\n", 0 );
  1228. }
  1229. //
  1230. // Get the SCB for this server.
  1231. //
  1232. Status = GetScb( &pScb,
  1233. pIrpContext,
  1234. Server,
  1235. pServerAddress,
  1236. UserName,
  1237. Password,
  1238. DeferLogon,
  1239. &ExistingScb );
  1240. if ( !NT_SUCCESS( Status ) ) {
  1241. *Scb = NULL;
  1242. return Status;
  1243. }
  1244. //
  1245. // At this point, we may or may not have an SCB.
  1246. //
  1247. // If we have an SCB, we know:
  1248. //
  1249. // 1. The scb is referenced.
  1250. // 2. We are at the head of the queue.
  1251. //
  1252. // IMPORTANT POINT: The SCB may be anonymous. If it is,
  1253. // we do not hold the RCB, but rather we have to re-check
  1254. // whether or not the server has shown up via a different
  1255. // create when we find out who the anonymous server is.
  1256. // We do this because there is a window where we have a
  1257. // servers name but not its address and so our lookup by
  1258. // address might be inaccurate.
  1259. //
  1260. if ( ( pScb ) && IS_ANONYMOUS_SCB( pScb ) ) {
  1261. AnonymousScb = TRUE;
  1262. }
  1263. //
  1264. // If we have a fully connected SCB, we need to go no further.
  1265. //
  1266. if ( ( pScb ) && ( pScb->pNpScb->State == SCB_STATE_IN_USE ) ) {
  1267. ASSERT( !AnonymousScb );
  1268. if ( ( pScb->MajorVersion >= 4 ) &&
  1269. ( pScb->UserName.Buffer == NULL ) ) {
  1270. //
  1271. // This is an NDS authenticated server and we have
  1272. // to make sure the credentials aren't locked for
  1273. // logout.
  1274. //
  1275. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  1276. pLogon = FindUser( &pScb->UserUid, FALSE );
  1277. NwReleaseRcb( &NwRcb );
  1278. if ( pLogon ) {
  1279. Status = NdsLookupCredentials( pIrpContext,
  1280. &pScb->NdsTreeName,
  1281. pLogon,
  1282. &pNdsContext,
  1283. CREDENTIAL_READ,
  1284. FALSE );
  1285. if ( NT_SUCCESS( Status ) ) {
  1286. if ( ( pNdsContext->Credential != NULL ) &&
  1287. ( pNdsContext->CredentialLocked == TRUE ) ) {
  1288. DebugTrace( 0, Dbg, "Denying create... we're logging out.\n", 0 );
  1289. Status = STATUS_DEVICE_BUSY;
  1290. }
  1291. NwReleaseCredList( pLogon, pIrpContext );
  1292. }
  1293. }
  1294. }
  1295. NwDequeueIrpContext( pIrpContext, FALSE );
  1296. //
  1297. // We must not change the irp context pointers if we're going
  1298. // to fail this call or we may mess up ref counts and what not.
  1299. //
  1300. if ( NT_SUCCESS( Status ) ) {
  1301. *Scb = pScb;
  1302. } else {
  1303. *Scb = NULL;
  1304. NwDereferenceScb( pScb->pNpScb );
  1305. pIrpContext->pNpScb = pOriginalNpScb;
  1306. pIrpContext->pScb = pOriginalScb;
  1307. }
  1308. DebugTrace( -1, Dbg, "CreateScb: pScb = %08lx\n", pScb );
  1309. return Status;
  1310. }
  1311. //
  1312. // Run through the connect routines for this scb. The scb may
  1313. // be NULL if we're still looking for a nearby server.
  1314. //
  1315. Status = ConnectScb( &pScb,
  1316. pIrpContext,
  1317. Server,
  1318. pServerAddress,
  1319. UserName,
  1320. Password,
  1321. DeferLogon,
  1322. DeleteConnection,
  1323. ExistingScb );
  1324. //
  1325. // If ConnectScb fails, remove the extra ref count so
  1326. // the scavenger will clean it up. Anonymous failures
  1327. // are also cleaned up by the scavenger.
  1328. //
  1329. if ( !NT_SUCCESS( Status ) ) {
  1330. if ( pScb ) {
  1331. NwDereferenceScb( pScb->pNpScb );
  1332. }
  1333. //
  1334. // We must not change the irp context pointers if we're going
  1335. // to fail this call or we may mess up ref counts and what not.
  1336. //
  1337. pIrpContext->pNpScb = pOriginalNpScb;
  1338. pIrpContext->pScb = pOriginalScb;
  1339. *Scb = NULL;
  1340. DebugTrace( -1, Dbg, "CreateScb: Status = %08lx\n", Status );
  1341. return Status;
  1342. }
  1343. //
  1344. // If ConnectScb succeeds, then we must have an scb, the scb must
  1345. // be in the IN_USE state (or LOGIN_REQUIRED if DeferLogon was
  1346. // specified), it must be referenced, and we should not be on the
  1347. // queue.
  1348. //
  1349. ASSERT( pScb );
  1350. ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
  1351. ASSERT( pIrpContext->pNpScb == pScb->pNpScb );
  1352. ASSERT( pIrpContext->pScb == pScb );
  1353. ASSERT( pScb->pNpScb->Reference > 0 );
  1354. *Scb = pScb;
  1355. DebugTrace(-1, Dbg, "CreateScb -> pScb = %08lx\n", pScb );
  1356. ASSERT( NT_SUCCESS( Status ) );
  1357. return Status;
  1358. }
  1359. #define CTX_Retries 10
  1360. PNONPAGED_SCB
  1361. FindServer(
  1362. PIRP_CONTEXT pIrpContext,
  1363. PNONPAGED_SCB pNpScb,
  1364. PUNICODE_STRING ServerName
  1365. )
  1366. /*++
  1367. Routine Description:
  1368. This routine attempts to get the network address of a server. If no
  1369. servers are known, it first sends a find nearest SAP.
  1370. Arguments:
  1371. pIrpContext - A pointer to the request parameters.
  1372. pNpScb - A pointer to the non paged SCB for the server to get the
  1373. address of.
  1374. Return Value:
  1375. NONPAGED_SCB - A pointer the nonpaged SCB. This is the same as the
  1376. input value, unless the input SCB was NULL. Then this is a
  1377. pointer to the nearest server SCB.
  1378. This routine raises status if it fails to get the server's address.
  1379. --*/
  1380. {
  1381. NTSTATUS Status;
  1382. ULONG Attempts;
  1383. BOOLEAN FoundServer = FALSE;
  1384. PNONPAGED_SCB pNearestNpScb = NULL;
  1385. PNONPAGED_SCB pLastNpScb = NULL;
  1386. BOOLEAN SentFindNearest = FALSE;
  1387. BOOLEAN SentGeneral = FALSE;
  1388. PMDL ReceiveMdl = NULL;
  1389. PUCHAR ReceiveBuffer = NULL;
  1390. IPXaddress ServerAddress;
  1391. BOOLEAN ConnectedToNearest = FALSE;
  1392. BOOLEAN AllocatedIrpContext = FALSE;
  1393. BOOLEAN LastScbWasValid;
  1394. PIRP_CONTEXT pNewIrpContext;
  1395. int ResponseCount;
  1396. int NewServers;
  1397. ULONG RetryCount = MAX_SAP_RETRIES;
  1398. static LARGE_INTEGER TimeoutWait = {0,0};
  1399. LARGE_INTEGER Now;
  1400. PAGED_CODE();
  1401. //
  1402. // If we had a SAP timeout less than 10 seconds ago, just fail this
  1403. // request immediately. This allows dumb apps to exit a lot faster.
  1404. //
  1405. KeQuerySystemTime( &Now );
  1406. if ( Now.QuadPart < TimeoutWait.QuadPart ) {
  1407. ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
  1408. }
  1409. try {
  1410. if (IsTerminalServer()) {
  1411. //
  1412. // 1/31/97 cjc (Citrix code merge)
  1413. // Fix for Mellon Bank who restricted access based on the
  1414. // preferred server the user logs into. This caused a
  1415. // problem for a user who tries to logon to a server that
  1416. // the previous user doesn't have accesss to. The previous
  1417. // user's server is used to get the address of the current
  1418. // logon user's preferred server but he can't see the new
  1419. // user's server. Modifed this so it loops thru 10 servers
  1420. // rather than just the 1st 2 in the list.
  1421. //
  1422. RetryCount =CTX_Retries;
  1423. }
  1424. for ( Attempts = 0; Attempts < RetryCount && !FoundServer ; Attempts++ ) {
  1425. //
  1426. // If this SCB is now marked RECONNECT_REQUIRED, then
  1427. // it responded to the find nearest and we can immediately
  1428. // try to connect to it.
  1429. //
  1430. if ( pNpScb != NULL &&
  1431. pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
  1432. return pNpScb;
  1433. }
  1434. //
  1435. // Pick a server to use to find the address of the server that
  1436. // we are really interested in.
  1437. //
  1438. if (pLastNpScb) {
  1439. //
  1440. // For some reason we couldn't use pNearestScb. Scan from this
  1441. // server onwards.
  1442. //
  1443. pNearestNpScb = SelectConnection( pLastNpScb );
  1444. // Allow pLastNpScb to be deleted.
  1445. NwDereferenceScb( pLastNpScb );
  1446. pLastNpScb = NULL;
  1447. LastScbWasValid = TRUE;
  1448. } else {
  1449. pNearestNpScb = SelectConnection( NULL );
  1450. LastScbWasValid = FALSE;
  1451. }
  1452. if ( pNearestNpScb == NULL ) {
  1453. int i;
  1454. if (LastScbWasValid) {
  1455. ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
  1456. return NULL;
  1457. }
  1458. //
  1459. // If we sent a find nearest, and still don't have a single
  1460. // entry in the server list, it's time to give up.
  1461. //
  1462. if (( SentFindNearest) &&
  1463. ( SentGeneral )) {
  1464. Error(
  1465. EVENT_NWRDR_NO_SERVER_ON_NETWORK,
  1466. STATUS_OBJECT_NAME_NOT_FOUND,
  1467. NULL,
  1468. 0,
  1469. 0 );
  1470. ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
  1471. return NULL;
  1472. }
  1473. //
  1474. // We don't have any active servers in the list. Queue our
  1475. // IrpContext to the NwPermanentNpScb. This insures that
  1476. // only one thread in the system in doing a find nearest at
  1477. // any one time.
  1478. //
  1479. DebugTrace( +0, Dbg, " Nearest Server\n", 0);
  1480. if ( !AllocatedIrpContext ) {
  1481. AllocatedIrpContext = NwAllocateExtraIrpContext(
  1482. &pNewIrpContext,
  1483. &NwPermanentNpScb );
  1484. if ( !AllocatedIrpContext ) {
  1485. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  1486. }
  1487. }
  1488. pNewIrpContext->pNpScb = &NwPermanentNpScb;
  1489. //
  1490. // Allocate an extra buffer large enough for 4
  1491. // find nearest responses, or a general SAP response.
  1492. //
  1493. pNewIrpContext->Specific.Create.FindNearestResponseCount = 0;
  1494. NewServers = 0;
  1495. ReceiveBuffer = ALLOCATE_POOL_EX(
  1496. NonPagedPool,
  1497. MAX_SAP_RESPONSE_SIZE );
  1498. pNewIrpContext->Specific.Create.FindNearestResponse[0] = ReceiveBuffer;
  1499. for ( i = 1; i < MAX_SAP_RESPONSES ; i++ ) {
  1500. pNewIrpContext->Specific.Create.FindNearestResponse[i] =
  1501. ReceiveBuffer + i * SAP_RECORD_SIZE;
  1502. }
  1503. //
  1504. // Get the tick count for this net, so that we know how
  1505. // long to wait for SAP responses.
  1506. //
  1507. (VOID)GetTickCount( pNewIrpContext, &NwPermanentNpScb.TickCount );
  1508. NwPermanentNpScb.SendTimeout = NwPermanentNpScb.TickCount + 10;
  1509. if (!SentFindNearest) {
  1510. //
  1511. // Send a find nearest SAP, and wait for up to several
  1512. // responses. This allows us to handle a busy server
  1513. // that responds quickly to SAPs but will not accept
  1514. // connections.
  1515. //
  1516. Status = ExchangeWithWait (
  1517. pNewIrpContext,
  1518. ProcessFindNearest,
  1519. "Aww",
  1520. SAP_FIND_NEAREST,
  1521. SAP_SERVICE_TYPE_SERVER );
  1522. if ( Status == STATUS_NETWORK_UNREACHABLE ) {
  1523. //
  1524. // IPX is not bound to anything that is currently
  1525. // up (which means it's probably bound only to the
  1526. // RAS WAN wrapper). Don't waste 20 seconds trying
  1527. // to find a server.
  1528. //
  1529. DebugTrace( 0, Dbg, "Aborting FindNearest. No Net.\n", 0 );
  1530. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1531. ExRaiseStatus( STATUS_NETWORK_UNREACHABLE );
  1532. }
  1533. //
  1534. // Process the set of find nearest responses.
  1535. //
  1536. for (i = 0; i < (int)pNewIrpContext->Specific.Create.FindNearestResponseCount; i++ ) {
  1537. if (ProcessFindNearestEntry(
  1538. pNewIrpContext,
  1539. (PSAP_FIND_NEAREST_RESPONSE)pNewIrpContext->Specific.Create.FindNearestResponse[i] )
  1540. ) {
  1541. //
  1542. // We found a server that was previously unknown.
  1543. //
  1544. NewServers++;
  1545. }
  1546. }
  1547. }
  1548. if (( !NewServers ) &&
  1549. ( !SentGeneral)){
  1550. SentGeneral = TRUE;
  1551. //
  1552. // Either no SAP responses or can't connect to nearest servers.
  1553. // Try a general SAP.
  1554. //
  1555. ReceiveMdl = ALLOCATE_MDL(
  1556. ReceiveBuffer,
  1557. MAX_SAP_RESPONSE_SIZE,
  1558. TRUE,
  1559. FALSE,
  1560. NULL );
  1561. if ( ReceiveMdl == NULL ) {
  1562. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  1563. }
  1564. MmBuildMdlForNonPagedPool( ReceiveMdl );
  1565. pNewIrpContext->RxMdl->Next = ReceiveMdl;
  1566. Status = ExchangeWithWait (
  1567. pNewIrpContext,
  1568. SynchronousResponseCallback,
  1569. "Aww",
  1570. SAP_GENERAL_REQUEST,
  1571. SAP_SERVICE_TYPE_SERVER );
  1572. if ( NT_SUCCESS( Status ) ) {
  1573. DebugTrace( 0, Dbg, "Received %d bytes\n", pNewIrpContext->ResponseLength );
  1574. ResponseCount = ( pNewIrpContext->ResponseLength - 2 ) / SAP_RECORD_SIZE;
  1575. //
  1576. // Process at most MAX_SAP_RESPONSES servers.
  1577. //
  1578. if ( ResponseCount > MAX_SAP_RESPONSES ) {
  1579. ResponseCount = MAX_SAP_RESPONSES;
  1580. }
  1581. for ( i = 0; i < ResponseCount; i++ ) {
  1582. ProcessFindNearestEntry(
  1583. pNewIrpContext,
  1584. (PSAP_FIND_NEAREST_RESPONSE)(pNewIrpContext->rsp + SAP_RECORD_SIZE * i) );
  1585. }
  1586. }
  1587. pNewIrpContext->RxMdl->Next = NULL;
  1588. FREE_MDL( ReceiveMdl );
  1589. ReceiveMdl = NULL;
  1590. }
  1591. //
  1592. // We're done with the find nearest. Free the buffer and
  1593. // dequeue from the permanent SCB.
  1594. //
  1595. FREE_POOL( ReceiveBuffer );
  1596. ReceiveBuffer = NULL;
  1597. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1598. if ( !NT_SUCCESS( Status ) &&
  1599. pNewIrpContext->Specific.Create.FindNearestResponseCount == 0 ) {
  1600. //
  1601. // If the SAP timed out, map the error for MPR.
  1602. //
  1603. if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
  1604. Status = STATUS_BAD_NETWORK_PATH;
  1605. }
  1606. //
  1607. // Setup the WaitTimeout, and fail this request.
  1608. //
  1609. KeQuerySystemTime( &TimeoutWait );
  1610. TimeoutWait.QuadPart += NwOneSecond * 10;
  1611. ExRaiseStatus( Status );
  1612. return NULL;
  1613. }
  1614. SentFindNearest = TRUE;
  1615. } else {
  1616. if ( !AllocatedIrpContext ) {
  1617. AllocatedIrpContext = NwAllocateExtraIrpContext(
  1618. &pNewIrpContext,
  1619. pNearestNpScb );
  1620. if ( !AllocatedIrpContext ) {
  1621. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  1622. }
  1623. }
  1624. //
  1625. // Point the IRP context at the nearest server.
  1626. //
  1627. pNewIrpContext->pNpScb = pNearestNpScb;
  1628. NwAppendToQueueAndWait( pNewIrpContext );
  1629. if ( pNearestNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
  1630. //
  1631. // We have no connection to this server, try to
  1632. // connect now. This is not a valid path for an
  1633. // anonymous create, so there's no chance that
  1634. // there will be a name collision.
  1635. //
  1636. Status = ConnectToServer( pNewIrpContext, NULL );
  1637. if ( !NT_SUCCESS( Status ) ) {
  1638. //
  1639. // Failed to connect to the server. Give up.
  1640. // We'll try another server.
  1641. //
  1642. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1643. // Keep pNearestScb referenced
  1644. // so it doesn't disappear.
  1645. pLastNpScb = pNearestNpScb;
  1646. continue;
  1647. } else {
  1648. pNearestNpScb->State = SCB_STATE_LOGIN_REQUIRED;
  1649. ConnectedToNearest = TRUE;
  1650. }
  1651. }
  1652. //
  1653. // update the last used time for this SCB.
  1654. //
  1655. KeQuerySystemTime( &pNearestNpScb->LastUsedTime );
  1656. if (( pNpScb == NULL ) ||
  1657. ( ServerName == NULL )) {
  1658. //
  1659. // We're looking for any server so use this one.
  1660. //
  1661. // We'll exit the for loop on the SCB queue,
  1662. // and with this SCB referenced.
  1663. //
  1664. pNpScb = pNearestNpScb;
  1665. Status = STATUS_SUCCESS;
  1666. FoundServer = TRUE;
  1667. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1668. } else {
  1669. Status = QueryServersAddress(
  1670. pNewIrpContext,
  1671. pNearestNpScb,
  1672. ServerName,
  1673. &ServerAddress );
  1674. //
  1675. // If we connect to this server just to query it's
  1676. // bindery, disconnect now.
  1677. //
  1678. if (IsTerminalServer()) {
  1679. if (ConnectedToNearest) {
  1680. ExchangeWithWait (
  1681. pNewIrpContext,
  1682. SynchronousResponseCallback,
  1683. "D-" ); // Disconnect
  1684. ConnectedToNearest = FALSE;
  1685. Stats.Sessions--;
  1686. if ( pNearestNpScb->MajorVersion == 2 ) {
  1687. Stats.NW2xConnects--;
  1688. } else if ( pNearestNpScb->MajorVersion == 3 ) {
  1689. Stats.NW3xConnects--;
  1690. } else if ( pNearestNpScb->MajorVersion == 4 ) {
  1691. Stats.NW4xConnects--;
  1692. }
  1693. pNearestNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  1694. }
  1695. } else {
  1696. if ( ConnectedToNearest && NT_SUCCESS(Status) ) {
  1697. ExchangeWithWait (
  1698. pNewIrpContext,
  1699. SynchronousResponseCallback,
  1700. "D-" ); // Disconnect
  1701. pNearestNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  1702. }
  1703. }
  1704. if ( NT_SUCCESS( Status ) ) {
  1705. //
  1706. // Success!
  1707. //
  1708. // Point the SCB at the real server address and connect to it,
  1709. // then logout. (We logout for no apparent reason except
  1710. // because this is what a netware redir does.)
  1711. //
  1712. RtlCopyMemory(
  1713. &pNpScb->ServerAddress,
  1714. &ServerAddress,
  1715. sizeof( TDI_ADDRESS_IPX ) );
  1716. if ( ServerAddress.Socket != NCP_SOCKET ) {
  1717. DebugTrace( 0, DEBUG_TRACE_ALWAYS, "FindServer server socket is deviant.\n", 0 );
  1718. }
  1719. BuildIpxAddress(
  1720. ServerAddress.Net,
  1721. ServerAddress.Node,
  1722. ServerAddress.Socket,
  1723. &pNpScb->RemoteAddress );
  1724. FoundServer = TRUE;
  1725. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1726. NwDereferenceScb( pNearestNpScb );
  1727. pNewIrpContext->pNpScb = pNpScb;
  1728. pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  1729. } else {
  1730. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1731. if ( (Status == STATUS_REMOTE_NOT_LISTENING ) ||
  1732. (Status == STATUS_BAD_NETWORK_PATH)) {
  1733. //
  1734. // This server is no longer talking to us.
  1735. // Try again. Keep pNearestScb referenced
  1736. // so it doesn't disappear.
  1737. //
  1738. pLastNpScb = pNearestNpScb;
  1739. continue;
  1740. } else {
  1741. NwDereferenceScb( pNearestNpScb );
  1742. //
  1743. // This nearest server doesn't know about
  1744. // the server we are looking for. Give up
  1745. // and let another rdr try.
  1746. //
  1747. ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
  1748. return NULL;
  1749. }
  1750. }
  1751. }
  1752. } // else
  1753. } // for
  1754. } finally {
  1755. if ( ReceiveBuffer != NULL ) {
  1756. FREE_POOL( ReceiveBuffer );
  1757. }
  1758. if ( ReceiveMdl != NULL ) {
  1759. FREE_MDL( ReceiveMdl );
  1760. }
  1761. if ( AllocatedIrpContext ) {
  1762. NwDequeueIrpContext( pNewIrpContext, FALSE );
  1763. NwFreeExtraIrpContext( pNewIrpContext );
  1764. }
  1765. if (IsTerminalServer()) {
  1766. if ( (Attempts == CTX_Retries) && pLastNpScb) {
  1767. NwDereferenceScb( pLastNpScb );
  1768. }
  1769. } else {
  1770. if (pLastNpScb) {
  1771. NwDereferenceScb( pLastNpScb );
  1772. }
  1773. }
  1774. }
  1775. if ( !FoundServer ) {
  1776. ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
  1777. }
  1778. return pNpScb;
  1779. }
  1780. NTSTATUS
  1781. ProcessFindNearest(
  1782. IN struct _IRP_CONTEXT* pIrpContext,
  1783. IN ULONG BytesAvailable,
  1784. IN PUCHAR Response
  1785. )
  1786. /*++
  1787. Routine Description:
  1788. This routine takes the full address of the remote server and builds
  1789. the corresponding TA_IPX_ADDRESS.
  1790. Arguments:
  1791. Return Value:
  1792. --*/
  1793. {
  1794. ULONG ResponseCount;
  1795. KIRQL OldIrql;
  1796. DebugTrace(+1, Dbg, "ProcessFindNearest...\n", 0);
  1797. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  1798. if ( BytesAvailable == 0) {
  1799. //
  1800. // Timeout.
  1801. //
  1802. pIrpContext->ResponseParameters.Error = 0;
  1803. pIrpContext->pNpScb->OkToReceive = FALSE;
  1804. ASSERT( pIrpContext->Event.Header.SignalState == 0 );
  1805. #if NWDBG
  1806. pIrpContext->DebugValue = 0x101;
  1807. #endif
  1808. NwSetIrpContextEvent( pIrpContext );
  1809. DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
  1810. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  1811. return STATUS_REMOTE_NOT_LISTENING;
  1812. }
  1813. if ( BytesAvailable >= FIND_NEAREST_RESP_SIZE &&
  1814. Response[0] == 0 &&
  1815. Response[1] == SAP_SERVICE_TYPE_SERVER ) {
  1816. //
  1817. // This is a valid find nearest response. Process the packet.
  1818. //
  1819. ResponseCount = pIrpContext->Specific.Create.FindNearestResponseCount++;
  1820. ASSERT( ResponseCount < MAX_SAP_RESPONSES );
  1821. //
  1822. // Copy the Find Nearest server response to the receive buffer.
  1823. //
  1824. RtlCopyMemory(
  1825. pIrpContext->Specific.Create.FindNearestResponse[ResponseCount],
  1826. Response,
  1827. FIND_NEAREST_RESP_SIZE );
  1828. //
  1829. // If we have reached critical mass on the number of find
  1830. // nearest responses, set the event to indicate that we
  1831. // are done.
  1832. //
  1833. if ( ResponseCount == MAX_SAP_RESPONSES - 1 ) {
  1834. ASSERT( pIrpContext->Event.Header.SignalState == 0 );
  1835. #ifdef NWDBG
  1836. pIrpContext->DebugValue = 0x102;
  1837. #endif
  1838. pIrpContext->ResponseParameters.Error = 0;
  1839. NwSetIrpContextEvent( pIrpContext );
  1840. } else {
  1841. pIrpContext->pNpScb->OkToReceive = TRUE;
  1842. }
  1843. } else {
  1844. //
  1845. // Discard the invalid find nearest response.
  1846. //
  1847. pIrpContext->pNpScb->OkToReceive = TRUE;
  1848. }
  1849. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  1850. DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_SUCCESS );
  1851. return( STATUS_SUCCESS );
  1852. }
  1853. BOOLEAN
  1854. ProcessFindNearestEntry(
  1855. PIRP_CONTEXT IrpContext,
  1856. PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
  1857. )
  1858. {
  1859. OEM_STRING OemServerName;
  1860. UNICODE_STRING UidServerName;
  1861. UNICODE_STRING ServerName;
  1862. NTSTATUS Status;
  1863. PSCB pScb;
  1864. PNONPAGED_SCB pNpScb = NULL;
  1865. BOOLEAN ExistingScb = TRUE;
  1866. PAGED_CODE();
  1867. DebugTrace(+1, Dbg, "ProcessFindNearestEntry\n", 0);
  1868. ServerName.Buffer = NULL;
  1869. UidServerName.Buffer = NULL;
  1870. try {
  1871. RtlInitString( &OemServerName, FindNearestResponse->ServerName );
  1872. ASSERT( OemServerName.Length < MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) );
  1873. Status = RtlOemStringToCountedUnicodeString(
  1874. &ServerName,
  1875. &OemServerName,
  1876. TRUE );
  1877. if ( !NT_SUCCESS( Status ) ) {
  1878. try_return( NOTHING );
  1879. }
  1880. //
  1881. // Lookup of the SCB by name. If it is not found, an SCB
  1882. // will be created.
  1883. //
  1884. Status = MakeUidServer(
  1885. &UidServerName,
  1886. &IrpContext->Specific.Create.UserUid,
  1887. &ServerName );
  1888. if (!NT_SUCCESS(Status)) {
  1889. try_return( NOTHING );
  1890. }
  1891. ExistingScb = NwFindScb( &pScb, IrpContext, &UidServerName, &ServerName );
  1892. ASSERT( pScb != NULL );
  1893. pNpScb = pScb->pNpScb;
  1894. //
  1895. // Copy the network address to the SCB, and calculate the
  1896. // IPX address.
  1897. //
  1898. RtlCopyMemory(
  1899. &pNpScb->ServerAddress,
  1900. &FindNearestResponse->Network,
  1901. sizeof( TDI_ADDRESS_IPX ) );
  1902. if ( pNpScb->ServerAddress.Socket != NCP_SOCKET ) {
  1903. DebugTrace( 0, DEBUG_TRACE_ALWAYS, "FindNearest server socket is deviant.\n", 0 );
  1904. }
  1905. BuildIpxAddress(
  1906. pNpScb->ServerAddress.Net,
  1907. pNpScb->ServerAddress.Node,
  1908. pNpScb->ServerAddress.Socket,
  1909. &pNpScb->RemoteAddress );
  1910. if ( pNpScb->State == SCB_STATE_ATTACHING ) {
  1911. //
  1912. // We are in the process of trying to connect to this
  1913. // server so mark it reconnect required so that
  1914. // CreateScb will know that we've found it address.
  1915. //
  1916. pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  1917. }
  1918. try_exit: NOTHING;
  1919. } finally {
  1920. if ( pNpScb != NULL ) {
  1921. NwDereferenceScb( pNpScb );
  1922. }
  1923. if (UidServerName.Buffer != NULL) {
  1924. FREE_POOL(UidServerName.Buffer);
  1925. }
  1926. RtlFreeUnicodeString( &ServerName );
  1927. }
  1928. //
  1929. // Tell the caller if we created a new Scb
  1930. //
  1931. if (ExistingScb) {
  1932. DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", FALSE );
  1933. return FALSE;
  1934. } else {
  1935. DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", TRUE );
  1936. return TRUE;
  1937. }
  1938. }
  1939. NTSTATUS
  1940. ConnectToServer(
  1941. IN struct _IRP_CONTEXT* pIrpContext,
  1942. OUT PSCB *pScbCollision
  1943. )
  1944. /*++
  1945. Routine Description:
  1946. This routine transfers connect and negotiate buffer NCPs to the server.
  1947. This routine may be called upon to connect an anonymous scb. Upon
  1948. learning the name of the anonymous scb, it will determine if another
  1949. create has completed while the name lookup was in progress. If it has,
  1950. then the routine will refer the called to that new scb. Otherwise, the
  1951. scb will be entered onto the scb list and used normally. The RCB
  1952. protects the scb list by name only. For more info, see the comment
  1953. in CreateScb().
  1954. Arguments:
  1955. pIrpContext - supplies context and server information
  1956. Return Value:
  1957. Status of operation
  1958. --*/
  1959. {
  1960. NTSTATUS Status, BurstStatus;
  1961. PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
  1962. PSCB pScb = pNpScb->pScb;
  1963. BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
  1964. ULONG MaxSafeSize ;
  1965. BOOLEAN LIPNegotiated ;
  1966. PLOGON Logon;
  1967. OEM_STRING OemServerName;
  1968. UNICODE_STRING ServerName;
  1969. UNICODE_STRING CredentialName;
  1970. PUNICODE_STRING puConnectName;
  1971. BYTE OemName[MAX_SERVER_NAME_LENGTH];
  1972. WCHAR Server[MAX_SERVER_NAME_LENGTH];
  1973. KIRQL OldIrql;
  1974. UNICODE_STRING UidServerName;
  1975. BOOLEAN Success;
  1976. PLIST_ENTRY ScbQueueEntry;
  1977. PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
  1978. PAGED_CODE();
  1979. DebugTrace( +0, Dbg, " Connect\n", 0);
  1980. RtlInitUnicodeString( &CredentialName, NULL );
  1981. //
  1982. // Get the tick count for our connection to this server
  1983. //
  1984. Status = GetTickCount( pIrpContext, &pNpScb->TickCount );
  1985. if ( !NT_SUCCESS( Status ) ) {
  1986. pNpScb->TickCount = DEFAULT_TICK_COUNT;
  1987. }
  1988. pNpScb->SendTimeout = pNpScb->TickCount + 10;
  1989. //
  1990. // Initialize timers for a server that supports burst but not LIP
  1991. //
  1992. pNpScb->NwLoopTime = pNpScb->NwSingleBurstPacketTime = pNpScb->SendTimeout;
  1993. pNpScb->NwReceiveDelay = pNpScb->NwSendDelay = 0;
  1994. pNpScb->NtSendDelay.QuadPart = 0;
  1995. //
  1996. // Request connection
  1997. //
  1998. Status = ExchangeWithWait (
  1999. pIrpContext,
  2000. SynchronousResponseCallback,
  2001. "C-");
  2002. DebugTrace( +0, Dbg, " %X\n", Status);
  2003. if (!NT_SUCCESS(Status)) {
  2004. if ( Status == STATUS_UNSUCCESSFUL ) {
  2005. #ifdef QFE_BUILD
  2006. Status = STATUS_TOO_MANY_SESSIONS;
  2007. #else
  2008. Status = STATUS_REMOTE_SESSION_LIMIT;
  2009. #endif
  2010. pNpScb->State = SCB_STATE_ATTACHING;
  2011. } else if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
  2012. //
  2013. // The connect timed out, suspect that the server is down
  2014. // and put it back in the attaching state.
  2015. //
  2016. pNpScb->State = SCB_STATE_ATTACHING;
  2017. }
  2018. goto ExitWithStatus;
  2019. }
  2020. pNpScb->SequenceNo++;
  2021. Stats.Sessions++;
  2022. //
  2023. // Get server information
  2024. //
  2025. DebugTrace( +0, Dbg, "Get file server information\n", 0);
  2026. Status = ExchangeWithWait ( pIrpContext,
  2027. SynchronousResponseCallback,
  2028. "S",
  2029. NCP_ADMIN_FUNCTION, NCP_GET_SERVER_INFO );
  2030. if ( NT_SUCCESS( Status ) ) {
  2031. Status = ParseResponse( pIrpContext,
  2032. pIrpContext->rsp,
  2033. pIrpContext->ResponseLength,
  2034. "Nrbb",
  2035. OemName,
  2036. MAX_SERVER_NAME_LENGTH,
  2037. &pScb->MajorVersion,
  2038. &pScb->MinorVersion );
  2039. }
  2040. pNpScb->MajorVersion = pScb->MajorVersion;
  2041. if (!NT_SUCCESS(Status)) {
  2042. goto ExitWithStatus;
  2043. }
  2044. //
  2045. // If this was an anonymous SCB, we need to check the name
  2046. // for a create collision before we do anything else.
  2047. //
  2048. if ( AnonymousScb ) {
  2049. //
  2050. // Grab the RCB to protect the server prefix table. We've
  2051. // spent the time sending the packet to look up the server
  2052. // name so we are a little greedy with the RCB to help
  2053. // minimize the chance of a collision.
  2054. //
  2055. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  2056. //
  2057. // Make the uid server name.
  2058. //
  2059. OemServerName.Buffer = OemName;
  2060. OemServerName.Length = 0;
  2061. OemServerName.MaximumLength = sizeof( OemName );
  2062. while ( ( OemServerName.Length < MAX_SERVER_NAME_LENGTH ) &&
  2063. ( OemName[OemServerName.Length] != '\0' ) ) {
  2064. OemServerName.Length++;
  2065. }
  2066. ServerName.Buffer = Server;
  2067. ServerName.MaximumLength = sizeof( Server );
  2068. ServerName.Length = 0;
  2069. RtlOemStringToUnicodeString( &ServerName,
  2070. &OemServerName,
  2071. FALSE );
  2072. //
  2073. // If this is an extended credential create, munge the server name.
  2074. //
  2075. if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
  2076. Status = BuildExCredentialServerName( &ServerName,
  2077. pIrpContext->Specific.Create.puCredentialName,
  2078. &CredentialName );
  2079. if ( !NT_SUCCESS( Status ) ) {
  2080. NwReleaseRcb( &NwRcb );
  2081. goto ExitWithStatus;
  2082. }
  2083. puConnectName = &CredentialName;
  2084. } else {
  2085. puConnectName = &ServerName;
  2086. }
  2087. //
  2088. // Tack on the uid.
  2089. //
  2090. Status = MakeUidServer( &UidServerName,
  2091. &pScb->UserUid,
  2092. puConnectName );
  2093. if ( !NT_SUCCESS( Status ) ) {
  2094. NwReleaseRcb( &NwRcb );
  2095. goto ExitWithStatus;
  2096. }
  2097. //
  2098. // Actually do the look up in the prefix table.
  2099. //
  2100. PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServerName, 0 );
  2101. if ( PrefixEntry != NULL ) {
  2102. //
  2103. // There was a collision with this anonymous create. Dump
  2104. // the anonymous scb and pick up the new one.
  2105. //
  2106. NwReleaseRcb( &NwRcb );
  2107. DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Anonymous create collided for %wZ.\n", &UidServerName );
  2108. //
  2109. // Disconnect this connection so we don't clutter the server.
  2110. //
  2111. ExchangeWithWait ( pIrpContext,
  2112. SynchronousResponseCallback,
  2113. "D-" );
  2114. FREE_POOL( UidServerName.Buffer );
  2115. //
  2116. // Since there was a collision, we know for a fact that there's another
  2117. // good SCB for this server somewhere. We set the state on this anonymous
  2118. // SCB to SCB_STATE_FLAG_SHUTDOWN so that no one ever plays with the
  2119. // anonymous SCB again. The scavenger will clean it up soon.
  2120. //
  2121. pNpScb->State = SCB_STATE_FLAG_SHUTDOWN;
  2122. if ( pScbCollision ) {
  2123. *pScbCollision = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
  2124. NwReferenceScb( (*pScbCollision)->pNpScb );
  2125. Status = STATUS_SUCCESS;
  2126. goto ExitWithStatus;
  2127. } else {
  2128. DebugTrace( 0, Dbg, "Invalid path for an anonymous create.\n", 0 );
  2129. Status = STATUS_INVALID_PARAMETER;
  2130. goto ExitWithStatus;
  2131. }
  2132. }
  2133. //
  2134. // This anonymous create didn't collide - cool! Fill in the server
  2135. // name, check the preferred, server setting, and put the SCB on the
  2136. // SCB queue in the correct location. This code is similar to pieces
  2137. // of code in NwAllocateAndInitScb() and NwFindScb().
  2138. //
  2139. DebugTrace( 0, Dbg, "Completing anonymous create for %wZ!\n", &UidServerName );
  2140. RtlCopyUnicodeString ( &pScb->UidServerName, &UidServerName );
  2141. pScb->UidServerName.Buffer[ UidServerName.Length / sizeof( WCHAR ) ] = L'\0';
  2142. pScb->UnicodeUid = pScb->UidServerName;
  2143. pScb->UnicodeUid.Length = UidServerName.Length -
  2144. puConnectName->Length -
  2145. sizeof(WCHAR);
  2146. //
  2147. // Make ServerName point partway down the buffer for UidServerName
  2148. //
  2149. pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
  2150. UidServerName.Length - puConnectName->Length);
  2151. pNpScb->ServerName.MaximumLength = puConnectName->Length;
  2152. pNpScb->ServerName.Length = puConnectName->Length;
  2153. //
  2154. // Determine if this is our preferred server.
  2155. //
  2156. Logon = FindUser( &pScb->UserUid, FALSE );
  2157. if (( Logon != NULL) &&
  2158. (RtlCompareUnicodeString( puConnectName, &Logon->ServerName, TRUE ) == 0 )) {
  2159. pScb->PreferredServer = TRUE;
  2160. NwReferenceScb( pNpScb );
  2161. }
  2162. FREE_POOL( UidServerName.Buffer );
  2163. //
  2164. // Insert the name of this server into the prefix table.
  2165. //
  2166. Success = RtlInsertUnicodePrefix( &NwRcb.ServerNameTable,
  2167. &pScb->UidServerName,
  2168. &pScb->PrefixEntry );
  2169. #ifdef NWDBG
  2170. if ( !Success ) {
  2171. DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
  2172. DbgBreakPoint();
  2173. }
  2174. #endif
  2175. //
  2176. // This create is complete, release the RCB.
  2177. //
  2178. NwReleaseRcb( &NwRcb );
  2179. //
  2180. // If this is our preferred server, we have to move this guy
  2181. // to the head of the scb list. We do this after the create
  2182. // because we can't acquire the ScbSpinLock while holding the
  2183. // RCB.
  2184. //
  2185. if ( pScb->PreferredServer ) {
  2186. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  2187. RemoveEntryList( &pNpScb->ScbLinks );
  2188. InsertHeadList( &ScbQueue, &pNpScb->ScbLinks );
  2189. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  2190. }
  2191. }
  2192. if ( pScb->MajorVersion == 2 ) {
  2193. Stats.NW2xConnects++;
  2194. pNpScb->PageAlign = TRUE;
  2195. } else if ( pScb->MajorVersion == 3 ) {
  2196. Stats.NW3xConnects++;
  2197. if (pScb->MinorVersion > 0xb) {
  2198. pNpScb->PageAlign = FALSE;
  2199. } else {
  2200. pNpScb->PageAlign = TRUE;
  2201. }
  2202. } else if ( pScb->MajorVersion >= 4 ) {
  2203. Stats.NW4xConnects++;
  2204. pNpScb->PageAlign = FALSE;
  2205. NdsPing( pIrpContext, pScb );
  2206. }
  2207. //
  2208. // Get the local net max packet size. This is the max frame size
  2209. // does not include space for IPX or lower level headers.
  2210. //
  2211. Status = GetMaximumPacketSize( pIrpContext, &pNpScb->Server, &pNpScb->MaxPacketSize );
  2212. //
  2213. // If the transport won't tell us, pick the largest size that
  2214. // is guaranteed to work.
  2215. //
  2216. if ( !NT_SUCCESS( Status ) ) {
  2217. pNpScb->BufferSize = DEFAULT_PACKET_SIZE;
  2218. pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
  2219. } else {
  2220. pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
  2221. }
  2222. MaxSafeSize = pNpScb->MaxPacketSize ;
  2223. //
  2224. // Negotiate a burst mode connection. Keep track of that status.
  2225. //
  2226. Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
  2227. BurstStatus = Status ;
  2228. if (!NT_SUCCESS(Status) || !LIPNegotiated) {
  2229. //
  2230. // Negotiate buffer size with server if we didnt do burst
  2231. // sucessfully or if burst succeeded but we didnt do LIP.
  2232. //
  2233. DebugTrace( +0, Dbg, "Negotiate Buffer Size\n", 0);
  2234. Status = ExchangeWithWait ( pIrpContext,
  2235. SynchronousResponseCallback,
  2236. "Fw",
  2237. NCP_NEGOTIATE_BUFFER_SIZE,
  2238. pNpScb->BufferSize );
  2239. DebugTrace( +0, Dbg, " %X\n", Status);
  2240. DebugTrace( +0, Dbg, " Parse response\n", 0);
  2241. if ( NT_SUCCESS( Status ) ) {
  2242. Status = ParseResponse( pIrpContext,
  2243. pIrpContext->rsp,
  2244. pIrpContext->ResponseLength,
  2245. "Nw",
  2246. &pNpScb->BufferSize );
  2247. //
  2248. // Dont allow the server to fool us into using a
  2249. // packet size bigger than what the media can support.
  2250. // We have at least one case of server returning 4K while
  2251. // on ethernet.
  2252. //
  2253. // Use PacketThreshold so that the PacketAdjustment can be
  2254. // avoided on small packet sizes such as those on ethernet.
  2255. //
  2256. if (MaxSafeSize > (ULONG)PacketThreshold) {
  2257. MaxSafeSize -= (ULONG)LargePacketAdjustment;
  2258. }
  2259. //
  2260. // If larger than number we got from transport, taking in account
  2261. // IPX header (30) & NCP header (BURST_RESPONSE is a good worst
  2262. // case), we adjust accordingly.
  2263. //
  2264. if (pNpScb->BufferSize >
  2265. (MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))))
  2266. {
  2267. pNpScb->BufferSize = (USHORT)
  2268. (MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))) ;
  2269. }
  2270. //
  2271. // An SFT III server responded with a BufferSize of 0!
  2272. //
  2273. pNpScb->BufferSize = MAX(pNpScb->BufferSize,DEFAULT_PACKET_SIZE);
  2274. //
  2275. // If an explicit registry default was set, we honour that.
  2276. // Note that this only applies in the 'default' case, ie. we
  2277. // didnt negotiate LIP successfully. Typically, we dont
  2278. // expect to use this, because the server will drop to 512 if
  2279. // it finds routers in between. But if for some reason the server
  2280. // came back with a number that was higher than what some router
  2281. // in between can take, we have this as manual override.
  2282. //
  2283. if (DefaultMaxPacketSize != 0)
  2284. {
  2285. pNpScb->BufferSize = MIN (pNpScb->BufferSize,
  2286. (USHORT)DefaultMaxPacketSize) ;
  2287. }
  2288. }
  2289. if (NT_SUCCESS(BurstStatus)) {
  2290. //
  2291. // We negotiated burst but not LIP. Save the packet size we
  2292. // have from above and renegotiate the burst so that the
  2293. // server knows how much it can send to us. And then take
  2294. // the minimum of the two to make sure we are safe.
  2295. //
  2296. USHORT SavedPacketSize = pNpScb->BufferSize ;
  2297. Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
  2298. pNpScb->BufferSize = MIN(pNpScb->BufferSize,SavedPacketSize) ;
  2299. }
  2300. }
  2301. ExitWithStatus:
  2302. if ( CredentialName.Buffer ) {
  2303. FREE_POOL( CredentialName.Buffer );
  2304. }
  2305. return Status;
  2306. }
  2307. NTSTATUS
  2308. NegotiateBurstMode(
  2309. PIRP_CONTEXT pIrpContext,
  2310. PNONPAGED_SCB pNpScb,
  2311. BOOLEAN *LIPNegotiated
  2312. )
  2313. /*++
  2314. Routine Description:
  2315. This routine negotiates a burst mode connection with the specified
  2316. server.
  2317. Arguments:
  2318. pIrpContext - Supplies context and server information.
  2319. pNpScb - A pointer to the NONPAGED_SCB for the server we are
  2320. negotiating with.
  2321. Return Value:
  2322. None.
  2323. --*/
  2324. {
  2325. NTSTATUS Status;
  2326. PAGED_CODE();
  2327. *LIPNegotiated = FALSE ;
  2328. if (pNpScb->MaxPacketSize == DEFAULT_PACKET_SIZE) {
  2329. return STATUS_NOT_SUPPORTED;
  2330. }
  2331. if ( NwBurstModeEnabled ) {
  2332. pNpScb->BurstRenegotiateReqd = TRUE;
  2333. pNpScb->SourceConnectionId = rand();
  2334. pNpScb->MaxSendSize = NwMaxSendSize;
  2335. pNpScb->MaxReceiveSize = NwMaxReceiveSize;
  2336. pNpScb->BurstSequenceNo = 0;
  2337. pNpScb->BurstRequestNo = 0;
  2338. Status = ExchangeWithWait(
  2339. pIrpContext,
  2340. SynchronousResponseCallback,
  2341. "FDdWdd",
  2342. NCP_NEGOTIATE_BURST_CONNECTION,
  2343. pNpScb->SourceConnectionId,
  2344. pNpScb->BufferSize,
  2345. pNpScb->Burst.Socket,
  2346. pNpScb->MaxSendSize,
  2347. pNpScb->MaxReceiveSize );
  2348. if ( NT_SUCCESS( Status )) {
  2349. Status = ParseResponse(
  2350. pIrpContext,
  2351. pIrpContext->rsp,
  2352. pIrpContext->ResponseLength,
  2353. "Ned",
  2354. &pNpScb->DestinationConnectionId,
  2355. &pNpScb->MaxPacketSize );
  2356. if (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE) {
  2357. pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
  2358. }
  2359. }
  2360. if ( NT_SUCCESS( Status )) {
  2361. if (NT_SUCCESS(GetMaxPacketSize( pIrpContext, pNpScb ))) {
  2362. *LIPNegotiated = TRUE ;
  2363. }
  2364. pNpScb->SendBurstModeEnabled = TRUE;
  2365. pNpScb->ReceiveBurstModeEnabled = TRUE;
  2366. //
  2367. // Use this size as the max read and write size instead of
  2368. // negotiating. This is what the VLM client does and is
  2369. // important because the negotiate will give a smaller value.
  2370. //
  2371. pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
  2372. return STATUS_SUCCESS;
  2373. }
  2374. }
  2375. return STATUS_NOT_SUPPORTED;
  2376. }
  2377. VOID
  2378. RenegotiateBurstMode(
  2379. PIRP_CONTEXT pIrpContext,
  2380. PNONPAGED_SCB pNpScb
  2381. )
  2382. /*++
  2383. Routine Description:
  2384. This routine renegotiates a burst mode connection with the specified
  2385. server. I don't know why we need this but it seems to be required
  2386. by Netware latest burst implementation.
  2387. Arguments:
  2388. pIrpContext - Supplies context and server information.
  2389. pNpScb - A pointer to the NONPAGED_SCB for the server we are
  2390. negotiating with.
  2391. Return Value:
  2392. None.
  2393. --*/
  2394. {
  2395. NTSTATUS Status;
  2396. PAGED_CODE();
  2397. DebugTrace( 0, DEBUG_TRACE_LIP, "Re-negotiating burst mode.\n", 0);
  2398. pNpScb->SourceConnectionId = rand();
  2399. pNpScb->MaxSendSize = NwMaxSendSize;
  2400. pNpScb->MaxReceiveSize = NwMaxReceiveSize;
  2401. pNpScb->BurstSequenceNo = 0;
  2402. pNpScb->BurstRequestNo = 0;
  2403. Status = ExchangeWithWait(
  2404. pIrpContext,
  2405. SynchronousResponseCallback,
  2406. "FDdWdd",
  2407. NCP_NEGOTIATE_BURST_CONNECTION,
  2408. pNpScb->SourceConnectionId,
  2409. pNpScb->MaxPacketSize,
  2410. pNpScb->Burst.Socket,
  2411. pNpScb->MaxSendSize,
  2412. pNpScb->MaxReceiveSize );
  2413. if ( NT_SUCCESS( Status )) {
  2414. Status = ParseResponse(
  2415. pIrpContext,
  2416. pIrpContext->rsp,
  2417. pIrpContext->ResponseLength,
  2418. "Ned",
  2419. &pNpScb->DestinationConnectionId,
  2420. &pNpScb->MaxPacketSize );
  2421. //
  2422. // Randomly downgrade the max burst size, because that is what
  2423. // the netware server does, and the new burst NLM requires.
  2424. //
  2425. pNpScb->MaxPacketSize -= 66;
  2426. }
  2427. if ( !NT_SUCCESS( Status ) ||
  2428. (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE)) {
  2429. pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
  2430. pNpScb->SendBurstModeEnabled = FALSE;
  2431. pNpScb->ReceiveBurstModeEnabled = FALSE;
  2432. } else {
  2433. //
  2434. // Use this size as the max read and write size instead of
  2435. // negotiating. This is what the VLM client does and is
  2436. // important because the negotiate will give a smaller value.
  2437. //
  2438. pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
  2439. }
  2440. }
  2441. NTSTATUS
  2442. GetMaxPacketSize(
  2443. PIRP_CONTEXT pIrpContext,
  2444. PNONPAGED_SCB pNpScb
  2445. )
  2446. /*++
  2447. Routine Description:
  2448. This routine attempts to use the LIP protocol to find the true MTU of
  2449. the network.
  2450. Arguments:
  2451. pIrpContext - Supplies context and server information.
  2452. pNpScb - A pointer to the NONPAGED_SCB for the server we are '
  2453. negotiating with.
  2454. Return Value:
  2455. None.
  2456. --*/
  2457. {
  2458. PUSHORT Buffer = NULL;
  2459. USHORT value;
  2460. int index;
  2461. PMDL PartialMdl = NULL, FullMdl = NULL;
  2462. PMDL ReceiveMdl;
  2463. NTSTATUS Status;
  2464. USHORT EchoSocket, LipPacketSize = 0;
  2465. int MinPacketSize, MaxPacketSize, CurrentPacketSize;
  2466. ULONG RxMdlLength = MdlLength(pIrpContext->RxMdl); // Save so we can restore it on exit.
  2467. BOOLEAN SecondTime = FALSE;
  2468. LARGE_INTEGER StartTime, Now, FirstPing, SecondPing, temp;
  2469. PAGED_CODE();
  2470. DebugTrace( +1, DEBUG_TRACE_LIP, "GetMaxPacketSize...\n", 0);
  2471. //
  2472. // Negotiate LIP, attempt to negotiate a buffer of full network
  2473. // size.
  2474. //
  2475. Status = ExchangeWithWait(
  2476. pIrpContext,
  2477. SynchronousResponseCallback,
  2478. "Fwb",
  2479. NCP_NEGOTIATE_LIP_CONNECTION,
  2480. pNpScb->BufferSize,
  2481. 0 ); // Flags
  2482. if ( NT_SUCCESS( Status )) {
  2483. Status = ParseResponse(
  2484. pIrpContext,
  2485. pIrpContext->rsp,
  2486. pIrpContext->ResponseLength,
  2487. "Nwx",
  2488. &LipPacketSize,
  2489. &EchoSocket );
  2490. }
  2491. //
  2492. // Speedup RAS
  2493. //
  2494. MaxPacketSize = (int) LipPacketSize - LipPacketAdjustment ;
  2495. if (( !NT_SUCCESS( Status )) ||
  2496. ( MaxPacketSize <= DEFAULT_PACKET_SIZE ) ||
  2497. ( EchoSocket == 0 )) {
  2498. //
  2499. // The server does not support LIP.
  2500. // Portable NW gives no error but socket 0.
  2501. // We have a report of a 3.11 server returning MaxPacketSize 0
  2502. //
  2503. return STATUS_NOT_SUPPORTED;
  2504. }
  2505. //
  2506. // Account for the IPX header, which is not counted in
  2507. // the reported packet size. This causes problems for
  2508. // servers with poorly written net card drivers that
  2509. // abend when they get an oversize packet.
  2510. //
  2511. // This was reported by Richard Florance (richfl).
  2512. //
  2513. MaxPacketSize -= 30;
  2514. pNpScb->EchoCounter = MaxPacketSize;
  2515. //
  2516. // We will use the Echo address for the LIP protocol.
  2517. //
  2518. BuildIpxAddress(
  2519. pNpScb->ServerAddress.Net,
  2520. pNpScb->ServerAddress.Node,
  2521. EchoSocket,
  2522. &pNpScb->EchoAddress );
  2523. try {
  2524. Buffer = ALLOCATE_POOL_EX( NonPagedPool, MaxPacketSize );
  2525. //
  2526. // Avoid RAS compression algorithm from making the large and small
  2527. // buffers the same length since we want to see the difference in
  2528. // transmission times.
  2529. //
  2530. for (index = 0, value = 0; index < MaxPacketSize/2; index++, value++) {
  2531. Buffer[index] = value;
  2532. }
  2533. FullMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
  2534. if ( FullMdl == NULL ) {
  2535. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2536. }
  2537. PartialMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
  2538. if ( PartialMdl == NULL ) {
  2539. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2540. }
  2541. ReceiveMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
  2542. if ( ReceiveMdl == NULL ) {
  2543. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2544. }
  2545. } except( NwExceptionFilter( pIrpContext->pOriginalIrp, GetExceptionInformation() )) {
  2546. if ( Buffer != NULL ) {
  2547. FREE_POOL( Buffer );
  2548. }
  2549. if ( FullMdl != NULL ) {
  2550. FREE_MDL( FullMdl );
  2551. }
  2552. if ( PartialMdl != NULL ) {
  2553. FREE_MDL( FullMdl );
  2554. }
  2555. return STATUS_NOT_SUPPORTED;
  2556. }
  2557. MmBuildMdlForNonPagedPool( FullMdl );
  2558. //
  2559. // Allocate a receive MDL and chain in to the IrpContext receive MDL.
  2560. //
  2561. pIrpContext->RxMdl->ByteCount = sizeof( NCP_RESPONSE ) + sizeof(ULONG);
  2562. MmBuildMdlForNonPagedPool( ReceiveMdl );
  2563. pIrpContext->RxMdl->Next = ReceiveMdl;
  2564. CurrentPacketSize = MaxPacketSize;
  2565. MinPacketSize = DEFAULT_PACKET_SIZE;
  2566. // Log values before we update them.
  2567. DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
  2568. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
  2569. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
  2570. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
  2571. //
  2572. // The LIP sequence number is used to let us know if the response packet we are looking
  2573. // at goes with the packet that we just sent. On a really slow link, it could be a
  2574. // response that we have already given up on.
  2575. //
  2576. // The LIP tick adjustment tells ExchangeWithWait to try waiting a little longer.
  2577. //
  2578. pNpScb->LipSequenceNumber = 0;
  2579. pNpScb->LipTickAdjustment = 0;
  2580. //
  2581. // Loop using the bisection method to find the maximum packet size. Feel free to
  2582. // use shortcuts to avoid unnecessary timeouts.
  2583. //
  2584. while (TRUE) {
  2585. //
  2586. // Every time we send a packet, increment the LIP sequence number.
  2587. // We check the LIP sequence number in ServerDatagramHandler.
  2588. //
  2589. pNpScb->LipSequenceNumber++;
  2590. DebugTrace( 0, DEBUG_TRACE_LIP, "Sending %d byte echo\n", CurrentPacketSize );
  2591. IoBuildPartialMdl(
  2592. FullMdl,
  2593. PartialMdl,
  2594. Buffer,
  2595. CurrentPacketSize - sizeof(NCP_RESPONSE) - sizeof(ULONG) );
  2596. //
  2597. // Send an echo packet. If we get a response, then we know that
  2598. // the minimum packet size we can use is at least as big as the
  2599. // echo packet size.
  2600. //
  2601. pIrpContext->pTdiStruct = &pIrpContext->pNpScb->Echo;
  2602. //
  2603. // Short-circuit the even better RAS compression.
  2604. //
  2605. for ( index = 0; index < MaxPacketSize/2; index++, value++) {
  2606. Buffer[index] = value;
  2607. }
  2608. KeQuerySystemTime( &StartTime );
  2609. Status = ExchangeWithWait(
  2610. pIrpContext,
  2611. SynchronousResponseCallback,
  2612. "E_DDf",
  2613. sizeof(NCP_RESPONSE ),
  2614. pNpScb->EchoCounter,
  2615. pNpScb->LipSequenceNumber,
  2616. PartialMdl );
  2617. if (( Status != STATUS_REMOTE_NOT_LISTENING ) ||
  2618. ( SecondTime )) {
  2619. KeQuerySystemTime( &Now );
  2620. DebugTrace( 0, DEBUG_TRACE_LIP, "Response received %08lx\n", Status);
  2621. if (!SecondTime) {
  2622. MinPacketSize = CurrentPacketSize;
  2623. FirstPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
  2624. }
  2625. } else {
  2626. DebugTrace( 0, DEBUG_TRACE_LIP, "No response\n", 0);
  2627. MaxPacketSize = CurrentPacketSize;
  2628. }
  2629. pNpScb->EchoCounter++;
  2630. MmPrepareMdlForReuse( PartialMdl );
  2631. if (( MaxPacketSize - MinPacketSize <= LipAccuracy ) ||
  2632. ( SecondTime )) {
  2633. //
  2634. // We have the maximum packet size.
  2635. // Now - StartTime is how long it takes for the round-trip. Now we'll
  2636. // try the same thing with a small packet and see how long it takes. From
  2637. // this we'll derive a throughput rating.
  2638. //
  2639. if ( SecondTime) {
  2640. SecondPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
  2641. break;
  2642. } else {
  2643. SecondTime = TRUE;
  2644. // Use a small packet size to verify that the server is still up.
  2645. CurrentPacketSize = sizeof(NCP_RESPONSE) + sizeof(ULONG) * 2;
  2646. }
  2647. } else {
  2648. //
  2649. // Calculate the next packet size guess.
  2650. //
  2651. if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
  2652. ( MaxPacketSize == 1463 )) {
  2653. CurrentPacketSize = 1458;
  2654. } else if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
  2655. ( MaxPacketSize == 1458 )) {
  2656. CurrentPacketSize = 1436;
  2657. } else {
  2658. //
  2659. // We didn't try one of our standard sizes so use the chop search
  2660. // to get to the next value.
  2661. //
  2662. CurrentPacketSize = ( MaxPacketSize + MinPacketSize ) / 2;
  2663. }
  2664. }
  2665. }
  2666. DebugTrace( 0, DEBUG_TRACE_LIP, "Set maximum burst packet size to %d\n", MinPacketSize );
  2667. DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing H = %08lx\n", FirstPing.HighPart );
  2668. DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing L = %08lx\n", FirstPing.LowPart );
  2669. DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing H = %08lx\n", SecondPing.HighPart );
  2670. DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing L = %08lx\n", SecondPing.LowPart );
  2671. //
  2672. // Avoid a divide by zero error if something bad happened.
  2673. //
  2674. if ( FirstPing.QuadPart != 0 ) {
  2675. pNpScb->LipDataSpeed = (ULONG) ( ( (LONGLONG)MinPacketSize * (LONGLONG)1600000 )
  2676. / FirstPing.QuadPart );
  2677. } else {
  2678. pNpScb->LipDataSpeed = 0;
  2679. }
  2680. DebugTrace( 0, DEBUG_TRACE_LIP, "LipDataSpeed: %d\n", pNpScb->LipDataSpeed );
  2681. if ((NT_SUCCESS(Status)) &&
  2682. ( MinPacketSize > DEFAULT_PACKET_SIZE )) {
  2683. temp.QuadPart = FirstPing.QuadPart - SecondPing.QuadPart;
  2684. if (temp.QuadPart > 0) {
  2685. //
  2686. // Convert to single trip instead of both ways.
  2687. //
  2688. temp.QuadPart = temp.QuadPart / (2 * 1000);
  2689. } else {
  2690. //
  2691. // Small packet ping is slower or the same speed as the big ping.
  2692. // We can't time a small enough interval so go for no delay at all.
  2693. //
  2694. temp.QuadPart = 0;
  2695. }
  2696. ASSERT(temp.HighPart == 0);
  2697. pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay =
  2698. MAX(temp.LowPart, (ULONG)MinSendDelay);
  2699. pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay =
  2700. MAX(temp.LowPart, (ULONG)MinReceiveDelay);
  2701. //
  2702. // Time for a big packet to go one way.
  2703. //
  2704. pNpScb->NwSingleBurstPacketTime = pNpScb->NwReceiveDelay;
  2705. pNpScb->NtSendDelay.QuadPart = pNpScb->NwReceiveDelay * -1000;
  2706. //
  2707. // Maximum that SendDelay is allowed to reach
  2708. //
  2709. pNpScb->NwMaxSendDelay = MAX( 52, MIN( pNpScb->NwSendDelay, MaxSendDelay ));
  2710. pNpScb->NwMaxReceiveDelay = MAX( 52, MIN( pNpScb->NwReceiveDelay, MaxReceiveDelay ));
  2711. //
  2712. // Time for a small packet to get to the server and back.
  2713. //
  2714. temp.QuadPart = SecondPing.QuadPart / 1000;
  2715. pNpScb->NwLoopTime = temp.LowPart;
  2716. DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
  2717. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
  2718. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxSendDelay = %08lx\n", pNpScb->NwMaxSendDelay );
  2719. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxReceiveDelay = %08lx\n", pNpScb->NwMaxReceiveDelay );
  2720. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwLoopTime = %08lx\n", pNpScb->NwLoopTime );
  2721. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
  2722. DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
  2723. //
  2724. // Reset Tdi struct so that we send future NCPs from the server socket.
  2725. //
  2726. pIrpContext->pTdiStruct = NULL;
  2727. //
  2728. // Now decouple the MDL
  2729. //
  2730. pIrpContext->TxMdl->Next = NULL;
  2731. pIrpContext->RxMdl->Next = NULL;
  2732. pIrpContext->RxMdl->ByteCount = RxMdlLength;
  2733. //
  2734. // Calculate the maximum amount of data we can send in a burst write
  2735. // packet after all the header info is stripped.
  2736. //
  2737. pNpScb->MaxPacketSize = MinPacketSize - sizeof( NCP_BURST_WRITE_REQUEST );
  2738. FREE_MDL( PartialMdl );
  2739. FREE_MDL( ReceiveMdl );
  2740. FREE_MDL( FullMdl );
  2741. FREE_POOL( Buffer );
  2742. DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
  2743. return STATUS_SUCCESS;
  2744. } else {
  2745. //
  2746. // If the small packet couldn't echo then assume the worst.
  2747. //
  2748. //
  2749. // Reset Tdi struct so that we send future NCPs from the server socket.
  2750. //
  2751. pIrpContext->pTdiStruct = NULL;
  2752. //
  2753. // Now decouple the MDL
  2754. //
  2755. pIrpContext->TxMdl->Next = NULL;
  2756. pIrpContext->RxMdl->Next = NULL;
  2757. pIrpContext->RxMdl->ByteCount = RxMdlLength;
  2758. FREE_MDL( PartialMdl );
  2759. FREE_MDL( ReceiveMdl );
  2760. FREE_MDL( FullMdl );
  2761. FREE_POOL( Buffer );
  2762. DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
  2763. return STATUS_NOT_SUPPORTED;
  2764. }
  2765. }
  2766. VOID
  2767. DestroyAllScb(
  2768. VOID
  2769. )
  2770. /*++
  2771. Routine Description:
  2772. This routine destroys all server control blocks.
  2773. Arguments:
  2774. Return Value:
  2775. --*/
  2776. {
  2777. KIRQL OldIrql;
  2778. PLIST_ENTRY ScbQueueEntry;
  2779. PNONPAGED_SCB pNpScb;
  2780. DebugTrace(+1, Dbg, "DestroyAllScbs....\n", 0);
  2781. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  2782. //
  2783. // Walk the list of SCBs and kill them all.
  2784. //
  2785. while (!IsListEmpty(&ScbQueue)) {
  2786. ScbQueueEntry = RemoveHeadList( &ScbQueue );
  2787. pNpScb = CONTAINING_RECORD(ScbQueueEntry, NONPAGED_SCB, ScbLinks);
  2788. //
  2789. // We can't hold the spin lock while deleting an SCB, so release
  2790. // it now.
  2791. //
  2792. KeReleaseSpinLock(&ScbSpinLock, OldIrql);
  2793. NwDeleteScb( pNpScb->pScb );
  2794. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  2795. }
  2796. KeReleaseSpinLock(&ScbSpinLock, OldIrql);
  2797. DebugTrace(-1, Dbg, "DestroyAllScb\n", 0 );
  2798. }
  2799. VOID
  2800. NwDeleteScb(
  2801. PSCB pScb
  2802. )
  2803. /*++
  2804. Routine Description:
  2805. This routine deletes an SCB. The SCB must not be in use.
  2806. *** The caller must own the RCB exclusive.
  2807. Arguments:
  2808. Scb - The SCB to delete
  2809. Return Value:
  2810. None.
  2811. --*/
  2812. {
  2813. PNONPAGED_SCB pNpScb;
  2814. PLIST_ENTRY CacheEntry;
  2815. PNDS_OBJECT_CACHE_ENTRY ObjectCache;
  2816. BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
  2817. PAGED_CODE();
  2818. DebugTrace(+1, Dbg, "NwDeleteScb...\n", 0);
  2819. pNpScb = pScb->pNpScb;
  2820. //
  2821. // Make sure we are not deleting a logged in connection
  2822. // or we will hang up the license until the server times
  2823. // it out.
  2824. //
  2825. ASSERT( pNpScb->State != SCB_STATE_IN_USE );
  2826. ASSERT( pNpScb->Reference == 0 );
  2827. ASSERT( !pNpScb->Sending );
  2828. ASSERT( !pNpScb->Receiving );
  2829. ASSERT( !pNpScb->OkToReceive );
  2830. ASSERT( IsListEmpty( &pNpScb->Requests ) );
  2831. ASSERT( IsListEmpty( &pScb->IcbList ) );
  2832. ASSERT( pScb->IcbCount == 0 );
  2833. ASSERT( pScb->VcbCount == 0 );
  2834. DebugTrace(0, Dbg, "Cleaning up SCB %08lx\n", pScb);
  2835. if ( AnonymousScb ) {
  2836. DebugTrace(0, Dbg, "SCB is anonymous\n", &pNpScb->ServerName );
  2837. } else {
  2838. ASSERT( IsListEmpty( &pScb->ScbSpecificVcbQueue ) );
  2839. DebugTrace(0, Dbg, "SCB is %wZ\n", &pNpScb->ServerName );
  2840. }
  2841. DebugTrace(0, Dbg, "SCB state is %d\n", &pNpScb->State );
  2842. if ( !AnonymousScb ) {
  2843. RtlRemoveUnicodePrefix ( &NwRcb.ServerNameTable, &pScb->PrefixEntry );
  2844. }
  2845. IPX_Close_Socket( &pNpScb->Server );
  2846. IPX_Close_Socket( &pNpScb->WatchDog );
  2847. IPX_Close_Socket( &pNpScb->Send );
  2848. IPX_Close_Socket( &pNpScb->Echo);
  2849. IPX_Close_Socket( &pNpScb->Burst);
  2850. NwUninitializePidTable( pNpScb );
  2851. FREE_POOL( pNpScb );
  2852. if ( pScb->UserName.Buffer != NULL ) {
  2853. FREE_POOL( pScb->UserName.Buffer );
  2854. }
  2855. //
  2856. // Free the object cache buffer if there is one.
  2857. //
  2858. if( pScb->ObjectCacheBuffer != NULL ) {
  2859. //
  2860. // Remove any references this cache has to other SCBs.
  2861. //
  2862. // NOTE: We do not need the lock here, since no one else
  2863. // can be using this SCB.
  2864. //
  2865. CacheEntry = pScb->ObjectCacheList.Flink;
  2866. while( CacheEntry != &(pScb->ObjectCacheList) ) {
  2867. ObjectCache = CONTAINING_RECORD( CacheEntry, NDS_OBJECT_CACHE_ENTRY, Links );
  2868. if( ObjectCache->Scb != NULL ) {
  2869. NwDereferenceScb( ObjectCache->Scb->pNpScb );
  2870. ObjectCache->Scb = NULL;
  2871. }
  2872. CacheEntry = CacheEntry->Flink;
  2873. }
  2874. FREE_POOL( pScb->ObjectCacheBuffer );
  2875. }
  2876. FREE_POOL( pScb );
  2877. DebugTrace(-1, Dbg, "NwDeleteScb -> VOID\n", 0);
  2878. }
  2879. PNONPAGED_SCB
  2880. SelectConnection(
  2881. PNONPAGED_SCB NpScb OPTIONAL
  2882. )
  2883. /*++
  2884. Routine Description:
  2885. Find a default server (which is also the nearest server).
  2886. If NpScb is not supplied, simply return the first server in
  2887. the list. If it is supplied return the next server in the
  2888. list after the given server.
  2889. Arguments:
  2890. NpScb - The starting point for the server search.
  2891. Return Value:
  2892. Scb to be used or NULL.
  2893. --*/
  2894. {
  2895. PLIST_ENTRY ScbQueueEntry;
  2896. KIRQL OldIrql;
  2897. PNONPAGED_SCB pNextNpScb;
  2898. DebugTrace(+1, Dbg, "SelectConnection....\n", 0);
  2899. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  2900. if ( NpScb == NULL ) {
  2901. ScbQueueEntry = ScbQueue.Flink ;
  2902. } else {
  2903. ScbQueueEntry = NpScb->ScbLinks.Flink;
  2904. }
  2905. for ( ;
  2906. ScbQueueEntry != &ScbQueue ;
  2907. ScbQueueEntry = ScbQueueEntry->Flink ) {
  2908. pNextNpScb = CONTAINING_RECORD(
  2909. ScbQueueEntry,
  2910. NONPAGED_SCB,
  2911. ScbLinks );
  2912. //
  2913. // Check to make sure that this SCB is usable.
  2914. //
  2915. if (( pNextNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
  2916. ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
  2917. ( pNextNpScb->State == SCB_STATE_IN_USE )) {
  2918. NwReferenceScb( pNextNpScb );
  2919. KeReleaseSpinLock(&ScbSpinLock, OldIrql);
  2920. DebugTrace(+0, Dbg, " NpScb = %lx\n", pNextNpScb );
  2921. DebugTrace(-1, Dbg, " NpScb->State = %x\n", pNextNpScb->State );
  2922. return pNextNpScb;
  2923. }
  2924. }
  2925. KeReleaseSpinLock( &ScbSpinLock, OldIrql);
  2926. DebugTrace(-1, Dbg, " NULL\n", 0);
  2927. return NULL;
  2928. }
  2929. VOID
  2930. NwLogoffAllServers(
  2931. PIRP_CONTEXT pIrpContext,
  2932. PLARGE_INTEGER Uid
  2933. )
  2934. /*++
  2935. Routine Description:
  2936. This routine sends a logoff to all connected servers created by the Logon
  2937. user or all servers if Logon is NULL.
  2938. Arguments:
  2939. Uid - Supplies the servers to disconnect from.
  2940. Return Value:
  2941. none.
  2942. --*/
  2943. {
  2944. KIRQL OldIrql;
  2945. PLIST_ENTRY ScbQueueEntry;
  2946. PLIST_ENTRY NextScbQueueEntry;
  2947. PNONPAGED_SCB pNpScb;
  2948. DebugTrace( 0, Dbg, "NwLogoffAllServers\n", 0 );
  2949. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  2950. for (ScbQueueEntry = ScbQueue.Flink ;
  2951. ScbQueueEntry != &ScbQueue ;
  2952. ScbQueueEntry = NextScbQueueEntry ) {
  2953. pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
  2954. //
  2955. // Reference the SCB so that it doesn't disappear while we
  2956. // are disconnecting.
  2957. //
  2958. NwReferenceScb( pNpScb );
  2959. //
  2960. // Release the SCB spin lock so that we can send a logoff
  2961. // NCP.
  2962. //
  2963. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  2964. //
  2965. // Destroy this Scb if its not the permanent Scb and either we
  2966. // are destroying all Scb's or it was created for this user.
  2967. //
  2968. if (( pNpScb->pScb != NULL ) &&
  2969. (( Uid == NULL ) ||
  2970. ( pNpScb->pScb->UserUid.QuadPart == (*Uid).QuadPart))) {
  2971. NwLogoffAndDisconnect( pIrpContext, pNpScb );
  2972. }
  2973. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  2974. //
  2975. // Release the temporary reference.
  2976. //
  2977. NextScbQueueEntry = pNpScb->ScbLinks.Flink;
  2978. NwDereferenceScb( pNpScb );
  2979. }
  2980. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  2981. }
  2982. VOID
  2983. NwLogoffAndDisconnect(
  2984. PIRP_CONTEXT pIrpContext,
  2985. PNONPAGED_SCB pNpScb
  2986. )
  2987. /*++
  2988. Routine Description:
  2989. This routine sends a logoff and disconnects from the name server.
  2990. Arguments:
  2991. pIrpContext - A pointer to the current IRP context.
  2992. pNpScb - A pointer to the server to logoff and disconnect.
  2993. Return Value:
  2994. None.
  2995. --*/
  2996. {
  2997. PSCB pScb = pNpScb->pScb;
  2998. PAGED_CODE();
  2999. pIrpContext->pNpScb = pNpScb;
  3000. pIrpContext->pScb = pScb;
  3001. //
  3002. // Queue ourselves to the SCB, and wait to get to the front to
  3003. // protect access to server State.
  3004. //
  3005. NwAppendToQueueAndWait( pIrpContext );
  3006. //
  3007. // If we are logging out from the preferred server, free the preferred
  3008. // server reference.
  3009. //
  3010. if ( pScb != NULL &&
  3011. pScb->PreferredServer ) {
  3012. pScb->PreferredServer = FALSE;
  3013. NwDereferenceScb( pNpScb );
  3014. }
  3015. //
  3016. // Nothing to do if we are not connected.
  3017. //
  3018. if ( pNpScb->State != SCB_STATE_IN_USE &&
  3019. pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) {
  3020. NwDequeueIrpContext( pIrpContext, FALSE );
  3021. return;
  3022. }
  3023. //
  3024. // If we timeout then we don't want to go to the bother of
  3025. // reconnecting.
  3026. //
  3027. ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
  3028. //
  3029. // Logout and disconnect.
  3030. //
  3031. if ( pNpScb->State == SCB_STATE_IN_USE ) {
  3032. ExchangeWithWait (
  3033. pIrpContext,
  3034. SynchronousResponseCallback,
  3035. "F",
  3036. NCP_LOGOUT );
  3037. }
  3038. ExchangeWithWait (
  3039. pIrpContext,
  3040. SynchronousResponseCallback,
  3041. "D-" ); // Disconnect
  3042. Stats.Sessions--;
  3043. if ( pScb->MajorVersion == 2 ) {
  3044. Stats.NW2xConnects--;
  3045. } else if ( pScb->MajorVersion == 3 ) {
  3046. Stats.NW3xConnects--;
  3047. } else if ( pScb->MajorVersion >= 4 ) {
  3048. Stats.NW4xConnects--;
  3049. }
  3050. //
  3051. // dfergus 19 Apr 2001 #302137
  3052. // Removed the following fix for fix #302137
  3053. //
  3054. // tommye - MS 25584 / MCS 274
  3055. //
  3056. // Added code to look for this tree in the user's cached
  3057. // credentials and remove it if there is one. This fixes
  3058. // a problem where the user could perform a tree connect
  3059. // providing the user name and password, delete the connection
  3060. // then connect again successfully providing only the user name
  3061. // (we would get the password from the cached credentials).
  3062. //
  3063. // 5/31/00 - made changes to locking per conversation with Anoop
  3064. //
  3065. /* // Begin 302137
  3066. {
  3067. PLOGON pLogon = NULL;
  3068. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  3069. pLogon = FindUser( &pScb->UserUid, FALSE );
  3070. NwReleaseRcb( &NwRcb );
  3071. if (pLogon) {
  3072. PLIST_ENTRY pFirst, pNext;
  3073. PNDS_SECURITY_CONTEXT pNdsContext;
  3074. // Lock the credential list while we walk it
  3075. NwAcquireExclusiveCredList( pLogon, pIrpContext );
  3076. pFirst = &pLogon->NdsCredentialList;
  3077. pNext = pLogon->NdsCredentialList.Flink;
  3078. // Go through the credential list and find a match for the tree name
  3079. while ( pNext && ( pFirst != pNext ) ) {
  3080. pNdsContext = (PNDS_SECURITY_CONTEXT)
  3081. CONTAINING_RECORD( pNext,
  3082. NDS_SECURITY_CONTEXT,
  3083. Next );
  3084. ASSERT( pNdsContext->ntc == NW_NTC_NDS_CREDENTIAL );
  3085. // If this is the same tree, free the entry
  3086. if ( !RtlCompareUnicodeString( &pScb->NdsTreeName,
  3087. &pNdsContext->NdsTreeName,
  3088. TRUE ) ) {
  3089. DebugTrace(0, Dbg, " Removing cached credential for tree %wZ\n", &pNdsContext->NdsTreeName);
  3090. RemoveEntryList( &pNdsContext->Next );
  3091. FreeNdsContext( pNdsContext );
  3092. break;
  3093. }
  3094. pNext = pNdsContext->Next.Flink;
  3095. }
  3096. // Release the lock
  3097. NwReleaseCredList( pLogon, pIrpContext );
  3098. }
  3099. }
  3100. */ // End 302137
  3101. //
  3102. // Free the remembered username and password.
  3103. //
  3104. if ( pScb != NULL && pScb->UserName.Buffer != NULL ) {
  3105. FREE_POOL( pScb->UserName.Buffer );
  3106. RtlInitUnicodeString( &pScb->UserName, NULL );
  3107. RtlInitUnicodeString( &pScb->Password, NULL );
  3108. }
  3109. pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
  3110. NwDequeueIrpContext( pIrpContext, FALSE );
  3111. return;
  3112. }
  3113. VOID
  3114. InitializeAttach (
  3115. VOID
  3116. )
  3117. /*++
  3118. Routine Description:
  3119. Initialize global structures for attaching to servers.
  3120. Arguments:
  3121. none.
  3122. Return Value:
  3123. none.
  3124. --*/
  3125. {
  3126. PAGED_CODE();
  3127. KeInitializeSpinLock( &ScbSpinLock );
  3128. InitializeListHead(&ScbQueue);
  3129. }
  3130. NTSTATUS
  3131. OpenScbSockets(
  3132. PIRP_CONTEXT pIrpContext,
  3133. PNONPAGED_SCB pNpScb
  3134. )
  3135. /*++
  3136. Routine Description:
  3137. Open the communications sockets for an SCB.
  3138. Arguments:
  3139. pIrpContext - The IRP context pointers for the request in progress.
  3140. pNpScb - The SCB to connect to the network.
  3141. Return Value:
  3142. The status of the operation.
  3143. --*/
  3144. {
  3145. NTSTATUS Status;
  3146. PAGED_CODE();
  3147. //
  3148. // Auto allocate to the server socket.
  3149. //
  3150. pNpScb->Server.Socket = 0;
  3151. Status = IPX_Open_Socket (pIrpContext, &pNpScb->Server);
  3152. if ( !NT_SUCCESS(Status) ) {
  3153. return( Status );
  3154. }
  3155. //
  3156. // Watchdog Socket is Server.Socket+1
  3157. //
  3158. pNpScb->WatchDog.Socket = NextSocket( pNpScb->Server.Socket );
  3159. Status = IPX_Open_Socket ( pIrpContext, &pNpScb->WatchDog );
  3160. if ( !NT_SUCCESS(Status) ) {
  3161. return( Status );
  3162. }
  3163. //
  3164. // Send Socket is WatchDog.Socket+1
  3165. //
  3166. pNpScb->Send.Socket = NextSocket( pNpScb->WatchDog.Socket );
  3167. Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Send );
  3168. if ( !NT_SUCCESS(Status) ) {
  3169. return( Status );
  3170. }
  3171. //
  3172. // Echo socket
  3173. //
  3174. pNpScb->Echo.Socket = NextSocket( pNpScb->Send.Socket );
  3175. Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Echo );
  3176. if ( !NT_SUCCESS(Status) ) {
  3177. return( Status );
  3178. }
  3179. //
  3180. // Burst socket
  3181. //
  3182. pNpScb->Burst.Socket = NextSocket( pNpScb->Echo.Socket );
  3183. Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Burst );
  3184. if ( !NT_SUCCESS(Status) ) {
  3185. return( Status );
  3186. }
  3187. return( STATUS_SUCCESS );
  3188. }
  3189. NTSTATUS
  3190. DoBinderyLogon(
  3191. IN PIRP_CONTEXT IrpContext,
  3192. IN PUNICODE_STRING UserName,
  3193. IN PUNICODE_STRING Password
  3194. )
  3195. /*++
  3196. Routine Description:
  3197. Performs a bindery based encrypted logon.
  3198. Note: Rcb is held exclusively so that we can access the Logon queue
  3199. safely.
  3200. Arguments:
  3201. pIrpContext - The IRP context pointers for the request in progress.
  3202. UserName - The user name to use to login.
  3203. Password - The password to use to login.
  3204. Return Value:
  3205. The status of the operation.
  3206. --*/
  3207. {
  3208. PNONPAGED_SCB pNpScb;
  3209. PSCB pScb;
  3210. UNICODE_STRING Name;
  3211. UNICODE_STRING PWord;
  3212. UCHAR EncryptKey[ENCRYPTION_KEY_SIZE ];
  3213. NTSTATUS Status;
  3214. PVOID Buffer;
  3215. PLOGON Logon = NULL;
  3216. PWCH OldBuffer;
  3217. PAGED_CODE();
  3218. DebugTrace( +1, Dbg, "DoBinderyLogon...\n", 0);
  3219. //
  3220. // First get an encryption key.
  3221. //
  3222. DebugTrace( +0, Dbg, " Get Login key\n", 0);
  3223. Status = ExchangeWithWait (
  3224. IrpContext,
  3225. SynchronousResponseCallback,
  3226. "S",
  3227. NCP_ADMIN_FUNCTION, NCP_GET_LOGIN_KEY );
  3228. pNpScb = IrpContext->pNpScb;
  3229. pScb = pNpScb->pScb;
  3230. DebugTrace( +0, Dbg, " %X\n", Status);
  3231. if ( NT_SUCCESS( Status ) ) {
  3232. Status = ParseResponse(
  3233. IrpContext,
  3234. IrpContext->rsp,
  3235. IrpContext->ResponseLength,
  3236. "Nr",
  3237. EncryptKey, sizeof(EncryptKey) );
  3238. }
  3239. DebugTrace( +0, Dbg, " %X\n", Status);
  3240. //
  3241. // Choose a name and password to use to connect to the server. Use
  3242. // the user supplied if the exist. Otherwise if the server already
  3243. // has a remembered username use the remembered name. If nothing
  3244. // else is available use the defaults from logon. Finally, if the
  3245. // user didn't even logon, use GUEST no password.
  3246. //
  3247. if ( UserName != NULL && UserName->Buffer != NULL ) {
  3248. Name = *UserName;
  3249. } else if ( pScb->UserName.Buffer != NULL ) {
  3250. Name = pScb->UserName;
  3251. } else {
  3252. Logon = FindUser( &pScb->UserUid, FALSE );
  3253. if (Logon != NULL ) {
  3254. Name = Logon->UserName;
  3255. } else {
  3256. ASSERT( FALSE && "No logon record found" );
  3257. return( STATUS_ACCESS_DENIED );
  3258. }
  3259. }
  3260. if ( Password != NULL && Password->Buffer != NULL ) {
  3261. PWord = *Password;
  3262. } else if ( pScb->Password.Buffer != NULL ) {
  3263. /*
  3264. * Password is not passed in, or implied.
  3265. * If the user name matches or was not passed in,
  3266. * then use the SCB password,
  3267. * else use a null password.
  3268. */
  3269. if ( UserName == NULL || UserName->Buffer == NULL ||
  3270. ( RtlEqualUnicodeString( &pScb->UserName, UserName, TRUE )) ) {
  3271. PWord = pScb->Password;
  3272. } else {
  3273. PWord.Buffer = L"";
  3274. PWord.Length = PWord.MaximumLength = 0;
  3275. }
  3276. } else {
  3277. if ( Logon == NULL ) {
  3278. Logon = FindUser( &pScb->UserUid, FALSE );
  3279. }
  3280. if ( Logon != NULL ) {
  3281. PWord = Logon->PassWord;
  3282. } else {
  3283. ASSERT( FALSE && "No logon record found" );
  3284. return( STATUS_ACCESS_DENIED );
  3285. }
  3286. }
  3287. if ( !NT_SUCCESS(Status) ) {
  3288. //
  3289. // Failed to get an encryption key. Login to server, plain text
  3290. //
  3291. DebugTrace( +0, Dbg, " Plain Text Login\n", 0);
  3292. Status = ExchangeWithWait (
  3293. IrpContext,
  3294. SynchronousResponseCallback,
  3295. "SwJU", // JimTh 5/19/2001 - Send Name using 'J' option, DBCS translation if on Far East system
  3296. NCP_ADMIN_FUNCTION, NCP_PLAIN_TEXT_LOGIN,
  3297. OT_USER,
  3298. &Name,
  3299. &PWord);
  3300. DebugTrace( +0, Dbg, " %X\n", Status);
  3301. if ( NT_SUCCESS( Status ) ) {
  3302. Status = ParseResponse(
  3303. IrpContext,
  3304. IrpContext->rsp,
  3305. IrpContext->ResponseLength,
  3306. "N" );
  3307. }
  3308. DebugTrace( +0, Dbg, " %X\n", Status);
  3309. if ( !NT_SUCCESS( Status )) {
  3310. return( STATUS_WRONG_PASSWORD);
  3311. }
  3312. } else if ( NT_SUCCESS( Status ) ) {
  3313. //
  3314. // We have an encryption key. Get the ObjectId
  3315. //
  3316. UCHAR Response[ENCRYPTION_KEY_SIZE];
  3317. UCHAR ObjectId[OBJECT_ID_SIZE];
  3318. OEM_STRING UOPassword;
  3319. DebugTrace( +0, Dbg, " Query users objectid\n", 0);
  3320. Status = ExchangeWithWait (
  3321. IrpContext,
  3322. SynchronousResponseCallback,
  3323. "SwJ", // JimTh 5/19/2001 - Send Name using 'J' option, DBCS translation if on Far East system
  3324. NCP_ADMIN_FUNCTION, NCP_QUERY_OBJECT_ID,
  3325. OT_USER,
  3326. &Name);
  3327. DebugTrace( +0, Dbg, " %X\n", Status);
  3328. if ( NT_SUCCESS( Status ) ) {
  3329. //
  3330. // Save the new address in a local copy so that we can logout.
  3331. //
  3332. Status = ParseResponse(
  3333. IrpContext,
  3334. IrpContext->rsp,
  3335. IrpContext->ResponseLength,
  3336. "Nr",
  3337. ObjectId, OBJECT_ID_SIZE );
  3338. }
  3339. DebugTrace( +0, Dbg, " %X\n", Status);
  3340. if (!NT_SUCCESS(Status)) {
  3341. return( STATUS_NO_SUCH_USER );
  3342. }
  3343. //
  3344. // Convert the unicode password to uppercase and then the oem
  3345. // character set.
  3346. //
  3347. if ( PWord.Length > 0 ) {
  3348. Status = RtlUpcaseUnicodeStringToOemString( &UOPassword, &PWord, TRUE );
  3349. if (!NT_SUCCESS(Status)) {
  3350. return( Status );
  3351. }
  3352. } else {
  3353. UOPassword.Buffer = "";
  3354. UOPassword.Length = UOPassword.MaximumLength = 0;
  3355. }
  3356. RespondToChallenge( ObjectId, &UOPassword, EncryptKey, Response);
  3357. if ( PWord.Length > 0) {
  3358. RtlFreeAnsiString( &UOPassword );
  3359. }
  3360. DebugTrace( +0, Dbg, " Encrypted Login\n", 0);
  3361. Status = ExchangeWithWait (
  3362. IrpContext,
  3363. SynchronousResponseCallback,
  3364. "SrwJ", // JimTh 5/19/2001 - Send Name using 'J' option, DBCS translation if on Far East system
  3365. NCP_ADMIN_FUNCTION, NCP_ENCRYPTED_LOGIN,
  3366. Response, sizeof(Response),
  3367. OT_USER,
  3368. &Name);
  3369. DebugTrace( +0, Dbg, " %X\n", Status);
  3370. if ( NT_SUCCESS( Status ) ) {
  3371. //
  3372. // Save the new address in a local copy so that we can logout
  3373. //
  3374. Status = ParseResponse(
  3375. IrpContext,
  3376. IrpContext->rsp,
  3377. IrpContext->ResponseLength,
  3378. "N" );
  3379. }
  3380. DebugTrace( +0, Dbg, " %X\n", Status);
  3381. if ( !NT_SUCCESS( Status )) {
  3382. //
  3383. // Special case error mappings.
  3384. //
  3385. if (( Status == STATUS_UNSUCCESSFUL ) ||
  3386. ( Status == STATUS_UNEXPECTED_NETWORK_ERROR /* 2.2 servers */ )) {
  3387. Status = STATUS_WRONG_PASSWORD;
  3388. }
  3389. if ( Status == STATUS_LOCK_NOT_GRANTED ) {
  3390. Status = STATUS_ACCOUNT_RESTRICTION; // Bindery locked
  3391. }
  3392. if ( Status == STATUS_DISK_FULL ) {
  3393. #ifdef QFE_BUILD
  3394. Status = STATUS_TOO_MANY_SESSIONS;
  3395. #else
  3396. Status = STATUS_REMOTE_SESSION_LIMIT;
  3397. #endif
  3398. }
  3399. if ( Status == STATUS_FILE_LOCK_CONFLICT ) {
  3400. Status = STATUS_SHARING_PAUSED;
  3401. }
  3402. if ( Status == STATUS_NO_MORE_ENTRIES ) {
  3403. Status = STATUS_NO_SUCH_USER; // No such object on "Login Object Encrypted" NCP.
  3404. }
  3405. //
  3406. // Netware 4.x servers return a different NCP error for
  3407. // a disabled account (from intruder lockout) on bindery login,
  3408. // and nwconvert maps this to a dos error. In this special case,
  3409. // we'll catch it and map it back.
  3410. //
  3411. if ( ( IrpContext->pNpScb->pScb->MajorVersion >= 4 ) &&
  3412. ( Status == 0xC001003B ) ) {
  3413. Status = STATUS_ACCOUNT_DISABLED;
  3414. }
  3415. return( Status );
  3416. }
  3417. } else {
  3418. return( Status );
  3419. }
  3420. //
  3421. // If the Uid is for the system process then the username must be
  3422. // in the NtGateway group on the server.
  3423. //
  3424. if ( IrpContext->Specific.Create.UserUid.QuadPart == DefaultLuid.QuadPart) {
  3425. NTSTATUS Status1 ;
  3426. // IsBinderyObjectInSet?
  3427. Status1 = ExchangeWithWait (
  3428. IrpContext,
  3429. SynchronousResponseCallback,
  3430. "SwppwU",
  3431. NCP_ADMIN_FUNCTION, NCP_IS_OBJECT_IN_SET,
  3432. OT_GROUP,
  3433. "NTGATEWAY",
  3434. "GROUP_MEMBERS",
  3435. OT_USER,
  3436. &Name);
  3437. if ( !NT_SUCCESS( Status1 ) ) {
  3438. return STATUS_ACCESS_DENIED;
  3439. }
  3440. }
  3441. //
  3442. // Success. Save the username & password for reconnect.
  3443. //
  3444. //
  3445. // Setup to free the old user name and password buffer.
  3446. //
  3447. if ( pScb->UserName.Buffer != NULL ) {
  3448. OldBuffer = pScb->UserName.Buffer;
  3449. } else {
  3450. OldBuffer = NULL;
  3451. }
  3452. Buffer = ALLOCATE_POOL( NonPagedPool, Name.Length + PWord.Length );
  3453. if ( Buffer == NULL ) {
  3454. return( STATUS_INSUFFICIENT_RESOURCES );
  3455. }
  3456. pScb->UserName.Buffer = Buffer;
  3457. pScb->UserName.Length = pScb->UserName.MaximumLength = Name.Length;
  3458. RtlMoveMemory( pScb->UserName.Buffer, Name.Buffer, Name.Length );
  3459. pScb->Password.Buffer = (PWCHAR)((PCHAR)Buffer + Name.Length);
  3460. pScb->Password.Length = pScb->Password.MaximumLength = PWord.Length;
  3461. RtlMoveMemory( pScb->Password.Buffer, PWord.Buffer, PWord.Length );
  3462. if ( OldBuffer != NULL ) {
  3463. FREE_POOL( OldBuffer );
  3464. }
  3465. return( Status );
  3466. }
  3467. NTSTATUS
  3468. NwAllocateAndInitScb(
  3469. IN PIRP_CONTEXT pIrpContext,
  3470. IN PUNICODE_STRING UidServerName OPTIONAL,
  3471. IN PUNICODE_STRING ServerName OPTIONAL,
  3472. OUT PSCB *ppScb
  3473. )
  3474. /*++
  3475. Routine Description:
  3476. This routine returns a pointer to a newly created SCB. If
  3477. the UidServerName and ServerName are supplied, the SCB name
  3478. fields are initialized to this name. Otherwise, the name
  3479. fields are left blank to be filled in later.
  3480. If UidServerName is provided, ServerName MUST also be provided!!
  3481. The returned SCB is NOT filed in the server prefix table since
  3482. it might not yet have a name.
  3483. Return Value:
  3484. The created SCB or NULL.
  3485. --*/
  3486. {
  3487. NTSTATUS Status;
  3488. PSCB pScb = NULL;
  3489. PNONPAGED_SCB pNpScb = NULL;
  3490. USHORT ServerNameLength;
  3491. PLOGON Logon;
  3492. PNDS_OBJECT_CACHE_ENTRY ObjectEntry;
  3493. ULONG EntrySize;
  3494. ULONG i;
  3495. //
  3496. // Allocate enough space for a credential munged tree name.
  3497. //
  3498. pScb = ALLOCATE_POOL ( PagedPool,
  3499. sizeof( SCB ) +
  3500. ( ( NDS_TREE_NAME_LEN + MAX_NDS_NAME_CHARS + 2 ) * sizeof( WCHAR ) ) );
  3501. if ( !pScb ) {
  3502. return STATUS_INSUFFICIENT_RESOURCES;
  3503. }
  3504. RtlZeroMemory( pScb, sizeof( SCB ) );
  3505. RtlInitializeBitMap( &pScb->DriveMapHeader, pScb->DriveMap, MAX_DRIVES );
  3506. //
  3507. // Initialize pointers to ensure cleanup on error case operates
  3508. // correctly.
  3509. //
  3510. if ( UidServerName &&
  3511. UidServerName->Length ) {
  3512. ServerNameLength = UidServerName->Length + sizeof( WCHAR );
  3513. } else {
  3514. ServerNameLength = ( MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) ) +
  3515. ( MAX_UNICODE_UID_LENGTH * sizeof( WCHAR ) ) +
  3516. ( 2 * sizeof( WCHAR ) );
  3517. }
  3518. pScb->pNpScb = ALLOCATE_POOL ( NonPagedPool,
  3519. sizeof( NONPAGED_SCB ) + ServerNameLength );
  3520. if ( !pScb->pNpScb ) {
  3521. Status = STATUS_INSUFFICIENT_RESOURCES;
  3522. goto ExitWithCleanup;
  3523. }
  3524. RtlZeroMemory( pScb->pNpScb, sizeof( NONPAGED_SCB ) );
  3525. pNpScb = pScb->pNpScb;
  3526. pNpScb->pScb = pScb;
  3527. //
  3528. // If we know the server name, copy it to the allocated buffer.
  3529. // Append a NULL so that we can use the name a nul-terminated string.
  3530. //
  3531. pScb->UidServerName.Buffer = (PWCHAR)( pScb->pNpScb + 1 );
  3532. pScb->UidServerName.MaximumLength = ServerNameLength;
  3533. pScb->UidServerName.Length = 0;
  3534. RtlInitUnicodeString( &(pNpScb->ServerName), NULL );
  3535. if ( UidServerName &&
  3536. UidServerName->Length ) {
  3537. RtlCopyUnicodeString ( &pScb->UidServerName, UidServerName );
  3538. pScb->UidServerName.Buffer[ UidServerName->Length / sizeof( WCHAR ) ] = L'\0';
  3539. pScb->UnicodeUid = pScb->UidServerName;
  3540. pScb->UnicodeUid.Length = UidServerName->Length -
  3541. ServerName->Length -
  3542. sizeof(WCHAR);
  3543. //
  3544. // Make ServerName point partway down the buffer for UidServerName
  3545. //
  3546. pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
  3547. UidServerName->Length - ServerName->Length);
  3548. pNpScb->ServerName.MaximumLength = ServerName->Length;
  3549. pNpScb->ServerName.Length = ServerName->Length;
  3550. }
  3551. pScb->NdsTreeName.MaximumLength = NDS_TREE_NAME_LEN * sizeof( WCHAR );
  3552. pScb->NdsTreeName.Buffer = (PWCHAR)(pScb + 1);
  3553. pScb->NodeTypeCode = NW_NTC_SCB;
  3554. pScb->NodeByteSize = sizeof(SCB);
  3555. InitializeListHead( &pScb->ScbSpecificVcbQueue );
  3556. InitializeListHead( &pScb->IcbList );
  3557. //
  3558. // Remember UID of the file creator so we can find the username and
  3559. // password to use for this Scb when we need it.
  3560. //
  3561. pScb->UserUid = pIrpContext->Specific.Create.UserUid;
  3562. //
  3563. // Initialize the non-paged part of the SCB.
  3564. //
  3565. pNpScb->NodeTypeCode = NW_NTC_SCBNP;
  3566. pNpScb->NodeByteSize = sizeof(NONPAGED_SCB);
  3567. //
  3568. // Set the initial SCB reference count.
  3569. //
  3570. if ( UidServerName &&
  3571. UidServerName->Length ) {
  3572. Logon = FindUser( &pScb->UserUid, FALSE );
  3573. if (( Logon != NULL) &&
  3574. (RtlCompareUnicodeString( ServerName, &Logon->ServerName, TRUE ) == 0 )) {
  3575. pScb->PreferredServer = TRUE;
  3576. }
  3577. }
  3578. if ( pScb->PreferredServer ) {
  3579. pNpScb->Reference = 2;
  3580. } else {
  3581. pNpScb->Reference = 1;
  3582. }
  3583. //
  3584. // Finish linking the two parts of the Scb together.
  3585. //
  3586. pNpScb->pScb = pScb;
  3587. KeInitializeSpinLock( &pNpScb->NpScbSpinLock );
  3588. KeInitializeSpinLock( &pNpScb->NpScbInterLock );
  3589. InitializeListHead( &pNpScb->Requests );
  3590. RtlFillMemory( &pNpScb->LocalAddress, sizeof(IPXaddress), 0xff);
  3591. pNpScb->State = SCB_STATE_ATTACHING;
  3592. pNpScb->SequenceNo = 1;
  3593. Status = OpenScbSockets( pIrpContext, pNpScb );
  3594. if ( !NT_SUCCESS(Status) ) {
  3595. goto ExitWithCleanup;
  3596. }
  3597. Status = SetEventHandler (
  3598. pIrpContext,
  3599. &pNpScb->Server,
  3600. TDI_EVENT_RECEIVE_DATAGRAM,
  3601. &ServerDatagramHandler,
  3602. pNpScb );
  3603. if ( !NT_SUCCESS(Status) ) {
  3604. goto ExitWithCleanup;
  3605. }
  3606. Status = SetEventHandler (
  3607. pIrpContext,
  3608. &pNpScb->WatchDog,
  3609. TDI_EVENT_RECEIVE_DATAGRAM,
  3610. &WatchDogDatagramHandler,
  3611. pNpScb );
  3612. if ( !NT_SUCCESS( Status ) ) {
  3613. goto ExitWithCleanup;
  3614. }
  3615. Status = SetEventHandler (
  3616. pIrpContext,
  3617. &pNpScb->Send,
  3618. TDI_EVENT_RECEIVE_DATAGRAM,
  3619. &SendDatagramHandler,
  3620. pNpScb );
  3621. if ( !NT_SUCCESS( Status ) ) {
  3622. goto ExitWithCleanup;
  3623. }
  3624. Status = SetEventHandler (
  3625. pIrpContext,
  3626. &pNpScb->Echo,
  3627. TDI_EVENT_RECEIVE_DATAGRAM,
  3628. &ServerDatagramHandler,
  3629. pNpScb );
  3630. pNpScb->EchoCounter = 2;
  3631. if ( !NT_SUCCESS( Status ) ) {
  3632. goto ExitWithCleanup;
  3633. }
  3634. Status = SetEventHandler (
  3635. pIrpContext,
  3636. &pNpScb->Burst,
  3637. TDI_EVENT_RECEIVE_DATAGRAM,
  3638. &ServerDatagramHandler,
  3639. pNpScb );
  3640. if ( !NT_SUCCESS( Status ) ) {
  3641. goto ExitWithCleanup;
  3642. }
  3643. KeQuerySystemTime( &pNpScb->LastUsedTime );
  3644. //
  3645. // Set burst mode data.
  3646. //
  3647. pNpScb->BurstRequestNo = 0;
  3648. pNpScb->BurstSequenceNo = 0;
  3649. //
  3650. // Initialize the NDS object cache if it is enabled.
  3651. //
  3652. if( NdsObjectCacheSize != 0 ) {
  3653. //
  3654. // Determine the size of each entry. This must be eight byte aligned,
  3655. // since they will all be allocated in one big block.
  3656. //
  3657. // NOTE: The NDS_OBJECT_CACHE_ENTRY structure must already be aligned.
  3658. //
  3659. EntrySize = sizeof(NDS_OBJECT_CACHE_ENTRY) + (((sizeof(WCHAR) * MAX_NDS_NAME_CHARS) + 7) & ~7);
  3660. //
  3661. // Allocate the buffer for the cache. If memory cannot be allocated, it is not
  3662. // fatal. In this case, the object cache is just disabled for this SCB.
  3663. //
  3664. pScb->ObjectCacheBuffer = ALLOCATE_POOL( PagedPool,
  3665. (EntrySize * NdsObjectCacheSize) );
  3666. if( pScb->ObjectCacheBuffer != NULL ) {
  3667. //
  3668. // Initialize the object cache buffer. It is one big block of memory that
  3669. // is broken up into cache entries. Each entry is connected to the next
  3670. // via a list entry. This way the entries can be manipulated on the linked
  3671. // list, but there is only one allocation to deal with.
  3672. //
  3673. InitializeListHead( &(pScb->ObjectCacheList) );
  3674. RtlZeroMemory( pScb->ObjectCacheBuffer,
  3675. (EntrySize * NdsObjectCacheSize) );
  3676. for( i = 0; i < NdsObjectCacheSize; i++ ) {
  3677. ObjectEntry = (PNDS_OBJECT_CACHE_ENTRY)(((PBYTE)(pScb->ObjectCacheBuffer)) + (EntrySize * i));
  3678. InsertTailList( &(pScb->ObjectCacheList), &(ObjectEntry->Links) );
  3679. //
  3680. // The buffer for the object name string immediately follows the cache entry.
  3681. //
  3682. ObjectEntry->ObjectName.Buffer = (PWCHAR)(ObjectEntry + 1);
  3683. ObjectEntry->ObjectName.MaximumLength = MAX_NDS_NAME_CHARS * sizeof(WCHAR);
  3684. }
  3685. //
  3686. // This semaphore protects the object cache.
  3687. //
  3688. KeInitializeSemaphore( &(pScb->ObjectCacheLock),
  3689. 1,
  3690. 1 );
  3691. }
  3692. }
  3693. if ( ppScb ) {
  3694. *ppScb = pScb;
  3695. }
  3696. if (NwInitializePidTable( pNpScb )) {
  3697. return STATUS_SUCCESS;
  3698. } else {
  3699. Status = STATUS_INSUFFICIENT_RESOURCES;
  3700. }
  3701. ExitWithCleanup:
  3702. if ( pNpScb != NULL ) {
  3703. IPX_Close_Socket( &pNpScb->Server );
  3704. IPX_Close_Socket( &pNpScb->WatchDog );
  3705. IPX_Close_Socket( &pNpScb->Send );
  3706. IPX_Close_Socket( &pNpScb->Echo );
  3707. IPX_Close_Socket( &pNpScb->Burst );
  3708. FREE_POOL( pNpScb );
  3709. }
  3710. FREE_POOL(pScb);
  3711. return Status;
  3712. }
  3713. BOOLEAN
  3714. NwFindScb(
  3715. OUT PSCB *Scb,
  3716. PIRP_CONTEXT IrpContext,
  3717. PUNICODE_STRING UidServerName,
  3718. PUNICODE_STRING ServerName
  3719. )
  3720. /*++
  3721. Routine Description:
  3722. This routine returns a pointer to the SCB for the named server.
  3723. The name is looked up in the SCB table. If it is found, a
  3724. pointer to the SCB is returned. If none is found an SCB is
  3725. created and initialized.
  3726. This routine returns with the SCB referenced and the SCB
  3727. resources held.
  3728. Arguments:
  3729. Scb - Returns a pointer to the found / created SCB.
  3730. IrpContext - The IRP context pointers for the request in progress.
  3731. ServerName - The name of the server to find / create.
  3732. Return Value:
  3733. TRUE - An old SCB was found.
  3734. FALSE - A new SCB was created, or an attempt to create the SCB failed.
  3735. --*/
  3736. {
  3737. BOOLEAN RcbHeld;
  3738. PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
  3739. NTSTATUS Status;
  3740. PSCB pScb = NULL;
  3741. PNONPAGED_SCB pNpScb = NULL;
  3742. KIRQL OldIrql;
  3743. BOOLEAN Success, PreferredServerIsSet;
  3744. //
  3745. // Acquire the RCB exclusive to protect the prefix table.
  3746. // Then lookup the name of this server.
  3747. //
  3748. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  3749. RcbHeld = TRUE;
  3750. PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, UidServerName, 0 );
  3751. if ( PrefixEntry != NULL ) {
  3752. PSCB pScb = NULL;
  3753. PNONPAGED_SCB pNpScb = NULL;
  3754. //
  3755. // We found the SCB, increment the reference count and return
  3756. // success.
  3757. //
  3758. pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
  3759. pNpScb = pScb->pNpScb;
  3760. NwReferenceScb( pNpScb );
  3761. //
  3762. // Release the RCB.
  3763. //
  3764. NwReleaseRcb( &NwRcb );
  3765. DebugTrace(-1, Dbg, "NwFindScb -> %08lx\n", pScb );
  3766. *Scb = pScb;
  3767. return( TRUE );
  3768. }
  3769. //
  3770. // We do not have a connection to this server so create the new Scb if requested.
  3771. //
  3772. if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
  3773. NwReleaseRcb( &NwRcb );
  3774. *Scb = NULL;
  3775. return(FALSE);
  3776. }
  3777. try {
  3778. Status = NwAllocateAndInitScb( IrpContext,
  3779. UidServerName,
  3780. ServerName,
  3781. &pScb );
  3782. if ( !NT_SUCCESS( Status )) {
  3783. ExRaiseStatus( Status );
  3784. }
  3785. ASSERT( pScb != NULL );
  3786. pNpScb = pScb->pNpScb;
  3787. PreferredServerIsSet = pScb->PreferredServer;
  3788. //
  3789. //*******************************************************************
  3790. //
  3791. // From this point on we must not fail to create the Scb because the
  3792. // another thread will be able to reference the Scb causing severe
  3793. // problems in the finaly clause or in the other thread.
  3794. //
  3795. //*******************************************************************
  3796. //
  3797. //
  3798. // Insert this SCB in the global list if SCBs.
  3799. // If it is the default (i.e. preferred) server, stick it at the
  3800. // front of the queue so that SelectConnection() will select it
  3801. // for bindery queries.
  3802. //
  3803. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  3804. if ( PreferredServerIsSet ) {
  3805. InsertHeadList(&ScbQueue, &pNpScb->ScbLinks);
  3806. } else {
  3807. InsertTailList(&ScbQueue, &pNpScb->ScbLinks);
  3808. }
  3809. KeReleaseSpinLock(&ScbSpinLock, OldIrql);
  3810. //
  3811. // Insert the name of this server into the prefix table.
  3812. //
  3813. Success = RtlInsertUnicodePrefix(
  3814. &NwRcb.ServerNameTable,
  3815. &pScb->UidServerName,
  3816. &pScb->PrefixEntry );
  3817. #ifdef NWDBG
  3818. if ( !Success ) {
  3819. DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
  3820. DbgBreakPoint();
  3821. }
  3822. #endif
  3823. //
  3824. // The Scb is now in the prefix table. Any new requests for this
  3825. // connection can be added to the Scb->Requests queue while we
  3826. // attach to the server.
  3827. //
  3828. NwReleaseRcb( &NwRcb );
  3829. RcbHeld = FALSE;
  3830. //
  3831. // If we got an error we should have raised an exception.
  3832. //
  3833. ASSERT( NT_SUCCESS( Status ) );
  3834. } finally {
  3835. if ( !NT_SUCCESS( Status ) || AbnormalTermination() ) {
  3836. *Scb = NULL;
  3837. } else {
  3838. *Scb = pScb;
  3839. }
  3840. if (RcbHeld) {
  3841. NwReleaseRcb( &NwRcb );
  3842. }
  3843. }
  3844. return( FALSE );
  3845. }
  3846. NTSTATUS
  3847. QueryServersAddress(
  3848. PIRP_CONTEXT pIrpContext,
  3849. PNONPAGED_SCB pNearestScb,
  3850. PUNICODE_STRING pServerName,
  3851. IPXaddress *pServerAddress
  3852. )
  3853. {
  3854. NTSTATUS Status;
  3855. UNICODE_STRING NewServer;
  3856. USHORT CurrChar = 0;
  3857. BOOLEAN SeedServerRedirect = FALSE;
  3858. PNONPAGED_SCB pOrigNpScb;
  3859. PAGED_CODE();
  3860. if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
  3861. //
  3862. // Unmunge the server name if this is a
  3863. // supplemental credential connect.
  3864. //
  3865. UnmungeCredentialName( pServerName, &NewServer );
  3866. } else if ( EnableMultipleConnects ) {
  3867. //
  3868. // Strip server name trailer, if it exists. If there
  3869. // was no trailer, the length will end up being exactly
  3870. // the same as when we started.
  3871. //
  3872. Status = DuplicateUnicodeStringWithString(
  3873. &NewServer,
  3874. pServerName,
  3875. PagedPool
  3876. );
  3877. if ( NT_SUCCESS( Status ) ) {
  3878. return Status;
  3879. }
  3880. while ( (CurrChar < (NewServer.Length / sizeof(WCHAR))) &&
  3881. NewServer.Buffer[CurrChar] != ((WCHAR)L'#') ) {
  3882. CurrChar++;
  3883. }
  3884. NewServer.Length = CurrChar * sizeof(WCHAR);
  3885. } else {
  3886. //
  3887. // If we support seed server indirection, check the server
  3888. // name for a seed server. If there is a seed server specified,
  3889. // connect us to it.
  3890. //
  3891. if ( AllowSeedServerRedirection ) {
  3892. pOrigNpScb = pIrpContext->pNpScb;
  3893. NwDequeueIrpContext( pIrpContext, FALSE );
  3894. Status = IndirectToSeedServer( pIrpContext,
  3895. pServerName,
  3896. &NewServer );
  3897. if ( NT_SUCCESS( Status ) ) {
  3898. SeedServerRedirect = TRUE;
  3899. } else {
  3900. NwAppendToQueueAndWait( pIrpContext );
  3901. }
  3902. }
  3903. //
  3904. // If we didn't get redirected to a seed server, then
  3905. // just set up the server name like normal and try that.
  3906. //
  3907. if ( !SeedServerRedirect ) {
  3908. NewServer.Length = pServerName->Length;
  3909. NewServer.MaximumLength = pServerName->MaximumLength;
  3910. NewServer.Buffer = pServerName->Buffer;
  3911. }
  3912. }
  3913. //
  3914. // Query the bindery of the nearest server looking for
  3915. // the network address of the target server.
  3916. //
  3917. DebugTrace( +0, Dbg, "Query servers address\n", 0);
  3918. Status = ExchangeWithWait (
  3919. pIrpContext,
  3920. SynchronousResponseCallback,
  3921. "SwUbp",
  3922. NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
  3923. OT_FILESERVER,
  3924. &NewServer,
  3925. 1, // Segment number
  3926. NET_ADDRESS_PROPERTY );
  3927. DebugTrace( +0, Dbg, " %X\n", Status);
  3928. if ( NT_SUCCESS( Status ) ) {
  3929. //
  3930. // Save the new address.
  3931. //
  3932. Status = ParseResponse(
  3933. pIrpContext,
  3934. pIrpContext->rsp,
  3935. pIrpContext->ResponseLength,
  3936. "Nr",
  3937. pServerAddress, sizeof(TDI_ADDRESS_IPX) );
  3938. }
  3939. DebugTrace( +0, Dbg, " %X\n", Status);
  3940. //
  3941. // Map the server not found error to something sensible.
  3942. //
  3943. if (( Status == STATUS_NO_MORE_ENTRIES ) ||
  3944. ( Status == STATUS_VIRTUAL_CIRCUIT_CLOSED ) ||
  3945. ( Status == NwErrorToNtStatus(ERROR_UNEXP_NET_ERR))) {
  3946. Status = STATUS_BAD_NETWORK_PATH;
  3947. }
  3948. if ( SeedServerRedirect ) {
  3949. //
  3950. // Dequeue from the seed server and free the ref count.
  3951. // There should always be an original server, but just
  3952. // in case there's not, we check.
  3953. //
  3954. NwDequeueIrpContext( pIrpContext, FALSE );
  3955. NwDereferenceScb( pIrpContext->pNpScb );
  3956. ASSERT( pOrigNpScb != NULL );
  3957. if ( pOrigNpScb ) {
  3958. pIrpContext->pNpScb = pOrigNpScb;
  3959. pIrpContext->pScb = pOrigNpScb->pScb;
  3960. NwAppendToQueueAndWait( pIrpContext );
  3961. } else {
  3962. pIrpContext->pNpScb = NULL;
  3963. pIrpContext->pScb = NULL;
  3964. }
  3965. }
  3966. return( Status );
  3967. }
  3968. VOID
  3969. TreeConnectScb(
  3970. IN PSCB Scb
  3971. )
  3972. /*++
  3973. Routine Description:
  3974. This routine increments the tree connect count for a SCB.
  3975. Arguments:
  3976. Scb - A pointer to the SCB to connect to.
  3977. Return Value:
  3978. None.
  3979. --*/
  3980. {
  3981. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  3982. Scb->AttachCount++;
  3983. Scb->OpenFileCount++;
  3984. NwReferenceScb( Scb->pNpScb );
  3985. NwReleaseRcb( &NwRcb );
  3986. }
  3987. NTSTATUS
  3988. TreeDisconnectScb(
  3989. IN PIRP_CONTEXT IrpContext,
  3990. IN PSCB Scb
  3991. )
  3992. /*++
  3993. Routine Description:
  3994. This routine decrements the tree connect count for a SCB.
  3995. *** This routine must be called with the RCB resource held.
  3996. Arguments:
  3997. Scb - A pointer to the SCB to disconnect.
  3998. Return Value:
  3999. None.
  4000. --*/
  4001. {
  4002. NTSTATUS Status;
  4003. if ( Scb->AttachCount > 0 ) {
  4004. Scb->AttachCount--;
  4005. Scb->OpenFileCount--;
  4006. NwDereferenceScb( Scb->pNpScb );
  4007. Status = STATUS_SUCCESS;
  4008. if ( Scb->OpenFileCount == 0 ) {
  4009. //
  4010. // Logoff and disconnect from the server now.
  4011. // Hold on to the SCB lock.
  4012. // This prevents another thread from trying to access
  4013. // SCB will this thread is logging off.
  4014. //
  4015. NwLogoffAndDisconnect( IrpContext, Scb->pNpScb );
  4016. }
  4017. } else {
  4018. Status = STATUS_INVALID_HANDLE;
  4019. }
  4020. NwDequeueIrpContext( IrpContext, FALSE );
  4021. return( Status );
  4022. }
  4023. VOID
  4024. ReconnectScb(
  4025. IN PIRP_CONTEXT pIrpContext,
  4026. IN PSCB pScb
  4027. )
  4028. /*++
  4029. Routine Description:
  4030. This routine reconnects all the dir handles to a server
  4031. when reconnecting an SCB.
  4032. Arguments:
  4033. pScb - A pointer to the SCB that has just been reconnected.
  4034. Return Value:
  4035. None.
  4036. --*/
  4037. {
  4038. //
  4039. // If this is a reconnect, kill all old ICB and VCB handles
  4040. //
  4041. if ( pScb->VcbCount != 0 ) {
  4042. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  4043. //
  4044. // Invalid all ICBs
  4045. //
  4046. NwInvalidateAllHandlesForScb( pScb );
  4047. NwReleaseRcb( &NwRcb );
  4048. //
  4049. // Acquire new VCB handles for all VCBs.
  4050. //
  4051. NwReopenVcbHandlesForScb( pIrpContext, pScb );
  4052. }
  4053. }
  4054. NTSTATUS
  4055. IndirectToSeedServer(
  4056. PIRP_CONTEXT pIrpContext,
  4057. PUNICODE_STRING pServerName,
  4058. PUNICODE_STRING pNewServer
  4059. )
  4060. /*+++
  4061. Description: This function takes a server name. If that server name
  4062. is in the format target_server(seed_server), this routine will:
  4063. 1) put the target server in pNewServer
  4064. 2) lookup the seed server and queue the irp context to it
  4065. ---*/
  4066. {
  4067. NTSTATUS Status;
  4068. UNICODE_STRING SeedServer;
  4069. PWCHAR pwCurrent;
  4070. PSCB pScb;
  4071. RtlInitUnicodeString( &SeedServer, NULL );
  4072. RtlInitUnicodeString( pNewServer, NULL );
  4073. pwCurrent = pServerName->Buffer;
  4074. DebugTrace( 0, Dbg, "IndirectToSeedServer: %wZ\n", pServerName );
  4075. while ( pNewServer->Length <= pServerName->Length ) {
  4076. if ( *pwCurrent == L'(' ) {
  4077. pNewServer->Buffer = pServerName->Buffer;
  4078. pNewServer->MaximumLength = pNewServer->Length;
  4079. DebugTrace( 0, Dbg, "Target server is %wZ.\n", pNewServer );
  4080. SeedServer.Length = pServerName->Length -
  4081. pNewServer->Length;
  4082. if ( SeedServer.Length <= ( 2 * sizeof( WCHAR ) ) ) {
  4083. SeedServer.Length = 0;
  4084. } else {
  4085. SeedServer.Length -= ( 2 * sizeof( WCHAR ) );
  4086. SeedServer.Buffer = pwCurrent + 1;
  4087. SeedServer.MaximumLength = SeedServer.Length;
  4088. DebugTrace( 0, Dbg, "Seed server is %wZ.\n", &SeedServer );
  4089. }
  4090. break;
  4091. } else {
  4092. pNewServer->Length += sizeof( WCHAR );
  4093. pwCurrent++;
  4094. }
  4095. }
  4096. if ( SeedServer.Length == 0 ) {
  4097. DebugTrace( 0, Dbg, "IndirectToSeedServer failed to decode nested name.\n", 0 );
  4098. return STATUS_UNSUCCESSFUL;
  4099. }
  4100. //
  4101. // Now do something about it.
  4102. //
  4103. Status = CreateScb( &pScb,
  4104. pIrpContext,
  4105. &SeedServer,
  4106. NULL,
  4107. NULL,
  4108. NULL,
  4109. TRUE,
  4110. FALSE );
  4111. if ( !NT_SUCCESS( Status ) ) {
  4112. DebugTrace( 0, Dbg, "Couldn't contact seed server.\n", 0 );
  4113. return STATUS_UNSUCCESSFUL;
  4114. }
  4115. NwAppendToQueueAndWait( pIrpContext );
  4116. return STATUS_SUCCESS;
  4117. }