2162 lines
57 KiB
C++
2162 lines
57 KiB
C++
/*++
|
||
|
||
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
|
||
|
||
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,
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
|
||
)
|
||
/*++
|
||
|
||
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\<pid in hex (8).<counter in hex (3)>\0
|
||
|
||
static LONG EndpointCounter = 0;
|
||
|
||
LocalPipeEndpoint = new RPC_CHAR[3 + 6 + 8 + 1 + 3 + 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('\\');
|
||
pwstrT = ULongToHexString(pwstrT, GetCurrentProcessId());
|
||
*pwstrT++ = RPC_CONST_CHAR('.');
|
||
counter = InterlockedExchangeAdd(&EndpointCounter, 1);
|
||
*pwstrT++ = HexDigits[(counter >> 8) & 0x0F];
|
||
*pwstrT++ = HexDigits[(counter >> 4) & 0x0F];
|
||
*pwstrT++ = HexDigits[counter & 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.
|
||
//
|
||
|
||
*ppAddressVector = pVector;
|
||
|
||
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);
|
||
}
|
||
|
||
|
||
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:
|
||
Status = RPC_S_OUT_OF_RESOURCES;
|
||
break;
|
||
|
||
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:
|
||
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
|
||
} 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,
|
||
BufferLength,
|
||
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
|
||
} 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
|
||
} 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_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,
|
||
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,
|
||
NMP_ConnectionImpersonateClient,
|
||
NMP_ConnectionRevertToSelf,
|
||
0, // FreeResolverHint
|
||
0, // CopyResolverHint
|
||
0, // CompareResolverHint
|
||
0 // SetLastBufferToFree
|
||
};
|
||
|
||
const RPC_CONNECTION_TRANSPORT *
|
||
NMP_TransportLoad (
|
||
)
|
||
{
|
||
return(&NMP_TransportInterface);
|
||
}
|