/*++

Copyright (c) 1991 Microsoft Corporation

Module Name:

    osfclnt.cxx

Abstract:

    This file contains the client side implementation of the OSF connection
    oriented RPC protocol engine.

Author:

    Michael Montague (mikemon) 17-Jul-1990

Revision History:

--*/

#include <precomp.hxx>
#include <rpctran.h>
#include <osfpcket.hxx>
#include <sdict2.hxx>
#include <bitset.hxx>
#include <osfclnt.hxx>
#include <tranclnt.hxx>
#include <rpccfg.h>
#include <epmap.h>
#include <twrtypes.h>

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

OSF_BINDING_HANDLE::OSF_BINDING_HANDLE (
    IN OUT RPC_STATUS PAPI * RpcStatus
    ) : BindingMutex(RpcStatus)
{
    ALLOCATE_THIS(OSF_BINDING_HANDLE);

    Association = 0;
    ReferenceCount = 1;
    DceBinding = 0;
    TransportInterface = 0;
}


RPC_STATUS
OSF_BINDING_HANDLE::GetBuffer (
    IN OUT PRPC_MESSAGE Message
    )
/*++

Routine Description:

Arguments:

    Message - Supplies the length of the buffer required, and returns the
        new buffer.

Return Value:

    RPC_S_OK - The operation completed successfully.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete
        the operation.

    RPC_S_NO_ENDPOINT_FOUND - The endpoint can not be resolved.

    RPC_S_OUT_OF_RESOURCES - The transport has insufficient resources
        inorder to make a connection with the server.

    RPC_S_SERVER_TOO_BUSY - The server is there, but it is too busy to
        talk with us right now.

    RPC_S_ACCESS_DENIED - The client is unable to make a connection with
        the server for security reasons.

    RPC_S_INVALID_NETWORK_OPTIONS - The supplied network options are
        invalid; see a description of the particular transport interface
        module for an explination.

    RPC_S_CALL_FAILED_DNE - The call failed and is guaranteed not to have
        executed.

    RPC_S_PROTOCOL_ERROR - A protocol error occured; this will typically
        be because of a protocol mismatch with the server.

    RPC_S_UNSUPPORTED_TRANS_SYN - The transfer syntax supplied by the client
        is not supported by the server.

    RPC_S_UNKNOWN_IF - The interface to which the client wished to bind is not
        supported by the server.

    RPC_S_UNKNOWN_AUTHN_TYPE - The server does not support the authentication
        type specified by the client.

    RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint supplied is in an incorrect
        format for the particular protocol sequence.

    EPT_S_NOT_REGISTERED  - There are no more endpoints to be found
        for the specified combination of interface, network address,
        and lookup handle.

    EPT_S_CANT_PERFORM_OP - The operation failed due to misc. error e.g.
        unable to bind to the EpMapper.

--*/
{
    OSF_CCONNECTION * CConnection;
    RPC_STATUS RpcStatus;
    unsigned int NotChangedRetry = 0;
    unsigned int Retry;

#ifdef WIN
    ASSERT( GetCurrentTask() == TaskId );

    // Verify that a there isn't a call already in progress
    RpcStatus = I_RpcWinCallInProgress();
    if (RpcStatus != RPC_S_OK)
        {
        VALIDATE((RpcStatus, RPC_S_CALL_IN_PROGRESS, RPC_S_OUT_OF_MEMORY, 0));
        return(RpcStatus);
        }
#endif

    for (;;)
        {

        Retry = 0;
        for (;;)
           {
           RpcStatus = AllocateConnection(&CConnection,
                (RPC_CLIENT_INTERFACE PAPI *) Message->RpcInterfaceInformation);
           if ( (RpcStatus != RPC_P_CONNECTION_SHUTDOWN)
              &&(RpcStatus != RPC_P_CONNECTION_CLOSED) )
             {
             break;
             }

           if (this->Association != 0)
              {
              Association->ShutdownRequested();
              }

           Retry++;
           if (Retry == MAX_RETRIES)
              break;
           }

        if ( RpcStatus != RPC_S_SERVER_UNAVAILABLE )
            {
            break;
            }
        if ( *InquireEpLookupHandle() == 0 )
            {
            break;
            }

        // If we reach here, it means that we are iterating through the list
        // of endpoints obtained from the endpoint mapper.

        BindingMutex.Request();

        if (   ( ReferenceCount == 1 )
            && ( Association != 0 ) )
            {
            DceBinding = Association->DuplicateDceBinding();
            Association->UnBind();
            Association = 0;
            DceBinding->MakePartiallyBound();
            NotChangedRetry = 0;
            }
        else
            {
            NotChangedRetry += 1;
            if ( NotChangedRetry > 4 )
                {
                BindingMutex.Clear();
                return(RPC_S_SERVER_UNAVAILABLE);
                }
            }

        BindingMutex.Clear();
        }

    if ( RpcStatus != RPC_S_OK )
        {
        if ( (RpcStatus == RPC_P_CONNECTION_CLOSED)
           ||(RpcStatus == RPC_P_CONNECTION_SHUTDOWN) )
            {
            return(RPC_S_CALL_FAILED_DNE);
            }
        return(RpcStatus);
        }

    RpcStatus = CConnection->GetBuffer(Message);

    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );
        CConnection->AbortConnection();
        return(RPC_S_OUT_OF_MEMORY);
        }
    return(RPC_S_OK);
}

OSF_BINDING_HANDLE::~OSF_BINDING_HANDLE (
    )
{
   OSF_ACTIVE_ENTRY *ActiveEntry ;

    if ( Association != 0 )
        {
        Association->UnBind();
        }
    else
        {
        delete DceBinding;
        }

     ActiveConnections.Reset();

     while ((ActiveEntry = ActiveConnections.Next()))
        {
        delete ActiveEntry->CConnection;
        }

}


RPC_STATUS
OSF_BINDING_HANDLE::BindingCopy (
    OUT BINDING_HANDLE * PAPI * DestinationBinding,
    IN unsigned int MaintainContext
    )
/*++

Routine Description:

    We need to copy this binding handle.  This is relatively easy to
    do: we just need to point the copied binding handle to the same
    association as this binding handle.  We also need to tell the
    association about the new binding handle.

Arguments:

    DestinationBinding - Returns a copy of this binding handle.

    MaintainContext - Supplies a flag that indicates whether or not context
        is being maintained over this binding handle.  A non-zero value
        indicates that context is being maintained.

Return Value:

    RPC_S_OUT_OF_MEMORY - This indicates that there is not enough memory
        to allocate a new binding handle.

    RPC_S_OK - We successfully copied this binding handle.

--*/
{
    RPC_STATUS RpcStatus = RPC_S_OK;
    OSF_BINDING_HANDLE * Binding;
    RPC_UUID Uuid;
    CLIENT_AUTH_INFO * AuthInfo;

    Binding = new OSF_BINDING_HANDLE(&RpcStatus);
    if ( RpcStatus != RPC_S_OK )
        {
        delete Binding;
        Binding = 0;
        }
    if ( Binding == 0 )
        {
        *DestinationBinding = 0;
        return(RPC_S_OUT_OF_MEMORY);
        }

    RequestGlobalMutex();
    InquireObjectUuid(&Uuid);
    Binding->SetObjectUuid(&Uuid);

    if ((AuthInfo = InquireAuthInformation()) != 0)
        {
        RpcStatus = Binding->SetAuthInformation(
                                AuthInfo->ServerPrincipalName,
                                AuthInfo->AuthenticationLevel,
                                AuthInfo->AuthenticationService,
                                AuthInfo->AuthIdentity,
                                AuthInfo->AuthorizationService,
#ifndef NTENV
                                AuthInfo->Credentials
#else
                                AuthInfo->Credentials,
                                AuthInfo->ImpersonationType,
                                AuthInfo->IdentityTracking,
                                AuthInfo->Capabilities
#endif
                               );

       if (RpcStatus != RPC_S_OK)
           {
           ASSERT (RpcStatus == RPC_S_OUT_OF_MEMORY);
           delete Binding;
           Binding = 0;
           *DestinationBinding = 0 ;

           ClearGlobalMutex();

           return(RPC_S_OUT_OF_MEMORY);
           }

        }

    Binding->Association = Association;
    if ( DceBinding != 0 )
        {
        ASSERT( MaintainContext == 0 );

        Binding->DceBinding = DceBinding->DuplicateDceBinding();
        }
    else
        {
        Binding->DceBinding = 0;
        }

    Binding->TransportInterface = TransportInterface;

    if ( Association != 0 )
        {
        Association->IncrementCount();
        if ( MaintainContext != 0 )
            {
            Association->MaintainingContext();
            }
        }



    ClearGlobalMutex();
    *DestinationBinding = (BINDING_HANDLE *) Binding;
    return(RPC_S_OK);
}


RPC_STATUS
OSF_BINDING_HANDLE::BindingFree (
    )
/*++

Routine Description:

    This method gets called when the application calls RpcBindingFree.
    All we have got to do is to decrement the reference count, and if
    it has reached zero, delete the binding handle.

Return Value:

    RPC_S_OK - This operation always succeeds.

--*/
{
    BindingMutex.Request();
    ReferenceCount -= 1;

    if ( ReferenceCount == 0 )
        {
        BindingMutex.Clear();
        delete this;
        }
    else
        {
        BindingMutex.Clear();
        }

    return(RPC_S_OK);
}


void
OSF_BINDING_HANDLE::PrepareBindingHandle (
    IN void * TransportInterface,
    IN DCE_BINDING * DceBinding
    )
/*++

Routine Description:

    This method will be called just before a new binding handle is returned
    to the user.  We just stash the transport interface and binding
    information so we can use it later when the first remote procedure
    call is made.  At that time, we will actually bind to the interface.

Arguments:

    TransportInterface - Supplies a pointer to a data structure describing
        a loadable transport.

    DceBinding - Supplies the binding information for this binding handle.

--*/
{
    this->TransportInterface = (RPC_CLIENT_TRANSPORT_INFO *) TransportInterface;
    this->DceBinding = DceBinding;
    Association = 0;
}


RPC_STATUS
OSF_BINDING_HANDLE::ToStringBinding (
    OUT RPC_CHAR PAPI * PAPI * StringBinding
    )
/*++

Routine Description:

    We need to convert the binding handle into a string binding.  If the
    binding handle has not yet been used to make a remote procedure
    call, then we can just use the information in the binding handle to
    create the string binding.  Otherwise, we need to ask the association
    to do it for us.

Arguments:

    StringBinding - Returns the string representation of the binding
        handle.

Return Value:

    RPC_S_OK - The operation completed successfully.

    RPC_S_OUT_OF_MEMORY - We do not have enough memory available to
        allocate space for the string binding.

--*/
{
    if ( Association == 0 )
        {
        *StringBinding = DceBinding->StringBindingCompose(
                InqPointerAtObjectUuid());
        if (*StringBinding == 0)
            return(RPC_S_OUT_OF_MEMORY);
        return(RPC_S_OK);
        }
    return(Association->ToStringBinding(StringBinding,
            InqPointerAtObjectUuid()));
}


RPC_STATUS
OSF_BINDING_HANDLE::BindingReset (
    )
/*++

Routine Description:

    This routine will set the endpoint of this binding handle to zero,
    if possible.  The binding handle will become partially bound as a
    result.  If a remote procedure call has been made on this binding
    handle, it will fail as well.

Return Value:

    RPC_S_OK - The binding handle has successfully been made partially
        bound.

    RPC_S_WRONG_KIND_OF_BINDING - The binding handle currently has remote
        procedure calls active.

--*/
{
    BindingMutex.Request();

    if ( Association != 0 )
        {
        if ( ReferenceCount != 1 )
            {
            BindingMutex.Clear();
            return(RPC_S_WRONG_KIND_OF_BINDING);
            }

        DceBinding = Association->DuplicateDceBinding();
        Association->UnBind();
        Association = 0;
        }

    DceBinding->MakePartiallyBound();

    if ( *InquireEpLookupHandle() != 0 )
        {
        EpFreeLookupHandle(*InquireEpLookupHandle());
        *InquireEpLookupHandle() = 0;
        }

    BindingMutex.Clear();
    return(RPC_S_OK);
}


unsigned long
OSF_BINDING_HANDLE::MapAuthenticationLevel (
    IN unsigned long AuthenticationLevel
    )
/*++

Routine Description:

    The connection oriented protocol module supports all authentication
    levels except for RPC_C_AUTHN_LEVEL_CALL.  We just need to map it
    to RPC_C_AUTHN_LEVEL_PKT.

--*/
{
    UNUSED(this);

    if ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_CALL )
        {
        return(RPC_C_AUTHN_LEVEL_PKT);
        }

    return(AuthenticationLevel);
}


RPC_STATUS
OSF_BINDING_HANDLE::ResolveBinding (
    IN PRPC_CLIENT_INTERFACE RpcClientInterface
    )
/*++

Routine Description:

    We need to try and resolve the endpoint for this binding handle
    if necessary (the binding handle is partially-bound).  We check
    to see if an association has been obtained for this binding
    handle; if so, we need to do nothing since the binding handle is
    fully-bound, otherwise, we try and resolve an endpoint for it.

Arguments:

    RpcClientInterface - Supplies interface information to be used
        in resolving the endpoint.

Return Value:

    RPC_S_OK - The binding handle is now fully-bound.

    RPC_S_NO_ENDPOINT_FOUND - We were unable to resolve the endpoint
        for this particular combination of binding handle (network address)
        and interface.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to resolve
        the endpoint.

--*/
{
    if ( Association == 0 )
        {
        return(DceBinding->ResolveEndpointIfNecessary(
                RpcClientInterface, InqPointerAtObjectUuid(),
                InquireEpLookupHandle(), FALSE, InqComTimeout()));

        }
    return(RPC_S_OK);
}


RPC_STATUS
OSF_BINDING_HANDLE::AllocateConnection (
    OUT OSF_CCONNECTION * PAPI * CConnection,
    IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
    )
/*++

Routine Description:

    This method will allocate a connection which has been bound to the
    interface specified by the interface information.  First, we have got
    to see if we have an association for this binding.  If not, we need
    to find or create one.  Before we can find or create an association,
    we need to resolve the endpoint if necessary.  Next we need to see
    if there is already a connection allocated for this interface and thread.
    Otherwise, we need to ask the association to allocate a connection
    for us.

Arguments:

    CConnection - Returns the allocated connection which has been bound
        to the interface specified by the rpc interface information.

    RpcInterfaceInformation - Supplies information describing the
        interface to which we wish to bind.

Return Value:

    RPC_S_OK - The operation completed successfully.

    RPC_S_NO_ENDPOINT_FOUND - The endpoint can not be resolved.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
        a connection.

    EPT_S_NOT_REGISTERED  - There are no more endpoints to be found
        for the specified combination of interface, network address,
        and lookup handle.

    EPT_S_CANT_PERFORM_OP - The operation failed due to misc. error e.g.
        unable to bind to the EpMapper.

    All the return values from OSF_CASSOCIATION::AllocateConnection will
    be passed through unchanged.

--*/
{
    RPC_STATUS Status;
    OSF_ACTIVE_ENTRY * ActiveEntry;
#ifdef NTENV
    CLIENT_AUTH_INFO * AuthInfo;
#endif

    BindingMutex.Request();

    // To start off, see if the binding handle points to an association
    // yet.  If not, we have got to get one.

    if (Association == 0)
        {
        Status = DceBinding->ResolveEndpointIfNecessary(
                RpcInterfaceInformation, InqPointerAtObjectUuid(),
                InquireEpLookupHandle(), FALSE, InqComTimeout());
        if ( Status != RPC_S_OK )
            {
            BindingMutex.Clear();
            return(Status);
            }

        Association = FindOrCreateAssociation(DceBinding,TransportInterface);

        if ( Association == 0 )
            {
            BindingMutex.Clear();
            return(RPC_S_OUT_OF_MEMORY);
            }

        // Ownership of the DCE binding passes to the association.  We are
        // going to set the field to zero so that no one screws with them.

        DceBinding = 0;

        // Ok, when we reach here, we go ahead and fall through to
        // see if we have an active connection (which we will not),
        // and then request the association to allocate one for us.

        }

    // First we need to check if there is already a connection active
    // for this thread and interface.  To make the common case quicker,
    // we will check to see if there are any connections in the dictionary
    // first.

    if ( ActiveConnections.Size() != 0 )
        {
        ActiveConnections.Reset();
        while ( (ActiveEntry = ActiveConnections.Next()) != 0 )
            {
            *CConnection = ActiveEntry->IsThisMyActiveConnection(
                    GetThreadIdentifier(), RpcInterfaceInformation);
            if ( *CConnection != 0 )
                {
                BindingMutex.Clear();
                return(RPC_S_OK);
                }
            }
        }

    // We will assume that we are successfully able to allocate a connection,
    // so we bump the reference count now.

    ReferenceCount += 1;

    BindingMutex.Clear();

#ifdef NTENV
    //
    // If this is a secure BH and it requires DYNAMIC TRACKING, check if
    // LogonID has changed. If it has changed, get new Credential Handle
    //

    AuthInfo  = InquireAuthInformation();
    if ((AuthInfo != 0) && (AuthInfo->AuthenticationLevel != 
                              RPC_C_AUTHN_LEVEL_NONE))
        {
        if (AuthInfo->IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC)
            {
            Status = ReAcquireCredentialsIfNecessary();
            if (Status != RPC_S_OK)
                {
                BindingMutex.Request();
                ReferenceCount -=1;
                BindingMutex.Clear();
                return (Status);
                }
            }
        }
#endif

    // There is not a connection active, so we need to ask the association
    // for one.

    Status = Association->AllocateConnection(RpcInterfaceInformation,
#ifdef NTENV
            CConnection, InqComTimeout(), AuthInfo);
#else
            CConnection, InqComTimeout(), InquireAuthInformation());
#endif

    if ( Status == RPC_S_OK )
        {
        (*CConnection)->SetCurrentBinding(this);
        }
    else
        {
        BindingMutex.Request();
        ReferenceCount -= 1;
        ASSERT( ReferenceCount != 0 );
        BindingMutex.Clear();
        }

    return(Status);
}

RPC_STATUS
OSF_BINDING_HANDLE::AddActiveEntry (
    IN OSF_CCONNECTION * CConnection,
    IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
    )
/*++

Routine Description:

    When a callback occurs, we need to add an entry for the thread and
    interface being using for the callback to the binding handle.  This
    is so that we can later turn original calls into callbacks if they
    are from the same thread (as the original call) and to the same
    interface (as the original call).

Arguments:

    CConnection - Supplies the connection on which the original call was
        sent.

    RpcInterfaceInformation - Supplies the interface used by the original
        call.

Return Value:

    RPC_S_OK - An active entry has been added to the binding handle for
        the supplied connection and interface.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
        operation.

--*/
{
    OSF_ACTIVE_ENTRY * ActiveEntry;

    BindingMutex.Request();
    ActiveEntry = new OSF_ACTIVE_ENTRY(GetThreadIdentifier(),
            RpcInterfaceInformation, CConnection);
    if ( ActiveEntry == 0 )
        {
        BindingMutex.Clear();
        return(RPC_S_OUT_OF_MEMORY);
        }

    CConnection->ActiveConnectionsKey = ActiveConnections.Insert(
            ActiveEntry);
    if ( CConnection->ActiveConnectionsKey == -1 )
        {
        BindingMutex.Clear();
        delete ActiveEntry;
        return(RPC_S_OUT_OF_MEMORY);
        }

    ReferenceCount += 1;
    BindingMutex.Clear();

    return(RPC_S_OK);
}


void
OSF_BINDING_HANDLE::FreeConnection (
    IN OSF_CCONNECTION * CConnection
    )
/*++

Routine Description:

    When the original call completes on an active connection, the
    connection notifies the binding handle using it that the connection
    can be freed.  (This does not mean that the connection is deleted,
    only that it is no longer active).

Arguments:

    CConnection - Supplies an active connection which should be made
        inactive.

--*/
{
    BindingMutex.Request();
    ReferenceCount -= 1;
    if (ReferenceCount == 0)
        {

        // No one points at this binding handle, so we can go ahead
        // and clean it up.

        BindingMutex.Clear();
        if ( Association->FreeConnection(CConnection) != 0 )
            {
            delete CConnection;
            }
        delete this;
        }
    else
        {
        BindingMutex.Clear();
        if ( Association->FreeConnection(CConnection) != 0 )
            delete CConnection;
        }
}


void
OSF_BINDING_HANDLE::AbortConnection (
    IN OSF_CCONNECTION * CConnection
    )
/*++

Routine Description:

    An error occured in the specified connection, so we need to abort
    it.  The caller will delete the connection after this call returns,
    so all we have got to do is to remove the connection from our
    dictionary of active connections.

Arguments:

    CConnection - Supplies the connection to be removed from the
        dictionary of active connections.

--*/
{
    UNUSED(CConnection);

    BindingMutex.Request();
    ReferenceCount -= 1;

    if (ReferenceCount == 0)
        {

        // No one points to this binding, so we go ahead and delete it.

        BindingMutex.Clear();
        delete this;
        }
    else
        BindingMutex.Clear();
}

void
OSF_BINDING_HANDLE::RemoveActiveConnection (
    IN OSF_CCONNECTION * CConnection
    )
/*++

Routine Description:

    The specified connection is removed from the dictionary of active
    connections for this binding handle.

Arguments:

    CConnection - Supplies the connection to be removed from the
        dictionary of active connections.

--*/
{
    OSF_ACTIVE_ENTRY * ActiveEntry;

    BindingMutex.Request();
    ActiveEntry = ActiveConnections.Delete(CConnection->ActiveConnectionsKey);
    if ( ActiveEntry != 0 )
        {
        delete ActiveEntry;
        }
    ReferenceCount -= 1;
    BindingMutex.Clear();
}


RPC_STATUS
OSF_CCONNECTION::Send (
    IN PRPC_MESSAGE Message
    )
/*++

Routine Description:
    This routine is used in conjunction with pipes. If it is the first send, it retries,
    otherwise, it simply sends the data. If the RPC_BUFFER_PARTIAL bit is not set,
    it waits for the first reply.

Return Value:
    RPC_S_OK: All the data was sent across
    RPC_S_SEND_INCOMPLETE: Some of the data still remains to be sent.
    Message->Buffer points to this data and Message->BufferLength is the length
    of the remaining data

--*/
{
    RPC_STATUS RpcStatus;

    // Apart from sending the data, SendRecur and SendNext Chunk check
    // Message->Flags for the RPC_BUFFER_PARTIAL bit. If it is not set,
    // the first fragment of the response is receive as a side effect of the send.
    // this is done to take advantage to the sendreceive feature of some
    // transports.
    if (FirstFrag)
        {
        if (EnableCancels == 0)
            {
            RpcStatus = DoPreSendProcessing() ;
            if (RpcStatus != RPC_S_OK)
                {
                AbortTheConnection(Message, Message->Buffer) ;
                return RpcStatus ;
                }
            }

        RpcStatus = SendRecur(Message, 0);
        }
    else
        {
        RpcStatus = SendNextChunk(Message) ;
        }

    //
    // WARNING! Do not touch any member privates of the this pointer directly
    //  after this point! The *this* pointer could have been deleted [see auto retry logic
    //   in SendRecur!
    //

    return (RpcStatus);
}


RPC_STATUS
OSF_CCONNECTION::SendNextChunk(
    IN OUT PRPC_MESSAGE Message
    )
{
    RPC_STATUS RpcStatus ;

    if ( ConnectionAbortedFlag != 0 )
        {
        return(RPC_S_CALL_FAILED);
        }

    if ( (CurrentBinding->InqIfNullObjectUuid() == 0) )
        {
        RpcStatus = SendRequestOrResponse(Message, rpc_request,
                          CurrentBinding->InqPointerAtObjectUuid(), 1);
        }
    else
        {
        RpcStatus = SendRequestOrResponse(Message, rpc_request, 0, 1);
        }

    // temporary fix
    if (RpcStatus == RPC_S_CALL_FAILED_DNE)
        {
        RpcStatus =  RPC_S_CALL_FAILED ;
        }


    if ((RpcStatus != RPC_S_OK)
        && (RpcStatus != RPC_S_SEND_INCOMPLETE))
        {
        DoConnectionCleanup(Message, NULL, RpcStatus, 0, 0) ;

        DoPostReceiveProcessing(Message, RpcStatus) ;
        }
    return (RpcStatus) ;
}


RPC_STATUS
OSF_CCONNECTION::SendRecur (
    IN OUT PRPC_MESSAGE Message,
    IN unsigned int RecursionCount
    )
/*++

Routine Description:

Arguments:

    Message - Supplies the request to send to the server. This is used only on the
                   first send.

    RecursionCount - Supplies the number of times that this routine has
        been recursively called.  We need this to prevent infinite recursion
        in some rather weird cases.

Return Value:

    RPC_S_OK - We successfully sent a remote procedure call request to the
        server and received back a response.

--*/
{
    RPC_STATUS RpcStatus, ExceptionCode;
    RPC_MESSAGE OriginalMessage;
    unsigned int ActiveEntrySet = 0;
    unsigned int BufferOffset = 0;

    if ( ConnectionAbortedFlag != 0 )
        {
        return(RPC_S_CALL_FAILED_DNE);
        }

    // Arbitrarly allow OSF_CCONNECTION::SendRecur to be recursively
    // called a maximum of eight times.

    if ( RecursionCount > 8 )
        {
        return(RPC_S_CALL_FAILED_DNE);
        }

    CallStack = 1 ;
    AlertMsgsSent = 0;

    OriginalMessage = *Message;
    if (CurrentBinding->InqIfNullObjectUuid() == 0)
        {
        RpcStatus = SendRequestOrResponse(Message, rpc_request,
                CurrentBinding->InqPointerAtObjectUuid(), 1);
        }
    else
        {
        RpcStatus = SendRequestOrResponse(Message, rpc_request, 0, 1);
        }

    if (   ( (RpcStatus == RPC_P_CONNECTION_SHUTDOWN)
            || (RpcStatus == RPC_S_CALL_FAILED_DNE) )
        && ( ClientSecurityContext.AuthenticationLevel !=
                         RPC_C_AUTHN_LEVEL_PKT_PRIVACY ) )
        {
        *Message = OriginalMessage;
        Message->Handle = (RPC_BINDING_HANDLE) CurrentBinding;
        if (RpcStatus == RPC_P_CONNECTION_SHUTDOWN)
           {
           Association->ShutdownRequested();
           RpcStatus = RPC_S_CALL_FAILED_DNE;
           }

        AbortConnection();

        RpcStatus = CurrentBinding->GetBuffer(Message);

        if ( RpcStatus == RPC_S_OK )
            {
            if (CurrentBinding->InqIfNullObjectUuid() == 0)
               BufferOffset = sizeof(UUID);

            RpcpMemoryCopy(
              Message->Buffer,
              (void PAPI *)((char PAPI *)(OriginalMessage.Buffer)+BufferOffset),
              OriginalMessage.BufferLength
              );
            }
        FreeBuffer(&OriginalMessage);
        CurrentBinding->AbortConnection(this) ;
        CallStack = 0 ;

        if ( RpcStatus == RPC_S_OK )
            {
            delete this;

            ((OSF_CCONNECTION *) Message->Handle)->DoPreSendProcessing() ;

            return(((OSF_CCONNECTION *) Message->Handle)->SendRecur(
                    Message, RecursionCount + 1));
            }
        else
            {
            delete this;
            }

        Message->Handle = 0;
        return(RpcStatus);
        }

    CallStack = 0 ;

    if ((RpcStatus != RPC_S_OK)
        && (RpcStatus != RPC_S_SEND_INCOMPLETE))
        {
        DoConnectionCleanup(Message, NULL, RpcStatus, 0, 1) ;

        DoPostReceiveProcessing(Message, RpcStatus) ;
        }

    return(RpcStatus);
}


RPC_STATUS
OSF_CCONNECTION::Receive (
    IN PRPC_MESSAGE Message,
    IN unsigned int Size
    )
{
    RPC_STATUS RpcStatus ;
    unsigned int RemoteFaultOccured = 0;
    unsigned int ActiveEntrySet = 0;
    unsigned long Partial = Message->RpcFlags & RPC_BUFFER_PARTIAL ;
    unsigned int DidNotExecute = 0;

    if (Message->RpcFlags & RPC_BUFFER_COMPLETE)
        {
        return RPC_S_OK ;
        }

    CallStack = 1;

    // First Receive is set in the constructor for OSF_CCONNECTION
    if (FirstReceive)
        {
        RpcStatus = ReceiveMessage(Message, &RemoteFaultOccured,
                                                    Size, &DidNotExecute) ;
        FirstReceive = 0 ;
        }
    else
        {
        Request = 0 ;
        RpcStatus = ReceiveNextChunk(Message, Size) ;
        }

#if 0
// need to understand the ramifications of cancelling in the middle of partial
// receives. This is disabled for the initial checkin..
#ifdef NTENV
    if (RpcStatus == RPC_S_OK)
        {
        if (  ((OSF_CCONNECTION *) Message->Handle)->PendingAlert == TRUE )
            {
            RpcCancelThread(GetCurrentThread());
            ((OSF_CCONNECTION *)Message->Handle)->PendingAlert = FALSE;
            }
        }
#endif
#endif


    if (RpcStatus == RPC_S_OK
        && NOT_MULTIPLE_OF_EIGHT(Message->BufferLength)
        && (Message->RpcFlags & RPC_BUFFER_COMPLETE) == 0)
        {
        SaveRemainingData(Message) ;
        }

    CallStack = 0;

    if ((Message->RpcFlags & RPC_BUFFER_COMPLETE)
        || RpcStatus != RPC_S_OK)
        {
        if ( ActiveEntrySet != 0 )
            {
            CurrentBinding->RemoveActiveConnection(this);
            }

        ASSERT(CallStack == 0) ;

        DoConnectionCleanup(Message, NULL, RpcStatus,
                                        RemoteFaultOccured, DidNotExecute) ;

        DoPostReceiveProcessing(Message, RpcStatus) ;
        }

    return(RpcStatus);
}


/* --------------------------------------------------------------------
-------------------------------------------------------------------- */


RPC_STATUS
OSF_CCONNECTION::SendReceive (
    IN PRPC_MESSAGE Message
    )
{
    RPC_STATUS RpcStatus;

    RpcStatus = DoPreSendProcessing() ;
    if (RpcStatus != RPC_S_OK)
        {
        AbortTheConnection(Message, Message->Buffer) ;
        return RpcStatus ;
        }

    RpcStatus = SendReceiveRecur(Message, 0);

    //
    // WARNING! Do not touch any member privates of the this pointer directly
    //  after this point! The *this* pointer could have been deleted [see auto retry logic
    //   in SendReceiveRecur!
    //
    DoPostReceiveProcessing(Message, RpcStatus) ;

    return (RpcStatus);
}


RPC_STATUS
OSF_CCONNECTION::SendReceiveRecur (
    IN OUT PRPC_MESSAGE Message,
    IN unsigned int RecursionCount
    )
/*++

Routine Description:

Arguments:

    Message - Supplies the request to send to the server and returns the
        response received from the server.

    RecursionCount - Supplies the number of times that this routine has
        been recursively called.  We need this to prevent infinite recursion
        in some rather weird cases.

Return Value:

    RPC_S_OK - We successfully sent a remote procedure call request to the
        server and received back a response.

--*/
{
    RPC_STATUS RpcStatus, ExceptionCode;
    RPC_MESSAGE OriginalMessage;
    unsigned int RemoteFaultOccured = 0;
    unsigned int ActiveEntrySet = 0;
    unsigned int BufferOffset = 0;
    unsigned int DidNotExecute = 0;
    unsigned int InShutdownRecoveryMode = 0;

    if ( ConnectionAbortedFlag != 0 )
        {
        return(RPC_S_CALL_FAILED_DNE);
        }

    // Arbitrarly allow OSF_CCONNECTION::SendReceiveRecur to be recursively
    // called a maximum of eight times.

    if ( RecursionCount > 8 )
        {
        return(RPC_S_CALL_FAILED_DNE);
        }

    if ( CallStack == 0 )
        {
        AlertMsgsSent = 0;
        }

    OriginalMessage = *Message;
    CallStack += 1;
    if (   (CallStack == 1)
        && (CurrentBinding->InqIfNullObjectUuid() == 0) )
        {
        RpcStatus = SendRequestOrResponse(Message, rpc_request,
                CurrentBinding->InqPointerAtObjectUuid());
        }
    else
        {
        RpcStatus = SendRequestOrResponse(Message, rpc_request, 0);
        }

    if (   ( (RpcStatus == RPC_P_CONNECTION_SHUTDOWN)
             ||(RpcStatus == RPC_S_CALL_FAILED_DNE) )
        && ( CallStack == 1 )
        && ( ClientSecurityContext.AuthenticationLevel !=
                         RPC_C_AUTHN_LEVEL_PKT_PRIVACY ) )
        {
        *Message = OriginalMessage;
        Message->Handle = (RPC_BINDING_HANDLE) CurrentBinding;
        if (RpcStatus == RPC_P_CONNECTION_SHUTDOWN)
           {
           /* Association->ShutdownRequested(); */
           //
           // We cannot shutdown all the connections .. 
           // OSF servrs keep Associations that have marshalled out
           // Context Handles active.. Active
           // We retry with alternate connections .. over and over 
           // till we endup blowing off all previously active connections
           // and get a *new* connection .. or endup using an old one
           // that has a context active.
           InShutdownRecoveryMode = 1;
           RpcStatus = RPC_S_CALL_FAILED_DNE;
           }

        AbortConnection();

        while ( 1 )
            {
            RpcStatus = CurrentBinding->GetBuffer(Message);
            if (RpcStatus != RPC_P_CONNECTION_SHUTDOWN)
                {
                break;
                }
            }

        if ( RpcStatus == RPC_S_OK )
            {
            if (CurrentBinding->InqIfNullObjectUuid() == 0)
               BufferOffset = sizeof(UUID);

            RpcpMemoryCopy(
              Message->Buffer,
              (void PAPI *)((char PAPI *)(OriginalMessage.Buffer)+BufferOffset),
              OriginalMessage.BufferLength
              );
            }
        FreeBuffer(&OriginalMessage);
        CurrentBinding->AbortConnection(this);
        CallStack -= 1;

        if ( RpcStatus == RPC_S_OK )
            {
            if ( InShutdownRecoveryMode == 0 )
                {
                RecursionCount++;
                }

            delete this;

            ((OSF_CCONNECTION *) Message->Handle)->DoPreSendProcessing() ;

            return(((OSF_CCONNECTION *) Message->Handle)->SendReceiveRecur(
                    Message, RecursionCount));
            }
        else
            {
            delete this;
            }

        Message->Handle = 0;
        return(RpcStatus);
        }

    for (; RpcStatus == RPC_S_OK ;)
        {
        // Sent message okay, original buffer has been freed.
        OriginalMessage.Buffer = 0;

        RpcStatus = ReceiveMessage(Message, &RemoteFaultOccured,
                                                    0, &DidNotExecute);
        if ( RpcStatus != RPC_P_OK_REQUEST )
            {
            break;
            }
        OriginalMessage.Buffer = Message->Buffer;
        Message->TransferSyntax = 0;

        if (   ( CallStack == 1 )
            && ( ActiveEntrySet == 0 ) )
            {
            RpcStatus = CurrentBinding->AddActiveEntry(this,
                    (RPC_CLIENT_INTERFACE PAPI *)
                    Message->RpcInterfaceInformation);
            if ( RpcStatus != RPC_S_OK )
                {
                break;
                }
            ActiveEntrySet = 1;
            }

        RpcStatus = DispatchCallback(DispatchTableCallback, Message,
                &ExceptionCode);

        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT(   (RpcStatus == RPC_P_EXCEPTION_OCCURED)
                   || (RpcStatus == RPC_S_PROCNUM_OUT_OF_RANGE));

            FreeBuffer(&OriginalMessage);

            if ( RpcStatus == RPC_S_PROCNUM_OUT_OF_RANGE )
                {
                SendFault(RPC_S_PROCNUM_OUT_OF_RANGE, 0);
                }
            else
                {
                SendFault(ExceptionCode, 0);
                }

            RpcStatus = TransReceive(&(Message->Buffer),
                    &(Message->BufferLength));
            ASSERT( (RpcStatus != RPC_S_CALL_CANCELLED)
                  &&(RpcStatus != RPC_P_RECEIVE_ALERTED) );
            if ( RpcStatus != RPC_S_OK )
                {

                if (   ( RpcStatus == RPC_P_RECEIVE_FAILED )
                    || ( RpcStatus == RPC_P_CONNECTION_CLOSED ) )
                    {
                    RpcStatus = RPC_S_CALL_FAILED;
                    }
                else
                    {
                    ASSERT(   ( RpcStatus == RPC_S_OUT_OF_MEMORY )
                           || ( RpcStatus == RPC_S_OUT_OF_RESOURCES ) );
                    }
                OriginalMessage.Buffer = 0;
                break;
                }
            }
        else
            {
            FreeBuffer(&OriginalMessage);

            OriginalMessage = *Message;
            RpcStatus = SendRequestOrResponse(Message, rpc_response, 0);
            if ( (RpcStatus == RPC_S_CALL_FAILED_DNE)
                ||(RpcStatus == RPC_P_CONNECTION_SHUTDOWN) )
                {
                RpcStatus = RPC_S_CALL_FAILED;
                }
            }
        }

    if ( ActiveEntrySet != 0 )
        {
        CurrentBinding->RemoveActiveConnection(this);
        }

    CallStack -= 1;


    DoConnectionCleanup(Message, OriginalMessage.Buffer, RpcStatus,
                                    RemoteFaultOccured, DidNotExecute) ;

    return(RpcStatus);
}



RPC_STATUS
OSF_CCONNECTION::ReceiveMessage (
    IN OUT PRPC_MESSAGE Message,
    OUT unsigned int PAPI * RemoteFaultOccured,
    IN unsigned int Size,
    OUT unsigned int PAPI * DidNotExecute
    )
/*++

Routine Description:

Arguments:

    Message - Supplies the first fragment of the message and returns the
        complete message.

    RemoteFaultOccured - Returns an indication of whether or not a remote
        fault occured.  A non-zero value indicates that a remote fault
        occured.

Return Value:

    RPC_S_OK - We successfully received a response message.

    RPC_P_OK_REQUEST - We successfully received a request message.

    RPC_S_PROTOCOL_ERROR - A protocol error occured; this is likely due
        to a protocol mismatch with the server.

    RPC_S_CALL_FAILED - The request failed, and may or may not have
        executed.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
        operation.

    RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to perform
        the operation.

Contract:

    On Entry Message->Buffer points to the first fragment [or complete response]
    On Successful return Message->Buffer points to the complete response.
    On Failure, Message->Buffer [first frag] is *freed* and Message->Buffer==0

--*/
{
    RPC_STATUS RpcStatus;
    unsigned long FaultStatus;
    rpcconn_common PAPI * pFragment = (rpcconn_common PAPI *) Message->Buffer;
    rpcconn_response PAPI * pResponse = (rpcconn_response PAPI *) Message->Buffer;

    *RemoteFaultOccured = 0;
    *DidNotExecute = 0;

    //If there is security save the rpc header
    if ( ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE )
       {
       if (SavedHeaderSize < sizeof(rpcconn_response))
          {

          if (SavedHeader != 0)
             {
             ASSERT(SavedHeaderSize != 0);
             RpcpFarFree(SavedHeader);
             }

          SavedHeader = RpcpFarAllocate(sizeof(rpcconn_response));
          if (SavedHeader == 0)
             {
             TransFreeBuffer(Message->Buffer);
             Message->Buffer = 0;
             return(RPC_S_OUT_OF_MEMORY);
             }
          SavedHeaderSize = sizeof(rpcconn_response);
          RpcpMemoryCopy(SavedHeader, Message->Buffer,
                                      sizeof(rpcconn_response));

          }

          else
          {
          RpcpMemoryCopy(SavedHeader, Message->Buffer,sizeof(rpcconn_response));
          }
       }

    RpcStatus = ValidatePacket(pFragment, Message->BufferLength);
    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT( RpcStatus == RPC_S_PROTOCOL_ERROR );
        TransFreeBuffer(Message->Buffer);
        Message->Buffer = 0;
        return(RpcStatus);
        }

    switch (pFragment->PTYPE)
        {
        case rpc_request :
            if ( pFragment->call_id != CallId )
                {
                TransFreeBuffer(Message->Buffer);
                Message->Buffer = 0;
                return(RPC_S_PROTOCOL_ERROR);
                }
            if ( ((rpcconn_request PAPI *) pFragment)->p_cont_id
                        != PresentationContext )
                {
                SendFault(RPC_S_UNKNOWN_IF, 0);

                RpcStatus = TransReceive(&(Message->Buffer),
                        &(Message->BufferLength));
                ASSERT( (RpcStatus != RPC_S_CALL_CANCELLED)
                      &&(RpcStatus != RPC_P_RECEIVE_ALERTED) );

                if ( RpcStatus != RPC_S_OK )
                    {
                    if (   ( RpcStatus == RPC_P_RECEIVE_FAILED )
                        || ( RpcStatus == RPC_P_CONNECTION_CLOSED ) )
                        {
                        TransFreeBuffer(Message->Buffer);
                        Message->Buffer = 0;
                        return(RPC_S_CALL_FAILED);
                        }
                    TransFreeBuffer(Message->Buffer);
                    Message->Buffer = 0;
                    return(RpcStatus);
                    }

                return(ReceiveMessage(Message, RemoteFaultOccured,
                                                   Size, DidNotExecute));
                }

            RpcStatus = ReceiveRequestOrResponse(Message, Size);
            if ( RpcStatus == RPC_S_OK )
                {
                return(RPC_P_OK_REQUEST);
                }
            return(RpcStatus);

        case rpc_response :
            if ( pFragment->call_id != CallId )
                {
                return(RPC_S_PROTOCOL_ERROR);
                }

            if (pResponse->alert_count != AlertMsgsSent)
                {
                PendingAlert = TRUE;
                }
            else if (pResponse->common.pfc_flags & PFC_PENDING_ALERT)
                {
                PendingAlert = TRUE;
                }
            else
                {
                PendingAlert = FALSE;
                }

            return(ReceiveRequestOrResponse(Message, Size));

        case rpc_fault :
            FaultStatus = ((rpcconn_fault PAPI *)pFragment)->status;

            if ( (FaultStatus == 0) &&
                 (pFragment->frag_length >= sizeof(rpcconn_fault) + 4) )
                {
                // DCE 1.0.x style fault status:
                // Zero status and stub data contains the fault.

                FaultStatus = *(unsigned long PAPI *)
                    (((rpcconn_fault PAPI *)pFragment)+1);
                }

            if (DataConvertEndian(pFragment->drep) != 0 )
                {
                ByteSwapLong(FaultStatus);
                }

            ASSERT(FaultStatus != 0);

            RpcStatus = MapFromNcaStatusCode(FaultStatus);
            *RemoteFaultOccured = 1;
            if (pFragment->pfc_flags & PFC_DID_NOT_EXECUTE)
                {
                *DidNotExecute = 1 ;
                }
            TransFreeBuffer(Message->Buffer);
            Message->Buffer = 0;


            // In 3.5 we didnt Sign/Seal Faults. So .. Unsign/UnSeal doesnt
            // get called and hence Client side and Server side Seq# are
            // out of Sync..  So cheat ..

            DceSecurityInfo.ReceiveSequenceNumber += 1;

            return(RpcStatus);

        case rpc_orphaned :
        case rpc_remote_alert :
        case rpc_shutdown :
            // For the first release, we will just ignore these messages.

            TransFreeBuffer(Message->Buffer);
            Message->Buffer = 0;

            RpcStatus = TransReceive(&Message->Buffer, &Message->BufferLength);
            ASSERT( (RpcStatus != RPC_S_CALL_CANCELLED)
                  &&(RpcStatus != RPC_P_RECEIVE_ALERTED) );
            if ( RpcStatus != RPC_S_OK )
                {
                if (   ( RpcStatus == RPC_P_RECEIVE_FAILED )
                    || ( RpcStatus == RPC_P_CONNECTION_CLOSED ))
                    {
                    return(RPC_S_CALL_FAILED);
                    }
                else
                if ( RpcStatus == RPC_P_CONNECTION_SHUTDOWN )
                    {
                    return (RPC_S_CALL_FAILED_DNE);
                    }
                return(RpcStatus);
                }
            return(ReceiveMessage(Message, RemoteFaultOccured, Size, DidNotExecute));

        default :
            return(RPC_S_PROTOCOL_ERROR);

        }

    // This will never be reached.

    return(RPC_S_INTERNAL_ERROR);
}


RPC_STATUS
OSF_CCONNECTION::SendRequestOrResponse (
    IN OUT PRPC_MESSAGE Message,
    IN unsigned char PacketType,
    IN RPC_UUID * ObjectUuid OPTIONAL,
    IN int PipeSend OPTIONAL
    )
/*++

Routine Description:

Arguments:

    Message - Supplies the buffer containing the request or response to be
        sent. Returns the first fragment received from the server, if
        the partial bit is not set in the message.

    PacketType - Supplies the packet type; this must be rpc_request or
        rpc_response.

    ObjectUuid - Optionally supplies the object UUID to use for this request.

    Note: If this function returns an error the input (request/response)
          Message->Buffer is not freed.  If it completes successfully, the
          Message->buffer is freed.

Return Value:

    RPC_S_OK - We successfully sent the request and received a fragment from
        the server.

    RPC_S_CALL_FAILED_DNE - The connection failed part way through sending
        the request or response.

    RPC_S_CALL_FAILED - The connection failed after sending the request or
        response, and the receive failed.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
        operation.

    RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to perform
        the operation.

--*/
{
    rpcconn_common PAPI * pFragment;
    unsigned int LastFragmentFlag = 0;
    unsigned int HeaderSize;
    RPC_MESSAGE SendBuffer;
    unsigned int LengthLeft = Message->BufferLength;
    RPC_STATUS RpcStatus;
    void PAPI * ReceiveBuffer = 0;
    unsigned int ReceiveBufferLength;
    unsigned char PAPI * Source, PAPI * Destination;
    unsigned int Count;
    unsigned int MaxSecuritySize = 0;
    unsigned char PAPI * ReservedForSecurity;
    unsigned int MaximumFragmentLength = MaxFrag;
    int Partial = (Message->RpcFlags & RPC_BUFFER_PARTIAL) ;

    ASSERT( sizeof(rpcconn_response) == sizeof(rpcconn_request) );
    ASSERT(   (PacketType == rpc_request)
           || (PacketType == rpc_response));

    SendBuffer.Buffer = Message->Buffer;

    if ( ObjectUuid != 0 )
        {
        HeaderSize = sizeof(rpcconn_request) + sizeof(UUID);

        // We saved space for an object uuid when we allocated the buffer,
        // so we can shift the stub data to make room for an object uuid
        // at the front.

        // BUGBUG - There has got to be a more efficient way to do this.

        Destination = ((unsigned char PAPI *) Message->Buffer) + sizeof(UUID)
                + Message->BufferLength;
        Source = ((unsigned char PAPI *) Message->Buffer)
                + Message->BufferLength;
        Count = Message->BufferLength;
        while ( Count-- != 0 )
            {
            *--Destination = *--Source;
            }

        Message->Buffer = ((unsigned char PAPI *) Message->Buffer)
                + sizeof(UUID);
        }
    else
        {
        HeaderSize = sizeof(rpcconn_request);
        }


    // We need to figure out about security: do we need to put authentication
    // information into each packet, and if so, how much space should we
    // reserve.  When we allocated the buffer (see OSF_CCONNECTION::GetBuffer)
    // we saved space for security information.  We did this for two reasons:
    // so that for the last fragment, we could just stick the authentication
    // information into there without having to copy anything.  And so that
    // we have space to save the contents of the buffer which will be
    // overwritten with authentication information (for all but the last
    // fragment).

    if (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
        {
        ASSERT(ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY ||
               ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY   ||
               ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT       ||
               ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT);

        MaxSecuritySize = AdditionalSpaceForSecurity
                - MAXIMUM_SECURITY_BLOCK_SIZE;

        if (MaxSecuritySize <= sizeof(sec_trailer) &&
            ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT)
           {
           return(RPC_S_CALL_FAILED_DNE);
           }

        if (MaxSecuritySize <= sizeof(sec_trailer))
           {
           MaxSecuritySize = 0;
           ASSERT(ClientSecurityContext.AuthenticationLevel ==
                                  RPC_C_AUTHN_LEVEL_CONNECT);
           }
        else
           {
           ReservedForSecurity = ((unsigned char PAPI *) Message->Buffer)
                + Message->BufferLength + AdditionalSpaceForSecurity;

           //We need to arrange things so that the length of the stub data
           //is a multiple of MAXIMUM_SECURITY_BLOCK_SIZE: this is a requirement
           //of the security package.

           MaximumFragmentLength -= ((MaximumFragmentLength - HeaderSize
                - MaxSecuritySize) % MAXIMUM_SECURITY_BLOCK_SIZE);
           }
       }

   if (Partial &&
       (LengthLeft + HeaderSize + MaxSecuritySize < MaximumFragmentLength))
       {
       ASSERT(PacketType == rpc_request) ;

       return (RPC_S_SEND_INCOMPLETE) ;
       }

    pFragment = (rpcconn_common PAPI *)
            ((char PAPI *) Message->Buffer - HeaderSize);

    for (;;)
        {
        // Check to see if the remaining data will fit into a single
        // fragment; if so, set the last fragment flag.

        if ( LengthLeft + HeaderSize + MaxSecuritySize
                    <= MaximumFragmentLength )
            {
            if (Partial)
                {
                ASSERT(PacketType == rpc_request) ;

                Message->BufferLength = LengthLeft ;
                RpcpMemoryCopy(Message->Buffer,
                                          (unsigned char PAPI *) pFragment+HeaderSize,
                                          LengthLeft) ;

                return (RPC_S_SEND_INCOMPLETE) ;
                }
            else
                {
                LastFragmentFlag = 1;
                }
            }

        ConstructPacket(pFragment, PacketType,
                (LastFragmentFlag != 0 ? LengthLeft + HeaderSize
                        + MaxSecuritySize : MaximumFragmentLength));

        if ( ObjectUuid != 0 )
            {
            pFragment->pfc_flags |= PFC_OBJECT_UUID;
            RpcpMemoryCopy(((unsigned char PAPI *) pFragment)
                            + sizeof(rpcconn_request),
                    ObjectUuid, sizeof(UUID));
            }

        if ( LengthLeft == Message->BufferLength)
            {
            if (PipeSend == 0 || FirstFrag)
                {
                pFragment->pfc_flags |= PFC_FIRST_FRAG;
                FirstFrag = 0;
    
                if (PendingAlert)
                    {
                    pFragment->pfc_flags |= PFC_PENDING_ALERT;
                    PendingAlert = FALSE;
                    }
                }
            }

        if ( PacketType == rpc_request )
            {
            ((rpcconn_request PAPI *) pFragment)->alloc_hint = LengthLeft;
            ((rpcconn_request PAPI *) pFragment)->p_cont_id =
                    PresentationContext;
            ((rpcconn_request PAPI *) pFragment)->opnum =
                    (unsigned short) Message->ProcNum;
            }
        else
            {
            ((rpcconn_response PAPI *) pFragment)->alloc_hint = LengthLeft;
            ((rpcconn_response PAPI *) pFragment)->p_cont_id =
                    (unsigned short) PresentationContext;
            ((rpcconn_response PAPI *) pFragment)->alert_count = 0;
            ((rpcconn_response PAPI *) pFragment)->reserved = 0;
            }

        pFragment->call_id = CallId;

        RpcStatus = SendFragment(pFragment, LastFragmentFlag, HeaderSize,
                      MaxSecuritySize, LengthLeft, MaximumFragmentLength,
                      ReservedForSecurity, &ReceiveBuffer, &ReceiveBufferLength) ;

        if (RpcStatus != RPC_S_OK)
            {
            return (RpcStatus) ;
            }

        if (LastFragmentFlag)
            {
            Message->Buffer = ReceiveBuffer;
            Message->BufferLength = ReceiveBufferLength;

            FreeBuffer(&SendBuffer) ;
            return (RPC_S_OK) ;
            }

        pFragment = (rpcconn_common PAPI *)
                (((unsigned char PAPI *) pFragment) + MaximumFragmentLength
                        - (HeaderSize + MaxSecuritySize));

        LengthLeft -= (MaximumFragmentLength - (HeaderSize + MaxSecuritySize));
        }
}

inline RPC_STATUS
OSF_CCONNECTION::SendFragment(
    IN OUT rpcconn_common PAPI *pFragment,
    IN unsigned int LastFragmentFlag,
    IN unsigned int HeaderSize,
    IN unsigned int MaxSecuritySize,
    IN unsigned int DataLength,
    IN unsigned int MaximumFragmentLength,
    IN unsigned char PAPI *ReservedForSecurity,
    OUT void PAPI * PAPI *ReceiveBuffer,
    OUT unsigned int PAPI *ReceiveBufferLength
    )
/*++
    Routine Description:

    Sends on fragment
--*/

{
    sec_trailer PAPI * SecurityTrailer;
    unsigned int SecurityLength;
    unsigned int AuthPadLength;
    SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
    SECURITY_BUFFER SecurityBuffers[5];
    DCE_MSG_SECURITY_INFO MsgSecurityInfo;
    RPC_STATUS RpcStatus;


    if ( ((ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
        &&(ClientSecurityContext.AuthenticationLevel
                              != RPC_C_AUTHN_LEVEL_CONNECT))
        ||((ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT)
        && (MaxSecuritySize != 0)) )
        {

        ASSERT
        (
           (ClientSecurityContext.AuthenticationLevel
             == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
        || (ClientSecurityContext.AuthenticationLevel
             == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
        || (ClientSecurityContext.AuthenticationLevel
             == RPC_C_AUTHN_LEVEL_PKT)
        || (ClientSecurityContext.AuthenticationLevel
             == RPC_C_AUTHN_LEVEL_CONNECT)
        );

    if ( LastFragmentFlag == 0 )
        {
        SecurityTrailer = (sec_trailer PAPI *)
                (((unsigned char PAPI *) pFragment)
                + MaximumFragmentLength - MaxSecuritySize);

        // It is not the last fragment, so we need to save away the
        // part of the buffer which could get overwritten with
        // authentication information.  We can not use memcpy,
        // because the source and destination regions may overlap.

        RpcpMemoryMove(ReservedForSecurity, SecurityTrailer,
                MaxSecuritySize);
        AuthPadLength = 0;
        }
    else
        {
        ASSERT( MAXIMUM_SECURITY_BLOCK_SIZE == 16 );
        AuthPadLength = Pad16(HeaderSize+DataLength+sizeof(sec_trailer)) ;
        DataLength += AuthPadLength;
        ASSERT( ((HeaderSize+DataLength+sizeof(sec_trailer))
                        % MAXIMUM_SECURITY_BLOCK_SIZE) == 0 );
        SecurityTrailer = (sec_trailer PAPI *)
                (((unsigned char PAPI *) pFragment) + DataLength
                + HeaderSize);

        pFragment->pfc_flags |= PFC_LAST_FRAG;
        }

    SecurityTrailer->auth_type = (unsigned char)
            ClientSecurityContext.AuthenticationService;
    SecurityTrailer->auth_level = (unsigned char)
            ClientSecurityContext.AuthenticationLevel;
    SecurityTrailer->auth_pad_length = AuthPadLength;
    SecurityTrailer->auth_reserved = 0;
    SecurityTrailer->auth_context_id = (unsigned long)(this);

    BufferDescriptor.ulVersion = 0;
    BufferDescriptor.cBuffers = 5;
    BufferDescriptor.pBuffers = SecurityBuffers;

    SecurityBuffers[0].cbBuffer = HeaderSize;
    SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
    SecurityBuffers[0].pvBuffer = ((unsigned char PAPI *) pFragment);

    SecurityBuffers[1].cbBuffer = (LastFragmentFlag != 0 ?
             (DataLength)
            : (MaximumFragmentLength - HeaderSize - MaxSecuritySize)
            );
    SecurityBuffers[1].BufferType = SECBUFFER_DATA;
    SecurityBuffers[1].pvBuffer = ((unsigned char PAPI *) pFragment)
            + HeaderSize;

    SecurityBuffers[2].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
    SecurityBuffers[2].cbBuffer = sizeof(sec_trailer);
    SecurityBuffers[2].pvBuffer = SecurityTrailer;

    SecurityBuffers[3].cbBuffer = MaxSecuritySize - sizeof(sec_trailer);
    SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
    SecurityBuffers[3].pvBuffer = SecurityTrailer + 1;

    SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO);
    SecurityBuffers[4].BufferType = (SECBUFFER_PKG_PARAMS
            | SECBUFFER_READONLY);
    SecurityBuffers[4].pvBuffer = &MsgSecurityInfo;

    MsgSecurityInfo.SendSequenceNumber =
            DceSecurityInfo.SendSequenceNumber;
    MsgSecurityInfo.ReceiveSequenceNumber =
            DceSecurityInfo.ReceiveSequenceNumber;
    MsgSecurityInfo.PacketType = pFragment->PTYPE;

    //DCE computes check sums for Header also
    //Make sure Header remains intact
    //Infact may need to extend security interface if
    //some packages return dynamic size seals/signatures

    pFragment->auth_length = SecurityLength = (unsigned short)
                                         SecurityBuffers[3].cbBuffer;

    SecurityLength += sizeof(sec_trailer);
    if ( LastFragmentFlag != 0)
       {
       pFragment->frag_length = HeaderSize + DataLength
                + SecurityLength;
       }
    else
       {
       pFragment->frag_length += SecurityLength - MaxSecuritySize;
       }

    RpcStatus = ClientSecurityContext.SignOrSeal(
            MsgSecurityInfo.SendSequenceNumber,
            ClientSecurityContext.AuthenticationLevel
            != RPC_C_AUTHN_LEVEL_PKT_PRIVACY, &BufferDescriptor);

    ASSERT(SecurityBuffers[3].cbBuffer <= pFragment->auth_length);

    if (RpcStatus != RPC_S_OK)
        {
        if ( LastFragmentFlag == 0 )
             {
              RpcpMemoryCopy(SecurityTrailer, ReservedForSecurity,
                  MaxSecuritySize);
             }
         if ( (RpcStatus == SEC_E_CONTEXT_EXPIRED)
             || (RpcStatus == SEC_E_QOP_NOT_SUPPORTED) )
           {
           return (RPC_S_SEC_PKG_ERROR);
           }
         return (RPC_S_ACCESS_DENIED);
         }
    }
    else
        {
        SecurityLength = 0;
        }

    if ( LastFragmentFlag != 0 )
        {

        ASSERT(!RpcpCheckHeap());
        AlertsEnabled = 1;

        pFragment->pfc_flags |= PFC_LAST_FRAG;

        RpcStatus = TransSendReceive(pFragment, DataLength + HeaderSize
                + SecurityLength, ReceiveBuffer, ReceiveBufferLength);

        AlertsEnabled = 0;

        if ( RpcStatus != RPC_S_OK )
            {
            if (   (RpcStatus == RPC_P_CONNECTION_CLOSED)
            || (RpcStatus == RPC_P_SEND_FAILED))
                {
                return(RPC_S_CALL_FAILED_DNE);
                }
#ifdef NTENV
            if ( RpcStatus == RPC_S_CALL_CANCELLED )
                {
                return (RPC_S_CALL_CANCELLED);
                }
#endif
            if ( RpcStatus == RPC_P_RECEIVE_FAILED)
                {
                return(RPC_S_CALL_FAILED);
                }

            ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
               || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
               || (RpcStatus == RPC_P_CONNECTION_SHUTDOWN));
            return(RpcStatus);
            }

        return(RPC_S_OK);
        }


    RpcStatus = TransSend(pFragment, MaximumFragmentLength
        - MaxSecuritySize + SecurityLength);

    // We need to restore the part of the buffer which we overwrote
    // with authentication information.

    if (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
        {
        ASSERT(
        (ClientSecurityContext.AuthenticationLevel
            == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
        || (ClientSecurityContext.AuthenticationLevel
            == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
        || (ClientSecurityContext.AuthenticationLevel
            == RPC_C_AUTHN_LEVEL_PKT)
        || (ClientSecurityContext.AuthenticationLevel
            == RPC_C_AUTHN_LEVEL_CONNECT)
        );

        RpcpMemoryCopy(SecurityTrailer, ReservedForSecurity,
            MaxSecuritySize);
        }

#if 0
     // BUGBUG - Due to a bug in NT named pipes, if you do not delay,
    // the receive operation will fail in weird and wonderful ways.
    // We only need to do it if we are sending authentication information.

    if ( MaxSecuritySize != 0 )
        {
        PauseExecution(50L);
        }
#endif

    if ( RpcStatus != RPC_S_OK )
        {
        if (   (RpcStatus == RPC_P_CONNECTION_CLOSED)
            || (RpcStatus == RPC_P_SEND_FAILED))
            {
            return(RPC_S_CALL_FAILED_DNE);
            }

        ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
           || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
           || (RpcStatus == RPC_P_CONNECTION_SHUTDOWN));
        return(RpcStatus);
        }

    return RpcStatus ;
}


RPC_STATUS
OSF_CCONNECTION::EatAuthInfoFromPacket (
    IN rpcconn_request PAPI * Request,
    IN OUT unsigned int PAPI * RequestLength
    )
/*++

Routine Description:

    If there is authentication information in the packet, this routine
    will check it, and perform security as necessary.  This may include
    calls to the security support package.

Arguments:

    Request - Supplies the packet which may contain authentication
        information.

    RequestLength - Supplies the length of the packet in bytes, and
        returns the length of the packet without authentication
        information.

Return Value:

    RPC_S_OK - Everything went just fine.

    RPC_S_ACCESS_DENIED - A security failure of some sort occured.

    RPC_S_PROTOCOL_ERROR - This will occur if no authentication information
        is in the packet, and some was expected, or visa versa.

--*/
{
    sec_trailer PAPI * SecurityTrailer;
    RPC_STATUS RpcStatus;
    SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
    SECURITY_BUFFER SecurityBuffers[5];
    DCE_MSG_SECURITY_INFO MsgSecurityInfo;

    if ( Request->common.auth_length != 0 )
        {
        SecurityTrailer = (sec_trailer PAPI *) (((unsigned char PAPI *)
                Request) + Request->common.frag_length
                - Request->common.auth_length - sizeof(sec_trailer));
        if (   (ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE) )

/*
 Cannot test this here
            || (ClientSecurityContext.AuthenticationLevel
                        != SecurityTrailer->auth_level)
            || (ClientSecurityContext.AuthenticationService
                        != SecurityTrailer->auth_type) )
*/
            {
            return(RPC_S_PROTOCOL_ERROR);
            }
        *RequestLength -= Request->common.auth_length;

        MsgSecurityInfo.SendSequenceNumber =
                DceSecurityInfo.SendSequenceNumber;
        MsgSecurityInfo.ReceiveSequenceNumber =
                DceSecurityInfo.ReceiveSequenceNumber;
        MsgSecurityInfo.PacketType = Request->common.PTYPE;

        BufferDescriptor.ulVersion = 0;
        BufferDescriptor.cBuffers = 5;
        BufferDescriptor.pBuffers = SecurityBuffers;

        SecurityBuffers[0].cbBuffer = sizeof(rpcconn_request);
        SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
        SecurityBuffers[0].pvBuffer = ((unsigned char PAPI *) SavedHeader);

        SecurityBuffers[1].cbBuffer = *RequestLength - sizeof(rpcconn_request)
                                      -sizeof (sec_trailer);
        SecurityBuffers[1].BufferType = SECBUFFER_DATA;
        SecurityBuffers[1].pvBuffer = ((unsigned char PAPI *) Request)
                + sizeof(rpcconn_request);

        SecurityBuffers[2].cbBuffer = sizeof(sec_trailer);
        SecurityBuffers[2].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
        SecurityBuffers[2].pvBuffer = SecurityTrailer;

        SecurityBuffers[3].cbBuffer = Request->common.auth_length;
        SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
        SecurityBuffers[3].pvBuffer = SecurityTrailer + 1;

        SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO);
        SecurityBuffers[4].BufferType = (SECBUFFER_PKG_PARAMS
                | SECBUFFER_READONLY);
        SecurityBuffers[4].pvBuffer = &MsgSecurityInfo;

        RpcStatus = ClientSecurityContext.VerifyOrUnseal(
                MsgSecurityInfo.ReceiveSequenceNumber,
                ClientSecurityContext.AuthenticationLevel
                != RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
                &BufferDescriptor);

        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT( RpcStatus == RPC_S_ACCESS_DENIED );

            return(RpcStatus);
            }
        *RequestLength -= (sizeof(sec_trailer) +
                             SecurityTrailer->auth_pad_length);
        }
    else
        {
        /*
        if (ClientSecurityContext.AuthenticationLevel
                        != RPC_C_AUTHN_LEVEL_NONE)
            {
            return(RPC_S_PROTOCOL_ERROR);
            }
        */
        if (  (ClientSecurityContext.AuthenticationLevel
                        == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
            ||(ClientSecurityContext.AuthenticationLevel
                        == RPC_C_AUTHN_LEVEL_PKT_PRIVACY) )
            {
            return(RPC_S_PROTOCOL_ERROR);
            }
        }
    DceSecurityInfo.ReceiveSequenceNumber += 1;
    return(RPC_S_OK);
}


RPC_STATUS
OSF_CCONNECTION::ReceiveRequestOrResponse (
    IN OUT PRPC_MESSAGE Message,
    IN unsigned int Size
    )
/*++

Routine Description:

Arguments:

    Message - Supplies the first fragment of a request or a response, and
        returns the completed message.

Return Value:

    RPC_S_OK - We successfully received the entire message.

    RPC_S_PROTOCOL_ERROR - A protocol error occured; this is typically
        because of a protocol version mismatch between the client and
        the server.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to receive
        all of the message.

    RPC_S_CALL_FAILED - The receive operation failed part way through.

    RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
        receive all of the message.

Notes:

    This method is very similar to OSF_SCONNECTION::ReceiveRequestOrResponse
    in osfsvr.cxx.

--*/
{
    RPC_STATUS RpcStatus;
    void PAPI * NewBuffer;
    int SecurityFailureOccured = 0;

    Request = (rpcconn_request PAPI *) Message->Buffer;
    PacketType = Request->common.PTYPE;

    FragmentLength = 0 ;
    Message->Buffer = 0;

    // Upon entry, the packet will have already been validated (and data
    // converted) by whomever called us.

    if ( (Request->common.pfc_flags & PFC_FIRST_FRAG) == 0 )
        {
        TransFreeBuffer(Request);
        return(RPC_S_PROTOCOL_ERROR);
        }

    Message->ProcNum = Request->opnum;
    Message->DataRepresentation =
        (Request->common.drep[0] | (Request->common.drep[1] << 8));

    if ( (Request->common.pfc_flags & PFC_OBJECT_UUID) != 0 )
        {
        // There can not be an object uuid in the message.  This is an
        // error.

        TransFreeBuffer(Request);
        return(RPC_S_PROTOCOL_ERROR);
        }

    RpcStatus = EatAuthInfoFromPacket(Request, &(Message->BufferLength));
    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT(   (RpcStatus == RPC_S_PROTOCOL_ERROR)
               || (RpcStatus == RPC_S_ACCESS_DENIED) );

        /*
        TransFreeBuffer(Request);
        return(RpcStatus);
        */
        SecurityFailureOccured = 1;
        }

    if ( (Request->common.pfc_flags & PFC_LAST_FRAG) != 0 )
        {
        if (SecurityFailureOccured != 0)
           {
           TransFreeBuffer(Request);
           return(RPC_S_ACCESS_DENIED);
           }
        Message->Buffer = (void PAPI *) (Request + 1);
        Message->BufferLength -= sizeof(rpcconn_request);
        Message->RpcFlags |= RPC_BUFFER_COMPLETE ;

        return(RPC_S_OK);
        }

    FragmentLength = Message->BufferLength;

    // If the server specified an allocation hint, we will go ahead and
    // use it.  On Dos and Win 3.1, only use allocation hints which are
    // less than 63.5K.

    Message->BufferLength = (unsigned int) Request->alloc_hint;

#ifdef DOS

    if ( Request->alloc_hint > 0xFE00L )
        {
        Message->BufferLength = 0;
        }

#endif // DOS

    if ( Message->BufferLength != 0 )
        {
        RpcStatus = GetBufferDo(Message->BufferLength, &Message->Buffer);
        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );

            // Oops, we cant allocate a buffer as large as the allocation
            // hint, so we will just not bother allocating one.

            Message->BufferLength = 0;
            }
        }

    return ReceiveNextChunk(Message, Size, SecurityFailureOccured) ;
}


RPC_STATUS
OSF_CCONNECTION::ReceiveNextChunk(
    IN OUT PRPC_MESSAGE Message,
    IN unsigned int Size,
    IN int SecurityFailureOccured
    )
{
    unsigned int BufferLengthUsed ;
    RPC_STATUS RpcStatus;
    void PAPI * NewBuffer;
    unsigned long Partial = Message->RpcFlags & RPC_BUFFER_PARTIAL ;
    unsigned long Extra = Message->RpcFlags & RPC_BUFFER_EXTRA ;

    if (Extra)
        {
        BufferLengthUsed = Message->BufferLength ;
        }
    else
        {
        BufferLengthUsed = 0 ;
        }

    if (RemainingData[0])
        {
        if (Message->BufferLength < BufferLengthUsed + RemainingData[0])
            {
            // we allocate enough to hold a fragment
            RpcStatus = GetBufferDo(BufferLengthUsed+FragmentLength, &NewBuffer) ;
            if (RpcStatus != RPC_S_OK)
                {
                if (Request)
                    {
                    TransFreeBuffer(Request) ;
                    }

                TransFreeBuffer(Message->Buffer) ;
                Message->Buffer = 0;

                return (RPC_S_OUT_OF_MEMORY) ;
                }

            if (BufferLengthUsed)
                {
                RpcpMemoryCopy(NewBuffer, Message->Buffer, BufferLengthUsed) ;
                }

            FreeBuffer(Message) ;
            Message->Buffer = NewBuffer ;
            }

        RpcpMemoryCopy((char PAPI *) Message->Buffer+BufferLengthUsed,
                                &RemainingData[1], RemainingData[0]) ;
        BufferLengthUsed += RemainingData[0] ;
        RemainingData[0] = 0;
        }

    for (;;)
        {
        // check if the remaining data buffer can fit a fragment
        if (Message->BufferLength - BufferLengthUsed
                < FragmentLength - sizeof(rpcconn_request))
            {

            // For Dos and Win 3.1, make sure that the buffer length does
            // not overflow the size of an integer.
#ifdef DOS

            if ( Message->BufferLength > 0xFE00 - FragmentLength )
                {
                TransFreeBuffer(Request);
                FreeBuffer(Message);
                Message->Buffer = 0;
                return(RPC_S_OUT_OF_MEMORY);
                }

#endif // DOS

            // The message buffer is too small, so we need to grow it
            // to hold the new fragment as well as those that we have
            // already received.

            RpcStatus = GetBufferDo(FragmentLength + Message->BufferLength,
                    &NewBuffer);
            if ( RpcStatus != RPC_S_OK )
                {
                ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );
                if (Request)
                    {
                    TransFreeBuffer(Request);
                    }

                if (Message->Buffer)
                    {
                    FreeBuffer(Message);
                    Message->Buffer = 0;
                    }
                return(RpcStatus);
                }

            if ( BufferLengthUsed != 0 )
                {
                RpcpMemoryCopy(NewBuffer, Message->Buffer, BufferLengthUsed);
                FreeBuffer(Message);
                }

            Message->Buffer = NewBuffer;
            Message->BufferLength += FragmentLength;
            }

        // We have enough space in the buffer for the next fragment; all we
        // have got to do is to copy it in.

        if (Request)
            {
            RpcpMemoryCopy((unsigned char PAPI *)
                            Message->Buffer + BufferLengthUsed,
                    ((unsigned char PAPI *) Request) + sizeof(rpcconn_request),
                    FragmentLength - sizeof(rpcconn_request));

            BufferLengthUsed += FragmentLength - sizeof(rpcconn_request);

            // If this is the last fragment, then we are all done receiving
            // the request or response.  We just need to free the last fragment
            // and then return.

            if ( (Request->common.pfc_flags & PFC_LAST_FRAG) != 0 )
                {
                TransFreeBuffer(Request);
                if (SecurityFailureOccured != 0)
                   {
                   FreeBuffer(Message);
                   Message->Buffer = 0;
                   return(RPC_S_ACCESS_DENIED);
                   }
                Message->BufferLength = BufferLengthUsed;
                Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
                return(RPC_S_OK);
                }

            // Otherwise, we need to try and receive another fragment.

            TransFreeBuffer(Request);

            Request = 0;

            if (Partial && BufferLengthUsed >= Size)
                {
                Message->BufferLength = BufferLengthUsed ;
                return (RPC_S_OK) ;
                }
            }

        RpcStatus = TransReceive((void PAPI * PAPI *) &Request,
                &FragmentLength);
        ASSERT( (RpcStatus != RPC_S_CALL_CANCELLED)
              &&(RpcStatus != RPC_P_RECEIVE_ALERTED) );
        if ( RpcStatus != RPC_S_OK )
            {
            FreeBuffer(Message);
            Message->Buffer = 0;
            if (   (RpcStatus == RPC_P_RECEIVE_FAILED)
                || (RpcStatus == RPC_P_CONNECTION_CLOSED))
                {
                return(RPC_S_CALL_FAILED);
                }
            ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
                   || (RpcStatus == RPC_S_OUT_OF_RESOURCES));
            return(RpcStatus);
            }


        if (   ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE )
           {

           //
           //Save the new request_header..
           //note we already should have enough memory saved
           //
           ASSERT ((SavedHeaderSize >= sizeof(rpcconn_response))
                  && (FragmentLength >= sizeof(rpcconn_response)));
           RpcpMemoryCopy(SavedHeader, Request, sizeof(rpcconn_response));

           }

        RpcStatus = ValidatePacket((rpcconn_common PAPI *) Request,
                FragmentLength);
        ASSERT(   (RpcStatus == RPC_S_OK)
               || (RpcStatus == RPC_S_PROTOCOL_ERROR));
        if (   (RpcStatus != RPC_S_OK)
            || (Request->common.PTYPE != PacketType)
            || (Request->common.call_id != CallId))
            {
            TransFreeBuffer(Request);
            FreeBuffer(Message);
            Message->Buffer = 0;
            return(RPC_S_PROTOCOL_ERROR);
            }

        RpcStatus = EatAuthInfoFromPacket(Request, &FragmentLength);
        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT(   (RpcStatus == RPC_S_PROTOCOL_ERROR)
                   || (RpcStatus == RPC_S_ACCESS_DENIED) );

            SecurityFailureOccured = 1;
            }
        }

    if (Partial)
        {
        ASSERT(Size <= Message->BufferLength) ;
        return (RPC_S_OK) ;
        }
    else
        {
        return (RPC_S_PROTOCOL_ERROR);
        }
}


void
OSF_CCONNECTION::SendFault (
    IN RPC_STATUS Status,
    IN int DidNotExecute
    )
{
    rpcconn_fault Fault;

// BUGBUG this should be a #define in sysinc.h
#if WIN
    _fmemset(&Fault, 0, sizeof(Fault));
#else
    memset(&Fault, 0, sizeof(Fault));
#endif

    ConstructPacket((rpcconn_common PAPI *) &Fault,rpc_fault,
                    sizeof(rpcconn_fault));

    if (DidNotExecute)
        Fault.common.pfc_flags |= PFC_DID_NOT_EXECUTE;

    Fault.common.pfc_flags |= PFC_FIRST_FRAG | PFC_LAST_FRAG;
    Fault.p_cont_id = (unsigned short) PresentationContext;
    Fault.status = MapToNcaStatusCode(Status);
    TransSend(&Fault,sizeof(rpcconn_fault));
}

void
OSF_CCONNECTION::SendOrphan (
    )
{
    rpcconn_common Orphan;

    ConstructPacket((rpcconn_common PAPI *) &Orphan, rpc_orphaned,
                    sizeof(rpcconn_common));

    Orphan.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;

    TransSend(&Orphan, sizeof(rpcconn_common));
}

void
OSF_CCONNECTION::SendAlert(
    )
{
    rpcconn_common Alert;

    ConstructPacket((rpcconn_common PAPI *) &Alert, rpc_remote_alert,
                    sizeof(rpcconn_common));

    Alert.pfc_flags = PFC_LAST_FRAG | PFC_PENDING_ALERT;

    TransSend(&Alert, sizeof(rpcconn_common));
    AlertMsgsSent++;
}

RPC_STATUS
OSF_CCONNECTION::GetBuffer (
    IN OUT PRPC_MESSAGE Message
    )
/*++

Routine Description:

Arguments:

    Message - Supplies a description containing the length of buffer to be
        allocated, and returns the allocated buffer.

Return Value:

    RPC_S_OK - A buffer of the requested size has successfully been allocated.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available.

--*/
{
    RPC_STATUS RpcStatus;

    Message->Handle = (RPC_BINDING_HANDLE) this;

    // In addition to saving space for the request (or response) header
    // and an object UUID, we want to save space for security information
    // if necessary.

    if ((Message->RpcFlags & RPC_BUFFER_PARTIAL) &&
        (Message->BufferLength < MaxFrag))
        {
        CurrentBufferLength = MaxFrag ;
        }
    else
        {
        CurrentBufferLength = Message->BufferLength ;
        }

    RpcStatus = TransGetBuffer(&Message->Buffer,
            CurrentBufferLength + sizeof(rpcconn_request) + sizeof(UUID)
            + (2*AdditionalSpaceForSecurity) );
    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );
        return(RPC_S_OUT_OF_MEMORY);
        }

    Message->Buffer = (char PAPI *)Message->Buffer + sizeof(rpcconn_request);
    return(RPC_S_OK);
}

RPC_STATUS
OSF_CCONNECTION::GetBufferDo (
    IN unsigned int culRequiredLength,
    OUT void PAPI *PAPI * ppBuffer
    )
{
    if (TransGetBuffer(ppBuffer,culRequiredLength + sizeof(rpcconn_request)
                + sizeof(UUID)))
        return(RPC_S_OUT_OF_MEMORY);

    *ppBuffer = (((unsigned char PAPI *) *ppBuffer) + sizeof(rpcconn_request));
    return(RPC_S_OK);
}

void
OSF_CCONNECTION::FreeBuffer (
    IN PRPC_MESSAGE Message
    )
{
    TransFreeBuffer((char PAPI *)Message->Buffer - sizeof(rpcconn_request));
    CurrentBufferLength = 0 ;

    if (CallStack == 0 &&
        OutstandingBuffers == 0)
        {
        FreeConnection();
        }
}

void OSF_CCONNECTION::FreePipeBuffer (
    IN PRPC_MESSAGE Message
    )
{
    TransFreeBuffer((char PAPI *)Message->Buffer - sizeof(rpcconn_request));
}


RPC_STATUS
OSF_CCONNECTION::ReallocPipeBuffer (
    IN PRPC_MESSAGE Message,
    IN unsigned int NewSize
    )
{
    void PAPI *TempBuffer ;
    RPC_STATUS RpcStatus ;
    unsigned int SizeToAlloc ;

    if (NewSize > CurrentBufferLength)
        {
        SizeToAlloc = (NewSize > MaxFrag) ? NewSize:MaxFrag ;

        RpcStatus = TransGetBuffer(&TempBuffer,
                SizeToAlloc + sizeof(rpcconn_request) + sizeof(UUID)
                + (2*AdditionalSpaceForSecurity) );
        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );
            return(RPC_S_OUT_OF_MEMORY);
            }

        if (Message->BufferLength > 0)
            {
            RpcpMemoryCopy((char PAPI *) TempBuffer+sizeof(rpcconn_request),
                                        Message->Buffer, Message->BufferLength) ;
            FreePipeBuffer(Message) ;
            }
        //BUGBUG: Think about changing this to return CurrentBufferLength
        Message->Buffer = (char PAPI *) TempBuffer  + sizeof(rpcconn_request);
        CurrentBufferLength = SizeToAlloc ;
        }

    Message->BufferLength = NewSize ;

    return (RPC_S_OK) ;
}


RPC_STATUS
OSF_CCONNECTION::BindingCopy (
    OUT BINDING_HANDLE * PAPI * DestinationBinding,
    IN unsigned int MaintainContext
    )
/*++

Routine Description:

    We check to see if this connection has an active binding, if
    so, we tell the binding using this connection to copy itself.
    Otherwise, it is an error.

Arguments:

    DestinationBinding - Returns a copy of the current binding handle
        for this connection.

    MaintainContext - Supplies a flag that indicates whether or not context
        is being maintained over this binding handle.  A non-zero value
        indicates that context is being maintained.

Return Value:

    RPC_S_INVALID_HANDLE - This connection does not have a current
        binding to be copied.

--*/
{
    if (CurrentBinding == 0)
        {
        return(RPC_S_INVALID_BINDING);
        }

    return(CurrentBinding->BindingCopy(DestinationBinding, MaintainContext));
}

#define FIRST_CACHED_BUFFER_AVAILABLE 0x1
#define SECOND_CACHED_BUFFER_AVAILABLE 0x2
#define CACHED_BUFFERS_ALLOCATED 0x4

#define CACHED_BUFFER_AVAILABLE_MASK 0x3

#define ALIGN_POWER2 3
#define ALIGN_REQUIRED (1 << ALIGN_POWER2)

#ifdef NTENV
#define UnAlignBuffer(Buffer) \
    (void PAPI *) (Buffer)
#else // NTENV
#define UnAlignBuffer(Buffer) \
    (void PAPI *) ((char PAPI *)Buffer - ((int PAPI *)Buffer)[-1])
#endif // NTENV

#ifdef NTENV
#define CACHED_BUFFER_LENGTH 1024
#else // NTENV
#define CACHED_BUFFER_LENGTH 512
#endif // NTENV


RPC_STATUS
OSF_CCONNECTION::TransGetBuffer (
    OUT void PAPI * PAPI * Buffer,
    IN unsigned int BufferLength
    )
/*++

Routine Description:

    We need a buffer to receive data into or to put data into to be sent.
    This should be really simple, but we need to make sure that buffer we
    return is aligned on an 8 byte boundary.  The stubs make this requirement.

Arguments:

    Buffer - Returns a pointer to the buffer.

    BufferLength - Supplies the required length of the buffer in bytes.

Return Value:

    RPC_S_OK - We successfully allocated a buffer of at least the required
        size.

    RPC_S_OUT_OF_MEMORY - There is insufficient memory available to allocate
        the required buffer.

--*/
{
    int PAPI * Memory;

#ifndef NTENV

    int AmountOfPad;

#endif // NTENV

    // First we need to check to see if we can use one of the cached
    // buffers.

    if ( BufferLength <= CACHED_BUFFER_LENGTH )
        {
        if ( BufferCacheFlags & FIRST_CACHED_BUFFER_AVAILABLE )
            {
            BufferCacheFlags &= ~FIRST_CACHED_BUFFER_AVAILABLE;
            *Buffer = FirstCachedBuffer;
            OutstandingBuffers += 1;
            return(RPC_S_OK);
            }

        if ( BufferCacheFlags & SECOND_CACHED_BUFFER_AVAILABLE )
            {
            BufferCacheFlags &= ~SECOND_CACHED_BUFFER_AVAILABLE;
            *Buffer = SecondCachedBuffer;
            OutstandingBuffers += 1;
            return(RPC_S_OK);
            }
        }

#ifdef NTENV

    // The NT memory allocator returns memory which is aligned by at least
    // 8, so we dont need to worry about aligning it.

    Memory = (int PAPI *) RpcpFarAllocate(BufferLength);
    if ( Memory == 0 )
        {
        return(RPC_S_OUT_OF_MEMORY);
        }

    ASSERT( (((long) Memory) % ALIGN_REQUIRED) == 0 );

#else // NTENV

    // We will allocate an extra 8 bytes of memory, so we have enough
    // space to pad the buffer.  We will place an integer just before
    // the pointer we return.  The integer will specify the amount we
    // had to pad the buffer to make it 8 byte aligned.  We need this
    // information when we free the buffer.  As a result, the original
    // buffer must be allocated to at least sizeof(int) alignment.

    Memory = (int PAPI *) RpcpFarAllocate(BufferLength + ALIGN_REQUIRED);

    if ( Memory == 0 )
        {
        return(RPC_S_OUT_OF_MEMORY);
        }

    ASSERT( (((long) Memory) % sizeof(int)) == 0 );

    AmountOfPad = Pad(Memory + 1, ALIGN_REQUIRED) + sizeof(int);
    Memory = (int PAPI *) (((char PAPI *) Memory) + AmountOfPad);
    Memory[-1] = AmountOfPad;

#endif // NTENV

    *Buffer = Memory;
    OutstandingBuffers += 1;

    ASSERT(Pad8(*Buffer) == 0);
    return(RPC_S_OK);
}


void
OSF_CCONNECTION::TransFreeBuffer (
    IN void PAPI * Buffer
    )
/*++

Routine Description:

    We need to free a buffer which was allocated via TransGetBuffer.  The
    only tricky part is remembering to remove the padding before actually
    freeing the memory.

--*/
{
    if ( Buffer == FirstCachedBuffer )
        {
        BufferCacheFlags |= FIRST_CACHED_BUFFER_AVAILABLE;
        }
    else if ( Buffer == SecondCachedBuffer )
        {
        BufferCacheFlags |= SECOND_CACHED_BUFFER_AVAILABLE;
        }
    else
        {
        RpcpFarFree(UnAlignBuffer(Buffer));
        }

    OutstandingBuffers -= 1;
}

void
ConstructPContextList ( // Construct the presentation context list in the
                        // rpc_bind packet (and implicitly rpc_alter_context)
                        // packet.
    OUT p_cont_list_t PAPI * pCon, // Place the list here.
    IN PRPC_SYNTAX_IDENTIFIER Interface,
    IN PRPC_SYNTAX_IDENTIFIER TransferSyntaxes,
    IN unsigned int TransferSyntaxCount,
    IN unsigned char PresentContext
    )
{
    pCon->n_context_elem = 1;
    pCon->reserved = 0;
    pCon->reserved2 = 0;
    pCon->p_cont_elem[0].p_cont_id = (unsigned char) PresentContext;
    pCon->p_cont_elem[0].n_transfer_syn = TransferSyntaxCount;
    pCon->p_cont_elem[0].reserved = 0;

    RpcpMemoryCopy(&pCon->p_cont_elem[0].abstract_syntax, Interface,
            sizeof(RPC_SYNTAX_IDENTIFIER));

    RpcpMemoryCopy(pCon->p_cont_elem[0].transfer_syntaxes, TransferSyntaxes,
            sizeof(RPC_SYNTAX_IDENTIFIER) * TransferSyntaxCount);


#ifdef MAC

    // Our RPC_SYNTAX_IDENTIFIER structure contains a RPC_VERSION structure
    // which is defined as two shorts making a little endian formatted long.
    // We need to send the versions as a big endian formatted long.
    // Rather then changing the stubs, we'll be swap it here.

    // Since the shorts are correct (big endian), we just need to swap
    // them.

    #define SWAP_WORDS_OF_DWORD(x) \
    x = (( (x & 0x0000FFFF) << 16 ) | ( (x & 0xFFFF0000) >> 16 ))

    SWAP_WORDS_OF_DWORD(pCon->p_cont_elem[0].abstract_syntax.if_version);


    // If we move to a multiple transfer syntaxes we'll should take the
    // time to fix it so that this byte swap is not necessary at all.

    ASSERT(TransferSyntaxCount == 1);

    SWAP_WORDS_OF_DWORD(pCon->p_cont_elem[0].transfer_syntaxes[0].if_version);

#endif

}


RPC_STATUS
OSF_CCONNECTION::PingServer(
    )
{
    return Association->PingServer() ;
}


RPC_STATUS
OSF_CCONNECTION::SendBindPacket (
    IN PRPC_SYNTAX_IDENTIFIER InterfaceSyntax,
    IN PRPC_SYNTAX_IDENTIFIER TransferSyntaxes,
    IN unsigned int TransferSyntaxCount,
    IN unsigned char PresentContext,
    IN unsigned long AssocGroup,
    IN unsigned char PacketType,
    OUT void PAPI * PAPI * Buffer,
    OUT unsigned int PAPI * BufferLength
    )
/*++

Routine Description:

    This routine is used to send a bind or alter context packet.  It
    will allocate a buffer, fill in the packet, and then send it and
    receive a reply.  The reply buffer is just returned to the caller.

Arguments:

    InterfaceSyntax - Supplies the interface UUID and version to which
        we want to bind.  This information will be placed in the packet.

    TransferSyntax - Supplies one or more transfer syntaxes which the
        client stub supports.  The server will select one, if possible,
        which the server stub supports.

    TransferSyntaxCount - Supplies the number of transfer syntaxes.

    PresentContext - Supplies the presentation context to be used for
        this particular binding.

    AssocGroup - Supplies the association group id for the association
        group of which this connection is a new member.

    PacketType - Supplies the packet type which must be one of rpc_bind
        or rpc_alter_context.

    Buffer - Returns the reply buffer.

    BufferLength - Returns the length of the reply buffer.

Return Value:

    RPC_S_OK - The operation completed successfully.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete
        the operation.

    RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
        complete the operation.

    RPC_S_ACCESS_DENIED - The security package won't allow this.

    RPC_P_CONNECTION_CLOSED - The connection has been closed and the
        receive operation failed.  The send operation may or may not
        have succeeded.

--*/
{
    rpcconn_bind PAPI * BindPacket = 0;
    unsigned int BindPacketLength, AuthPadLength, SecurityTokenLength;
    RPC_STATUS RpcStatus;
    sec_trailer PAPI * SecurityTrailer;
    SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
    SECURITY_BUFFER SecurityBuffers[4];
    DCE_INIT_SECURITY_INFO InitSecurityInfo;
    unsigned int CompleteNeeded = 0;

    BindPacketLength = sizeof(rpcconn_bind) + sizeof(p_cont_list_t) +
            (TransferSyntaxCount - 1) * sizeof(p_syntax_id_t);

    // If we need to send authentication information in the packet, we
    // need to save space for it.  This method prepares and sends both
    // rpc_bind and rpc_alter_context packets; we will only send
    // authentication information in rpc_bind packets.  This is due to
    // a design decision that each connection supports only a single
    // security context, which is determined when the connection is
    // created.

    if (   ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE )
        {
        ASSERT(   (ClientSecurityContext.AuthenticationLevel
                          == RPC_C_AUTHN_LEVEL_CONNECT)
               || (ClientSecurityContext.AuthenticationLevel
                          == RPC_C_AUTHN_LEVEL_PKT)
               || (ClientSecurityContext.AuthenticationLevel
                          == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
               || (ClientSecurityContext.AuthenticationLevel
                          == RPC_C_AUTHN_LEVEL_PKT_PRIVACY) );

        RpcStatus = UuidCreate(&(DceSecurityInfo.AssociationUuid));
        if (   (RpcStatus != RPC_S_OK )
            && (RpcStatus != RPC_S_UUID_LOCAL_ONLY) )
            {
            return(RpcStatus);
            }

        // We align the packet length to a four byte boundary, and then
        // save space for the token and the sec_trailer.  We also need
        // to save the length of the token because we will need it later
        // if we do third leg authentication.

        AuthPadLength = Pad4(BindPacketLength);
        BindPacketLength += AuthPadLength;
        TokenLength = ClientSecurityContext.Credentials->MaximumTokenLength();
        BindPacketLength += TokenLength + sizeof(sec_trailer);
        }

    RpcStatus = TransGetBuffer((void PAPI * PAPI *) &BindPacket,
            BindPacketLength);
    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );
        if (   ( PacketType == rpc_bind )
            && ( ClientSecurityContext.AuthenticationLevel
                    != RPC_C_AUTHN_LEVEL_NONE ) )
            {
            ;
            }
        return(RPC_S_OUT_OF_MEMORY);
        }

    ConstructPacket((rpcconn_common PAPI *) BindPacket, PacketType,
            BindPacketLength);

    BindPacket->max_xmit_frag = BindPacket->max_recv_frag
            = (unsigned short) TransMaximumSend();
    BindPacket->assoc_group_id = AssocGroup;

    ConstructPContextList((p_cont_list_t PAPI *) (BindPacket + 1),
            InterfaceSyntax, TransferSyntaxes, TransferSyntaxCount,
            PresentContext);

    // If this connection is using security, we need to stick the
    // authentication information into the packet.

    if ( ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE )
        {
        InitSecurityInfo.DceSecurityInfo = DceSecurityInfo;
        InitSecurityInfo.AuthorizationService =
                ClientSecurityContext.AuthorizationService;
        InitSecurityInfo.PacketType = PacketType;
        BufferDescriptor.ulVersion = 0;
        BufferDescriptor.cBuffers = 4;
        BufferDescriptor.pBuffers = SecurityBuffers;
        SecurityBuffers[0].cbBuffer = sizeof(rpcconn_bind);
        SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
        SecurityBuffers[0].pvBuffer = BindPacket;
        SecurityBuffers[1].cbBuffer = BindPacketLength - sizeof(rpcconn_bind)
                - ClientSecurityContext.Credentials->MaximumTokenLength();
        SecurityBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
        SecurityBuffers[1].pvBuffer = ((unsigned char PAPI *) BindPacket)
                + sizeof(rpcconn_bind);
        SecurityBuffers[2].cbBuffer = 
                 ClientSecurityContext.Credentials->MaximumTokenLength();
        SecurityBuffers[2].BufferType = SECBUFFER_TOKEN;
        SecurityBuffers[2].pvBuffer = ((unsigned char PAPI *) BindPacket)
                + BindPacketLength - 
                ClientSecurityContext.Credentials->MaximumTokenLength();
        SecurityBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
        SecurityBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
        SecurityBuffers[3].pvBuffer = &InitSecurityInfo;

        // Let the security packet take care of sticking appropriate stuff
        // into the packet for us.

        if (PacketType == rpc_bind)
           {
           RpcStatus = ClientSecurityContext.InitializeFirstTime(
                ClientSecurityContext.Credentials, 
                ClientSecurityContext.ServerPrincipalName,
                ClientSecurityContext.AuthenticationLevel, 
                &BufferDescriptor);
           }
        else
           {

           //
           // a third leg auth may not be needed with some packages
           // on an alter context [where only pcon is changed as opposed
           // to an alternative client principal
           //
           ThirdLegAuthNeeded = 0;

           RpcStatus = ClientSecurityContext.InitializeThirdLeg(
                         *((unsigned long PAPI *)&(BindPacket->common.drep[0])),
                         0L,
                         &BufferDescriptor);
           }

        if ( RpcStatus == RPC_P_CONTINUE_NEEDED )
            {
            // Remember the fact that the security package requested that
            // it be called again.  This will be important later: see
            // OSF_CASSOCIATION::ActuallyDoBinding.

            ThirdLegAuthNeeded = 1;
            }
        else if ( RpcStatus == RPC_P_COMPLETE_NEEDED )
            {
            CompleteNeeded = 1;
            }
        else if ( RpcStatus == RPC_P_COMPLETE_AND_CONTINUE )
            {
            ThirdLegAuthNeeded = 1;
            CompleteNeeded = 1;
            }
        else if ( RpcStatus != RPC_S_OK )
            {
            ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
                   || (RpcStatus == RPC_S_ACCESS_DENIED)
                   || (RpcStatus == RPC_S_SEC_PKG_ERROR) );
            TransFreeBuffer(BindPacket);
            return(RpcStatus);
            }
        SecurityTokenLength = (unsigned int) SecurityBuffers[2].cbBuffer;

        // We need to fill in the fields of the security trailer.

        SecurityTrailer = (sec_trailer PAPI *)
                (((unsigned char PAPI *) BindPacket) + BindPacketLength
                - ClientSecurityContext.Credentials->MaximumTokenLength()
                - sizeof(sec_trailer));

        SecurityTrailer->auth_type = (unsigned char)
                ClientSecurityContext.AuthenticationService;
        SecurityTrailer->auth_level = (unsigned char)
                ClientSecurityContext.AuthenticationLevel;
        SecurityTrailer->auth_pad_length = AuthPadLength;
        SecurityTrailer->auth_reserved = 0;
        SecurityTrailer->auth_context_id = (unsigned long)(this);

        // Ok, finally, we need to adjust the length of the packet,
        // and set the length of the authentication information.

        BindPacket->common.auth_length = SecurityTokenLength;
        BindPacketLength = BindPacketLength
                - ClientSecurityContext.Credentials->MaximumTokenLength()
                + SecurityTokenLength;
        BindPacket->common.frag_length = BindPacketLength;
        BindPacket->assoc_group_id = AssocGroup;

        if ( CompleteNeeded != 0 )
            {
            RpcStatus = ClientSecurityContext.CompleteSecurityToken(
                    &BufferDescriptor);
            if (RpcStatus != 0)
                {
                TransFreeBuffer(BindPacket);
                return(RpcStatus);
                }
            }
        }

    RpcStatus = TransSendReceiveWithTimeout(BindPacket, BindPacketLength,
                        Buffer, BufferLength, RPC_BIND_TIMEOUT);

    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
               || (RpcStatus == RPC_S_ACCESS_DENIED)
               || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
               || (RpcStatus == RPC_P_CONNECTION_CLOSED)
               || (RpcStatus == RPC_P_RECEIVE_FAILED)
               || (RpcStatus == RPC_P_SEND_FAILED));

        TransFreeBuffer(BindPacket);
        if (   (RpcStatus == RPC_P_RECEIVE_FAILED)
            || (RpcStatus == RPC_P_SEND_FAILED))
            {
            return(RPC_P_CONNECTION_CLOSED);
            }
        return(RpcStatus);
        }

    TransFreeBuffer(BindPacket);
    return(RPC_S_OK);
}

void
OSF_CCONNECTION::SetMaxFrag (
    IN unsigned short max_xmit_frag,
    IN unsigned short max_recv_frag
    )
{
    UNUSED(max_recv_frag);

    unsigned short TranMax = TransMaximumSend();

    MaxFrag = max_xmit_frag;

    if (MaxFrag > TranMax || MaxFrag == 0)
        {
        MaxFrag = TranMax;
        }

#ifndef WIN
    ASSERT( MaxFrag >= MUST_RECV_FRAG_SIZE );
#endif // WIN
}


void
OSF_CCONNECTION::FreeConnection (
    )
/*++

Routine Description:

    This routine is used to free a connection when the original remote
    procedure call using it completes.

--*/
{
    OSF_BINDING_HANDLE * Binding;

    ASSERT(CurrentBinding != 0);

    Binding = CurrentBinding;
    CurrentBinding = 0;
    Binding->FreeConnection(this);
}


void
OSF_CCONNECTION::AbortConnection (
    )
/*++

Routine Description:

    Any time that an error occurs while a remote procedure call is using
    this connection, this routine will be called.  We need to indicate that
    the connection has been aborted, and in addition, if all calls using
    this connection have completed, we need to delete it.

--*/
{
    OSF_BINDING_HANDLE * Binding;

    if ( ConnectionAbortedFlag == 0 )
        {
        ConnectionAbortedFlag = 1;
        Association->NotifyConnectionClosed();
        }

    if (CallStack == 0 )
        {
        ASSERT( CurrentBinding != 0 );
        ASSERT( OutstandingBuffers == 0);
#ifdef DEBUGRPC
        if ( OutstandingBuffers != 0 )
            {
            PrintToDebugger("RPCCLNT: OutstandingBuffers != 0\n");
            }
#endif // DEBUGRPC

        Binding = CurrentBinding;
        CurrentBinding = 0;
        Binding->AbortConnection(this);
        delete this;
        }
}


OSF_CCONNECTION::OSF_CCONNECTION (
    CLIENT_AUTH_INFO  * ClientAuthInfo,
    RPC_STATUS __RPC_FAR * pStatus
    )
    : CCONNECTION(ClientAuthInfo, pStatus),
      ClientSecurityContext(ClientAuthInfo, pStatus)
{
#ifndef NTENV

    int AmountOfPad;

#endif // NTENV

    Association = 0;
    MaxFrag = 512;
    AssociationKey = -1;
    CurrentBinding = Nil;
    OutstandingBuffers = 0;
    CallStack = 0;
    CallId = 0xFFFFFFFF;
    ConnectionAbortedFlag = 0;

    ThirdLegAuthNeeded = 0;
    AdditionalSpaceForSecurity = 0;
    LastTimeUsed = 0;

    PendingAlert = FALSE;
    AlertsEnabled = 0;
    DceSecurityInfo.SendSequenceNumber = 0;
    DceSecurityInfo.ReceiveSequenceNumber = 0;

    // Ok, try and allocate the cached buffers.

#ifdef NTENV

    FirstCachedBuffer = (int PAPI *) RpcpFarAllocate(CACHED_BUFFER_LENGTH * 2);

#else // NTENV

    FirstCachedBuffer = (int PAPI *) RpcpFarAllocate(CACHED_BUFFER_LENGTH * 2
            + ALIGN_REQUIRED);

#endif // NTENV

    if ( FirstCachedBuffer == 0 )
        {
        BufferCacheFlags = 0;
        SecondCachedBuffer = 0;
        }
    else
        {
#ifndef NTENV

        ASSERT( (((long) FirstCachedBuffer) % sizeof(int)) == 0 );

        AmountOfPad = Pad((char __RPC_FAR *)FirstCachedBuffer + 1, ALIGN_REQUIRED) + 1;
        FirstCachedBuffer = (void PAPI *)
                (((char PAPI *) FirstCachedBuffer) + AmountOfPad);
        ((int PAPI *) FirstCachedBuffer)[-1] = AmountOfPad;

#endif // NTENV

        SecondCachedBuffer = (void PAPI *)
                (((char PAPI *) FirstCachedBuffer) + CACHED_BUFFER_LENGTH);
        BufferCacheFlags = FIRST_CACHED_BUFFER_AVAILABLE
                | SECOND_CACHED_BUFFER_AVAILABLE | CACHED_BUFFERS_ALLOCATED;
        ASSERT(Pad8(SecondCachedBuffer) == 0);
        }

   SavedHeader = 0;
   SavedHeaderSize = 0;
   FirstFrag = 1 ;
   FirstReceive = 1 ;
   EnableCancels = 0;
}

OSF_CCONNECTION::~OSF_CCONNECTION (
    )
{
    if ( ConnectionAbortedFlag == 0 )
        {
        if ( Association != 0 )
            {
            Association->NotifyConnectionClosed();
            }
        }
    if ( BufferCacheFlags & CACHED_BUFFERS_ALLOCATED )
        {
        RpcpFarFree(UnAlignBuffer(FirstCachedBuffer));
        }

   if (SavedHeader != 0)
      {
      ASSERT(SavedHeaderSize != 0);
      RpcpFarFree(SavedHeader);
      }

  if (EnableCancels)
      {
      EVAL_AND_ASSERT(RPC_S_OK == UnregisterForCancels());
      }
}


RPC_STATUS
OSF_CCONNECTION::MaybeDo3rdLegAuth (
    IN void PAPI * Buffer,
    IN unsigned int BufferLength
    )
/*++

Routine Description:

    We use this method to deal with third leg authentication if necessary.
    In addition, we do error checking for whether or not there should be
    security information in the packet.

Arguments:

    Buffer - Supplies an rpc_bind_ack or rpc_bind_nak packet which may or
        may not contain some authentication information.

    BufferLength - Supplies the length of the buffer in bytes.

Return Value:

    RPC_S_OK - Everything proceeded as expected.

    RPC_S_PROTOCOL_ERROR - The server either specified authentication
        information when none was expected, or did not specify some when
        we expected some.

    RPC_S_OUT_OF_MEMORY - Insufficient space is available to allocate
        the third leg

    RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
        send the third leg authentication packet to the server.

    RPC_S_ACCESS_DENIED - We are unable to access the server for security
        related reasons.

    RPC_P_CONNECTION_CLOSED - The connection has closed.

--*/
{
    RPC_STATUS RpcStatus;
    unsigned int AuthThirdLegPacketLength, SecurityTokenLength;
    rpcconn_auth3 PAPI * AuthThirdLegPacket;
    sec_trailer PAPI * SecurityTrailer;
    SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor;
    SECURITY_BUFFER_DESCRIPTOR OutputBufferDescriptor;
    SECURITY_BUFFER InputBuffers[4];
    SECURITY_BUFFER OutputBuffers[4];
    DCE_INIT_SECURITY_INFO InitSecurityInfo;
    unsigned int CompleteNeeded = 0;

    // To begin with, check to see if there is not authentication information
    // in the packet; if there is not, we need to check to see if there
    // should have been some.

    if ( ((rpcconn_common PAPI *) Buffer)->auth_length == 0 )
        {
        if ( ThirdLegAuthNeeded != 0 )
            {
            // We have got a problem: there should have been some
            // authentication information in this packet, but there is not.

            return(RPC_S_PROTOCOL_ERROR);
            }

        return(RPC_S_OK);
        }

    // Now we need to allocate an rpc_auth3 packet to send the security
    // information back to the server.  We do not need padding, because
    // the rpc_auth3 packet is already aligned.

    ASSERT( Pad4(sizeof(rpcconn_auth3)) == 0 );
    AuthThirdLegPacketLength = sizeof(rpcconn_auth3) + sizeof(sec_trailer)
            + TokenLength;

    RpcStatus = TransGetBuffer((void PAPI * PAPI *) &AuthThirdLegPacket,
            AuthThirdLegPacketLength);
    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY );
        return(RpcStatus);
        }

    ConstructPacket((rpcconn_common PAPI *) AuthThirdLegPacket, rpc_auth_3,
            AuthThirdLegPacketLength);

    SecurityTrailer = (sec_trailer PAPI *) (AuthThirdLegPacket + 1);

    InitSecurityInfo.DceSecurityInfo = DceSecurityInfo;
    InitSecurityInfo.AuthorizationService = AuthInfo.AuthorizationService;
    InitSecurityInfo.PacketType = ((rpcconn_common PAPI *) Buffer)->PTYPE;
    InputBufferDescriptor.ulVersion = 0;
    InputBufferDescriptor.cBuffers = 4;
    InputBufferDescriptor.pBuffers = InputBuffers;
    ASSERT((SavedHeader != 0) && (SavedHeaderSize != 0));

    InputBuffers[0].cbBuffer = sizeof(rpcconn_bind_ack)
            - sizeof(unsigned short);
    InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
    InputBuffers[0].pvBuffer = SavedHeader;

    InputBuffers[1].cbBuffer = BufferLength - (sizeof(rpcconn_bind_ack)
            - sizeof(unsigned short))
            - ((rpcconn_common PAPI *) Buffer)->auth_length;
    InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
    InputBuffers[1].pvBuffer = ((unsigned char PAPI *) SavedHeader)
            + sizeof(rpcconn_bind_ack) - sizeof(unsigned short);

    InputBuffers[2].cbBuffer = ((rpcconn_common PAPI *) Buffer)->auth_length;
    InputBuffers[2].BufferType = SECBUFFER_TOKEN;
    InputBuffers[2].pvBuffer = ((unsigned char PAPI *) Buffer)
            + BufferLength - ((rpcconn_common PAPI *) Buffer)->auth_length;
    InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
    InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
    InputBuffers[3].pvBuffer = &InitSecurityInfo;

    OutputBufferDescriptor.ulVersion = 0;
    OutputBufferDescriptor.cBuffers = 4;
    OutputBufferDescriptor.pBuffers = OutputBuffers;
    OutputBuffers[0].cbBuffer = sizeof(rpcconn_auth3);
    OutputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
    OutputBuffers[0].pvBuffer = AuthThirdLegPacket;
    OutputBuffers[1].cbBuffer = sizeof(sec_trailer);
    OutputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
    OutputBuffers[1].pvBuffer = ((unsigned char PAPI *) AuthThirdLegPacket)
            + sizeof(rpcconn_auth3);
    OutputBuffers[2].cbBuffer = TokenLength;
    OutputBuffers[2].BufferType = SECBUFFER_TOKEN;
    OutputBuffers[2].pvBuffer = SecurityTrailer + 1;
    OutputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
    OutputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
    OutputBuffers[3].pvBuffer = &InitSecurityInfo;

    RpcStatus = ClientSecurityContext.InitializeThirdLeg(
            *((unsigned long PAPI *) ((rpcconn_common PAPI *)
            Buffer)->drep), &InputBufferDescriptor, &OutputBufferDescriptor);

    SecurityTokenLength = (unsigned int) OutputBuffers[2].cbBuffer;

    if ( RpcStatus == RPC_P_COMPLETE_NEEDED )
        {
        CompleteNeeded = 1;
        }
    else if ( RpcStatus != RPC_S_OK )
        {
        TransFreeBuffer(AuthThirdLegPacket);

        ASSERT(   (RpcStatus == RPC_S_ACCESS_DENIED)
               || (RpcStatus == RPC_S_OUT_OF_MEMORY)
               || (RpcStatus == RPC_S_SEC_PKG_ERROR));
        return(RpcStatus);
        }
    //Now that we accepted the packet bump up the recv #
    DceSecurityInfo.ReceiveSequenceNumber += 1;

    if ( ThirdLegAuthNeeded != 0 )
        {
        SecurityTrailer->auth_type = (unsigned char)
                AuthInfo.AuthenticationService;
        SecurityTrailer->auth_level = (unsigned char)
                AuthInfo.AuthenticationLevel;
        SecurityTrailer->auth_pad_length = 0;
        SecurityTrailer->auth_reserved = 0;
        SecurityTrailer->auth_context_id = (unsigned long) this;

        AuthThirdLegPacket->common.auth_length = SecurityTokenLength;
        AuthThirdLegPacketLength = AuthThirdLegPacketLength - TokenLength
                + SecurityTokenLength;
        AuthThirdLegPacket->common.frag_length = AuthThirdLegPacketLength;

        if ( CompleteNeeded != 0 )
            {
            ClientSecurityContext.CompleteSecurityToken(
                                           &OutputBufferDescriptor);
            }
        RpcStatus = TransSend(AuthThirdLegPacket, AuthThirdLegPacketLength);

        // BUGBUG - Due to a bug in NT named pipes, if you do not delay, the
        // receive operation will fail.

#ifdef NTENV

        PauseExecution(0L);

#endif // NTENV
        TransFreeBuffer(AuthThirdLegPacket);
        if ( RpcStatus != RPC_S_OK )
            {
            if (   (RpcStatus == RPC_P_CONNECTION_CLOSED)
                || (RpcStatus == RPC_P_SEND_FAILED) )
                {
                return(RPC_P_CONNECTION_CLOSED);
                }
            ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
                   || (RpcStatus == RPC_S_OUT_OF_RESOURCES) );
            return(RpcStatus);
            }
        }
    else
        {
        TransFreeBuffer(AuthThirdLegPacket);
        }

    // This is as good a place as any to figure out how much additional
    // space we need to reserve in each buffer for security information.


    ASSERT( AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE );

    // We need to figure out how much space to reserve for security
    // information at the end of request and response packets.
    // In addition to saving space for the signature or header,
    // we need space to pad the packet to a multiple of the maximum
    // security block size as well as for the security trailer.

    switch ( AuthInfo.AuthenticationLevel )
        {

        case RPC_C_AUTHN_LEVEL_CONNECT:
        case RPC_C_AUTHN_LEVEL_PKT:
        case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY:
             AdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE +
                 ClientSecurityContext.MaximumSignatureLength()
                + sizeof(sec_trailer);
             break;

        case RPC_C_AUTHN_LEVEL_PKT_PRIVACY:
             AdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE +
                ClientSecurityContext.MaximumHeaderLength()
                + sizeof(sec_trailer);
             break;

        default:
             ASSERT(!"Unknown Security Level\n");

        }

    return(RPC_S_OK);
}


inline int
OSF_CCONNECTION::SupportedAuthInfo (
    IN CLIENT_AUTH_INFO * ClientAuthInfo
    )
/*++

Arguments:

    ClientAuthInfo - Supplies the authentication and authorization information
        required of this connection.  A value of zero (the pointer is
        zero) indicates that we want an unauthenticated connection.

Return Value:

    Non-zero indicates that the connection has the requested authentication
    and authorization information; otherwise, zero will be returned.

--*/
{
    return (ClientSecurityContext.IsSupportedAuthInfo(ClientAuthInfo));
}


RPC_STATUS
OSF_CCONNECTION::SetAuthInformation (
    IN CLIENT_AUTH_INFO * ClientAuthInfo
    )
/*++

Routine Description:

    We need to use this routine to initialize the authentication and
    authorization information for this connection.

Arguments:

    ClientAuthInfo - Supplies the authentication and authorization information
        to use.

Return Value:

    RPC_S_OK - Everybody's happy.

    RPC_S_OUT_OF_MEMORY - We were unable to make a copy of the server
        principal name.

--*/
{
    AuthInfo = *ClientAuthInfo;
    if ( ClientAuthInfo->ServerPrincipalName != 0 )
        {
        AuthInfo.ServerPrincipalName = DuplicateString(
                ClientAuthInfo->ServerPrincipalName);
        if ( AuthInfo.ServerPrincipalName == 0 )
            {
            return(RPC_S_OUT_OF_MEMORY);
            }
        }
    return(RPC_S_OK);
}

/* --------------------------------------------------------------------
-------------------------------------------------------------------- */

NEW_SDICT(OSF_CASSOCIATION);

static OSF_CASSOCIATION_DICT * AssociationDict;

/* --------------------------------------------------------------------
-------------------------------------------------------------------- */


OSF_CASSOCIATION::OSF_CASSOCIATION (
    IN DCE_BINDING * DceBinding,
    IN RPC_CLIENT_TRANSPORT_INFO * RpcClientInfo,
    IN OUT RPC_STATUS PAPI * RpcStatus
    ) : AssociationMutex(RpcStatus), CallIdCounter(1)
/*++

Routine Description:

    We construct a OSF_CASSOCIATION object in this routine.  This consists
    of initializing some instance variables, and saving the parameters
    away.

Arguments:

    DceBinding - Supplies the binding information for this association.
        Ownership of this data passes to this object.

    RpcClientInfo - Supplies the information necessary to use the loadable
        transport corresponding to the network interface type used by
        this association.

--*/
{
    ALLOCATE_THIS(OSF_CASSOCIATION);

    BindHandleCount = 1;
    AssocGroupId = 0;

    this->DceBinding = DceBinding;
    this->RpcClientInfo = RpcClientInfo;

    SecondaryEndpoint = 0;
    OpenConnectionCount = 0;

    MaintainContext = 0;

#ifdef WIN
    TaskId = GetCurrentTask();
#endif
    AssociationValid = TRUE ;
    FailureCount = 0;
}

OSF_CASSOCIATION::~OSF_CASSOCIATION (
    )
{
    OSF_BINDING * Binding;
    OSF_CCONNECTION * CConnection;

    if (DceBinding != 0)
       {
       delete DceBinding;
       }

    Bindings.Reset();
    while ((Binding = Bindings.Next()))
        delete Binding;

    FreeConnections.Reset();
    while ((CConnection = FreeConnections.Next()))
        {
        delete CConnection;
        }

    if ( SecondaryEndpoint != 0 )
        {
        delete SecondaryEndpoint;
        }
}


void
OSF_CASSOCIATION::NotifyConnectionClosed (
    )
/*++

Routine Description:

    This routine is necessary so that we can know when to set the association
    group id back to zero.  We do this when no more connections owned by
    this association can possibly be connected with the server.

--*/
{
    RequestGlobalMutex();

    ASSERT( OpenConnectionCount > 0 );

    OpenConnectionCount -= 1;
    if ( OpenConnectionCount == 0 )
        {
        AssocGroupId = 0;
        }

    ClearGlobalMutex();
}

RPC_STATUS
OSF_CASSOCIATION::ProcessBindAckOrNak (
    IN rpcconn_common PAPI * Buffer,
    IN unsigned int BufferLength,
    IN OSF_CCONNECTION * CConnection
    )
/*++

Routine Description:

Arguments:

    Buffer - Supplies the buffer containing either the bind_ack, bind_nak,
        or alter_context_resp packet.

    BufferLength - Supplies the length of the buffer, less the length of
        the authorization information.

    CConnection - Supplies the connection from which we received the packet.

Return Value:

    RPC_S_OK - The client has successfully bound with the server.

    RPC_S_PROTOCOL_ERROR - The packet received from the server does not
        follow the protocol.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to make a
        copy of the secondary endpoint.

    RPC_S_UNSUPPORTED_TRANS_SYN - The transfer syntax supplied by the client
        is not supported by the server.

    RPC_S_UNKNOWN_IF - The interface to which the client wished to bind is not
        supported by the server.

    RPC_S_SERVER_TOO_BUSY - The server is too busy to accept the clients
        bind request.

    RPC_S_UNKNOWN_AUTHN_TYPE - The server does not support the authentication
        type specified by the client.

--*/
{
    rpcconn_bind_ack PAPI *pBindAck;
    rpcconn_bind_nak PAPI *pBindNak;
    p_result_list_t PAPI *pResults;
    int port_spec_plus_pad;
    unsigned int SecondaryEndpointLength;
    unsigned char PAPI * Pointer;

    // The common header of the packet has already been validated and data
    // converted, if necessary, by whoever called this method.

    if (   (Buffer->PTYPE == rpc_bind_ack)
        || (Buffer->PTYPE == rpc_alter_context_resp))
        {
        FailureCount = 0;

        // The bind_ack and alter_context_resp packets are the same.

        pBindAck = (rpcconn_bind_ack PAPI *) Buffer;

        // We need to convert the max_xmit_frag, max_recv_frag, and
        // assoc_group_id fields of the packet.

        if ( DataConvertEndian(Buffer->drep) != 0 )
            {
            ByteSwapShort(pBindAck->max_xmit_frag);
            ByteSwapShort(pBindAck->max_recv_frag);
            ByteSwapLong(pBindAck->assoc_group_id);
            ByteSwapShort(pBindAck->sec_addr_length);
            }

        if ( Buffer->PTYPE == rpc_bind_ack )
            {
            CConnection->SetMaxFrag(pBindAck->max_xmit_frag,
                    pBindAck->max_recv_frag);
            }

        BufferLength -= sizeof(rpcconn_bind_ack);
        Pointer = (unsigned char PAPI *) (pBindAck + 1);

        if ( pBindAck->sec_addr_length )
            {
            SecondaryEndpointLength = pBindAck->sec_addr_length;

            // The secondary address length is two bytes long.  We want
            // to align the total of the secondary address length itself,
            // the the secondary address.  Hence, the length of the secondary
            // address and the necessary pad is calculated below.  Think
            // very carefully before changing this piece of code.

            port_spec_plus_pad = SecondaryEndpointLength +
                                 Pad4(SecondaryEndpointLength + 2);

            if ( BufferLength < (unsigned int) port_spec_plus_pad )
                {
                return(RPC_S_PROTOCOL_ERROR);
                }

            if ( SecondaryEndpoint != 0 )
                {
                delete SecondaryEndpoint;
                }

            SecondaryEndpoint = new unsigned char[SecondaryEndpointLength];

            if ( SecondaryEndpoint == 0 )
                return(RPC_S_OUT_OF_MEMORY);

            RpcpMemoryCopy(SecondaryEndpoint, Pointer, SecondaryEndpointLength);
            if ( DataConvertCharacter(Buffer->drep) != 0 )
                {
                ConvertStringEbcdicToAscii(SecondaryEndpoint);
                }

            BufferLength -= port_spec_plus_pad;
            Pointer = Pointer + port_spec_plus_pad;
            }
        else
            {
            Pointer = Pointer + 2;
            BufferLength -= 2;
            }

        pResults = (p_result_list_t PAPI*) Pointer;
        if ( BufferLength < sizeof(p_result_list_t) )
            {
            return(RPC_S_PROTOCOL_ERROR);
            }

        if ( DataConvertEndian(Buffer->drep) != 0 )
            {
            ByteSwapShort(pResults->p_results[0].result);
            ByteSwapShort(pResults->p_results[0].reason);
            ByteSwapSyntaxId(&(pResults->p_results[0].transfer_syntax));
            }

        if ( pResults->n_results != 1 )
            {
            return(RPC_S_UNSUPPORTED_TRANS_SYN);
            }

        if ( pResults->p_results[0].result != acceptance )
            {
            if ( pResults->p_results[0].result != provider_rejection )
                {
                return(RPC_S_CALL_FAILED_DNE);
                }

            if ( pResults->p_results[0].reason
                        == abstract_syntax_not_supported)
                {
                return(RPC_S_UNKNOWN_IF);
                }

            if ( pResults->p_results[0].reason ==
                            proposed_transfer_syntaxes_not_supported )
                {
                return(RPC_S_UNSUPPORTED_TRANS_SYN);
                }

            if ( pResults->p_results[0].reason == local_limit_exceeded )
                {
                return(RPC_S_SERVER_TOO_BUSY);
                }

            return(RPC_S_CALL_FAILED_DNE);
            }

        // Once we reach here, we know that the binding has been excepted,
        // so we can go ahead and set the association group id.
        // warning: as soon as the AssocGroupId is set, threads 
        // will start sending the bind without acquiring the mutex
        if ( Buffer->PTYPE == rpc_bind_ack )
            {
            AssocGroupId = pBindAck->assoc_group_id;
            }
        }
    else if (Buffer->PTYPE == rpc_bind_nak)
        {

        //
        // BUGBUG: Fix Osfpcket.cxx to pack!
        // 21 bytes is the minimum BINDNAK
        //
        if (BufferLength < 21)
            {
            return(RPC_S_PROTOCOL_ERROR);
            }

        pBindNak = (rpcconn_bind_nak PAPI *) Buffer;

        if ( DataConvertEndian(Buffer->drep) != 0 )
            {
            ByteSwapShort(pBindNak->provider_reject_reason);
            }

        if (   (pBindNak->provider_reject_reason == temporary_congestion)
            || (pBindNak->provider_reject_reason
                    == local_limit_exceeded_reject))
            {
            return(RPC_S_SERVER_TOO_BUSY);
            }

        if ( pBindNak->provider_reject_reason
                    == protocol_version_not_supported )
            {
            return(RPC_S_PROTOCOL_ERROR);
            }

        if ( pBindNak->provider_reject_reason
                    == authentication_type_not_recognized )
            {
            return(RPC_S_UNKNOWN_AUTHN_SERVICE);
            }

        if ( pBindNak->provider_reject_reason
                    == invalid_checksum )
            {
            return(RPC_S_ACCESS_DENIED);
            }

        FailureCount++ ;
        if (FailureCount >= 5)
            {
            AssociationValid = FALSE ;
            }

        return(RPC_S_CALL_FAILED_DNE);
        }
    else
        {
        return(RPC_S_PROTOCOL_ERROR);
        }

    return(RPC_S_OK);
}


RPC_STATUS
OSF_CASSOCIATION::PingServer(
    )
{
    RPC_STATUS RpcStatus ;
    OSF_CCONNECTION * CConnection;

    RpcStatus = RPC_S_OK;

    CConnection = new(0, RpcClientInfo->SizeOfConnection)
                  TRANS_CCONNECTION(
                        RpcClientInfo,
                        DceBinding->InqNetworkAddress(),
                        DceBinding->InqEndpoint(),
                        DceBinding->InqNetworkOptions(),
                        DceBinding->InqRpcProtocolSequence(),
                        &RpcStatus,
                        0,
                        NULL
                        );

    if (CConnection != 0)
        {
        delete CConnection ;
        }

    return RpcStatus ;
}


RPC_STATUS
OSF_CASSOCIATION::ActuallyDoBinding (
    IN PRPC_SYNTAX_IDENTIFIER InterfaceSyntax,
    IN PRPC_SYNTAX_IDENTIFIER TransferSyntaxes,
    IN unsigned int TransferSyntaxCount,
    OUT OSF_CCONNECTION * PAPI * TheCConnection,
    IN int PresentContext,
    IN unsigned int Timeout,
    IN CLIENT_AUTH_INFO * ClientAuthInfo,
    IN unsigned long MyAssocGroupId
    )
/*++

Routine Description:

    We need to find or create a connection which supports the specified
    interface and transfer syntax(es).

Arguments:

    InterfaceSyntax - Supplies a description of the interface to which
        we wish to bind.

    TransferSyntaxes - Supplies a list of one or more transfer syntaxes
        which the client stub is willing to talk with the server stub.

    TransferSyntaxCount - Supplies the number of transfer syntaxes; this
        must be at least one.

    TheCConnection - Returns the allocated connection assuming that a
        connection was obtained and binding occured successfully.

    PresentContext - Supplies the presentation context to be used for
        the binding.

    Timeout - Supplies the timeout to be used if an attempt is made to
        create a new connection.  See the documentation for
        RpcMgmtSetComTimeout for a description of the possible values
        for the timeout.

    ClientAuthInfo - Supplies the authentication and authorization
        information required for the connection.

Return Value:

    RPC_S_OK - We successfully bound with the server.  Things should
        be all ready now to make remote procedure calls.

    RPC_S_SERVER_UNAVAILABLE - We are unable to connect with the server.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to bind.

    RPC_S_OUT_OF_RESOURCES - The transport has insufficient resources
        inorder to make a connection with the server.

    RPC_S_SERVER_TOO_BUSY - The server is there, but it is too busy to
        talk with us right now.

    RPC_S_ACCESS_DENIED - The client is unable to make a connection with
        the server for security reasons.

    RPC_S_INVALID_NETWORK_OPTIONS - The supplied network options are
        invalid; see a description of the particular transport interface
        module for an explination.

    RPC_P_CONNECTION_CLOSED - The connection closed while the client was
        attempting to send the bind packet or waiting to receive the
        response.

    RPC_S_PROTOCOL_ERROR - A protocol error occured; this will typically
        be because of a protocol mismatch with the server.

    RPC_S_UNSUPPORTED_TRANS_SYN - The transfer syntax supplied by the client
        is not supported by the server.

    RPC_S_UNKNOWN_IF - The interface to which the client wished to bind is not
        supported by the server.

    RPC_S_UNKNOWN_AUTHN_TYPE - The server does not support the authentication
        type specified by the client.

    RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint supplied is in an incorrect
        format for the particular protocol sequence.

--*/
{
    OSF_CCONNECTION * CConnection;
    int NewConnectionFlag = 0;
    RPC_STATUS RpcStatus;
    void PAPI * Buffer = 0;
    unsigned int BufferLength = 0;

    ASSERT( TransferSyntaxCount >= 1 );

    // To start off with, we want to try and find an existing connection
    // to use.  We need to try and find one which supports the requested
    // authentication and authorization information.

    *TheCConnection = Nil;
    RequestGlobalMutex();
    FreeConnections.Reset();
    while ( (CConnection = FreeConnections.Next()) != 0 )
        {
        if ( CConnection->SupportedAuthInfo(ClientAuthInfo) != 0 )
            {
            break;
            }
        }

    if ( CConnection == 0 )
        {
        // There are no free connections, so we go ahead and try to
        // create one.

        NewConnectionFlag = 1;

        // Once an initial connection has been established, calls will
        // not be timed out any more aggressively than the default.

        if ( OpenConnectionCount > 0 )
            {
            if ( Timeout < RPC_C_BINDING_DEFAULT_TIMEOUT )
                {
                Timeout = RPC_C_BINDING_DEFAULT_TIMEOUT;
                }
            }
        ClearGlobalMutex();

        // If the timeout specified is RPC_C_BINDING_INFINITE_TIMEOUT, we
        // need to loop trying to connect as long as the result is
        // RPC_S_SERVER_UNAVAILABLE or RPC_S_SERVER_TOO_BUSY.

        for (;;)
            {
            // BUGBUG - Secondary endpoint support is needed.

            RpcStatus = RPC_S_OK;

            CConnection = new(0, RpcClientInfo->SizeOfConnection)
                          TRANS_CCONNECTION(
                                RpcClientInfo,
                                DceBinding->InqNetworkAddress(),
                                DceBinding->InqEndpoint(),
                                DceBinding->InqNetworkOptions(),
                                DceBinding->InqRpcProtocolSequence(),
                                &RpcStatus,
                                Timeout,
                                ClientAuthInfo
                                );

            ASSERT(   (CConnection == 0)
                   || (RpcStatus == RPC_S_PROTSEQ_NOT_SUPPORTED)
                   || (RpcStatus == RPC_S_OK)
                   || (RpcStatus == RPC_S_SERVER_UNAVAILABLE)
                   || (RpcStatus == RPC_S_OUT_OF_MEMORY)
                   || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
                   || (RpcStatus == RPC_S_SERVER_TOO_BUSY)
                   || (RpcStatus == RPC_S_ACCESS_DENIED)
                   || (RpcStatus == RPC_S_INVALID_NET_ADDR)
                   || (RpcStatus == RPC_S_INVALID_ENDPOINT_FORMAT)
                   || (RpcStatus == RPC_S_INVALID_NETWORK_OPTIONS));

            if ( CConnection == 0 )
               {
               RpcStatus = RPC_S_OUT_OF_MEMORY;
               }

            if ( RpcStatus == RPC_S_OK )
               {
               break;
               }

           if ( CConnection != 0 )
               {
               delete CConnection;
               }

           if ( Timeout != RPC_C_BINDING_INFINITE_TIMEOUT )
               {
               return(RpcStatus);
               }

           if (   (RpcStatus != RPC_S_SERVER_UNAVAILABLE)
               && (RpcStatus != RPC_S_SERVER_TOO_BUSY) )
               {
               return(RpcStatus);
               }
           }

        CConnection->SetAssociation(this);
        }
    else
        {
        // There is a connection available, so we grab it from the
        // set of free connections.

        FreeConnections.Delete(CConnection->AssociationKey);
        ClearGlobalMutex();
        }

    RpcStatus = CConnection->SendBindPacket(InterfaceSyntax,
            TransferSyntaxes, TransferSyntaxCount, PresentContext,
            MyAssocGroupId, (NewConnectionFlag ? rpc_bind : rpc_alter_context),
            &Buffer, &BufferLength);

    //Now mark this connection as a part of the pool
    if ( NewConnectionFlag != 0)
        {
        RequestGlobalMutex();
        OpenConnectionCount += 1;
        ClearGlobalMutex();
        }

    if ( RpcStatus != RPC_S_OK )
        {
#if DBG
         if ( (RpcStatus != RPC_S_OUT_OF_MEMORY)
               && (RpcStatus != RPC_S_OUT_OF_RESOURCES)
               && (RpcStatus != RPC_P_CONNECTION_SHUTDOWN)
               && (RpcStatus != RPC_P_CONNECTION_CLOSED)
               && (RpcStatus != RPC_S_UUID_NO_ADDRESS)
               && (RpcStatus != RPC_S_ACCESS_DENIED)
               && (RpcStatus != RPC_S_SEC_PKG_ERROR) )
             {
             PrintToDebugger("RPC SendBindPacket Returned [%x] \n",RpcStatus);
             }
#endif
        ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
               || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
               || (RpcStatus == RPC_P_CONNECTION_SHUTDOWN)
               || (RpcStatus == RPC_P_CONNECTION_CLOSED)
               || (RpcStatus == RPC_S_UUID_NO_ADDRESS)
               || (RpcStatus == RPC_S_ACCESS_DENIED)
               || (RpcStatus == RPC_S_SEC_PKG_ERROR) );
        delete CConnection;
        return(RpcStatus);
        }

    // We loop around ignoring shutdown packets until we get a response.

    for (;;)
        {

        //If there is security, we need to save away the packet
        //and the header. This is because we may have to byte swap stuff
        //The correct check here should be
        //if ((AluthLevel != NONE) && (ByteSwapEndian == TRUE))
        //I am avoiding the second check for now so that we can test
        //this code w/o a big endian machine
        //another potential opt. is not saving the whole packet!
        //but just header + result_list..
        //again the opt. will be done once all this starts working well


        if ( CConnection->AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE )
           {

           //Security is on.. save the packet before we byte swap
           if (CConnection->SavedHeaderSize < BufferLength)
              {

              if (CConnection->SavedHeader != 0)
                 {
                 ASSERT(CConnection->SavedHeaderSize != 0);
                 RpcpFarFree(CConnection->SavedHeader);
                 }

              CConnection->SavedHeader = RpcpFarAllocate(BufferLength);
              if (CConnection->SavedHeader == 0)
                {
                CConnection->TransFreeBuffer(Buffer);
                delete CConnection;
                return(RPC_S_OUT_OF_MEMORY);
                }
              CConnection->SavedHeaderSize = BufferLength;
              RpcpMemoryCopy(CConnection->SavedHeader, Buffer, BufferLength);

              }

           else
              {
              RpcpMemoryCopy(CConnection->SavedHeader, Buffer, BufferLength);
              }

           }

        RpcStatus = ValidatePacket((rpcconn_common PAPI *) Buffer,
                BufferLength);
        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT( RpcStatus == RPC_S_PROTOCOL_ERROR );
            CConnection->TransFreeBuffer(Buffer);
            delete CConnection;
            return(RPC_S_PROTOCOL_ERROR);
            }

        if ( NewConnectionFlag != 0 )
            {
            // Since this is a new connection, the packet we receive
            // must be either a bind_ack or a bind_nak; anything else
            // is an error.

            if (   (((rpcconn_common PAPI *) Buffer)->PTYPE != rpc_bind_ack)
                && (((rpcconn_common PAPI *) Buffer)->PTYPE != rpc_bind_nak)
                && (((rpcconn_common PAPI *) Buffer)->PTYPE != rpc_shutdown))
                {
                return(RPC_S_PROTOCOL_ERROR);
                }

            if (((rpcconn_common PAPI *) Buffer)->PTYPE != rpc_shutdown)
                {
                break;
                }
            }

        if ( ((rpcconn_common PAPI *) Buffer)->PTYPE == rpc_alter_context_resp )
            {
            break;
            }

        if ( ((rpcconn_common PAPI *) Buffer)->PTYPE == rpc_shutdown )
            {
            ShutdownRequested();
            }
        else
            {
            CConnection->TransFreeBuffer(Buffer);
            delete CConnection;
            return(RPC_S_PROTOCOL_ERROR);
            }

        CConnection->TransFreeBuffer(Buffer);

        RpcStatus = CConnection->TransReceive(&Buffer,&BufferLength);
        ASSERT( (RpcStatus != RPC_S_CALL_CANCELLED)
              &&(RpcStatus != RPC_P_RECEIVE_ALERTED) );

        if ( RpcStatus != RPC_S_OK )
            {
            ASSERT(   (RpcStatus == RPC_S_OUT_OF_MEMORY)
                   || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
                   || (RpcStatus == RPC_P_RECEIVE_FAILED)
                   || (RpcStatus == RPC_P_CONNECTION_CLOSED));
            CConnection->TransFreeBuffer(Buffer);
            delete CConnection;
            if ( RpcStatus == RPC_P_RECEIVE_FAILED )
                {
                return(RPC_P_CONNECTION_CLOSED);
                }
            return(RpcStatus);
            }
        }

    // We subtract from BufferLength the length of the authentication
    // information; that way ProcessBinAckOrNak can check the length
    // correctly, whether or not there is security information.
    if (MyAssocGroupId == 0)
        {
        RpcStatus = ProcessBindAckOrNak((rpcconn_common PAPI *) Buffer,
                BufferLength -  ((rpcconn_common PAPI *) Buffer)->auth_length,
                CConnection);
        }
    else
        {
        AssociationMutex.Request() ;

        RpcStatus = ProcessBindAckOrNak((rpcconn_common PAPI *) Buffer,
                BufferLength -  ((rpcconn_common PAPI *) Buffer)->auth_length,
                CConnection);

        AssociationMutex.Clear() ;
        }

    // Let the connection deal with 3rd leg authentication if it is
    // necessary.  Note that OSF_CCONNECTION::MaybeDo3rdLegAuth knows
    // that we are only doing authentication on the initial bind packets.

    if ( RpcStatus == RPC_S_OK )
        {
        RpcStatus = CConnection->MaybeDo3rdLegAuth(Buffer, BufferLength);

        ASSERT(   (RpcStatus == RPC_S_OK)
               || (RpcStatus == RPC_S_PROTOCOL_ERROR)
               || (RpcStatus == RPC_S_OUT_OF_MEMORY)
               || (RpcStatus == RPC_S_OUT_OF_RESOURCES)
               || (RpcStatus == RPC_P_CONNECTION_CLOSED)
               || (RpcStatus == RPC_S_SEC_PKG_ERROR)
               || (RpcStatus == RPC_S_ACCESS_DENIED) );
        }

    CConnection->TransFreeBuffer(Buffer);

    if ( RpcStatus == RPC_S_OK )
        {
        if ( CConnection->AddPContext(PresentContext) != 0 )
            RpcStatus = RPC_S_OUT_OF_RESOURCES;
        }

    if ( RpcStatus != RPC_S_OK )
        {
        if ( NewConnectionFlag == 0 )
            {
            // We failed to bind, but since the connection is not a new
            // connection, we can stick it back into the pool of free
            // connections.

            if ( FreeConnection(CConnection) != 0 )
                {
                delete CConnection;
                }
            }
        else
            {

            //
            // If RpcStatus == DNE, it means that we probably got a B-NAK
            // [Also note this is a new connection]
            // If we were using security, [Auth Level != NONE]
            // delete this connection, and return RPC_P_CONNECTION_SHUTDOWN
            // which will cause BH->GetBuffer code to retry 2 more times
            //
            if (RpcStatus == RPC_S_CALL_FAILED_DNE)
/*
               What the hell .. retry failures over non-authenticated
               binds also.. the ones we retry over are bind naks with
               unspecifed reason .. one day we can get OSF to send
               bind_nk with reason assoc_group_shutdown..
               && (CConnection->AuthInfo.AuthenticationLevel
                                             != RPC_C_AUTHN_LEVEL_NONE))
*/
               {
               RpcStatus = RPC_P_CONNECTION_SHUTDOWN;
               }

            delete CConnection;
            }
        }
    else
        {
        *TheCConnection = CConnection;
#ifdef NTENV
        if (CConnection->PendingAlert)
            {
            RpcCancelThread(GetCurrentThread());
            CConnection->PendingAlert = FALSE;
            }
#endif
        }

    return(RpcStatus);
}

#if 0

void
OSF_CASSOCIATION::CleanupAuthenticatedConnections(
   IN CLIENT_AUTH_INFO *ClientAuthInfo
   )
{
    OSF_CCONNECTION *CConnection;
    OSF_BINDING     *Binding;

    //
    // Temp fix for Digital, we need to cleanup Cconnections
    // holding the security context associated with a binding
    // when that binding is destroyed.
    //
    // This needs a better soln, this has holes with binding copy.

    if (ClientAuthInfo == 0) return;

    RequestGlobalMutex();

    FreeConnections.Reset();
    while ((CConnection = FreeConnections.Next()) != 0)
        {
        if (CConnection->SupportedAuthInfo(ClientAuthInfo) != 0)
            {
            FreeConnections.Delete(CConnection->AssociationKey);
            delete CConnection;
            }
        }

    ClearGlobalMutex();
}

#endif

void
OSF_CASSOCIATION::UnBind (
    )
{
    RequestGlobalMutex();
    BindHandleCount -= 1;

    if (BindHandleCount == 0)
        {
        AssociationDict->Delete(Key);
        ClearGlobalMutex();

        delete this ;
        }
    else
        {
        ClearGlobalMutex();
        }
}


OSF_BINDING *
OSF_CASSOCIATION::FindOrCreateOsfBinding (
    IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation
    )
/*++

Routine Description:

    This method gets called to find the osf binding (a dictionary
    entry) corresponding to the specified rpc interface information.
    The caller of this routine must be holding (ie. requested) the
    association mutex.

Arguments:

    RpcInterfaceInformation - Supplies the interface information for
        which we are looking for an osf binding object.

Return Value:

    An osf binding object corresponding to the interface information
    specified will be returned unless we run out of memory, in which
    case zero will be returned.

--*/
{
    OSF_BINDING * Binding;

    // First we search for an existing presentation context
    // corresponding to the specified interface information.  Otherwise,
    // we create a new presentation context.

    Bindings.Reset();
    while ((Binding = Bindings.Next()) != 0)
        {
        if (Binding->CompareWithRpcInterfaceInformation(
                RpcInterfaceInformation) == 0)
            return(Binding);
        }

    Binding = new OSF_BINDING(RpcInterfaceInformation);

    if (Binding == 0)
        return(0);

    Binding->PresentContext = Bindings.Insert(Binding);
    if (Binding->PresentContext == -1)
        {
        delete Binding;
        return(0);
        }

    return(Binding);
}


RPC_STATUS
OSF_CASSOCIATION::AllocateConnection (
    IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
    OUT OSF_CCONNECTION * PAPI * CConnection,
    IN unsigned int Timeout,
    IN CLIENT_AUTH_INFO * ClientAuthInfo
    )
/*++

Routine Description:

    In this method, we allocate a connection supporting the requested
    interface information.  This means that first we need to find the
    presentation context corresponding to the requested interface
    interface.  Then we search for an existing connection supporting
    the presentation context, and then we try and create a new
    connection.

Arguments:

    RpcInterfaceInformation - Supplies the information which describes
        the interface so we can find or create a connection which has
        been bound to the correct interface.

    CConnection - Returns the allocated connection.

    Timeout - Supplies the timeout to be used if an attempt is made to
        create a new connection.  See the documentation for
        RpcMgmtSetComTimeout for a description of the possible values
        for the timeout.

    ClientAuthInfo - Supplies the authentication and authorization
        information required for the connection.

Return Value:

    RPC_S_OK - The operation completed successfully.

    RPC_S_OUT_OF_MEMORY - Insufficient memory is available to create
        objects necessary to allocate a connection.

    All of the return values from OSF_CASSOCIATION::ActuallyDoBinding will
    be passed through unchanged.

--*/
{
    OSF_BINDING * Binding;
    RPC_STATUS RpcStatus;
    unsigned long CallIdToUse;
    int MutexHeld = 0;
    unsigned long MyAssocGroupId ;

    // To begin with, we need to obtain the presentation context
    // corresponding to the specified interface information.

    RequestGlobalMutex();
    Binding = FindOrCreateOsfBinding(RpcInterfaceInformation);
    if ( Binding == 0 )
        {
        ClearGlobalMutex();
        return(RPC_S_OUT_OF_MEMORY);
        }

    // Ok, now we search for an available connection supporting the
    // requested presentation context.

    FreeConnections.Reset();
    while ( (*CConnection = FreeConnections.Next()) != 0 )
        {
        if (   ((*CConnection)->SupportedAuthInfo(ClientAuthInfo) != 0)
            && ((*CConnection)->SupportedPContext(Binding->PresentContext)
                != 0) )
            {
            FreeConnections.Delete((*CConnection)->AssociationKey);
            (*CConnection)->CallId = CallIdCounter++;
            ClearGlobalMutex();
            (*CConnection)->ActivateConnection(Binding->PresentContext,
                    (PRPC_DISPATCH_TABLE)
                    RpcInterfaceInformation->DispatchTable);
            return(RPC_S_OK);
            }
        }
    CallIdToUse = CallIdCounter++;
    ClearGlobalMutex();

    AssociationMutex.Request();
    MyAssocGroupId = AssocGroupId ;

    if (AssocGroupId != 0)
        {
        AssociationMutex.Clear() ;
        }
    else
        {
        MutexHeld = 1 ;
        }

    RpcStatus = ActuallyDoBinding(
            &(Binding->RpcInterfaceInformation.InterfaceId),
            &(Binding->RpcInterfaceInformation.TransferSyntax), 1,
            CConnection, Binding->PresentContext,
            Timeout, ClientAuthInfo,  MyAssocGroupId);

    if (MutexHeld)
        {
        AssociationMutex.Clear();
        }

    if ( RpcStatus == RPC_S_OK )
        {
        ASSERT((*CConnection) != 0);
        (*CConnection)->ActivateConnection(Binding->PresentContext,
                (PRPC_DISPATCH_TABLE) RpcInterfaceInformation->DispatchTable);
        (*CConnection)->CallId = CallIdToUse;
        }
    return(RpcStatus);
}

int
OSF_CASSOCIATION::FreeConnection (
    IN OSF_CCONNECTION * CConnection
    )
{
    if (!AssociationValid)
        {
        delete CConnection ;
        return 0;
        }
    else
        {
        if ( CConnection->InquireLastTimeUsed() != 0 )
            {
            CConnection->SetLastTimeUsedToNow();
            }
    
        RequestGlobalMutex();
        if ((CConnection->AssociationKey =
                    FreeConnections.Insert(CConnection)) == -1)
            {
            ClearGlobalMutex();
            return(-1);
            }
    
        ClearGlobalMutex();
        }
    return(0);
}

void
OSF_CASSOCIATION::ShutdownRequested (
    )
{
    OSF_CCONNECTION * CConnection;

    RequestGlobalMutex();
    FreeConnections.Reset();
    while ((CConnection = FreeConnections.Next()) != Nil)
        {
        FreeConnections.Delete(CConnection->AssociationKey);
        delete CConnection;
        }

    //
    // We have received a shutdown notice
    // Just reset the AssocGrpId to 0 [looks like an over kill.. but...]
    // We need this because If another thread is trying a bind
    // he will [or she will] have bumped up the ConnectionCount
    // On his/her retry
    //


    ClearGlobalMutex();
}


RPC_STATUS
OSF_CASSOCIATION::ToStringBinding (
    OUT RPC_CHAR PAPI * PAPI * StringBinding,
    IN RPC_UUID * ObjectUuid
    )
/*++

Routine Description:

    We need to convert the binding handle into a string binding.  If the
    binding handle has not yet been used to make a remote procedure
    call, then we can just use the information in the binding handle to
    create the string binding.  Otherwise, we need to ask the association
    to do it for us.

Arguments:

    StringBinding - Returns the string representation of the binding
        handle.

    ObjectUuid - Supplies the object uuid of the binding handle which
        is requesting that we create a string binding.

Return Value:

    RPC_S_OK - The operation completed successfully.

    RPC_S_OUT_OF_MEMORY - We do not have enough memory available to
        allocate space for the string binding.

--*/
{
    *StringBinding = DceBinding->StringBindingCompose(ObjectUuid);
    if (*StringBinding == 0)
        return(RPC_S_OUT_OF_MEMORY);
    return(RPC_S_OK);
}

/* --------------------------------------------------------------------
-------------------------------------------------------------------- */

// This value specifies the minimum amount of time in seconds to wait before
// deleting an idle connection.

#define CLIENT_DISCONNECT_TIME 10


OSF_CASSOCIATION *
FindOrCreateAssociation (
    IN DCE_BINDING * DceBinding,
    IN RPC_CLIENT_TRANSPORT_INFO * TransportInterface
    )
/*++

Routine Description:

    This routine finds an existing association supporting the requested
    DCE binding, or create a new association which supports the
    requested DCE binding.  Ownership of the passed DceBinding pass
    to this routine.

Arguments:

    DceBinding - Supplies binding information; ownership of this object
        passes to this routine.

    TransportInterface - Supplies a pointer to the data structure which
        describes a loadable transport.

Return Value:

    An association which supports the requested binding will be returned;
    Otherwise, zero will be returned, indicating insufficient memory.

--*/
{
    OSF_CASSOCIATION * CAssociation;
    RPC_STATUS RpcStatus = RPC_S_OK;

    // We start be looking in the dictionary of existing associations
    // to see if there is one supporting the binding information specified.

    RequestGlobalMutex();
    AssociationDict->Reset();
    while ( (CAssociation = AssociationDict->Next()) != 0 )
        {
        if (CAssociation->IsValid() &&
            CAssociation->CompareWithDceBinding(DceBinding) == 0)
            {
            CAssociation->IncrementCount();
            ClearGlobalMutex();

            delete DceBinding;

            return(CAssociation);
            }
        }

    CAssociation = new OSF_CASSOCIATION(DceBinding, TransportInterface,
            &RpcStatus);

    if ( RpcStatus != RPC_S_OK )
        {
        ASSERT(CAssociation != 0);
        CAssociation->DceBinding = 0;
        delete CAssociation;
        CAssociation = 0;
        }

    if (CAssociation != 0)
        CAssociation->Key = AssociationDict->Insert(CAssociation);

    ClearGlobalMutex();

    // Finally, we need to notify the protocol independent layer that
    // the code to delete idle connections should be executed periodically.
    // We divide by two to reduce the amount of extra time an idle
    // connection lives beyond the minimum.

    GarbageCollectionNeeded(CLIENT_DISCONNECT_TIME / 2);

    return(CAssociation);
}


int
OSF_CASSOCIATION::CompareWithDceBinding (
    IN DCE_BINDING * DceBinding
    )
/*++

Routine Description:

    This routine compares the specified binding information with the
    binding information in the object, this.

Arguments:

    DceBinding - Supplies the binding information to compare against
        the binding information in this.

Return Value:

    Zero will be returned if the specified binding information,
    DceBinding, is the same as in this.  Otherwise, non-zero will be
    returned.

--*/
{
    int Result;

    if ((Result = this->DceBinding->Compare(DceBinding)) != 0)
        return(Result);

#ifdef WIN

    if ( (Result = TaskId - GetCurrentTask()) != 0 )
        {
        return(Result);
        }

#endif // WIN

    return(0);
}


OSF_CCONNECTION *
OSF_CASSOCIATION::FindIdleConnection (
    IN unsigned long MinimumIdleSeconds,
    IN unsigned long CurrentTime
    )
/*++

Routine Description:

    This routine is used to find a connection which has been idle more
    than the minimum number of seconds specified.  If one is found, it
    is removed from the set of free connections and returned.  The
    global mutex will be held when this routine is called.

Arguments:

    MinimumIdleSeconds - Supplies the minimum number of seconds a connection
        must be idle to be returned.

    CurrentTime - Supplies the current time in seconds.

Return Value:

    If an idle connection is found, it will be returned; otherwise, zero
    will be returned.

--*/
{
    OSF_CCONNECTION * CConnection;

    // If we need to maintain context with server, we do not want to close
    // the last open connection.  To be on the safe side, we will make
    // sure that there is at least one free connection.

    if ( MaintainContext != 0 )
        {
        if ( FreeConnections.Size() <= 1 )
            {
            return(0);
            }
        }

    FreeConnections.Reset();
    while ( (CConnection = FreeConnections.Next()) != 0 )
        {
        if ( CConnection->InquireLastTimeUsed() == 0 )
            {
            CConnection->SetLastTimeUsedToNow();
            }
        else if ( CurrentTime - CConnection->InquireLastTimeUsed()
                    > MinimumIdleSeconds )
            {
            FreeConnections.Delete(CConnection->AssociationKey);

            return(CConnection);
            }
        }
    return(0);
}


void
OsfDeleteIdleConnections (
    void
    )
/*++

Routine Description:

    This routine will be called to delete connections which have been
    idle for a certain amount of time.  We need to be careful of a couple
    of things in writing this routine:

    (1) We dont want to grab the global mutex for too long, because this
        will prevent threads which are trying to do real work from doing
        it.

    (2) We dont want to be holding the global mutex when we delete the
        connection.

--*/
{
    OSF_CASSOCIATION * Association;
    OSF_CCONNECTION * CConnection;
    unsigned int IdleConnectionFound;
    unsigned long CurrentTime;

    do
        {
        IdleConnectionFound = 0;

        RequestGlobalMutex();

        CurrentTime = CurrentTimeInSeconds();

        AssociationDict->Reset();
        while ( (Association = AssociationDict->Next()) != 0 )
            {
            // The architecture says that the client should disconnect
            // connections which have been idle too long.

            CConnection = Association->FindIdleConnection(
                    CLIENT_DISCONNECT_TIME, CurrentTime);

            if ( CConnection != 0 )
                {
                ClearGlobalMutex();

                delete CConnection;
                IdleConnectionFound = 1;

                RequestGlobalMutex();
                break;
                }
            }
        ClearGlobalMutex();
        }
    while ( IdleConnectionFound != 0);
}


int
InitializeRpcProtocolOfsClient (
    )
/*++

Routine Description:

    We perform loadtime initialization necessary for the code in this
    file.  In particular, it means we allocate the association dictionary
    and the association dictionary mutex.

Return Value:

    Zero will be returned if initialization completes successfully;
    otherwise, non-zero will be returned.

--*/
{
    AssociationDict = new OSF_CASSOCIATION_DICT;
    if (AssociationDict == 0)
        return(1);

    return(0);
}


void *
OsfClientMapRpcProtocolSequence (
    IN RPC_CHAR PAPI * RpcProtocolSequence,
    OUT RPC_STATUS PAPI * Status
    )
/*++

Routine Description:

    This routine is used to determine whether a given rpc protocol sequence
    is supported, and to get a pointer to the transport interface, so we
    do not have to keep looking it up.

Arguments:

    RpcProtocolSequence - Supplies the rpc protocol sequence.

    Status - Returns the status of the operation.  This will be one
        of the following values.

        RPC_S_OK - The rpc protocol sequence is supported.

        RPC_S_PROTSEQ_NOT_SUPPORTED - The rpc protocol sequence is
            not supported.

        RPC_S_OUT_OF_MEMORY - There is insufficient memory to perform
            the operation.

Return Value:

    A pointer to the transport interface is returned.  This pointer
    must not be dereferenced by the caller.

--*/
{
    RPC_CHAR * DllName;
    RPC_CLIENT_TRANSPORT_INFO * RpcClientInfo;

    *Status= RpcConfigMapRpcProtocolSequence(0, RpcProtocolSequence, &DllName);
    if ( *Status != RPC_S_OK )
        {
        return(0);
        }

    RpcClientInfo = LoadableTransportClientInfo(DllName, RpcProtocolSequence,
            Status);
    delete DllName;

    if ( *Status != RPC_S_OK )
        {
        return(0);
        }
    return(RpcClientInfo);
}


BINDING_HANDLE *
OsfCreateBindingHandle (
    )
/*++

Routine Description:

    This routine does exactly one thing: it creates a binding handle of
    the appropriate type for the osf connection protocol module.

Return Value:

    A new binding handle will be returned.  Zero will be returned if
    insufficient memory is available to create the binding handle.

--*/
{
   BINDING_HANDLE * BindingHandle;
   RPC_STATUS RpcStatus = RPC_S_OK;

   BindingHandle = new OSF_BINDING_HANDLE(&RpcStatus);
   if ( RpcStatus != RPC_S_OK )
       {
       delete BindingHandle;
       return(0);
       }
   return(BindingHandle);
}

#ifdef NTENV
RPC_STATUS RPC_ENTRY
I_RpcIOAlerted (
    IN RPC_TRANSPORT_CONNECTION TransConnection
    )
{


 unsigned long Timeout;
 TRANS_CCONNECTION *Connection = InqTransCConnection(TransConnection);

 //
 //We dont send alerts/orphans or anything in certain states
 //These states are  marked by setting AlertsEnabled to 0.
 //
 if (Connection->AlertsEnabled == 0)
    {
    return (RPC_S_OK);
    }

 if ( (Timeout = ThreadGetRpcCancelTimeout()) == 0)
    {
    Connection->SendOrphan();
    return(RPC_S_CALL_CANCELLED); //Ask Transport To Cancel IO
    }

 Connection->SendAlert();
 Connection->PendingAlert = FALSE;
 Connection->TransSetTimeout(Timeout);

 return(RPC_S_OK);

}
#endif

extern "C" {


RPC_STATUS RPC_ENTRY
OsfTowerConstruct(
    IN char PAPI * ProtocolSeq,
    IN char PAPI * Endpoint,
    IN char PAPI * NetworkAddress,
    OUT unsigned short PAPI * Floors,
    OUT unsigned long PAPI * ByteCount,
    OUT unsigned char PAPI * PAPI * Tower
    )
/*++

Routine Description:

    This routine constructs and returns the upper floors of a tower.
    It invokes the appropriate loadable transport and has them construct
    it.

Return Value:


--*/
{

  RPC_CLIENT_TRANSPORT_INFO PAPI * ClientInfo;
  RPC_STATUS Status = RPC_S_OK;
  RPC_CHAR PAPI * Pseq;
#ifdef NTENV
  UNICODE_STRING UnicodeStr;
#endif

#ifdef NTENV

  if (Status = AnsiToUnicodeString((unsigned char PAPI *)ProtocolSeq,
                                    &UnicodeStr))
     {
      return(Status);
     }

  Pseq = UnicodeStr.Buffer;

#else

  Pseq = (RPC_CHAR PAPI * )ProtocolSeq;

#endif

#ifdef DEBUGRPC

    char * CoOffset = (char *) &(((   RPC_CLIENT_TRANSPORT_INFO *) 0)->TowerConstruct);
    char * DgOffset = (char *) &(((DG_RPC_CLIENT_TRANSPORT_INFO *) 0)->TowerConstruct);

    ASSERT( CoOffset == DgOffset);

    if (CoOffset != DgOffset)
        {
        return RPC_S_INTERNAL_ERROR;
        }
#endif


  if (Status == RPC_S_OK)
    {
      ClientInfo = (RPC_CLIENT_TRANSPORT_INFO PAPI *)
                     OsfClientMapRpcProtocolSequence(Pseq, &Status);
    }

  if (Status == RPC_S_OK)
     {
       Status = ClientInfo->TowerConstruct(Endpoint,
                                           NetworkAddress,
                                           Floors,
                                           ByteCount,
                                           Tower,
                                           ProtocolSeq);
     }

#ifdef NTENV
  RtlFreeUnicodeString(&UnicodeStr);
#endif

  return(Status);
}



RPC_STATUS RPC_ENTRY
OsfTowerExplode(
    IN unsigned char PAPI * Tower,
    OUT char PAPI * PAPI * Protseq,
    OUT char PAPI * PAPI * Endpoint,
    OUT char PAPI * PAPI * NWAddress
    )
/*++

Routine Description:

    This routine accepts upper floors of a tower [Floor 3 onwards]
    It invokes the appropriate loadable transport based on the opcode
    it finds in level 3 to return protocol sequence, endpoint and nwaddress.

Return Value:


--*/
{
  RPC_STATUS Status;
  unsigned short TransportId;
  unsigned char PAPI * Id;
  unsigned char PAPI * ProtocolSequence;
  RPC_CHAR PAPI * PSeq;
  RPC_CLIENT_TRANSPORT_INFO PAPI * ClientInfo;
#ifdef NTENV
  UNICODE_STRING UnicodeStr;
#endif

  Id = (Tower + sizeof(unsigned short));

  TransportId = (0x00FF & *Id);

  ClientInfo = (RPC_CLIENT_TRANSPORT_INFO PAPI *)
                      GetLoadedClientTransportInfoFromId(TransportId);
  if (ClientInfo != 0)
     {
     Status = ClientInfo->TowerExplode(
                                    Tower,
                                    Protseq,
                                    Endpoint,
                                    NWAddress);
     return(Status);
     }

  //
  //Unfortunately the transport was not loaded ..
  //

  Status = RpcGetAdditionalTransportInfo(TransportId, &ProtocolSequence);

  if (Status == RPC_S_OK)
     {

#ifdef NTENV

     if (Status = AnsiToUnicodeString(
                        (unsigned char PAPI *)ProtocolSequence, &UnicodeStr) )
        {
        return(Status);
        }

     PSeq = UnicodeStr.Buffer;

#else

     PSeq = (RPC_CHAR PAPI * )ProtocolSequence;

#endif

     ClientInfo = (RPC_CLIENT_TRANSPORT_INFO PAPI *)
            OsfClientMapRpcProtocolSequence(PSeq, &Status);
     }
  else
     {
     return (Status);
     }

  if (Status == RPC_S_OK)
     {
     if (ClientInfo != 0)
        {
        Status = ClientInfo->TowerExplode(
                                    Tower,
                                    Protseq,
                                    Endpoint,
                                    NWAddress);
        }
    else
        {
        Status = RPC_S_INVALID_RPC_PROTSEQ;
        }
     }

#ifdef NTENV
  RtlFreeUnicodeString(&UnicodeStr);
#endif

  return(Status);
}

}

#ifdef NTENV


RPC_STATUS
OSF_CCONNECTION::Cancel(
    void * ThreadHandle
    )
{
    NTSTATUS NtStatus = NtAlertThread((HANDLE) ThreadHandle);

    if (NT_SUCCESS(NtStatus))
        {
        return RPC_S_OK;
        }
    else
        {
        return RtlNtStatusToDosError(NtStatus);
        }
}

unsigned
OSF_CCONNECTION::TestCancel(
    )
{
    if (NtTestAlert() == STATUS_ALERTED)
        {
        return 1;
        }
    else
        {
        return 0;
        }
}

#elif (DOSWIN32RPC)

RPC_STATUS
OSF_CCONNECTION::Cancel(
    void * ThreadHandle
    )
{
    return (RPC_S_CANNOT_SUPPORT);
}

unsigned
OSF_CCONNECTION::TestCancel(
    )
{
   return 0;
}


#endif