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.
2271 lines
63 KiB
2271 lines
63 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
nptrans.cxx
|
|
|
|
Abstract:
|
|
|
|
Named pipes specific transport interface layer.
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
Revision History:
|
|
|
|
MarioGo 3/18/1996 Bits 'n pieces
|
|
MarioGo 10/30/1996 ASync RPC + client side
|
|
|
|
--*/
|
|
|
|
#include <precomp.hxx>
|
|
|
|
#include <rpcqos.h> // mtrt for I_RpcParseSecurity
|
|
|
|
//
|
|
// Support functions not exported to the runtime
|
|
//
|
|
|
|
// Hard coded world (aka EveryOne) SID
|
|
const SID World = { 1, 1, { 0, 0, 0, 0, 0, 1}, 0};
|
|
|
|
// Hard coded world (aka EveryOne) SID
|
|
const SID AnonymousLogonSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_ANONYMOUS_LOGON_RID};
|
|
|
|
RPC_STATUS
|
|
NMP_SetSecurity(
|
|
IN NMP_ADDRESS *pAddress,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the caller supplies an SD this validates and makes a copy of the
|
|
security descriptor. Otherwise is generates a good default SD.
|
|
|
|
Arguments:
|
|
|
|
ThisAddress - Supplies the address which will own the security descriptor.
|
|
|
|
SecurityDescriptor - Supplies the security descriptor to be copied.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - Everyone is happy; we successfully duplicated the security
|
|
descriptor.
|
|
|
|
RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is invalid.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to duplicate the
|
|
security descriptor.
|
|
|
|
--*/
|
|
{
|
|
BOOL b;
|
|
SECURITY_DESCRIPTOR_CONTROL Control;
|
|
DWORD Revision;
|
|
DWORD BufferLength;
|
|
|
|
if ( SecurityDescriptor == 0 )
|
|
{
|
|
// By default, RPC will create a SD which only allows the process owner to
|
|
// create more pipe instances. This prevents other users from stealing
|
|
// the pipe.
|
|
|
|
pAddress->SecurityDescriptor = new SECURITY_DESCRIPTOR;
|
|
if ( pAddress->SecurityDescriptor == 0
|
|
|| !InitializeSecurityDescriptor(pAddress->SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION) )
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
// Open our thread token and pull out the owner SID. This is SID will be
|
|
// added to the DACL below.
|
|
|
|
ASSERT(GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES) <= 0x44);
|
|
|
|
DWORD cTokenOwner = sizeof(TOKEN_OWNER) + 0x44;
|
|
PVOID buffer[sizeof(TOKEN_OWNER) + 0x44];
|
|
PTOKEN_OWNER pTokenOwner = (PTOKEN_OWNER)buffer;
|
|
HANDLE hToken;
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
|
|
{
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
b = GetTokenInformation(hToken, TokenOwner, pTokenOwner, cTokenOwner, &cTokenOwner);
|
|
|
|
ASSERT(cTokenOwner <= sizeof(buffer));
|
|
|
|
CloseHandle(hToken);
|
|
|
|
if (!b)
|
|
{
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
// Now allocate the ACL and add the owner and EveryOne (world) ACEs and Anonymous Logon ACESs
|
|
|
|
DWORD size = 3*sizeof(ACCESS_ALLOWED_ACE) + sizeof(World) + sizeof(AnonymousLogonSid) + 0x44;
|
|
PACL pdacl = new(size) ACL;
|
|
ULONG ldacl = size + sizeof(ACL);
|
|
|
|
if (NULL == pdacl)
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
ASSERT(IsValidSid((PVOID)&World));
|
|
ASSERT(IsValidSid((PVOID)&AnonymousLogonSid));
|
|
|
|
InitializeAcl(pdacl, ldacl, ACL_REVISION);
|
|
|
|
if (!AddAccessAllowedAce(pdacl, ACL_REVISION,
|
|
(FILE_GENERIC_READ|FILE_GENERIC_WRITE)&(~FILE_CREATE_PIPE_INSTANCE),
|
|
(PVOID)&World))
|
|
{
|
|
ASSERT(0);
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
if (!AddAccessAllowedAce(pdacl, ACL_REVISION,
|
|
(FILE_GENERIC_READ|FILE_GENERIC_WRITE)&(~FILE_CREATE_PIPE_INSTANCE),
|
|
(PVOID)&AnonymousLogonSid ))
|
|
{
|
|
ASSERT(0);
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
if (!AddAccessAllowedAce(pdacl, ACL_REVISION, FILE_ALL_ACCESS, pTokenOwner->Owner))
|
|
{
|
|
ASSERT(0);
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
if (!SetSecurityDescriptorDacl(pAddress->SecurityDescriptor, TRUE, pdacl, FALSE))
|
|
{
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
// Caller supplied SecurityDescriptor. Make sure it is valid and, if needed, make a
|
|
// self relative copy.
|
|
|
|
if ( IsValidSecurityDescriptor(SecurityDescriptor) == FALSE )
|
|
{
|
|
return(RPC_S_INVALID_SECURITY_DESC);
|
|
}
|
|
|
|
if (FALSE == GetSecurityDescriptorControl(SecurityDescriptor, &Control, &Revision))
|
|
{
|
|
return(RPC_S_INVALID_SECURITY_DESC);
|
|
}
|
|
|
|
if (Control & SE_SELF_RELATIVE)
|
|
{
|
|
// Already self-relative, just copy it.
|
|
|
|
BufferLength = GetSecurityDescriptorLength(SecurityDescriptor);
|
|
ASSERT(BufferLength >= sizeof(SECURITY_DESCRIPTOR));
|
|
pAddress->SecurityDescriptor = new(BufferLength
|
|
- sizeof(SECURITY_DESCRIPTOR))
|
|
SECURITY_DESCRIPTOR;
|
|
if (pAddress->SecurityDescriptor == 0 )
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
memcpy(pAddress->SecurityDescriptor, SecurityDescriptor, BufferLength);
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
// Make self-relative and copy it.
|
|
BufferLength = 0;
|
|
b = MakeSelfRelativeSD(SecurityDescriptor, 0, &BufferLength);
|
|
ASSERT(b == FALSE);
|
|
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
return(RPC_S_INVALID_SECURITY_DESC);
|
|
}
|
|
|
|
//
|
|
// self-relative SD's can be of different size than the original SD.
|
|
//
|
|
|
|
ASSERT(BufferLength >= sizeof(SECURITY_DESCRIPTOR_RELATIVE));
|
|
pAddress->SecurityDescriptor = new(BufferLength
|
|
- sizeof(SECURITY_DESCRIPTOR_RELATIVE))
|
|
SECURITY_DESCRIPTOR;
|
|
|
|
if (pAddress->SecurityDescriptor == 0)
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
b = MakeSelfRelativeSD(SecurityDescriptor,
|
|
pAddress->SecurityDescriptor,
|
|
&BufferLength);
|
|
|
|
if (b == FALSE)
|
|
{
|
|
ASSERT(GetLastError() != ERROR_INSUFFICIENT_BUFFER);
|
|
delete pAddress->SecurityDescriptor;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
//
|
|
// Functions exported to the RPC runtime.
|
|
//
|
|
|
|
RPC_STATUS RPC_ENTRY
|
|
NMP_AbortHelper(
|
|
IN RPC_TRANSPORT_CONNECTION Connection,
|
|
IN BOOL fDontFlush
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes a connection, will be called only before NMP_Close() and
|
|
maybe called by several threads at once. It must also handle
|
|
the case where another thread is about to start IO on the connection.
|
|
|
|
Arguments:
|
|
|
|
Connection - pointer to a server connection object to abort.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
HANDLE h;
|
|
BOOL b;
|
|
PNMP_CONNECTION p = (PNMP_CONNECTION)Connection;
|
|
|
|
if (InterlockedIncrement(&p->fAborted) != 1)
|
|
{
|
|
// Another thread beat us to it. Normal during a call to NMP_Close.
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
I_RpcLogEvent(SU_TRANS_CONN, EV_ABORT, Connection, 0, 0, 1, 2);
|
|
|
|
|
|
// Wait for any threads which are starting IO to do so.
|
|
while(p->IsIoStarting())
|
|
Sleep(1);
|
|
|
|
RTL_SOFT_ASSERT(p->fAborted != 0 && p->IsIoStarting() == 0);
|
|
|
|
if (p->type & SERVER)
|
|
{
|
|
if (fDontFlush == 0)
|
|
{
|
|
// This will block until all pending writes on the connection
|
|
// have been read. Needed on the server which writes (example: a
|
|
// a fault) and closes the connection.
|
|
b = FlushFileBuffers(p->Conn.Handle);
|
|
|
|
//
|
|
// the above call can fail if the pipe was disconnected
|
|
//
|
|
}
|
|
|
|
// Once a pipe instance has been disconnected, it can be reused
|
|
// for a future client connection. Each NMP address keeps a
|
|
// small cache of free pipe instances. This is a performance
|
|
// optimization.
|
|
|
|
ASSERT(p->pAddress);
|
|
|
|
b = DisconnectNamedPipe(p->Conn.Handle);
|
|
if (b)
|
|
{
|
|
// will zero out Conn.Handle if it fits in the cache
|
|
p->pAddress->sparePipes.CheckinHandle(&(p->Conn.Handle));
|
|
}
|
|
// else nothing - code down below will close it
|
|
|
|
h = p->Conn.Handle;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(p->pAddress == 0);
|
|
h = p->Conn.Handle;
|
|
}
|
|
|
|
if (h)
|
|
{
|
|
b = CloseHandle(h);
|
|
ASSERT(b);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_STATUS NMP_CONNECTION::Abort(void)
|
|
{
|
|
return NMP_AbortHelper(this, 0);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_Close(
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
IN BOOL fDontFlush
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Actually cleans up the resources associated with a connection.
|
|
This is called exactly once one any connection. It may or
|
|
may not have previously been aborted.
|
|
|
|
Arguments:
|
|
|
|
ThisConnection - pointer to the connection object to close.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
BOOL b;
|
|
HANDLE h;
|
|
|
|
PNMP_CONNECTION p = (PNMP_CONNECTION)ThisConnection;
|
|
|
|
NMP_AbortHelper(ThisConnection, fDontFlush);
|
|
|
|
if (p->iLastRead)
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "Closing connection %p with left over data (%d) %p \n",
|
|
p,
|
|
p->iLastRead,
|
|
p->pReadBuffer));
|
|
}
|
|
|
|
TransConnectionFreePacket(p, p->pReadBuffer);
|
|
p->pReadBuffer = 0;
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
void RPC_ENTRY
|
|
NMP_ServerAbortListen(
|
|
IN RPC_TRANSPORT_ADDRESS Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will be called if an error occurs in setting up the
|
|
address between the time that SetupWithEndpoint or SetupUnknownEndpoint
|
|
successfully completed and before the next call into this loadable
|
|
transport module. We need to do any cleanup from Setup*.
|
|
|
|
Arguments:
|
|
|
|
pAddress - The address which is being aborted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NMP_ADDRESS *p = (NMP_ADDRESS *)Address;
|
|
|
|
// p->Endpoint is actually a pointer into p->LocalEndpoint
|
|
|
|
delete p->SecurityDescriptor;
|
|
delete p->LocalEndpoint;
|
|
delete p->pAddressVector;
|
|
|
|
// These are zero except when everything is setup ok.
|
|
|
|
ASSERT(p->hConnectPipe == 0);
|
|
ASSERT(p->sparePipes.IsSecondHandleUsed() == FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
NMP_CreatePipeInstance(
|
|
NMP_ADDRESS *pAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper around CreateNamedPipe.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - A new pipe instance created.
|
|
RPC_P_FOUND_IN_CACHE - Found a pipe instance to recycle.
|
|
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_INVALID_ENDPOINT_FORMAT
|
|
RPC_S_INTERNAL_ERROR
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
|
|
ASSERT(pAddress->hConnectPipe == 0);
|
|
|
|
sa.lpSecurityDescriptor = pAddress->SecurityDescriptor;
|
|
sa.bInheritHandle = FALSE;
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
// See if there are any cached pipe instances to reuse.
|
|
|
|
pAddress->hConnectPipe = pAddress->sparePipes.CheckOutHandle();
|
|
if (pAddress->hConnectPipe)
|
|
return(RPC_P_FOUND_IN_CACHE);
|
|
|
|
// The cache is empty, create a new pipe instance
|
|
|
|
pAddress->hConnectPipe = CreateNamedPipeW(pAddress->LocalEndpoint,
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
PIPE_WAIT | PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
2048,
|
|
2048,
|
|
NMPWAIT_USE_DEFAULT_WAIT,
|
|
&sa);
|
|
|
|
if (pAddress->hConnectPipe != INVALID_HANDLE_VALUE)
|
|
{
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
pAddress->hConnectPipe = 0;
|
|
switch(GetLastError())
|
|
{
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_NOT_ENOUGH_QUOTA:
|
|
case ERROR_NO_SYSTEM_RESOURCES:
|
|
{
|
|
status = RPC_S_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_INVALID_NAME:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
{
|
|
status = RPC_S_INVALID_ENDPOINT_FORMAT;
|
|
break;
|
|
}
|
|
case ERROR_ACCESS_DENIED:
|
|
{
|
|
// An odd mapping, but this error means the pipe already exists
|
|
// which is what this error means.
|
|
status = RPC_S_DUPLICATE_ENDPOINT;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "CreateNamedPipe failed: %d\n",
|
|
GetLastError() ));
|
|
|
|
ASSERT(0);
|
|
status = RPC_S_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
inline
|
|
RPC_STATUS
|
|
NMP_ConnectNamedPipe(
|
|
NMP_ADDRESS *pAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inline wrapper for ConnectNamedPipe.
|
|
|
|
Arguments:
|
|
|
|
pAddress - The address to use to connect.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
ConnectNamedPipe() error
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
BOOL b = ConnectNamedPipe(pAddress->hConnectPipe,
|
|
&pAddress->Listen.ol);
|
|
|
|
if (!b)
|
|
{
|
|
status = GetLastError();
|
|
|
|
switch(status)
|
|
{
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_IO_PENDING:
|
|
case ERROR_PIPE_CONNECTED:
|
|
case ERROR_NO_SYSTEM_RESOURCES:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case ERROR_NO_DATA:
|
|
{
|
|
b = CloseHandle(pAddress->hConnectPipe);
|
|
ASSERT(b);
|
|
pAddress->hConnectPipe = 0;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "ConnectNamedPipe failed: %d\n",
|
|
status));
|
|
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
void
|
|
NMP_SubmitConnect(
|
|
IN BASE_ADDRESS *Address
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called on an address without a pending connect pipe or on an address
|
|
who previous connect pipe has been aborted.
|
|
|
|
Arguments:
|
|
|
|
Address - The address to submit the connect on.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
NMP_ADDRESS *pAddress = (NMP_ADDRESS *)Address;
|
|
BOOL b;
|
|
|
|
//
|
|
// We may or may not need to create a new instance. If a previous
|
|
// ConnectNamedPipe was aborted then the existing instance is ok.
|
|
//
|
|
|
|
if (pAddress->hConnectPipe == 0)
|
|
{
|
|
|
|
status = NMP_CreatePipeInstance(pAddress);
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
status = COMMON_PrepareNewHandle(pAddress->hConnectPipe);
|
|
}
|
|
else
|
|
{
|
|
if (status == RPC_P_FOUND_IN_CACHE)
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
}
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
if (pAddress->hConnectPipe)
|
|
{
|
|
b = CloseHandle(pAddress->hConnectPipe);
|
|
ASSERT(b);
|
|
pAddress->hConnectPipe = 0;
|
|
}
|
|
|
|
COMMON_AddressManager(pAddress);
|
|
return;
|
|
}
|
|
}
|
|
|
|
status = NMP_ConnectNamedPipe(pAddress);
|
|
|
|
if (status == ERROR_PIPE_CONNECTED)
|
|
{
|
|
// When a client connects here means that there will not be an IO
|
|
// completion notification for this connection. We could call
|
|
// NMP_NewConnection here but that makes error handling hard. We'll
|
|
// just post the notification directly.
|
|
|
|
b = PostQueuedCompletionStatus(RpcCompletionPort,
|
|
0,
|
|
TRANSPORT_POSTED_KEY,
|
|
&pAddress->Listen.ol);
|
|
|
|
if (!b)
|
|
{
|
|
// Give up on this connection.
|
|
b = CloseHandle(pAddress->hConnectPipe);
|
|
ASSERT(b);
|
|
pAddress->hConnectPipe = 0;
|
|
COMMON_AddressManager(pAddress);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (status != ERROR_IO_PENDING)
|
|
{
|
|
COMMON_AddressManager(pAddress);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
NMP_NewConnection(
|
|
IN PADDRESS Address,
|
|
OUT PCONNECTION *ppConnection
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when an ConnectNamedPipe completes on the main recv any thread.
|
|
|
|
Can't fail after it calls I_RpcTransServerNewConnection().
|
|
|
|
Arguments:
|
|
|
|
pAddress - The address used as context in a previous AcceptEx.
|
|
ppConnection - A place to store the new pConnection. Used
|
|
when a connection been created and then a failure occurs.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_RESOURCES
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
NMP_ADDRESS *pAddress = (NMP_ADDRESS *)Address;
|
|
HANDLE hClient = pAddress->hConnectPipe;
|
|
NMP_CONNECTION *pConnection;
|
|
|
|
// First, submit an new instance for the next client
|
|
|
|
pAddress->hConnectPipe = 0;
|
|
NMP_SubmitConnect(pAddress);
|
|
|
|
// Allocate a new connection object
|
|
|
|
pConnection = (PNMP_CONNECTION)I_RpcTransServerNewConnection(pAddress);
|
|
|
|
*ppConnection = pConnection;
|
|
|
|
if (!pConnection)
|
|
{
|
|
CloseHandle(hClient);
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
// Got a good connection, initialize it..
|
|
// start with the vtbl
|
|
pConnection = new (pConnection) NMP_CONNECTION;
|
|
pConnection->type = SERVER | CONNECTION;
|
|
pConnection->id = NMP;
|
|
pConnection->Conn.Handle = hClient;
|
|
pConnection->fAborted = 0;
|
|
pConnection->pReadBuffer = 0;
|
|
pConnection->maxReadBuffer = 0;
|
|
pConnection->iLastRead = 0;
|
|
pConnection->iPostSize = gPostSize;
|
|
memset(&pConnection->Read.ol, 0, sizeof(pConnection->Read.ol));
|
|
pConnection->Read.pAsyncObject = pConnection;
|
|
pConnection->InitIoCounter();
|
|
pConnection->pAddress = pAddress;
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_CHAR *
|
|
ULongToHexString (
|
|
IN RPC_CHAR * String,
|
|
IN unsigned long Number
|
|
);
|
|
|
|
#ifdef TRANSPORT_DLL
|
|
|
|
RPC_CHAR *
|
|
ULongToHexString (
|
|
IN RPC_CHAR * String,
|
|
IN unsigned long Number
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We convert an unsigned long into hex representation in the specified
|
|
string. The result is always eight characters long; zero padding is
|
|
done if necessary.
|
|
|
|
Arguments:
|
|
|
|
String - Supplies a buffer to put the hex representation into.
|
|
|
|
Number - Supplies the unsigned long to convert to hex.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the end of the hex string is returned.
|
|
|
|
--*/
|
|
{
|
|
*String++ = HexDigits[(Number >> 28) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 24) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 20) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 16) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 12) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 8) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 4) & 0x0F];
|
|
*String++ = HexDigits[Number & 0x0F];
|
|
return(String);
|
|
}
|
|
#endif
|
|
|
|
|
|
#define NMP_EP_TRAILER_LENGTH 8
|
|
|
|
RPC_STATUS
|
|
NMP_ServerListen(
|
|
IN RPC_TRANSPORT_ADDRESS ThisAddress,
|
|
IN PWSTR NetworkAddress,
|
|
IN OUT PWSTR *pEndpoint,
|
|
IN UINT PendingQueueSize,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN ULONG EndpointFlags,
|
|
IN ULONG NICFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a new pipe to receive new client connections.
|
|
If successful a call to NMP_CompleteListen() will actually allow
|
|
new connection callbacks to the RPC runtime to occur. Setup
|
|
before the call to NMP_CompleteListen() can be stopped after this
|
|
function call with a call to NMP_ServerAbortListen().
|
|
|
|
Arguments:
|
|
|
|
pAddress - A pointer to the loadable transport interface address.
|
|
pEndpoint - Optionally, the endpoint (pipe) to listen on. Set
|
|
to listened pipe for dynamic endpoints.
|
|
PendingQueueSize - Meaningless for named pipes.
|
|
EndpointFlags - Meaningless for named pipes.
|
|
NICFlags - Meaningless for named pipes.
|
|
SecurityDescriptor - The SD to associate with this address.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OUT_OF_RESOURCES
|
|
RPC_S_CANT_CREATE_ENDPOINT
|
|
RPC_S_INVALID_SECURITY_DESC
|
|
|
|
--*/
|
|
{
|
|
BOOL b;
|
|
INT i;
|
|
RPC_STATUS status;
|
|
PWSTR LocalPipeEndpoint;
|
|
PNMP_ADDRESS pAddress = (PNMP_ADDRESS)ThisAddress;
|
|
BOOL fEndpointCreated = FALSE;
|
|
|
|
pAddress->type = ADDRESS;
|
|
pAddress->id = NMP;
|
|
pAddress->NewConnection = NMP_NewConnection;
|
|
pAddress->SubmitListen = NMP_SubmitConnect;
|
|
pAddress->InAddressList = NotInList;
|
|
pAddress->pNext = 0;
|
|
pAddress->hConnectPipe = 0;
|
|
memset(&pAddress->Listen, 0, sizeof(BASE_OVERLAPPED));
|
|
pAddress->Listen.pAsyncObject = pAddress;
|
|
pAddress->pAddressVector = 0;
|
|
pAddress->LocalEndpoint = 0;
|
|
pAddress->Endpoint = 0;
|
|
pAddress->pNextAddress = 0;
|
|
pAddress->pFirstAddress = pAddress;
|
|
pAddress->sparePipes.Init();
|
|
|
|
|
|
// Determine the network address we'll try to listen on
|
|
|
|
if (*pEndpoint)
|
|
{
|
|
// User specified an endpoint to use.
|
|
|
|
if (RpcpStringNCompare(*pEndpoint, RPC_CONST_STRING("\\pipe\\"), 6) != 0)
|
|
return(RPC_S_INVALID_ENDPOINT_FORMAT);
|
|
|
|
i = RpcpStringLength(*pEndpoint) + 1 + 3; // NULL, \\, \\, .
|
|
|
|
LocalPipeEndpoint = new RPC_CHAR[i];
|
|
|
|
if (NULL == LocalPipeEndpoint)
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
LocalPipeEndpoint[0] = L'\\';
|
|
LocalPipeEndpoint[1] = L'\\';
|
|
LocalPipeEndpoint[2] = L'.';
|
|
RpcpStringCopy(&LocalPipeEndpoint[3], *pEndpoint);
|
|
}
|
|
else
|
|
{
|
|
// Make up an endpoint to use.
|
|
// Format: \\.\pipe\<eight byte random number (16 hex digits)>\0
|
|
BYTE RandomBytes[NMP_EP_TRAILER_LENGTH];
|
|
|
|
status = GenerateRandomNumber(&RandomBytes[0], 8);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
LocalPipeEndpoint = new RPC_CHAR[3 + 6 + 16 + 1];
|
|
|
|
if (NULL == LocalPipeEndpoint)
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
LONG counter;
|
|
PWSTR pwstrT = LocalPipeEndpoint;
|
|
|
|
*pwstrT++ = RPC_CONST_CHAR('\\');
|
|
*pwstrT++ = RPC_CONST_CHAR('\\');
|
|
*pwstrT++ = RPC_CONST_CHAR('.');
|
|
*pwstrT++ = RPC_CONST_CHAR('\\');
|
|
*pwstrT++ = RPC_CONST_CHAR('p');
|
|
*pwstrT++ = RPC_CONST_CHAR('i');
|
|
*pwstrT++ = RPC_CONST_CHAR('p');
|
|
*pwstrT++ = RPC_CONST_CHAR('e');
|
|
*pwstrT++ = RPC_CONST_CHAR('\\');
|
|
|
|
for (i=0; i < NMP_EP_TRAILER_LENGTH; i++)
|
|
{
|
|
*pwstrT++ = HexDigits[(RandomBytes[i] >> 4) & 0x0F];
|
|
*pwstrT++ = HexDigits[RandomBytes[i] & 0x0F];
|
|
}
|
|
*pwstrT = 0;
|
|
|
|
*pEndpoint = DuplicateString(LocalPipeEndpoint + 3);
|
|
if (!(*pEndpoint))
|
|
{
|
|
delete [] LocalPipeEndpoint;
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
fEndpointCreated = TRUE;
|
|
}
|
|
|
|
// Security setup
|
|
|
|
status = NMP_SetSecurity(pAddress, SecurityDescriptor);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
delete [] LocalPipeEndpoint;
|
|
if (fEndpointCreated)
|
|
delete *pEndpoint;
|
|
return(status);
|
|
}
|
|
|
|
// Network address setup
|
|
|
|
NETWORK_ADDRESS_VECTOR *pVector;
|
|
|
|
pVector = new( sizeof(RPC_CHAR *)
|
|
+ (3 + gdwComputerNameLength) * sizeof(RPC_CHAR))
|
|
NETWORK_ADDRESS_VECTOR;
|
|
|
|
if (NULL == pVector)
|
|
{
|
|
NMP_ServerAbortListen(pAddress);
|
|
delete [] LocalPipeEndpoint;
|
|
if (fEndpointCreated)
|
|
delete *pEndpoint;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
pVector->Count = 1;
|
|
pVector->NetworkAddresses[0] = (RPC_CHAR *)&pVector->NetworkAddresses[1];
|
|
|
|
RpcpStringCopy(pVector->NetworkAddresses[0], RPC_CONST_STRING("\\\\"));
|
|
RpcpStringCat(pVector->NetworkAddresses[0], gpstrComputerName);
|
|
|
|
//
|
|
// Setup address
|
|
//
|
|
|
|
pAddress->Endpoint = LocalPipeEndpoint + 3;
|
|
pAddress->LocalEndpoint = LocalPipeEndpoint;
|
|
pAddress->pAddressVector = pVector;
|
|
|
|
// We need to create two pipe instances to start with. This is necessary
|
|
// to avoid a race where a client connects and quickly disconnects.
|
|
// Before the server can submit the next connect another client fails with
|
|
// RPC_S_SERVER_UNAVAILABLE. The extra pipe instance forces the client to
|
|
// retry and everything works.
|
|
|
|
HANDLE hSpares[2];
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
status = NMP_CreatePipeInstance(pAddress);
|
|
ASSERT(status != RPC_P_FOUND_IN_CACHE);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
ASSERT(pAddress->hConnectPipe == 0);
|
|
break;
|
|
}
|
|
|
|
hSpares[i] = pAddress->hConnectPipe;
|
|
pAddress->hConnectPipe = 0;
|
|
|
|
status = COMMON_PrepareNewHandle(hSpares[i]);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
NMP_ServerAbortListen(pAddress);
|
|
if (fEndpointCreated)
|
|
delete *pEndpoint;
|
|
return(status);
|
|
}
|
|
|
|
// Move the pipe instances back into the address.
|
|
|
|
pAddress->sparePipes.CheckinHandle(&hSpares[0]);
|
|
// assert that it succeeded (i.e. did not zero out the handle)
|
|
ASSERT(hSpares[0] == NULL);
|
|
|
|
pAddress->sparePipes.CheckinHandle(&hSpares[1]);
|
|
ASSERT(hSpares[1] == NULL);
|
|
|
|
//
|
|
// Done with phase one, now return to the runtime and wait.
|
|
//
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_ConnectionImpersonateClient (
|
|
IN RPC_TRANSPORT_CONNECTION SConnection
|
|
)
|
|
// Impersonate the client at the other end of the connection.
|
|
{
|
|
NMP_CONNECTION *p = (NMP_CONNECTION *)SConnection;
|
|
BOOL b;
|
|
|
|
p->StartingOtherIO();
|
|
|
|
if (p->fAborted)
|
|
{
|
|
p->OtherIOFinished();
|
|
return(RPC_S_NO_CONTEXT_AVAILABLE);
|
|
}
|
|
|
|
b = ImpersonateNamedPipeClient(p->Conn.Handle);
|
|
|
|
p->OtherIOFinished();
|
|
|
|
if (!b)
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "ImpersonateNamedPipeClient (%p) failed %d\n",
|
|
p,
|
|
GetLastError() ));
|
|
|
|
return(RPC_S_NO_CONTEXT_AVAILABLE);
|
|
}
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_ConnectionRevertToSelf (
|
|
RPC_TRANSPORT_CONNECTION SConnection
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We want to stop impersonating the client. This means we want
|
|
the current thread's security context to revert the the
|
|
default.
|
|
|
|
Arguments:
|
|
|
|
SConnection - unused
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
RPC_S_INTERNAL_ERROR - Not impersonating or something else went wrong.
|
|
(Debug systems only)
|
|
--*/
|
|
{
|
|
BOOL b;
|
|
|
|
UNUSED(SConnection);
|
|
|
|
b = RevertToSelf();
|
|
|
|
#if DBG
|
|
if (!b)
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "RevertToSelf failed %d\n",
|
|
GetLastError()));
|
|
|
|
ASSERT(b);
|
|
return(RPC_S_INTERNAL_ERROR);
|
|
}
|
|
#endif
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_STATUS RPC_ENTRY
|
|
NMP_ConnectionQueryClientAddress(
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
OUT RPC_CHAR **pNetworkAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the address of the client. Uses an extended in NT 5
|
|
ioctl to reterive the client machine name from named pipes.
|
|
|
|
Arguments:
|
|
|
|
ThisConnection - The server connection of interest.
|
|
NetworkAddress - Will contain the string on success.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_RESOURCES
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS NtStatus;
|
|
RPC_STATUS status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
NMP_CONNECTION *p = (NMP_CONNECTION *)ThisConnection;
|
|
FILE_PIPE_CLIENT_PROCESS_BUFFER_EX ClientProcess;
|
|
HANDLE hEvent = I_RpcTransGetThreadEvent();
|
|
DWORD size;
|
|
|
|
ClientProcess.ClientComputerNameLength = 0;
|
|
|
|
ASSERT(hEvent);
|
|
|
|
p->StartingOtherIO();
|
|
|
|
if (p->fAborted)
|
|
{
|
|
p->OtherIOFinished();
|
|
return(RPC_S_NO_CONTEXT_AVAILABLE);
|
|
}
|
|
|
|
NtStatus = NtFsControlFile(p->Conn.Handle,
|
|
hEvent,
|
|
0,
|
|
0,
|
|
&IoStatus,
|
|
FSCTL_PIPE_QUERY_CLIENT_PROCESS,
|
|
0,
|
|
0,
|
|
&ClientProcess,
|
|
sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER_EX));
|
|
|
|
p->OtherIOFinished();
|
|
|
|
if (NtStatus == STATUS_PENDING)
|
|
{
|
|
status = WaitForSingleObject(hEvent, INFINITE);
|
|
ASSERT(status == WAIT_OBJECT_0);
|
|
if (status != WAIT_OBJECT_0)
|
|
{
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "QUERY_PIPE_PROCESS ioctl failed %x\n",
|
|
NtStatus));
|
|
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
if (ClientProcess.ClientComputerNameLength == 0)
|
|
{
|
|
// Must be local, no ID. Just copy the local computer name into the
|
|
// buffer and continue.
|
|
|
|
size = gdwComputerNameLength; // Includes null
|
|
wcscpy(ClientProcess.ClientComputerBuffer, gpstrComputerName);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(wcslen(ClientProcess.ClientComputerBuffer) < 16);
|
|
|
|
// Convert from bytes to characters, add one for the null.
|
|
|
|
size = ClientProcess.ClientComputerNameLength/2 + 1;
|
|
}
|
|
|
|
*pNetworkAddress = new WCHAR[size];
|
|
if (!*pNetworkAddress)
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
wcscpy(*pNetworkAddress, ClientProcess.ClientComputerBuffer);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_STATUS RPC_ENTRY
|
|
NMP_ConnectionQueryClientId (
|
|
IN RPC_TRANSPORT_CONNECTION SConnection,
|
|
OUT RPC_CLIENT_PROCESS_IDENTIFIER * ClientProcess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We want to query the identifier of the client process at the other
|
|
of this named pipe. Two pipes from the same client process will always
|
|
return the same identifier for their client process. Likewise, two
|
|
pipes from different client processes will never return the same
|
|
identifier for their respective client process.
|
|
|
|
This is one of the few things you can't do in win32.
|
|
|
|
Arguments:
|
|
|
|
SConnection - Supplies the named pipe instance for which we want to
|
|
obtain the client process identifier.
|
|
|
|
ClientProcess - Returns the identifier for the client process at the
|
|
other end of this named pipe instance.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - This value will always be returned.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus;
|
|
RPC_STATUS status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
NMP_CONNECTION *p = (NMP_CONNECTION *)SConnection;
|
|
FILE_PIPE_CLIENT_PROCESS_BUFFER ClientProcessBuffer;
|
|
HANDLE hEvent = I_RpcTransGetThreadEvent();
|
|
BOOL fLocal;
|
|
|
|
ClientProcess->ZeroOut();
|
|
|
|
ASSERT(hEvent);
|
|
|
|
p->StartingOtherIO();
|
|
|
|
if (p->fAborted)
|
|
{
|
|
p->OtherIOFinished();
|
|
return(RPC_S_NO_CONTEXT_AVAILABLE);
|
|
}
|
|
|
|
NtStatus = NtFsControlFile(p->Conn.Handle,
|
|
hEvent,
|
|
0,
|
|
0,
|
|
&IoStatus,
|
|
FSCTL_PIPE_QUERY_CLIENT_PROCESS,
|
|
0,
|
|
0,
|
|
&ClientProcessBuffer,
|
|
sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER));
|
|
|
|
p->OtherIOFinished();
|
|
|
|
if (NtStatus == STATUS_PENDING)
|
|
{
|
|
status = WaitForSingleObject(hEvent, INFINITE);
|
|
ASSERT(status == WAIT_OBJECT_0);
|
|
if (status != WAIT_OBJECT_0)
|
|
{
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
if (ClientProcessBuffer.ClientSession)
|
|
{
|
|
ClientProcessBuffer.ClientSession = 0;
|
|
fLocal = FALSE;
|
|
}
|
|
else
|
|
fLocal = TRUE;
|
|
|
|
ClientProcess->SetNMPClientIdentifier(&ClientProcessBuffer, fLocal);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
NETWORK_ADDRESS_VECTOR *
|
|
NMP_GetNetworkAddressVector (
|
|
IN RPC_TRANSPORT_ADDRESS Address
|
|
)
|
|
{
|
|
NMP_ADDRESS *p = (NMP_ADDRESS *)Address;
|
|
return p->pAddressVector;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_Initialize (
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
IN RPC_CHAR * NetworkAddress,
|
|
IN RPC_CHAR * NetworkOptions,
|
|
IN BOOL fAsync
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the connection. This function is guaranteed to be called
|
|
in the thread that called GetBuffer.
|
|
|
|
Arguments:
|
|
|
|
ThisConnection - A place to store the connection
|
|
*/
|
|
{
|
|
PNMP_CONNECTION pConnection = (PNMP_CONNECTION)ThisConnection;
|
|
|
|
// use explicit placement to initialize vtbl
|
|
pConnection = new (pConnection) NMP_CONNECTION;
|
|
|
|
pConnection->id = NMP;
|
|
pConnection->Initialize();
|
|
pConnection->pAddress = 0;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_Open(
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
IN RPC_CHAR * ProtocolSequence,
|
|
IN RPC_CHAR * NetworkAddress,
|
|
IN RPC_CHAR * Endpoint,
|
|
IN RPC_CHAR * NetworkOptions,
|
|
IN UINT ConnTimeout,
|
|
IN UINT SendBufferSize,
|
|
IN UINT RecvBufferSize,
|
|
IN void *ResolverHint,
|
|
IN BOOL fHintInitialized,
|
|
IN ULONG CallTimeout,
|
|
IN ULONG AdditionalTransportCredentialsType, OPTIONAL
|
|
IN void *AdditionalCredentials OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a connection to a server.
|
|
|
|
Arguments:
|
|
|
|
ThisConnection - A place to store the connection
|
|
ProtocolSeqeunce - "ncacn_np"
|
|
NetworkAddress - The name of the server, with or without the '\\'
|
|
NetworkOptions - Options from the string binding. For security:
|
|
|
|
security=
|
|
[anonymous|identification|impersonation|delegation]
|
|
[dynamic|static]
|
|
[true|false]
|
|
|
|
All three fields must be present. To specify impersonation
|
|
with dynamic tracking and effective only, use the following
|
|
string for the network options.
|
|
|
|
"security=impersonation dynamic true"
|
|
|
|
ConnTimeout - See RpcMgmtSetComTimeout
|
|
0 - Min
|
|
5 - Default
|
|
9 - Max
|
|
10 - Infinite
|
|
|
|
{Send,Recv}BufferSize - Ignored.
|
|
|
|
CallTimeout - call timeout in milliseconds. Ignored for named pipes.
|
|
|
|
AdditionalTransportCredentialsType - the type of additional credentials that we were
|
|
given. Not used for named pipes.
|
|
|
|
AdditionalCredentials - additional credentials that we were given.
|
|
Not used for named pipes.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OUT_OF_RESOURCES
|
|
RPC_S_INVALID_NETWORK_OPTIONS
|
|
RPC_S_SERVER_UNAVAILABLE
|
|
RPC_S_INVALID_ENDPOINT_FORMAT
|
|
RPC_S_INVALID_NET_ADDR
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
PNMP_CONNECTION pConnection = (PNMP_CONNECTION)ThisConnection;
|
|
BOOL f;
|
|
HANDLE hPipe;
|
|
DWORD SecurityQOSFlags = 0;
|
|
NTSTATUS NtStatus;
|
|
UINT AddressLen ;
|
|
UINT EndpointLen;
|
|
HANDLE ImpersonationToken = 0;
|
|
DWORD StartTickCount;
|
|
DWORD RetryCount;
|
|
BOOL fLocalAddress = FALSE;
|
|
|
|
if ((AdditionalTransportCredentialsType != 0) || (AdditionalCredentials != NULL))
|
|
return RPC_S_CANNOT_SUPPORT;
|
|
|
|
if (NetworkOptions && *NetworkOptions)
|
|
{
|
|
//
|
|
// Enable transport level security.
|
|
//
|
|
// By default named pipes (CreateFile) uses security with impersonation,
|
|
// dynamic tracking and effective only enabled.
|
|
//
|
|
// RPC level security sits on top of this and provides other features.
|
|
//
|
|
// Named pipes security is controlled via an options string in the string
|
|
// binding. The runtime exports an API to parse the string which is used
|
|
// here to do most of the work.
|
|
//
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|
|
|
Status = I_RpcParseSecurity(NetworkOptions,
|
|
&SecurityQualityOfService);
|
|
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
ASSERT( Status == RPC_S_INVALID_NETWORK_OPTIONS );
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Convert into SecurityQOS into CreateFile flags
|
|
|
|
ASSERT(SECURITY_ANONYMOUS == (SecurityAnonymous <<16));
|
|
ASSERT(SECURITY_IDENTIFICATION == (SecurityIdentification <<16));
|
|
ASSERT(SECURITY_IMPERSONATION == (SecurityImpersonation <<16));
|
|
ASSERT(SECURITY_DELEGATION == (SecurityDelegation <<16));
|
|
|
|
SecurityQOSFlags = SECURITY_SQOS_PRESENT
|
|
| (SecurityQualityOfService.ImpersonationLevel << 16);
|
|
|
|
SecurityQOSFlags |= (SecurityQualityOfService.ContextTrackingMode
|
|
!= SECURITY_STATIC_TRACKING) ? SECURITY_CONTEXT_TRACKING : 0;
|
|
|
|
SecurityQOSFlags |= (SecurityQualityOfService.EffectiveOnly)
|
|
? SECURITY_EFFECTIVE_ONLY : 0;
|
|
}
|
|
|
|
ASSERT(NetworkAddress);
|
|
|
|
if (NetworkAddress[0] == '\\')
|
|
{
|
|
if ( NetworkAddress[1] == '\\'
|
|
&& NetworkAddress[2] != '\0'
|
|
&& NetworkAddress[3] != '\\')
|
|
{
|
|
NetworkAddress += 2;
|
|
}
|
|
else
|
|
{
|
|
RpcpErrorAddRecord(EEInfoGCRuntime,
|
|
RPC_S_INVALID_NET_ADDR,
|
|
EEInfoDLNMPOpen30,
|
|
NetworkAddress);
|
|
|
|
Status = RPC_S_INVALID_NET_ADDR;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( (NetworkAddress[0] == 0)
|
|
|| (RpcpStringCompare(NetworkAddress, gpstrComputerName) == 0) )
|
|
{
|
|
NetworkAddress = RPC_STRING_LITERAL(".");
|
|
fLocalAddress = TRUE;
|
|
}
|
|
|
|
ASSERT(Endpoint);
|
|
if ( Endpoint[0] != 0
|
|
&& RpcpStringNCompare(Endpoint, RPC_CONST_STRING("\\pipe\\"), 6) != 0)
|
|
{
|
|
Status = RPC_S_INVALID_ENDPOINT_FORMAT;
|
|
goto Cleanup;
|
|
}
|
|
|
|
AddressLen = RpcpStringLength(NetworkAddress);
|
|
EndpointLen = RpcpStringLength(Endpoint);
|
|
|
|
RPC_CHAR *TransportAddress;
|
|
TransportAddress = (RPC_CHAR *)alloca( (2 + AddressLen + EndpointLen + 1)
|
|
* sizeof(RPC_CHAR) );
|
|
|
|
TransportAddress[0] = TransportAddress[1] = '\\';
|
|
memcpy(TransportAddress + 2,
|
|
NetworkAddress,
|
|
AddressLen * sizeof(RPC_CHAR));
|
|
|
|
memcpy(TransportAddress + 2 + AddressLen,
|
|
Endpoint,
|
|
(EndpointLen + 1) * sizeof(RPC_CHAR));
|
|
|
|
ASSERT( ((long)ConnTimeout >= RPC_C_BINDING_MIN_TIMEOUT)
|
|
&& (ConnTimeout <= RPC_C_BINDING_INFINITE_TIMEOUT));
|
|
|
|
StartTickCount = GetTickCount();
|
|
RetryCount = 0;
|
|
do
|
|
{
|
|
hPipe = CreateFile((RPC_SCHAR *)TransportAddress,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED | SecurityQOSFlags,
|
|
0
|
|
);
|
|
|
|
if (hPipe != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD Mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
|
|
|
|
f = SetNamedPipeHandleState(hPipe, &Mode, 0, 0);
|
|
|
|
if (f)
|
|
{
|
|
Status = COMMON_PrepareNewHandle(hPipe);
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "COMMON_PrepareNewHandle: %d\n",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "SetNamedPipeHandleState: %d\n",
|
|
GetLastError()));
|
|
|
|
Status = GetLastError();
|
|
}
|
|
|
|
CloseHandle(hPipe);
|
|
hPipe = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = GetLastError();
|
|
}
|
|
|
|
if (Status != ERROR_PIPE_BUSY)
|
|
{
|
|
RpcpErrorAddRecord(fLocalAddress ? EEInfoGCNPFS : EEInfoGCRDR,
|
|
Status,
|
|
EEInfoDLNMPOpen10,
|
|
TransportAddress,
|
|
SecurityQOSFlags);
|
|
|
|
switch(Status)
|
|
{
|
|
case ERROR_INVALID_NAME:
|
|
Status = RPC_S_INVALID_ENDPOINT_FORMAT;
|
|
break;
|
|
|
|
case ERROR_BAD_NET_NAME:
|
|
case ERROR_INVALID_NETNAME:
|
|
Status = RPC_S_INVALID_NET_ADDR;
|
|
break;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_NOT_ENOUGH_QUOTA:
|
|
case ERROR_COMMITMENT_LIMIT:
|
|
case ERROR_TOO_MANY_OPEN_FILES:
|
|
case ERROR_OUTOFMEMORY:
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
break;
|
|
|
|
case ERROR_NOT_ENOUGH_SERVER_MEMORY:
|
|
Status = RPC_S_SERVER_OUT_OF_MEMORY;
|
|
break;
|
|
|
|
case ERROR_NO_SYSTEM_RESOURCES:
|
|
case ERROR_WORKING_SET_QUOTA:
|
|
case ERROR_TOO_MANY_SESS:
|
|
Status = RPC_S_OUT_OF_RESOURCES;
|
|
break;
|
|
|
|
case ERROR_DOMAIN_TRUST_INCONSISTENT:
|
|
case ERROR_ACCESS_DENIED:
|
|
case ERROR_ACCOUNT_EXPIRED:
|
|
case ERROR_ACCOUNT_RESTRICTION:
|
|
case ERROR_ACCOUNT_LOCKED_OUT:
|
|
case ERROR_ACCOUNT_DISABLED:
|
|
case ERROR_NO_SUCH_USER:
|
|
case ERROR_BAD_IMPERSONATION_LEVEL:
|
|
case ERROR_BAD_LOGON_SESSION_STATE:
|
|
case ERROR_INVALID_PASSWORD:
|
|
case ERROR_INVALID_LOGON_HOURS:
|
|
case ERROR_INVALID_WORKSTATION:
|
|
case ERROR_INVALID_SERVER_STATE:
|
|
case ERROR_INVALID_DOMAIN_STATE:
|
|
case ERROR_INVALID_DOMAIN_ROLE:
|
|
case ERROR_LOGON_FAILURE:
|
|
case ERROR_LICENSE_QUOTA_EXCEEDED:
|
|
case ERROR_LOGON_NOT_GRANTED:
|
|
case ERROR_LOGON_TYPE_NOT_GRANTED:
|
|
case ERROR_MUTUAL_AUTH_FAILED:
|
|
case ERROR_NETWORK_ACCESS_DENIED:
|
|
case ERROR_NO_SUCH_DOMAIN:
|
|
case ERROR_NO_SUCH_LOGON_SESSION:
|
|
case ERROR_NO_LOGON_SERVERS:
|
|
case ERROR_NO_TRUST_SAM_ACCOUNT:
|
|
case ERROR_PASSWORD_EXPIRED:
|
|
case ERROR_PASSWORD_MUST_CHANGE:
|
|
case ERROR_TRUSTED_DOMAIN_FAILURE:
|
|
case ERROR_TRUSTED_RELATIONSHIP_FAILURE:
|
|
case ERROR_WRONG_TARGET_NAME:
|
|
case ERROR_WRONG_PASSWORD:
|
|
case ERROR_TIME_SKEW:
|
|
case ERROR_NO_TRUST_LSA_SECRET:
|
|
case ERROR_LOGIN_WKSTA_RESTRICTION:
|
|
case ERROR_SHUTDOWN_IN_PROGRESS:
|
|
case ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT:
|
|
case ERROR_DOWNGRADE_DETECTED:
|
|
case ERROR_CONTEXT_EXPIRED:
|
|
case SCARD_E_NO_SMARTCARD:
|
|
case ERROR_SMARTCARD_SUBSYSTEM_FAILURE:
|
|
case SCARD_E_COMM_DATA_LOST:
|
|
case ERROR_AUTHENTICATION_FIREWALL_FAILED:
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
break;
|
|
|
|
//case ERROR_INTERNAL_ERROR:
|
|
case ERROR_NOACCESS:
|
|
// Really unexpected errors - fall into default on retail.
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "Unexpected error: %d\n",
|
|
Status));
|
|
ASSERT(0);
|
|
|
|
default:
|
|
VALIDATE(Status)
|
|
{
|
|
ERROR_REM_NOT_LIST,
|
|
ERROR_SHARING_PAUSED,
|
|
ERROR_NETNAME_DELETED,
|
|
ERROR_SEM_TIMEOUT,
|
|
ERROR_FILE_NOT_FOUND,
|
|
ERROR_PATH_NOT_FOUND,
|
|
ERROR_BAD_NETPATH,
|
|
ERROR_NETWORK_UNREACHABLE,
|
|
ERROR_UNEXP_NET_ERR,
|
|
ERROR_REQ_NOT_ACCEP,
|
|
ERROR_GEN_FAILURE,
|
|
ERROR_BAD_NET_RESP,
|
|
ERROR_PIPE_NOT_CONNECTED,
|
|
ERROR_NETLOGON_NOT_STARTED,
|
|
ERROR_DOMAIN_CONTROLLER_NOT_FOUND,
|
|
ERROR_CONNECTION_ABORTED,
|
|
ERROR_CONNECTION_INVALID,
|
|
ERROR_HOST_UNREACHABLE,
|
|
ERROR_CANT_ACCESS_DOMAIN_INFO,
|
|
ERROR_DUP_NAME,
|
|
ERROR_NO_SUCH_PACKAGE,
|
|
ERROR_INVALID_FUNCTION,
|
|
ERROR_BAD_DEV_TYPE,
|
|
ERROR_CONNECTION_REFUSED,
|
|
ERROR_BAD_COMMAND
|
|
} END_VALIDATE;
|
|
Status = RPC_S_SERVER_UNAVAILABLE;
|
|
break;
|
|
}
|
|
|
|
RpcpErrorAddRecord(EEInfoGCRuntime,
|
|
Status,
|
|
EEInfoDLNMPOpen40);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(hPipe == INVALID_HANDLE_VALUE);
|
|
|
|
// No pipe instances available, wait for one to show up...
|
|
|
|
WaitNamedPipe((RPC_SCHAR *)TransportAddress, 1000);
|
|
|
|
// attempt the connect for at least 40 seconds and at least 20 times
|
|
// note that since this is DWORD, even if the counter has wrapped
|
|
// the difference will be accurate
|
|
RetryCount ++;
|
|
}
|
|
while ((GetTickCount() - StartTickCount < 40000) || (RetryCount <= 20));
|
|
|
|
if (hPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
Status = RPC_S_SERVER_TOO_BUSY;
|
|
RpcpErrorAddRecord(fLocalAddress ? EEInfoGCNPFS : EEInfoGCRDR,
|
|
Status,
|
|
EEInfoDLNMPOpen20,
|
|
TransportAddress);
|
|
// No instances available
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Pipe opened successfully
|
|
|
|
pConnection->Conn.Handle = hPipe;
|
|
pConnection->fAborted = 0;
|
|
pConnection->pReadBuffer = 0;
|
|
pConnection->maxReadBuffer = 0;
|
|
pConnection->iPostSize = gPostSize;
|
|
pConnection->iLastRead = 0;
|
|
memset(&pConnection->Read.ol, 0, sizeof(pConnection->Read.ol));
|
|
pConnection->Read.pAsyncObject = pConnection;
|
|
pConnection->Read.thread = 0;
|
|
pConnection->Read.ol.Internal = 0;
|
|
pConnection->InitIoCounter();
|
|
pConnection->pAddress = 0;
|
|
|
|
Status = RPC_S_OK;
|
|
|
|
Cleanup:
|
|
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_SyncSend(
|
|
IN RPC_TRANSPORT_CONNECTION Connection,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN BOOL fDisableShutdownCheck,
|
|
IN BOOL fDisableCancelCheck,
|
|
ULONG Timeout
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a message on the connection. This method must appear
|
|
to be synchronous from the callers perspective.
|
|
|
|
Arguments:
|
|
|
|
pConnection - The connection.
|
|
Buffer - The data to send.
|
|
BufferLength - The size of the buffer.
|
|
fDisableShutdownCheck - N/A to named pipes.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - Data sent
|
|
RPC_P_SEND_FAILED - Connection will be aborted if this is returned.
|
|
RPC_S_CALL_CANCELLED - The call was cancelled
|
|
|
|
--*/
|
|
{
|
|
NMP_CONNECTION *p = (NMP_CONNECTION *)Connection;
|
|
DWORD bytes;
|
|
RPC_STATUS status;
|
|
OVERLAPPED olWrite;
|
|
HANDLE hEvent;
|
|
BOOL fPendingReturned = FALSE;
|
|
|
|
hEvent = I_RpcTransGetThreadEvent();
|
|
|
|
ASSERT(hEvent);
|
|
|
|
p->StartingWriteIO();
|
|
|
|
if (p->fAborted)
|
|
{
|
|
p->WriteIOFinished();
|
|
return(RPC_P_SEND_FAILED);
|
|
}
|
|
|
|
// Setting the low bit of the event indicates that the write
|
|
// completion should NOT be sent to the i/o completion port.
|
|
olWrite.Internal = 0;
|
|
olWrite.InternalHigh = 0;
|
|
olWrite.Offset = 0;
|
|
olWrite.OffsetHigh = 0;
|
|
olWrite.hEvent = (HANDLE) ((ULONG_PTR)hEvent | 0x1);
|
|
|
|
#ifdef _INTERNAL_RPC_BUILD_
|
|
if (gpfnFilter)
|
|
{
|
|
(*gpfnFilter) (Buffer, BufferLength, 0);
|
|
}
|
|
#endif
|
|
|
|
status = p->NMP_CONNECTION::Send(p->Conn.Handle,
|
|
Buffer,
|
|
BufferLength,
|
|
&bytes,
|
|
&olWrite
|
|
);
|
|
|
|
p->WriteIOFinished();
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
ASSERT(bytes == BufferLength);
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
if (status == ERROR_IO_PENDING)
|
|
{
|
|
fPendingReturned = TRUE;
|
|
// if fDisableCancelCheck - not alertable. Else, alertable.
|
|
status = UTIL_GetOverlappedResultEx(Connection,
|
|
&olWrite,
|
|
&bytes,
|
|
(fDisableCancelCheck ? FALSE : TRUE),
|
|
Timeout);
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
ASSERT(bytes == BufferLength);
|
|
return(RPC_S_OK);
|
|
}
|
|
}
|
|
|
|
ASSERT(status != ERROR_SUCCESS);
|
|
|
|
RpcpErrorAddRecord(EEInfoGCNMP,
|
|
status,
|
|
EEInfoDLNMPSyncSend10,
|
|
(ULONGLONG)Connection,
|
|
(ULONGLONG)Buffer,
|
|
(ULONG)BufferLength,
|
|
(ULONG)fPendingReturned);
|
|
|
|
VALIDATE(status)
|
|
{
|
|
ERROR_NETNAME_DELETED,
|
|
ERROR_GRACEFUL_DISCONNECT,
|
|
ERROR_BROKEN_PIPE,
|
|
ERROR_PIPE_NOT_CONNECTED,
|
|
ERROR_NO_DATA,
|
|
ERROR_NO_SYSTEM_RESOURCES,
|
|
ERROR_WORKING_SET_QUOTA,
|
|
ERROR_SEM_TIMEOUT,
|
|
ERROR_UNEXP_NET_ERR,
|
|
ERROR_BAD_NET_RESP,
|
|
ERROR_HOST_UNREACHABLE,
|
|
RPC_S_CALL_CANCELLED,
|
|
ERROR_CONNECTION_ABORTED,
|
|
ERROR_NOT_ENOUGH_QUOTA,
|
|
RPC_P_TIMEOUT,
|
|
ERROR_LOGON_FAILURE,
|
|
ERROR_TIME_SKEW,
|
|
ERROR_NETWORK_UNREACHABLE,
|
|
ERROR_NO_LOGON_SERVERS,
|
|
ERROR_NO_SUCH_USER,
|
|
ERROR_NOT_ENOUGH_SERVER_MEMORY,
|
|
ERROR_INVALID_NETNAME,
|
|
ERROR_BAD_COMMAND,
|
|
ERROR_SMARTCARD_SUBSYSTEM_FAILURE
|
|
} END_VALIDATE;
|
|
|
|
p->NMP_CONNECTION::Abort();
|
|
|
|
if ((status == RPC_S_CALL_CANCELLED) || (status == RPC_P_TIMEOUT))
|
|
{
|
|
// Wait for the write to finish. Since we closed the pipe
|
|
// this won't take very long.
|
|
UTIL_WaitForSyncIO(&olWrite,
|
|
FALSE,
|
|
INFINITE);
|
|
}
|
|
else
|
|
{
|
|
status = RPC_P_SEND_FAILED;
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_SyncSendRecv(
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
IN UINT InputBufferSize,
|
|
IN BUFFER InputBuffer,
|
|
OUT PUINT pOutputBufferSize,
|
|
OUT BUFFER *pOutputBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a request to the server and waits to receive the next PDU
|
|
to arrive at the connection.
|
|
|
|
Arguments:
|
|
|
|
ThisConnection - The connection to wait on.
|
|
InputBufferSize - The size of the data to send to the server.
|
|
InputBuffer - The data to send to the server
|
|
pOutputBufferSize - On return it contains the size of the PDU received.
|
|
pOutputBuffer - On return contains the PDU received.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
RPC_P_SEND_FAILED - Connection aborted, data did not reach the server.
|
|
RPC_P_RECEIVE_FAILED - Connection aborted, data might have reached
|
|
the server.
|
|
|
|
--*/
|
|
{
|
|
PNMP_CONNECTION p = (PNMP_CONNECTION)ThisConnection;
|
|
BOOL b;
|
|
DWORD bytes;
|
|
DWORD readbytes;
|
|
RPC_STATUS status;
|
|
HANDLE hEvent;
|
|
|
|
ASSERT(p->pReadBuffer == 0);
|
|
ASSERT(p->iLastRead == 0);
|
|
|
|
p->pReadBuffer = TransConnectionAllocatePacket(p, p->iPostSize);
|
|
if (!p->pReadBuffer)
|
|
{
|
|
p->NMP_CONNECTION::Abort();
|
|
return(RPC_P_SEND_FAILED);
|
|
}
|
|
|
|
hEvent = I_RpcTransGetThreadEvent();
|
|
|
|
ASSERT(hEvent);
|
|
|
|
p->Read.ol.hEvent = (HANDLE) ((ULONG_PTR)hEvent | 0x1);
|
|
|
|
p->maxReadBuffer = p->iPostSize;
|
|
bytes = p->maxReadBuffer;
|
|
|
|
#ifdef _INTERNAL_RPC_BUILD_
|
|
if (gpfnFilter)
|
|
{
|
|
(*gpfnFilter) (InputBuffer, InputBufferSize, 0);
|
|
}
|
|
#endif
|
|
|
|
b = TransactNamedPipe(p->Conn.Handle,
|
|
InputBuffer,
|
|
InputBufferSize,
|
|
p->pReadBuffer,
|
|
bytes,
|
|
&bytes,
|
|
&p->Read.ol
|
|
);
|
|
|
|
|
|
if (!b)
|
|
{
|
|
status = GetLastError();
|
|
if (status == ERROR_IO_PENDING)
|
|
{
|
|
status = UTIL_GetOverlappedResult(p,
|
|
&p->Read.ol,
|
|
&bytes);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(status != RPC_S_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
// Success - got the whole reply in the transact
|
|
|
|
*pOutputBuffer = p->pReadBuffer;
|
|
p->pReadBuffer = 0;
|
|
*pOutputBufferSize = bytes;
|
|
|
|
ASSERT(bytes == MessageLength((PCONN_RPC_HEADER)*pOutputBuffer));
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
if (status != ERROR_MORE_DATA)
|
|
{
|
|
RpcpErrorAddRecord(EEInfoGCNMP,
|
|
status,
|
|
EEInfoDLNMPSyncSendReceive10,
|
|
(ULONGLONG)p,
|
|
(ULONGLONG)InputBuffer,
|
|
InputBufferSize);
|
|
|
|
if (status == ERROR_BAD_PIPE)
|
|
{
|
|
status = RPC_P_SEND_FAILED;
|
|
}
|
|
else
|
|
{
|
|
// surprisingly enough, ERROR_PIPE_NOT_CONNECTED can be
|
|
// returned if the server died midway through the
|
|
// operation.
|
|
VALIDATE(status)
|
|
{
|
|
ERROR_NETNAME_DELETED,
|
|
ERROR_GRACEFUL_DISCONNECT,
|
|
ERROR_BROKEN_PIPE,
|
|
ERROR_PIPE_BUSY,
|
|
ERROR_NO_DATA,
|
|
ERROR_NO_SYSTEM_RESOURCES,
|
|
ERROR_SEM_TIMEOUT,
|
|
ERROR_UNEXP_NET_ERR,
|
|
ERROR_BAD_NET_RESP,
|
|
ERROR_CONNECTION_ABORTED,
|
|
ERROR_NETWORK_UNREACHABLE,
|
|
ERROR_HOST_UNREACHABLE,
|
|
ERROR_BAD_NETPATH,
|
|
ERROR_REM_NOT_LIST,
|
|
ERROR_ACCESS_DENIED,
|
|
ERROR_NOT_ENOUGH_QUOTA,
|
|
ERROR_LOGON_FAILURE,
|
|
ERROR_TIME_SKEW,
|
|
ERROR_PIPE_NOT_CONNECTED,
|
|
ERROR_WORKING_SET_QUOTA,
|
|
ERROR_NOT_ENOUGH_SERVER_MEMORY,
|
|
ERROR_INVALID_NETNAME,
|
|
ERROR_BAD_COMMAND,
|
|
ERROR_WORKING_SET_QUOTA
|
|
} END_VALIDATE;
|
|
|
|
status = RPC_P_RECEIVE_FAILED;
|
|
}
|
|
|
|
p->NMP_CONNECTION::Abort();
|
|
return(status);
|
|
}
|
|
|
|
// More data to read.
|
|
|
|
ASSERT(p->Read.ol.InternalHigh == p->maxReadBuffer);
|
|
|
|
ASSERT(MessageLength((PCONN_RPC_HEADER)p->pReadBuffer) > p->maxReadBuffer);
|
|
|
|
bytes = p->maxReadBuffer;
|
|
|
|
status = p->ProcessRead(bytes, pOutputBuffer, pOutputBufferSize);
|
|
|
|
if (status == RPC_P_PARTIAL_RECEIVE)
|
|
{
|
|
// More to arrive, submit a read for all of it.
|
|
|
|
status = CO_SubmitSyncRead(p, pOutputBuffer, pOutputBufferSize);
|
|
|
|
if (status == RPC_P_IO_PENDING)
|
|
{
|
|
status = UTIL_GetOverlappedResult(p, &p->Read.ol, &bytes);
|
|
|
|
// Since we read all the expected data and np is message mode
|
|
// this should either fail or give back all the data.
|
|
|
|
ASSERT(status != ERROR_MORE_DATA);
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
status = p->ProcessRead(bytes, pOutputBuffer, pOutputBufferSize);
|
|
|
|
ASSERT(status != RPC_P_PARTIAL_RECEIVE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
RpcpErrorAddRecord(EEInfoGCNMP,
|
|
status,
|
|
EEInfoDLNMPSyncSendReceive20,
|
|
(ULONGLONG)p,
|
|
(ULONGLONG)InputBuffer,
|
|
InputBufferSize);
|
|
|
|
p->NMP_CONNECTION::Abort();
|
|
if (status != RPC_P_TIMEOUT)
|
|
{
|
|
RpcpErrorAddRecord(EEInfoGCRuntime,
|
|
RPC_P_RECEIVE_FAILED,
|
|
EEInfoDLNMPSyncSendReceive30,
|
|
status);
|
|
return(RPC_P_RECEIVE_FAILED);
|
|
}
|
|
else
|
|
return (status);
|
|
}
|
|
|
|
ASSERT(*pOutputBufferSize == MessageLength((PCONN_RPC_HEADER)*pOutputBuffer));
|
|
ASSERT(p->pReadBuffer == 0);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
NMP_SyncSendRecv_Avrf(
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
IN UINT InputBufferSize,
|
|
IN BUFFER InputBuffer,
|
|
OUT PUINT pOutputBufferSize,
|
|
OUT BUFFER *pOutputBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper for NMP_SyncSendRecv implementing corruption injection
|
|
under the RPC verifier.
|
|
|
|
SyncSendRecv member of the transport interface may only be called
|
|
by the cliet, hence we inject the corruption for a client receive.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
Status = NMP_SyncSendRecv(
|
|
ThisConnection,
|
|
InputBufferSize,
|
|
InputBuffer,
|
|
pOutputBufferSize,
|
|
pOutputBuffer);
|
|
|
|
if (!Status)
|
|
{
|
|
if (gfRPCVerifierEnabled)
|
|
{
|
|
CorruptionInject(ClientReceive,
|
|
pOutputBufferSize,
|
|
(void **)pOutputBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
RPC_STATUS RPC_ENTRY NMP_Abort(IN RPC_TRANSPORT_CONNECTION Connection)
|
|
{
|
|
return ((NMP_CONNECTION *)Connection)->NMP_CONNECTION::Abort();
|
|
}
|
|
|
|
|
|
const RPC_CONNECTION_TRANSPORT
|
|
NMP_TransportInterface = {
|
|
RPC_TRANSPORT_INTERFACE_VERSION,
|
|
NMP_TOWER_ID,
|
|
UNC_ADDRESS_ID,
|
|
RPC_STRING_LITERAL("ncacn_np"),
|
|
"\\pipe\\epmapper",
|
|
COMMON_ProcessCalls,
|
|
COMMON_StartPnpNotifications,
|
|
COMMON_ListenForPNPNotifications,
|
|
COMMON_TowerConstruct,
|
|
COMMON_TowerExplode,
|
|
COMMON_PostRuntimeEvent,
|
|
FALSE,
|
|
NMP_GetNetworkAddressVector,
|
|
sizeof(NMP_ADDRESS),
|
|
sizeof(NMP_CONNECTION),
|
|
sizeof(NMP_CONNECTION),
|
|
sizeof(CO_SEND_CONTEXT),
|
|
0,
|
|
NMP_MAX_SEND,
|
|
NMP_Initialize,
|
|
0,
|
|
NMP_Open,
|
|
NMP_SyncSendRecv,
|
|
CO_SyncRecv,
|
|
NMP_Abort,
|
|
NMP_Close,
|
|
CO_Send,
|
|
CO_Recv,
|
|
NMP_SyncSend,
|
|
0, // turn on/off keep alives
|
|
NMP_ServerListen,
|
|
NMP_ServerAbortListen,
|
|
COMMON_ServerCompleteListen,
|
|
NMP_ConnectionQueryClientAddress,
|
|
0, // query local address
|
|
NMP_ConnectionQueryClientId,
|
|
0, // query client ip address
|
|
NMP_ConnectionImpersonateClient,
|
|
NMP_ConnectionRevertToSelf,
|
|
0, // FreeResolverHint
|
|
0, // CopyResolverHint
|
|
0, // CompareResolverHint
|
|
0 // SetLastBufferToFree
|
|
};
|
|
|
|
// When the RPC verifier is enabled and we are corrupting client receives,
|
|
// we will use a modified transport interface given below. It will have the sync
|
|
// receive members overwritten.
|
|
RPC_CONNECTION_TRANSPORT *pNMP_TransportInterface_Avrf = NULL;
|
|
|
|
const RPC_CONNECTION_TRANSPORT *
|
|
NMP_TransportLoad (
|
|
)
|
|
{
|
|
// Overwrite the SyncSendRecv and SyncRecv members of the transport interfaces if the
|
|
// RPC verifier is enabled.
|
|
if (gfRpcVerifierCorruptionInjectClientReceives)
|
|
{
|
|
// Check if we have previously initialized the Avrf transport interface.
|
|
if (pNMP_TransportInterface_Avrf == NULL)
|
|
{
|
|
// Allocate a transport interface structure to override the default.
|
|
pNMP_TransportInterface_Avrf = new (RPC_CONNECTION_TRANSPORT);
|
|
if (pNMP_TransportInterface_Avrf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
// Initialize the Avrf transport interface with the default values.
|
|
RpcpMemoryCopy(pNMP_TransportInterface_Avrf,
|
|
&NMP_TransportInterface,
|
|
sizeof(RPC_CONNECTION_TRANSPORT));
|
|
// Override the interface functions for sync receive.
|
|
ASSERT(pNMP_TransportInterface_Avrf->SyncSendRecv == NMP_SyncSendRecv);
|
|
pNMP_TransportInterface_Avrf->SyncSendRecv = NMP_SyncSendRecv_Avrf;
|
|
ASSERT(pNMP_TransportInterface_Avrf->SyncRecv == CO_SyncRecv);
|
|
pNMP_TransportInterface_Avrf->SyncRecv = CO_SyncRecv_Avrf;
|
|
}
|
|
return(pNMP_TransportInterface_Avrf);
|
|
}
|
|
|
|
return(&NMP_TransportInterface);
|
|
}
|