/*++

Copyright (C) Microsoft Corporation, 1991 - 1999

Module Name:

    osfclnt.hxx

Abstract:

    This file contains the client side classes for the OSF connection
    oriented RPC protocol engine.

Author:

    Michael Montague (mikemon) 17-Jul-1990

Revision History:
    Mazhar Mohammed (mazharm) 11-08-1996 - Major re-haul to support async
      - Added support for Async RPC, Pipes
      - Changed class structure
    Kamen Moutafov      (kamenm)    Jan-2000    Support for multiple transfer syntaxes
    Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
--*/

#ifndef __OSFCLNT_HXX__
#define __OSFCLNT_HXX__

enum OSF_CCALL_STATE
{
    //
    // Need to open the connection and bind, in order to
    // handle the call
    //
    NeedOpenAndBind = 0,

    //
    // Need to send an alter context on the connection
    // in order to handle this call
    //
    NeedAlterContext,

    //
    // We sent an alter-context, we are waiting for a reply
    //
    WaitingForAlterContext,

    //
    // The call is still sending the non pipe data
    // we need to finish sending this before
    // we can move on the the next call.
    //
    SendingFirstBuffer,

    //
    // The call is now sending pipe data
    //
    SendingMoreData,

    //
    // The the call is done sending data. It is now waiting for a reply.
    // The reply may be either a response or a callback.
    //
    WaitingForReply,

    //
    // The call is receiving a callback from the server
    //
    InCallbackRequest,

    //
    // The call is in the process of sending a reply to a callback
    //
    InCallbackReply,

    //
    // We move into this state after receiving the first fragment
    //
    Receiving,

    //
    // Some failure occured. the call is now in an
    // aborted state
    //
    Aborted,

    //
    // The call is complete
    //
    Complete
} ;

//
// Maximum retries in light of getting a shutdown
// or closed in doing a bind or shutdown
//
#define MAX_RETRIES  3

// 15 min timeout for bind
#define RPC_C_SERVER_BIND_TIMEOUT               15*60*1000

//
// These values specify the minimum amount of time in milliseconds to wait before
// deleting an idle connection. The second is the more aggressive. It will be used
// when we have more than 500 connections in an association
//

const ULONG AGGRESSIVE_TIMEOUT_THRESHOLD = 500;

const ULONG CLIENT_DISCONNECT_TIME1 = 10 * 1000;
const ULONG CLIENT_DISCONNECT_TIME2 = 5 * 1000;

#define InqTransCConnection(RpcTransportConnection) \
    ((OSF_CCONNECTION *) \
            ((char *) RpcTransportConnection - sizeof(OSF_CCONNECTION)))

#define TransConnection() ((RPC_TRANSPORT_CONNECTION) \
                                       ((char *) this+sizeof(OSF_CCONNECTION)))
#define TransResolverHint() ((void *) ((char *) this+sizeof(OSF_CASSOCIATION)))

class OSF_CASSOCIATION;
class OSF_CCONNECTION ;
class OSF_BINDING ;
class OSF_CCALL ;

extern MUTEX *AssocDictMutex;


class OSF_RECURSIVE_ENTRY
/*++

Class Description:

    This class is used to describe the entries in the dictionary of
    recursive calls maintained by each binding handle.

Fields:

    Thread - Contains the thread owning this recursive call.

    RpcInterfaceInformation - Contains information describing the
        interface

    CCall - Contains the call

--*/
{
friend class OSF_BINDING_HANDLE;
private:

    THREAD_IDENTIFIER Thread;
    PRPC_CLIENT_INTERFACE RpcInterfaceInformation;
    OSF_CCALL * CCall;

public:

    OSF_RECURSIVE_ENTRY (
        IN THREAD_IDENTIFIER Thread,
        IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
        IN OSF_CCALL * CCall
        );

    OSF_CCALL *
    IsThisMyRecursiveCall (
        IN THREAD_IDENTIFIER Thread,
        IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
        );
};


inline
OSF_RECURSIVE_ENTRY::OSF_RECURSIVE_ENTRY (
    IN THREAD_IDENTIFIER Thread,
    IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
    IN OSF_CCALL * CCall
    )
/*++

Routine Description:

    All we do in this constructor is stash the arguments passed to us
    away in the object.

Arguments:

    Thread - Supplies the thread for which this is the recursive call.

    RpcInterfaceInformation - Supplies information describing the
        interface

    CCall - Supplies the recursive call.

--*/
{
    this->Thread = Thread;
    this->RpcInterfaceInformation = RpcInterfaceInformation;
    this->CCall = CCall;
}


inline OSF_CCALL *
OSF_RECURSIVE_ENTRY::IsThisMyRecursiveCall (
    IN THREAD_IDENTIFIER Thread,
    IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
    )
/*++

Routine Description:

    This routine determines if this object contains the recursive call
    for the specified thread and interface information.

Arguments:

    Thread - Supplies the thread.

    RpcInterfaceInformation - Supplies the interface information.

Return Value:

    If this object contains the recursive call corresponding to the
    specified thread and interface information, then the connection is
    returned.  Otherwise, zero will be returned.

--*/
{
    return((((Thread == this->Thread)
            && (RpcInterfaceInformation == this->RpcInterfaceInformation))
             ? CCall : 0));
}

NEW_SDICT(OSF_RECURSIVE_ENTRY);


class RPC_TOKEN
{
public:
    HANDLE hToken;
    LUID ModifiedId;
    int RefCount;
    int Key;

    RPC_TOKEN (HANDLE hToken, LUID *pModifiedId)
    {
        this->hToken = hToken;
        RefCount = 1;
        FastCopyLUIDAligned(&ModifiedId, pModifiedId);
    }

    ~RPC_TOKEN ()
    {
        CloseHandle(hToken);
    }
};



class OSF_BINDING_HANDLE : public BINDING_HANDLE
/*++

Class Description:

    Client applications use instances (referenced via an RPC_BINDING_HANDLE)
    of this class to make remote procedure calls.

Fields:

    Association - Contains a pointer to the association used by this
        binding handle.  The association can allocate connection for
        making remote procedure calls.  Before the first remote procedure
        call is made using this binding handle, the Association will
        be zero.  When the first remote procedure call is made, an
        assocation will be found or created for use by this binding handle.

    DceBinding - Before the first remote procedure call for this binding
        handle, this will contain the DCE binding information necessary
        to create or find an association to be used by this binding handle.
        After we have an association, this field will be zero.

    TransportInterface - This field is the same as DceBinding, except that
        it points to a rpc client info data structure used for describing
        a loadable transport.

    RecursiveCalls - This is a dictionary of recursive calls indexed
        by thread identifier and rpc interface information.

    BindingMutex - The binding handle can be used by more than one thread
        at a time.  Hence, we need to serialize access to the object;
        we use this mutex for that.

    ReferenceCount - We count the number of active connections and the
        application RPC_BINDING_HANDLE which point at this object.  This
        is so that we know when to free it.

--*/
{
friend class OSF_CCALL;

private:

    OSF_CASSOCIATION * Association;
    DCE_BINDING * DceBinding;
    TRANS_INFO *TransInfo ;
    OSF_RECURSIVE_ENTRY_DICT RecursiveCalls;
    UINT ReferenceCount;
    RPC_TOKEN *pToken;

public:
    // set for Remote named pipes only
    BOOL fNamedPipe;
    BOOL TransAuthInitialized;
    BOOL fDynamicEndpoint;

    OSF_BINDING_HANDLE (
        IN OUT RPC_STATUS  * RpcStatus
        );

    ~OSF_BINDING_HANDLE (
        );

    virtual RPC_STATUS
    NegotiateTransferSyntax (
        IN OUT PRPC_MESSAGE Message
        );

    virtual RPC_STATUS
    GetBuffer (
        IN OUT PRPC_MESSAGE Message,
        IN UUID *ObjectUuid
        );

    virtual RPC_STATUS
    BindingCopy (
        OUT BINDING_HANDLE *  * DestinationBinding,
        IN UINT MaintainContext
        );

    virtual RPC_STATUS
    BindingFree (
        );

    virtual RPC_STATUS
    PrepareBindingHandle (
        IN TRANS_INFO  * TransportInterface,
        IN DCE_BINDING * DceBinding
        );

    virtual RPC_STATUS
    ToStringBinding (
        OUT RPC_CHAR  *  * StringBinding
        );

    virtual RPC_STATUS
    ResolveBinding (
        IN RPC_CLIENT_INTERFACE  * RpcClientInterface
        );

    virtual RPC_STATUS
    BindingReset (
        );

    virtual RPC_STATUS
    InquireTransportType(
        OUT UINT  *Type
        )
    { *Type = TRANSPORT_TYPE_CN; return(RPC_S_OK); }

    virtual ULONG
    MapAuthenticationLevel (
        IN ULONG AuthenticationLevel
        );

    virtual void
    AddReference (
        )
        {
        BindingMutex.Request();
        ReferenceCount++;
        BindingMutex.Clear();
        }

    RPC_STATUS
    AllocateCCall (
        OUT OSF_CCALL  *  * CCall,
        IN PRPC_MESSAGE Message,
        OUT BOOL *Retry
        );

    RPC_STATUS
    AddRecursiveEntry (
        IN OSF_CCALL * CCall,
        IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
        );

    void
    RemoveRecursiveCall (
        IN OSF_CCALL * CCall
        );

    OSF_CASSOCIATION *
    TheAssociation (
        ) {return(Association);}

    OSF_CASSOCIATION *
    FindOrCreateAssociation (
        IN DCE_BINDING * DceBinding,
        IN TRANS_INFO * TransInfo,
        IN RPC_CLIENT_INTERFACE *InterfaceInfo
        );

    RPC_STATUS
    AcquireCredentialsForTransport (
        );

    BOOL
    SwapToken (
        HANDLE *OldToken
        );

    void Unbind ();

    virtual RPC_STATUS
    SetTransportOption( unsigned long option,
                        ULONG_PTR     optionValue );

    virtual RPC_STATUS
    InqTransportOption( unsigned long  option,
                        ULONG_PTR    * pOptionValue );
};

const unsigned int BindingListPresent = 1;

class OSF_CCONNECTION ;
enum CANCEL_STATE {
 CANCEL_NOTREGISTERED = 0,
 CANCEL_INFINITE,
 CANCEL_NOTINFINITE
 };

class OSF_CCALL : public CCALL
/*++

Class Description:
        This class encapsulates an RPC call.

Fields:

    PresentationContext - This field is only valid when there is a remote
        procedure call in progress on this connection; it contains the
        presentation context for the call.

    DispatchTableCallback - This field is only valid when there is a remote
        procedure call in progress on this connection; it contains the
        dispatch table to use for callbacks.

    Association - Contains a pointer to the association which owns this
        connection.

    TokenLength - Contains the maximum size of a token for the security
        package being used by this connection; we need to keep track of
        this for the third leg authentication case.

--*/
{

//
// This class will only access the AssociationKey member.
//

friend class OSF_CASSOCIATION;
friend class OSF_CCONNECTION;
friend class OSF_BINDING_HANDLE;

public:
    OSF_CCALL_STATE CurrentState ;

private:
    OSF_CCONNECTION *Connection ;
    OSF_BINDING_HANDLE *BindingHandle;
#ifdef DEBUGRPC
    int CallbackLevel;
#endif

    // don't access the struct members directly - only through
    // the access functions - this will ensure that the
    // contents of the struct is properly checked. Async first
    // calls on new connection have both elements valid if there
    // are no preferences. Since this is the only case where
    // this condition is true, we can use it as a flag.
    struct
        {
        // NULL if no binding is selected
        OSF_BINDING *SelectedBinding;

        // this is the list of differning only by transfer syntax
        // bindings this call can use. It can have one or more
        // elements.
        // This may be NULL if the call doesn't need a list
        OSF_BINDING *AvailableBindingsList;
        } Bindings;

    void *CurrentBuffer ;
    BOOL fDataLengthNegotiated;
    int CurrentOffset ;
    ULONG CurrentBufferLength ;
    ULONG CallId;
    UINT RcvBufferLength ;
    BOOL FirstSend ;
    PRPC_DISPATCH_TABLE DispatchTableCallback;
    UINT MaximumFragmentLength;

    // starts from 0, and set to non-zero if security is used after
    // negotiating the bind
    UINT MaxSecuritySize ;
    UINT MaxDataLength;
    int ProcNum ;
    unsigned char  *ReservedForSecurity ;
    UINT SecBufferLength;

    // When the call is created, set to 0, because we don't know whether
    // there is object UUID or not. During GetBuffer we will know, and then
    // we will set it properly. Starting from 0 allows the bind and GetBuffer 
    // threads (if different) to check and to synchronize the updating of the
    // max fragment size, since it depends both on the object UUID and
    // the security negotiation
    UINT HeaderSize;
    UINT AdditionalSpaceForSecurity;
    ULONG SavedHeaderSize;
    void  *   SavedHeader;
    void  * LastBuffer ;
    EVENT SyncEvent ;
    UINT ActualBufferLength;
    UINT NeededLength;
    void  *CallSendContext;
    INTERLOCKED_INTEGER fAdvanceCallCount;
    BOOL fPeerChoked;

    // Extended Error Info stuff
    // used in async calls only to hold
    // information b/n call failure and 
    // RpcAsyncCompleteCall
    ExtendedErrorInfo *EEInfo;

    BOOL fDoFlowControl;
public:
    BOOL fLastSendComplete;
    MUTEX CallMutex ;
    int RecursiveCallsKey;

    //
    // Indicates the call stack.  Whenever a request message is sent or
    // received, the stack is incremented.  Likewise, whenever a response
    // message is sent or received, the stack is decremented.
    //
    int CallStack;
    BOOL fCallCancelled;
    CANCEL_STATE CancelState;

private:
    QUEUE BufferQueue ;
    BOOL InReply;
    BOOL fChoked;

    OSF_CCALL (
        RPC_STATUS  * pStatus
        );

public:

    virtual~OSF_CCALL (
        );

    virtual RPC_STATUS
    NegotiateTransferSyntax (
        IN OUT PRPC_MESSAGE Message
        );

    virtual RPC_STATUS
    GetBuffer (
        IN OUT PRPC_MESSAGE Message,
        IN UUID *ObjectUuid = 0
        );

    RPC_STATUS 
    GetBufferWithoutCleanup (
        IN OUT PRPC_MESSAGE Message,
        IN UUID *ObjectUuid
        );

    virtual RPC_STATUS
    SendReceive (
        IN OUT PRPC_MESSAGE Message
        );

    RPC_STATUS
    SendReceiveHelper (
       IN OUT PRPC_MESSAGE Message,
       OUT BOOL *fRetry
       );

    virtual RPC_STATUS
    Send (
        IN PRPC_MESSAGE Message
        );

    virtual RPC_STATUS
    Receive (
        IN PRPC_MESSAGE Message,
        IN UINT Size
        );

    virtual void
    FreeBuffer (
        IN PRPC_MESSAGE Message
        );

    virtual void
    FreePipeBuffer (
        IN PRPC_MESSAGE Message
        );

    virtual RPC_STATUS
    ReallocPipeBuffer (
        IN PRPC_MESSAGE Message,
        IN UINT NewSize
        );

    virtual RPC_STATUS
    AsyncSend (
        IN OUT PRPC_MESSAGE Message
        );

    virtual RPC_STATUS
    AsyncReceive (
        IN OUT PRPC_MESSAGE Message,
        IN UINT Size
        );

    virtual void
    ProcessSendComplete (
        IN RPC_STATUS EventStatus,
        IN BUFFER Buffer
        );

    virtual RPC_STATUS
    CancelAsyncCall (
        IN BOOL fAbort
        );

    virtual void
    FreeObject (
        );

    RPC_STATUS
    SendHelper (
        IN PRPC_MESSAGE Message,
        OUT BOOL *fFirstSend
        );

    RPC_STATUS
    SendData (
        IN BUFFER Buffer
        );

    RPC_STATUS
    SendMoreData (
        IN BUFFER Buffer
        );

    RPC_STATUS
    ActuallyAllocateBuffer (
        OUT void  *  * Buffer,
        IN UINT BufferLength
        );

    void
    ActuallyFreeBuffer (
        IN void  * Buffer
        );

    //
    // Actually perform the buffer allocation.
    //
    RPC_STATUS
    GetBufferDo (
        IN UINT culRequiredLength,
        OUT void  * * ppBuffer,
        IN int fDataValid = 0,
        IN int DataLength = 0
        );

    void
    FreeBufferDo (
        IN void  *Buffer
        );

    RPC_STATUS
    ActivateCall (
        IN OSF_BINDING_HANDLE *BindingHandle,
        IN OSF_BINDING *Binding,
        IN OSF_BINDING *AvailableBindingsList,
        IN ULONG CallIdToUse,
        IN OSF_CCALL_STATE InitialCallState,
        IN PRPC_DISPATCH_TABLE DispatchTable,
        IN OSF_CCONNECTION *CConnection
        );

    void
    FreeCCall (
        IN RPC_STATUS Status
        );

    RPC_STATUS
    SendCancelPDU(
        );

    RPC_STATUS
    SendOrphanPDU (
        );

    RPC_STATUS
    BindToServer (
        BOOL fAsyncBind
        );

    RPC_STATUS
    Cancel(
        void * ThreadHandle
        );

    RPC_STATUS 
    InqWireIdForSnego (
        OUT unsigned char *WireId
        );

    RPC_STATUS 
    BindingHandleToAsyncHandle (
        OUT void **AsyncHandle
        );

    // if fMultipleBindingsAvailable is set, the return value is a head of a linked
    // list. If fMultipleBindingsAvailable is FALSE, only the element in the return 
    // value is meaningful
    inline OSF_BINDING *
    GetListOfAvaialbleBindings (
        OUT BOOL *fMultipleBindingsAvailable
        )
    {
        if (Bindings.AvailableBindingsList)
            {
            *fMultipleBindingsAvailable = TRUE;
            return Bindings.AvailableBindingsList;
            }
        else
            {
            *fMultipleBindingsAvailable = FALSE;
            return Bindings.SelectedBinding;
            }
    }

    RPC_STATUS 
    BindCompleteNotify (
        IN p_result_t *OsfResult, 
        IN int IndexOfPresentationContextAccepted,
		OUT OSF_BINDING **BindingNegotiated
        );

#ifdef DEBUGRPC
    inline void EnterCallback(void)
    {
        CallbackLevel ++;
    }

    inline void ExitCallback(void)
    {
        CallbackLevel --;
    }

    inline BOOL IsCallInCallback(void)
    {
        return CallbackLevel > 0;
    }
#endif

private:
    void
    SendFault (
        IN RPC_STATUS Status,
        IN int DidNotExecute
        );

#define MAX_ALLOC_Hint 16*1024

    RPC_STATUS
    EatAuthInfoFromPacket (
        IN rpcconn_request  * Request,
        IN OUT UINT  * RequestLength
        );

    BOOL
    ProcessReceivedPDU (
        IN void *Buffer,
        IN int BufferLength
        );

    RPC_STATUS
    GetCoalescedBuffer (
        IN PRPC_MESSAGE Message,
        IN BOOL BufferValid
        );

    RPC_STATUS
    SendAlterContextPDU (
        );

    RPC_STATUS
    GetCoalescedBuffer(
        IN PRPC_MESSAGE Message);

    RPC_STATUS
    FastSendReceive (
        IN OUT PRPC_MESSAGE Message,
        OUT BOOL *fRetry
        );

    RPC_STATUS
    SendNextFragment (
        IN unsigned char PacketType = rpc_request,
        IN BOOL fFirstSend = TRUE,
        OUT void **ReceiveBuffer = 0,
        OUT UINT *ReceivedLength = 0
        );

    RPC_STATUS
    ActuallyProcessPDU (
        IN rpcconn_common *Packet,
        IN UINT PacketLength,
        IN OUT PRPC_MESSAGE Message,
        IN BOOL fAsync = 0,
        OUT BOOL *pfSubmitReceive = NULL
        );

    RPC_STATUS
    ProcessResponse (
        IN rpcconn_response *Packet,
        IN PRPC_MESSAGE Message,
        OUT BOOL *pfSubmitReceive
        );

    RPC_STATUS
    ProcessRequestOrResponse (
        IN rpcconn_request *Request,
        IN UINT PacketLength,
        IN BOOL fRequest,
        IN PRPC_MESSAGE Message
        );

    RPC_STATUS
    DealWithCallback (
        IN rpcconn_request *Request,
        IN PRPC_MESSAGE Message
        );

    RPC_STATUS
    ReceiveReply (
        IN rpcconn_request *Request,
        IN PRPC_MESSAGE Message
        );

    void
    CallFailed (
        IN RPC_STATUS Status
        ) ;

    int
    QueueBuffer (
        IN void *Buffer,
        IN int BufferLength);

    BOOL
    IssueNotification (
        IN RPC_ASYNC_EVENT Event = RpcCallComplete
        );

    RPC_STATUS ReserveSpaceForSecurityIfNecessary (void);

    void UpdateObjectUUIDInfo (IN UUID *ObjectUuid);

    void UpdateMaxFragLength (const IN ULONG AuthnLevel);

    RPC_STATUS AutoRetryCall(IN OUT RPC_MESSAGE *Message, IN BOOL fSendReceivePath,
        IN OSF_BINDING_HANDLE *LocalBindingHandle, IN RPC_STATUS CurrentStatus,
        IN RPC_ASYNC_STATE *AsyncState OPTIONAL);

    void CleanupOldCallOnAutoRetry(IN PVOID Buffer, IN BOOL fSendReceivePath, 
        IN RPC_STATUS CurrentStatus)
    {
        FreeBufferDo(Buffer);

        if (fSendReceivePath)        
            FreeCCall(CurrentStatus);
        else
            {
            //
            // Remove the call reference, CCALL--
            //
            RemoveReference();
            }        
    }

    OSF_BINDING *GetSelectedBinding(void)
    {
        ASSERT(Bindings.SelectedBinding != NULL);
        return Bindings.SelectedBinding;
    }

    OSF_BINDING *GetBindingList(void)
    {
        ASSERT(Bindings.AvailableBindingsList != NULL);
        return Bindings.AvailableBindingsList;
    }

    void *
    ActualBuffer (
        IN void *Buffer
        );

    RPC_STATUS
    CallCancelled (
        OUT PDWORD Timeout
        );

    RPC_STATUS
    RegisterCallForCancels (
        );

    void
    UnregisterCallForCancels (
        );

    void * operator new (
        size_t allocBlock,
        unsigned int xtraBytes
        );

    RPC_STATUS
    UpdateBufferSize (
       IN OUT void **Buffer,
       IN int CurrentBufferLength
       );

    inline BOOL
    fOkToAdvanceCall()
    {
        return (fAdvanceCallCount.Increment() == 2);
    }

    virtual RPC_STATUS
    InqSecurityContext (
        OUT void **SecurityContextHandle
        );

    static RPC_STATUS 
    NegotiateTransferSyntaxAndGetBuffer (
        IN OUT PRPC_MESSAGE Message,
        IN UUID *ObjectUuid
        );

    inline ULONG
    GetBindingHandleTimeout (
        IN OSF_BINDING_HANDLE *BindingToUse
        )
    {
        RPC_STATUS RpcStatus;
        ULONG_PTR OptionValue;

        RpcStatus = BindingToUse->OSF_BINDING_HANDLE::InqTransportOption(
            RPC_C_OPT_CALL_TIMEOUT,
            &OptionValue);

        ASSERT(RpcStatus == RPC_S_OK);

        if (OptionValue == 0)
            return INFINITE;
        else
            return (ULONG) OptionValue;
    }

    inline RPC_STATUS
    GetStatusForTimeout (
        IN OSF_BINDING_HANDLE *BindingHandleToUse,
        IN RPC_STATUS Status,
        BOOL fBindingHandleTimeoutUsed
        )
    {
        if (Status == RPC_P_TIMEOUT)
            {
            if (fBindingHandleTimeoutUsed)
                {
                Status = RPC_S_CALL_CANCELLED;
                RpcpErrorAddRecord(EEInfoGCRuntime,
                    Status,
                    EEInfoDLGetStatusForTimeout10,
                    RPC_P_TIMEOUT,
                    GetBindingHandleTimeout(BindingHandle));
                }
            else
                {
                Status = RPC_S_CALL_FAILED_DNE;
                RpcpErrorAddRecord(EEInfoGCRuntime,
                    Status,
                    EEInfoDLGetStatusForTimeout20,
                    (ULONG)RPC_P_TIMEOUT,
                    (ULONG)RPC_C_SERVER_BIND_TIMEOUT);
                }
            }

        return Status;
    }

    inline ULONG
    GetEffectiveTimeoutForBind (
        IN OSF_BINDING_HANDLE *BindingToUse,
        OUT BOOL *fBindingHandleTimeoutUsed
        )
    {
        ULONG Timeout;

        Timeout = GetBindingHandleTimeout(BindingToUse);
        if (Timeout > RPC_C_SERVER_BIND_TIMEOUT)
            {
            Timeout = RPC_C_SERVER_BIND_TIMEOUT;
            *fBindingHandleTimeoutUsed = FALSE;
            }
        else
            {
            *fBindingHandleTimeoutUsed = TRUE;
            }

        return Timeout;
    }
};

inline void *
OSF_CCALL::operator new (
        size_t allocBlock,
        unsigned int xtraBytes
        )
{
    I_RPC_HANDLE pvTemp = (I_RPC_HANDLE) new char[allocBlock + xtraBytes];

    return(pvTemp);
}

inline void
OSF_CCALL::FreeObject (
    )
{
    if (AsyncStatus == RPC_S_ASYNC_CALL_PENDING)
        {
        AsyncStatus = RPC_S_CALL_FAILED;
        }

    FreeCCall(AsyncStatus);
}

inline RPC_STATUS
OSF_CCALL::RegisterCallForCancels (
    )
{
    RPC_STATUS Status;

    if (pAsync == 0)
        {
        Status = RegisterForCancels(this);
        if (Status != RPC_S_OK)
            {
            return Status;
            }

        if (ThreadGetRpcCancelTimeout() == RPC_C_CANCEL_INFINITE_TIMEOUT)
            {
            CancelState = CANCEL_INFINITE;
            }
        else
            {
            CancelState = CANCEL_NOTINFINITE;
            }
        }
    return RPC_S_OK;
}

inline void
OSF_CCALL::UnregisterCallForCancels (
    )
{
    if (pAsync == 0)
        {
        EVAL_AND_ASSERT(RPC_S_OK == UnregisterForCancels());
        }
}


inline void *
OSF_CCALL::ActualBuffer (
    IN void *Buffer
    )
{
    ASSERT (HeaderSize != 0);
    if (UuidSpecified)
        {
        Buffer = (char  *) Buffer  - sizeof(rpcconn_request) - sizeof(UUID);
        }
    else
        {
        Buffer = (char  *) Buffer  - sizeof(rpcconn_request);
        }

    return Buffer;
}

inline int
OSF_CCALL::QueueBuffer (
    IN void *Buffer,
    IN int BufferLength
    )
{
   RcvBufferLength += BufferLength;

   return BufferQueue.PutOnQueue(Buffer, BufferLength);
}

NEW_SDICT2(OSF_CCALL, PVOID);

#define SYNC_CONN_FREE 0
#define SYNC_CONN_BUSY -1
#define ASYNC_CONN_BUSY -2
#define ASYNC_CONN_FREE -3

enum CONNECTION_STATES
{
    ConnUninitialized,
    ConnAborted,
    ConnOpen
};

const unsigned int FreshFromCache = 1;

enum FAILURE_COUNT_STATE
{
    FailureCountUnknown,
    FailureCountNotExceeded,
    FailureCountExceeded
};


class OSF_CCONNECTION : public REFERENCED_OBJECT
/*++

Class Description:
        This class encapsulates a transport connection. The transport
        connection may be used by one or more RPC calls. Each RPC
        call is encapsulated by an CALL object. This object may
        be exclusively used by a call. Each connection is owned by
        exacly one thread (ie: all calls on a connection have been
        initiated by a single thread).

Fields:
        Association: Pointer to the association object

        ConnectionKey: Key to our entry in the dictionary of connections in
                the association.

        ThreadId: Thread Id of the thread owning this call.

        DceSecurityInfo - Contains information necessary for DCE security to
        work properly.  This includes the association uuid (a different
        value for each connection), and sequence numbers (both directions).

        ThirdLegAuthNeeded - Contains a flag indicating whether or not third
        leg authentication is needed; a non-zero value indicates that it
        is needed.

        ClientSecurityContext - Optionally contains the security context being
        used for this connection.

        AdditionalSpaceForSecurity - Contains the amount of space to save
        for security in each buffer we allocate.

        LastTimeUsed - Contains the time in seconds when this connection was
        last used.

        CurrentCall - The call on which we are currently **sending** data

        ClientInfo - Contains the pointers to the loadable transport routines
        for the transport type of this connection.

        fConnectionAborted - If this field is non-zero it indicates that the
        connection has been aborted, meaning that any attempts to send
        or receive data on the connection will fail.

--*/
{
friend class OSF_CASSOCIATION;
friend class OSF_CCALL;
friend class OSF_BINDING_HANDLE;

private:
    OSF_CASSOCIATION * Association;
    OSF_CCALL *CurrentCall ;
    int  ConnectionKey;
    CONNECTION_STATES State;
    unsigned char WireAuthId;
    unsigned short MaxFrag;
    ULONG ThreadId ;
    BOOL CachedCCallAvailable ;
    ULONG MaxSavedHeaderSize;
    OSF_CCALL *CachedCCall ;
    ULONG SavedHeaderSize;
    unsigned int ComTimeout ;
    void  *   SavedHeader;
    BOOL AdditionalLegNeeded;
    ULONG LastTimeUsed;
    UINT TokenLength;
    UINT AdditionalSpaceForSecurity;
    BOOL fIdle ;
    BOOL fExclusive ;
    BOOL fConnectionAborted;
    CompositeFlags Flags;

    BITSET Bindings;
    QUEUE CallQueue ;
    MUTEX ConnMutex ;
    OSF_CCALL_DICT2 ActiveCalls ;
    SECURITY_CONTEXT ClientSecurityContext;

    RPC_CONNECTION_TRANSPORT * ClientInfo;
    union
        {
        void *ConnSendContext ;
        OSF_CCONNECTION *NextConnection;     // used to link connections in some case
        } u;

    DCE_SECURITY_INFO DceSecurityInfo;

    void *BufferToFree;
    BOOL ConnectionReady;
    BOOL fSeparateConnection;

public:
    OSF_CCONNECTION (
        IN OSF_CASSOCIATION *Association,
        IN RPC_CONNECTION_TRANSPORT * RpcClientInfo,
        IN unsigned int Timeout,
        IN CLIENT_AUTH_INFO * myAuthInfo,
        IN BOOL fExclusive,
        IN BOOL fSeparateConnection,
        OUT RPC_STATUS  * pStatus
        );

    virtual ~OSF_CCONNECTION (
        );

    virtual void
    ProcessSendComplete (
        IN RPC_STATUS EventStatus,
        IN BUFFER Buffer
        );

    RPC_STATUS
    TransInitialize (
        IN RPC_CHAR *NetworkAddress,
        IN RPC_CHAR *NetworkOptions
        )
    {
        if (ClientInfo->Initialize)
            {
            return ClientInfo->Initialize(TransConnection(), 
                                          NetworkAddress, 
                                          NetworkOptions,
                                          (fExclusive == 0));
            }

        return (RPC_S_OK);
    }

    void
    TransInitComplete (
        )
    {
        if (ClientInfo->InitComplete)
            {
            ClientInfo->InitComplete(TransConnection());
            }
    }

    RPC_STATUS
    TransOpen (
        IN OSF_BINDING_HANDLE * BindingHandle,
        IN RPC_CHAR * RpcProtocolSequence,
        IN RPC_CHAR * NetworkAddress,
        IN RPC_CHAR * Endpoint,
        IN RPC_CHAR * NetworkOptions,
        IN void *ResolverHint,
        IN BOOL fHintInitialized,
        IN ULONG Timeout
        ) ;

    RPC_STATUS
    TransReceive (
        OUT void  *  * Buffer,
        OUT unsigned int  * BufferLength,
        IN ULONG Timeout
        );

    RPC_STATUS
    TransSend (
        IN void  * Buffer,
        IN unsigned int BufferLength,
        IN BOOL fDisableShutdownCheck,
        IN BOOL fDisableCancelCheck,
        IN ULONG Timeout
        );

#ifdef WIN96
    RPC_STATUS
    TransClose (
        );
#endif

    RPC_STATUS
    TransSendReceive (
        IN void  * SendBuffer,
        IN unsigned int SendBufferLength,
        OUT void  *  * ReceiveBuffer,
        OUT unsigned int  * ReceiveBufferLength,
        IN ULONG Timeout
        );

    RPC_STATUS
    TransAsyncSend (
        IN void  * SendBuffer,
        IN unsigned int SendBufferLength,
        IN void  *SendContext
        ) ;

    RPC_STATUS
    TransAsyncReceive (
        ) ;

    RPC_STATUS
    TransPostEvent (
        IN PVOID Context
        ) ;

    void
    TransAbortConnection (
        );

    void
    TransClose (
        );

    unsigned int
    TransMaximumSend (
        );

    unsigned int
    GuessPacketLength (
        );

    void * operator new (
        size_t allocBlock,
        unsigned int xtraBytes
        );

    RPC_STATUS
    TransGetBuffer (
        OUT void  *  * Buffer,
        IN UINT BufferLength
        );

    void
    TransFreeBuffer (
        IN void  * Buffer
        );

    RPC_STATUS
    TransReallocBuffer (
        IN OUT void  *  * Buffer,
        IN UINT OldSize,
        IN UINT NewSize
        );

    RPC_STATUS
    AllocateCCall (
        OUT OSF_CCALL **CCall
        );

    RPC_STATUS
    AddCall (
        IN OSF_CCALL *CCall
        );

    void
    FreeCCall (
        IN OSF_CCALL *CCall,
        IN RPC_STATUS Status,
        IN ULONG ComTimeout
        );

    RPC_STATUS
    OpenConnectionAndBind (
        IN OSF_BINDING_HANDLE *BindingHandle,
        IN ULONG Timeout,
        IN BOOL fAlwaysNegotiateNDR20,
        OUT FAILURE_COUNT_STATE *fFailureCountExceeded OPTIONAL
        );

    RPC_STATUS
    ActuallyDoBinding (
        IN OSF_CCALL *CCall,
        IN ULONG MyAssocGroupId,
        IN BOOL fNewConnection,
        IN ULONG Timeout,
        OUT OSF_BINDING **BindingNegotiated,
        OUT BOOL *fPossibleAssociationReset,
        OUT FAILURE_COUNT_STATE *fFailureCountExceeded OPTIONAL
        );

    RPC_STATUS
    SendBindPacket (
        IN BOOL fInitialPass,
        IN OSF_CCALL *Call,
        IN ULONG AssocGroup,
        IN unsigned char PacketType,
        IN ULONG Timeout,
        IN BOOL fAsync = 1,
        OUT rpcconn_common * * Buffer       = 0,
        OUT UINT           *   BufferLength = 0,
        IN rpcconn_common * InputPacket     = 0,
        IN unsigned int  InputPacketLength  = 0
        );

    RPC_STATUS
    MaybeDo3rdLegAuth (
        IN void  * Buffer,
        IN UINT BufferLength
        );

    //
    // Set the value of the max frag for the connection.
    //
    void
    SetMaxFrag (
        IN unsigned short max_xmit_frag,
        IN unsigned short max_recv_frag
        );

    UINT
    InqMaximumFragmentLength (
        );

    int
    AddPContext (
        IN int PresentContext
        ) {return(Bindings.Insert(PresentContext));}

    void
    DeletePContext (
        IN int PresentContext
        ) {Bindings.Delete(PresentContext);}

    int
    SupportedPContext (
        IN int *PresentationContexts,
        IN int NumberOfPresentationContexts,
        OUT int *PresentationContextSupported
        );

    int
    SupportedAuthInfo (
        IN CLIENT_AUTH_INFO * ClientAuthInfo,
        IN BOOL fNamedPipe
        );

    RPC_STATUS
    SendFragment (
        IN rpcconn_common  *pFragment,
        IN OSF_CCALL *CCall,
        IN UINT LastFragmentFlag,
        IN UINT HeaderSize,
        IN UINT MaxSecuritySize,
        IN UINT DataLength,
        IN UINT MaximumFragmentLength,
        IN unsigned char  *ReservedForSecurity,
        IN BOOL fAsync,
        IN void *SendContext,
        IN ULONG Timeout,
        OUT void **ReceiveBuffer,
        OUT UINT *ReceiveBufferLength
        );

    void
    ProcessReceiveComplete (
        IN RPC_STATUS EventStatus,
        IN BUFFER Buffer,
        IN UINT BufferLength
        );

    ULONG
    InquireSendSequenceNumber (
        );

    ULONG
    InquireReceiveSequenceNumber (
        );

    RPC_CHAR *InqEndpoint(void);

    RPC_CHAR *InqNetworkAddress(void);

    void
    IncSendSequenceNumber (
        );

    inline void
    IncReceiveSequenceNumber (
        );

    void
    NotifyCallDeleted (
        );

    void
    SetLastTimeUsedToNow (
        );

    ULONG
    InquireLastTimeUsed (
        );

    ULONG
    GetAssocGroupId (
        );

    void
    ConnectionAborted (
        IN RPC_STATUS Status,
        IN BOOL fShutdownAssoc = 1
        );

    void
    AdvanceToNextCall (
        );

    RPC_STATUS
    AddActiveCall (
        IN ULONG CallId,
        IN OSF_CCALL *CCall
        );

    RPC_STATUS
    DealWithAlterContextResp (
        IN OSF_CCALL *CCall,
        IN rpcconn_common *Packet,
        IN int PacketLength,
        IN OUT BOOL *AlterContextToNDR20IfNDR64Negotiated
        );

    void
    MakeConnectionIdle (
        );

    void
    MakeConnectionActive (
        );

    BOOL
    IsIdle (
        );

    void
    DeleteConnection (
        );

    BOOL IsExclusive (void)
    {
        return fExclusive;
    }

    RPC_STATUS
    ValidateHeader(
         rpcconn_common * Buffer,
         unsigned long BufferLength
         );

    RPC_STATUS
    FinishSecurityContextSetup (
        IN OSF_CCALL *Call,
        IN unsigned long AssocGroup,
        OUT rpcconn_common * * Buffer,
        OUT unsigned int * BufferLength,
        IN ULONG Timeout
        );

    RPC_STATUS
    CallCancelled (
        OUT PDWORD Timeout
        );

    void
    WaitForSend(
        );

    BOOL
    MatchModifiedId (
        LUID *pModifiedId
        )
    {
        if (ClientSecurityContext.DefaultLogonId)
            {
            return FALSE;
            }

        return (FastCompareLUIDAligned(pModifiedId, &ClientSecurityContext.ModifiedId));
    }

    void
    MaybeAdvanceToNextCall(
        OSF_CCALL *Call
        )
    {
        if (fExclusive == 0
            && CurrentCall == Call)
            {
            AdvanceToNextCall();
            }
    }

    RPC_STATUS
    AbortAndWait (
        OSF_CCALL *Call
        )
    {
        int retval;
        
        // CCONN++
        AddReference();

        //
        // There is still a race condition here
        //
        ConnMutex.Request();
        ActiveCalls.Delete(IntToPtr(Call->CallId));
        ConnMutex.Clear();

        TransAbortConnection();

        //
        // Wait until only the call reference (and the reference added above)
        // remains
        //
        while (RefCount.GetInteger() > 2)
            {
            SleepEx(1, 0);
            }

        ASSERT(IsDeleted());

        TransClose();

        //
        // Ugly hack alert
        //
        SetNotDeleted();

        ConnMutex.Request();
        retval = ActiveCalls.Insert(IntToPtr(Call->CallId), Call);
        ConnMutex.Clear();

        //
        // Don't remove the above reference
        // the connection reference was removed by the thread
        // that did called abort. We are just restoring that reference
        //

        if (retval == -1)
            {
            return RPC_S_OUT_OF_MEMORY;
            }

        return RPC_S_OK;
    }

    inline void SetFreshFromCacheFlag(void)
    {
        Flags.SetFlagUnsafe(FreshFromCache);
    }
    inline void ClearFreshFromCacheFlag(void)
    {
        Flags.ClearFlagUnsafe(FreshFromCache);
    }
    inline BOOL GetFreshFromCacheFlag(void)
    {
        return Flags.GetFlag(FreshFromCache);
    }

    static void
    OsfDeleteIdleConnections (
        void
        );

    RPC_STATUS
    TurnOnOffKeepAlives (
        IN BOOL TurnOn,
        IN ULONG Timeout
        );

private:

    inline void
    InitializeWireAuthId (
        IN CLIENT_AUTH_INFO * ClientAuthInfo
        )
    {
        if (ClientAuthInfo)
            WireAuthId = (unsigned char) ClientAuthInfo->AuthenticationService;
        else
            WireAuthId = RPC_C_AUTHN_NONE;        
    }
};

inline void *
OSF_CCONNECTION::operator new (
        size_t allocBlock,
        unsigned int xtraBytes
        )
{
    I_RPC_HANDLE pvTemp = (I_RPC_HANDLE) new char[allocBlock + xtraBytes];

    return(pvTemp);
}

#pragma optimize ("t", on)
inline void
OSF_CCONNECTION::MakeConnectionIdle (
    )
{
    LogEvent(SU_CCONN, EV_STOP, this, 0, 0, 1);
    fIdle = 1;
}

inline void
OSF_CCONNECTION::MakeConnectionActive (
    )
{
    LogEvent(SU_CCONN, EV_START, this, 0, 0, 1);
    fIdle = 0;
}

inline BOOL
OSF_CCONNECTION::IsIdle (
    )
{
    return fIdle;
}

inline void
OSF_CCONNECTION::NotifyCallDeleted (
    )
{
}

inline void
OSF_CCONNECTION::IncSendSequenceNumber (
    )
{
    DceSecurityInfo.ReceiveSequenceNumber += 1;
}

inline void
OSF_CCONNECTION::IncReceiveSequenceNumber (
    )
{
    DceSecurityInfo.ReceiveSequenceNumber += 1;
}

inline ULONG
OSF_CCONNECTION::InquireSendSequenceNumber (
    )
{
    return DceSecurityInfo.SendSequenceNumber ;
}

inline ULONG
OSF_CCONNECTION::InquireReceiveSequenceNumber (
    )
{
    return DceSecurityInfo.ReceiveSequenceNumber ;
}


inline int
OSF_CCONNECTION::SupportedPContext (
    IN int *PresentationContexts,
    IN int NumberOfPresentationContexts,
    OUT int *PresentationContextSupported
    )
/*++

Return Value:

    Non-zero will be returned if this connection supports the supplied
    presentation context; otherwise, zero will be returned.

--*/
{
    int i;
    int Result;

    for (i = 0; i < NumberOfPresentationContexts; i ++)
        {
        Result = Bindings.MemberP(PresentationContexts[i]);
        if (Result)
            {
            *PresentationContextSupported = i;
            break;
            }
        }
    return Result;
}


inline UINT
OSF_CCONNECTION::InqMaximumFragmentLength (
    )
/*++

Return Value:

    The maximum fragment length negotiated for this connection will be
    returned.

--*/
{
    return(MaxFrag);
}

#pragma optimize("", on)




inline void
OSF_CCONNECTION::SetLastTimeUsedToNow (
    )
/*++

Routine Description:

    We the the last time that this connection was used to now.

--*/
{
    LastTimeUsed = NtGetTickCount();
}

inline ULONG
OSF_CCONNECTION::InquireLastTimeUsed (
    )
/*++

Return Value:

    The last time this connection was used will be returned.

--*/
{
    return(LastTimeUsed);
}


inline RPC_STATUS
OSF_CCALL::InqSecurityContext (
    OUT void **SecurityContextHandle
    )
{
    *SecurityContextHandle = Connection->ClientSecurityContext.InqSecurityContext();
    return RPC_S_OK;
}

inline RPC_STATUS
OSF_CCALL::InqWireIdForSnego (
    OUT unsigned char *WireId
    )
{
    if ((Connection->ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE)
        || (Connection->ClientSecurityContext.AuthenticationService == RPC_C_AUTHN_NONE))
        return RPC_S_INVALID_BINDING;

    return Connection->ClientSecurityContext.GetWireIdForSnego(WireId);
}

inline RPC_STATUS 
OSF_CCALL::BindingHandleToAsyncHandle (
    OUT void **AsyncHandle
    )
{
    if (pAsync == 0)
        return RPC_S_INVALID_BINDING;

    *AsyncHandle = pAsync;
    return RPC_S_OK;
}

class OSF_BINDING : public MTSyntaxBinding
{
public:
    OSF_BINDING (
        IN RPC_SYNTAX_IDENTIFIER *InterfaceId,
        IN TRANSFER_SYNTAX_STUB_INFO *TransferSyntaxInfo,
        IN int CapabilitiesBitmap
        ) : MTSyntaxBinding(InterfaceId, TransferSyntaxInfo, CapabilitiesBitmap)
    {
    }

    inline void SetNextBinding(OSF_BINDING *Next)
    {
        MTSyntaxBinding::SetNextBinding(Next);
    }

    inline OSF_BINDING *GetNextBinding(void)
    {
        return (OSF_BINDING *)(MTSyntaxBinding::GetNextBinding());
    }    

    inline int CompareWithTransferSyntax (IN const p_syntax_id_t *TransferSyntax)
    {
        return CompareWithTransferSyntax ((RPC_SYNTAX_IDENTIFIER *)TransferSyntax);
    }

    inline int CompareWithTransferSyntax (IN const RPC_SYNTAX_IDENTIFIER *TransferSyntax)
    {
        return MTSyntaxBinding::CompareWithTransferSyntax ((RPC_SYNTAX_IDENTIFIER *)TransferSyntax);
    }

};


NEW_SDICT(OSF_BINDING);
NEW_SDICT(OSF_CCONNECTION);

enum MPX_TYPES
    {
    mpx_unknown,
    mpx_yes,
    mpx_no
    };

NEW_SDICT(RPC_TOKEN);


class OSF_CASSOCIATION : public REFERENCED_OBJECT
/*++

Class Description:

Fields:

    MaintainContext - Contains a flag indicating whether or not this
        association needs to keep at least one connection open with
        the server if at all possible.  A non-zero value indicates that
        at least one connection should be kept open.

    CallIdCounter - Contains an interlocked integer used to allocate
        unique call identifiers.

    BindHandleCount - Counts the number of OSF_BINDING_HANDLEs using
        this association.  This particular variable is operated
        with interlocks. However, adding a refcount if you don't have
        one (as in FindOrCreateAssociation - through the global data
        structure) requires holding AssocDictMutex.
--*/
{
friend class OSF_CCONNECTION;
friend class OSF_CCALL;
friend class OSF_BINDING_HANDLE;

private:
    DCE_BINDING * DceBinding;
    INTERLOCKED_INTEGER BindHandleCount;
    ULONG AssocGroupId;
    OSF_BINDING_DICT    Bindings;
    OSF_CCONNECTION_DICT ActiveConnections;

    TRANS_INFO *TransInfo ;
    unsigned char * SecondaryEndpoint;
    int Key;
    UINT OpenConnectionCount;
    UINT ConnectionsDoingBindCount;
    BOOL fPossibleServerReset;

    UINT MaintainContext;
    ULONG  CallIdCounter;

    MUTEX AssociationMutex;

    BOOL AssociationValid;

    // in some cases we will decide that the association is dead,
    // and we will mark it as such so that other calls can
    // quickly fail instead of banging against a dead server.
    // The error with which the association was shutdown is
    // stored here. Callers that implement quick failure detection
    // can read it off here. This is valid only if
    // AssociationValid == FALSE.
    RPC_STATUS AssociationShutdownError;

    BOOL DontLinger;

    BOOL ResolverHintInitialized;

    // protected by the AssociationMutex
    BOOL fIdleConnectionCleanupNeeded;

    int FailureCount;
    MPX_TYPES fMultiplex;
    unsigned long SavedDrep;
    RPC_TOKEN_DICT TokenDict;

    union
        {
        struct
            {
            // TRUE if this is an association without binding handle
            // references to it (i.e. it is lingered), FALSE
            // otherwise. Protected by the AssocDictMutex
            BOOL fAssociationLingered;

            // The timestamp for garbage collecting this item. Defined
            // only if fAssociationLingered == TRUE. Protected by the 
            // AssocDictMutex
            DWORD Timestamp;
            } Linger;

        // this arm of the union is used only during destruction - never use
        // it outside
        OSF_CASSOCIATION *NextAssociation;
    };

public:

    OSF_CASSOCIATION (
        IN DCE_BINDING * DceBinding,
        IN TRANS_INFO *TransInfo,
        IN OUT RPC_STATUS  * RpcStatus
        );

    ~OSF_CASSOCIATION (
        );

public:

#if 0
    void
    CleanupAuthenticatedConnections(
        IN CLIENT_AUTH_INFO *ClientAuthInfo
        );
#endif

    void
    UnBind ( // Decrement the BindingCount, and clean up the
             // association if it reaches zero.
        );

    RPC_STATUS 
    FindOrCreateOsfBinding (
        IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
        IN RPC_MESSAGE *Message,
        OUT int *NumberOfBindings,
        IN OUT OSF_BINDING *BindingsForThisInterface[]
        );

    BOOL
    DoesBindingForInterfaceExist (
        IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
        );

    RPC_STATUS
    AllocateCCall (
        IN OSF_BINDING_HANDLE *BindingHandle,
        IN PRPC_MESSAGE Message,
        IN CLIENT_AUTH_INFO * ClientAuthInfo,
        OUT OSF_CCALL ** CCall,
        OUT BOOL *fBindingHandleReferenceRemoved
        );

private:

    RPC_STATUS
    ProcessBindAckOrNak (
        IN rpcconn_common *Buffer,
        IN UINT BufferLength,
        IN OSF_CCONNECTION *CConnection,
        IN OSF_CCALL *CCall,
        OUT ULONG *NewGroupId,
        OUT OSF_BINDING **BindingNegotiated,
        OUT FAILURE_COUNT_STATE *fFailureCountExceeded OPTIONAL
        );

    OSF_CCONNECTION *
    LookForExistingConnection (
        IN OSF_BINDING_HANDLE *BindingHandle,
        IN BOOL fExclusive,
        IN CLIENT_AUTH_INFO *ClientAuthInfo,
        IN int *PresentContext,
        IN int NumberOfPresentationContexts,
        OUT int *PresentationContextSupported,
        OUT OSF_CCALL_STATE *InitialCallState,
        IN BOOL fNonCausal
        );

    inline void
    ClearIdleConnectionCleanupFlag (
        void
        )
    {
        AssociationMutex.VerifyOwned();
        
        if (fIdleConnectionCleanupNeeded)
            {
            fIdleConnectionCleanupNeeded = FALSE;
            if (InterlockedDecrement(&PeriodicGarbageCollectItems) == 0)
                {
#if defined (RPC_GC_AUDIT)
                DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) PeriodicGarbageCollectItems dropped to 0 (a)\n",
                    GetCurrentProcessId(), GetCurrentProcessId());
#endif
                }
            }
    }

public:

    int
    CompareWithDceBinding (
        IN DCE_BINDING * DceBinding,
        OUT BOOL *fOnlyEndpointDifferent
        );

    //
    // Note, whoever calls this routine must have already
    // requested the AssociationDictMutex.
    //
    void
    IncrementCount (
        void
        ) 
    {
        LogEvent(SU_CASSOC, EV_INC, this, (PVOID)2, BindHandleCount.GetInteger(), 1, 0);
        BindHandleCount.Increment();
    }

    void
    ShutdownRequested (
        IN RPC_STATUS AssociationShutdownError OPTIONAL,
        IN OSF_CCONNECTION *ExemptConnection OPTIONAL
        );

    inline OSF_BINDING *
    FindBinding (
        IN int PresentContext
        ) 
    {
        return(Bindings.Find(PresentContext));
    }

    virtual RPC_STATUS
    ToStringBinding (
        OUT RPC_CHAR  *  * StringBinding,
        IN RPC_UUID * ObjectUuid
        );

    OSF_CCONNECTION *
    FindIdleConnection (
        void
        );

    void
    MaintainingContext (
        );

    DCE_BINDING *
    DuplicateDceBinding (
        );

    BOOL
    IsValid (
        );

    BOOL
    ConnectionAborted (
        IN OSF_CCONNECTION *Connection
        );

    void
    NotifyConnectionOpen (
        );

    void
    NotifyConnectionClosed (
        );

    void NotifyConnectionBindInProgress (
        void
        );

    void NotifyConnectionBindCompleted (
        void
        );

    void * operator new (
        size_t allocBlock,
        unsigned int xtraBytes
        );

    int
    CompareResolverHint (
        void *Hint
        );

    void *
    InqResolverHint (
        );

    void
    SetResolverHint (
        void *Hint
        );

    void
    FreeResolverHint (
        void *Hint
        );

    inline BOOL
    IsResolverHintSynchronizationNeeded (
        void
        );

    inline void
    ResetAssociation (
       ) 
    {
        AssocGroupId = 0;
        if (ResolverHintInitialized)
            {
            ResolverHintInitialized = FALSE;
            FreeResolverHint(InqResolverHint());
            }
    }

    BOOL
    IsAssociationReset ( void )
    {
        return (AssocGroupId == 0);
    }

    RPC_STATUS
    FindOrCreateToken (
        IN HANDLE hToken,
        IN LUID *pLuid,
        OUT RPC_TOKEN **ppToken,
        OUT BOOL *pfTokenFound
        );

    void
    ReferenceToken(
        IN RPC_TOKEN *pToken
        );

    void
    DereferenceToken(
        IN RPC_TOKEN *pToken
        );

    void
    CleanupConnectionList(
        IN RPC_TOKEN *pToken
        );

    static void
    OsfDeleteLingeringAssociations (
        void
        );

    inline BOOL
    GetDontLingerState (
        void
        )
    {
        return DontLinger;
    }

    inline void
    SetDontLingerState (
        IN BOOL DontLinger
        )
    {
        ASSERT(DontLinger == TRUE);
        ASSERT(this->DontLinger == FALSE);
        this->DontLinger = DontLinger;
    }
};

inline int
OSF_CASSOCIATION::CompareResolverHint (
        void *Hint
        )
{
    RPC_CONNECTION_TRANSPORT *ClientInfo =
            (RPC_CONNECTION_TRANSPORT *) TransInfo->InqTransInfo();

    if (ClientInfo->CompareResolverHint)
        {
        return ClientInfo->CompareResolverHint(TransResolverHint(), Hint);
        }
    else
        {
        return RpcpMemoryCompare(TransResolverHint(), Hint, ClientInfo->ResolverHintSize);
        }
}

inline void *
OSF_CASSOCIATION::InqResolverHint (
        )
{
    return TransResolverHint();
}

inline void
OSF_CASSOCIATION::SetResolverHint (
        void *Hint
        )
{
    RPC_CONNECTION_TRANSPORT *ClientInfo =
            (RPC_CONNECTION_TRANSPORT *) TransInfo->InqTransInfo();

    if (ClientInfo->CopyResolverHint)
        {
        if (TransResolverHint() != Hint)
            {
            ClientInfo->CopyResolverHint(TransResolverHint(), 
                Hint, 
                TRUE    // SourceWillBeAbandoned
                );
            }
        }
    else
        {
        RpcpMemoryCopy(TransResolverHint(), Hint, ClientInfo->ResolverHintSize);
        }
}

inline void
OSF_CASSOCIATION::FreeResolverHint (
    void *Hint
    )
{
    RPC_CONNECTION_TRANSPORT *ClientInfo =
            (RPC_CONNECTION_TRANSPORT *) TransInfo->InqTransInfo();

    if (ClientInfo->FreeResolverHint)
        {
        ClientInfo->FreeResolverHint(Hint);
        }
}

BOOL
OSF_CASSOCIATION::IsResolverHintSynchronizationNeeded (
    void
    )
{
    RPC_CONNECTION_TRANSPORT *ClientInfo =
            (RPC_CONNECTION_TRANSPORT *) TransInfo->InqTransInfo();

    return (ClientInfo->FreeResolverHint != NULL);
}

inline void *
OSF_CASSOCIATION::operator new (
        size_t allocBlock,
        unsigned int xtraBytes
        )
{
    I_RPC_HANDLE pvTemp = (I_RPC_HANDLE) new char[allocBlock + xtraBytes];

    return(pvTemp);
}

inline BOOL
OSF_CASSOCIATION::IsValid (
    )
{
    return AssociationValid ;
}


inline void
OSF_CASSOCIATION::MaintainingContext (
    )
/*++

Routine Description:

    This routine is used to indicate that a binding handle using this
    association is maintaining context with the server.  This means
    that at least one connection for this association must always be
    open.

--*/
{
    MaintainContext = 1;
}


inline DCE_BINDING *
OSF_CASSOCIATION::DuplicateDceBinding (
    )
{
    return(DceBinding->DuplicateDceBinding());
}

inline ULONG
OSF_CCONNECTION::GetAssocGroupId (
    )
{
    return Association->AssocGroupId ;
}

inline void
OSF_CCONNECTION::WaitForSend (
    )
{
    while (ConnectionReady == 0)
        {
        //
        // We need a listening thread
        // so the send can complete
        //
        Association->TransInfo->CreateThread();
        Sleep(10);
        }

    ASSERT(ConnectionReady == 1);
    ConnectionReady = 0;
}

inline void
OSF_CCONNECTION::DeleteConnection (
    )
{
    if (fExclusive == 0)
        {
        TransAbortConnection();
        }

    Delete();
}

inline RPC_CHAR *OSF_CCONNECTION::InqEndpoint(void)
    {
    return Association->DceBinding->InqEndpoint();
    }

inline RPC_CHAR *OSF_CCONNECTION::InqNetworkAddress(void)
    {
    return Association->DceBinding->InqNetworkAddress();
    }

inline void 
OSF_BINDING_HANDLE::Unbind ()
{
    BOOL fMutexTaken;

    // try to take the two mutexes, but if fail on the second, release
    // the first as well and retry, because there is a potential
    // deadlock condition for which this is a workaround
    while (TRUE)
        {
        AssocDictMutex->Request();
        fMutexTaken = Association->AssociationMutex.TryRequest();
        if (fMutexTaken)
            break;
        else
            {
            AssocDictMutex->Clear();
            Sleep(10);
            }
        }

    if (pToken)
        {
        Association->DereferenceToken(pToken);
        pToken = 0;
        }

    // unbind will clear the association dict mutex
    // and the association mutex
    Association->UnBind();

    Association = 0;
}

RPC_STATUS
LoadableTransportClientInfo (
    IN RPC_CHAR * DllName,
    IN RPC_CHAR  * RpcProtocolSequence,
    OUT TRANS_INFO *  *ClientTransInfo
    );

TRANS_INFO  *
GetLoadedClientTransportInfoFromId (
    IN unsigned short TransportId
    );

RPC_STATUS SetAuthInformation (
    IN RPC_BINDING_HANDLE BindingHandle,
    IN CLIENT_AUTH_INFO *AuthInfo
    );

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

#endif // __OSFCLNT_HXX__