windows-nt/Source/XPSP1/NT/com/rpc/runtime/trans/common/mqtrans.cxx
2020-09-26 16:20:57 +08:00

1499 lines
39 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1997 - 1999
Module Name:
mqtrans.cxx
Abstract:
Support for MSMQ (Falcon) datagram transport. Based on MarioGo's
DG transport code (dgtrans.cxx).
Author:
Edward Reus (edwardr) 04-Jul-1997
Revision History:
--*/
#include <precomp.hxx>
#include <trans.hxx>
#include <dgtrans.hxx>
#include <wswrap.hxx>
#include "mqtrans.hxx"
////////////////////////////////////////////////////////////////////////
//
// MSMQ datagram routines.
//
//----------------------------------------------------------------
RPC_STATUS
MQ_SubmitReceive( IN MQ_DATAGRAM_ENDPOINT *pEndpoint,
IN MQ_DATAGRAM *pDatagram )
/*++
Arguments:
pEndpoint - The endpoint on which the receive should be posted.
pDatagram - The datagram object to manage the receive.
Return Value:
RPC_P_IO_PENDING - OK
RPC_S_OUT_OF_MEMORY
RPC_S_OUT_OF_RESOURCES
--*/
{
RPC_STATUS status;
BOOL fRetry = TRUE;
DWORD dwBytes = 0;
DWORD dwStatus;
HRESULT hr;
while (fRetry)
{
if (pDatagram->pPacket == 0)
{
pDatagram->dwPacketSize = dwBytes;
status = I_RpcTransDatagramAllocate2( pEndpoint,
(BUFFER *)&pDatagram->pPacket,
(PUINT) &pDatagram->dwPacketSize,
(PVOID *) &pDatagram->pAddress);
if (status != RPC_S_OK)
{
return RPC_S_OUT_OF_MEMORY;
}
ASSERT( pDatagram->pPacket );
memset(pDatagram->pAddress,0,sizeof(MQ_ADDRESS));
}
pDatagram->cRecvAddr = sizeof(MQ_ADDRESS);
ASSERT(*(PDWORD)pDatagram->pPacket = 0xDEADF00D);
hr = AsyncReadQueue( pEndpoint,
&pDatagram->Read,
pDatagram->pAddress,
pDatagram->pPacket,
pDatagram->dwPacketSize );
if (!FAILED(hr))
{
return RPC_P_IO_PENDING;
}
if (hr == MQ_ERROR_BUFFER_OVERFLOW)
{
// This can happen if there is already a large sized call on the queue.
dwBytes = pDatagram->Read.aMsgPropVar[1].ulVal;
dwStatus = I_RpcTransDatagramFree( pEndpoint,
(BUFFER)pDatagram->pPacket,
(PVOID) pDatagram->pAddress);
pDatagram->pPacket = 0;
pDatagram->dwPacketSize = 0;
if (dwStatus != RPC_S_OK)
{
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "MQ_SubmitReceive(): I_RpcTransDatagram() failed: %d\n",
dwStatus));
return RPC_S_OUT_OF_MEMORY;
}
}
else
{
fRetry = FALSE;
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "MQ_SubmitReceive(): AsyncReadQueue() failed: 0x%x\n",
hr));
}
}
return RPC_S_OUT_OF_RESOURCES;
}
//----------------------------------------------------------------
void
MQ_SubmitReceives(
BASE_ADDRESS *ThisEndpoint
)
/*++
Routine Description:
Helper function called when the pending IO count
on an address is too low.
Arguments:
ThisEndpoint - The address to submit IOs on.
Return Value:
None
--*/
{
PMQ_DATAGRAM pDg;
PMQ_DATAGRAM_ENDPOINT pEndpoint = (PMQ_DATAGRAM_ENDPOINT)ThisEndpoint;
if ( (!pEndpoint->fAllowReceives) || (!pEndpoint->hQueue) )
{
return;
}
do
{
BOOL fIoSubmitted;
fIoSubmitted = FALSE;
// Only one thread should be trying to submit IOs at a time.
// This saves locking each DATAGRAM object.
// Simple lock - but requires a loop. See the comment at the end
// of the loop.
if (pEndpoint->fSubmittingIos != 0)
break;
if (InterlockedIncrement(&pEndpoint->fSubmittingIos) != 1)
break;
// Submit new IOs on all the idle datagram objects
for (int i = 0; i < pEndpoint->cMaximumIos; i++)
{
pDg = &pEndpoint->aDatagrams[i];
if (pDg->Busy)
{
continue;
}
// Must be all set for the IO to complete before trying
// to submit the IO.
InterlockedIncrement(&pEndpoint->cPendingIos);
pDg->Busy = TRUE;
if (MQ_SubmitReceive(pEndpoint, pDg) == RPC_P_IO_PENDING)
{
fIoSubmitted = TRUE;
}
else
{
pDg->Busy = FALSE;
InterlockedDecrement(&pEndpoint->cPendingIos);
break;
}
}
// Release the "lock" on the endpoint object.
pEndpoint->fSubmittingIos = 0;
if (!fIoSubmitted && pEndpoint->cPendingIos == 0)
{
// It appears that no IO is pending on the endpoint.
COMMON_AddressManager(pEndpoint);
return;
}
// Even if we submitted new IOs, they may all have completed
// already. Which means we may need to loop and submit more
// IOs. This is needed since the thread which completed the
// last IO may have run into our lock and returned.
}
while (pEndpoint->cPendingIos == 0);
return;
}
BOOL RPC_ENTRY MQ_AllowReceives(
IN DG_TRANSPORT_ENDPOINT pvTransEndpoint,
IN BOOL fAllowReceives,
IN BOOL fCancelPendingIos )
{
BOOL fPrevAllowReceives;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)pvTransEndpoint;
fPrevAllowReceives = pEndpoint->fAllowReceives;
pEndpoint->fAllowReceives = fAllowReceives;
if (!pEndpoint->cPendingIos)
{
MQ_SubmitReceives(pEndpoint);
}
ASSERT( !fCancelPendingIos ); // Not implemented yet.
return fPrevAllowReceives;
}
RPC_STATUS RPC_ENTRY
MQ_SendPacket(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint,
IN DG_TRANSPORT_ADDRESS pvAddress,
IN BUFFER pHeader,
IN unsigned cHeader,
IN BUFFER pBody,
IN unsigned cBody,
IN BUFFER pTrailer,
IN unsigned cTrailer
)
/*++
Routine Description:
Sends a packet to an address.
The routine will send a packet built out of the three buffers supplied.
All the buffers are optional, the actual packet sent will be built from
all the buffers actually supplied. In each call at least buffer should
NOT be null.
Arguments:
ThisEndpoint - Endpoint to send from.
pAddress - Address to send to.
pHeader - First data buffer
cHeader - Size of the first data buffer or 0.
pBody - Second data buffer
cBody - Size of the second data buffer or 0.
pTrailer - Third data buffer.
cTrailer - Size of the third data buffer or 0.
Return Value:
RPC_S_OK
RPC_S_OUT_OF_RESOURCES
RPC_S_OUT_OF_MEMORY
RPC_P_SEND_FAILED
--*/
{
RPC_STATUS Status;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)ThisEndpoint;
MQ_ADDRESS *pAddress = (MQ_ADDRESS*)pvAddress;
UCHAR *pBuffer;
UCHAR *pTemp;
DWORD dwBytes = cHeader + cBody + cTrailer;
HRESULT hr;
BOOL fNeedToFree = FALSE;
ASSERT(dwBytes);
//
// Buffer assembly. I get the data PDU as up to three separate pieces,
// so it needs to be assembled before being sent. For Falcon (which can't
// do scatter/gather) this is expensive (allocate() + memcpy()'s). We try
// to be cheap and avoid the allocation, or do an _alloca() off the stack
// instead of the heap.
//
if ( (cHeader==0) && (cBody > 0) && (cTrailer == 0) )
{
pBuffer = pBody;
}
else if ( ((cBody == 0) && (cTrailer == 0))
|| ((pHeader+cHeader == pBody) && (pTrailer == 0)) )
{
pBuffer = pHeader;
}
else
{
pBuffer = (UCHAR*)I_RpcAllocate(dwBytes);
fNeedToFree = TRUE;
if (!pBuffer)
{
return RPC_S_OUT_OF_MEMORY;
}
pTemp = pBuffer;
if (cHeader)
{
memcpy(pTemp,pHeader,cHeader);
pTemp += cHeader;
}
if (cBody)
{
memcpy(pTemp,pBody,cBody);
pTemp += cBody;
}
if (cTrailer)
{
memcpy(pTemp,pTrailer,cTrailer);
}
ASSERT(pTemp != pBuffer);
}
#ifdef MAJOR_DBG
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "MQ_SendPacket():\n"));
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
" To: %S: %S\n",
pAddress->wsMachine,
pAddress->wsQName));
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
" Size: %d\n",
dwBytes));
DG_DbgPrintPacket(pBuffer);
#endif
hr = MQ_SendToQueue( pEndpoint, pAddress, pBuffer, dwBytes );
Status = MQ_MapStatusCode(hr,RPC_P_SEND_FAILED);
if (Status != RPC_S_OK)
{
if (fNeedToFree)
{
I_RpcFree(pBuffer);
}
return Status;
}
if ( (pEndpoint->cMinimumIos)
&& (pEndpoint->cPendingIos <= pEndpoint->cMaximumIos) )
{
MQ_SubmitReceives(pEndpoint);
}
if (fNeedToFree)
{
I_RpcFree(pBuffer);
}
return RPC_S_OK;
}
RPC_STATUS RPC_ENTRY
MQ_ForwardPacket(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint,
IN BUFFER pHeader,
IN unsigned cHeader,
IN BUFFER pBody,
IN unsigned cBody,
IN BUFFER pTrailer,
IN unsigned cTrailer,
IN CHAR * pszPort
)
/*++
Routine Description:
Sends a packet to the server it was originally destined for (that
is, the client had a dynamic endpoint it wished the enpoint mapper
to resolve and forward the packet to).
Arguments:
ThisEndpoint - The endpoint to forward the packet from.
// Buffer like DG_SendPacket
pszPort - Pointer to the server port num to forward to.
This is in an Ansi string.
Return Value:
RPC_S_CANT_CREATE_ENDPOINT - pEndpoint invalid.
results of MQ_SendPacket().
--*/
{
PMQ_DATAGRAM_ENDPOINT pEndpoint = (PMQ_DATAGRAM_ENDPOINT)ThisEndpoint;
MQ_ADDRESS Address;
MQ_ADDRESS *pAddress = &Address;
RPC_CHAR wsPort[MQ_MAX_Q_NAME_LEN];
UNICODE_STRING UnicodePort;
ANSI_STRING AsciiPort;
DWORD Status;
NTSTATUS NtStatus;
//
// Convert pszPort to Unicode:
//
RtlInitAnsiString(&AsciiPort, pszPort);
UnicodePort.Buffer = wsPort;
UnicodePort.Length = 0;
UnicodePort.MaximumLength = MQ_MAX_Q_NAME_LEN * sizeof(RPC_CHAR);
NtStatus = RtlAnsiStringToUnicodeString(&UnicodePort,
&AsciiPort,
FALSE);
if (!NT_SUCCESS(NtStatus))
{
return RPC_S_CANT_CREATE_ENDPOINT;
}
//
// Try to connect to the server (to forward the packet to):
//
Status = ConnectToServerQueue(pAddress, NULL, wsPort );
if (Status != RPC_S_OK)
{
return RPC_S_CANT_CREATE_ENDPOINT;
}
//
// Forward the packet on:
//
Status = MQ_SendPacket(ThisEndpoint,
pAddress,
pHeader,
cHeader,
pBody,
cBody,
pTrailer,
cTrailer );
//
// Release the connection, we shouldn't need it any more:
//
DisconnectFromServer(pAddress);
return Status;
}
RPC_STATUS
RPC_ENTRY
MQ_ResizePacket(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint,
OUT DG_TRANSPORT_ADDRESS *pReplyAddress,
OUT PUINT pBufferLength,
OUT BUFFER *pBuffer
)
{
RPC_STATUS Status = RPC_P_TIMEOUT;
DWORD dwBytes = 0;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)ThisEndpoint;
MQ_DATAGRAM *pDatagram = &pEndpoint->aDatagrams[0];
dwBytes = pDatagram->Read.aMsgPropVar[1].ulVal;
ASSERT(dwBytes);
#ifdef DBG
DbgPrint("MQ_ResizePacket(): dwBytes: %d\n",dwBytes);
#endif
if ( (pDatagram->pPacket) && (pDatagram->dwPacketSize < dwBytes) )
{
Status = I_RpcTransDatagramFree( pEndpoint,
(BUFFER)pDatagram->pPacket,
(PVOID) pDatagram->pAddress);
pDatagram->pPacket = 0;
pDatagram->dwPacketSize = 0;
if (Status != RPC_S_OK)
{
return RPC_S_OUT_OF_MEMORY;
}
}
if (!pDatagram->pPacket)
{
pDatagram->dwPacketSize = dwBytes;
Status = I_RpcTransDatagramAllocate2( pEndpoint,
(BUFFER *)&pDatagram->pPacket,
(PUINT) &pDatagram->dwPacketSize,
(PVOID *) &pDatagram->pAddress);
if (Status != RPC_S_OK)
{
return RPC_S_OUT_OF_MEMORY;
}
pDatagram->cRecvAddr = sizeof(MQ_ADDRESS);
ASSERT( pDatagram->pPacket );
}
#ifdef DBG
DbgPrint("MQ_ResizePacket(): Ok\n");
#endif
return RPC_P_TIMEOUT;
}
RPC_STATUS
RPC_ENTRY
MQ_ReceivePacket(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint,
OUT DG_TRANSPORT_ADDRESS *pReplyAddress,
OUT PUINT pBufferLength,
OUT BUFFER *pBuffer,
IN LONG Timeout
)
/*++
Routine Description:
Used to wait for a datagram from a server. Returns the data
returned and the address of the machine which replied.
This is a blocking API. It should only be called during sync
client RPC threads.
Arguments:
Endpoint - The endpoint to receive from.
ReplyAddress - Contain the source address of the datagram if
successful.
BufferLength - The size of Buffer on input, the size of the
datagram received on output.
Timeout - Milliseconds to wait for a datagram.
Return Value:
RPC_S_OK
RPC_P_OVERSIZE_PACKET - Datagram > BufferLength arrived,
first BufferLength bytes of Buffer contain the partial datagram.
RPC_P_RECEIVE_FAILED
RPC_P_TIMEOUT
--*/
{
RPC_STATUS Status = RPC_P_TIMEOUT;
DWORD dwBytes;
BOOL fRetry = TRUE;
HRESULT hr;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)ThisEndpoint;
MQ_DATAGRAM *pDatagram = &pEndpoint->aDatagrams[0];
ASSERT((pEndpoint->type & TYPE_MASK) == CLIENT);
ASSERT(pEndpoint->aDatagrams[0].Read.ol.hEvent);
#if TRUE
while (fRetry)
{
fRetry = FALSE;
if (pDatagram->Busy == 0)
{
Status = MQ_SubmitReceive(pEndpoint,pDatagram);
if (Status != RPC_P_IO_PENDING)
{
return Status;
}
pDatagram->Busy = TRUE;
}
else
{
ASSERT(pDatagram->Busy);
ASSERT(pDatagram->pPacket);
Status = RPC_P_IO_PENDING;
}
if (Status == RPC_P_IO_PENDING)
{
Status = WaitForSingleObjectEx(pDatagram->Read.ol.hEvent,
Timeout,
TRUE);
if (Status != STATUS_WAIT_0)
{
// In the timeout case we just want to return.
if (Status == WAIT_IO_COMPLETION)
{
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "MQ_ReceivePacket() cancelled.\n"));
}
else
{
ASSERT(Status == STATUS_TIMEOUT);
}
ASSERT(pDatagram->Busy);
return RPC_P_TIMEOUT;
}
}
MQ_FillInAddress(pDatagram->pAddress,pDatagram->Read.aMsgPropVar);
}
ASSERT((Status == RPC_S_OK)||(Status == RPC_P_OVERSIZE_PACKET));
ASSERT(pDatagram->Busy);
ASSERT(pDatagram->pPacket);
// ASSERT(dwBytes <= pDatagram->dwPacketSize);
*pBuffer = (BUFFER)pDatagram->pPacket;
*pBufferLength = pDatagram->Read.aMsgPropVar[1].ulVal;
// dwBytes;
*pReplyAddress = pDatagram->pAddress;
pDatagram->pPacket = 0;
pDatagram->dwPacketSize = 0;
pDatagram->Busy = 0;
return Status;
#else
if (pDatagram->Busy == 0)
{
hr = PeekQueue( pEndpoint, Timeout, &dwBytes );
Status = MQ_MapStatusCode(hr,RPC_P_RECEIVE_FAILED);
if (Status != RPC_S_OK)
{
return Status;
}
if ( (pDatagram->pPacket) && (pDatagram->dwPacketSize < dwBytes) )
{
Status = I_RpcTransDatagramFree( pEndpoint,
(BUFFER)pDatagram->pPacket,
(PVOID) pDatagram->pAddress);
pDatagram->pPacket = 0;
pDatagram->dwPacketSize = 0;
if (Status != RPC_S_OK)
{
return RPC_S_OUT_OF_MEMORY;
}
}
if (!pDatagram->pPacket)
{
pDatagram->dwPacketSize = dwBytes;
Status = I_RpcTransDatagramAllocate2( pEndpoint,
(BUFFER *)&pDatagram->pPacket,
(PUINT) &pDatagram->dwPacketSize,
(PVOID *) &pDatagram->pAddress);
if (Status != RPC_S_OK)
{
return RPC_S_OUT_OF_MEMORY;
}
pDatagram->cRecvAddr = sizeof(MQ_ADDRESS);
ASSERT( pDatagram->pPacket );
}
pDatagram->Busy = TRUE;
dwBytes = pDatagram->dwPacketSize;
hr = ReadQueue( pEndpoint, Timeout, pDatagram->pAddress, pDatagram->pPacket, &dwBytes );
Status = MQ_MapStatusCode(hr,RPC_P_RECEIVE_FAILED);
if (FAILED(hr))
{
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "ReadQueue() failed: 0x%x\n",
hr));
pDatagram->Busy = FALSE;
return Status;
}
}
#ifdef MAJOR_DBG
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "MQ_ReceivePacket():\n"));
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
" Receive on: %S\n",
pEndpoint->wsQName));
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
" From: %S\n",
pDatagram->pAddress->wsQName));
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
" Size: %d\n",
dwBytes));
DG_DbgPrintPacket(pDatagram->pPacket);
#endif
ASSERT((Status == RPC_S_OK)||(Status == RPC_P_OVERSIZE_PACKET));
ASSERT(pDatagram->Busy);
ASSERT(pDatagram->pPacket);
ASSERT(dwBytes <= pDatagram->dwPacketSize);
*pBuffer = (BUFFER)pDatagram->pPacket;
*pBufferLength = dwBytes;
*pReplyAddress = pDatagram->pAddress;
pDatagram->pPacket = 0;
pDatagram->dwPacketSize = 0;
pDatagram->Busy = 0;
return Status;
#endif
}
RPC_STATUS
MQ_CreateEndpoint(
OUT MQ_DATAGRAM_ENDPOINT *pEndpoint,
IN MQ_ADDRESS *pAddress,
IN void *pSecurityDescriptor,
IN DWORD dwEndpointFlags,
IN PROTOCOL_ID id,
IN BOOL fClient,
IN BOOL fAsync
)
/*++
Routine Description:
Creates a new endpoint.
Arguments:
pEndpoint - The runtime allocated endpoint structure to
filled in.
pSockAddr - An initialized sockaddr with the correct
(or no) endpoint.
id - The id of the protocol to use in creating the address.
fClient - If TRUE this is a client endpoint
fAsync - If TRUE this endpoint is "async" which means that
a) It should be added to the IO completion port and
b) that the transport should pend a number of receives
on the endpoint automatically.
Return Value:
RPC_S_OK
RPC_S_OUT_OF_MEMORY
RPC_S_OUT_OF_RESOURCES
RPC_S_CANT_CREATE_ENDPOINT
RPC_S_DUPLICATE_ENDPOINT
--*/
{
MQ_DATAGRAM *pDatagram;
int i;
int err;
int length;
RPC_STATUS Status = RPC_S_OK;
HRESULT hr;
UUID uuid;
DWORD dwSize;
RPC_CHAR *pwsUuid;
RPC_CHAR wsQName[MQ_MAX_Q_NAME_LEN];
RPC_CHAR wsMachine[MAX_COMPUTERNAME_LEN];
pEndpoint->type = DATAGRAM | ADDRESS;
pEndpoint->id = id;
pEndpoint->pAddressVector = 0;
pEndpoint->SubmitListen = MQ_SubmitReceives;
pEndpoint->InAddressList = NotInList;
pEndpoint->pNext = 0;
pEndpoint->fSubmittingIos = 0;
pEndpoint->cPendingIos = 0;
pEndpoint->cMinimumIos = 0;
pEndpoint->cMaximumIos = 0;
pEndpoint->aDatagrams = 0;
pEndpoint->pFirstAddress = pEndpoint;
pEndpoint->pNextAddress = 0;
pEndpoint->hQueue = 0;
pEndpoint->hAdminQueue = 0;
// If we're told not to listen, then we won't allow receives
// until we are specifically told to do so...
#ifdef RPC_C_MQ_DONT_LISTEN
pEndpoint->fAllowReceives = !(dwEndpointFlags & RPC_C_MQ_DONT_LISTEN);
#else
pEndpoint->fAllowReceives = TRUE;
#endif
pEndpoint->fAck = FALSE;
pEndpoint->ulDelivery = RPC_C_MQ_EXPRESS;
pEndpoint->ulPriority = DEFAULT_PRIORITY;
pEndpoint->ulJournaling = RPC_C_MQ_JOURNAL_NONE;
pEndpoint->ulTimeToReachQueue = INFINITE;
pEndpoint->ulTimeToReceive = INFINITE;
pEndpoint->fAuthenticate = FALSE;
pEndpoint->fEncrypt = FALSE;
pEndpoint->ulPrivacyLevel = 0;
// Machine name:
dwSize = sizeof(wsMachine);
if (!GetComputerName((RPC_SCHAR *)wsMachine,&dwSize))
{
return RPC_S_OUT_OF_MEMORY;
}
// Get the name of the machine that the QM (mqsvc.exe)
// is running on:
dwSize = sizeof(pEndpoint->wsMachine);
hr = QueryQM(pEndpoint->wsMachine,&dwSize);
if (FAILED(hr))
{
Status = MQ_MapStatusCode(hr,RPC_S_CANT_CREATE_ENDPOINT);
return Status;
}
//
// See if this is a call from an RPC server or RPC client:
//
if (fClient)
{
pEndpoint->type |= CLIENT;
// Queue name (a unique string...):
Status = UuidCreate(&uuid);
if ((Status != RPC_S_OK) && (Status != RPC_S_UUID_LOCAL_ONLY))
{
return Status;
}
if (UuidToString(&uuid,&pwsUuid) != RPC_S_OK)
{
return RPC_S_OUT_OF_MEMORY;
}
RpcpStringCopy(wsQName,TEXT("RpcCl-"));
RpcpStringCat(wsQName,pwsUuid);
// Function UuidCreate() allocated a string, free it:
RpcStringFree(&pwsUuid);
//
// Create the queue for this endpoint:
//
hr = ClientSetupQueue( pEndpoint, pEndpoint->wsMachine, wsQName );
Status = MQ_MapStatusCode(hr,RPC_S_CANT_CREATE_ENDPOINT);
if (Status == RPC_S_OK)
{
MQ_RegisterQueueToDelete(pEndpoint->wsQFormat,wsMachine);
}
}
else
{
pEndpoint->type |= SERVER;
//
// Create the queue for this server endpoint:
//
hr = ServerSetupQueue( pEndpoint,
pEndpoint->wsMachine,
pEndpoint->wsQName,
pSecurityDescriptor,
dwEndpointFlags );
Status = MQ_MapStatusCode(hr,RPC_S_CANT_CREATE_ENDPOINT);
//
// Check to see if this queue is temporary. If so, then
// register it with RPCSS to delete.
//
if ((Status == RPC_S_OK) && !(RPC_C_MQ_PERMANENT & dwEndpointFlags))
{
MQ_RegisterQueueToDelete(pEndpoint->wsQFormat,wsMachine);
}
}
//
// If the endpoint is going to async initialize async part
// and add the socket to the IO completion port.
//
if (Status == RPC_S_OK)
{
int cMaxIos;
int cMinIos;
ASSERT(fAsync || fClient);
// Step one, figure out the high and low mark for ios.
if (fAsync)
{
// PERF REVIEW these parameters.
cMaxIos = 2
+ (gfServerPlatform == TRUE) * 2
+ (fClient == FALSE) * gNumberOfProcessors;
// This should be larger than zero so that we'll generally submit new
// recvs during idle time rather then just after receiving a datagram.
cMinIos = 1 + (fClient == FALSE ) * (gNumberOfProcessors/2);
cMinIos = 1;
cMaxIos = 1;
}
else
{
// For sync endpoints we need to allocate a single datagram
// object for the receive.
cMinIos = 0;
cMaxIos = 1;
}
// ASSERT(cMinIos < cMaxIos); Not currently true...
pEndpoint->cMinimumIos = cMinIos;
pEndpoint->cMaximumIos = cMaxIos;
// Allocate a chunk on memory to hold the array of datagrams
// PERF: For clients, allocate larger array but don't submit all
// the IOs unless we determine that the port is "really" active.
pEndpoint->aDatagrams = new MQ_DATAGRAM[cMaxIos];
if (pEndpoint->aDatagrams)
{
UINT type;
type = DATAGRAM | RECEIVE;
type |= (fClient) ? CLIENT : SERVER;
for (i = 0; i < cMaxIos; i++)
{
pDatagram = &pEndpoint->aDatagrams[i];
pDatagram->id = id;
pDatagram->type = type;
pDatagram->pEndpoint = pEndpoint;
pDatagram->Busy = 0;
pDatagram->pPacket = 0;
pDatagram->dwPacketSize = 0;
memset(&pDatagram->Read, 0, sizeof(pDatagram->Read));
pDatagram->Read.pAsyncObject = pDatagram;
}
if (fAsync)
{
Status = COMMON_PrepareNewHandle((HANDLE)pEndpoint->hQueue);
}
else
{
// The receive operation on sync endpoints will may span
// several receives. This means it can't use the thread
// event, so allocate an event for the receive.
HANDLE hEvent = CreateEvent(0, TRUE, FALSE, 0);
if (!hEvent)
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
ASSERT(pDatagram == &pEndpoint->aDatagrams[0]);
pDatagram->Read.ol.hEvent = hEvent;
}
}
}
else
{
Status = RPC_S_OUT_OF_MEMORY;
}
}
// If adding a new failure case here, add code to close the sync receive event.
if (Status != RPC_S_OK)
{
delete pEndpoint->aDatagrams;
return Status;
}
return(RPC_S_OK);
}
void RPC_ENTRY
MQ_ServerAbortListen(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint
)
/*++
Routine Description:
Callback after DG_CreateEndpoint has completed successfully
but the runtime for some reason is not going to be able to
listen on the endpoint.
--*/
{
HRESULT hr;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)ThisEndpoint;
#ifdef MAJOR_DBG
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "MQ_ServerAbortListen(): %S\n",
pEndpoint->wsQName));
#endif
ASSERT(pEndpoint->cPendingIos == 0);
ASSERT(pEndpoint->hQueue);
ASSERT(pEndpoint->pNext == 0);
ASSERT(pEndpoint->type & SERVER);
if (pEndpoint->pAddressVector)
{
delete pEndpoint->pAddressVector;
}
if (pEndpoint->aDatagrams)
{
delete pEndpoint->aDatagrams;
}
hr = ServerCloseQueue(pEndpoint);
return;
}
RPC_STATUS RPC_ENTRY
MQ_ClientCloseEndpoint(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint
)
/*++
Routine Description:
Called on sync client endpoints when they are no longer needed.
Arguments:
ThisEndpoint
Return Value:
RPC_S_OK
--*/
{
HRESULT hr;
PMQ_DATAGRAM_ENDPOINT pEndpoint = (PMQ_DATAGRAM_ENDPOINT)ThisEndpoint;
PMQ_DATAGRAM pDatagram = &pEndpoint->aDatagrams[0];
ASSERT((pEndpoint->type & TYPE_MASK) == CLIENT);
ASSERT(pEndpoint->hQueue); // MQOpenQueue must have worked
ASSERT(pEndpoint->cMinimumIos == 0);
ASSERT(pEndpoint->cMaximumIos == 1); // Must not be async!
ASSERT(pEndpoint->aDatagrams);
// ASSERT(pEndpoint->Endpoint == 0);
ASSERT(pEndpoint->pAddressVector == 0);
// ASSERT(pEndpoint->pNext == 0);
// Close & delete the client queue:
hr = ClientCloseQueue(pEndpoint);
// Free the receive buffer if allocated
if (pDatagram->pPacket)
{
I_RpcTransDatagramFree(pEndpoint,
(BUFFER)pDatagram->pPacket,
pDatagram->pAddress
);
}
if (pDatagram->Read.ol.hEvent)
{
CloseHandle(pDatagram->Read.ol.hEvent);
}
delete pDatagram;
pEndpoint->aDatagrams = 0;
return(RPC_S_OK);
}
RPC_STATUS RPC_ENTRY
MQ_ServerListen(
IN OUT DG_TRANSPORT_ENDPOINT ThisEndpoint,
IN RPC_CHAR *NetworkAddress,
IN OUT RPC_CHAR **ppEndpoint,
IN void *pSecurityDescriptor,
IN ULONG EndpointFlags,
IN ULONG NICFlags,
OUT NETWORK_ADDRESS_VECTOR **ppNetworkAddressVector
)
/*++
Routine Description:
Creates a server endpoint object to receive packets. New
packets won't actually arrive until CompleteListen is
called.
Arguments:
ThisEndpoint - Storage for the server endpoint object.
ppEndpoint - The RPC_CHAR name of the endpoint to listen
on or a pointer to 0 if the transport should choose
the address. Contains the endpoint listened to on
output. The caller should free this.
EndpointFlags - Application flags passed into RPC via
RpcServerUseProtseq*Ex.
NICFlags - Application flags passed into RPC via
RpcServerUseProtseq*Ex.
pNetworkAddresses - A vector of the network addresses
listened on by this call. This vector does
not need to be freed.
Return Value:
RPC_S_OK
RPC_S_OUT_OF_MEMORY
RPC_S_OUT_OF_RESOURCES
RPC_S_CANT_CREATE_ENDPOINT
RPC_S_INVALID_ENDPOINT_FORMAT
RPC_S_DUPLICATE_ENDPOINT
--*/
{
RPC_STATUS Status;
DWORD dwSize;
UUID uuid;
RPC_CHAR *pUuidStr;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)ThisEndpoint;
*ppNetworkAddressVector = 0;
if (*ppEndpoint)
{
// Known Endpoint:
RpcpStringCopy(pEndpoint->wsQName,*ppEndpoint);
}
else
{
// Dynamic Endpoint:
Status = UuidCreate(&uuid);
if ((Status != RPC_S_OK) && (Status != RPC_S_UUID_LOCAL_ONLY))
{
return Status;
}
Status = UuidToString(&uuid,&pUuidStr);
if (Status != RPC_S_OK)
{
return Status;
}
RpcpStringCopy(pEndpoint->wsQName,TEXT("RpcSvr-"));
RpcpStringCat(pEndpoint->wsQName,pUuidStr);
RpcStringFree(&pUuidStr);
dwSize = (1+RpcpStringLength(pEndpoint->wsQName))*(sizeof(RPC_CHAR));
*ppEndpoint = (RPC_CHAR*)I_RpcAllocate(dwSize);
if (!*ppEndpoint)
{
return RPC_S_OUT_OF_MEMORY;
}
RpcpStringCopy(*ppEndpoint,pEndpoint->wsQName);
}
//
// Actually create the endpoint
//
Status = MQ_CreateEndpoint( pEndpoint,
NULL,
pSecurityDescriptor,
EndpointFlags,
MSMQ,
FALSE, // Server
TRUE ); // Async
if (Status != RPC_S_OK)
{
return Status;
}
Status = MQ_BuildAddressVector(&pEndpoint->pAddressVector);
if (Status != RPC_S_OK)
{
MQ_ServerAbortListen(ThisEndpoint);
return Status;
}
*ppNetworkAddressVector = pEndpoint->pAddressVector;
#if FALSE
// If needed, figure out the dynamically allocated endpoint.
if (!*pPort)
{
*pPort = new RPC_CHAR[IP_MAXIMUM_ENDPOINT];
if (!*pPort)
{
MQ_ServerAbortListen(ThisEndpoint);
return(RPC_S_OUT_OF_MEMORY);
}
port = ntohs(addr.inetaddr.sin_port);
PortNumberToEndpoint(port, *pPort);
}
// Figure out the network addresses
status = IP_BuildAddressVector(&pEndpoint->pAddressVector);
if (status != RPC_S_OK)
{
MQ_ServerAbortListen(ThisEndpoint);
return(status);
}
*ppNetworkAddressVector = pEndpoint->pAddressVector;
#endif
return RPC_S_OK;
}
RPC_STATUS
MQ_QueryEndpoint
(
IN void * pOriginalEndpoint,
OUT RPC_CHAR * pClientEndpoint
)
{
MQ_ADDRESS *pAddress = (MQ_ADDRESS*)pOriginalEndpoint;
if (!RpcpStringLength(pAddress->wsQName))
{
ParseQueuePathName(pAddress->wsMsgLabel,
pAddress->wsMachine,
pAddress->wsQName);
}
RpcpStringCopy(pClientEndpoint,pAddress->wsQName);
return RPC_S_OK;
}
RPC_STATUS
MQ_QueryAddress
(
IN void * pOriginalEndpoint,
OUT RPC_CHAR * pClientAddress
)
{
MQ_ADDRESS *pAddress = (MQ_ADDRESS*)pOriginalEndpoint;
if (!RpcpStringLength(pAddress->wsMachine))
{
ParseQueuePathName(pAddress->wsMsgLabel,
pAddress->wsMachine,
pAddress->wsQName);
}
RpcpStringCopy(pClientAddress,pAddress->wsMachine);
return RPC_S_OK;
}
RPC_STATUS
RPC_ENTRY
MQ_ClientInitializeAddress
(
OUT DG_TRANSPORT_ADDRESS pvAddress,
IN RPC_CHAR *pNetworkAddress,
IN RPC_CHAR *pEndpoint,
IN BOOL fUseCache,
IN BOOL fBroadcast
)
/*++
Routine Description:
Initializes a address object for sending to a server.
Arguments:
pvAddress - Storage for the address
pNetworkAddress - The address of the server or 0 if local
pEndpoint - The endpoint of the server
fUseCache - If TRUE then the transport may use a cached
value from a previous call on the same NetworkAddress.
fBroadcast - If TRUE, NetworkAddress is ignored and a broadcast
address is used.
Return Value:
RPC_S_OK - Success, name resolved and, optionally, added to cache.
RPC_P_FOUND_IN_CACHE - Success, returned only if fUseCache is TRUE
and the was name found in local cache.
RPC_P_MATCHED_CACHE - Partial success, fUseCache is FALSE and the
result of the lookup was the same as the value previously
in the cache.
RPC_S_OUT_OF_MEMORY
RPC_S_OUT_OF_RESOURCES
RPC_S_INVALID_ENDPOINT_FORMAT
RPC_S_SERVER_UNAVAILABLE
--*/
{
RPC_STATUS Status = RPC_S_OK;
MQ_ADDRESS *pAddress = (MQ_ADDRESS*)pvAddress;
ASSERT(pvAddress);
Status = ConnectToServerQueue(pAddress,pNetworkAddress,pEndpoint);
return Status;
}
RPC_STATUS
RPC_ENTRY
MQ_ClientOpenEndpoint(
OUT DG_TRANSPORT_ENDPOINT ThisEndpoint,
IN BOOL fAsync,
DWORD Flags
)
{
RPC_STATUS Status;
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)ThisEndpoint;
Status = MQ_CreateEndpoint(pEndpoint, NULL, NULL, 0, MSMQ, TRUE, fAsync);
return Status;
}
RPC_STATUS
RPC_ENTRY
MQ_GetEndpointStats(
IN DG_TRANSPORT_ENDPOINT ThisEndpoint,
OUT DG_ENDPOINT_STATS * pStats
)
{
pStats->PreferredPduSize = MQ_PREFERRED_PDU_SIZE;
pStats->MaxPduSize = MQ_MAX_PDU_SIZE;
pStats->MaxPacketSize = MQ_MAX_PACKET_SIZE;
pStats->ReceiveBufferSize = MQ_RECEIVE_BUFFER_SIZE;
return RPC_S_OK;
}
//----------------------------------------------------------------
// MQ_InquireAuthClient()
//
// Fill out security information for the transport.
//
// NOTE: The returned SID (ppSid) is a pointer to the one in the
// client endpoint. The caller can't free it and should make
// its own copy...
//----------------------------------------------------------------
RPC_STATUS
RPC_ENTRY
MQ_InquireAuthClient( void *pvClientEndpoint,
RPC_CHAR **ppPrincipal,
SID **ppSid,
ULONG *pulAuthnLevel,
ULONG *pulAuthnService,
ULONG *pulAuthzService )
{
RPC_STATUS Status = RPC_S_OK;
MQ_ADDRESS *pClientEndpoint = (MQ_ADDRESS*)(pvClientEndpoint);
SID *pClientSid;
DWORD dwSize;
ASSERT(pulAuthnLevel);
ASSERT(pulAuthnService);
ASSERT(pulAuthzService);
if (pClientEndpoint)
{
*ppPrincipal = NULL;
*pulAuthnService = RPC_C_AUTHN_MQ;
*pulAuthzService = RPC_C_AUTHZ_NONE;
//
// The authentication level:
//
if (pClientEndpoint->fAuthenticated)
{
if (pClientEndpoint->ulPrivacyLevel == MQMSG_PRIV_LEVEL_BODY)
*pulAuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
else
*pulAuthnLevel = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
}
else if (pClientEndpoint->ulPrivacyLevel == MQMSG_PRIV_LEVEL_BODY)
{
*pulAuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
}
else
{
*pulAuthnLevel = RPC_C_AUTHN_LEVEL_NONE;
}
if ( IsValidSid((PSID)(pClientEndpoint->aSidBuffer)) )
*ppSid = (SID*)(pClientEndpoint->aSidBuffer);
else
*ppSid = NULL;
}
else
Status = RPC_S_BINDING_HAS_NO_AUTH;
return Status;
}