windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/dgpkt.cxx

2192 lines
56 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1994 - 1999
//
// File: dgpkt.cxx
//
//--------------------------------------------------------------------------
/*++
Module Name:
dgpkt.cxx
Abstract:
Author:
Jeff Roberts (jroberts) 22-May-1995
Revision History:
22-May-1995 jroberts
Created this module.
09-Jul-1997 edwardr
Added support for large packets (>65535) for Falcon/RPC.
--*/
#include <precomp.hxx>
#include <dgpkt.hxx>
unsigned long ProcessStartTime;
unsigned RandomCounter = 0x6789abce;
const unsigned
RpcToPacketFlagsArray[8] =
{
0,
DG_PF_IDEMPOTENT,
DG_PF_BROADCAST,
DG_PF_IDEMPOTENT | DG_PF_BROADCAST,
DG_PF_MAYBE,
DG_PF_IDEMPOTENT | DG_PF_MAYBE,
DG_PF_BROADCAST | DG_PF_MAYBE,
DG_PF_IDEMPOTENT | DG_PF_BROADCAST | DG_PF_MAYBE,
};
const unsigned
PacketToRpcFlagsArray[8] =
{
0 | 0 | 0 ,
RPC_NCA_FLAGS_MAYBE | 0 | 0 ,
0 | RPC_NCA_FLAGS_IDEMPOTENT | 0 ,
RPC_NCA_FLAGS_MAYBE | RPC_NCA_FLAGS_IDEMPOTENT | 0 ,
0 | 0 | RPC_NCA_FLAGS_BROADCAST,
RPC_NCA_FLAGS_MAYBE | 0 | RPC_NCA_FLAGS_BROADCAST,
0 | RPC_NCA_FLAGS_IDEMPOTENT | RPC_NCA_FLAGS_BROADCAST,
RPC_NCA_FLAGS_MAYBE | RPC_NCA_FLAGS_IDEMPOTENT | RPC_NCA_FLAGS_BROADCAST,
};
DG_PACKET_ENGINE::DG_PACKET_ENGINE(
unsigned char a_PacketType,
DG_PACKET * a_Packet,
RPC_STATUS * pStatus
) :
pSavedPacket (a_Packet),
PacketType (a_PacketType),
ReferenceCount (0),
BaseConnection (0),
SourceEndpoint (0),
RemoteAddress (0),
Buffer (0),
BufferLength (0),
QueuedBufferHead (0),
QueuedBufferTail (0),
pReceivedPackets (0),
pLastConsecutivePacket(0),
ConsecutiveDataBytes (0),
ReceiveFragmentBase (0),
CachedPacket (0),
LastReceiveBuffer (0),
LastReceiveBufferLength(0),
Cancelled (FALSE)
{
if (!a_Packet)
{
*pStatus = RPC_S_OUT_OF_MEMORY;
}
if (*pStatus)
{
return;
}
pSavedPacket->Header.RpcVersion = DG_RPC_PROTOCOL_VERSION;
pSavedPacket->Header.PacketFlags = 0;
SetMyDataRep(&pSavedPacket->Header);
#ifdef DEBUGRPC
BasePacketFlags = ~0;
#endif
}
void
DG_PACKET_ENGINE::ReadConnectionInfo(
DG_COMMON_CONNECTION * a_Connection,
DG_TRANSPORT_ADDRESS a_RemoteAddress
)
{
BaseConnection = a_Connection;
RemoteAddress = a_RemoteAddress;
CurrentPduSize = 0;
SetFragmentLengths();
pSavedPacket->Header.InterfaceHint = 0xffff;
RpcpMemoryCopy( &pSavedPacket->Header.ActivityId,
&a_Connection->ActivityNode.Uuid,
sizeof(UUID)
);
}
DG_PACKET_ENGINE::~DG_PACKET_ENGINE(
)
{
ASSERT( !LastReceiveBuffer );
ASSERT( !pReceivedPackets );
ASSERT( !QueuedBufferHead );
ASSERT( !Buffer );
if (pSavedPacket)
{
FreePacket(pSavedPacket);
}
if (CachedPacket)
{
FreePacket(CachedPacket);
}
CleanupReceiveWindow();
}
void
DG_PACKET_ENGINE::NewCall()
/*++
Routine Description:
A new call dawns.
Arguments:
Return Value:
none
--*/
{
ASSERT( !pLastConsecutivePacket );
ASSERT( !ConsecutiveDataBytes );
ASSERT( !LastReceiveBuffer );
SetFragmentLengths();
Buffer = 0;
fReceivedAllFragments = FALSE;
fRetransmitted = FALSE;
TimeoutCount = 0;
RepeatedFack = 0;
FackSerialNumber = 0;
SendSerialNumber = 0;
ReceiveSerialNumber = 0;
SendWindowBase = 0;
FirstUnsentFragment = 0;
SendBurstLength = SendWindowSize;
ReceiveFragmentBase = 0;
RingBufferBase = 0;
#ifdef DEBUGRPC
for (unsigned i=0; i < MAX_WINDOW_SIZE; i++)
{
FragmentRingBuffer[i].SerialNumber = 0xeeee0000;
FragmentRingBuffer[i].Length = 0xdd000000;
FragmentRingBuffer[i].Offset = 0xb000b000;
}
#endif
BasePacketFlags2 = 0;
}
RPC_STATUS
DG_PACKET_ENGINE::PushBuffer(
PRPC_MESSAGE Message
)
/*++
Function Description:
Submits a buffer to be sent over the network.
If a buffer is in progress, the new buffer is added to the
"pending" list. IF not, the buffer is placed in the "active" slot.
The only time a buffer will not go in the active slot is during an
async pipe call when the app is not waiting for send-complete
notifications before submitting new buffers.
Notes:
The buffer sent will be truncated to the nearest packet if
RPC_BUFFER_PARTIAL is set, and Message.BufferLength is set to
the amount actually sent.
--*/
{
// ASSERT( Buffer == 0 || IsBufferAcknowledged() );
RPC_STATUS Status = 0;
RPC_STATUS FixupStatus = 0;
unsigned FractionalPart = Message->BufferLength % MaxFragmentSize;
unsigned SendLength = Message->BufferLength;
if (Message->RpcFlags & RPC_BUFFER_PARTIAL)
{
SendLength -= FractionalPart;
}
if (!Buffer || IsBufferAcknowledged())
{
SetCurrentBuffer(Message->Buffer,
SendLength,
Message->RpcFlags
);
Status = SendSomeFragments();
}
else
{
QUEUED_BUFFER * Node = new QUEUED_BUFFER;
if (!Node)
{
return RPC_S_OUT_OF_MEMORY;
}
Node->Buffer = Message->Buffer;
Node->BufferLength = SendLength;
Node->BufferFlags = Message->RpcFlags;
Node->Next = 0;
if (QueuedBufferTail)
{
QueuedBufferTail->Next = Node;
}
else
{
ASSERT( !QueuedBufferHead );
QueuedBufferHead = Node;
}
QueuedBufferTail = Node;
Status = 0;
}
FixupStatus = FixupPartialSend(Message);
if (!Status)
{
Status = FixupStatus;
}
return Status;
}
RPC_STATUS
DG_PACKET_ENGINE::PopBuffer(
BOOL fSend
)
/*++
Function Description:
Move the next pending send buffer into the active send buffer slot.
This is a no-op except unless this is an async pipe call and the app
is not waiting for send-complete notifications.
Notes:
The buffer sent will be truncated to the nearest packet if
RPC_BUFFER_PARTIAL is set, and Message.BufferLength is set to
the amount actually sent.
--*/
{
RPC_STATUS Status = 0;
RPC_MESSAGE Message;
Message.Buffer = Buffer;
Message.BufferLength = BufferLength;
QUEUED_BUFFER * Node = QueuedBufferHead;
if (Node)
{
QueuedBufferHead = Node->Next;
if (!QueuedBufferHead)
{
QueuedBufferTail = 0;
}
SetCurrentBuffer(Node->Buffer, Node->BufferLength, Node->BufferFlags);
if (fSend)
{
Status = SendSomeFragments();
}
delete Node;
}
else
{
Buffer = 0;
BufferLength = 0;
BufferFlags = 0;
}
if (Message.Buffer)
{
CommonFreeBuffer(&Message);
}
return Status;
}
RPC_STATUS
DG_PACKET_ENGINE::FixupPartialSend(
RPC_MESSAGE * Message
)
/*++
Function Description:
This fn "does the right thing" with the unsent bit at the end of
a pipe send. For sync sends, this means moving the unsent bit
to the front of the existing buffer. For async sends, this means
allocating a new buffer and copying the unsent bit into it.
--*/
{
if (!(Message->RpcFlags & RPC_BUFFER_PARTIAL))
{
// We need this so Receive will be passed a null buffer
// unless the stub sticks one in the message.
Message->Buffer = 0;
Message->BufferLength = 0;
return RPC_S_OK;
}
unsigned FractionalPart;
// if (Message->RpcFlags & RPC_BUFFER_ASYNC)
// {
RPC_MESSAGE NewMessage;
FractionalPart = Message->BufferLength % MaxFragmentSize;
if (!FractionalPart)
{
Message->Buffer = 0;
Message->BufferLength = 0;
return RPC_S_OK;
}
DG_PACKET * Packet = DG_PACKET::AllocatePacket(CurrentPduSize);
if (!Packet)
{
return RPC_S_OUT_OF_MEMORY;
}
RpcpMemoryCopy(Packet->Header.Data,
((char *) Message->Buffer) + (Message->BufferLength - FractionalPart),
FractionalPart
);
Message->Buffer = Packet->Header.Data;
Message->BufferLength = FractionalPart;
// }
// else
// {
// char * Temp = (char *) Message->Buffer;
//
// FractionalPart = Message->BufferLength - FirstUnsentOffset;
// if (!FractionalPart)
// {
// return RPC_S_OK;
// }
//
// RpcpMemoryMove(Message->Buffer, Temp + FirstUnsentOffset, FractionalPart);
// Message->BufferLength = FractionalPart;
// }
return RPC_S_OK;
}
void
DG_PACKET_ENGINE::SetCurrentBuffer(
void * a_Buffer,
unsigned a_BufferLength,
unsigned long a_BufferFlags
)
{
Buffer = a_Buffer;
BufferLength = a_BufferLength;
BufferFlags = a_BufferFlags;
TimeoutCount = 0;
SendWindowBits = 0;
FirstUnsentOffset = 0;
if (BufferLength == 0)
{
FinalSendFrag = SendWindowBase;
}
else
{
FinalSendFrag = SendWindowBase + (BufferLength-1) / MaxFragmentSize;
}
}
RPC_STATUS
DG_PACKET_ENGINE::CommonGetBuffer(
RPC_MESSAGE * Message
)
{
unsigned char SubjectType;
void * Subject;
unsigned Length;
PDG_PACKET pPacket;
Length = sizeof(NCA_PACKET_HEADER)
+ Align8(Message->BufferLength)
+ SecurityTrailerSize;
unsigned BaseLength = BaseConnection->TransportInterface->BasePduSize;
// if (Length <= BaseLength)
// {
// pPacket = DG_PACKET::AllocatePacket(BaseLength);
// }
// else if (Length <= CurrentPduSize)
// {
// pPacket = AllocatePacket();
// }
// else
{
pPacket = DG_PACKET::AllocatePacket(Length);
}
if (0 == pPacket)
{
return RPC_S_OUT_OF_MEMORY;
}
//
// Point the buffer at the appropriate place in the packet.
//
Message->Buffer = pPacket->Header.Data;
// if (pPacket->MaxDataLength < 256)
// {
// AddActivePacket(pPacket);
// }
return RPC_S_OK;
}
void
DG_PACKET_ENGINE::CommonFreeBuffer(
RPC_MESSAGE * Message
)
{
if (!Message->Buffer)
{
return;
}
PDG_PACKET Packet = DG_PACKET::FromStubData(Message->Buffer);
LogEvent(SU_ENGINE, EV_PROC, this, Message->Buffer, 'F' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
ASSERT( Packet->MaxDataLength < 0x7fffffffUL );
// if (Packet && Packet->MaxDataLength < 256)
// {
// RemoveActivePacket(Packet);
// }
FreePacket(Packet);
if (Message->Buffer == LastReceiveBuffer)
{
LastReceiveBuffer = 0;
LastReceiveBufferLength = 0;
}
Message->Buffer = 0;
}
RPC_STATUS
DG_PACKET_ENGINE::CommonReallocBuffer(
IN RPC_MESSAGE * Message,
IN unsigned int NewSize
)
{
if (Message->Buffer == LastReceiveBuffer &&
NewSize <= LastReceiveBufferLength)
{
Message->BufferLength = NewSize;
return RPC_S_OK;
}
RPC_STATUS Status;
RPC_MESSAGE NewMessage;
NewMessage.BufferLength = NewSize;
Status = CommonGetBuffer(&NewMessage);
if (RPC_S_OK != Status)
{
return Status;
}
LastReceiveBuffer = NewMessage.Buffer;
LastReceiveBufferLength = NewMessage.BufferLength;
if (NewSize >= Message->BufferLength)
{
RpcpMemoryCopy(NewMessage.Buffer,
Message->Buffer,
Message->BufferLength
);
}
CommonFreeBuffer(Message);
Message->Buffer = NewMessage.Buffer;
Message->BufferLength = NewMessage.BufferLength;
return RPC_S_OK;
}
void
DG_PACKET_ENGINE::CleanupSendWindow()
{
while (Buffer)
{
PopBuffer(FALSE);
}
}
void
DG_PACKET_ENGINE::CleanupReceiveWindow()
{
//
// Free any response packets.
//
while (pReceivedPackets)
{
PDG_PACKET Next = pReceivedPackets->pNext;
FreePacket(pReceivedPackets);
pReceivedPackets = Next;
}
pLastConsecutivePacket = 0;
ConsecutiveDataBytes = 0;
}
RPC_STATUS
DG_PACKET_ENGINE::SendSomeFragments()
/*++
Routine Description:
Sends some fragments of the user buffer.
Arguments:
Return Value:
result of the send operation
--*/
{
RPC_STATUS Status = RPC_S_OK;
unsigned short i = 0;
unsigned short AckFragment;
unsigned short Frag;
unsigned short Remainder;
unsigned Offset;
unsigned FragmentsSent = 0;
#ifdef DEBUGRPC
if (!Buffer &&
(BufferFlags & RPC_BUFFER_PARTIAL))
{
// return RPC_S_SEND_INCOMPLETE;
RpcpBreakPoint();
}
#endif
if (SendBurstLength > SendWindowSize)
{
SendBurstLength = SendWindowSize;
}
//
// If we can extend the window, do so; otherwise, resend old packets.
//
if (FirstUnsentFragment <= FinalSendFrag &&
FirstUnsentFragment < SendWindowBase + SendWindowSize)
{
unsigned short ThisBurstLength;
Frag = FirstUnsentFragment;
ThisBurstLength = SendBurstLength;
if (ThisBurstLength > FinalSendFrag + 1 - Frag)
{
ThisBurstLength = FinalSendFrag + 1 - Frag;
}
if (Frag + ThisBurstLength > SendWindowBase + SendWindowSize)
{
ThisBurstLength = (SendWindowBase + SendWindowSize) - Frag;
}
while (++FragmentsSent <= ThisBurstLength && Status == RPC_S_OK)
{
if (FragmentsSent == ThisBurstLength &&
(Frag != FinalSendFrag || (BufferFlags & RPC_BUFFER_PARTIAL) || (BasePacketFlags2 & DG_PF2_UNRELATED)))
{
Status = SendFragment(Frag, PacketType, TRUE);
}
else
{
Status = SendFragment(Frag, PacketType, FALSE);
}
++Frag;
}
//
// Cut down the burst length if our window is maxed out.
//
if (Frag - SendWindowBase >= SendWindowSize)
{
SendBurstLength = (1+SendBurstLength)/2;
}
}
if (0 == FragmentsSent && !IsBufferAcknowledged())
{
// We can get here if all the unacknowledged fragments have serial
// numbers greater than the one the client last acknowledged, and
// the window is also maxed out.
//
// This could mean the network is very slow, or the unack'ed packets
// have been lost.
//
Status = SendFragment(SendWindowBase, PacketType, TRUE);
}
return Status;
}
RPC_STATUS
DG_PACKET_ENGINE::SendFragment(
unsigned FragNum,
unsigned char PacketType,
BOOL fFack
)
{
NCA_PACKET_HEADER PriorData;
UNALIGNED NCA_PACKET_HEADER __RPC_FAR * pHeader;
//
// Figure out where the packet starts and how long it is.
//
unsigned Offset;
unsigned Length;
unsigned Index = (FragNum - SendWindowBase + RingBufferBase) % MAX_WINDOW_SIZE;
unsigned DistanceToEnd;
if (FragNum < FirstUnsentFragment)
{
Offset = FragmentRingBuffer[Index].Offset;
Length = FragmentRingBuffer[Index].Length;
DistanceToEnd = BufferLength - Offset;
#ifdef DEBUGRPC
if (Offset >= 0xb000b000 || Length >= 0xdd000000)
{
RpcpBreakPoint();
}
#endif
fRetransmitted = TRUE;
}
else
{
ASSERT(FragNum == FirstUnsentFragment);
Offset = FirstUnsentOffset;
Length = MaxFragmentSize;
DistanceToEnd = BufferLength - Offset;
if (DistanceToEnd < Length)
{
Length = DistanceToEnd;
}
FirstUnsentOffset += Length;
FirstUnsentFragment = 1 + FragNum;
}
if (BufferLength)
{
// this is harmless and can sometimes be triggered on the first call of an activity
// ASSERT(Length);
// ASSERT(Offset < BufferLength);
}
else
{
ASSERT(!Length);
ASSERT(!Offset);
}
//
// Time to start assembling the buffer.
//
pHeader = (PNCA_PACKET_HEADER) (PCHAR(Buffer) - sizeof(NCA_PACKET_HEADER));
*pHeader = pSavedPacket->Header;
pHeader->PacketType = PacketType;
pHeader->PacketFlags = BasePacketFlags;
pHeader->PacketFlags2 = BasePacketFlags2;
if (FinalSendFrag != 0 ||
(BufferFlags & RPC_BUFFER_PARTIAL))
{
pHeader->PacketFlags |= DG_PF_FRAG;
if (FragNum == FinalSendFrag &&
0 == (BufferFlags & RPC_BUFFER_PARTIAL))
{
pHeader->PacketFlags |= DG_PF_LAST_FRAG;
}
}
pHeader->SetPacketBodyLen (Length);
pHeader->SetFragmentNumber((unsigned short) FragNum);
if (FALSE == fFack)
{
pHeader->PacketFlags |= DG_PF_NO_FACK;
}
AddSerialNumber(pHeader);
RPC_STATUS Status;
//
// Stub data is encrypted in-place; we need not to encrypt the original data
// so we can retransmit it if necessary.
//
unsigned Frag = (pHeader->PacketType << 16) | pHeader->GetFragmentNumber();
LogEvent(SU_ENGINE, EV_PKT_OUT, this, 0, Frag);
if (BaseConnection->ActiveSecurityContext &&
BaseConnection->ActiveSecurityContext->AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
RpcpMemoryCopy(&pSavedPacket->Header, pHeader, sizeof(NCA_PACKET_HEADER));
RpcpMemoryCopy(pSavedPacket->Header.Data, pHeader->Data + Offset, pHeader->GetPacketBodyLen());
Status = BaseConnection->SealAndSendPacket(SourceEndpoint, RemoteAddress, &pSavedPacket->Header, 0);
}
else
{
Status = BaseConnection->SealAndSendPacket(SourceEndpoint, RemoteAddress, pHeader, Offset);
}
FragmentRingBuffer[Index].SerialNumber = SendSerialNumber;
FragmentRingBuffer[Index].Length = Length;
FragmentRingBuffer[Index].Offset = Offset;
++SendSerialNumber;
if (Status)
{
LogError(SU_ENGINE, EV_STATUS, this, 0, Status);
}
return Status;
}
RPC_STATUS
DG_PACKET_ENGINE::UpdateSendWindow(
PDG_PACKET pPacket,
BOOL * pfUpdated
)
/*++
Routine Description:
Update the send window based upon a received FACK or NOCALL.
The caller should filter out other packet types.
Arguments:
pPacket - the packet received
Return Value:
return code:
TRUE if PDU size or window size changed
FALSE if not
--*/
{
RPC_STATUS Status = 0;
FACK_BODY_VER_0 PAPI * pBody = (FACK_BODY_VER_0 PAPI *) pPacket->Header.Data;
*pfUpdated = FALSE;
unsigned short Diff;
unsigned short RemoteBase = 1+pPacket->GetFragmentNumber();
ASSERT(pPacket->TimeReceived == 0x31415926);
//
// Check that we can understand this packet.
//
if (0 != pPacket->GetPacketBodyLen())
{
//
// Version 0 and version 1 are identical.
//
if (0 != pBody->Version &&
1 != pBody->Version)
{
#ifdef DEBUGRPC
PrintToDebugger("RPC DG: warning - FACK body version %u\n", pBody->Version);
#endif
pPacket->SetPacketBodyLen(0);
}
else if (pPacket->GetPacketBodyLen() < sizeof(FACK_BODY_VER_0))
{
#ifdef DEBUGRPC
PrintToDebugger("RPC DG: warning - FACK body truncated\n");
#endif
pPacket->SetPacketBodyLen(0);
}
else if (pPacket->GetPacketBodyLen() < sizeof(FACK_BODY_VER_0) + pBody->AckWordCount * sizeof(unsigned long))
{
#ifdef DEBUGRPC
PrintToDebugger("RPC DG: warning - FACK body length inconsistent\n");
#endif
pPacket->SetPacketBodyLen(0);
}
else
{
if (NeedsByteSwap(&pPacket->Header))
{
ByteSwapFackBody0(pBody);
}
//
// NT 1057 used 0xffff to signal no packets have been received.
// This doesn't match OSF.
//
if (0xffff == pBody->SerialNumber)
{
pBody->SerialNumber = 0;
}
//
// If the other guy is resending the same FACK, we should resend.
// If it's different, then we are likely OK.
//
if (pBody->SerialNumber == FackSerialNumber)
{
goto send;
}
if (pBody->SerialNumber < FackSerialNumber)
{
FackSerialNumber = pBody->SerialNumber;
goto dont_send;
}
FackSerialNumber = pBody->SerialNumber;
}
}
//
// Update send window.
//
if (RemoteBase < SendWindowBase)
{
//
// Fragments previously acknowledged are now missing. Either this
// packet was delivered out of order, or the server crashed and
// restarted. Ignore the packet.
//
goto dont_send;
}
if (RemoteBase > FirstUnsentFragment)
{
#ifdef DEBUGRPC
PrintToDebugger("RPC DG: bogus FACK packet received\n");
#endif
goto dont_send;
}
//
// We are moving the window base forward. We need to advance the
// ring buffer base by the same amount, and clear the entries
// corresponding to unsent packets.
//
Diff = RemoteBase - SendWindowBase;
ASSERT(Diff <= MAX_WINDOW_SIZE);
#ifdef DEBUGRPC
while (Diff)
{
FragmentRingBuffer[RingBufferBase].SerialNumber |= 0xeeee0000;
FragmentRingBuffer[RingBufferBase].Length |= 0xdd000000;
FragmentRingBuffer[RingBufferBase].Offset |= 0xb0000000;
++RingBufferBase;
RingBufferBase %= MAX_WINDOW_SIZE;
--Diff;
}
#else
RingBufferBase += Diff;
RingBufferBase %= MAX_WINDOW_SIZE;
#endif
SendWindowBase = RemoteBase;
SendWindowBits = 0;
ASSERT( SendWindowBase <= FirstUnsentFragment );
if (IsBufferAcknowledged())
{
PopBuffer(FALSE);
}
if (0 != pPacket->GetPacketBodyLen())
{
LogEvent(SU_ENGINE, EV_WINDOW, this, (void *) pBody->WindowSize, pBody->Acks[0]);
if (pBody->AckWordCount)
{
//
// Save missing-packet bitmask.
//
SendWindowBits = pBody->Acks[0];
}
//
// Adjust window size.
//
if (pBody->WindowSize > MAX_WINDOW_SIZE)
{
pBody->WindowSize = MAX_WINDOW_SIZE;
}
SendWindowSize = pBody->WindowSize;
if (SendBurstLength > SendWindowSize)
{
SendBurstLength = SendWindowSize;
}
//
// Adjust maximum PDU length.
//
unsigned NewPduSize;
NewPduSize = pBody->MaxDatagramSize;
if (NewPduSize > SourceEndpoint->Stats.PreferredPduSize)
{
NewPduSize = SourceEndpoint->Stats.PreferredPduSize;
}
BaseConnection->CurrentPduSize = (unsigned short) NewPduSize;
BaseConnection->RemoteWindowSize = pBody->WindowSize;
//
// If no packets are getting through, we probably are sending
// packets that are too large.
//
if (0 == RemoteBase &&
0 == SendWindowBits &&
NewPduSize < CurrentPduSize)
{
CurrentPduSize = (unsigned short) NewPduSize;
SetFragmentLengths();
FirstUnsentFragment = 0;
FirstUnsentOffset = 0;
FinalSendFrag = SendWindowBase + (BufferLength-1) / MaxFragmentSize;
}
*pfUpdated = TRUE;
}
send:
Status = SendSomeFragments();
dont_send:
return Status;
}
BOOL
DG_PACKET_ENGINE::UpdateReceiveWindow(
PDG_PACKET pPacket
)
/*++
Routine Description:
Adds a fragment to the receive list, and sends a FACK.
Arguments:
Return Value:
--*/
{
ASSERT(pPacket->TimeReceived == 0x31415926);
//
// Don't retain data from previous pipe buffers.
//
if (pPacket->GetFragmentNumber() < ReceiveFragmentBase)
{
if (0 == (pPacket->Header.PacketFlags & DG_PF_NO_FACK))
{
SendFackOrNocall(pPacket, DG_FACK);
}
return FALSE;
}
//
// Attempt to guess the client's max PDU size. Round down to a multiple
// of eight, for NDR.
//
if (pPacket->DataLength + sizeof(NCA_PACKET_HEADER) > BaseConnection->CurrentPduSize)
{
unsigned RemoteTransportBuffer = BaseConnection->CurrentPduSize * BaseConnection->RemoteWindowSize;
BaseConnection->CurrentPduSize = ((pPacket->DataLength + sizeof(NCA_PACKET_HEADER)) & ~7);
BaseConnection->RemoteWindowSize = RemoteTransportBuffer / BaseConnection->CurrentPduSize;
if (0 == BaseConnection->RemoteWindowSize)
{
BaseConnection->RemoteWindowSize = 1;
}
}
PNCA_PACKET_HEADER pHeader = &pPacket->Header;
unsigned short Serial = ReadSerialNumber(pHeader);
if (Serial > ReceiveSerialNumber)
{
ReceiveSerialNumber = Serial;
}
//
// Authentication levels above AUTHN_LEVEL_PKT will checksum the packet,
// so we must remove these bits from the header.
//
pPacket->Header.PacketFlags &= ~(DG_PF_FORWARDED);
pPacket->Header.PacketFlags2 &= ~(DG_PF2_FORWARDED_2);
//
// Check the easy case: is this a single packet call?
//
if ((pHeader->PacketFlags & DG_PF_FRAG) == 0 &&
(pHeader->PacketFlags & DG_PF_LAST_FRAG) == 0)
{
if (pReceivedPackets)
{
ASSERT( pReceivedPackets->Header.SequenceNumber == pPacket->Header.SequenceNumber );
return FALSE;
}
pReceivedPackets = pPacket;
pLastConsecutivePacket = pPacket;
pPacket->pNext = pPacket->pPrevious = 0;
ConsecutiveDataBytes += pHeader->GetPacketBodyLen();
fReceivedAllFragments = TRUE;
return TRUE;
}
//
// This is a multi-packet call. Insert the packet in pReceivedPackets
// and send a FACK.
//
PDG_PACKET pScan;
PDG_PACKET pTrail;
BOOL PacketAddedToList = TRUE;
if (pReceivedPackets == 0)
{
pReceivedPackets = pPacket;
if (ReceiveFragmentBase == pHeader->GetFragmentNumber())
{
pLastConsecutivePacket = pPacket;
ConsecutiveDataBytes += pHeader->GetPacketBodyLen();
}
pPacket->pNext = pPacket->pPrevious = 0;
}
else
{
//
// Not the first packet to be received. So scan for its place in the
// list.
//
unsigned short FragNum = pHeader->GetFragmentNumber();
if (pLastConsecutivePacket)
{
pScan = pLastConsecutivePacket;
}
else
{
pScan = pReceivedPackets;
}
pTrail = 0;
while (pScan && pScan->GetFragmentNumber() < FragNum)
{
ASSERT(pScan->TimeReceived == 0x31415926);
ASSERT(pScan->Header.SequenceNumber == SequenceNumber);
pTrail = pScan;
pScan = pScan->pNext;
}
if (pScan != 0)
{
if (pScan->GetFragmentNumber() > FragNum)
{
if (pScan->pPrevious &&
pScan->pPrevious->GetFragmentNumber() >= FragNum)
{
//
// The new packet is a duplicate of a preexisting one
// upstream from pLastConsecutivePacket.
//
PacketAddedToList = FALSE;
}
else
{
//
// Our fragment fills a gap in the series.
//
pPacket->pPrevious = pScan->pPrevious;
pPacket->pNext = pScan;
if (pScan->pPrevious == 0)
{
pReceivedPackets = pPacket;
}
else
{
pScan->pPrevious->pNext = pPacket;
}
pScan->pPrevious = pPacket;
}
}
else
{
//
// The new packet is a duplicate of a preexisting one
// downstream from pLastConsecutivePacket.
//
PacketAddedToList = FALSE;
}
}
else
{
//
// The fragnum is larger than everything seen so far.
//
pTrail->pNext = pPacket;
pPacket->pPrevious = pTrail;
pPacket->pNext = 0;
}
}
if (TRUE == PacketAddedToList)
{
//
// Scan the list for the first missing fragment.
//
unsigned short ScanNum;
if (pLastConsecutivePacket)
{
pScan = pLastConsecutivePacket->pNext;
ScanNum = pLastConsecutivePacket->GetFragmentNumber() + 1;
}
else
{
pScan = pReceivedPackets;
ScanNum = ReceiveFragmentBase;
}
while (pScan)
{
if (ScanNum == pScan->GetFragmentNumber())
{
ConsecutiveDataBytes += pScan->GetPacketBodyLen();
pLastConsecutivePacket = pScan;
}
pScan = pScan->pNext;
++ScanNum;
}
//
// We have updated pLastConsecutivePacket; is the whole buffer here?
//
if (pLastConsecutivePacket &&
pLastConsecutivePacket->Header.PacketFlags & DG_PF_LAST_FRAG)
{
fReceivedAllFragments = TRUE;
}
}
ASSERT(pReceivedPackets);
//
// Fack the fragment if necessary.
//
if (0 == (pHeader->PacketFlags & DG_PF_NO_FACK))
{
SendFackOrNocall(pPacket, DG_FACK);
}
return PacketAddedToList;
}
RPC_STATUS
DG_PACKET_ENGINE::SendFackOrNocall(
PDG_PACKET pPacket,
unsigned char PacketType
)
{
unsigned ReceiveWindowSize;
ReceiveWindowSize = SourceEndpoint->Stats.ReceiveBufferSize
/ ((1+SourceEndpoint->NumberOfCalls) * CurrentPduSize);
if (0 == ReceiveWindowSize)
{
ReceiveWindowSize = 1;
}
else if (ReceiveWindowSize > MAX_WINDOW_SIZE)
{
ReceiveWindowSize = MAX_WINDOW_SIZE;
}
pSavedPacket->Header.PacketType = PacketType;
pSavedPacket->Header.SequenceNumber = SequenceNumber;
pSavedPacket->Header.PacketFlags2 = 0;
FACK_BODY_VER_0 PAPI * pBody = (FACK_BODY_VER_0 PAPI *) pSavedPacket->Header.Data;
pBody->Version = 1;
pBody->Pad1 = 0;
pBody->MaxDatagramSize = SourceEndpoint->Stats.PreferredPduSize;
pBody->MaxPacketSize = SourceEndpoint->Stats.MaxPacketSize;
pBody->AckWordCount = 1;
pBody->WindowSize = (unsigned short) ReceiveWindowSize;
if (pPacket)
{
pBody->SerialNumber = ReadSerialNumber(&pPacket->Header);
}
else
{
pBody->SerialNumber = ReceiveSerialNumber;
}
unsigned short FragNum = ReceiveFragmentBase-1;
PDG_PACKET pScan = 0;
if (pLastConsecutivePacket)
{
FragNum = pLastConsecutivePacket->GetFragmentNumber();
pScan = pLastConsecutivePacket->pNext;
}
else if (pReceivedPackets)
{
pScan = pReceivedPackets->pNext;
}
unsigned Bit;
pBody->Acks[0] = 0;
while ( pScan )
{
Bit = pScan->GetFragmentNumber() - FragNum - 1;
pBody->Acks[0] |= (1 << Bit);
pScan = pScan->pNext;
}
if (pBody->Acks[0] == 0)
{
pBody->AckWordCount = 0;
}
pSavedPacket->SetPacketBodyLen( sizeof(FACK_BODY_VER_0) + sizeof(unsigned long) );
pSavedPacket->SetFragmentNumber(FragNum);
AddSerialNumber(&pSavedPacket->Header);
unsigned Frag = (pSavedPacket->Header.PacketType << 16) | pSavedPacket->GetFragmentNumber();
LogEvent(SU_ENGINE, EV_PKT_OUT, this, 0, Frag);
RPC_STATUS Status;
Status = BaseConnection->SealAndSendPacket(SourceEndpoint, RemoteAddress, &pSavedPacket->Header, 0);
if (Status)
{
LogError(SU_ENGINE, EV_STATUS, this, 0, Status);
}
return Status;
}
RPC_STATUS
DG_PACKET_ENGINE::AssembleBufferFromPackets(
RPC_MESSAGE * Message,
CALL * Call
)
/*++
Routine Description:
This function coalesces the list of consecutive packets into a monolithic
buffer.
Arguments:
Message - if .Buffer != 0 , use it. Otherwise allocate one.
Call - in case we need to call GetBuffer
Return Value:
RPC_S_OK - success
RPC_S_OUT_OF_MEMORY - we couldn't allocate or reallocate Message.Buffer
--*/
{
ASSERT( pLastConsecutivePacket );
//
// If only one packet is available, use the packet buffer itself.
//
if (0 == Message->Buffer && 0 == pReceivedPackets->pNext)
{
ASSERT(ConsecutiveDataBytes == pReceivedPackets->GetPacketBodyLen());
Message->Buffer = pReceivedPackets->Header.Data;
Message->BufferLength = ConsecutiveDataBytes;
Message->DataRepresentation = 0x00ffffff & (*(unsigned long PAPI *) &pReceivedPackets->Header.DataRep);
if (0 == (pReceivedPackets->Header.PacketFlags & DG_PF_FRAG))
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE;
}
if (pReceivedPackets->Header.PacketFlags & DG_PF_LAST_FRAG)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE;
}
pReceivedPackets = 0;
pLastConsecutivePacket = 0;
ConsecutiveDataBytes = 0;
++ReceiveFragmentBase;
LastReceiveBuffer = Message->Buffer;
LastReceiveBufferLength = Message->BufferLength;
return RPC_S_OK;
}
//
// Get a buffer if we need it.
//
RPC_STATUS Status;
if (0 == Message->Buffer)
{
ASSERT(0 == (Message->RpcFlags & RPC_BUFFER_EXTRA));
Message->BufferLength = ConsecutiveDataBytes;
Status = Call->GetBuffer(Message, 0);
if (RPC_S_OK != Status)
{
return Status;
}
LastReceiveBuffer = Message->Buffer;
LastReceiveBufferLength = Message->BufferLength;
}
//
// Reallocate the buffer if it is too small.
//
char __RPC_FAR * CopyBuffer = (char __RPC_FAR *) Message->Buffer;
if (Message->RpcFlags & (RPC_BUFFER_EXTRA | RPC_BUFFER_PARTIAL))
{
ASSERT( !LastReceiveBufferLength ||
(CopyBuffer >= LastReceiveBuffer &&
CopyBuffer <= ((char __RPC_FAR *) LastReceiveBuffer) + LastReceiveBufferLength) );
if (0 == (Message->RpcFlags & RPC_BUFFER_EXTRA))
{
Message->BufferLength = 0;
}
unsigned Offset = Message->BufferLength;
CopyBuffer += Offset;
if (CopyBuffer + ConsecutiveDataBytes > ((char __RPC_FAR *) LastReceiveBuffer) + LastReceiveBufferLength)
{
Status = I_RpcReallocPipeBuffer(Message, Offset + ConsecutiveDataBytes);
if (RPC_S_OK != Status)
{
return Status;
}
CopyBuffer = (char __RPC_FAR *) Message->Buffer + Offset;
}
else
{
Message->BufferLength += ConsecutiveDataBytes;
}
}
else
{
Message->BufferLength = ConsecutiveDataBytes;
}
Message->DataRepresentation = 0x00ffffff & (*(unsigned long PAPI *) &pReceivedPackets->Header.DataRep);
{
PDG_PACKET pkt = DG_PACKET::FromStubData(Message->Buffer);
ASSERT( pkt->MaxDataLength >= Message->BufferLength );
}
//
// Copy the stub data into the buffer.
//
#ifdef DEBUGRPC
unsigned long Count = 0;
#endif
PDG_PACKET Packet;
BOOL fLastPacket = FALSE;
do
{
ASSERT(pReceivedPackets->TimeReceived == 0x31415926);
ASSERT(ReceiveFragmentBase == pReceivedPackets->GetFragmentNumber());
if (pReceivedPackets == pLastConsecutivePacket)
{
fLastPacket = TRUE;
if (0 == (pReceivedPackets->Header.PacketFlags & DG_PF_FRAG))
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE;
}
if (pReceivedPackets->Header.PacketFlags & DG_PF_LAST_FRAG)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE;
}
}
unsigned Length = pReceivedPackets->GetPacketBodyLen();
RpcpMemoryCopy(CopyBuffer, pReceivedPackets->Header.Data, Length);
#ifdef DEBUGRPC
Count += Length;
#endif
CopyBuffer += Length;
Packet = pReceivedPackets;
pReceivedPackets = pReceivedPackets->pNext;
ASSERT(!pReceivedPackets || pReceivedPackets->pPrevious == Packet);
FreePacket(Packet);
++ReceiveFragmentBase;
}
while (!fLastPacket);
ASSERT(Count == ConsecutiveDataBytes);
ASSERT(fLastPacket || 0 == Count % 8);
pLastConsecutivePacket = 0;
ConsecutiveDataBytes = 0;
if (pReceivedPackets)
{
pReceivedPackets->pPrevious = 0;
}
return RPC_S_OK;
}
void
DG_PACKET_ENGINE::SetFragmentLengths()
{
PSECURITY_CONTEXT pSecurityContext = BaseConnection->ActiveSecurityContext;
if (0 == pSecurityContext)
{
SecurityTrailerSize = 0;
}
else switch (pSecurityContext->AuthenticationLevel)
{
case RPC_C_AUTHN_LEVEL_NONE:
{
SecurityTrailerSize = 0;
break;
}
case RPC_C_AUTHN_LEVEL_PKT:
case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY:
{
SecurityTrailerSize = (unsigned short) pSecurityContext->MaximumSignatureLength();
SecurityTrailerSize += (unsigned short) Align4(sizeof(DG_SECURITY_TRAILER));
break;
}
case RPC_C_AUTHN_LEVEL_PKT_PRIVACY:
{
SecurityTrailerSize = (unsigned short) pSecurityContext->MaximumHeaderLength();
SecurityTrailerSize += (unsigned short) Align(sizeof(DG_SECURITY_TRAILER), Align4(pSecurityContext->BlockSize()));
break;
}
default:
{
ASSERT(0 && "RPC: unknown protect level");
break;
}
}
if (CurrentPduSize != BaseConnection->CurrentPduSize)
{
CurrentPduSize = (unsigned short) BaseConnection->CurrentPduSize;
SendWindowSize = (unsigned short) BaseConnection->RemoteWindowSize;
}
MaxFragmentSize = CurrentPduSize - sizeof(NCA_PACKET_HEADER);
if (SecurityTrailerSize)
{
MaxFragmentSize -= SecurityTrailerSize;
MaxFragmentSize -= MaxFragmentSize % SECURITY_HEADER_ALIGNMENT;
}
}
void
ByteSwapPacketHeader(
PDG_PACKET pPacket
)
/*++
Routine Description:
Byte swaps the packet header of the specified packet.
Arguments:
pPacket - Pointer to the packet whose header needs byte swapping.
Return Value:
<none>
--*/
{
unsigned long __RPC_FAR * VerNum = (unsigned long __RPC_FAR *) &(pPacket->Header.InterfaceVersion);
ByteSwapUuid(&(pPacket->Header.ObjectId));
ByteSwapUuid(&(pPacket->Header.InterfaceId));
ByteSwapUuid(&(pPacket->Header.ActivityId));
pPacket->Header.ServerBootTime = RpcpByteSwapLong(pPacket->Header.ServerBootTime);
*VerNum = RpcpByteSwapLong(*VerNum);
pPacket->Header.SequenceNumber = RpcpByteSwapLong(pPacket->Header.SequenceNumber);
pPacket->Header.OperationNumber = RpcpByteSwapShort(pPacket->Header.OperationNumber);
pPacket->Header.InterfaceHint = RpcpByteSwapShort(pPacket->Header.InterfaceHint);
pPacket->Header.ActivityHint = RpcpByteSwapShort(pPacket->Header.ActivityHint);
pPacket->Header.PacketBodyLen = RpcpByteSwapShort(pPacket->Header.PacketBodyLen);
pPacket->Header.FragmentNumber = RpcpByteSwapShort(pPacket->Header.FragmentNumber);
}
void
ByteSwapFackBody0(
FACK_BODY_VER_0 __RPC_FAR * pBody
)
{
pBody->WindowSize = RpcpByteSwapShort(pBody->WindowSize);
pBody->MaxDatagramSize = RpcpByteSwapLong (pBody->MaxDatagramSize);
pBody->MaxPacketSize = RpcpByteSwapLong (pBody->MaxPacketSize);
pBody->SerialNumber = RpcpByteSwapShort(pBody->SerialNumber);
pBody->AckWordCount = RpcpByteSwapShort(pBody->AckWordCount);
unsigned u;
for (u=0; u < pBody->AckWordCount; ++u)
{
pBody->Acks[u] = RpcpByteSwapLong (pBody->Acks[u]);
}
}
RPCRTAPI RPC_STATUS RPC_ENTRY
I_RpcTransDatagramAllocate(
IN DG_TRANSPORT_ENDPOINT TransportEndpoint,
OUT BUFFER *pBuffer,
OUT PUINT pBufferLength,
OUT DatagramTransportPair **pAddressPair
)
{
DG_ENDPOINT * Endpoint = DG_ENDPOINT::FromEndpoint(TransportEndpoint);
RPC_DATAGRAM_TRANSPORT * Transport = Endpoint->TransportInterface;
if ( !Endpoint->Stats.PreferredPduSize )
{
RpcpBreakPoint();
}
DG_PACKET * Packet = DG_PACKET::AllocatePacket(Endpoint->Stats.PreferredPduSize
+ Transport->AddressSize
+ sizeof(DatagramTransportPair));
if (!Packet)
{
return RPC_S_OUT_OF_MEMORY;
}
DG_TRANSPORT_ADDRESS Address = DG_TRANSPORT_ADDRESS(Packet->Header.Data - sizeof(NCA_PACKET_HEADER) + Endpoint->Stats.PreferredPduSize);
*pBuffer = &Packet->Header;
*pBufferLength = Endpoint->Stats.PreferredPduSize;
*pAddressPair = (DatagramTransportPair *)((char *)Address + Transport->AddressSize);
(*pAddressPair)->RemoteAddress = Address;
return RPC_S_OK;
}
RPCRTAPI RPC_STATUS RPC_ENTRY
I_RpcTransDatagramAllocate2(
IN DG_TRANSPORT_ENDPOINT TransportEndpoint,
OUT BUFFER *pBuffer,
IN OUT PUINT pBufferLength,
OUT DG_TRANSPORT_ADDRESS *pAddress
)
{
DG_ENDPOINT *pEndpoint = DG_ENDPOINT::FromEndpoint(TransportEndpoint);
RPC_DATAGRAM_TRANSPORT *pTransport = pEndpoint->TransportInterface;
DWORD dwSize = *pBufferLength;
if (dwSize < pEndpoint->Stats.PreferredPduSize)
{
dwSize = pEndpoint->Stats.PreferredPduSize;
}
DG_PACKET * Packet = DG_PACKET::AllocatePacket( dwSize + pTransport->AddressSize);
if (!Packet)
{
return RPC_S_OUT_OF_MEMORY;
}
DG_TRANSPORT_ADDRESS Address = DG_TRANSPORT_ADDRESS(Packet->Header.Data - sizeof(NCA_PACKET_HEADER) + dwSize);
*pBuffer = &Packet->Header;
*pBufferLength = dwSize;
*pAddress = Address;
return RPC_S_OK;
}
RPCRTAPI RPC_STATUS RPC_ENTRY
I_RpcTransDatagramFree(
IN RPC_TRANSPORT_ADDRESS ThisAddress,
IN BUFFER Buffer
)
{
DG_PACKET * Packet = DG_PACKET::FromPacketHeader( Buffer );
Packet->Free();
return RPC_S_OK;
}
RPC_STATUS
DG_PACKET::Initialize(
)
{
return RPC_S_OK;
}
BOOL
DG_PACKET::DeleteIdlePackets(
long CurrentTime
)
/*++
Routine Description:
This fn scans the free packet list for very old packets and deletes them.
Arguments:
none
Return Value:
none
--*/
{
return FALSE;
}
DG_COMMON_CONNECTION::DG_COMMON_CONNECTION(
RPC_DATAGRAM_TRANSPORT *a_TransportInterface,
RPC_STATUS * pStatus
) :
Mutex (pStatus),
TimeStamp (0),
TransportInterface (a_TransportInterface),
ReferenceCount (0),
CurrentPduSize (a_TransportInterface->BasePduSize),
RemoteWindowSize (1),
RemoteDataUpdated (FALSE),
LowestActiveSequence(0),
LowestUnusedSequence(0),
ActiveSecurityContext(0)
{
}
DG_COMMON_CONNECTION::~DG_COMMON_CONNECTION()
{
delete ActiveSecurityContext;
}
RPC_STATUS
SendSecurePacket(
IN DG_ENDPOINT * SourceEndpoint,
IN DG_TRANSPORT_ADDRESS RemoteAddress,
IN UNALIGNED NCA_PACKET_HEADER *pHeader,
IN unsigned long DataOffset,
IN SECURITY_CONTEXT * SecurityContext
)
{
RPC_STATUS Status = RPC_S_OK;
unsigned TrailerLength = 0;
unsigned MaxTrailerSize = 0;
PDG_SECURITY_TRAILER pTrailer = 0;
if (SecurityContext && SecurityContext->AuthenticationLevel > RPC_C_AUTHN_LEVEL_NONE)
{
// Pad the stub data length to a multiple of 8, so the security
// trailer is properly aligned. OSF requires that the pad bytes
// be included in PacketBodyLen.
//
pHeader->SetPacketBodyLen(Align8(pHeader->GetPacketBodyLen()));
pHeader->AuthProto = (unsigned char) SecurityContext->AuthenticationService;
SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
SECURITY_BUFFER SecurityBuffers[5];
DCE_MSG_SECURITY_INFO MsgSecurityInfo;
BufferDescriptor.ulVersion = 0;
BufferDescriptor.cBuffers = 5;
BufferDescriptor.pBuffers = SecurityBuffers;
SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
SecurityBuffers[0].pvBuffer = pHeader;
SecurityBuffers[0].cbBuffer = sizeof(NCA_PACKET_HEADER);
SecurityBuffers[1].BufferType = SECBUFFER_DATA;
SecurityBuffers[1].pvBuffer = pHeader->Data + DataOffset;
SecurityBuffers[1].cbBuffer = pHeader->GetPacketBodyLen();
if (SecurityContext->AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
SecurityBuffers[2].cbBuffer = (ULONG) Align(sizeof(DG_SECURITY_TRAILER), Align4(SecurityContext->BlockSize()));
SecurityBuffers[3].cbBuffer = SecurityContext->MaximumHeaderLength();
}
else
{
SecurityBuffers[2].cbBuffer = (ULONG) Align4(sizeof(DG_SECURITY_TRAILER));
SecurityBuffers[3].cbBuffer = SecurityContext->MaximumSignatureLength();
}
pTrailer = (PDG_SECURITY_TRAILER) _alloca(SecurityBuffers[2].cbBuffer + SecurityBuffers[3].cbBuffer);
SecurityBuffers[2].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
SecurityBuffers[2].pvBuffer = pTrailer;
SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
SecurityBuffers[3].pvBuffer = (unsigned char *) pTrailer
+ SecurityBuffers[2].cbBuffer;
SecurityBuffers[4].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
SecurityBuffers[4].pvBuffer = &MsgSecurityInfo;
SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO);
MsgSecurityInfo.SendSequenceNumber = pHeader->GetFragmentNumber();
MsgSecurityInfo.ReceiveSequenceNumber = SecurityContext->AuthContextId;
MsgSecurityInfo.PacketType = ~0;
TrailerLength = SecurityBuffers[2].cbBuffer;
pTrailer->protection_level = (unsigned char) SecurityContext->AuthenticationLevel;
pTrailer->key_vers_num = (unsigned char) SecurityContext->AuthContextId;
Status = SecurityContext->SignOrSeal (
pHeader->SequenceNumber,
SecurityContext->AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
&BufferDescriptor );
ASSERT( SecurityBuffers[3].cbBuffer > 0 );
TrailerLength += SecurityBuffers[3].cbBuffer;
}
else
{
//
// Unsecure packet.
//
pHeader->AuthProto = 0;
}
if (RPC_S_OK == Status)
{
Status = SourceEndpoint->TransportInterface->Send(
&SourceEndpoint->TransportEndpoint,
RemoteAddress,
pHeader,
sizeof(NCA_PACKET_HEADER),
pHeader->Data + DataOffset,
pHeader->GetPacketBodyLen(),
pTrailer,
TrailerLength
);
}
return Status;
}
RPC_STATUS
VerifySecurePacket(
PDG_PACKET pPacket,
SECURITY_CONTEXT * pSecurityContext
)
{
RPC_STATUS Status = RPC_S_OK;
PDG_SECURITY_TRAILER pVerifier = (PDG_SECURITY_TRAILER)
(pPacket->Header.Data + pPacket->GetPacketBodyLen());
if (pSecurityContext->AuthenticationLevel < RPC_C_AUTHN_LEVEL_PKT)
{
return RPC_S_OK;
}
ASSERT(pVerifier->protection_level >= RPC_C_AUTHN_LEVEL_PKT);
ASSERT(pVerifier->protection_level <= RPC_C_AUTHN_LEVEL_PKT_PRIVACY);
SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
SECURITY_BUFFER SecurityBuffers[5];
DCE_MSG_SECURITY_INFO MsgSecurityInfo;
BufferDescriptor.ulVersion = 0;
BufferDescriptor.cBuffers = 5;
BufferDescriptor.pBuffers = SecurityBuffers;
SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
SecurityBuffers[0].pvBuffer = &pPacket->Header;
SecurityBuffers[0].cbBuffer = sizeof(NCA_PACKET_HEADER);
SecurityBuffers[1].BufferType = SECBUFFER_DATA;
SecurityBuffers[1].pvBuffer = pPacket->Header.Data;
SecurityBuffers[1].cbBuffer = pPacket->GetPacketBodyLen();
SecurityBuffers[2].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
SecurityBuffers[2].pvBuffer = pVerifier;
if (pVerifier->protection_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
unsigned Alignment = Align4(pSecurityContext->BlockSize());
SecurityBuffers[2].cbBuffer = (ULONG) Align(sizeof(DG_SECURITY_TRAILER), Alignment);
SecurityBuffers[3].pvBuffer = AlignPtr(pVerifier + 1, Alignment);
}
else
{
SecurityBuffers[2].cbBuffer = (ULONG) Align4(sizeof(DG_SECURITY_TRAILER));
SecurityBuffers[3].pvBuffer = AlignPtr4(pVerifier + 1);
}
SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
SecurityBuffers[3].cbBuffer = pPacket->DataLength
- SecurityBuffers[1].cbBuffer
- SecurityBuffers[2].cbBuffer;
SecurityBuffers[4].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
SecurityBuffers[4].pvBuffer = &MsgSecurityInfo;
SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO);
MsgSecurityInfo.SendSequenceNumber = pPacket->GetFragmentNumber();
MsgSecurityInfo.ReceiveSequenceNumber = pSecurityContext->AuthContextId;
MsgSecurityInfo.PacketType = ~0;
//
// If the packet came from a big-endian machine, we must restore
// the header to its original condition or the checksum will fail.
//
ByteSwapPacketHeaderIfNecessary(pPacket);
Status = pSecurityContext->VerifyOrUnseal(
pPacket->Header.SequenceNumber,
pVerifier->protection_level != RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
&BufferDescriptor
);
//
// Gotta re-swap the header so we can still look at its fields.
//
ByteSwapPacketHeaderIfNecessary(pPacket);
if (RPC_S_OK != Status)
{
#ifdef DEBUGRPC
DbgPrint("dg rpc: %lx: pkt %lx type %lx has bad security info (error 0x%lx)\n",
GetCurrentProcessId(), pPacket, pPacket->Header.PacketType, Status);
#endif
ASSERT(Status == RPC_S_ACCESS_DENIED ||
Status == ERROR_SHUTDOWN_IN_PROGRESS ||
Status == ERROR_PASSWORD_MUST_CHANGE ||
Status == ERROR_PASSWORD_EXPIRED ||
Status == ERROR_ACCOUNT_DISABLED ||
Status == ERROR_INVALID_LOGON_HOURS);
}
return(Status);
}
BOOL
DG_PickleEEInfoIntoPacket (
IN DG_PACKET * Packet,
IN size_t PickleStartOffset
)
/*++
Function Name: PickeEEInfoIntoPacket
Parameters:
PickleStartOffset - the offset in bytes where the pickling starts
pHeader - pointer to the packet to fill
Description:
Checks for EEInfo on the thread, trims the EEInfo to Packet->MaxDataLength,
and pickles the EEInfo starting from PickleStartOffset.
Returns:
TRUE if EEInfo was pickled. FALSE if not.
--*/
{
BOOL fEEInfoPresent = FALSE;
ExtendedErrorInfo *EEInfo;
RPC_STATUS RpcStatus;
size_t CurrentPacketSize;
EEInfo = RpcpGetEEInfo();
if (EEInfo)
{
AddComputerNameToChain(EEInfo);
TrimEEInfoToLength (Packet->MaxDataLength, &CurrentPacketSize);
if (CurrentPacketSize != 0)
{
CurrentPacketSize += PickleStartOffset;
ASSERT(IsBufferAligned(Packet->Header.Data + PickleStartOffset));
RpcpMemorySet(Packet->Header.Data, 0, CurrentPacketSize);
RpcStatus = PickleEEInfo( EEInfo,
Packet->Header.Data + PickleStartOffset,
CurrentPacketSize - PickleStartOffset
);
if (RpcStatus == RPC_S_OK)
{
fEEInfoPresent = TRUE;
Packet->SetPacketBodyLen( CurrentPacketSize );
}
}
}
return fEEInfoPresent;
}
void
InitErrorPacket(
DG_PACKET * pPacket,
unsigned char PacketType,
RPC_STATUS RpcStatus
)
/*++
Routine Description:
Maps <ProcessStatus> to an NCA error code and sends
a FAULT or REJECT packet to the client, as appropriate.
Arguments:
pSendPacket - a packet to use, or zero if this fn should allocate one
ProcessStatus - NT RPC error code
Return Value:
none
--*/
{
NCA_PACKET_HEADER * pHeader = &pPacket->Header;
CleanupPacket(pHeader);
pHeader->PacketType = PacketType;
size_t FaultSize;
BOOL fEEInfoPresent = FALSE;
//
// This mapping distinguishes client-side shutdown from server-side shutdown.
//
if (RpcStatus == ERROR_SHUTDOWN_IN_PROGRESS)
{
if (PacketType == DG_REJECT)
{
RpcStatus = RPC_S_SERVER_UNAVAILABLE;
}
else
{
RpcStatus = ERROR_SERVER_SHUTDOWN_IN_PROGRESS;
}
}
else if (RpcStatus == RPC_P_CLIENT_SHUTDOWN_IN_PROGRESS)
{
RpcStatus = ERROR_SHUTDOWN_IN_PROGRESS;
}
if (g_fSendEEInfo)
{
fEEInfoPresent = DG_PickleEEInfoIntoPacket( pPacket, FIELD_OFFSET( EXTENDED_FAULT_BODY, EeInfo) );
}
if (fEEInfoPresent)
{
//
// Packet body length already set.
//
EXTENDED_FAULT_BODY * body = (EXTENDED_FAULT_BODY *) pHeader->Data;
body->NcaStatus = MapToNcaStatusCode(RpcStatus);
body->Magic = DG_EE_MAGIC_VALUE;
body->reserved1 = 0;
body->reserved2 = 0;
}
else
{
size_t XopenFaultSize = sizeof(unsigned long);
*(unsigned long *)(pHeader->Data) = MapToNcaStatusCode(RpcStatus);
pHeader->SetPacketBodyLen(XopenFaultSize);
}
}