/*++ Copyright (c) 2000 Microsoft Corporation Module Name: drprov.c Abstract: This module implements the routines required for interaction with network provider router interface in NT for RDP mini-redirector Author: Joy Chik 1/20/2000 --*/ #define TRC_FILE "drprov" #include "drprov.h" #include "drdbg.h" DWORD GLOBAL_DEBUG_FLAGS=0x0; // // the RDP mini redirector and provider name. The original constants // are defined in rdpdr.h // // The length does not include the null terminator // UNICODE_STRING DrDeviceName = {RDPDR_DEVICE_NAME_U_LENGTH - sizeof(WCHAR), RDPDR_DEVICE_NAME_U_LENGTH, RDPDR_DEVICE_NAME_U}; extern UNICODE_STRING DrProviderName; // // Function prototypes defined in drenum.c // DWORD DrOpenMiniRdr(HANDLE *DrDeviceHandle); DWORD DrDeviceControlGetInfo(IN HANDLE FileHandle, IN ULONG DeviceControlCode, IN PVOID RequestPacket, IN ULONG RequestPacketLength, OUT LPBYTE *OutputBuffer, IN ULONG PreferedMaximumLength, IN ULONG BufferHintSize, OUT PULONG_PTR Information OPTIONAL); DWORD DrEnumServerInfo(IN PRDPDR_ENUMERATION_HANDLE pEnumHandle, OUT LPDWORD lpcCount, OUT LPNETRESOURCEW pBufferResource, IN OUT LPDWORD lpBufferSize); DWORD DrEnumShareInfo(IN PRDPDR_ENUMERATION_HANDLE pEnumHandle, OUT LPDWORD lpcCount, OUT LPNETRESOURCEW pBufferResource, IN OUT LPDWORD lpBufferSize); DWORD DrEnumConnectionInfo(IN PRDPDR_ENUMERATION_HANDLE pEnumHandle, OUT LPDWORD lpcCount, OUT LPNETRESOURCEW pBufferResource, IN OUT LPDWORD lpBufferSize); BOOL ValidateRemoteName(IN PWCHAR pRemoteName); DWORD APIENTRY NPGetCaps( DWORD nIndex ) /*++ Routine Description: This routine returns the capabilities of the RDP Mini redirector network provider implementation Arguments: nIndex - category of capabilities desired Return Value: the appropriate capabilities --*/ { DBGMSG(DBG_TRACE, ("DRPROV: NPGetCaps, index: %d\n", nIndex)); switch (nIndex) { case WNNC_SPEC_VERSION: return WNNC_SPEC_VERSION51; case WNNC_NET_TYPE: return WNNC_NET_TERMSRV; case WNNC_DRIVER_VERSION: #define WNNC_DRIVER(major,minor) (major*0x00010000 + minor) return (WNNC_DRIVER(RDPDR_MAJOR_VERSION, RDPDR_MINOR_VERSION)); case WNNC_USER: return WNNC_USR_GETUSER; case WNNC_CONNECTION: return (WNNC_CON_GETCONNECTIONS | WNNC_CON_CANCELCONNECTION | WNNC_CON_ADDCONNECTION | WNNC_CON_ADDCONNECTION3); case WNNC_DIALOG: return WNNC_DLG_GETRESOURCEINFORMATION; //return (WNNC_DLG_SEARCHDIALOG | // WNNC_DLG_FORMATNETNAME); case WNNC_ADMIN: return 0; case WNNC_ENUMERATION: return (WNNC_ENUM_LOCAL | WNNC_ENUM_GLOBAL | WNNC_ENUM_SHAREABLE); case WNNC_START: // // JOYC: Need to figure what we should return here // return 1; default: return 0; } } DWORD APIENTRY NPOpenEnum( DWORD dwScope, DWORD dwType, DWORD dwUsage, LPNETRESOURCE lpNetResource, LPHANDLE lphEnum ) /*++ Routine Description: This routine opens a handle for enumeration of resources. Arguments: dwScope - the scope of enumeration dwType - the type of resources to be enumerated dwUsage - the usage parameter lpNetResource - a pointer to the desired NETRESOURCE struct. lphEnum - a pointer for passing back the enumeration handle Return Value: WN_SUCCESS if successful, otherwise the appropriate error --*/ { DWORD Status = WN_NOT_SUPPORTED; RDPDR_ENUMERATION_HANDLE *pEnum; DWORD ConsoleId, CurrentId; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, dwScope=%d, dwType=%d, dwUsage=%d\n", dwScope, dwType, dwUsage)); // // Basic parameter checking, make sure lphEnum is not NULL // if (lphEnum != NULL) { *lphEnum = NULL; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, null lphEnum parameter.\n")); Status = WN_BAD_VALUE; goto EXIT; } // // Check if the request comes from console, if so, bail immediately // ConsoleId = WTSGetActiveConsoleSessionId(); if (ProcessIdToSessionId(GetCurrentProcessId(), &CurrentId)) { if (ConsoleId == CurrentId) { if (!(dwScope == RESOURCE_GLOBALNET && lpNetResource == NULL)) { DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, console request, bail.\n")); Status = WN_NOT_SUPPORTED; goto EXIT; } } } // // Allocating the enumeration handle // *lphEnum = MemAlloc(sizeof(RDPDR_ENUMERATION_HANDLE)); if (*lphEnum == NULL) { DBGMSG(DBG_ERROR, ("DRPROV: NPOpenEnum, MemAlloc failed for enum handle.\n")); Status = WN_OUT_OF_MEMORY; goto EXIT; } RtlZeroMemory(*lphEnum, sizeof(RDPDR_ENUMERATION_HANDLE)); if (dwScope == RESOURCE_CONNECTED) { // // we are looking for current uses // if (lpNetResource != NULL) { DBGMSG(DBG_ERROR, ("DRPROV: NPOpenEnum invalid parameter\n")); Status = WN_BAD_VALUE; goto EXIT; } pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = CONNECTION; pEnum->enumIndex = 0; Status = WN_SUCCESS; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_CONNECTED.\n")); goto EXIT; } else if (dwScope == RESOURCE_SHAREABLE) { // // We are looking for shareable resources // If we're not given a server, return an EMPTY_ENUM // if ((lpNetResource != NULL) && (lpNetResource->lpRemoteName != NULL) && (lpNetResource->lpRemoteName[0] == L'\\') && (lpNetResource->lpRemoteName[1] == L'\\')) { // // Check if the lpRemoteName is what we recognize if (ValidateRemoteName(lpNetResource->lpRemoteName)) { pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = SHARE; pEnum->enumIndex = 0; pEnum->RemoteName.MaximumLength = (wcslen(lpNetResource->lpRemoteName) + 1) * sizeof(WCHAR); pEnum->RemoteName.Buffer = MemAlloc(pEnum->RemoteName.MaximumLength); if (pEnum->RemoteName.Buffer) { pEnum->RemoteName.Length = pEnum->RemoteName.MaximumLength - sizeof(WCHAR); wcscpy(pEnum->RemoteName.Buffer, lpNetResource->lpRemoteName); DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_SHARABLE for remote name: %ws\n", lpNetResource->lpRemoteName)); Status = WN_SUCCESS; goto EXIT; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, MemAlloc failed for RemoteName\n")); Status = WN_OUT_OF_MEMORY; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_SHAREABLE, RemoteName: %ws not supported\n", lpNetResource->lpRemoteName)); Status = WN_NOT_SUPPORTED; goto EXIT; } } else { pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = EMPTY; pEnum->enumIndex = 0; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_SHAREABLE, NetResource empty\n")); Status = WN_SUCCESS; goto EXIT; } } else if (dwScope == RESOURCE_GLOBALNET) { /* Look for the combination of all bits and substitute "All" for * them. Ignore bits we don't know about. */ dwUsage &= (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER); if ( dwUsage == (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER) ) { dwUsage = 0 ; } /* * we are looking for global resources out on the net */ if (lpNetResource == NULL || lpNetResource->lpRemoteName == NULL) { /* * at top level, therefore enumerating servers. if user * asked for connectable, well, there aint none. */ if (dwUsage == RESOURCEUSAGE_CONNECTABLE) { pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = EMPTY; pEnum->enumIndex = 0; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_GLOBALNET, empty node\n")); Status = WN_SUCCESS; goto EXIT; } else { // return server name, i.e. tsclient pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = SERVER; pEnum->enumIndex = 0; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_GLOBALNET, enumerate server name.\n")); Status = WN_SUCCESS; goto EXIT; } } else { /* * we are assured of lpRemoteName != NULL. * things get interesting here. the cases are as follows: * * if (dwUsage == 0) * if have \\ in front * return shares * else * return empty enum * else if (dwUsage == CONNECTABLE) * if have \\ in front * return shares * else * empty enum * else if (dwUsage == CONTAINER) * if have \\ in front * empty enum * else * return empty enum * */ if (((dwUsage == RESOURCEUSAGE_CONNECTABLE) || (dwUsage == 0)) && ((lpNetResource->lpRemoteName[0] == L'\\') && (lpNetResource->lpRemoteName[1] == L'\\'))) { /* Confirm that this really is a computer name (i.e., a * container we can enumerate). */ if (ValidateRemoteName(lpNetResource->lpRemoteName)) { pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = SHARE; pEnum->enumIndex = 0; pEnum->RemoteName.MaximumLength = (wcslen(lpNetResource->lpRemoteName) + 1) * sizeof(WCHAR); pEnum->RemoteName.Buffer = MemAlloc(pEnum->RemoteName.MaximumLength); if (pEnum->RemoteName.Buffer) { pEnum->RemoteName.Length = pEnum->RemoteName.MaximumLength - sizeof(WCHAR); wcscpy(pEnum->RemoteName.Buffer, lpNetResource->lpRemoteName); DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_GLOBALNET for remote name: %ws\n", lpNetResource->lpRemoteName)); Status = WN_SUCCESS; goto EXIT; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, MemAlloc failed for RemoteName\n")); Status = WN_OUT_OF_MEMORY; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_GLOBALNET, RemoteName: %ws not supported\n", lpNetResource->lpRemoteName)); Status = WN_NOT_SUPPORTED; goto EXIT; } } else if (((dwUsage == RESOURCEUSAGE_CONTAINER) || (dwUsage == 0)) && (lpNetResource->lpRemoteName[0] != L'\\')) { // return empty enum. pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = EMPTY; pEnum->enumIndex = 0; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_GLOBALNET, empty node\n")); Status = WN_SUCCESS; goto EXIT; } else if ( // ask for share but aint starting from server ((dwUsage == RESOURCEUSAGE_CONNECTABLE) && (lpNetResource->lpRemoteName[0] != L'\\')) || // ask for server but is starting from server ((dwUsage == RESOURCEUSAGE_CONTAINER) && ((lpNetResource->lpRemoteName[0] == L'\\') && (lpNetResource->lpRemoteName[1] == L'\\'))) ) { // return empty pEnum = (PRDPDR_ENUMERATION_HANDLE)(*lphEnum); pEnum->dwScope = dwScope; pEnum->dwType = dwType; pEnum->dwUsage = dwUsage; pEnum->enumType = EMPTY; pEnum->enumIndex = 0; DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, RESOURCE_GLOBALNET, empty node\n")); Status = WN_SUCCESS; goto EXIT; } else { // incorrect dwUsage DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, invalid dwUsage parameter\n")); Status = WN_BAD_VALUE; goto EXIT; } } } else { // invalid dwScope DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, invalid dwScope parameter\n")); Status = WN_BAD_VALUE; goto EXIT; } EXIT: // // clean up enumeration handle in failure case if (Status != WN_SUCCESS && lphEnum != NULL && *lphEnum != NULL) { MemFree(*lphEnum); *lphEnum = NULL; } DBGMSG(DBG_TRACE, ("DRPROV: NPOpenEnum, return status: %x\n", Status)); return Status; } DWORD APIENTRY NPEnumResource( HANDLE hEnum, LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize) /*++ Routine Description: This routine uses the handle obtained by a call to NPOpenEnum for enuerating the connected shares Arguments: hEnum - the enumeration handle lpcCount - the number of resources returned lpBuffer - the buffere for passing back the entries lpBufferSize - the size of the buffer Return Value: WN_SUCCESS if successful, otherwise the appropriate error WN_NO_MORE_ENTRIES - if the enumeration has exhausted the entries WN_MORE_DATA - if nmore data is available --*/ { DWORD status = WN_SUCCESS; LPNETRESOURCEW pBufferResource; PRDPDR_ENUMERATION_HANDLE pEnumHandle; pEnumHandle = (PRDPDR_ENUMERATION_HANDLE)hEnum; pBufferResource = (LPNETRESOURCEW)lpBuffer; if (lpcCount == NULL || lpBuffer == NULL || lpBufferSize == NULL) { DBGMSG(DBG_TRACE, ("DRPROV: NPEnumResource, Invalid parameter(s)\n")); status = WN_BAD_VALUE; goto EXIT; } if (pEnumHandle != NULL) { DBGMSG(DBG_TRACE, ("DRPROV: NPEnumResource, EnumType: %d\n", pEnumHandle->enumType)); if ( pEnumHandle->enumType == SERVER ) { status = DrEnumServerInfo(pEnumHandle, lpcCount, pBufferResource, lpBufferSize); goto EXIT; } else if ( pEnumHandle->enumType == SHARE ) { status = DrEnumShareInfo(pEnumHandle, lpcCount, pBufferResource, lpBufferSize); goto EXIT; } else if ( pEnumHandle->enumType == CONNECTION ) { status = DrEnumConnectionInfo(pEnumHandle, lpcCount, pBufferResource, lpBufferSize); goto EXIT; } else if ( pEnumHandle->enumType == EMPTY) { status = WN_NO_MORE_ENTRIES; goto EXIT; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPEnumResource, invalid enum type\n")); status = WN_BAD_HANDLE; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPEnumResource, NULL enum handle\n")); status = WN_BAD_HANDLE; goto EXIT; } EXIT: DBGMSG(DBG_TRACE, ("DRPROV: NPEnumResource, return status: %x\n", status)); return status; } DWORD APIENTRY NPCloseEnum( HANDLE hEnum ) /*++ Routine Description: This routine closes the handle for enumeration of resources. Arguments: hEnum - the enumeration handle Return Value: WN_SUCCESS if successful, otherwise the appropriate error --*/ { DWORD Status = WN_SUCCESS; DBGMSG(DBG_TRACE, ("DRPROV: NPCloseEnum, handle: %p\n", hEnum)); if (hEnum != NULL) { PRDPDR_ENUMERATION_HANDLE pEnumHandle = (PRDPDR_ENUMERATION_HANDLE)hEnum; // free the enumeration buffer if (pEnumHandle->pEnumBuffer != NULL) { MemFree(pEnumHandle->pEnumBuffer); } // free the remote name if (pEnumHandle->RemoteName.Buffer != NULL) { MemFree(pEnumHandle->RemoteName.Buffer); } // free the enum handle MemFree(hEnum); hEnum = NULL; } Status = WN_SUCCESS; return Status; } DWORD OpenConnection( PUNICODE_STRING pConnectionName, DWORD Disposition, DWORD CreateOption, PFILE_FULL_EA_INFORMATION pEABuffer, DWORD EABufferLength, PHANDLE pConnectionHandle ) /*++ Routine Description: This routine opens the connection. This routine is shared by NpAddConnection and NPCancelConnection Arguments: pConnectionName - the connection name Disposition - the Open disposition CreateOption - the create option pEABuffer - the EA buffer associated with the open EABufferLength - the EA buffer length pConnectionHandle - the placeholder for the connection handle Return Value: WN_SUCCESS if successful, otherwise the appropriate error Notes: --*/ { NTSTATUS Status; DWORD NPStatus; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ConnectionObjectAttributes; DBGMSG(DBG_TRACE, ("DRPROV: OpenConnection, connectionName: %ws\n", pConnectionName->Buffer)); ASSERT(pConnectionName != NULL); ASSERT(pConnectionHandle != NULL); InitializeObjectAttributes( &ConnectionObjectAttributes, pConnectionName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateFile( pConnectionHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ConnectionObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, Disposition, CreateOption, pEABuffer, EABufferLength); DBGMSG(DBG_TRACE, ("DRPROV: OpenConnection, NtCreateFile status: %x\n", Status)); if (Status != STATUS_SUCCESS) { NPStatus = ERROR_BAD_NETPATH; } else { NPStatus = WN_SUCCESS; } return NPStatus; } DWORD CreateConnectionName( PWCHAR pLocalName, PWCHAR pRemoteName, PUNICODE_STRING pConnectionName) /*++ Routine Description: This routine create connection name from the remote name Arguments: pLocalName - the local name for the connection pRemoteName - the UNC remote name pConnectionName - the connection name used to talk to mini redirector Return Value: WN_SUCCESS if successful, otherwise the appropriate error Notes: --*/ { DWORD status; DWORD LocalNameLength,RemoteNameLength; DWORD dwSessionId; WCHAR pSessionId[16]; WCHAR LocalName[MAX_PATH + 1]; ASSERT(pRemoteName != NULL); ASSERT(pConnectionName != NULL); DBGMSG(DBG_TRACE, ("DRPROV: CreateConnectionName, RemoteName: %ws\n", pRemoteName)); if (pLocalName != NULL) { DBGMSG(DBG_TRACE, ("DRPROV: CreateConnection Name, LocalName: %ws\n", pLocalName)); } // // The remote name is in the UNC format \\Server\Share. This name // needs to be translated to an appropriate NT name in order to // issue the request to the underlying mini redirector to create the // connection. // // The NT style name is of the form // // \device\rdpdr\;:\Server\Share // // The additional ; is required by the new RDR for extensibility. // // skip past the first back slash since the name to be appended for the // NT name does not require this. pRemoteName++; RemoteNameLength = wcslen(pRemoteName) * sizeof(WCHAR); if (pLocalName != NULL) { // Local name should not be greater than MAX_PATH; LocalNameLength = wcslen(pLocalName) * sizeof(WCHAR); if (LocalNameLength <= MAX_PATH * sizeof(WCHAR)) { wcscpy(LocalName, pLocalName); } else { wcsncpy(LocalName, pLocalName, MAX_PATH); LocalName[MAX_PATH] = L'\0'; } // remove the trailing : in localname if there is one if (LocalName[LocalNameLength/sizeof(WCHAR) - 1] == L':') { LocalName[LocalNameLength/sizeof(WCHAR) - 1] = L'\0'; LocalNameLength -= sizeof(WCHAR); } } else { LocalNameLength = 0; } // // Get SessionId // dwSessionId = NtCurrentPeb()->SessionId; swprintf(pSessionId, L"%d", dwSessionId); pConnectionName->MaximumLength = (USHORT)(DrDeviceName.Length + RemoteNameLength + LocalNameLength + sizeof(WCHAR) * 3 + // account for \; and : wcslen(pSessionId) * sizeof(WCHAR) + sizeof(WCHAR)); // account of terminator null pConnectionName->Buffer = MemAlloc(pConnectionName->MaximumLength); if (pConnectionName->Buffer == NULL) { DBGMSG(DBG_TRACE, ("DRPROV: CreateConnectionName, MemAlloc failed\n")); status = WN_OUT_OF_MEMORY; goto EXIT; } // Copy the name into the buffer pConnectionName->Length = 0; pConnectionName->Buffer[0] = L'\0'; RtlAppendUnicodeToString(pConnectionName, DrDeviceName.Buffer); RtlAppendUnicodeToString(pConnectionName, L"\\;"); if (LocalNameLength != 0) { RtlAppendUnicodeToString(pConnectionName, LocalName); } RtlAppendUnicodeToString(pConnectionName, L":"); RtlAppendUnicodeToString(pConnectionName, pSessionId); RtlAppendUnicodeToString(pConnectionName, pRemoteName); DBGMSG(DBG_TRACE, ("DRPROV: CreateConnectionName, %wZ\n", pConnectionName)); status = WN_SUCCESS; EXIT: return status; } BOOL ValidateRemoteName(PWCHAR pRemoteName) /*++ Routine Description: This routine checks if the remote name belongs to our provider Arguments: pRemoteName - the UNC remote name Return Value: TRUE if the remote name belongs to our provider, otherwise FALSE Notes: --*/ { BOOL rc = FALSE; DWORD status; RDPDR_REQUEST_PACKET Rrp; // Redirector request packet HANDLE DrDeviceHandle = INVALID_HANDLE_VALUE; LPBYTE Buffer = NULL; PRDPDR_SERVER_INFO pServerEntry; if (DrOpenMiniRdr(&DrDeviceHandle) != WN_SUCCESS) { // // MPR doesn't like return device error in this case // We'll just return 0 entries // DBGMSG(DBG_TRACE, ("DRPROV: ValidateRemoteName, DrOpenMiniRdr failed\n")); DrDeviceHandle = INVALID_HANDLE_VALUE; goto EXIT; } // // Ask the redirector to enumerate the information of server // established by the caller. // Rrp.SessionId = NtCurrentPeb()->SessionId; Rrp.Parameters.Get.ResumeHandle = 0; // // Make the request to the Redirector // status = DrDeviceControlGetInfo(DrDeviceHandle, FSCTL_DR_ENUMERATE_SERVERS, &Rrp, sizeof(RDPDR_REQUEST_PACKET), (LPBYTE *) &Buffer, MAXULONG, 0, NULL); if (status == WN_SUCCESS) { UNICODE_STRING ServerName; pServerEntry = ((PRDPDR_SERVER_INFO) Buffer); ServerName.Length = pServerEntry->ServerName.Length; ServerName.MaximumLength = pServerEntry->ServerName.MaximumLength; ServerName.Buffer = (PWCHAR)((PCHAR)(pServerEntry) + pServerEntry->ServerName.BufferOffset); if ((wcslen(pRemoteName) == ServerName.Length / sizeof(WCHAR)) && _wcsnicmp(pRemoteName, ServerName.Buffer, ServerName.Length/sizeof(WCHAR)) == 0) { rc = TRUE; } else { DBGMSG(DBG_TRACE, ("DRPROV: ValidateRemoteName, remote name not for drprov, %wZ\n", pRemoteName)); goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRENUM: ValidateRemoteName, DrDeviceControlGetInfo failed, %x\n", status)); goto EXIT; } EXIT: if (DrDeviceHandle != INVALID_HANDLE_VALUE) { CloseHandle(DrDeviceHandle); } if (Buffer != NULL) { MemFree(Buffer); } DBGMSG(DBG_TRACE, ("DRPROV: ValidateRemoteName, return, %d\n", rc)); return rc; } DWORD APIENTRY NPAddConnection( LPNETRESOURCE lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName ) /*++ Routine Description: This routine adds a connection to the list of connections associated with this network provider Arguments: lpNetResource - the NETRESOURCE struct lpPassword - the password lpUserName - the user name Return Value: WN_SUCCESS if successful, otherwise the appropriate error Notes: --*/ { DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection.\n")); return NPAddConnection3(NULL, lpNetResource, lpPassword, lpUserName, 0); } DWORD TestAddConnection( LPNETRESOURCE lpNetResource) /*++ Routine Description: This routine tests adding a connection to the list of connections associated with this network provider Arguments: lpNetResource - the NETRESOURCE struct Return Value: WN_SUCCESS if successful, otherwise the appropriate error --*/ { DWORD Status = 0; UNICODE_STRING ConnectionName; HANDLE ConnectionHandle = INVALID_HANDLE_VALUE; PWCHAR pRemoteName; DBGMSG(DBG_TRACE, ("DRPROV: TestAddConnection\n")); pRemoteName = lpNetResource->lpRemoteName; // Create ConnectionName with NULL local name Status = CreateConnectionName(NULL, pRemoteName, &ConnectionName); if (Status != WN_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: TestAddConnection, CreateConnectName failed\n")); goto EXIT; } Status = OpenConnection( &ConnectionName, FILE_OPEN, (FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT), NULL, 0, &ConnectionHandle); if (Status != WN_SUCCESS) { ConnectionHandle = INVALID_HANDLE_VALUE; goto EXIT; } EXIT: if (ConnectionHandle != INVALID_HANDLE_VALUE) { NtClose(ConnectionHandle); ConnectionHandle = INVALID_HANDLE_VALUE; } if (ConnectionName.Buffer != NULL) { MemFree(ConnectionName.Buffer); ConnectionName.Buffer = NULL; } return Status; } DWORD APIENTRY NPAddConnection3( HWND hwndOwner, LPNETRESOURCE lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName, DWORD dwFlags ) /*++ Routine Description: This routine adds a connection to the list of connections associated with this network provider Arguments: hwndOwner - the owner handle lpNetResource - the NETRESOURCE struct lpPassword - the password lpUserName - the user name dwFlags - flags for the connection Return Value: WN_SUCCESS if successful, otherwise the appropriate error Notes: // JOYC: do we need to pass credential to the redirector? // Seems the sessionId verification is enough --*/ { DWORD Status = 0; UNICODE_STRING ConnectionName; HANDLE ConnectionHandle; PWCHAR pLocalName,pRemoteName; DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection 3.\n")); ConnectionName.Buffer = NULL; ConnectionName.Length = 0; ConnectionHandle = INVALID_HANDLE_VALUE; // // Make sure remote name starts with \\ // if ((lpNetResource == NULL) || (lpNetResource->lpRemoteName == NULL) || (lpNetResource->lpRemoteName[0] != L'\\') || (lpNetResource->lpRemoteName[1] != L'\\')) { DBGMSG(DBG_TRACE, ("DRPROV: invalid lpNetResource parameter.\n")); Status = WN_BAD_NETNAME; goto EXIT; } // // The remote name is in the UNC format \\Server\Share. This name // needs to be translated to an appropriate NT name in order to // issue the request to the underlying mini redirector to create the // connection. // // The NT style name is of the form // // \device\rdpdr\;:\Server\Share // // The additional ; is required by the new RDR for extensibility. // // Test if rdpdr provider recognize this remote name or not Status = TestAddConnection(lpNetResource); if (Status != WN_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, TestAddConnection failed\n")); goto EXIT; } pLocalName = lpNetResource->lpLocalName; pRemoteName = lpNetResource->lpRemoteName; Status = CreateConnectionName(pLocalName, pRemoteName, &ConnectionName); if (Status != WN_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, CreateConnectName failed\n")); goto EXIT; } if ((Status == WN_SUCCESS) && (pLocalName != NULL)) { WCHAR TempBuf[64]; DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, create dos symbolic link\n")); if (!QueryDosDeviceW(pLocalName, TempBuf, 64)) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { // // ERROR_FILE_NOT_FOUND (translated from OBJECT_NAME_NOT_FOUND) // means it does not exist and we can redirect this device. goto Done; } else { // // Most likely failure occurred because our output // buffer is too small. It still means someone already // has an existing symbolic link for this device. // DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, DosName already assigned, %ws\n", pLocalName)); Status = ERROR_ALREADY_ASSIGNED; goto EXIT; } } else { // // QueryDosDevice successfully an existing symbolic link-- // somebody is already using this device. // DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, DosName already assigned, %ws\n", pLocalName)); Status = ERROR_ALREADY_ASSIGNED; goto EXIT; } } Done: // // We are not doing anything with username/password // Status = OpenConnection( &ConnectionName, FILE_OPEN, (FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT), NULL, 0, &ConnectionHandle); if (Status != WN_SUCCESS) { ConnectionHandle = INVALID_HANDLE_VALUE; } else { // // Create a symbolic link object to the device we are redirecting // if (DefineDosDeviceW( DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, pLocalName, ConnectionName.Buffer)) { Status = WN_SUCCESS; } else { Status = GetLastError(); DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, failed DefineDosDeviceW, %x.\n", Status)); goto EXIT; } } EXIT: if (ConnectionHandle != INVALID_HANDLE_VALUE) { NtClose(ConnectionHandle); } if (ConnectionName.Buffer != NULL) { MemFree(ConnectionName.Buffer); } DBGMSG(DBG_TRACE, ("DRPROV: NPAddConnection3, return status: %x\n", Status)); return Status; } DWORD APIENTRY NPCancelConnection( LPWSTR lpName, BOOL fForce ) /*++ Routine Description: This routine cancels ( deletes ) a connection from the list of connections associated with this network provider Arguments: lpName - name of the connection fForce - forcefully delete the connection Return Value: WN_SUCCESS if successful, otherwise the appropriate error Notes: --*/ { BOOL bLocalName = FALSE; DWORD Status = 0; NTSTATUS ntStatus; HANDLE ConnectionHandle; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING ConnectionName; WCHAR TargetPath[MAX_PATH + 1]; DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection.\n")); ConnectionName.Buffer = NULL; ConnectionName.Length = 0; ConnectionHandle = INVALID_HANDLE_VALUE; // lpName should contain at least two characters: either two back slashes or dos name if (lpName == NULL || wcslen(lpName) == 0) { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, invalid lpName parameter.\n")); Status = WN_BAD_VALUE; goto EXIT; } // We get the UNC name if (*lpName == L'\\' && *(lpName + 1) == L'\\') { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, lpName is UNC name, %ws.\n", *lpName)); bLocalName = FALSE; // Setup the NT Device Name Status = CreateConnectionName(NULL, lpName, &ConnectionName); if (Status != WN_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, CreateConnectName failed\n")); goto EXIT; } } // We get the local name else { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, lpName is local name, %ws.\n", *lpName)); bLocalName = TRUE; // Find the NT devive path if (QueryDosDevice(lpName, TargetPath, sizeof(TargetPath)/sizeof(WCHAR) - 1)) { ConnectionName.Length = wcslen(TargetPath) * sizeof(WCHAR); ConnectionName.MaximumLength = ConnectionName.Length + sizeof(WCHAR); ConnectionName.Buffer = TargetPath; } else { Status = WN_BAD_NETNAME; DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, QueryDosDevice failed, %x.\n", Status)); goto EXIT; } } Status = OpenConnection( &ConnectionName, FILE_OPEN, (FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT), NULL, 0, &ConnectionHandle); if (Status == WN_SUCCESS) { // Request the driver to delete the connection entry ntStatus = NtFsControlFile( ConnectionHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_DR_DELETE_CONNECTION, NULL, 0, NULL, 0); if (ntStatus == STATUS_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, Deleting dos symbolic link, %ws\n", lpName)); if (bLocalName) { if (DefineDosDevice( DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE, lpName, ConnectionName.Buffer)) { Status = WN_SUCCESS; goto EXIT; } else { Status = GetLastError(); DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection failed to delete symbolic link, %x.\n", Status)); goto EXIT; } } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, failed NtFsControlFile, %x\n", ntStatus)); Status = WN_BAD_NETNAME; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, OpenConnection %wZ failed, %x\n", &ConnectionName, Status)); ConnectionHandle = INVALID_HANDLE_VALUE; Status = WN_BAD_NETNAME; goto EXIT; } EXIT: if (bLocalName != TRUE && ConnectionName.Buffer != NULL) { MemFree(ConnectionName.Buffer); } if (ConnectionHandle != INVALID_HANDLE_VALUE) { NtClose(ConnectionHandle); } DBGMSG(DBG_TRACE, ("DRPROV: NPCancelConnection, return status: %x\n", Status)); return Status; } DWORD APIENTRY NPGetConnection( LPWSTR lpLocalName, LPWSTR lpRemoteName, LPDWORD lpBufferSize ) /*++ Routine Description: This routine returns the information associated with a connection Arguments: lpLocalName - local name associated with the connection lpRemoteName - the remote name associated with the connection lpBufferSize - the remote name buffer size Return Value: WN_SUCCESS if successful, otherwise the appropriate error Notes: --*/ { DWORD Status = 0; NTSTATUS ntStatus; HANDLE ConnectionHandle; RDPDR_REQUEST_PACKET Rrp; // Redirector request packet UNICODE_STRING ConnectionName; WCHAR TargetPath[MAX_PATH + 1]; LPBYTE Buffer = NULL; PRDPDR_CONNECTION_INFO ConnectionInfo; DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection.\n")); ConnectionName.Buffer = NULL; ConnectionName.Length = 0; ConnectionHandle = INVALID_HANDLE_VALUE; if (lpLocalName == NULL || lpRemoteName == NULL || lpBufferSize == NULL) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, invalid parameter(s).\n")); Status = WN_BAD_VALUE; goto EXIT; } // Find the NT devive path if (QueryDosDevice(lpLocalName, TargetPath, sizeof(TargetPath)/sizeof(WCHAR) - 1)) { ConnectionName.Length = wcslen(TargetPath) * sizeof(WCHAR); ConnectionName.MaximumLength = ConnectionName.Length + sizeof(WCHAR); ConnectionName.Buffer = TargetPath; } else { Status = GetLastError(); DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, querydosdevice failed, %x\n", Status)); goto EXIT; } // Check if this connection belongs to rdpdr if (wcsstr(TargetPath, RDPDR_DEVICE_NAME_U) != NULL) { Status = OpenConnection( &ConnectionName, FILE_OPEN, (FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT), NULL, 0, &ConnectionHandle); if (Status == WN_SUCCESS) { // Request the driver to retrieve the connection entry info Rrp.SessionId = NtCurrentPeb()->SessionId; Rrp.Parameters.Get.ResumeHandle = 0; DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, call DrDeviceControlGetInfo\n")); // // Make the request to the Redirector // if (DrDeviceControlGetInfo( ConnectionHandle, FSCTL_DR_GET_CONNECTION_INFO, &Rrp, sizeof(RDPDR_REQUEST_PACKET), (LPBYTE *) &Buffer, MAXULONG, 0, NULL ) == WN_SUCCESS) { UNICODE_STRING RemoteName; ConnectionInfo = (PRDPDR_CONNECTION_INFO)Buffer; RemoteName.Length = ConnectionInfo->RemoteName.Length; RemoteName.MaximumLength = ConnectionInfo->RemoteName.MaximumLength; RemoteName.Buffer = (PWCHAR)((PCHAR)(ConnectionInfo) + ConnectionInfo->RemoteName.BufferOffset); if (*lpBufferSize > RemoteName.Length) { *lpBufferSize = RemoteName.Length + sizeof(WCHAR); RtlCopyMemory( lpRemoteName, RemoteName.Buffer, RemoteName.Length); lpRemoteName[RemoteName.Length/sizeof(WCHAR)] = L'\0'; DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, remote name %ws\n", lpRemoteName)); Status = WN_SUCCESS; goto EXIT; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, buffer too small\n")); *lpBufferSize = RemoteName.Length + sizeof(WCHAR); Status = WN_MORE_DATA; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, DrDeviceControlGetInfo failed\n")); Status = WN_BAD_NETNAME; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, OpenConnection failed, %x\n", Status)); ConnectionHandle = INVALID_HANDLE_VALUE; Status = WN_BAD_NETNAME; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, DrDeviceControlGetInfo failed\n")); Status = WN_BAD_NETNAME; goto EXIT; } EXIT: if (ConnectionHandle != INVALID_HANDLE_VALUE) { NtClose(ConnectionHandle); } if (Buffer != NULL) { MemFree(Buffer); } DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection, return status: %x\n", Status)); return Status; } DWORD APIENTRY NPGetResourceParent( LPNETRESOURCE lpNetResource, LPVOID lpBuffer, LPDWORD lpBufferSize ) /*++ Routine Description: This routine returns the parent of a given resource Arguments: lpNetResource - the NETRESOURCE struct lpBuffer - the buffer for passing back the parent information lpBufferSize - the buffer size Return Value: WN_NOT_SUPPORTED Notes: The current sample does not handle this call. --*/ { // // JOYC: Need to support this? // DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceParent.\n")); return WN_NOT_SUPPORTED; } DWORD APIENTRY NPGetResourceInformation( LPNETRESOURCE lpNetResource, LPVOID lpBuffer, LPDWORD lpBufferSize, LPWSTR *lplpSystem ) /*++ Routine Description: This routine returns the information associated net resource Arguments: lpNetResource - the NETRESOURCE struct lpBuffer - the buffer for passing back the resource information lpBufferSize - the buffer size lplpSystem - Return Value: Notes: --*/ { DWORD Status = 0; LPNETRESOURCE pOutNetResource; UNICODE_STRING RemoteName; UNICODE_STRING SystemPath; BOOL fResourceTypeDisk = FALSE ; WORD wSlashCount = 0; BYTE *BufferResourceStart, *BufferResourceEnd; PWCHAR pCurPos; DBGMSG(DBG_TRACE, ("DRPROV: NPGetConnection.\n")); RemoteName.Buffer = NULL; RemoteName.Length = RemoteName.MaximumLength = 0; if (lpBuffer == NULL || lpBufferSize == NULL) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, invalid parameter(s).\n")); Status = WN_BAD_VALUE; goto EXIT; } pOutNetResource = (LPNETRESOURCE)lpBuffer; BufferResourceStart = (PBYTE)lpBuffer; BufferResourceEnd = ((PBYTE)(pOutNetResource)) + *lpBufferSize; SystemPath.Buffer = NULL; SystemPath.Length = SystemPath.MaximumLength = 0; // // JOYC: Do we need to check if we are the right provider from lpProvider? // And the dwType? // if (lpNetResource == NULL || lpNetResource->lpRemoteName == NULL) { if (*lpBufferSize >= sizeof(NETRESOURCEW)) { // // Handle this as if we are at the root of our provider hierarchy. // pOutNetResource->dwScope = RESOURCE_GLOBALNET; pOutNetResource->dwType = RESOURCETYPE_ANY; pOutNetResource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK; pOutNetResource->dwUsage = RESOURCEUSAGE_CONTAINER; pOutNetResource->lpLocalName = NULL; pOutNetResource->lpRemoteName = NULL; // JOYC: need to set this to our provider? pOutNetResource->lpProvider = NULL; pOutNetResource->lpComment = NULL; *lpBufferSize = sizeof(NETRESOURCEW); if (lplpSystem) { *lplpSystem = NULL; } DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, NULL remote Name\n")); Status = WN_SUCCESS; goto EXIT; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, buffer too small.\n")); *lpBufferSize = sizeof(NETRESOURCEW); Status = WN_MORE_DATA; goto EXIT; } } // // Find out if we are looking at a \\server, \\server\vol, or // \\server\vol\dir . . . // wSlashCount = 0; pCurPos = lpNetResource->lpRemoteName; while (*pCurPos != '\0') { if (*pCurPos == L'\\') { wSlashCount++; } // Get the system path if (wSlashCount == 4) { SystemPath.Buffer = pCurPos; SystemPath.Length = (USHORT) (wcslen(lpNetResource->lpRemoteName) * sizeof(WCHAR) - (SystemPath.Buffer - lpNetResource->lpRemoteName) * sizeof(WCHAR)); SystemPath.MaximumLength = SystemPath.Length + sizeof(WCHAR); break; } pCurPos++; } if ( wSlashCount > 2 ) fResourceTypeDisk = TRUE; // // Open a connection handle to \\server\vol\... // // Setup remote name pCurPos = lpNetResource->lpRemoteName; if (SystemPath.Length != 0) { RemoteName.Length = (USHORT)((SystemPath.Buffer - pCurPos) * sizeof(WCHAR)); RemoteName.MaximumLength = RemoteName.Length + sizeof(WCHAR); } else { RemoteName.Length = wcslen(pCurPos) * sizeof(WCHAR); RemoteName.MaximumLength = RemoteName.Length + sizeof(WCHAR); } RemoteName.Buffer = MemAlloc(RemoteName.MaximumLength); if (RemoteName.Buffer == NULL) { Status = GetLastError(); DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, MemAlloc failed.\n")); goto EXIT; } RtlCopyMemory(RemoteName.Buffer, pCurPos, RemoteName.Length); RemoteName.Buffer[RemoteName.Length/sizeof(WCHAR)] = L'\0'; DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, RemoteName, %ws\n", RemoteName.Buffer)); if (fResourceTypeDisk) { UNICODE_STRING ConnectionName; HANDLE ConnectionHandle; ConnectionName.Buffer = NULL; ConnectionName.Length = 0; ConnectionHandle = INVALID_HANDLE_VALUE; // Setup the NT Device Name Status = CreateConnectionName(NULL, RemoteName.Buffer, &ConnectionName); if (Status == WN_SUCCESS) { Status = OpenConnection(&ConnectionName, FILE_OPEN, (FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT), NULL, 0, &ConnectionHandle); if (ConnectionName.Buffer != NULL) { MemFree(ConnectionName.Buffer); } if (Status == WN_SUCCESS) { CloseHandle(ConnectionHandle); } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, OpenConnection failed")); goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, CreateConnectionName failed\n")); goto EXIT; } } else { RDPDR_REQUEST_PACKET Rrp; // Redirector request packet HANDLE DrDeviceHandle = 0; PRDPDR_SERVER_INFO pServerEntry; LPBYTE Buffer = NULL; UNICODE_STRING ServerName; if (DrOpenMiniRdr(&DrDeviceHandle) != WN_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, failed to Open rdpdr\n")); Status = WN_BAD_NETNAME; goto EXIT; } // // Ask the redirector to enumerate the information of server // established by the caller. // Rrp.SessionId = NtCurrentPeb()->SessionId; Rrp.Parameters.Get.ResumeHandle = 0; // // Make the request to the Redirector // Status = DrDeviceControlGetInfo( DrDeviceHandle, FSCTL_DR_ENUMERATE_SERVERS, &Rrp, sizeof(RDPDR_REQUEST_PACKET), (LPBYTE *) &Buffer, MAXULONG, 0, NULL); CloseHandle(DrDeviceHandle); if (Status != WN_SUCCESS) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, DrDeviceControlGetInfo failed\n")); Status = WN_BAD_NETNAME; goto EXIT; } pServerEntry = ((PRDPDR_SERVER_INFO) Buffer); ServerName.Length = pServerEntry->ServerName.Length; ServerName.MaximumLength = pServerEntry->ServerName.MaximumLength; ServerName.Buffer = (PWCHAR)((PCHAR)(pServerEntry) + pServerEntry->ServerName.BufferOffset); if ((RemoteName.Length == ServerName.Length) && _wcsnicmp(RemoteName.Buffer, ServerName.Buffer, ServerName.Length/sizeof(WCHAR)) == 0) { if (Buffer != NULL) { MemFree(Buffer); } Status = WN_SUCCESS; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, invalid net name, %wZ\n", RemoteName)); if (Buffer != NULL) { MemFree(Buffer); } Status = WN_BAD_NETNAME; goto EXIT; } } if (Status == WN_SUCCESS) { // // The resource exists, setup info. // *lpBufferSize = sizeof(NETRESOURCEW) + RemoteName.Length + sizeof(WCHAR) + DrProviderName.Length + sizeof(WCHAR) + SystemPath.Length + sizeof(WCHAR); if ((unsigned) (BufferResourceEnd - BufferResourceStart) > *lpBufferSize) { pOutNetResource->dwScope = 0; pOutNetResource->dwType = fResourceTypeDisk ? RESOURCETYPE_DISK : RESOURCETYPE_ANY; pOutNetResource->dwDisplayType = fResourceTypeDisk ? RESOURCEDISPLAYTYPE_SHARE : RESOURCEDISPLAYTYPE_SERVER; pOutNetResource->dwUsage = fResourceTypeDisk ? RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE : RESOURCEUSAGE_CONTAINER; pOutNetResource->lpLocalName = NULL; // Setup remote name BufferResourceEnd -= RemoteName.Length + sizeof(WCHAR); pOutNetResource->lpRemoteName = (PWCHAR) (BufferResourceStart + sizeof(NETRESOURCE)); RtlCopyMemory(pOutNetResource->lpRemoteName, RemoteName.Buffer, RemoteName.Length); pOutNetResource->lpRemoteName[RemoteName.Length / sizeof(WCHAR)] = L'\0'; DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, RemoteName, %ws\n", pOutNetResource->lpRemoteName)); // Setup provider name BufferResourceEnd -= DrProviderName.Length + sizeof(WCHAR); pOutNetResource->lpProvider = (PWCHAR) ((PBYTE)(pOutNetResource->lpRemoteName) + RemoteName.Length + sizeof(WCHAR)); RtlCopyMemory(pOutNetResource->lpProvider, DrProviderName.Buffer, DrProviderName.Length); pOutNetResource->lpProvider[DrProviderName.Length / sizeof(WCHAR)] = L'\0'; pOutNetResource->lpComment = NULL; // Setup system path if (lplpSystem) { if (SystemPath.Length) { BufferResourceEnd -= SystemPath.Length + sizeof(WCHAR); *lplpSystem = (PWCHAR) ((PBYTE)(pOutNetResource->lpProvider) + DrProviderName.Length + sizeof(WCHAR)); RtlCopyMemory(*lplpSystem, SystemPath.Buffer, SystemPath.Length); (*lplpSystem)[SystemPath.Length / sizeof(WCHAR)] = L'\0'; DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, SystemPath, %ws\n", *lplpSystem)); } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, SystemPath null\n")); *lplpSystem = NULL; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, user doesn't require systempath\n")); } Status = WN_SUCCESS; goto EXIT; } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInfo, buffer too small\n")); Status = WN_MORE_DATA; goto EXIT; } } else { DBGMSG(DBG_TRACE, ("DRPROV: NPGetResourceInformation, bad net name.\n")); Status = WN_BAD_NETNAME; goto EXIT; } EXIT: if (RemoteName.Buffer != NULL) { MemFree(RemoteName.Buffer); } return Status; } DWORD APIENTRY NPGetUser( IN LPTSTR lpName, OUT LPTSTR lpUserName, IN OUT LPDWORD lpBufferSize ) /*++ Routine Description: This function determines the user name that created the connection. Arguments: lpName - Name of the local drive or the remote name that the user has made a connection to. If NULL, return currently logged on user. lpUserName - The buffer to be filled in with the requested user name. lpBufferSize - Contains the length (in chars not bytes )of the lpUserName buffer. If the length is insufficient, this place is used to inform the user the actual length needed. Return Value: WN_SUCCESS - Successful. OR The appropriate network error code. --*/ { DWORD Status = WN_SUCCESS; WCHAR NameBuffer[USERNAMELEN + 1]; DWORD NumOfChars = USERNAMELEN + 1; DBGMSG(DBG_TRACE, ("DRPROV: NPGetUser.\n")); if (lpUserName == NULL || lpBufferSize == NULL) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetUser, invalid parameter(s)\n")); Status = WN_BAD_VALUE; goto EXIT; } // // Get the name of the currently logged on user. // if (!GetUserName( NameBuffer, &(NumOfChars))) { Status = GetLastError(); DBGMSG(DBG_TRACE, ("DRPROV: NPGetUser, failed to get user name, %x\n", Status)); goto EXIT; } // // Check to see if the buffer passed in is of the required length. // if ( *lpBufferSize < NumOfChars ) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetUser, buffer too small.\n")); *lpBufferSize = NumOfChars; Status = WN_MORE_DATA; goto EXIT; } // // Copy the user name. // wcscpy(lpUserName, NameBuffer); EXIT: DBGMSG(DBG_TRACE, ("DRPROV: NPGetUser, return status: %x\n", Status)); return Status; } DWORD APIENTRY NPGetUniversalName( LPCWSTR lpLocalPath, DWORD dwInfoLevel, LPVOID lpBuffer, LPDWORD lpBufferSize ) /*++ Routine Description: This routine returns the information associated net resource Arguments: lpLocalPath - the local path name dwInfoLevel - the desired info level lpBuffer - the buffer for the univeral name lpBufferSize - the buffer size Return Value: WN_SUCCESS if successful Notes: --*/ { DWORD Status = WN_SUCCESS; DWORD BufferRequired = 0; DWORD UniversalNameLength = 0; DWORD RemoteNameLength = 0; DWORD RemainingPathLength = 0; LPWSTR pDriveLetter, pRemainingPath, SourceStrings[3]; WCHAR RemoteName[MAX_PATH], LocalPath[MAX_PATH], UniversalName[MAX_PATH], ReplacedChar; DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName\n")); // parameter checking if (dwInfoLevel != UNIVERSAL_NAME_INFO_LEVEL && dwInfoLevel != REMOTE_NAME_INFO_LEVEL) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, bad InfoLevel, %d\n", dwInfoLevel)); Status = WN_BAD_LEVEL; goto EXIT; } if (lpLocalPath == NULL || lpBuffer == NULL || lpBufferSize == NULL) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, invalid parameter(s)\n")); Status = WN_BAD_VALUE; goto EXIT; } // Get the local name wcscpy(LocalPath, lpLocalPath); pDriveLetter = LocalPath; if (pRemainingPath = wcschr(pDriveLetter, L':')) { ReplacedChar = *(++pRemainingPath); *pRemainingPath = L'\0'; } // Get the remote name by calling NPGetConnection if ((Status = NPGetConnection(pDriveLetter, RemoteName, &RemoteNameLength)) != WN_SUCCESS) { // MPR expects WN_BAD_LOCALNAME to bypass us. if (Status == WN_BAD_NETNAME) { Status = WN_BAD_LOCALNAME; } DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, NPGetConnection failed\n")); goto EXIT; } if (pRemainingPath) { *pRemainingPath = ReplacedChar; } wcscpy(UniversalName, RemoteName); if (pRemainingPath) wcscat(UniversalName, pRemainingPath); // Determine if the provided buffer is large enough. UniversalNameLength = (wcslen(UniversalName) + 1) * sizeof(WCHAR); BufferRequired = UniversalNameLength; if (dwInfoLevel == UNIVERSAL_NAME_INFO_LEVEL) { BufferRequired += sizeof(UNIVERSAL_NAME_INFO); } else { RemoteNameLength = (wcslen(RemoteName) + 1) * sizeof(WCHAR); BufferRequired += sizeof(REMOTE_NAME_INFO) + RemoteNameLength; if (pRemainingPath) { RemainingPathLength = (wcslen(pRemainingPath) + 1) * sizeof(WCHAR); BufferRequired += RemainingPathLength; } } if (*lpBufferSize < BufferRequired) { DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, buffer too small\n")); *lpBufferSize = BufferRequired; Status = WN_MORE_DATA; goto EXIT; } if (dwInfoLevel == UNIVERSAL_NAME_INFO_LEVEL) { LPUNIVERSAL_NAME_INFOW pUniversalNameInfo; pUniversalNameInfo = (LPUNIVERSAL_NAME_INFOW)lpBuffer; pUniversalNameInfo->lpUniversalName = (PWCHAR)((PBYTE)lpBuffer + sizeof(UNIVERSAL_NAME_INFOW)); RtlCopyMemory( pUniversalNameInfo->lpUniversalName, UniversalName, UniversalNameLength); DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, UniversalName, %ws\n", UniversalName)); Status = WN_SUCCESS; } else { LPREMOTE_NAME_INFOW pRemoteNameInfo; pRemoteNameInfo = (LPREMOTE_NAME_INFOW)lpBuffer; pRemoteNameInfo->lpUniversalName = (PWCHAR)((PBYTE)lpBuffer + sizeof(REMOTE_NAME_INFOW)); pRemoteNameInfo->lpConnectionName = pRemoteNameInfo->lpUniversalName + UniversalNameLength; pRemoteNameInfo->lpRemainingPath = pRemoteNameInfo->lpConnectionName + RemoteNameLength; RtlCopyMemory( pRemoteNameInfo->lpUniversalName, UniversalName, UniversalNameLength); RtlCopyMemory( pRemoteNameInfo->lpConnectionName, RemoteName, RemoteNameLength); RtlCopyMemory( pRemoteNameInfo->lpRemainingPath, pRemainingPath, RemainingPathLength); DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, UniversalName, %ws\n", UniversalName)); DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, RemoteName, %ws\n", RemoteName)); DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, Remaining Path, %ws\n", pRemainingPath)); Status = WN_SUCCESS; } EXIT: DBGMSG(DBG_TRACE, ("DRPROV: NPGetUniversalName, return status, %x\n", Status)); return Status; }