You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1504 lines
45 KiB
1504 lines
45 KiB
/*++
|
|
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
drioctl.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements IOCTL handling specific to the Dr (as opposed to
|
|
the devices it redirects). This includes rdpwsx notification for clients
|
|
coming and going, and start/stop service requests.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#define TRC_FILE "drioctl"
|
|
#include "trc.h"
|
|
|
|
#include <kernutil.h>
|
|
#include <rdpdr.h>
|
|
#include <rdpnp.h>
|
|
|
|
#define DR_STARTABLE 0
|
|
#define DR_STARTING 1
|
|
#define DR_STARTED 2
|
|
|
|
extern PRDBSS_DEVICE_OBJECT DrDeviceObject;
|
|
#define RxNetNameTable (*(DrDeviceObject->pRxNetNameTable))
|
|
|
|
LONG DrStartStatus = DR_STARTABLE;
|
|
|
|
NTSTATUS
|
|
DrDevFcbXXXControlFile (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles all the device FCB related FSCTL's in the mini rdr.
|
|
Which is to say this handles IOCTLs for this driver instead of what we're
|
|
redirecting.
|
|
|
|
Arguments:
|
|
|
|
RxContext - Describes the Fsctl and Context.
|
|
|
|
Return Value:
|
|
|
|
a valid NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFobx;
|
|
UCHAR MajorFunctionCode = RxContext->MajorFunction;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
ULONG ControlCode = LowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
|
|
BEGIN_FN("DrDevFcbXXXControlFile");
|
|
|
|
switch (MajorFunctionCode) {
|
|
case IRP_MJ_FILE_SYSTEM_CONTROL:
|
|
{
|
|
switch (LowIoContext->ParamsFor.FsCtl.MinorFunction) {
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
switch (ControlCode) {
|
|
|
|
case FSCTL_DR_ENUMERATE_CONNECTIONS:
|
|
{
|
|
Status = DrEnumerateConnections(RxContext);
|
|
}
|
|
break;
|
|
|
|
case FSCTL_DR_ENUMERATE_SHARES:
|
|
{
|
|
Status = DrEnumerateShares(RxContext);
|
|
}
|
|
break;
|
|
|
|
case FSCTL_DR_ENUMERATE_SERVERS:
|
|
{
|
|
Status = DrEnumerateServers(RxContext);
|
|
}
|
|
break;
|
|
|
|
case FSCTL_DR_GET_CONNECTION_INFO:
|
|
if (capFobx) {
|
|
Status = DrGetConnectionInfo(RxContext);
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
break;
|
|
|
|
case FSCTL_DR_DELETE_CONNECTION:
|
|
if (capFobx) {
|
|
Status = DrDeleteConnection(RxContext, &RxContext->PostRequest);
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
RxContext->pFobx = NULL;
|
|
}
|
|
break;
|
|
default : //minor function != IRP_MN_USER_FS_REQUEST
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
RxContext->pFobx = NULL;
|
|
}
|
|
} // FSCTL case
|
|
break;
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
switch (LowIoContext->ParamsFor.FsCtl.IoControlCode) {
|
|
case IOCTL_CHANNEL_CONNECT:
|
|
Status = DrOnSessionConnect(RxContext);
|
|
break;
|
|
|
|
case IOCTL_CHANNEL_DISCONNECT:
|
|
Status = DrOnSessionDisconnect(RxContext);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
RxContext->pFobx = NULL;
|
|
}
|
|
break;
|
|
|
|
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
|
|
{
|
|
// warning C4065: switch statement contains 'default' but no 'case' labels
|
|
//switch (ControlCode) {
|
|
//default :
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
RxContext->pFobx = NULL;
|
|
//}
|
|
}
|
|
break;
|
|
default:
|
|
TRC_ASSERT(FALSE, (TB, "unimplemented major function"));
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
RxContext->pFobx = NULL;
|
|
}
|
|
|
|
TRC_NRM((TB, "MRxIfsDevFcb st,info=%08lx,%08lx",
|
|
Status,RxContext->InformationToReturn));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
DrOnSessionConnect(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Called when we a session is connected for the first time. Searches the
|
|
list of virtual channels for our channel name, and opens the channel if
|
|
it is found.
|
|
|
|
Arguments:
|
|
RxContext - Context information about the IOCTL call
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS - Successful operation
|
|
STATUS_INSUFFICIENT_RESOURCES - Out of memory
|
|
|
|
--*/
|
|
{
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
PCHANNEL_CONNECT_IN ConnectIn =
|
|
(PCHANNEL_CONNECT_IN)LowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
PCHANNEL_CONNECT_OUT ConnectOut =
|
|
(PCHANNEL_CONNECT_OUT)LowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
PCHANNEL_CONNECT_DEF Channels = (PCHANNEL_CONNECT_DEF)(ConnectIn + 1);
|
|
|
|
BEGIN_FN("DrOnSessionConnect");
|
|
|
|
__try {
|
|
|
|
ProbeForRead(ConnectIn, sizeof(CHANNEL_CONNECT_IN), sizeof(BYTE));
|
|
ProbeForWrite(ConnectOut, sizeof(CHANNEL_CONNECT_OUT), sizeof(BYTE));
|
|
|
|
TRC_ASSERT(ConnectIn != NULL, (TB, "ConnectIn != NULL"));
|
|
TRC_NRM((TB, "Session ID %ld", ConnectIn->hdr.sessionID));
|
|
|
|
//
|
|
// Basic parameter validation
|
|
//
|
|
|
|
if ((LowIoContext->ParamsFor.FsCtl.pInputBuffer == NULL) ||
|
|
(LowIoContext->ParamsFor.FsCtl.InputBufferLength < sizeof(CHANNEL_CONNECT_IN)) ||
|
|
(LowIoContext->ParamsFor.FsCtl.OutputBufferLength < sizeof(UINT_PTR)) ||
|
|
(LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL)) {
|
|
|
|
TRC_ERR((TB, "Received invalid pramater for SessionCreate IOCTL"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Make sure the Minirdr is started
|
|
//
|
|
|
|
DrStartMinirdr(RxContext);
|
|
|
|
ASSERT(Sessions != NULL);
|
|
Sessions->OnConnect(ConnectIn, ConnectOut);
|
|
|
|
// While we may have sadly failed somewhere along the way, if we want
|
|
// rdpwsx to save our context out, we must return STATUS_SUCCESS
|
|
return STATUS_SUCCESS;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
TRC_NRM((TB, "Error accessing buffer in DrOnSessionConnect"));
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
DrOnSessionDisconnect(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Called when we a session is ended. Searches the list of clients, and
|
|
initiates a shutdown of each of those information sets.
|
|
|
|
Arguments:
|
|
RxContext - Context information about the IOCTL call
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS - Successful operation
|
|
STATUS_INSUFFICIENT_RESOURCES - Out of memory
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
PCHANNEL_DISCONNECT_IN DisconnectIn =
|
|
(PCHANNEL_DISCONNECT_IN)LowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
PCHANNEL_DISCONNECT_OUT DisconnectOut =
|
|
(PCHANNEL_DISCONNECT_OUT)LowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
|
|
BEGIN_FN("DrOnSessionDisconnect");
|
|
|
|
__try {
|
|
|
|
ProbeForRead(DisconnectIn, sizeof(CHANNEL_DISCONNECT_IN), sizeof(BYTE));
|
|
ProbeForWrite(DisconnectOut, sizeof(CHANNEL_DISCONNECT_OUT), sizeof(BYTE));
|
|
|
|
//
|
|
// Basic parameter validation
|
|
//
|
|
|
|
if ((LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL) ||
|
|
(LowIoContext->ParamsFor.FsCtl.InputBufferLength < sizeof(CHANNEL_DISCONNECT_IN)) ||
|
|
(LowIoContext->ParamsFor.FsCtl.OutputBufferLength < sizeof(UINT_PTR)) ||
|
|
(LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL)) {
|
|
|
|
TRC_ERR((TB, "Received invalid pramater for SessionClose IOCTL"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ASSERT(Sessions != NULL);
|
|
Sessions->OnDisconnect(DisconnectIn, DisconnectOut);
|
|
|
|
// While we may have sadly failed somewhere along the way, if we want
|
|
// rdpwsx to save our context out, we must return STATUS_SUCCESS
|
|
return STATUS_SUCCESS;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
TRC_NRM((TB, "Error accessing buffer in DrOnSessionDisconnect"));
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DrStartMinirdr(
|
|
PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We use this to start the minirdr. Checks if the work is needed and
|
|
kicks off a system thread if we need to.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE ThreadHandle;
|
|
PVOID Thread = NULL;
|
|
|
|
BEGIN_FN("DrStartMinirdr");
|
|
//
|
|
// Make sure it needs to be started, and start it if we can
|
|
//
|
|
if (InterlockedCompareExchange(&DrStartStatus, DR_STARTING, DR_STARTABLE) == DR_STARTABLE) {
|
|
//
|
|
// We need to call RxStartMinirdr from the system process
|
|
//
|
|
|
|
Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, NULL,
|
|
NULL, NULL, DrStartMinirdrWorker, RxContext);
|
|
|
|
|
|
//
|
|
// Get a pointer to the thread
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_ALL_ACCESS, NULL, KernelMode, &Thread, NULL);
|
|
ZwClose(ThreadHandle);
|
|
}
|
|
|
|
//
|
|
// Wait on the thread pointer
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
KeWaitForSingleObject(Thread, UserRequest, KernelMode, FALSE, NULL);
|
|
ObfDereferenceObject(Thread);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DrStartMinirdrWorker(
|
|
IN PVOID StartContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We use this to start the minirdr. Checks if the work is needed and
|
|
kicks off a system thread if we need to.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PRX_CONTEXT RxContext2;
|
|
PRX_CONTEXT RxContext = (PRX_CONTEXT)StartContext;
|
|
|
|
BEGIN_FN("DrStartMinirdrWorker");
|
|
RxContext2 = RxCreateRxContext(
|
|
NULL,
|
|
RxContext->RxDeviceObject,
|
|
RX_CONTEXT_FLAG_IN_FSP);
|
|
|
|
//
|
|
// Start Redirecting
|
|
//
|
|
if (RxContext2 != NULL) {
|
|
Status = RxStartMinirdr(RxContext2, &RxContext2->PostRequest);
|
|
|
|
TRC_NRM((TB, "RxStartMinirdr returned: %lx", Status));
|
|
|
|
RxDereferenceAndDeleteRxContext(RxContext2);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
InterlockedExchange(&DrStartStatus, DR_STARTED);
|
|
} else {
|
|
InterlockedCompareExchange(&DrStartStatus, DR_STARTABLE,
|
|
DR_STARTING);
|
|
}
|
|
|
|
PsTerminateSystemThread(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DrDeleteConnection (
|
|
IN PRX_CONTEXT RxContext,
|
|
OUT PBOOLEAN PostToFsp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes a single vnetroot.
|
|
|
|
Arguments:
|
|
|
|
IN PRX_CONTEXT RxContext - Describes the Fsctl and Context.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
RxCaptureFobx;
|
|
PNET_ROOT NetRoot;
|
|
PV_NET_ROOT VNetRoot;
|
|
|
|
BEGIN_FN("DrDeleteConnection");
|
|
|
|
BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
|
|
|
|
TRC_NRM((TB, "Request DrDeleteConnection"));
|
|
|
|
if (!Wait) {
|
|
TRC_NRM((TB, "WAIT flag is not on for DeleteConnection"));
|
|
|
|
//just post right now!
|
|
*PostToFsp = TRUE;
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
__try
|
|
{
|
|
if (NodeType(capFobx)==RDBSS_NTC_V_NETROOT) {
|
|
VNetRoot = (PV_NET_ROOT)capFobx;
|
|
NetRoot = (PNET_ROOT)((PMRX_V_NET_ROOT)VNetRoot->pNetRoot);
|
|
} else {
|
|
TRC_ASSERT((FALSE), (TB, "Not VNet Root"));
|
|
try_return(Status = STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
Status = RxFinalizeConnection(NetRoot,VNetRoot,TRUE);
|
|
TRC_NRM((TB, "RxFinalizeConnection returned %lx", Status));
|
|
|
|
try_return(Status);
|
|
|
|
try_exit:NOTHING;
|
|
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
TRC_NRM((TB, "Error accessing capFobx in DrDeleteConnection"));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
DrPackStringIntoInfoBuffer(
|
|
IN OUT PRDPDR_UNICODE_STRING String,
|
|
IN PUNICODE_STRING Source,
|
|
IN PCHAR BufferStart,
|
|
IN OUT PCHAR * BufferEnd,
|
|
IN ULONG BufferDisplacement,
|
|
IN OUT PULONG TotalBytes
|
|
)
|
|
/*
|
|
|
|
Routine Description:
|
|
|
|
This code copies a string to the end of the buffer IF THERE'S ROOM. the buffer
|
|
displacement is used to map the buffer back into the user's space in case we
|
|
have posted.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
*/
|
|
{
|
|
LONG size;
|
|
|
|
BEGIN_FN("DrPackStringIntoInfoBuffer");
|
|
|
|
TRC_ASSERT((BufferStart <= *BufferEnd),
|
|
(TB, "Invalid BufferStart %p, Buffer End %p", BufferStart, *BufferEnd));
|
|
|
|
//
|
|
// is there room for the string?
|
|
//
|
|
size = Source->Length;
|
|
|
|
if ((*BufferEnd - BufferStart) < size) {
|
|
String->Length = 0;
|
|
return FALSE;
|
|
} else {
|
|
//
|
|
// Copy the source string to the end of the buffer and store
|
|
// the buffer pointer in output string accordingly
|
|
//
|
|
String->Length = Source->Length;
|
|
String->MaximumLength = Source->Length;
|
|
|
|
*BufferEnd -= size;
|
|
if (TotalBytes != NULL) { *TotalBytes += size; }
|
|
RtlCopyMemory(*BufferEnd, Source->Buffer, size);
|
|
String->BufferOffset = (LONG)((LONG_PTR)((PCHAR)(*BufferEnd) - (PCHAR)(BufferStart)));
|
|
String->BufferOffset = String->BufferOffset - BufferDisplacement;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
DrPackConnectEntry (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PCHAR *BufferStart,
|
|
IN OUT PCHAR *BufferEnd,
|
|
IN PV_NET_ROOT VNetRoot,
|
|
IN OUT ULONG BufferDisplacement,
|
|
OUT PULONG TotalBytesNeeded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine packs a connectlistentry into the buffer provided updating
|
|
all relevant pointers. The way that this works is that constant length stuff is
|
|
copied to the front of the buffer and variable length stuff to the end. The
|
|
"start and end" pointers are updated. You have to calculate the totalbytes correctly
|
|
no matter what but a last can be setup incompletely as long as you return false.
|
|
|
|
the way that this works is that it calls down into the minirdr on the devfcb
|
|
interface. it calls down twice and passes a structure back and forth thru the
|
|
context to maintain state.
|
|
|
|
Arguments:
|
|
|
|
IN OUT PCHAR *BufferStart - Supplies the output buffer.
|
|
Updated to point to the next buffer
|
|
|
|
IN OUT PCHAR *BufferEnd - Supplies the end of the buffer. Updated to
|
|
point before the start of the strings being packed.
|
|
|
|
IN PVNET_ROOT NetRoot - Supplies the VNetRoot to enumerate.
|
|
|
|
IN OUT PULONG TotalBytesNeeded - Updated to account for the length of this
|
|
entry
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - True if the entry was successfully packed into the buffer.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN ReturnValue = TRUE;
|
|
|
|
UNICODE_STRING Name;
|
|
ULONG BufferSize;
|
|
PRDPDR_CONNECTION_INFO ConnectionInfo = (PRDPDR_CONNECTION_INFO)*BufferStart;
|
|
PNET_ROOT NetRoot = (PNET_ROOT)(((PMRX_V_NET_ROOT)VNetRoot)->pNetRoot);
|
|
PUNICODE_STRING VNetRootName = &VNetRoot->PrefixEntry.Prefix;
|
|
PCHAR ConnectEntryStart;
|
|
|
|
BEGIN_FN("DrPackConnectEntry");
|
|
|
|
//
|
|
// We want the connection name to have string null terminator
|
|
//
|
|
Name.Buffer = (PWCHAR)RxAllocatePoolWithTag(NonPagedPool,
|
|
MAX_PATH, DR_POOLTAG);
|
|
|
|
if ( Name.Buffer == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
BufferSize = sizeof(RDPDR_CONNECTION_INFO);
|
|
ConnectEntryStart = *BufferStart;
|
|
|
|
__try {
|
|
//
|
|
// Account for the constant length stuff
|
|
//
|
|
*BufferStart = ((PCHAR)*BufferStart) + BufferSize;
|
|
*TotalBytesNeeded += BufferSize;
|
|
|
|
//
|
|
// Initialize the name to "\" then add in the rest of the connection name
|
|
//
|
|
|
|
Name.Length = NetRoot->PrefixEntry.Prefix.Length + sizeof(WCHAR);
|
|
Name.MaximumLength = Name.Length;
|
|
|
|
ASSERT(Name.Length <= MAX_PATH);
|
|
|
|
Name.Buffer[0] = L'\\';
|
|
RtlCopyMemory(&Name.Buffer[1], NetRoot->PrefixEntry.Prefix.Buffer,
|
|
NetRoot->PrefixEntry.Prefix.Length);
|
|
|
|
//
|
|
// Update the total number of bytes needed for this structure.
|
|
//
|
|
|
|
*TotalBytesNeeded += Name.Length;
|
|
|
|
if (*BufferStart > *BufferEnd) {
|
|
try_return(ReturnValue = FALSE);
|
|
}
|
|
|
|
if ((*BufferEnd - *BufferStart) < Name.Length) {
|
|
ConnectionInfo->RemoteName.Length = 0;
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
else if (!DrPackStringIntoInfoBuffer(
|
|
&ConnectionInfo->RemoteName,
|
|
&Name,
|
|
ConnectEntryStart,
|
|
BufferEnd,
|
|
BufferDisplacement,
|
|
NULL)) {
|
|
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
|
|
//
|
|
// Setup the local name
|
|
//
|
|
if (VNetRootName->Buffer[2] != L':') {
|
|
Name.Buffer[0] = towupper(VNetRootName->Buffer[2]);
|
|
Name.Buffer[1] = L':';
|
|
Name.Buffer[2] = L'\0';
|
|
Name.Length = sizeof(WCHAR) * 2;
|
|
Name.MaximumLength = Name.Length;
|
|
|
|
//
|
|
// Update the total number of bytes needed for this structure.
|
|
//
|
|
|
|
*TotalBytesNeeded += Name.Length;
|
|
|
|
if (*BufferStart > *BufferEnd) {
|
|
try_return(ReturnValue = FALSE);
|
|
}
|
|
|
|
|
|
if ((*BufferEnd - *BufferStart) < Name.Length) {
|
|
ConnectionInfo->LocalName.Length = 0;
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
else if (!DrPackStringIntoInfoBuffer(
|
|
&ConnectionInfo->LocalName,
|
|
&Name,
|
|
ConnectEntryStart,
|
|
BufferEnd,
|
|
BufferDisplacement,
|
|
NULL)) {
|
|
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
}
|
|
else {
|
|
ConnectionInfo->LocalName.Length = 0;
|
|
ConnectionInfo->LocalName.BufferOffset = 0;
|
|
}
|
|
|
|
ConnectionInfo->ResumeKey = NetRoot->SerialNumberForEnum;
|
|
ConnectionInfo->SharedResourceType = NetRoot->DeviceType;
|
|
ConnectionInfo->ConnectionStatus = NetRoot->MRxNetRootState;
|
|
ConnectionInfo->NumberFilesOpen = NetRoot->NumberOfSrvOpens;
|
|
|
|
|
|
//TRC_NRM((TB, "PackConnection data---> Remote Local Type Key Status Numfiles %wZ %wZ %08lx %08lx %08lx %08lx\n",
|
|
// &(ConnectionInfo->RemoteName),
|
|
// ConnectionInfo->LocalName.BufferOffset ? &Name : NULL,
|
|
// ConnectionInfo->SharedResourceType,
|
|
// ConnectionInfo->ResumeKey,
|
|
// ConnectionInfo->ConnectionStatus,
|
|
// ConnectionInfo->NumberFilesOpen));
|
|
|
|
try_exit:
|
|
RxFreePool(Name.Buffer);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
RxFreePool(Name.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DrEnumerateConnections (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates the connections on minirdr.
|
|
|
|
Arguments:
|
|
|
|
IN PRX_CONTEXT RxContext - Describes the Fsctl and Context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
PRDPDR_REQUEST_PACKET InputBuffer = (PRDPDR_REQUEST_PACKET)(LowIoContext->ParamsFor.FsCtl.pInputBuffer);
|
|
PCHAR OriginalOutputBuffer = (PCHAR)(LowIoContext->ParamsFor.FsCtl.pOutputBuffer);
|
|
ULONG OutputBufferLength = LowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
ULONG InputBufferLength = LowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
PCHAR OutputBuffer;
|
|
ULONG BufferDisplacement;
|
|
|
|
ULONG ResumeHandle;
|
|
|
|
PCHAR BufferStart;
|
|
PCHAR BufferEnd;
|
|
PCHAR PreviousBufferStart;
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
ULONG SessionId, IrpSessionId;
|
|
BOOLEAN TableLockHeld = FALSE;
|
|
|
|
BEGIN_FN("DrEnumerateConnections");
|
|
|
|
OutputBuffer = (PCHAR)RxMapUserBuffer( RxContext, RxContext->CurrentIrp );
|
|
BufferDisplacement = (ULONG)(OutputBuffer - OriginalOutputBuffer);
|
|
BufferStart = OutputBuffer;
|
|
BufferEnd = OutputBuffer + OutputBufferLength;
|
|
|
|
Status = IoGetRequestorSessionId(RxContext->CurrentIrp, &IrpSessionId);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if (InputBuffer == NULL || OutputBuffer == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
TRC_ASSERT((BufferDisplacement == 0),
|
|
(TB, "Request mode is not kernel, non zero Displacement"));
|
|
|
|
__try {
|
|
ProbeForWrite(InputBuffer,InputBufferLength,sizeof(UCHAR));
|
|
ProbeForWrite(OutputBuffer,OutputBufferLength,sizeof(UCHAR));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
__try {
|
|
|
|
if (InputBufferLength < sizeof(RDPDR_REQUEST_PACKET)) {
|
|
try_return(Status = STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
ResumeHandle = InputBuffer->Parameters.Get.ResumeHandle;
|
|
SessionId = InputBuffer->SessionId;
|
|
|
|
if (SessionId != IrpSessionId) {
|
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
InputBuffer->Parameters.Get.EntriesRead = 0;
|
|
InputBuffer->Parameters.Get.TotalEntries = 0;
|
|
InputBuffer->Parameters.Get.TotalBytesNeeded = 0;
|
|
|
|
RxAcquirePrefixTableLockExclusive(&RxNetNameTable, TRUE);
|
|
TableLockHeld = TRUE;
|
|
|
|
if (IsListEmpty(&RxNetNameTable.MemberQueue)) {
|
|
try_return(Status = STATUS_SUCCESS);
|
|
}
|
|
|
|
//must do the list forwards!
|
|
ListEntry = RxNetNameTable.MemberQueue.Flink;
|
|
for (;ListEntry != &RxNetNameTable.MemberQueue;) {
|
|
PVOID Container;
|
|
PRX_PREFIX_ENTRY PrefixEntry;
|
|
PNET_ROOT NetRoot;
|
|
PV_NET_ROOT VNetRoot;
|
|
PUNICODE_STRING VNetRootName;
|
|
|
|
PrefixEntry = CONTAINING_RECORD(ListEntry, RX_PREFIX_ENTRY, MemberQLinks);
|
|
ListEntry = ListEntry->Flink;
|
|
TRC_ASSERT((NodeType(PrefixEntry) == RDBSS_NTC_PREFIX_ENTRY),
|
|
(TB, "Invalid PrefixEntry type"));
|
|
Container = PrefixEntry->ContainingRecord;
|
|
|
|
switch (NodeType(Container)) {
|
|
case RDBSS_NTC_NETROOT :
|
|
continue;
|
|
|
|
case RDBSS_NTC_SRVCALL :
|
|
continue;
|
|
|
|
case RDBSS_NTC_V_NETROOT :
|
|
VNetRoot = (PV_NET_ROOT)Container;
|
|
NetRoot = (PNET_ROOT)(((PMRX_V_NET_ROOT)VNetRoot)->pNetRoot);
|
|
VNetRootName = &VNetRoot->PrefixEntry.Prefix;
|
|
|
|
TRC_NRM((TB, "SerialNum: %x, VNetRootName = %wZ, Condition = %d, SessionId = %d, IsExplicit = %d",
|
|
VNetRoot->SerialNumberForEnum,
|
|
VNetRootName,
|
|
VNetRoot->Condition,
|
|
VNetRoot->SessionId,
|
|
VNetRoot->IsExplicitConnection));
|
|
|
|
if ((VNetRoot->SerialNumberForEnum >= ResumeHandle) &&
|
|
(VNetRoot->Condition == Condition_Good) &&
|
|
(SessionId == VNetRoot->SessionId) &&
|
|
(VNetRoot->IsExplicitConnection == TRUE)) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
InputBuffer->Parameters.Get.TotalEntries ++ ;
|
|
|
|
PreviousBufferStart = BufferStart;
|
|
if (DrPackConnectEntry(RxContext,
|
|
&BufferStart,
|
|
&BufferEnd,
|
|
VNetRoot,
|
|
BufferDisplacement,
|
|
&InputBuffer->Parameters.Get.TotalBytesNeeded)) {
|
|
InputBuffer->Parameters.Get.EntriesRead ++ ;
|
|
} else {
|
|
// We want to continue the enumeration even pack connection
|
|
// entry failed, because we want to enumerate the total bytes
|
|
// needed and inform the user mode program
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
try_return(Status);
|
|
|
|
try_exit:
|
|
if (TableLockHeld) {
|
|
RxReleasePrefixTableLock( &RxNetNameTable );
|
|
TableLockHeld = FALSE;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if (TableLockHeld) {
|
|
RxReleasePrefixTableLock( &RxNetNameTable );
|
|
TableLockHeld = FALSE;
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DrGetConnectionInfo (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets the connection info for a single vnetroot.
|
|
|
|
There is some happiness here about the output buffer. What happens is that we
|
|
pick up the output buffer in the usual way. However, there are all sorts of
|
|
pointers in the return structure and these pointers must obviously be in terms
|
|
of the original process. so, if we post then we have to apply a fixup!
|
|
|
|
Arguments:
|
|
|
|
IN PRX_CONTEXT RxContext - Describes the Fsctl and Context
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
RxCaptureFobx;
|
|
|
|
PRDPDR_REQUEST_PACKET InputBuffer = (PRDPDR_REQUEST_PACKET)LowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
PCHAR OriginalOutputBuffer = (PCHAR)LowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
ULONG OutputBufferLength = LowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
ULONG InputBufferLength = LowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
PCHAR OutputBuffer;
|
|
ULONG BufferDisplacement;
|
|
|
|
PCHAR BufferStart;
|
|
PCHAR OriginalBufferStart;
|
|
PCHAR BufferEnd;
|
|
|
|
BOOLEAN TableLockHeld = FALSE;
|
|
|
|
PNET_ROOT NetRoot;
|
|
PV_NET_ROOT VNetRoot;
|
|
|
|
BEGIN_FN("DrGetConnectionInfo");
|
|
|
|
OutputBuffer = (PCHAR)RxMapUserBuffer( RxContext, RxContext->CurrentIrp );
|
|
BufferDisplacement = (ULONG)(OutputBuffer - OriginalOutputBuffer);
|
|
BufferStart = OutputBuffer;
|
|
OriginalBufferStart = BufferStart;
|
|
BufferEnd = OutputBuffer+OutputBufferLength;
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
TRC_ASSERT((BufferDisplacement == 0),
|
|
(TB, "Request mode is not kernel, non zero Displacement"));
|
|
__try {
|
|
ProbeForWrite(InputBuffer,InputBufferLength,sizeof(UCHAR));
|
|
ProbeForWrite(OutputBuffer,OutputBufferLength,sizeof(UCHAR));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
__try {
|
|
TRC_ASSERT((NodeType(capFobx)==RDBSS_NTC_V_NETROOT), (TB, "Invalid Node type"));
|
|
|
|
VNetRoot = (PV_NET_ROOT)capFobx;
|
|
NetRoot = (PNET_ROOT)((PMRX_V_NET_ROOT)VNetRoot->pNetRoot);
|
|
|
|
if (NetRoot == NULL) {
|
|
try_return(Status = STATUS_ALREADY_DISCONNECTED);
|
|
}
|
|
|
|
if (InputBufferLength < sizeof(RDPDR_REQUEST_PACKET)) {
|
|
try_return(Status = STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
InputBuffer->Parameters.Get.TotalEntries = 1;
|
|
InputBuffer->Parameters.Get.TotalBytesNeeded = 0;
|
|
|
|
RxAcquirePrefixTableLockExclusive( &RxNetNameTable, TRUE);
|
|
TableLockHeld = TRUE;
|
|
|
|
if (DrPackConnectEntry(RxContext,
|
|
&BufferStart,
|
|
&BufferEnd,
|
|
VNetRoot,
|
|
BufferDisplacement,
|
|
&InputBuffer->Parameters.Get.TotalBytesNeeded)) {
|
|
|
|
InputBuffer->Parameters.Get.EntriesRead = 1;
|
|
try_return(Status = STATUS_SUCCESS);
|
|
} else {
|
|
try_return(Status = STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
try_exit:
|
|
if (TableLockHeld) {
|
|
RxReleasePrefixTableLock( &RxNetNameTable );
|
|
TableLockHeld = FALSE;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
if (TableLockHeld) {
|
|
RxReleasePrefixTableLock( &RxNetNameTable );
|
|
TableLockHeld = FALSE;
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
DrPackShareEntry (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PCHAR *BufferStart,
|
|
IN OUT PCHAR *BufferEnd,
|
|
IN DrDevice* Device,
|
|
IN OUT ULONG BufferDisplacement,
|
|
OUT PULONG TotalBytesNeeded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine packs a sharelistentry into the buffer provided updating
|
|
all relevant pointers. The way that this works is that constant length stuff is
|
|
copied to the front of the buffer and variable length stuff to the end. The
|
|
"start and end" pointers are updated. You have to calculate the totalbytes correctly
|
|
no matter what but a last can be setup incompletely as long as you return false.
|
|
|
|
the way that this works is that it calls down into the minirdr on the devfcb
|
|
interface. it calls down twice and passes a structure back and forth thru the
|
|
context to maintain state.
|
|
|
|
Arguments:
|
|
|
|
IN OUT PCHAR *BufferStart - Supplies the output buffer.
|
|
Updated to point to the next buffer
|
|
|
|
IN OUT PCHAR *BufferEnd - Supplies the end of the buffer. Updated to
|
|
point before the start of the strings being packed.
|
|
|
|
IN PNET_ROOT NetRoot - Supplies the NetRoot to enumerate.
|
|
|
|
IN OUT PULONG TotalBytesNeeded - Updated to account for the length of this
|
|
entry
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - True if the entry was successfully packed into the buffer.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN ReturnValue = TRUE;
|
|
|
|
UNICODE_STRING ShareName; // Buffer to hold the packed name
|
|
PUCHAR DeviceDosName;
|
|
ULONG BufferSize;
|
|
PRDPDR_SHARE_INFO ShareInfo = (PRDPDR_SHARE_INFO)*BufferStart;
|
|
PCHAR ShareEntryStart;
|
|
|
|
BEGIN_FN("DrPackShareEntry");
|
|
|
|
//
|
|
// We want the connection name to have string null terminator
|
|
//
|
|
ShareName.Buffer = (PWCHAR)RxAllocatePoolWithTag(NonPagedPool,
|
|
MAX_PATH * sizeof(WCHAR), DR_POOLTAG);
|
|
|
|
if ( ShareName.Buffer == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
BufferSize = sizeof(RDPDR_SHARE_INFO);
|
|
ShareEntryStart = *BufferStart;
|
|
|
|
__try {
|
|
unsigned len, devicelen, i;
|
|
|
|
*BufferStart = ((PCHAR)*BufferStart) + BufferSize;
|
|
*TotalBytesNeeded += BufferSize;
|
|
|
|
//
|
|
// Initialize the name to "\\" then add in the rest
|
|
//
|
|
wcscpy(ShareName.Buffer, L"\\\\");
|
|
#if 0
|
|
wcscat(ServerName.Buffer, Session->GetClientName());
|
|
#endif
|
|
wcscat(ShareName.Buffer, DRUNCSERVERNAME_U);
|
|
wcscat(ShareName.Buffer, L"\\");
|
|
|
|
DeviceDosName = Device->GetDeviceDosName();
|
|
|
|
len = wcslen(ShareName.Buffer);
|
|
devicelen = strlen((char *)DeviceDosName);
|
|
|
|
for (i = 0; i < devicelen; i++) {
|
|
ShareName.Buffer[i + len] = (WCHAR) DeviceDosName[i];
|
|
}
|
|
ShareName.Buffer[i + len] = L'\0';
|
|
|
|
ShareName.Length = wcslen(ShareName.Buffer) * sizeof(WCHAR);
|
|
ShareName.MaximumLength = ShareName.Length;
|
|
|
|
ASSERT(ShareName.Length < MAX_PATH);
|
|
|
|
//
|
|
// Update the total number of bytes needed for this structure.
|
|
//
|
|
*TotalBytesNeeded += ShareName.MaximumLength;
|
|
|
|
if (*BufferStart > *BufferEnd) {
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
|
|
ShareInfo->ResumeKey = Device->GetDeviceId();
|
|
ShareInfo->SharedResourceType = RxDeviceType(DISK);
|
|
|
|
if ((*BufferEnd - *BufferStart) < ShareName.Length) {
|
|
ShareInfo->ShareName.Length = 0;
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
else if (!DrPackStringIntoInfoBuffer(
|
|
&ShareInfo->ShareName,
|
|
&ShareName,
|
|
ShareEntryStart,
|
|
BufferEnd,
|
|
BufferDisplacement,
|
|
NULL)) {
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
|
|
try_exit:
|
|
RxFreePool(ShareName.Buffer);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
RxFreePool(ShareName.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DrEnumerateShares (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates the connections on all minirdrs. we may have to do
|
|
it by minirdr.
|
|
|
|
Arguments:
|
|
|
|
IN PRX_CONTEXT RxContext - Describes the Fsctl and Context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
PRDPDR_REQUEST_PACKET InputBuffer = (PRDPDR_REQUEST_PACKET)(LowIoContext->ParamsFor.FsCtl.pInputBuffer);
|
|
PCHAR OriginalOutputBuffer = (PCHAR)(LowIoContext->ParamsFor.FsCtl.pOutputBuffer);
|
|
ULONG OutputBufferLength = LowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
ULONG InputBufferLength = LowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
PCHAR OutputBuffer;
|
|
ULONG BufferDisplacement;
|
|
|
|
ULONG ResumeHandle;
|
|
|
|
PCHAR BufferStart;
|
|
PCHAR BufferEnd;
|
|
PCHAR PreviousBufferStart;
|
|
|
|
ULONG SessionId;
|
|
SmartPtr<DrSession> Session;
|
|
|
|
BOOLEAN TableLockHeld = FALSE;
|
|
|
|
BEGIN_FN("DrEnumerateShares");
|
|
|
|
OutputBuffer = (PCHAR)RxMapUserBuffer( RxContext, RxContext->CurrentIrp );
|
|
BufferDisplacement = (ULONG)(OutputBuffer - OriginalOutputBuffer);
|
|
BufferStart = OutputBuffer;
|
|
BufferEnd = OutputBuffer+OutputBufferLength;
|
|
|
|
if (InputBuffer == NULL || OutputBuffer == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
TRC_ASSERT((BufferDisplacement == 0),
|
|
(TB, "Request mode is not kernel, non zero Displacement"));
|
|
|
|
__try {
|
|
ProbeForWrite(InputBuffer,InputBufferLength,sizeof(UCHAR));
|
|
ProbeForWrite(OutputBuffer,OutputBufferLength,sizeof(UCHAR));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
__try {
|
|
|
|
if (InputBufferLength < sizeof(RDPDR_REQUEST_PACKET)) {
|
|
try_return(Status = STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
ResumeHandle = InputBuffer->Parameters.Get.ResumeHandle;
|
|
SessionId = InputBuffer->SessionId;
|
|
|
|
InputBuffer->Parameters.Get.EntriesRead = 0;
|
|
InputBuffer->Parameters.Get.TotalEntries = 0;
|
|
InputBuffer->Parameters.Get.TotalBytesNeeded = 0;
|
|
|
|
if (Sessions->FindSessionById(SessionId, Session)) {
|
|
DrDevice *DeviceEnum;
|
|
ListEntry *ListEnum;
|
|
|
|
Session->GetDevMgr().GetDevList().LockShared();
|
|
TableLockHeld = TRUE;
|
|
|
|
ListEnum = Session->GetDevMgr().GetDevList().First();
|
|
while (ListEnum != NULL) {
|
|
|
|
DeviceEnum = (DrDevice *)ListEnum->Node();
|
|
ASSERT(DeviceEnum->IsValid());
|
|
|
|
if ((DeviceEnum->IsAvailable()) &&
|
|
(DeviceEnum->GetDeviceType() == RDPDR_DTYP_FILESYSTEM)) {
|
|
InputBuffer->Parameters.Get.TotalEntries ++ ;
|
|
|
|
PreviousBufferStart = BufferStart;
|
|
|
|
if (DrPackShareEntry(RxContext,
|
|
&BufferStart,
|
|
&BufferEnd,
|
|
DeviceEnum,
|
|
BufferDisplacement,
|
|
&InputBuffer->Parameters.Get.TotalBytesNeeded)) {
|
|
InputBuffer->Parameters.Get.EntriesRead ++ ;
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
ListEnum = Session->GetDevMgr().GetDevList().Next(ListEnum);
|
|
}
|
|
|
|
Session->GetDevMgr().GetDevList().Unlock();
|
|
TableLockHeld = FALSE;
|
|
}
|
|
|
|
try_return(Status);
|
|
|
|
try_exit:
|
|
if (TableLockHeld) {
|
|
Session->GetDevMgr().GetDevList().Unlock();
|
|
TableLockHeld = FALSE;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
if (TableLockHeld) {
|
|
Session->GetDevMgr().GetDevList().Unlock();
|
|
TableLockHeld = FALSE;
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
DrPackServerEntry (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PCHAR *BufferStart,
|
|
IN OUT PCHAR *BufferEnd,
|
|
IN DrSession* Session,
|
|
IN OUT ULONG BufferDisplacement,
|
|
OUT PULONG TotalBytesNeeded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine packs a serverlistentry into the buffer provided updating
|
|
all relevant pointers. The way that this works is that constant length stuff is
|
|
copied to the front of the buffer and variable length stuff to the end. The
|
|
"start and end" pointers are updated. You have to calculate the totalbytes correctly
|
|
no matter what but a last can be setup incompletely as long as you return false.
|
|
|
|
the way that this works is that it calls down into the minirdr on the devfcb
|
|
interface. it calls down twice and passes a structure back and forth thru the
|
|
context to maintain state.
|
|
|
|
Arguments:
|
|
|
|
IN OUT PCHAR *BufferStart - Supplies the output buffer.
|
|
Updated to point to the next buffer
|
|
|
|
IN OUT PCHAR *BufferEnd - Supplies the end of the buffer. Updated to
|
|
point before the start of the strings being packed.
|
|
|
|
IN PNET_ROOT NetRoot - Supplies the NetRoot to enumerate.
|
|
|
|
IN OUT PULONG TotalBytesNeeded - Updated to account for the length of this
|
|
entry
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - True if the entry was successfully packed into the buffer.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN ReturnValue = TRUE;
|
|
|
|
UNICODE_STRING ServerName; // Buffer to hold the packed name
|
|
ULONG BufferSize;
|
|
PRDPDR_SERVER_INFO ServerInfo = (PRDPDR_SERVER_INFO)*BufferStart;
|
|
PCHAR ServerEntryStart;
|
|
|
|
BEGIN_FN("DrPackServerEntry");
|
|
|
|
//
|
|
// We want the connection name to have string null terminator
|
|
//
|
|
ServerName.Buffer = (PWCHAR)RxAllocatePoolWithTag(NonPagedPool,
|
|
MAX_PATH * sizeof(WCHAR), DR_POOLTAG);
|
|
|
|
if (ServerName.Buffer == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
BufferSize = sizeof(RDPDR_SERVER_INFO);
|
|
ServerEntryStart = *BufferStart;
|
|
|
|
__try {
|
|
*BufferStart = ((PCHAR)*BufferStart) + BufferSize;
|
|
*TotalBytesNeeded += BufferSize;
|
|
|
|
//
|
|
// Initialize the name to "\" then add in the rest
|
|
//
|
|
wcscpy(ServerName.Buffer , L"\\\\");
|
|
#if 0
|
|
wcscat(ServerName.Buffer, Session->GetClientName());
|
|
#endif
|
|
wcscat(ServerName.Buffer, DRUNCSERVERNAME_U);
|
|
|
|
ServerName.Length = wcslen(ServerName.Buffer) * sizeof(WCHAR);
|
|
ServerName.MaximumLength = ServerName.Length;
|
|
|
|
//
|
|
// Update the total number of bytes needed for this structure.
|
|
//
|
|
|
|
*TotalBytesNeeded += ServerName.MaximumLength;
|
|
|
|
if (*BufferStart > *BufferEnd) {
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
|
|
ServerInfo->ResumeKey = 0;
|
|
|
|
if ((*BufferEnd - *BufferStart) < ServerName.Length) {
|
|
ServerInfo->ServerName.Length = 0;
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
else if (!DrPackStringIntoInfoBuffer(
|
|
&ServerInfo->ServerName,
|
|
&ServerName,
|
|
ServerEntryStart,
|
|
BufferEnd,
|
|
BufferDisplacement,
|
|
NULL)) {
|
|
|
|
try_return( ReturnValue = FALSE);
|
|
}
|
|
|
|
try_exit:
|
|
RxFreePool(ServerName.Buffer);
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
RxFreePool(ServerName.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DrEnumerateServers (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates the server name on minirdr for a session.
|
|
|
|
Arguments:
|
|
|
|
IN PRX_CONTEXT RxContext - Describes the Fsctl and Context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
PRDPDR_REQUEST_PACKET InputBuffer = (PRDPDR_REQUEST_PACKET)(LowIoContext->ParamsFor.FsCtl.pInputBuffer);
|
|
PCHAR OriginalOutputBuffer = (PCHAR)(LowIoContext->ParamsFor.FsCtl.pOutputBuffer);
|
|
ULONG OutputBufferLength = LowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
ULONG InputBufferLength = LowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
PCHAR OutputBuffer;
|
|
ULONG BufferDisplacement;
|
|
|
|
ULONG ResumeHandle;
|
|
|
|
PCHAR BufferStart;
|
|
PCHAR BufferEnd;
|
|
PCHAR PreviousBufferStart;
|
|
|
|
ULONG SessionId;
|
|
BOOLEAN TableLockHeld = FALSE;
|
|
SmartPtr<DrSession> Session;
|
|
|
|
BEGIN_FN("DrEnumerateServers");
|
|
|
|
OutputBuffer = (PCHAR)RxMapUserBuffer( RxContext, RxContext->CurrentIrp );
|
|
BufferDisplacement = (ULONG)(OutputBuffer - OriginalOutputBuffer);
|
|
BufferStart = OutputBuffer;
|
|
BufferEnd = OutputBuffer+OutputBufferLength;
|
|
|
|
if (InputBuffer == NULL || OutputBuffer == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
TRC_ASSERT((BufferDisplacement == 0),
|
|
(TB, "Request mode is not kernel, non zero Displacement"));
|
|
|
|
__try {
|
|
ProbeForWrite(InputBuffer,InputBufferLength,sizeof(UCHAR));
|
|
ProbeForWrite(OutputBuffer,OutputBufferLength,sizeof(UCHAR));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
__try {
|
|
|
|
if (InputBufferLength < sizeof(RDPDR_REQUEST_PACKET)) {
|
|
try_return(Status = STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
ResumeHandle = InputBuffer->Parameters.Get.ResumeHandle;
|
|
SessionId = InputBuffer->SessionId;
|
|
|
|
InputBuffer->Parameters.Get.EntriesRead = 0;
|
|
InputBuffer->Parameters.Get.TotalEntries = 0;
|
|
InputBuffer->Parameters.Get.TotalBytesNeeded = 0;
|
|
|
|
if (Sessions->FindSessionById(SessionId, Session)) {
|
|
InputBuffer->Parameters.Get.TotalEntries ++ ;
|
|
|
|
PreviousBufferStart = BufferStart;
|
|
|
|
if (DrPackServerEntry(RxContext,
|
|
&BufferStart,
|
|
&BufferEnd,
|
|
Session,
|
|
BufferDisplacement,
|
|
&InputBuffer->Parameters.Get.TotalBytesNeeded)) {
|
|
InputBuffer->Parameters.Get.EntriesRead ++ ;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
try_return(Status);
|
|
|
|
try_exit:NOTHING;
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|