File:  [WindowsNT SDKs] / ntddk / src / network / tdi / connobj.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

/*++

Copyright (c) 1989-1993  Microsoft Corporation

Module Name:

    connobj.c

Abstract:

    This module contains code which implements the TP_CONNECTION object.
    Routines are provided to create, destroy, reference, and dereference,
    transport connection objects.

Environment:

    Kernel mode

Revision History:

--*/

#include "st.h"



VOID
StAllocateConnection(
    IN PDEVICE_CONTEXT DeviceContext,
    OUT PTP_CONNECTION *TransportConnection
    )

/*++

Routine Description:

    This routine allocates storage for a transport connection. Some
    minimal initialization is done.

    NOTE: This routine is called with the device context spinlock
    held, or at such a time as synchronization is unnecessary.

Arguments:

    DeviceContext - the device context for this connection to be
        associated with.

    TransportConnection - Pointer to a place where this routine will
        return a pointer to a transport connection structure. Returns
        NULL if the storage cannot be allocated.

Return Value:

    None.

--*/

{

    PTP_CONNECTION Connection;

    if ((DeviceContext->MemoryLimit != 0) &&
            ((DeviceContext->MemoryUsage + sizeof(TP_CONNECTION)) >
                DeviceContext->MemoryLimit)) {
        PANIC("ST: Could not allocate connection: limit\n");
        StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 103);
        *TransportConnection = NULL;
        return;
    }

    Connection = (PTP_CONNECTION)ExAllocatePool (NonPagedPool,
                                                 sizeof (TP_CONNECTION));
    if (Connection == NULL) {
        PANIC("ST: Could not allocate connection: no pool\n");
        StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 203);
        *TransportConnection = NULL;
        return;
    }
    RtlZeroMemory (Connection, sizeof(TP_CONNECTION));

    DeviceContext->MemoryUsage += sizeof(TP_CONNECTION);
    ++DeviceContext->ConnectionAllocated;

    Connection->Type = ST_CONNECTION_SIGNATURE;
    Connection->Size = sizeof (TP_CONNECTION);

    Connection->Provider = DeviceContext;
    Connection->ProviderInterlock = &DeviceContext->Interlock;
    KeInitializeSpinLock (&Connection->SpinLock);

    InitializeListHead (&Connection->LinkList);
    InitializeListHead (&Connection->AddressFileList);
    InitializeListHead (&Connection->AddressList);
    InitializeListHead (&Connection->PacketWaitLinkage);
    InitializeListHead (&Connection->PacketizeLinkage);
    InitializeListHead (&Connection->SendQueue);
    InitializeListHead (&Connection->ReceiveQueue);
    InitializeListHead (&Connection->InProgressRequest);

    StAddSendPacket (DeviceContext);

    *TransportConnection = Connection;

}   /* StAllocateConnection */


VOID
StDeallocateConnection(
    IN PDEVICE_CONTEXT DeviceContext,
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routine frees storage for a transport connection.

    NOTE: This routine is called with the device context spinlock
    held, or at such a time as synchronization is unnecessary.

Arguments:

    DeviceContext - the device context for this connection to be
        associated with.

    TransportConnection - Pointer to a transport connection structure.

Return Value:

    None.

--*/

{

    ExFreePool (TransportConnection);
    --DeviceContext->ConnectionAllocated;
    DeviceContext->MemoryUsage -= sizeof(TP_CONNECTION);

    StRemoveSendPacket (DeviceContext);

}   /* StDeallocateConnection */


NTSTATUS
StCreateConnection(
    IN PDEVICE_CONTEXT DeviceContext,
    OUT PTP_CONNECTION *TransportConnection
    )

/*++

Routine Description:

    This routine creates a transport connection. The reference count in the
    connection is automatically set to 1, and the reference count in the
    DeviceContext is incremented.

Arguments:

    Address - the address for this connection to be associated with.

    TransportConnection - Pointer to a place where this routine will
        return a pointer to a transport connection structure.

Return Value:

    NTSTATUS - status of operation.

--*/

{
    PTP_CONNECTION Connection;
    KIRQL oldirql;
    PLIST_ENTRY p;
    UINT TempDataLen;

    ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);

    p = RemoveHeadList (&DeviceContext->ConnectionPool);
    if (p == &DeviceContext->ConnectionPool) {

        if ((DeviceContext->ConnectionMaxAllocated == 0) ||
            (DeviceContext->ConnectionAllocated < DeviceContext->ConnectionMaxAllocated)) {

            StAllocateConnection (DeviceContext, &Connection);

        } else {

            StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 403);
            Connection = NULL;

        }

        if (Connection == NULL) {
            ++DeviceContext->ConnectionExhausted;
            RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
            PANIC ("StCreateConnection: Could not allocate connection object!\n");
            return STATUS_INSUFFICIENT_RESOURCES;
        }

    } else {

        Connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);

    }

    ++DeviceContext->ConnectionInUse;
    if (DeviceContext->ConnectionInUse > DeviceContext->ConnectionMaxInUse) {
        ++DeviceContext->ConnectionMaxInUse;
    }

    DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse;
    ++DeviceContext->ConnectionSamples;

    RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);


    //
    // We have two references; one is for creation, and the
    // other is a temporary one so that the connection won't
    // go away before the creator has a chance to access it.
    //

    Connection->SpecialRefCount = 1;
    Connection->ReferenceCount = -1;   // this is -1 based

    //
    // Initialize the request queues & components of this connection.
    //

    InitializeListHead (&Connection->SendQueue);
    InitializeListHead (&Connection->ReceiveQueue);
    InitializeListHead (&Connection->InProgressRequest);
    InitializeListHead (&Connection->AddressList);
    InitializeListHead (&Connection->AddressFileList);
    Connection->SpecialReceiveIrp = (PIRP)NULL;
    Connection->Flags = 0;
    Connection->Flags2 = 0;
    Connection->MessageBytesReceived = (USHORT)0;   // no data yet
    Connection->MessageBytesAcked = (USHORT)0;
    Connection->Context = NULL;                 // no context yet.
    Connection->Status = STATUS_PENDING;        // default StStopConnection status.
    Connection->SendState = CONNECTION_SENDSTATE_IDLE;
    Connection->CurrentReceiveRequest = (PTP_REQUEST)NULL;
    Connection->DisconnectIrp = (PIRP)NULL;
    Connection->CloseIrp = (PIRP)NULL;
    Connection->AddressFile = NULL;
    Connection->IndicationInProgress = FALSE;

    MacReturnMaxDataSize(
        &DeviceContext->MacInfo,
        NULL,
        0,
        DeviceContext->MaxSendPacketSize,
        &TempDataLen);
    Connection->MaximumDataSize = TempDataLen - sizeof(ST_HEADER);

    StReferenceDeviceContext ("Create Connection", DeviceContext);

    *TransportConnection = Connection;  // return the connection.

    return STATUS_SUCCESS;
} /* StCreateConnection */


NTSTATUS
StVerifyConnectionObject (
    IN PTP_CONNECTION Connection
    )

/*++

Routine Description:

    This routine is called to verify that the pointer given us in a file
    object is in fact a valid connection object.

Arguments:

    Connection - potential pointer to a TP_CONNECTION object.

Return Value:

    STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise

--*/

{
    KIRQL oldirql;
    NTSTATUS status = STATUS_SUCCESS;

    //
    // try to verify the connection signature. If the signature is valid,
    // get the connection spinlock, check its state, and increment the
    // reference count if it's ok to use it. Note that being in the stopping
    // state is an OK place to be and reference the connection; we can
    // disassociate the address while running down.
    //

    try {

        if ((Connection->Size == sizeof (TP_CONNECTION)) &&
            (Connection->Type == ST_CONNECTION_SIGNATURE)) {

            ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);

            if ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) {

                StReferenceConnection ("Verify Temp Use", Connection);

            } else {

                status = STATUS_INVALID_CONNECTION;
            }

            RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);

        } else {

            status = STATUS_INVALID_CONNECTION;
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {

         return GetExceptionCode();
    }

    return status;

}


NTSTATUS
StDestroyAssociation(
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routine destroys the association between a transport connection and
    the address it was formerly associated with. The only action taken is
    to disassociate the address and remove the connection from all address
    queues.

    This routine is only called by StDereferenceConnection.  The reason for
    this is that there may be multiple streams of execution which are
    simultaneously referencing the same connection object, and it should
    not be deleted out from under an interested stream of execution.

Arguments:

    TransportConnection - Pointer to a transport connection structure to
        be destroyed.

Return Value:

    NTSTATUS - status of operation.

--*/

{
    KIRQL oldirql, oldirql2;
    PTP_ADDRESS_FILE addressFile;


    ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2);
    if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) {
        RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
        return STATUS_SUCCESS;
    } else {
        TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED;
        RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
    }

    addressFile = TransportConnection->AddressFile;

    //
    // Delink this connection from its associated address connection
    // database.  To do this we must spin lock on the address object as
    // well as on the connection,
    //

    ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql);
    ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2);
    RemoveEntryList (&TransportConnection->AddressFileList);
    RemoveEntryList (&TransportConnection->AddressList);

    InitializeListHead (&TransportConnection->AddressList);
    InitializeListHead (&TransportConnection->AddressFileList);

    //
    // remove the association between the address and the connection.
    //

    TransportConnection->AddressFile = NULL;

    RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2);
    RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql);

    //
    // and remove a reference to the address
    //

    StDereferenceAddress ("Destroy association", addressFile->Address);


    return STATUS_SUCCESS;

} /* StDestroyAssociation */


NTSTATUS
StIndicateDisconnect(
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routine indicates a remote disconnection on this connection if it
    is necessary to do so. No other action is taken here.

    This routine is only called by StDereferenceConnection.  The reason for
    this is that there may be multiple streams of execution which are
    simultaneously referencing the same connection object, and it should
    not be deleted out from under an interested stream of execution.

Arguments:

    TransportConnection - Pointer to a transport connection structure to
        be destroyed.

Return Value:

    NTSTATUS - status of operation.

--*/

{
    PTP_ADDRESS_FILE addressFile;
    PDEVICE_CONTEXT DeviceContext;
    ULONG DisconnectReason;
    PIRP DisconnectIrp;
    KIRQL oldirql;

    ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql);

    if (((TransportConnection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) != 0)) {

        //
        // Turn off all but the still-relevant bits in the flags.
        //

        ASSERT (TransportConnection->Flags & CONNECTION_FLAGS_STOPPING);

        TransportConnection->Flags = CONNECTION_FLAGS_STOPPING;
        TransportConnection->Flags2 &=
            (CONNECTION_FLAGS2_ASSOCIATED |
             CONNECTION_FLAGS2_DISASSOCIATED |
             CONNECTION_FLAGS2_CLOSING);

        //
        // Clean up other stuff -- basically everything gets
        // done here except for the flags and the status, since
        // they are used to block other requests. When the connection
        // is given back to us (in Accept, Connect, or Listen)
        // they are cleared.
        //

        TransportConnection->MessageBytesReceived = (USHORT)0;   // no data yet
        TransportConnection->MessageBytesAcked = (USHORT)0;

        TransportConnection->CurrentReceiveRequest = (PTP_REQUEST)NULL;

        DisconnectIrp = TransportConnection->DisconnectIrp;
        TransportConnection->DisconnectIrp = (PIRP)NULL;

        RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);


        DeviceContext = TransportConnection->Provider;
        addressFile = TransportConnection->AddressFile;


        //
        // If this connection was stopped by a call to TdiDisconnect,
        // we have to complete that. We save the Irp so we can return
        // the connection to the pool before we complete the request.
        //


        if (DisconnectIrp != (PIRP)NULL) {

            //
            // Now complete the IRP if needed. This will be non-null
            // only if TdiDisconnect was called, and we have not
            // yet completed it.
            //

            DisconnectIrp->IoStatus.Information = 0;
            DisconnectIrp->IoStatus.Status = STATUS_SUCCESS;
            IoCompleteRequest (DisconnectIrp, IO_NETWORK_INCREMENT);

        } else if ((TransportConnection->Status != STATUS_LOCAL_DISCONNECT) &&
                (addressFile->RegisteredDisconnectHandler == TRUE)) {

            //
            // This was a remotely spawned disconnect, so indicate that
            // to our client. Note that in the comparison above we
            // check the status first, since if it is LOCAL_DISCONNECT
            // addressFile may be NULL (BUGBUG: This is sort of a hack
            // for PDK2, we should really indicate the disconnect inside
            // StStopConnection, where we know addressFile is valid).
            //

            //
            // if the disconnection was remotely spawned, then indicate
            // disconnect. In the case that a disconnect was issued at
            // the same time as the connection went down remotely, we
            // won't do this because DisconnectIrp will be non-NULL.
            //

            //
            // Invoke the user's disconnection event handler, if any. We do this here
            // so that any outstanding sends will complete before we tear down the
            // connection.
            //

            DisconnectReason = 0;
            if (TransportConnection->Flags & CONNECTION_FLAGS_ABORT) {
                DisconnectReason |= TDI_DISCONNECT_ABORT;
            }
            if (TransportConnection->Flags & CONNECTION_FLAGS_DESTROY) {
                DisconnectReason |= TDI_DISCONNECT_RELEASE;
            }

            (*addressFile->DisconnectHandler)(
                    addressFile->DisconnectHandlerContext,
                    TransportConnection->Context,
                    0,
                    NULL,
                    0,
                    NULL,
                    DisconnectReason);

        }

    } else {

        //
        // The client does not yet think that this connection
        // is up...generally this happens due to request count
        // fluctuation during connection setup.
        //

        RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);

    }


    return STATUS_SUCCESS;

} /* StIndicateDisconnect */


NTSTATUS
StDestroyConnection(
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routine destroys a transport connection and removes all references
    made by it to other objects in the transport.  The connection structure
    is returned to our lookaside list.  It is assumed that the caller
    has removed all IRPs from the connections's queues first.

    This routine is only called by StDereferenceConnection.  The reason for
    this is that there may be multiple streams of execution which are
    simultaneously referencing the same connection object, and it should
    not be deleted out from under an interested stream of execution.

Arguments:

    TransportConnection - Pointer to a transport connection structure to
        be destroyed.

Return Value:

    NTSTATUS - status of operation.

--*/

{
    KIRQL oldirql;
    PDEVICE_CONTEXT DeviceContext;
    PIRP CloseIrp;


    DeviceContext = TransportConnection->Provider;

    //
    // Destroy any association that this connection has.
    //

    StDestroyAssociation (TransportConnection);

    //
    // Clear out any associated nasties hanging around the connection. Note
    // that the current flags are set to STOPPING; this way anyone that may
    // maliciously try to use the connection after it's dead and gone will
    // just get ignored.
    //

    TransportConnection->Flags = CONNECTION_FLAGS_STOPPING;
    TransportConnection->Flags2 = CONNECTION_FLAGS2_CLOSING;
    TransportConnection->MessageBytesReceived = (USHORT)0;   // no data yet
    TransportConnection->MessageBytesAcked = (USHORT)0;


    //
    // Now complete the close IRP. This will be set to non-null
    // when CloseConnection was called.
    //

    CloseIrp = TransportConnection->CloseIrp;

    if (CloseIrp != (PIRP)NULL) {

        TransportConnection->CloseIrp = (PIRP)NULL;
        CloseIrp->IoStatus.Information = 0;
        CloseIrp->IoStatus.Status = STATUS_SUCCESS;
        IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT);

    }

    //
    // Return the connection to the provider's pool.
    //

    ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);

    DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse;
    ++DeviceContext->ConnectionSamples;
    --DeviceContext->ConnectionInUse;

    if ((DeviceContext->ConnectionAllocated - DeviceContext->ConnectionInUse) >
            DeviceContext->ConnectionInitAllocated) {
        StDeallocateConnection (DeviceContext, TransportConnection);
    } else {
        InsertTailList (&DeviceContext->ConnectionPool, &TransportConnection->LinkList);
    }

    RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);

    StDereferenceDeviceContext ("Destroy Connection", DeviceContext);

    return STATUS_SUCCESS;

} /* StDestroyConnection */


VOID
StRefConnection(
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routine increments the reference count on a transport connection.

Arguments:

    TransportConnection - Pointer to a transport connection object.

Return Value:

    none.

--*/

{
    INTERLOCKED_RESULT result;

    result = ExInterlockedIncrementLong (
                 &TransportConnection->ReferenceCount,
                 TransportConnection->ProviderInterlock);

    if (result == ResultZero) {

        //
        // The first increment causes us to increment the
        // "ref count is not zero" special ref.
        //

        ExInterlockedAddUlong(
            (PULONG)(&TransportConnection->SpecialRefCount),
            1,
            TransportConnection->ProviderInterlock);

    }

    ASSERT (result != ResultNegative);

} /* StRefConnection */


VOID
StDerefConnection(
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routine dereferences a transport connection by decrementing the
    reference count contained in the structure.  If, after being
    decremented, the reference count is zero, then this routine calls
    StDestroyConnection to remove it from the system.

Arguments:

    TransportConnection - Pointer to a transport connection object.

Return Value:

    none.

--*/

{
    INTERLOCKED_RESULT result;

    result = ExInterlockedDecrementLong (
                &TransportConnection->ReferenceCount,
                TransportConnection->ProviderInterlock);

    //
    // If all the normal references to this connection are gone, then
    // we can remove the special reference that stood for
    // "the regular ref count is non-zero".
    //

    if (result == ResultNegative) {

        //
        // If the refcount is -1, then we need to indicate
        // disconnect. However, we need to
        // do this before we actually do the special deref, since
        // otherwise the connection might go away while we
        // are doing that.
        //

        StIndicateDisconnect (TransportConnection);

        //
        // Now it is OK to let the connection go away.
        //

        StDereferenceConnectionSpecial ("Regular ref gone", TransportConnection);

    }

} /* StDerefConnection */


VOID
StDerefConnectionSpecial(
    IN PTP_CONNECTION TransportConnection
    )

/*++

Routine Description:

    This routines completes the dereferencing of a connection.
    It may be called any time, but it does not do its work until
    the regular reference count is also 0.

Arguments:

    TransportConnection - Pointer to a transport connection object.

Return Value:

    none.

--*/

{
    KIRQL oldirql;

    ACQUIRE_SPIN_LOCK (TransportConnection->ProviderInterlock, &oldirql);

    --TransportConnection->SpecialRefCount;

    if ((TransportConnection->SpecialRefCount == 0) &&
        (TransportConnection->ReferenceCount == -1)) {

        //
        // If we have deleted all references to this connection, then we can
        // destroy the object.  It is okay to have already released the spin
        // lock at this point because there is no possible way that another
        // stream of execution has access to the connection any longer.
        //

        RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql);

        StDestroyConnection (TransportConnection);

    } else {

        RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql);

    }

} /* StDerefConnectionSpecial */


PTP_CONNECTION
StFindConnection(
    IN PDEVICE_CONTEXT DeviceContext,
    IN PUCHAR LocalName,
    IN PUCHAR RemoteName
    )

/*++

Routine Description:

    This routine scans the connections associated with a
    device context, and determines if there is an connection
    associated with the specific remote address on the
    specific local address.

Arguments:

    DeviceContext - Pointer to the device context.

    LocalName - The 16-character Netbios name of the local address.

    RemoteName - The 16-character Netbios name of the remote.

Return Value:

    The connection if one is found, NULL otherwise.

--*/

{
    KIRQL oldirql;
    PLIST_ENTRY Flink;
    PTP_ADDRESS Address;
    BOOLEAN MatchedAddress = FALSE;
    PTP_CONNECTION Connection;

    ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);

    for (Flink = DeviceContext->AddressDatabase.Flink;
         Flink != &DeviceContext->AddressDatabase;
         Flink = Flink->Flink) {

        Address = CONTAINING_RECORD (
                    Flink,
                    TP_ADDRESS,
                    Linkage);

        if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) {
            continue;
        }

        if (StMatchNetbiosAddress (Address, LocalName)) {

            StReferenceAddress ("Looking for connection", Address);   // prevent address from being destroyed.
            MatchedAddress = TRUE;
            break;

        }
    }

    RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);

    if (!MatchedAddress) {
        return NULL;
    }

    Connection = StLookupRemoteName (Address, RemoteName);

    StDereferenceAddress ("Looking for connection", Address);

    return Connection;

}


PTP_CONNECTION
StLookupConnectionByContext(
    IN PTP_ADDRESS Address,
    IN CONNECTION_CONTEXT ConnectionContext
    )

/*++

Routine Description:

    This routine accepts a connection identifier and an address and
    returns a pointer to the connection object, TP_CONNECTION.  If the
    connection identifier is not found on the address, then NULL is returned.
    This routine automatically increments the reference count of the
    TP_CONNECTION structure if it is found.  It is assumed that the
    TP_ADDRESS structure is already held with a reference count.

    BUGBUG: Should the ConnectionDatabase go in the address file?

Arguments:

    Address - Pointer to a transport address object.

    ConnectionContext - Connection Context for this address.

Return Value:

    A pointer to the connection we found

--*/

{
    KIRQL oldirql, oldirql1;
    PLIST_ENTRY p;
    PTP_CONNECTION Connection;

    //
    // Currently, this implementation is inefficient, but brute force so
    // that a system can get up and running.  Later, a cache of the mappings
    // of popular connection id's and pointers to their TP_CONNECTION structures
    // will be searched first.
    //

    ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);

    for (p=Address->ConnectionDatabase.Flink;
         p != &Address->ConnectionDatabase;
         p=p->Flink) {


        Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList);

        ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1);

        if ((Connection->Context == ConnectionContext) &&
            ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0)) {
            // This reference is removed by the calling function
            StReferenceConnection ("Lookup up for request", Connection);
            RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
            RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);

            return Connection;
        }

        RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);

    }

    RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);

    return NULL;

} /* StLookupConnectionByContext */


PTP_CONNECTION
StLookupListeningConnection(
    IN PTP_ADDRESS Address
    )

/*++

Routine Description:

    This routine scans the connection database on an address to find
    a TP_CONNECTION object which has CONNECTION_FLAGS_WAIT_NQ
    flag set.   It returns a pointer to the found connection object (and
    simultaneously resets the flag) or NULL if it could not be found.
    The reference count is also incremented atomically on the connection.

Arguments:

    Address - Pointer to a transport address object.

Return Value:

    NTSTATUS - status of operation.

--*/

{
    KIRQL oldirql, oldirql1;
    PTP_CONNECTION Connection;
    PLIST_ENTRY p;


    ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);

    for (p=Address->ConnectionDatabase.Flink;
         p != &Address->ConnectionDatabase;
         p=p->Flink) {


        Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList);
        if (Connection->Flags & CONNECTION_FLAGS_WAIT_LISTEN) {

            // This reference is removed by the calling function
            StReferenceConnection ("Found Listening", Connection);

            ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1);
            Connection->Flags &= ~CONNECTION_FLAGS_WAIT_LISTEN;
            RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
            RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);

            return Connection;
        }

    }

    RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);

    return NULL;

} /* StLookupListeningConnection */


VOID
StStopConnection(
    IN PTP_CONNECTION Connection,
    IN NTSTATUS Status
    )

/*++

Routine Description:

    This routine is called to terminate all activity on a connection and
    destroy the object.  This is done in a graceful manner; i.e., all
    outstanding requests are terminated by cancelling them, etc.  It is
    assumed that the caller has a reference to this connection object,
    but this routine will do the dereference for the one issued at creation
    time.

    Orderly release is a function of this routine, but it is not a provided
    service of this transport provider, so there is no code to do it here.

Arguments:

    Connection - Pointer to a TP_CONNECTION object.

    Status - The status that caused us to stop the connection. This
        will determine what status pending requests are aborted with,
        and also how we proceed during the stop (whether to send a
        session end, and whether to indicate disconnect).

Return Value:

    none.

--*/

{
    KIRQL oldirql, oldirql1, cancelirql;
    PLIST_ENTRY p;
    PIRP Irp;
    PTP_REQUEST Request;
    ULONG DisconnectReason;
    PULONG StopCounter;
    PDEVICE_CONTEXT DeviceContext;
    BOOLEAN WasConnected;


    DeviceContext = Connection->Provider;

    ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
    if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING)) {

        //
        // We are stopping the connection, record statistics
        // about it.
        //

        if (Connection->Flags & CONNECTION_FLAGS_READY) {

            DECREMENT_COUNTER (DeviceContext, OpenConnections);
            WasConnected = TRUE;

        } else {

            WasConnected = FALSE;

        }

        Connection->Flags &= ~CONNECTION_FLAGS_READY;        // no longer open for business
        Connection->Flags |= CONNECTION_FLAGS_STOPPING;
        Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID;
        Connection->SendState = CONNECTION_SENDSTATE_IDLE;
        Connection->Status = Status;

        //
        // If this connection is waiting to packetize,
        // remove it from the device context queue it is on.
        //
        // NOTE: If the connection is currently in the
        // packetize queue, it will eventually go to get
        // packetized and at that point it will get
        // removed.
        //

        if (Connection->Flags & CONNECTION_FLAGS_SUSPENDED) {

            ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1);
            RemoveEntryList (&Connection->PacketWaitLinkage);
            RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1);
        }


        RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);

        IoAcquireCancelSpinLock(&cancelirql);
        ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);


        //
        // Run down all TdiSend requests on this connection.
        //

        while (TRUE) {
            p = RemoveHeadList (&Connection->SendQueue);
            if (p == &Connection->SendQueue) {
                break;
            }
            RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
            Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
            Irp->CancelRoutine = (PDRIVER_CANCEL)NULL;
            IoReleaseCancelSpinLock(cancelirql);
            StCompleteSendIrp (Irp, Connection->Status, 0);
            IoAcquireCancelSpinLock(&cancelirql);
            ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
        }

        //
        // NOTE: We hold the connection spinlock AND the
        // cancel spinlock here.
        //

        Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE;

        //
        // Run down all TdiReceive requests on this connection.
        //

        while (TRUE) {
            p = RemoveHeadList (&Connection->ReceiveQueue);
            if (p == &Connection->ReceiveQueue) {
                break;
            }
            RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
            Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
            Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL;
            IoReleaseCancelSpinLock(cancelirql);

            StCompleteRequest (Request, Connection->Status, 0);
            IoAcquireCancelSpinLock(&cancelirql);
            ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
        }


        //
        // NOTE: We hold the connection spinlock AND the
        // cancel spinlock here.
        //

        //
        // Run down all TdiConnect/TdiDisconnect/TdiListen requests.
        //

        while (TRUE) {
            p = RemoveHeadList (&Connection->InProgressRequest);
            if (p == &Connection->InProgressRequest) {
                break;
            }
            RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
            Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
            Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL;
            IoReleaseCancelSpinLock(cancelirql);

            StCompleteRequest (Request, Connection->Status, 0);

            IoAcquireCancelSpinLock(&cancelirql);
            ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
        }

        RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
        IoReleaseCancelSpinLock(cancelirql);

        //
        // If we aren't DESTROYing the link, then send a SESSION_END frame
        // to the remote side.  When the SESSION_END frame is acknowleged,
        // we will decrement the connection's reference count by one, removing
        // its creation reference.  This will cause the connection object to
        // be disposed of, and will begin running down the link.
        // DGB: add logic to avoid blowing away link if one doesn't exist yet.
        //

        DisconnectReason = 0;
        if (Connection->Flags & CONNECTION_FLAGS_ABORT) {
            DisconnectReason |= TDI_DISCONNECT_ABORT;
        }
        if (Connection->Flags & CONNECTION_FLAGS_DESTROY) {
            DisconnectReason |= TDI_DISCONNECT_RELEASE;
        }

        //
        // When this completes we will dereference the connection.
        //

        if (WasConnected) {
            StSendDisconnect (Connection);
        }


        switch (Status) {

        case STATUS_LOCAL_DISCONNECT:
            StopCounter = &DeviceContext->LocalDisconnects;
            break;
        case STATUS_REMOTE_DISCONNECT:
            StopCounter = &DeviceContext->RemoteDisconnects;
            break;
        case STATUS_LINK_FAILED:
            StopCounter = &DeviceContext->LinkFailures;
            break;
        case STATUS_IO_TIMEOUT:
            StopCounter = &DeviceContext->SessionTimeouts;
            break;
        case STATUS_CANCELLED:
            StopCounter = &DeviceContext->CancelledConnections;
            break;
        case STATUS_REMOTE_RESOURCES:
            StopCounter = &DeviceContext->RemoteResourceFailures;
            break;
        case STATUS_INSUFFICIENT_RESOURCES:
            StopCounter = &DeviceContext->LocalResourceFailures;
            break;
        case STATUS_BAD_NETWORK_PATH:
            StopCounter = &DeviceContext->NotFoundFailures;
            break;
        case STATUS_REMOTE_NOT_LISTENING:
            StopCounter = &DeviceContext->NoListenFailures;
            break;

        default:
            StopCounter = NULL;
            break;

        }

        if (StopCounter != NULL) {

            ExInterlockedIncrementLong(
                (PLONG)StopCounter,
                &DeviceContext->StatisticsInterlock);

        }


        //
        // Note that we've blocked all new requests being queued during the
        // time we have been in this teardown code; StDestroyConnection also
        // sets the connection flags to STOPPING when returning the
        // connection to the queue. This avoids lingerers using non-existent
        // connections.
        //

    } else {

        //
        // The connection was already stopping.
        //

        RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);

    }

} /* StStopConnection */


VOID
StCancelConnection(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine is called by the I/O system to cancel a connect
    or a listen. It is simple since there can only be one of these
    active on a connection; we just stop the connection, the IRP
    will get completed as part of normal session teardown.

    NOTE: This routine is called with the CancelSpinLock held and
    is responsible for releasing it.

Arguments:

    DeviceObject - Pointer to the device object for this driver.

    Irp - Pointer to the request packet representing the I/O request.

Return Value:

    none.

--*/

{
    KIRQL oldirql;
    PIO_STACK_LOCATION IrpSp;
    PTP_CONNECTION Connection;
    PTP_REQUEST Request;
    PLIST_ENTRY p;

    UNREFERENCED_PARAMETER (DeviceObject);

    //
    // Get a pointer to the current stack location in the IRP.  This is where
    // the function codes and parameters are stored.
    //

    IrpSp = IoGetCurrentIrpStackLocation (Irp);

    ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
            (IrpSp->MinorFunction == TDI_CONNECT || IrpSp->MinorFunction == TDI_LISTEN));

    Connection = IrpSp->FileObject->FsContext;

    //
    // Since this IRP is still in the cancellable state, we know
    // that the connection is still around (although it may be in
    // the process of being torn down).
    //

    ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
    StReferenceConnection ("Cancelling Send", Connection);

    p = RemoveHeadList (&Connection->InProgressRequest);
    ASSERT (p != &Connection->InProgressRequest);

    RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);

    Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
    ASSERT (Request->IoRequestPacket == Irp);
    Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL;

    IoReleaseCancelSpinLock(Irp->CancelIrql);

    StCompleteRequest (Request, STATUS_CANCELLED, 0);
    StStopConnection (Connection, STATUS_CANCELLED);

    StDereferenceConnection ("Cancel done", Connection);

}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.