/*++ Copyright (c) 1991-1993 Microsoft Corporation Module Name: device.c Abstract: This module contains the support routines for the APIs that call into the NetWare redirector Author: Rita Wong (ritaw) 20-Feb-1991 Colin Watson (colinw) 30-Dec-1992 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include #define NW_LINKAGE_REGISTRY_PATH L"NWCWorkstation\\Linkage" #define NW_BIND_VALUENAME L"Bind" #define TWO_KB 2048 #define EIGHT_KB 8192 #define EXTRA_BYTES 256 #define TREECHAR L'*' #define BUFFSIZE 1024 //-------------------------------------------------------------------// // // // Local Function Prototypes // // // //-------------------------------------------------------------------// STATIC NTSTATUS BindToEachTransport( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); DWORD NwBindTransport( IN LPWSTR TransportName, IN DWORD QualityOfService ); DWORD GetConnectedBinderyServers( OUT LPNW_ENUM_CONTEXT ContextHandle ); DWORD GetTreeEntriesFromBindery( OUT LPNW_ENUM_CONTEXT ContextHandle ); DWORD NwGetConnectionStatus( IN LPWSTR pszServerName, IN OUT PDWORD_PTR ResumeKey, OUT LPBYTE *Buffer, OUT PDWORD EntriesRead ); VOID GetLuid( IN OUT PLUID plogonid ); VOID GetNearestDirServer( IN LPWSTR TreeName, OUT LPDWORD lpdwReplicaAddressSize, OUT LPBYTE lpReplicaAddress ); VOID GetPreferredServerAddress( IN LPWSTR PreferredServerName, OUT LPDWORD lpdwReplicaAddressSize, OUT LPBYTE lpReplicaAddress ); BOOL NwpCompareTreeNames( LPWSTR lpServiceInstanceName, LPWSTR lpTreeName ); //-------------------------------------------------------------------// // // // Global variables // // // //-------------------------------------------------------------------// // // Handle to the Redirector FSD // STATIC HANDLE RedirDeviceHandle = NULL; // // Redirector name in NT string format // STATIC UNICODE_STRING RedirDeviceName; extern BOOL NwLUIDDeviceMapsEnabled; DWORD NwInitializeRedirector( VOID ) /*++ Routine Description: This routine initializes the NetWare redirector FSD. Arguments: None. Return Value: NO_ERROR or reason for failure. --*/ { DWORD error; NWR_REQUEST_PACKET Rrp; // // Initialize global handles // RedirDeviceHandle = NULL; // // Initialize the global NT-style redirector device name string. // RtlInitUnicodeString(&RedirDeviceName, DD_NWFS_DEVICE_NAME_U); // // Load driver // error = NwLoadOrUnloadDriver(TRUE); if (error != NO_ERROR && error != ERROR_SERVICE_ALREADY_RUNNING) { return error; } if ((error = NwOpenRedirector()) != NO_ERROR) { // // Unload the redirector driver // (void) NwLoadOrUnloadDriver(FALSE); return error; } // // Send the start FSCTL to the redirector // Rrp.Version = REQUEST_PACKET_VERSION; return NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_START, &Rrp, sizeof(NWR_REQUEST_PACKET), NULL, 0, NULL ); } DWORD NwOpenRedirector( VOID ) /*++ Routine Description: This routine opens the NT NetWare redirector FSD. Arguments: None. Return Value: NO_ERROR or reason for failure. --*/ { return RtlNtStatusToDosError( NwOpenHandle(&RedirDeviceName, FALSE, &RedirDeviceHandle) ); } DWORD NwShutdownRedirector( VOID ) /*++ Routine Description: This routine stops the NetWare Redirector FSD and unloads it if possible. Arguments: None. Return Value: NO_ERROR or ERROR_REDIRECTOR_HAS_OPEN_HANDLES --*/ { NWR_REQUEST_PACKET Rrp; DWORD error; Rrp.Version = REQUEST_PACKET_VERSION; error = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_STOP, &Rrp, sizeof(NWR_REQUEST_PACKET), NULL, 0, NULL ); (void) NtClose(RedirDeviceHandle); RedirDeviceHandle = NULL; if (error != ERROR_REDIRECTOR_HAS_OPEN_HANDLES) { // // Unload the redirector only if all its open handles are closed. // (void) NwLoadOrUnloadDriver(FALSE); } return error; } DWORD NwLoadOrUnloadDriver( BOOL Load ) /*++ Routine Description: This routine loads or unloads the NetWare redirector driver. Arguments: Load - Supplies the flag which if TRUE load the driver; otherwise unloads the driver. Return Value: NO_ERROR or reason for failure. --*/ { LPWSTR DriverRegistryName; UNICODE_STRING DriverRegistryString; NTSTATUS ntstatus; BOOLEAN WasEnabled; DriverRegistryName = (LPWSTR) LocalAlloc( LMEM_FIXED, (UINT) (sizeof(SERVICE_REGISTRY_KEY) + (wcslen(NW_DRIVER_NAME) * sizeof(WCHAR))) ); if (DriverRegistryName == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } ntstatus = RtlAdjustPrivilege( SE_LOAD_DRIVER_PRIVILEGE, TRUE, FALSE, &WasEnabled ); if (! NT_SUCCESS(ntstatus)) { (void) LocalFree(DriverRegistryName); return RtlNtStatusToDosError(ntstatus); } wcscpy(DriverRegistryName, SERVICE_REGISTRY_KEY); wcscat(DriverRegistryName, NW_DRIVER_NAME); RtlInitUnicodeString(&DriverRegistryString, DriverRegistryName); if (Load) { ntstatus = NtLoadDriver(&DriverRegistryString); } else { ntstatus = NtUnloadDriver(&DriverRegistryString); } (void) RtlAdjustPrivilege( SE_LOAD_DRIVER_PRIVILEGE, WasEnabled, FALSE, &WasEnabled ); (void) LocalFree(DriverRegistryName); if (Load) { if (ntstatus != STATUS_SUCCESS && ntstatus != STATUS_IMAGE_ALREADY_LOADED) { LPWSTR SubString[1]; KdPrint(("NWWORKSTATION: NtLoadDriver returned %08lx\n", ntstatus)); SubString[0] = NW_DRIVER_NAME; NwLogEvent( EVENT_NWWKSTA_CANT_CREATE_REDIRECTOR, 1, SubString, ntstatus ); } } if (ntstatus == STATUS_OBJECT_NAME_NOT_FOUND) { return ERROR_FILE_NOT_FOUND; } return NwMapStatus(ntstatus); } DWORD NwRedirFsControl( IN HANDLE FileHandle, IN ULONG RedirControlCode, IN PNWR_REQUEST_PACKET Rrp, IN ULONG RrpLength, IN PVOID SecondBuffer OPTIONAL, IN ULONG SecondBufferLength, OUT PULONG Information OPTIONAL ) /*++ Routine Description: Arguments: FileHandle - Supplies a handle to the file or device on which the service is being performed. RedirControlCode - Supplies the NtFsControlFile function code given to the redirector. Rrp - Supplies the redirector request packet. RrpLength - Supplies the length of the redirector request packet. SecondBuffer - Supplies the second buffer in call to NtFsControlFile. SecondBufferLength - Supplies the length of the second buffer. Information - Returns the information field of the I/O status block. Return Value: NO_ERROR or reason for failure. --*/ { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; // // Send the request to the Redirector FSD. // ntstatus = NtFsControlFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, RedirControlCode, (PVOID) Rrp, RrpLength, SecondBuffer, SecondBufferLength ); if (ntstatus == STATUS_SUCCESS) { ntstatus = IoStatusBlock.Status; } if (ARGUMENT_PRESENT(Information)) { *Information = (ULONG) IoStatusBlock.Information; } #if DBG if (ntstatus != STATUS_SUCCESS) { IF_DEBUG(DEVICE) { KdPrint(("NWWORKSTATION: fsctl to redir returns %08lx\n", ntstatus)); } } #endif return NwMapStatus(ntstatus); } DWORD NwBindToTransports( VOID ) /*++ Routine Description: This routine binds to every transport specified under the linkage key of the NetWare Workstation service. Arguments: None. Return Value: NET_API_STATUS - success/failure of the operation. --*/ { NTSTATUS ntstatus; PRTL_QUERY_REGISTRY_TABLE QueryTable; ULONG NumberOfBindings = 0; // // Ask the RTL to call us back for each subvalue in the MULTI_SZ // value \NWCWorkstation\Linkage\Bind. // if ((QueryTable = (PVOID) LocalAlloc( LMEM_ZEROINIT, sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 )) == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } QueryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) BindToEachTransport; QueryTable[0].Flags = 0; QueryTable[0].Name = NW_BIND_VALUENAME; QueryTable[0].EntryContext = NULL; QueryTable[0].DefaultType = REG_NONE; QueryTable[0].DefaultData = NULL; QueryTable[0].DefaultLength = 0; QueryTable[1].QueryRoutine = NULL; QueryTable[1].Flags = 0; QueryTable[1].Name = NULL; ntstatus = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES, NW_LINKAGE_REGISTRY_PATH, QueryTable, &NumberOfBindings, NULL ); (void) LocalFree((HLOCAL) QueryTable); // // If failed to bind to any transports, the workstation will // not start. // if (! NT_SUCCESS(ntstatus)) { #if DBG IF_DEBUG(INIT) { KdPrint(("NwBindToTransports: RtlQueryRegistryValues failed: " "%lx\n", ntstatus)); } #endif return RtlNtStatusToDosError(ntstatus); } if (NumberOfBindings == 0) { #if 0 // // tommye - MS 24187 / MCS 255 // // // We don't want to log an event unnecessarily and panic the user that // G/CSNW could not bind. This could have been caused by the user unbinding // G/CSNW and rebooting. // NwLogEvent( EVENT_NWWKSTA_NO_TRANSPORTS, 0, NULL, NO_ERROR ); #endif KdPrint(("NWWORKSTATION: NwBindToTransports: could not bind " "to any transport\n")); return ERROR_INVALID_PARAMETER; } return NO_ERROR; } STATIC NTSTATUS BindToEachTransport( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { DWORD error; LPDWORD NumberOfBindings = Context; LPWSTR SubStrings[2]; static DWORD QualityOfService = 65536; UNREFERENCED_PARAMETER(ValueName); UNREFERENCED_PARAMETER(ValueLength); UNREFERENCED_PARAMETER(EntryContext); // // The value type must be REG_SZ (translated from REG_MULTI_SZ by // the RTL). // if (ValueType != REG_SZ) { SubStrings[0] = ValueName; SubStrings[1] = NW_LINKAGE_REGISTRY_PATH; NwLogEvent( EVENT_NWWKSTA_INVALID_REGISTRY_VALUE, 2, SubStrings, NO_ERROR ); KdPrint(("NWWORKSTATION: Skipping invalid value %ws\n", ValueName)); return STATUS_SUCCESS; } // // The value data is the name of the transport device object. // // // Bind to the transport. // #if DBG IF_DEBUG(INIT) { KdPrint(("NWWORKSTATION: Binding to transport %ws with QOS %lu\n", ValueData, QualityOfService)); } #endif error = NwBindTransport(ValueData, QualityOfService--); if (error != NO_ERROR) { // // If failed to bind to one transport, don't fail starting yet. // Try other transports. // SubStrings[0] = ValueData; NwLogEvent( EVENT_NWWKSTA_CANT_BIND_TO_TRANSPORT, 1, SubStrings, error ); } else { (*NumberOfBindings)++; } return STATUS_SUCCESS; } DWORD NwBindTransport( IN LPWSTR TransportName, IN DWORD QualityOfService ) /*++ Routine Description: This function binds the specified transport to the redirector and the datagram receiver. NOTE: The transport name length pass to the redirector and datagram receiver is the number of bytes. Arguments: TransportName - Supplies the name of the transport to bind to. QualityOfService - Supplies a value which specifies the search order of the transport with respect to other transports. The highest value is searched first. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; DWORD RequestPacketSize; DWORD TransportNameSize = wcslen(TransportName) * sizeof(WCHAR); PNWR_REQUEST_PACKET Rrp; // // Size of request packet buffer // RequestPacketSize = TransportNameSize + sizeof(NWR_REQUEST_PACKET); // // Allocate memory for redirector/datagram receiver request packet // if ((Rrp = (PVOID) LocalAlloc(LMEM_ZEROINIT, (UINT) RequestPacketSize)) == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } // // Get redirector to bind to transport // Rrp->Version = REQUEST_PACKET_VERSION; Rrp->Parameters.Bind.QualityOfService = QualityOfService; Rrp->Parameters.Bind.TransportNameLength = TransportNameSize; wcscpy((LPWSTR) Rrp->Parameters.Bind.TransportName, TransportName); if ((status = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_BIND_TO_TRANSPORT, Rrp, RequestPacketSize, NULL, 0, NULL )) != NO_ERROR) { KdPrint(("NWWORKSTATION: NwBindTransport fsctl to bind to transport %ws failed\n", TransportName)); } (void) LocalFree((HLOCAL) Rrp); return status; } DWORD NwGetCallerLuid ( IN OUT PLUID pLuid ) /*++ Routine Description: Retrieves the caller's LUID from the effective access_token The effective access_token will be the thread's token if impersonating, else the process' token Arguments: pLuid [IN OUT] - pointer to a buffer to hold the LUID Return Value: STATUS_SUCCESS - operations successful, did not encounter any errors STATUS_INVALID_PARAMETER - pLuid is NULL STATUS_NO_TOKEN - could not find a token for the user appropriate NTSTATUS code - an unexpected error encountered --*/ { TOKEN_STATISTICS TokenStats; HANDLE hToken = NULL; DWORD dwLength = 0; NTSTATUS Status; ULONG DosError; if( (pLuid == NULL) || (sizeof(*pLuid) != sizeof(LUID)) ) { return( STATUS_INVALID_PARAMETER ); } // // Get the access token // Try to get the impersonation token, else the primary token // Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_READ, TRUE, &hToken ); if( Status == STATUS_NO_TOKEN ) { Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_READ, &hToken ); } if( NT_SUCCESS(Status) ) { // // Query the LUID for the user. // Status = NtQueryInformationToken( hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &dwLength ); if( NT_SUCCESS(Status) ) { RtlCopyLuid( pLuid, &(TokenStats.AuthenticationId) ); } } if( hToken != NULL ) { NtClose( hToken ); } DosError = RtlNtStatusToDosError(Status); return( (DWORD)DosError ); } DWORD NwCreateTreeConnectName( IN LPWSTR UncName, IN LPWSTR LocalName OPTIONAL, OUT PUNICODE_STRING TreeConnectStr ) /*++ Routine Description: This function replaces \\ with \Device\NwRdr\LocalName:\ in the UncName to form the NT-style tree connection name. LocalName:\ is part of the tree connection name only if LocalName is specified. A buffer is allocated by this function and returned as the output string. Arguments: UncName - Supplies the UNC name of the shared resource. LocalName - Supplies the local device name for the redirection. TreeConnectStr - Returns a string with a newly allocated buffer that contains the NT-style tree connection name. Return Value: NO_ERROR - the operation was successful. ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer. --*/ { WCHAR LUIDBuffer[NW_MAX_LOGON_ID_LEN]; DWORD UncNameLength = wcslen(UncName); LUID CallerLuid; BOOLEAN UseLUID; //UseLUID = (ARGUMENT_PRESENT(LocalName) && NwLUIDDeviceMapsEnabled); // // Temporary disable passing the LUID until LUID support is added in // the nwrdr.sys for parsing the device name // UseLUID = FALSE; // // Initialize tree connect string maximum length to hold // If LUID DosDevices enabled && LocalName Specified, // \Device\NwRdr\LocalName:XXXXXXXXxxxxxxxx\Server\Volume\Path // XXXXXXXX - LUID.HighPart // xxxxxxxx - LUID.LowPart // else // \Device\NwRdr\LocalName:\Server\Volume\Path // if( UseLUID ) { DWORD DosError; DosError = NwGetCallerLuid(&CallerLuid); if( DosError != NO_ERROR) { return DosError; } } TreeConnectStr->MaximumLength = RedirDeviceName.Length + sizeof(WCHAR) + // For '\' (ARGUMENT_PRESENT(LocalName) ? (wcslen(LocalName) * sizeof(WCHAR)) : 0) + (UseLUID ? NW_MAX_LOGON_ID_LEN * sizeof(WCHAR): 0) + (USHORT) (UncNameLength * sizeof(WCHAR)); // Includes '\' and // term char if ((TreeConnectStr->Buffer = (PWSTR) LocalAlloc( LMEM_ZEROINIT, (UINT) TreeConnectStr->MaximumLength )) == NULL) { KdPrint(("NWWORKSTATION: NwCreateTreeConnectName LocalAlloc failed %lu\n", GetLastError())); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy \Device\NwRdr // RtlCopyUnicodeString(TreeConnectStr, &RedirDeviceName); // // Concatenate \LocalName: // if (ARGUMENT_PRESENT(LocalName)) { wcscat(TreeConnectStr->Buffer, L"\\"); TreeConnectStr->Length += sizeof(WCHAR); wcscat(TreeConnectStr->Buffer, LocalName); TreeConnectStr->Length += (USHORT) (wcslen(LocalName) * sizeof(WCHAR)); // // Concatenate the caller's LUID // if( UseLUID ) { _snwprintf( LUIDBuffer, sizeof(LUIDBuffer)/sizeof(WCHAR), L"%08x%08x", CallerLuid.HighPart, CallerLuid.LowPart ); wcscat(TreeConnectStr->Buffer, LUIDBuffer); TreeConnectStr->Length += (USHORT) (wcslen(LUIDBuffer) * sizeof(WCHAR)); } } // // Concatenate \Server\Volume\Path // wcscat(TreeConnectStr->Buffer, &UncName[1]); TreeConnectStr->Length += (USHORT) ((UncNameLength - 1) * sizeof(WCHAR)); #if DBG IF_DEBUG(CONNECT) { KdPrint(("NWWORKSTATION: NwCreateTreeConnectName %ws, maxlength %u, length %u\n", TreeConnectStr->Buffer, TreeConnectStr->MaximumLength, TreeConnectStr->Length)); } #endif return NO_ERROR; } DWORD NwOpenCreateConnection( IN PUNICODE_STRING TreeConnectionName, IN LPWSTR UserName OPTIONAL, IN LPWSTR Password OPTIONAL, IN LPWSTR UncName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN ULONG ConnectionType, OUT PHANDLE TreeConnectionHandle, OUT PULONG_PTR Information OPTIONAL ) /*++ Routine Description: This function asks the redirector to either open an existing tree connection (CreateDisposition == FILE_OPEN), or create a new tree connection if one does not exist (CreateDisposition == FILE_CREATE). The password and user name passed to the redirector via the EA buffer in the NtCreateFile call. The EA buffer is NULL if neither password or user name is specified. The redirector expects the EA descriptor strings to be in ANSI but the password and username themselves are in Unicode. Arguments: TreeConnectionName - Supplies the name of the tree connection in NT-style file name format: \Device\NwRdr\Server\Volume\Directory UserName - Supplies the user name to create the tree connection with. Password - Supplies the password to create the tree connection with. DesiredAccess - Supplies the access need on the connection handle. CreateDisposition - Supplies the create disposition value to either open or create the tree connection. CreateOptions - Supplies the options used when creating or opening the tree connection. ConnectionType - Supplies the type of the connection (DISK, PRINT, or ANY). TreeConnectionHandle - Returns the handle to the tree connection created/opened by the redirector. Information - Returns the information field of the I/O status block. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; NTSTATUS ntstatus; OBJECT_ATTRIBUTES UncNameAttributes; IO_STATUS_BLOCK IoStatusBlock; PFILE_FULL_EA_INFORMATION EaBuffer = NULL; PFILE_FULL_EA_INFORMATION Ea; ULONG EaBufferSize = 0; UCHAR EaNamePasswordSize = (UCHAR) (ROUND_UP_COUNT( strlen(EA_NAME_PASSWORD) + sizeof(CHAR), ALIGN_WCHAR ) - sizeof(CHAR)); UCHAR EaNameUserNameSize = (UCHAR) (ROUND_UP_COUNT( strlen(EA_NAME_USERNAME) + sizeof(CHAR), ALIGN_WCHAR ) - sizeof(CHAR)); UCHAR EaNameTypeSize = (UCHAR) (ROUND_UP_COUNT( strlen(EA_NAME_TYPE) + sizeof(CHAR), ALIGN_DWORD ) - sizeof(CHAR)); USHORT PasswordSize = 0; USHORT UserNameSize = 0; USHORT TypeSize = sizeof(ULONG); InitializeObjectAttributes( &UncNameAttributes, TreeConnectionName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Calculate the number of bytes needed for the EA buffer to put the // password or user name. // if (ARGUMENT_PRESENT(Password)) { #if DBG IF_DEBUG(CONNECT) { KdPrint(("NWWORKSTATION: NwOpenCreateConnection password is %ws\n", Password)); } #endif PasswordSize = (USHORT) (wcslen(Password) * sizeof(WCHAR)); EaBufferSize = ROUND_UP_COUNT( FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + EaNamePasswordSize + sizeof(CHAR) + PasswordSize, ALIGN_DWORD ); } if (ARGUMENT_PRESENT(UserName)) { #if DBG IF_DEBUG(CONNECT) { KdPrint(("NWWORKSTATION: NwOpenCreateConnection username is %ws\n", UserName)); } #endif UserNameSize = (USHORT) (wcslen(UserName) * sizeof(WCHAR)); EaBufferSize += ROUND_UP_COUNT( FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + EaNameUserNameSize + sizeof(CHAR) + UserNameSize, ALIGN_DWORD ); } EaBufferSize += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + EaNameTypeSize + sizeof(CHAR) + TypeSize; // // Allocate the EA buffer // if ((EaBuffer = (PFILE_FULL_EA_INFORMATION) LocalAlloc( LMEM_ZEROINIT, (UINT) EaBufferSize )) == NULL) { status = GetLastError(); goto FreeMemory; } Ea = EaBuffer; if (ARGUMENT_PRESENT(Password)) { // // Copy the EA name into EA buffer. EA name length does not // include the zero terminator. // strcpy((LPSTR) Ea->EaName, EA_NAME_PASSWORD); Ea->EaNameLength = EaNamePasswordSize; // // Copy the EA value into EA buffer. EA value length does not // include the zero terminator. // wcscpy( (LPWSTR) &(Ea->EaName[EaNamePasswordSize + sizeof(CHAR)]), Password ); Ea->EaValueLength = PasswordSize; Ea->NextEntryOffset = ROUND_UP_COUNT( FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + EaNamePasswordSize + sizeof(CHAR) + PasswordSize, ALIGN_DWORD ); Ea->Flags = 0; (ULONG_PTR) Ea += Ea->NextEntryOffset; } if (ARGUMENT_PRESENT(UserName)) { // // Copy the EA name into EA buffer. EA name length does not // include the zero terminator. // strcpy((LPSTR) Ea->EaName, EA_NAME_USERNAME); Ea->EaNameLength = EaNameUserNameSize; // // Copy the EA value into EA buffer. EA value length does not // include the zero terminator. // wcscpy( (LPWSTR) &(Ea->EaName[EaNameUserNameSize + sizeof(CHAR)]), UserName ); Ea->EaValueLength = UserNameSize; Ea->NextEntryOffset = ROUND_UP_COUNT( FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + EaNameUserNameSize + sizeof(CHAR) + UserNameSize, ALIGN_DWORD ); Ea->Flags = 0; (ULONG_PTR) Ea += Ea->NextEntryOffset; } // // Copy the connection type name into EA buffer. EA name length // does not include the zero terminator. // strcpy((LPSTR) Ea->EaName, EA_NAME_TYPE); Ea->EaNameLength = EaNameTypeSize; *((PULONG) &(Ea->EaName[EaNameTypeSize + sizeof(CHAR)])) = ConnectionType; Ea->EaValueLength = TypeSize; // // Terminate the EA. // Ea->NextEntryOffset = 0; Ea->Flags = 0; // // Create or open a tree connection // ntstatus = NtCreateFile( TreeConnectionHandle, DesiredAccess, &UncNameAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_VALID_FLAGS, CreateDisposition, CreateOptions, (PVOID) EaBuffer, EaBufferSize ); if (ntstatus == NWRDR_PASSWORD_HAS_EXPIRED) { // // wait till other thread is not using the popup data struct. // if we timeout, then we just lose the popup. // switch (WaitForSingleObject(NwPopupDoneEvent, 3000)) { case WAIT_OBJECT_0: { LPWSTR lpServerStart, lpServerEnd ; WCHAR UserNameW[NW_MAX_USERNAME_LEN+1] ; DWORD dwUserNameWSize = sizeof(UserNameW)/sizeof(UserNameW[0]) ; DWORD dwServerLength, dwGraceLogins ; DWORD dwMessageId = NW_PASSWORD_HAS_EXPIRED ; // // get the current username // if (UserName) { wcscpy(UserNameW, UserName) ; } else { if (!GetUserNameW(UserNameW, &dwUserNameWSize)) { SetEvent(NwPopupDoneEvent) ; break ; } } // // allocate string and fill in the username // if (!(PopupData.InsertStrings[0] = (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(WCHAR) * (wcslen(UserNameW)+1)))) { SetEvent(NwPopupDoneEvent) ; break ; } wcscpy(PopupData.InsertStrings[0], UserNameW) ; // // find the server name from unc name // lpServerStart = (*UncName == L'\\') ? UncName+2 : UncName ; lpServerEnd = wcschr(lpServerStart,L'\\') ; dwServerLength = lpServerEnd ? (DWORD) (lpServerEnd-lpServerStart) : wcslen(lpServerStart) ; // // allocate string and fill in the server insert string // if (!(PopupData.InsertStrings[1] = (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(WCHAR) * (dwServerLength+1)))) { (void) LocalFree((HLOCAL) PopupData.InsertStrings[0]); SetEvent(NwPopupDoneEvent) ; break ; } wcsncpy(PopupData.InsertStrings[1], lpServerStart, dwServerLength) ; // // now call the NCP. if an error occurs while getting // the grace login count, dont use it. // if (NwGetGraceLoginCount( PopupData.InsertStrings[1], UserNameW, &dwGraceLogins) != NO_ERROR) { dwMessageId = NW_PASSWORD_HAS_EXPIRED1 ; dwGraceLogins = 0 ; } // // stick the number of grace logins in second insert string. // if (!(PopupData.InsertStrings[2] = (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(WCHAR) * 16))) { (void) LocalFree((HLOCAL) PopupData.InsertStrings[0]); (void) LocalFree((HLOCAL) PopupData.InsertStrings[1]); SetEvent(NwPopupDoneEvent) ; break ; } wsprintfW(PopupData.InsertStrings[2], L"%d", dwGraceLogins); PopupData.InsertCount = 3 ; PopupData.MessageId = dwMessageId ; //--Mutl-user change ---- GetLuid( &PopupData.LogonId ); // // all done at last, trigger the other thread do the popup // SetEvent(NwPopupEvent) ; break ; } default: break ; // dont bother if we cannot } } if (NT_SUCCESS(ntstatus)) { ntstatus = IoStatusBlock.Status; } if (ntstatus == NWRDR_PASSWORD_HAS_EXPIRED) { ntstatus = STATUS_SUCCESS ; } if (ARGUMENT_PRESENT(Information)) { *Information = IoStatusBlock.Information; } #if DBG IF_DEBUG(CONNECT) { KdPrint(("NWWORKSTATION: NtCreateFile returns %lx\n", ntstatus)); } #endif status = NwMapStatus(ntstatus); FreeMemory: if (EaBuffer != NULL) { RtlZeroMemory( EaBuffer, EaBufferSize ); // Clear the password (void) LocalFree((HLOCAL) EaBuffer); } return status; } DWORD NwNukeConnection( IN HANDLE TreeConnection, IN DWORD UseForce ) /*++ Routine Description: This function asks the redirector to delete an existing tree connection. Arguments: TreeConnection - Supplies the handle to an existing tree connection. UseForce - Supplies the force flag to delete the tree connection. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; NWR_REQUEST_PACKET Rrp; // Redirector request packet // // Tell the redirector to delete the tree connection // Rrp.Version = REQUEST_PACKET_VERSION; Rrp.Parameters.DeleteConn.UseForce = (BOOLEAN) UseForce; status = NwRedirFsControl( TreeConnection, FSCTL_NWR_DELETE_CONNECTION, &Rrp, sizeof(NWR_REQUEST_PACKET), NULL, 0, NULL ); return status; } DWORD NwGetServerResource( IN LPWSTR LocalName, IN DWORD LocalNameLength, OUT LPWSTR RemoteName, IN DWORD RemoteNameLen, OUT LPDWORD CharsRequired ) /*++ Routine Description: This function Arguments: Return Value: --*/ { DWORD status = NO_ERROR; BYTE Buffer[sizeof(NWR_REQUEST_PACKET) + 2 * sizeof(WCHAR)]; PNWR_REQUEST_PACKET Rrp = (PNWR_REQUEST_PACKET) Buffer; // // local device name should not be longer than 4 characters e.g. LPTx, X: // if ( LocalNameLength > 4 ) return ERROR_INVALID_PARAMETER; Rrp->Version = REQUEST_PACKET_VERSION; wcsncpy(Rrp->Parameters.GetConn.DeviceName, LocalName, LocalNameLength); Rrp->Parameters.GetConn.DeviceNameLength = LocalNameLength * sizeof(WCHAR); status = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_GET_CONNECTION, Rrp, sizeof(NWR_REQUEST_PACKET) + Rrp->Parameters.GetConn.DeviceNameLength, RemoteName, RemoteNameLen * sizeof(WCHAR), NULL ); if (status == ERROR_INSUFFICIENT_BUFFER) { *CharsRequired = Rrp->Parameters.GetConn.BytesNeeded / sizeof(WCHAR); } else if (status == ERROR_FILE_NOT_FOUND) { // // Redirector could not find the specified LocalName // status = WN_NOT_CONNECTED; } return status; } DWORD NwEnumerateConnections( IN OUT PDWORD_PTR ResumeId, IN DWORD_PTR EntriesRequested, IN LPBYTE Buffer, IN DWORD BufferSize, OUT LPDWORD BytesNeeded, OUT LPDWORD EntriesRead, IN DWORD ConnectionType, IN PLUID LogonId ) /*++ Routine Description: This function asks the redirector to enumerate all existing connections. Arguments: ResumeId - On input, supplies the resume ID of the next entry to begin the enumeration. This ID is an integer value that is either the smaller or the same value as the ID of the next entry to return. On output, this ID indicates the next entry to start resuming from for the subsequent call. EntriesRequested - Supplies the number of entries to return. If this value is -1, return all available entries. Buffer - Receives the entries we are listing. BufferSize - Supplies the size of the output buffer. BytesNeeded - Receives the number of bytes required to get the first entry. This value is returned iff ERROR_MORE_DATA is the return code, and Buffer is too small to even fit one entry. EntriesRead - Receives the number of entries returned in Buffer. This value is only returned iff NO_ERROR is the return code. NO_ERROR is returned as long as at least one entry was written into Buffer but does not necessarily mean that it's the number of EntriesRequested. ConnectionType - The type of connected resource wanted ( DISK, PRINT, ...) Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; NWR_REQUEST_PACKET Rrp; // Redirector request packet // // Tell the redirector to enumerate all connections. // Rrp.Version = REQUEST_PACKET_VERSION; Rrp.Parameters.EnumConn.ResumeKey = *ResumeId; Rrp.Parameters.EnumConn.EntriesRequested = (ULONG) EntriesRequested; Rrp.Parameters.EnumConn.ConnectionType = ConnectionType; //Multi-user change if (LogonId != NULL ) { Rrp.Parameters.EnumConn.Uid = *LogonId; } // // This is good to do, the the fix below is also needed. // Rrp.Parameters.EnumConn.EntriesReturned = 0; status = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_ENUMERATE_CONNECTIONS, &Rrp, sizeof(NWR_REQUEST_PACKET), Buffer, // User output buffer BufferSize, NULL ); *EntriesRead = Rrp.Parameters.EnumConn.EntriesReturned; // // Strange bug on shutdown // WinLogon was clearing connections after the shutdown // if (status == ERROR_INVALID_HANDLE ) { KdPrint(("NWWORKSTATION: NwEnumerateConnections Invalid Handle!\n")); *EntriesRead = 0; } else if (status == WN_MORE_DATA) { *BytesNeeded = Rrp.Parameters.EnumConn.BytesNeeded; // // NP specs expect WN_SUCCESS in this case. // if (*EntriesRead) status = WN_SUCCESS ; } *ResumeId = Rrp.Parameters.EnumConn.ResumeKey; return status; } DWORD NwGetNextServerEntry( IN HANDLE PreferredServer, IN OUT LPDWORD LastObjectId, OUT LPSTR ServerName ) /*++ Routine Description: This function uses an opened handle to the preferred server to scan it bindery for all file server objects. Arguments: PreferredServer - Supplies the handle to the preferred server on which to scan the bindery. LastObjectId - On input, supplies the object ID to the last file server object returned, which is the resume handle to get the next file server object. On output, receives the object ID of the file server object returned. ServerName - Receives the name of the returned file server object. Return Value: NO_ERROR - Successfully gotten a file server name. WN_NO_MORE_ENTRIES - No other file server object past the one specified by LastObjectId. --*/ { NTSTATUS ntstatus; WORD ObjectType; #if DBG IF_DEBUG(ENUM) { KdPrint(("NWWORKSTATION: NwGetNextServerEntry LastObjectId %lu\n", *LastObjectId)); } #endif ntstatus = NwlibMakeNcp( PreferredServer, FSCTL_NWR_NCP_E3H, // Bindery function 58, // Max request packet size 59, // Max response packet size "bdwp|dwc", // Format string 0x37, // Scan bindery object *LastObjectId, // Previous ID 0x4, // File server object "*", // Wildcard to match all LastObjectId, // Current ID &ObjectType, // Ignore ServerName // Currently returned server ); #if DBG if (ntstatus == STATUS_SUCCESS) { IF_DEBUG(ENUM) { KdPrint(("NWWORKSTATION: NwGetNextServerEntry NewObjectId %08lx, ServerName %s\n", *LastObjectId, ServerName)); } } #endif return NwMapBinderyCompletionCode(ntstatus); } DWORD GetConnectedBinderyServers( OUT LPNW_ENUM_CONTEXT ContextHandle ) /*++ Routine Description: This function is a helper routine for the function NwGetNextServerConnection. It allocates a buffer to cache bindery server names returned from calls to the redirector. Since the redirector may return duplicate bindery server names, this function checks to see if the server name already exist in the buffer before adding it. Arguments: ContextHandle - Used to track cached bindery information and the current server name pointer in the cache buffer. Return Value: NO_ERROR - Successfully returned a server name and cache buffer. WN_NO_MORE_ENTRIES - No other server object past the one specified by CH->ResumeId. ERROR_NOT_ENOUGH_MEMORY - Function was unable to allocate a buffer. ++*/ { DWORD_PTR ResumeKey = 0; LPBYTE pBuffer = NULL; DWORD EntriesRead = 0; BYTE tokenIter; LPWSTR tokenPtr; BOOL fAddToList; DWORD status = NwGetConnectionStatus( NULL, &ResumeKey, &pBuffer, &EntriesRead ); if ( status == NO_ERROR && EntriesRead > 0 ) { DWORD i; PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer; ContextHandle->ResumeId = 0; ContextHandle->NdsRawDataCount = 0; ContextHandle->NdsRawDataSize = (NW_MAX_SERVER_LEN + 2) * EntriesRead; ContextHandle->NdsRawDataBuffer = (DWORD_PTR) LocalAlloc( LMEM_ZEROINIT, ContextHandle->NdsRawDataSize ); if ( ContextHandle->NdsRawDataBuffer == 0 ) { KdPrint(("NWWORKSTATION: GetConnectedBinderyServers LocalAlloc failed %lu\n", GetLastError())); ContextHandle->NdsRawDataSize = 0; return ERROR_NOT_ENOUGH_MEMORY; } for ( i = 0; i < EntriesRead ; i++ ) { fAddToList = FALSE; if ( pConnStatus->fNds == 0 && ( pConnStatus->dwConnType == NW_CONN_BINDERY_LOGIN || pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE || pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED || pConnStatus->dwConnType == NW_CONN_DISCONNECTED ) ) { fAddToList = TRUE; tokenPtr = (LPWSTR) ContextHandle->NdsRawDataBuffer; tokenIter = 0; // // Walk through buffer to see if the tree name already exists. // while ( tokenIter < ContextHandle->NdsRawDataCount ) { if ( !wcscmp( tokenPtr, pConnStatus->pszServerName ) ) { fAddToList = FALSE; } tokenPtr = tokenPtr + wcslen( tokenPtr ) + 1; tokenIter++; } } // // Add the new tree name to end of buffer if needed. // if ( fAddToList ) { wcscpy( tokenPtr, pConnStatus->pszServerName ); _wcsupr( tokenPtr ); ContextHandle->NdsRawDataCount += 1; } pConnStatus = (PCONN_STATUS) ( pConnStatus + pConnStatus->dwTotalLength ); } if ( pBuffer != NULL ) { LocalFree( pBuffer ); pBuffer = NULL; } if ( ContextHandle->NdsRawDataCount > 0 ) { // // Set ResumeId to point to the first entry in buffer // and have NdsRawDataCount set to the number // of tree entries left in buffer (ie. substract 1) // ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer; ContextHandle->NdsRawDataCount -= 1; } return NO_ERROR; } return WN_NO_MORE_ENTRIES; } DWORD NwGetNextServerConnection( OUT LPNW_ENUM_CONTEXT ContextHandle ) /*++ Routine Description: This function queries the redirector for bindery server connections Arguments: ContextHandle - Receives the name of the returned bindery server. Return Value: NO_ERROR - Successfully returned a server name. WN_NO_MORE_ENTRIES - No other server objects past the one specified by CH->ResumeId exist. --*/ { #if DBG IF_DEBUG(ENUM) { KdPrint(("NWWORKSTATION: NwGetNextServerConnection ResumeId %lu\n", ContextHandle->ResumeId)); } #endif if ( ContextHandle->ResumeId == (DWORD_PTR) -1 && ContextHandle->NdsRawDataBuffer == 0 && ContextHandle->NdsRawDataCount == 0 ) { // // Fill the buffer and point ResumeId to the last // server entry name in it. NdsRawDataCount will be // set to one less than the number of server names in buffer. // return GetConnectedBinderyServers( ContextHandle ); } if ( ContextHandle->NdsRawDataBuffer != 0 && ContextHandle->NdsRawDataCount > 0 ) { // // Move ResumeId to point to the next entry in the buffer // and decrement the NdsRawDataCount by one. Watch for case // where we backed up to -1. // if (ContextHandle->ResumeId == (DWORD_PTR) -1) { // // Reset to start of buffer. // ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer; } else { // // Treat as pointer and advance as need. // ContextHandle->ResumeId = ContextHandle->ResumeId + ( ( wcslen( (LPWSTR) ContextHandle->ResumeId ) + 1 ) * sizeof(WCHAR) ); } ContextHandle->NdsRawDataCount -= 1; return NO_ERROR; } if ( ContextHandle->NdsRawDataBuffer != 0 && ContextHandle->NdsRawDataCount == 0 ) { // // We already have a buffer and processed all server names // in it, and there is no more data to get. // So free the memory used for the buffer and return // WN_NO_MORE_ENTRIES to tell WinFile that we are done. // (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer ); ContextHandle->NdsRawDataBuffer = 0; ContextHandle->NdsRawDataSize = 0; return WN_NO_MORE_ENTRIES; } // // Were done // return WN_NO_MORE_ENTRIES; } DWORD GetTreeEntriesFromBindery( OUT LPNW_ENUM_CONTEXT ContextHandle ) /*++ Routine Description: This function is a helper routine for the function NwGetNextNdsTreeEntry. It allocates a buffer (if needed) to cache NDS tree names returned from calls to the bindery. Since the bindery often returns duplicates of a NDS tree name, this function checks to see if the tree name already exist in the buffer before adding it to it if not present. Arguments: ContextHandle - Used to track cached bindery information and the current tree name pointer in the cache buffer. Return Value: NO_ERROR - Successfully returned a NDS tree name and cache buffer. WN_NO_MORE_ENTRIES - No other NDS tree object past the one specified by CH->ResumeId. ERROR_NOT_ENOUGH_MEMORY - Function was unable to allocate a buffer. ++*/ { NTSTATUS ntstatus = STATUS_SUCCESS; SERVERNAME TreeName; LPWSTR UTreeName = NULL; //Unicode tree name DWORD tempDataId; WORD ObjectType; BYTE iter; BYTE tokenIter; LPWSTR tokenPtr; BOOL fAddToList; // // Check to see if we need to allocate a buffer for use // if ( ContextHandle->NdsRawDataBuffer == 0x00000000 ) { ContextHandle->NdsRawDataId = (DWORD) ContextHandle->ResumeId; ContextHandle->NdsRawDataSize = EIGHT_KB; ContextHandle->NdsRawDataBuffer = (DWORD_PTR) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, ContextHandle->NdsRawDataSize ); if ( ContextHandle->NdsRawDataBuffer == 0 ) { KdPrint(("NWWORKSTATION: GetTreeEntriesFromBindery LocalAlloc failed %lu\n", GetLastError())); ContextHandle->NdsRawDataSize = 0; ContextHandle->NdsRawDataId = (DWORD) -1; return ERROR_NOT_ENOUGH_MEMORY; } } // // Repeatedly call bindery to fill buffer with NDS tree names until // buffer is full. // while ( ntstatus == STATUS_SUCCESS ) { RtlZeroMemory( TreeName, sizeof( TreeName ) ); tempDataId = ContextHandle->NdsRawDataId; ntstatus = NwlibMakeNcp( ContextHandle->TreeConnectionHandle, FSCTL_NWR_NCP_E3H, // Bindery function 58, // Max request packet size 59, // Max response packet size "bdwp|dwc", // Format string 0x37, // Scan bindery object ContextHandle->NdsRawDataId, // Previous ID 0x278, // Directory server object "*", // Wildcard to match all &ContextHandle->NdsRawDataId, // Current ID &ObjectType, // Ignore TreeName // Currently returned NDS tree ); // // We got a tree name, clean it up (i.e. get rid of underscores ), // and add it to buffer if unique. // if ( ntstatus == STATUS_SUCCESS ) { iter = 31; while ( TreeName[iter] == '_' && iter > 0 ) { iter--; } TreeName[iter + 1] = '\0'; // // Convert tree name to a UNICODE string and proccess it, // else just skip it and move on to the next tree name. // if ( NwConvertToUnicode( &UTreeName, TreeName ) ) { tokenPtr = (LPWSTR) ContextHandle->NdsRawDataBuffer; tokenIter = 0; fAddToList = TRUE; // // Walk through buffer to see if the tree name already exists. // while ( tokenIter < ContextHandle->NdsRawDataCount ) { if ( !wcscmp( tokenPtr, UTreeName ) ) { fAddToList = FALSE; } tokenPtr = tokenPtr + wcslen( tokenPtr ) + 1; tokenIter++; } // // Add the new tree name to end of buffer if needed. // if ( fAddToList ) { DWORD BytesNeededToAddTreeName = (wcslen(UTreeName)+1) * sizeof(WCHAR); DWORD NumberOfBytesAvailable =(DWORD) ( ContextHandle->NdsRawDataBuffer + ContextHandle->NdsRawDataSize - (DWORD_PTR) tokenPtr ); if ( BytesNeededToAddTreeName < NumberOfBytesAvailable ) { wcscpy( tokenPtr, UTreeName ); ContextHandle->NdsRawDataCount += 1; } else { ContextHandle->NdsRawDataId = tempDataId; ntstatus = ERROR_NOT_ENOUGH_MEMORY; } } (void) LocalFree((HLOCAL) UTreeName); } } } // // We are done filling buffer, and there are no more tree names // to request. Set id to indicate last value. // if ( ntstatus == STATUS_NO_MORE_ENTRIES ) { ContextHandle->NdsRawDataId = (DWORD) -1; ntstatus = STATUS_SUCCESS; } // // We are done because the buffer is full. So we return NO_ERROR to // indicate completion, and leave ContextHandle->NdsRawDataId as is // to indicate where we left off. // if ( ntstatus == ERROR_NOT_ENOUGH_MEMORY ) { ntstatus = STATUS_SUCCESS; } if ( ContextHandle->NdsRawDataCount == 0 ) { if ( ContextHandle->NdsRawDataBuffer ) (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer ); ContextHandle->NdsRawDataBuffer = 0; ContextHandle->NdsRawDataSize = 0; ContextHandle->NdsRawDataId = (DWORD) -1; return WN_NO_MORE_ENTRIES; } if ( ContextHandle->NdsRawDataCount > 0 ) { // // Set ResumeId to point to the first entry in buffer // and have NdsRawDataCount set to the number // of tree entries left in buffer (ie. substract 1) // ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer; ContextHandle->NdsRawDataCount -= 1; return NO_ERROR; } if ( ContextHandle->NdsRawDataBuffer ) (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer ); ContextHandle->NdsRawDataBuffer = 0; ContextHandle->NdsRawDataSize = 0; ContextHandle->NdsRawDataId = (DWORD) -1; return NwMapStatus( ntstatus ); } DWORD NwGetNextNdsTreeEntry( OUT LPNW_ENUM_CONTEXT ContextHandle ) /*++ Routine Description: This function uses an opened handle to the preferred server to scan it bindery for all NDS tree objects. Arguments: ContextHandle - Receives the name of the returned NDS tree object given the current preferred server connection and CH->ResumeId. Return Value: NO_ERROR - Successfully returned a NDS tree name. WN_NO_MORE_ENTRIES - No other NDS tree objects past the one specified by CH->ResumeId exist. --*/ { #if DBG IF_DEBUG(ENUM) { KdPrint(("NWWORKSTATION: NwGetNextNdsTreeEntry ResumeId %lu\n", ContextHandle->ResumeId)); } #endif if ( ContextHandle->ResumeId == (DWORD_PTR) -1 && ContextHandle->NdsRawDataBuffer == 0 && ContextHandle->NdsRawDataCount == 0 ) { // // Fill the buffer and point ResumeId to the last // tree entry name in it. NdsRawDataCount will be // set to one less than the number of tree names in buffer. // return GetTreeEntriesFromBindery( ContextHandle ); } if ( ContextHandle->NdsRawDataBuffer != 0 && ContextHandle->NdsRawDataCount > 0 ) { // // Move ResumeId to point to the next entry in the buffer // and decrement the NdsRawDataCount by one. Watch for case // where we backed up to -1. // if (ContextHandle->ResumeId == (DWORD_PTR) -1) { // // Reset to start of buffer. // ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer; } else { // // Move ResumeId to point to the next entry in the buffer // and decrement the NdsRawDataCount by one // ContextHandle->ResumeId = ContextHandle->ResumeId + ( ( wcslen( (LPWSTR) ContextHandle->ResumeId ) + 1 ) * sizeof(WCHAR) ); } ContextHandle->NdsRawDataCount -= 1; return NO_ERROR; } if ( ContextHandle->NdsRawDataBuffer != 0 && ContextHandle->NdsRawDataCount == 0 && ContextHandle->NdsRawDataId != (DWORD) -1 ) { // // We already have a buffer and processed all tree names // in it, and there is more data in the bindery to get. // So go get it and point ResumeId to the last tree // entry name in the buffer and set NdsRawDataCount to // one less than the number of tree names in buffer. // return GetTreeEntriesFromBindery( ContextHandle ); } if ( ContextHandle->NdsRawDataBuffer != 0 && ContextHandle->NdsRawDataCount == 0 && ContextHandle->NdsRawDataId == (DWORD) -1 ) { // // We already have a buffer and processed all tree names // in it, and there is no more data in the bindery to get. // So free the memory used for the buffer and return // WN_NO_MORE_ENTRIES to tell WinFile that we are done. // (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer ); ContextHandle->NdsRawDataBuffer = 0; ContextHandle->NdsRawDataSize = 0; return WN_NO_MORE_ENTRIES; } // // We should never hit this area! // return WN_NO_MORE_ENTRIES; } DWORD NwGetNextVolumeEntry( IN HANDLE ServerConnection, IN DWORD NextVolumeNumber, OUT LPSTR VolumeName ) /*++ Routine Description: This function lists the volumes on the server specified by an opened tree connection handle to the server. Arguments: ServerConnection - Supplies the tree connection handle to the server to enumerate volumes from. NextVolumeNumber - Supplies the volume number which to look up the name. VolumeName - Receives the name of the volume associated with NextVolumeNumber. Return Value: NO_ERROR - Successfully gotten the volume name. WN_NO_MORE_ENTRIES - No other volume name associated with the specified volume number. --*/ { NTSTATUS ntstatus; #if DBG IF_DEBUG(ENUM) { KdPrint(("NWWORKSTATION: NwGetNextVolumeEntry volume number %lu\n", NextVolumeNumber)); } #endif ntstatus = NwlibMakeNcp( ServerConnection, FSCTL_NWR_NCP_E2H, // Directory function 4, // Max request packet size 19, // Max response packet size "bb|p", // Format string 0x6, // Get volume name (BYTE) NextVolumeNumber, // Previous ID VolumeName // Currently returned server ); return NwMapStatus(ntstatus); } DWORD NwRdrLogonUser( IN PLUID LogonId, IN LPWSTR UserName, IN DWORD UserNameSize, IN LPWSTR Password OPTIONAL, IN DWORD PasswordSize, IN LPWSTR PreferredServer OPTIONAL, IN DWORD PreferredServerSize, IN LPWSTR NdsPreferredServer OPTIONAL, IN DWORD NdsPreferredServerSize, IN DWORD PrintOption ) /*++ Routine Description: This function tells the redirector the user logon credential. Arguments: UserName - Supplies the user name. UserNameSize - Supplies the size in bytes of the user name string without the NULL terminator. Password - Supplies the password. PasswordSize - Supplies the size in bytes of the password string without the NULL terminator. PreferredServer - Supplies the preferred server name. PreferredServerSize - Supplies the size in bytes of the preferred server string without the NULL terminator. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; PNWR_REQUEST_PACKET Rrp; // Redirector request packet DWORD RrpSize = sizeof(NWR_REQUEST_PACKET) + UserNameSize + PasswordSize + PreferredServerSize; LPBYTE Dest; BYTE lpReplicaAddress[sizeof(TDI_ADDRESS_IPX)]; DWORD ReplicaAddressSize = 0; #if DBG IF_DEBUG(LOGON) { BYTE PW[128]; RtlZeroMemory(PW, sizeof(PW)); if (PasswordSize > (sizeof(PW) - 1)) { memcpy(PW, Password, sizeof(PW) - 1); } else { memcpy(PW, Password, PasswordSize); } KdPrint(("NWWORKSTATION: NwRdrLogonUser: UserName %ws\n", UserName)); KdPrint((" Password %ws\n", PW)); if ( PreferredServer ) KdPrint((" Server %ws\n", PreferredServer )); } #endif if ( PreferredServer && PreferredServer[0] == TREECHAR && PreferredServer[1] ) { WCHAR TreeName[MAX_NDS_NAME_CHARS + 1]; LPWSTR lpTemp; // // Find the nearest dir server for the tree that the user wants to // connect to. // // Citrix Terminal Server Merge // 12/09/96 cjc PreferredServer also includes organizational units - // not just the tree name so the size of it can be // > MAX_NDS_TREE_NAME_LEN and when it is, the wcscpy // below overwrites other stack data and causes errors // during NW logins. if ( PreferredServerSize > (MAX_NDS_TREE_NAME_LEN*sizeof(WCHAR)) ) { memcpy(TreeName, PreferredServer+1, (MAX_NDS_TREE_NAME_LEN*sizeof(WCHAR)) ); TreeName[MAX_NDS_TREE_NAME_LEN] = L'\0'; } else { wcscpy( TreeName, PreferredServer + 1 ); } lpTemp = wcschr( TreeName, L'\\' ); if (lpTemp) { lpTemp[0] = L'\0'; } if (NdsPreferredServer != NULL) { KdPrint(("NWWORKSTATION: NdsPreferredServer: %ws\n", PreferredServer)); GetPreferredServerAddress( NdsPreferredServer/*L"red_41b"*/, &ReplicaAddressSize, lpReplicaAddress ); } else { GetNearestDirServer( TreeName, &ReplicaAddressSize, lpReplicaAddress ); } RrpSize += ReplicaAddressSize; } if ( PreferredServer && PreferredServer[0] == TREECHAR && !PreferredServer[1] ) { PreferredServerSize = 0; } // // Allocate the request packet // if ((Rrp = (PVOID) LocalAlloc( LMEM_ZEROINIT, RrpSize )) == NULL) { KdPrint(("NWWORKSTATION: NwRdrLogonUser LocalAlloc failed %lu\n", GetLastError())); return ERROR_NOT_ENOUGH_MEMORY; } // // Tell the redirector the user logon credential. // Rrp->Version = REQUEST_PACKET_VERSION; RtlCopyLuid(&(Rrp->Parameters.Logon.LogonId), LogonId); #if DBG IF_DEBUG(LOGON) { KdPrint(("NWWORKSTATION: NwRdrLogonUser passing to Rdr logon ID %lu %lu\n", *LogonId, *((PULONG) ((DWORD_PTR) LogonId + sizeof(ULONG))))); } #endif Rrp->Parameters.Logon.UserNameLength = UserNameSize; Rrp->Parameters.Logon.PasswordLength = PasswordSize; Rrp->Parameters.Logon.ServerNameLength = PreferredServerSize; Rrp->Parameters.Logon.ReplicaAddrLength = ReplicaAddressSize; Rrp->Parameters.Logon.PrintOption = PrintOption; memcpy(Rrp->Parameters.Logon.UserName, UserName, UserNameSize); Dest = (LPBYTE) ((DWORD_PTR) Rrp->Parameters.Logon.UserName + UserNameSize); if (PasswordSize > 0) { memcpy(Dest, Password, PasswordSize); Dest = (LPBYTE) ((DWORD_PTR) Dest + PasswordSize); } if (PreferredServerSize > 0) { memcpy(Dest, PreferredServer, PreferredServerSize); if (ReplicaAddressSize > 0) { Dest = (LPBYTE) ((DWORD_PTR) Dest + PreferredServerSize); memcpy(Dest, lpReplicaAddress, ReplicaAddressSize); } } status = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_LOGON, Rrp, RrpSize, NULL, // No logon script in this release 0, NULL ); RtlZeroMemory(Rrp, RrpSize); // Clear the password (void) LocalFree((HLOCAL) Rrp); return status; } VOID NwRdrChangePassword( IN PNWR_REQUEST_PACKET Rrp ) /*++ Routine Description: This function tells the redirector the new password for a user on a particular server. Arguments: Rrp - Supplies the username, new password and servername. RrpSize - Supplies the size of the request packet. Return Value: None. --*/ { // // Tell the redirector the user new password. // Rrp->Version = REQUEST_PACKET_VERSION; (void) NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_CHANGE_PASS, Rrp, sizeof(NWR_REQUEST_PACKET) + Rrp->Parameters.ChangePass.UserNameLength + Rrp->Parameters.ChangePass.PasswordLength + Rrp->Parameters.ChangePass.ServerNameLength, NULL, 0, NULL ); } DWORD NwRdrSetInfo( IN DWORD PrintOption, IN DWORD PacketBurstSize, IN LPWSTR PreferredServer OPTIONAL, IN DWORD PreferredServerSize, IN LPWSTR ProviderName OPTIONAL, IN DWORD ProviderNameSize ) /*++ Routine Description: This function passes some workstation configuration and current user's preference to the redirector. This includes the network provider name, the packet burst size, the user's selected preferred server and print option. Arguments: PrintOption - The current user's print option PacketBurstSize - The packet burst size stored in the registry PreferredServer - The preferred server the current user selected PreferredServerSize - Supplies the size in bytes of the preferred server string without the NULL terminator. ProviderName - Supplies the provider name. ProviderNameSize - Supplies the size in bytes of the provider name string without the NULL terminator. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; PNWR_REQUEST_PACKET Rrp; // Redirector request packet DWORD RrpSize = sizeof(NWR_REQUEST_PACKET) + PreferredServerSize + ProviderNameSize; LPBYTE Dest; BOOL Impersonate = FALSE; // // Allocate the request packet // if ((Rrp = (PVOID) LocalAlloc( LMEM_ZEROINIT, RrpSize )) == NULL) { KdPrint(("NWWORKSTATION: NwRdrSetInfo LocalAlloc failed %lu\n", GetLastError())); return ERROR_NOT_ENOUGH_MEMORY; } Rrp->Version = REQUEST_PACKET_VERSION; Rrp->Parameters.SetInfo.PrintOption = PrintOption; Rrp->Parameters.SetInfo.MaximumBurstSize = PacketBurstSize; Rrp->Parameters.SetInfo.PreferredServerLength = PreferredServerSize; Rrp->Parameters.SetInfo.ProviderNameLength = ProviderNameSize; if (ProviderNameSize > 0) { memcpy( Rrp->Parameters.SetInfo.PreferredServer, PreferredServer, PreferredServerSize); } Dest = (LPBYTE) ((DWORD_PTR) Rrp->Parameters.SetInfo.PreferredServer + PreferredServerSize); if (ProviderNameSize > 0) { memcpy(Dest, ProviderName, ProviderNameSize); } /* --- Multi-user change * For print options * It's OK if it doesn't work */ if ((status = NwImpersonateClient()) == NO_ERROR) { Impersonate = TRUE; } status = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_SET_INFO, Rrp, RrpSize, NULL, 0, NULL ); if ( Impersonate ) { (void) NwRevertToSelf() ; } (void) LocalFree((HLOCAL) Rrp); if ( status != NO_ERROR ) { KdPrint(("NwRedirFsControl: FSCTL_NWR_SET_INFO failed with %d\n", status )); } return status; } DWORD NwRdrLogoffUser( IN PLUID LogonId ) /*++ Routine Description: This function asks the redirector to log off the interactive user. Arguments: None. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; NWR_REQUEST_PACKET Rrp; // Redirector request packet // // Tell the redirector to logoff user. // Rrp.Version = REQUEST_PACKET_VERSION; RtlCopyLuid(&Rrp.Parameters.Logoff.LogonId, LogonId); status = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_LOGOFF, &Rrp, sizeof(NWR_REQUEST_PACKET), NULL, 0, NULL ); return status; } DWORD NwConnectToServer( IN LPWSTR ServerName ) /*++ Routine Description: This function opens a handle to \Device\Nwrdr\ServerName, given ServerName, and then closes the handle if the open was successful. It is to validate that the current user credential can access the server. Arguments: ServerName - Supplies the name of the server to validate the user credential. Return Value: NO_ERROR or reason for failure. --*/ { DWORD status; UNICODE_STRING ServerStr; HANDLE ServerHandle; ServerStr.MaximumLength = (wcslen(ServerName) + 2) * sizeof(WCHAR) + // \ServerName0 RedirDeviceName.Length; // \Device\Nwrdr if ((ServerStr.Buffer = (PWSTR) LocalAlloc( LMEM_ZEROINIT, (UINT) ServerStr.MaximumLength )) == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } // // Copy \Device\NwRdr // RtlCopyUnicodeString(&ServerStr, &RedirDeviceName); // // Concatenate \ServerName // wcscat(ServerStr.Buffer, L"\\"); ServerStr.Length += sizeof(WCHAR); wcscat(ServerStr.Buffer, ServerName); ServerStr.Length += (USHORT) (wcslen(ServerName) * sizeof(WCHAR)); status = NwOpenCreateConnection( &ServerStr, NULL, NULL, ServerName, SYNCHRONIZE | FILE_WRITE_DATA, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, RESOURCETYPE_DISK, &ServerHandle, NULL ); if (status == ERROR_FILE_NOT_FOUND) { status = ERROR_BAD_NETPATH; } (void) LocalFree((HLOCAL) ServerStr.Buffer); if (status == NO_ERROR || status == NW_PASSWORD_HAS_EXPIRED) { (void) NtClose(ServerHandle); } return status; } DWORD NWPGetConnectionStatus( IN LPWSTR pszRemoteName, IN OUT PDWORD_PTR ResumeKey, OUT LPBYTE Buffer, IN DWORD BufferSize, OUT PDWORD BytesNeeded, OUT PDWORD EntriesRead ) { NTSTATUS ntstatus = STATUS_SUCCESS; HANDLE handleRdr = NULL; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING uRdrName; WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*"; PNWR_REQUEST_PACKET RequestPacket = NULL; DWORD RequestPacketSize = 0; DWORD dwRemoteNameLen = 0; // // Set up the object attributes. // RtlInitUnicodeString( &uRdrName, RdrPrefix ); InitializeObjectAttributes( &ObjectAttributes, &uRdrName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ntstatus = NtOpenFile( &handleRdr, SYNCHRONIZE | FILE_LIST_DIRECTORY, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT ); if ( !NT_SUCCESS(ntstatus) ) goto CleanExit; dwRemoteNameLen = pszRemoteName? wcslen(pszRemoteName)*sizeof(WCHAR) : 0; RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwRemoteNameLen; RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, RequestPacketSize ); if ( RequestPacket == NULL ) { ntstatus = STATUS_NO_MEMORY; goto CleanExit; } // // Fill out the request packet for FSCTL_NWR_GET_CONN_STATUS. // RequestPacket->Parameters.GetConnStatus.ResumeKey = *ResumeKey; RequestPacket->Version = REQUEST_PACKET_VERSION; RequestPacket->Parameters.GetConnStatus.ConnectionNameLength = dwRemoteNameLen; RtlCopyMemory( &(RequestPacket->Parameters.GetConnStatus.ConnectionName[0]), pszRemoteName, dwRemoteNameLen ); ntstatus = NtFsControlFile( handleRdr, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_GET_CONN_STATUS, (PVOID) RequestPacket, RequestPacketSize, (PVOID) Buffer, BufferSize ); if ( NT_SUCCESS( ntstatus )) ntstatus = IoStatusBlock.Status; *EntriesRead = RequestPacket->Parameters.GetConnStatus.EntriesReturned; *ResumeKey = RequestPacket->Parameters.GetConnStatus.ResumeKey; *BytesNeeded = RequestPacket->Parameters.GetConnStatus.BytesNeeded; CleanExit: if ( handleRdr != NULL ) NtClose( handleRdr ); if ( RequestPacket != NULL ) LocalFree( RequestPacket ); return RtlNtStatusToDosError( ntstatus ); } DWORD NwGetConnectionStatus( IN LPWSTR pszRemoteName, OUT PDWORD_PTR ResumeKey, OUT LPBYTE *Buffer, OUT PDWORD EntriesRead ) { DWORD err = NO_ERROR; DWORD dwBytesNeeded = 0; DWORD dwBufferSize = TWO_KB; *Buffer = NULL; *EntriesRead = 0; do { *Buffer = (LPBYTE) LocalAlloc( LMEM_ZEROINIT, dwBufferSize ); if ( *Buffer == NULL ) return ERROR_NOT_ENOUGH_MEMORY; err = NWPGetConnectionStatus( pszRemoteName, ResumeKey, *Buffer, dwBufferSize, &dwBytesNeeded, EntriesRead ); if ( err == ERROR_INSUFFICIENT_BUFFER ) { dwBufferSize = dwBytesNeeded + EXTRA_BYTES; LocalFree( *Buffer ); *Buffer = NULL; } } while ( err == ERROR_INSUFFICIENT_BUFFER ); if ( err == ERROR_INVALID_PARAMETER ) // not attached { err = NO_ERROR; *EntriesRead = 0; } return err; } VOID GetNearestDirServer( IN LPWSTR TreeName, OUT LPDWORD lpdwReplicaAddressSize, OUT LPBYTE lpReplicaAddress ) { WCHAR Buffer[BUFFSIZE]; PWSAQUERYSETW Query = (PWSAQUERYSETW)Buffer; HANDLE hRnr; DWORD dwQuerySize = BUFFSIZE; GUID gdService = SVCID_NETWARE(0x278); WSADATA wsaData; WCHAR ServiceInstanceName[] = L"*"; WSAStartup(MAKEWORD(1, 1), &wsaData); memset(Query, 0, sizeof(*Query)); // // putting a "*" in the lpszServiceInstanceName causes // the query to look for all server instances. Putting a // specific name in here will search only for instance of // that name. If you have a specific name to look for, // put a pointer to the name here. // Query->lpszServiceInstanceName = ServiceInstanceName; Query->dwNameSpace = NS_SAP; Query->dwSize = sizeof(*Query); Query->lpServiceClassId = &gdService; // // Find the servers. The flags indicate: // LUP_NEAREST: look for nearest servers // LUP_DEEP : if none are found on the local segement look // for server using a general query // LUP_RETURN_NAME: return the name // LUP_RETURN_ADDR: return the server address // // if only servers on the local segment are acceptable, omit // setting LUP_DEEP // if( WSALookupServiceBegin( Query, LUP_NEAREST | LUP_DEEP | LUP_RETURN_NAME | LUP_RETURN_ADDR, &hRnr ) == SOCKET_ERROR ) { // // Something went wrong, return no address. The redirector will // have to come up with a dir server on its own. // *lpdwReplicaAddressSize = 0; return ; } else { // // Ready to look for one of them ... // Query->dwSize = BUFFSIZE; while( WSALookupServiceNext( hRnr, 0, &dwQuerySize, Query ) == NO_ERROR ) { // // Found a dir server, now see if it is a server for the NDS tree // TreeName. // if ( NwpCompareTreeNames( Query->lpszServiceInstanceName, TreeName ) ) { *lpdwReplicaAddressSize = sizeof(TDI_ADDRESS_IPX); memcpy( lpReplicaAddress, Query->lpcsaBuffer->RemoteAddr.lpSockaddr->sa_data, sizeof(TDI_ADDRESS_IPX) ); WSALookupServiceEnd(hRnr); return ; } } // // Could not find a dir server, return no address. The redirector will // have to come up with a dir server on its own. // *lpdwReplicaAddressSize = 0; WSALookupServiceEnd(hRnr); } } BOOL NwpCompareTreeNames( LPWSTR lpServiceInstanceName, LPWSTR lpTreeName ) { DWORD iter = 31; while ( lpServiceInstanceName[iter] == '_' && iter > 0 ) { iter--; } lpServiceInstanceName[iter + 1] = '\0'; if ( !_wcsicmp( lpServiceInstanceName, lpTreeName ) ) { return TRUE; } return FALSE; } #define SIZE_OF_STATISTICS_TOKEN_INFORMATION \ sizeof( TOKEN_STATISTICS ) VOID GetLuid( IN OUT PLUID plogonid ) /*++ Routine Description: Returns an LUID Arguments: none Return Value: LUID --*/ { HANDLE TokenHandle; UCHAR TokenInformation[ SIZE_OF_STATISTICS_TOKEN_INFORMATION ]; ULONG ReturnLength; LUID NullId = { 0, 0 }; // We can use OpenThreadToken because this server thread // is impersonating a client if ( !OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, /* Open as self */ &TokenHandle )) { #if DBG KdPrint(("GetLuid: OpenThreadToken failed: Error %d\n", GetLastError())); #endif *plogonid = NullId; return; } // notice that we've allocated enough space for the // TokenInformation structure. so if we fail, we // return a NULL pointer indicating failure if ( !GetTokenInformation( TokenHandle, TokenStatistics, TokenInformation, sizeof( TokenInformation ), &ReturnLength )) { #if DBG KdPrint(("GetLuid: GetTokenInformation failed: Error %d\n", GetLastError())); #endif *plogonid = NullId; return; } CloseHandle( TokenHandle ); *plogonid = ( ((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId ); return; } DWORD NwCloseAllConnections( VOID ) /*++ Routine Description: This routine closes all connections. It is used when stopping the redirector. Arguments: None. Return Value: NO_ERROR or error --*/ { NWR_REQUEST_PACKET Rrp; DWORD error; Rrp.Version = REQUEST_PACKET_VERSION; error = NwRedirFsControl( RedirDeviceHandle, FSCTL_NWR_CLOSEALL, &Rrp, sizeof(NWR_REQUEST_PACKET), NULL, 0, NULL ); return error; } VOID GetPreferredServerAddress( IN LPWSTR PreferredServerName, OUT LPDWORD lpdwReplicaAddressSize, OUT LPBYTE lpReplicaAddress ) { WCHAR Buffer[1024]; PWSAQUERYSETW Query = (PWSAQUERYSETW)Buffer; HANDLE hRnr; DWORD dwQuerySize = 1024; GUID gdService = SVCID_NETWARE( 0x4 ); WSADATA wsaData; PWCHAR ServiceInstanceName = PreferredServerName; WSAStartup(MAKEWORD(1, 1), &wsaData); memset(Query, 0, sizeof(*Query)); // // putting a "*" in the lpszServiceInstanceName causes // the query to look for all server instances. Putting a // specific name in here will search only for instance of // that name. If you have a specific name to look for, // put a pointer to the name here. // Query->lpszServiceInstanceName = ServiceInstanceName; Query->dwNameSpace = NS_SAP; Query->dwSize = sizeof(*Query); Query->lpServiceClassId = &gdService; // // Find the servers. The flags indicate: // LUP_NEAREST: look for nearest servers // LUP_DEEP : if none are found on the local segement look // for server using a general query // LUP_RETURN_NAME: return the name // LUP_RETURN_ADDR: return the server address // // if only servers on the local segment are acceptable, omit // setting LUP_DEEP // if( WSALookupServiceBeginW( Query, // LUP_NEAREST | LUP_DEEP | LUP_RETURN_NAME | LUP_RETURN_ADDR, &hRnr ) == SOCKET_ERROR ) { // // Something went wrong, return no address. The redirector will // have to come up with a dir server on its own. // *lpdwReplicaAddressSize = 0; return ; } else { // // Ready to look for one of them ... // Query->dwSize = 1024; while( WSALookupServiceNextW( hRnr, 0, &dwQuerySize, Query ) == NO_ERROR ) { // // Found a dir server, now see if it is a server for the NDS tree // TreeName. // // if ( NwpCompareTreeNames( Query->lpszServiceInstanceName, // TreeName ) ) { *lpdwReplicaAddressSize = sizeof(TDI_ADDRESS_IPX); memcpy( lpReplicaAddress, Query->lpcsaBuffer->RemoteAddr.lpSockaddr->sa_data, sizeof(TDI_ADDRESS_IPX) ); WSALookupServiceEnd(hRnr); return ; } } // // Could not find a dir server, return no address. The redirector will // have to come up with a dir server on its own. // *lpdwReplicaAddressSize = 0; WSALookupServiceEnd(hRnr); } }