/*++

Copyright (C) Microsoft Corporation, 1993 - 1999

Module Name:

    dgpkt.hxx

Abstract:

    This file contains the definitions for a dg packet.

Author:

    Dave Steckler (davidst) 3-Mar-1992

Revision History:

    EdwardR    09-Jul-1997    Added support for large packets (Falcon).

--*/

#ifndef __DGPKT_HXX__
#define __DGPKT_HXX__

#include <rpctrans.hxx>
#include <limits.h>
#include <hashtabl.hxx>

typedef MESSAGE_OBJECT * PMESSAGE_OBJECT;

#define MULTITHREADED

#include <threads.hxx>
#include <delaytab.hxx>

#define  DG_RPC_PROTOCOL_VERSION 4

//
// Our code allows only one additional ULONG in a FACK packet body.
//
#define MAX_WINDOW_SIZE (CHAR_BIT * sizeof(unsigned))

//
// delay times
//
#define     TWO_SECS_IN_MSEC       (2 * 1000)
#define   THREE_SECS_IN_MSEC       (3 * 1000)
#define     TEN_SECS_IN_MSEC      (10 * 1000)
#define FIFTEEN_SECS_IN_MSEC      (15 * 1000)

#define   ONE_MINUTE_IN_MSEC      (60 * 1000)
#define FIVE_MINUTES_IN_MSEC  (5 * 60 * 1000)


//
// test hook identifiers
//

//
// sending the delayed ack
//
#define TH_DG_SEND_ACK          (TH_DG_BASE+1)
#define TH_DG_CONN              (TH_DG_BASE+2)


inline unsigned long
CurrentTimeInMsec(
     void
     )
{
#ifdef MULTITHREADED
    return GetTickCount();
#else
    return 0;
#endif
}

// PacketType values:

#define DG_REQUEST       0
#define DG_PING          1
#define DG_RESPONSE      2
#define DG_FAULT         3
#define DG_WORKING       4
#define DG_NOCALL        5
#define DG_REJECT        6
#define DG_ACK           7
#define DG_QUIT          8
#define DG_FACK          9
#define DG_QUACK        10

// PacketFlags values:

#define DG_PF_INIT          0x0000

#define DG_PF_FORWARDED     0x0001
#define DG_PF_LAST_FRAG     0x0002
#define DG_PF_FRAG          0x0004
#define DG_PF_NO_FACK       0x0008
#define DG_PF_MAYBE         0x0010
#define DG_PF_IDEMPOTENT    0x0020
#define DG_PF_BROADCAST     0x0040

//
// for PacketFlags2:
//
#define DG_PF2_FORWARDED_2      0x0001
#define DG_PF2_CANCEL_PENDING   0x0002
#define DG_PF2_LARGE_PACKET     0x0080

// In the AES this bit is reserved.
//
#define DG_PF2_UNRELATED        0x0004

//
// For DG_PACKET.Flags
//
#define DG_PF_PARTIAL       0x1

// for DREP[0]:
#define DG_DREP_CHAR_ASCII     0
#define DG_DREP_CHAR_EBCDIC    1
#define DG_DREP_INT_BIG        0
#define DG_DREP_INT_LITTLE    16

// for DREP[1]
#define DG_DREP_FP_IEEE    0
#define DG_DREP_FP_VAX     1
#define DG_DREP_FP_CRAY    2
#define DG_DREP_FP_IBM     3

#define DG_MSG_DREP_INITIALIZE 0x11111100

#define NDR_DREP_ENDIAN_MASK 0xF0

#define RPC_NCA_PACKET_FLAGS  (RPC_NCA_FLAGS_IDEMPOTENT | RPC_NCA_FLAGS_BROADCAST | RPC_NCA_FLAGS_MAYBE)

//
// The RPC packet header and security verifier must each be 8-aligned.
//
#define PACKET_HEADER_ALIGNMENT    (8)
#define SECURITY_HEADER_ALIGNMENT  (8)


enum PENDING_OPERATION
{
    PWT_NONE = 0x111,
    PWT_RECEIVE,
    PWT_SEND,
    PWT_SEND_RECEIVE
};

extern const unsigned RpcToPacketFlagsArray[];
extern const unsigned PacketToRpcFlagsArray[];

extern unsigned DefaultSocketBufferLength;
extern unsigned DefaultMaxDatagramSize;

//
// Controls on the number of packets in the packet cache.
// Note that these values are for each packet size, not the
// cache as a whole.
//
#ifndef NO_PACKET_CACHE

#define MIN_FREE_PACKETS 3

#if defined(__RPC_DOS__)
#define MAX_FREE_PACKETS 8
#elif defined(__RPC_WIN16__)
#define MAX_FREE_PACKETS 20
#else
#define MAX_FREE_PACKETS 1000
#endif

#else

#define MIN_FREE_PACKETS 0
#define MAX_FREE_PACKETS 0

#endif

#if defined(DOS) && !defined(WIN)

typedef long LONG;
typedef long PAPI * PLONG;

#endif // Windows

#if defined(MAC)

typedef long LONG;
typedef long PAPI * PLONG;

#endif // Windows

#ifndef min
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#endif

typedef unsigned char boolean;

#ifndef DISABLE_DG_LOGGING

inline void
DgLogEvent(
    IN unsigned char Subject,
    IN unsigned char Verb,
    IN void *        SubjectPointer,
    IN void *        ObjectPointer = 0,
    IN ULONG_PTR     Data          = 0,
    IN BOOL          fCaptureStackTrace = 0,
    IN int           AdditionalFramesToSkip = 0
    )
{
    LogEvent( Subject,
              Verb,
              SubjectPointer,
              ObjectPointer,
              Data,
              fCaptureStackTrace,
              AdditionalFramesToSkip
              );
}

#else

inline void
DgxLogEvent(
    IN unsigned char Subject,
    IN unsigned char Verb,
    IN void *        SubjectPointer,
    IN void *        ObjectPointer = 0,
    IN ULONG_PTR     Data          = 0,
    IN BOOL          fCaptureStackTrace = 0,
    IN int           AdditionalFramesToSkip = 0
    )
{
}

#endif


//-------------------------------------------------------------------

extern unsigned long __RPC_FAR
MapToNcaStatusCode (
    IN RPC_STATUS RpcStatus
    );

extern RPC_STATUS __RPC_FAR
MapFromNcaStatusCode (
    IN unsigned long NcaStatus
    );


inline unsigned
PacketToRpcFlags(
    unsigned PacketFlags
    )
{
    return PacketToRpcFlagsArray[(PacketFlags >> 4) & 7];
}

#ifndef WIN32

inline LONG
InterlockedExchange(
    LONG * pDest,
    LONG New
    )
{
    LONG Old = *pDest;

    *pDest = New;

    return Old;
}

inline LONG
InterlockedIncrement(
    LONG * pDest
    )
{
    LONG Old = *pDest;

    *pDest = 1+Old;

    return Old;
}

inline LONG
InterlockedDecrement(
    LONG * pDest
    )
{
    LONG Old = *pDest;

    *pDest = -1+Old;

    return Old;
}

#endif

//-------------------------------------------------------------------


struct DG_SECURITY_TRAILER
{
    unsigned char protection_level;
    unsigned char key_vers_num;
};

typedef DG_SECURITY_TRAILER __RPC_FAR * PDG_SECURITY_TRAILER;

struct FACK_BODY_VER_0
{
    // FACK body version; we understand only zero.
    //
    unsigned char   Version;

    // pad byte
    //
    unsigned char   Pad1;

    // Window size, in packets.
    // AES/DC contradicts itself on page 12-18, sometimes saying kilobytes.
    //
    unsigned short  WindowSize;

    // Largest datagram the sender can handle, in bytes.
    //
    unsigned long   MaxDatagramSize;

    // Largest datagram that won't be fragmented over the wire, in bytes.
    //
    unsigned long   MaxPacketSize;

    // Serial number of packet that caused this FACK.
    //
    unsigned short  SerialNumber;

    // Number of unsigned longs in the Acks[] array.
    //
    unsigned short  AckWordCount;

#pragma warning(disable:4200)

    // Array of bit masks.
    //
    unsigned long   Acks[0];

#pragma warning(default:4200)

};

void
ByteSwapFackBody0(
    FACK_BODY_VER_0 __RPC_FAR * pBody
    );


typedef unsigned char    DREP[4];

//
// The following structure is the NCA Datagram RPC packet header.
//
struct _NCA_PACKET_HEADER
{
    unsigned char   RpcVersion;
    unsigned char   PacketType;
    unsigned char   PacketFlags;
    unsigned char   PacketFlags2;
    DREP            DataRep;
    RPC_UUID        ObjectId;
    RPC_UUID        InterfaceId;
    RPC_UUID        ActivityId;
    unsigned long   ServerBootTime;
    RPC_VERSION     InterfaceVersion;
    unsigned long   SequenceNumber;
    unsigned short  OperationNumber;
    unsigned short  InterfaceHint;
    unsigned short  ActivityHint;
    unsigned short  PacketBodyLen;
    unsigned short  FragmentNumber;
    unsigned char   AuthProto;
    unsigned char   SerialLo;

#pragma warning(disable:4200)

    unsigned char   Data[0];

#pragma warning(default:4200)

    //--------------------------------------------------------
    // Large packet support
    inline BOOL
    IsLargePacket()
    {
       return (PacketFlags2 & DG_PF2_LARGE_PACKET);
    }

    inline void
    SetPacketBodyLen(
        unsigned long      ulPacketBodyLen
        )
    {
       if (ulPacketBodyLen <= 65535)
          {
          PacketBodyLen = (unsigned short)ulPacketBodyLen;
          // NOTE: FragmentNumber isn't touched in this case.
          PacketFlags2 &= ~DG_PF2_LARGE_PACKET;
          }
       else
          {
          PacketBodyLen  = (unsigned short)(ulPacketBodyLen & 0x0000ffff);
          FragmentNumber = (unsigned short)( (ulPacketBodyLen>>16) & 0x0000ffff );
          PacketFlags2   |= DG_PF2_LARGE_PACKET;
          }
    }

    inline unsigned long
    GetPacketBodyLen()
    {
    if (PacketFlags2 & DG_PF2_LARGE_PACKET)
       {
       unsigned long ulLen = ((unsigned long) PacketBodyLen)|( ((unsigned long)FragmentNumber)<<16 );
       return ulLen;
       }
    else
       {
       return PacketBodyLen;
       }
    }

    inline void
    SetFragmentNumber(
        unsigned short     usFragmentNumber
        )
    {
       if ( !(PacketFlags2 & DG_PF2_LARGE_PACKET) )
          {
          FragmentNumber = usFragmentNumber;
          }
    }

    inline unsigned short
    GetFragmentNumber()
    {
       if (PacketFlags2 & DG_PF2_LARGE_PACKET)
          {
          return 0;
          }
       else
          {
          return FragmentNumber;
          }
    }

};

typedef struct _NCA_PACKET_HEADER NCA_PACKET_HEADER, PAPI * PNCA_PACKET_HEADER;

struct QUIT_BODY_0
{
    unsigned long   Version;
    unsigned long   EventId;
};


struct QUACK_BODY_0
{
    unsigned long   Version;
    unsigned long   EventId;
    unsigned char   Accepted;
};

//
// The nornal DCE fault or reject packet contains only a single ulong status code.
// A fault or reject packet containing Microsoft extended error info looks more like
// this.  The offset of the EE info buffer needs to 0 mod 16, so that 64-bit platforms
// can unmarshal the info without relocating the buffer in memory.
//
#define DG_EE_MAGIC_VALUE  ('M' + (('S' + (('E' + ('E' << 8)) << 8)) << 8))

struct EXTENDED_FAULT_BODY
{
    unsigned long   NcaStatus;
    unsigned long   Magic;
    unsigned long   reserved1;
    unsigned long   reserved2;
    char            EeInfo[1];
};

class DG_PACKET;
typedef DG_PACKET PAPI * PDG_PACKET;


class __RPC_FAR DG_PACKET

/*++

Class Description:

    This class represents a packet that will be sent or received on the
    network.

Fields:

    pTransAddress - A pointer to either a DG_CLIENT_TRANS_ADDRESS or
        a DG_SERVER_TRANS_ADDRESS that this packet will be sent or
        received through.

    pNcaPacketHeader - Where the packet information goes. Marshalled data
        follows immediately after this header.

    DataLength - Length of the marshalled data.

    TimeReceive - Time in seconds that this packet was
        received. This is filled in by the transport.

    pNext, pPrevious - Used to keep these packets in a list.

--*/

{
public:

    unsigned    MaxDataLength;
    unsigned    DataLength;
    DG_PACKET * pNext;
    DG_PACKET * pPrevious;

    unsigned    Flags;

    // Tick count when the packet was added to the free list.
    //
    unsigned    TimeReceived;

#ifdef MONITOR_SERVER_PACKET_COUNT
    unsigned unused;
    long * pCount;
#endif

    // WARNING: Header must be 8-byte-aligned.
    //
    NCA_PACKET_HEADER   Header;

    //--------------------------------------------------------------------

    DG_PACKET(
        unsigned PacketLength
        );

    ~DG_PACKET();

    void PAPI *
    operator new(
        size_t      ObjectSize,
        unsigned    BufferLength
        );

    void
    operator delete(
        void PAPI * UserBuffer,
        size_t ObjectSize
        );

    static PDG_PACKET
    AllocatePacket(
        unsigned    BufferLength
        );

    static void
    FreePacket(
        PDG_PACKET pPacket,
        BOOL       fCreateNewList = TRUE
        );

    static BOOL
    DeleteIdlePackets(
        long CurrentTime
        );

    static void
    FlushPacketLists(
        );

    static RPC_STATUS
    Initialize(
        );

    inline void
    Free(
        BOOL CreateNewList = TRUE
        )
    {
        FreePacket(this, CreateNewList);
    }


    inline static PDG_PACKET
    FromStubData(
        void * Buffer
        )
    {
        return CONTAINING_RECORD (Buffer, DG_PACKET, Header.Data);
    }

    inline static PDG_PACKET
    FromPacketHeader(
        void * Buffer
        )
    {
        return CONTAINING_RECORD(Buffer, DG_PACKET, Header);
    }

    //--------------------------------------------------------
    // Large packet support
    inline BOOL
    IsLargePacket()
    {
        return Header.IsLargePacket();
    }

    inline void
    SetPacketBodyLen(
        unsigned long ulPacketBodyLen
        )
    {
        Header.SetPacketBodyLen( ulPacketBodyLen );
    }

    inline unsigned long
    GetPacketBodyLen()
    {
        return Header.GetPacketBodyLen();
    }

    inline void
    SetFragmentNumber(
        unsigned short usFragmentNumber
        )
    {
        Header.SetFragmentNumber( usFragmentNumber );
    }

    inline unsigned short
    GetFragmentNumber()
    {
        return Header.GetFragmentNumber();
    }

private:

    enum
    {
        NUMBER_OF_PACKET_LISTS = 6,
        IDLE_PACKET_LIFETIME   = (30 * 1000)
    };

    struct PACKET_LIST
    {
        unsigned        PacketLength;
        unsigned        Count;
        PDG_PACKET      Head;
    };

    static long PacketListTimeStamp;
    static MUTEX * PacketListMutex;
    static PACKET_LIST PacketLists[NUMBER_OF_PACKET_LISTS];

};



inline
DG_PACKET::DG_PACKET(
    unsigned PacketLength
    )
{
    MaxDataLength = PacketLength;
    LogEvent(SU_PACKET, EV_CREATE, this, 0, MaxDataLength);

#ifdef MONITOR_SERVER_PACKET_COUNT
    pCount = 0;
#endif
}

inline

DG_PACKET::~DG_PACKET()
{
#ifdef MONITOR_SERVER_PACKET_COUNT
    ASSERT( pCount == 0 );
#endif

    LogEvent(SU_PACKET, EV_DELETE, this, 0, MaxDataLength);
}


inline void PAPI *
DG_PACKET::operator new(
    size_t      ObjectSize,
    unsigned    BufferLength
    )
/*++

Routine Description:

    Allocates a DG_PACKET with the specified buffer size.

Arguments:

    ObjectSize - generated by compiler; same as sizeof(DG_PACKET)

    BufferLength - PDU size, including NCA header

Return Value:

    an 8-byte-aligned pointer to an obect of the requested size

--*/

{
    unsigned Size = ObjectSize + BufferLength - sizeof(NCA_PACKET_HEADER);

    return RpcAllocateBuffer(Size);
}

inline void
DG_PACKET::operator delete(
    void PAPI * UserBuffer,
    size_t ObjectSize
    )
{
    RpcFreeBuffer(UserBuffer);
}


inline
PDG_PACKET
DG_PACKET::AllocatePacket(
    unsigned    BufferLength
    )
/*++

Routine Description:

    Allocates a DG_PACKET with the specified buffer size.

Arguments:

    ObjectSize - generated by compiler; same as sizeof(DG_PACKET)

    BufferLength - actual packet size

Return Value:

    an 8-byte-aligned pointer to an obect of the requested size

--*/

{
    PDG_PACKET Packet;

    Packet = new (BufferLength) DG_PACKET(BufferLength);

#if defined(DEBUGRPC)

    if (Packet)
        {
        Packet->TimeReceived = 0x31415926;
        }
#endif

    if (Packet)
        {
        Packet->Flags = 0;
        }

    LogEvent(SU_PACKET, EV_START, Packet);

    return Packet;
}


inline
void
DG_PACKET::FreePacket(
    PDG_PACKET Packet,
    BOOL       fCreateNewList
    )
{
    LogEvent(SU_PACKET, EV_STOP, Packet);

#ifdef DEBUGRPC
    ASSERT(Packet->TimeReceived == 0x31415926);
    Packet->TimeReceived = 0;
#endif

#ifdef MONITOR_SERVER_PACKET_COUNT
    if (Packet->pCount)
        {
        InterlockedDecrement( Packet->pCount );
        LogEvent( SU_SCALL, ')', PVOID(((ULONG_PTR) Packet->pCount)-0x260), Packet, *Packet->pCount );

        Packet->pCount = 0;
        }
#endif

    delete Packet;
}


void
ByteSwapPacketHeader(
    PDG_PACKET  pPacket
    );

inline BOOL
NeedsByteSwap(
    PNCA_PACKET_HEADER pHeader
    )
{
#ifdef __RPC_MAC__
    if (pHeader->DataRep[0] & DG_DREP_INT_LITTLE)
        {
        return TRUE;
        }
    else
        {
        return FALSE;
        }
#else
    if (pHeader->DataRep[0] & DG_DREP_INT_LITTLE)
        {
        return FALSE;
        }
    else
        {
        return TRUE;
        }
#endif
}

inline void
ByteSwapPacketHeaderIfNecessary(
    PDG_PACKET pPacket
    )
{
    if (NeedsByteSwap(&pPacket->Header))
        {
        ByteSwapPacketHeader(pPacket);
        }
}


inline unsigned short
ReadSerialNumber(
    PNCA_PACKET_HEADER pHeader
    )
{
    unsigned short   SerialNum = 0;

    SerialNum  =  pHeader->SerialLo;
    SerialNum |= (pHeader->DataRep[3] << 8);

    return SerialNum;
}


inline void
SetMyDataRep(
    PNCA_PACKET_HEADER pHeader
    )
{
#ifdef __RPC_MAC__
    pHeader->DataRep[0] = DG_DREP_CHAR_ASCII | DG_DREP_INT_BIG;
    pHeader->DataRep[1] = DG_DREP_FP_IEEE;
    pHeader->DataRep[2] = 0;
#else
    pHeader->DataRep[0] = DG_DREP_CHAR_ASCII | DG_DREP_INT_LITTLE;
    pHeader->DataRep[1] = DG_DREP_FP_IEEE;
    pHeader->DataRep[2] = 0;
#endif
}

inline void
DeleteSpuriousAuthProto(
    PDG_PACKET pPacket
    )
/*++

Routine Description:

    Some versions of OSF DCE generate packets that specify an auth proto,
    but do not actually have an auth trailer.  They should be interpreted
    as unsecure packets.

Arguments:

    the packet to clean up

Return Value:

    none

--*/

{
    if (pPacket->Header.AuthProto != 0 &&
        pPacket->GetPacketBodyLen() == pPacket->DataLength)
        {
        pPacket->Header.AuthProto = 0;
        }
}


#pragma warning(disable:4200)   // nonstandard extension: zero-length array

struct DG_ENDPOINT
{
    RPC_DATAGRAM_TRANSPORT *    TransportInterface;
    BOOL                        Async;
    DWORD                       Flags;
    long                        TimeStamp;
    struct DG_ENDPOINT *        Next;
    LONG                        NumberOfCalls;
    DG_ENDPOINT_STATS           Stats;
    PVOID                       TransportEndpoint[];

    static DG_ENDPOINT *
    FromEndpoint(
        IN DG_TRANSPORT_ENDPOINT Endpoint
        )
    {
        return CONTAINING_RECORD( Endpoint, DG_ENDPOINT, TransportEndpoint );
    }
};

#pragma warning(3:4200)   // nonstandard extension: zero-length array


class NO_VTABLE DG_COMMON_CONNECTION : public GENERIC_OBJECT
{
public:

    RPC_DATAGRAM_TRANSPORT *TransportInterface;
    UUID_HASH_TABLE_NODE    ActivityNode;
    SECURITY_CONTEXT *      ActiveSecurityContext;
    MUTEX                   Mutex;

    unsigned                CurrentPduSize;
    unsigned                RemoteWindowSize;
    boolean                 RemoteDataUpdated;

    long                    TimeStamp;

    //--------------------------------------------------------------------

    virtual RPC_STATUS
    SealAndSendPacket(
        IN DG_ENDPOINT *                 SourceEndpoint,
        IN DG_TRANSPORT_ADDRESS          RemoteAddress,
        IN UNALIGNED NCA_PACKET_HEADER * pHeader,
        IN unsigned long                 DataOffset
        ) = 0;

    DG_COMMON_CONNECTION(
        RPC_DATAGRAM_TRANSPORT *TransportInterface,
        RPC_STATUS *            pStatus
        );

    ~DG_COMMON_CONNECTION(
        );

protected:

    long                    ReferenceCount;

    unsigned                LowestActiveSequence;
    unsigned                LowestUnusedSequence;
};

struct QUEUED_BUFFER
{
    QUEUED_BUFFER * Next;
    void *      Buffer;
    unsigned    BufferLength;
    unsigned long BufferFlags;
};


class NO_VTABLE DG_PACKET_ENGINE
{
public:

    unsigned short  ActivityHint;
    unsigned short  InterfaceHint;

    //--------------------------------------------------------------------

    DG_PACKET_ENGINE(
        unsigned char   PacketType,
        DG_PACKET *     Packet,
        RPC_STATUS *    pStatus
        );

    ~DG_PACKET_ENGINE(
        );

    virtual RPC_STATUS
    SendSomeFragments();

    unsigned long GetSequenceNumber()
    {
        return SequenceNumber;
    }

    void
    SetSequenceNumber(
        unsigned long Seq
        )
    {
        SequenceNumber = Seq;
        pSavedPacket->Header.SequenceNumber = Seq;
    }

    void CheckForLeakedPackets();

    void
    AddActivePacket(
        DG_PACKET * Packet
        );

    void
    RemoveActivePacket(
        DG_PACKET * Packet
        );


    unsigned long   SequenceNumber;
    PDG_PACKET      pSavedPacket;

    DG_ENDPOINT *           SourceEndpoint;
    DG_TRANSPORT_ADDRESS    RemoteAddress;

    unsigned        ReferenceCount;

    unsigned long   CancelEventId;

    unsigned char   PacketType;
    unsigned char   BasePacketFlags;
    unsigned char   BasePacketFlags2;

    //--------------------------------------------------------------------

    PDG_PACKET
    AllocatePacket(
        )
    {
        PDG_PACKET Packet = 0;

        Packet = DG_PACKET::AllocatePacket(CurrentPduSize);

#ifdef DEBUGRPC
        if (Packet)
            {
            Packet->Flags = PtrToUlong(this);
            ASSERT( 0 == (Packet->Flags & DG_PF_PARTIAL) );
            }
#endif

        return Packet;
    }

    void
    FreePacket(
        PDG_PACKET Packet
        )
    {
#ifdef DEBUGRPC

        Packet->Flags &= ~DG_PF_PARTIAL;

        ASSERT( Packet->Flags == 0 ||
                Packet->Flags == PtrToUlong(this) );
#endif

        Packet->Free(FALSE);
    }

    //
    //--------------data common to send and receive buffers-------------------
    //

    //
    // used to be MaxPdu
    //
    unsigned short Reserved0;

    //
    // Biggest packet that transport won't fragment.
    //
    unsigned short  MaxPacketSize;

    //
    // Largest PDU that this object will send.
    //
    unsigned short  CurrentPduSize;

    //
    // Number of bytes of stub data in a datagram.
    //
    unsigned short  MaxFragmentSize;

    //
    // Number of bytes of security trailer in a datagram.
    //
    unsigned short  SecurityTrailerSize;

    //
    // number of consecutive unacknowledged packets, including retransmissions
    //
    unsigned        TimeoutCount;

    unsigned short  SendSerialNumber;
    unsigned short  ReceiveSerialNumber;

    LONG            Cancelled;

    // Many of these could be made [private].

    //
    // -------------------data concerning send buffer-------------------------
    //

    void PAPI *     Buffer;

    unsigned        BufferLength;

    unsigned long   BufferFlags;

    //
    // maximum number of packets in send window
    //
    unsigned short  SendWindowSize;

    //
    // number of packets to transmit in one shot
    //
    unsigned short  SendBurstLength;

    //
    // lowest unacknowledged fragment
    //
    unsigned short  SendWindowBase;

    //
    // first fragment that has never been sent
    //
    unsigned short  FirstUnsentFragment;

    //
    // Buffer offset of FirstUnsentFragment.
    //
    unsigned        FirstUnsentOffset;

    //
    // bit mask showing which fragments to send
    // (same format as in FACK packet with body)
    //
    unsigned        SendWindowBits;

    //
    // For each unacknowledged fragment, we need to know the serial number
    // of the last retransmission.  When a FACK arrives, we will retransmit
    // only those packets with a serial number less than that of the FACK.
    //
    struct
    {
        unsigned long  SerialNumber;
        unsigned long  Length;
        unsigned long  Offset;
    }
    FragmentRingBuffer[MAX_WINDOW_SIZE];

    unsigned        RingBufferBase;

    //
    // last fragment of buffer
    //
    unsigned short  FinalSendFrag;

    // serial number of last packet FACKed by other end
    //
    unsigned short FackSerialNumber;

    //
    // ----------------data concerning receive buffer-------------------------
    //

    //
    // all received packets
    //
    PDG_PACKET      pReceivedPackets;

    //
    // last packet before a gap
    //
    PDG_PACKET      pLastConsecutivePacket;

    //
    // used to be ReceiveWindowSize
    //
    unsigned short  Reserved1;

    //
    // First fragment we should keep.  Elder fragments belong to a previous
    // pipe buffer.
    //
    unsigned short  ReceiveFragmentBase;

    //
    // Length of the underlying transport's socket buffer.
    //
    unsigned        TransportBufferLength;

    //
    // Number of bytes in consecutive fragments.
    //
    unsigned        ConsecutiveDataBytes;

    //
    // The last-allocated pipe receive buffer, and its length.
    //
    void __RPC_FAR *LastReceiveBuffer;
    unsigned        LastReceiveBufferLength;

    boolean         fReceivedAllFragments;
    boolean         fRetransmitted;
    unsigned        RepeatedFack;

    DG_COMMON_CONNECTION * BaseConnection;

    //--------------------------------------------------------------------

    RPC_STATUS
    PushBuffer(
        PRPC_MESSAGE Message
        );

    RPC_STATUS
    PopBuffer(
                   BOOL fSendPackets
                   );

    RPC_STATUS
    FixupPartialSend(
        RPC_MESSAGE * Message
        );

    void
    CommonFreeBuffer(
        RPC_MESSAGE * Message
        );

    RPC_STATUS
    CommonGetBuffer(
        RPC_MESSAGE * MEssage
        );

    RPC_STATUS
    CommonReallocBuffer(
        IN RPC_MESSAGE * Message,
        IN unsigned int NewSize
        );

    void
    ReadConnectionInfo(
        DG_COMMON_CONNECTION * a_Connection,
        DG_TRANSPORT_ADDRESS   a_RemoteAddress
        );

    void NewCall();

    RPC_STATUS
    SetupSendWindow(
        PRPC_MESSAGE Message
        );

    void CleanupSendWindow();
    void CleanupReceiveWindow();

    RPC_STATUS
    SendFragment(
        unsigned FragNum,
        unsigned char PacketType,
        BOOL fFack
        );

    RPC_STATUS
    SendFackOrNocall(
        PDG_PACKET pPacket,
        unsigned char PacketType
        );

    RPC_STATUS
    UpdateSendWindow(
        PDG_PACKET pPacket,
        BOOL * pfUpdated
        );

    BOOL
    UpdateReceiveWindow(
        PDG_PACKET pPacket
        );

    RPC_STATUS
    AssembleBufferFromPackets(
        IN OUT RPC_MESSAGE * Message,
        IN CALL *            Call
        );

    void SetFragmentLengths();
    void RecalcReceiveWindow();

    unsigned short LastConsecutiveFragment()
    {
        if (0 == pLastConsecutivePacket)
            {
            return 0xffff;
            }
        else
            {
            return pLastConsecutivePacket->GetFragmentNumber();
            }
    }

    void MarkAllPacketsReceived()
    {
        ASSERT( FirstUnsentFragment > FinalSendFrag );

        unsigned short Diff  = FinalSendFrag+1 - SendWindowBase;

        ASSERT(Diff <= MAX_WINDOW_SIZE);
        ASSERT( SendWindowBase+Diff <= FirstUnsentFragment );

        SendWindowBase += Diff;

        RingBufferBase += Diff;
        RingBufferBase %= MAX_WINDOW_SIZE;

        PopBuffer(TRUE);
        }

    BOOL
    IsBufferAcknowledged(
        )
    {
        if (SendWindowBase > FinalSendFrag)
            {
            return TRUE;
            }

        return FALSE;
    }

    BOOL
    IsBufferSent(
        )
    {
        if (FirstUnsentFragment > FinalSendFrag)
            {
            return TRUE;
            }

        return FALSE;
    }

    inline void
    AddSerialNumber(
        UNALIGNED NCA_PACKET_HEADER *pHeader
        );

private:

    QUEUED_BUFFER * QueuedBufferHead;
    QUEUED_BUFFER * QueuedBufferTail;

    PDG_PACKET      CachedPacket;

    void
    SetCurrentBuffer(
         void *        a_Buffer,
         unsigned      a_BufferLength,
         unsigned long BufferFlags
         );
};

typedef DG_PACKET_ENGINE * PDG_PACKET_ENGINE;


inline void
SetSerialNumber(
    UNALIGNED NCA_PACKET_HEADER *pHeader,
    unsigned short SerialNumber
    )
{
    pHeader->SerialLo = SerialNumber & 0x00ffU;
    pHeader->DataRep[3] = (unsigned char) (SerialNumber >> 8);
}

inline void
DG_PACKET_ENGINE::AddSerialNumber(
    UNALIGNED NCA_PACKET_HEADER *pHeader
    )
{
    SetSerialNumber(pHeader, SendSerialNumber);
}

extern unsigned long            ProcessStartTime;

inline void
CleanupPacket(
    NCA_PACKET_HEADER * pHeader
    )
{
    pHeader->RpcVersion       = DG_RPC_PROTOCOL_VERSION;
    pHeader->ServerBootTime   = ProcessStartTime;

    SetMyDataRep(pHeader);

    pHeader->PacketFlags &= DG_PF_IDEMPOTENT;
    pHeader->PacketFlags2 = 0;

    pHeader->AuthProto    = 0;
}

//------------------------------------------------------------------------

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
VerifySecurePacket(
    PDG_PACKET pPacket,
    SECURITY_CONTEXT * pSecurityContext
    );

void
InitErrorPacket(
    DG_PACKET *     Packet,
    unsigned char   PacketType,
    RPC_STATUS      RpcStatus
    );

void
DumpBuffer(
    void FAR * Buffer,
    unsigned Length
    );

extern void EnableGlobalScavenger();

extern DELAYED_ACTION_TABLE *   DelayedProcedures;

extern unsigned RandomCounter;
inline unsigned GetRandomCounter()
{
    ::RandomCounter *= 37;
    ::RandomCounter += GetTickCount();

    return ::RandomCounter;
}
#endif // __DGPKT_HXX__