/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    ALGio.c

Abstract:

    This module contains code for the ALG transparent proxy's network
    I/O completion routines.

Author:

    Qiang Wang  (qiangw)        10-Apr-2000

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop
#include "Algmsg.h"

VOID
AlgAcceptCompletionRoutine(
    ULONG ErrorCode,
    ULONG BytesTransferred,
    PNH_BUFFER Bufferp
    )

/*++

Routine Description:

    This routine is invoked upon completion of an accept operation
    on a ALG transparent proxy stream socket.

Arguments:

    ErrorCode - Win32 status code for the I/O operation

    BytesTransferred - number of bytes in 'Bufferp'

    Bufferp - holds the local and remote IP address and port
        for the connection.

Return Value:

    none.

Environment:

    Runs in the context of a worker-thread which has just dequeued
    an I/O completion packet from the common I/O completion port
    with which our stream sockets are associated.
    A reference to the component will have been made on our behalf
    by 'NhAcceptStreamSocket'.
    A reference to the interface will have been made on our behalf
    by whoever issued the I/O request.

--*/

{
    SOCKET AcceptedSocket;
    PALG_CONNECTION Connectionp;
    ULONG Error;
    PALG_INTERFACE Interfacep;
    SOCKET ListeningSocket;
    PROFILE("AlgAcceptCompletionRoutine");
    do {
        AcceptedSocket = (SOCKET)Bufferp->Socket;
        Interfacep = (PALG_INTERFACE)Bufferp->Context;
        ListeningSocket = (SOCKET)Bufferp->Context2;

        //
        // Acquire three additional references to the interface
        // for the followup requests that we will issue below,
        // and lock the interface.
        //

        EnterCriticalSection(&AlgInterfaceLock);
        if (!ALG_REFERENCE_INTERFACE(Interfacep)) {
            LeaveCriticalSection(&AlgInterfaceLock);
            NhReleaseBuffer(Bufferp);
            NhDeleteStreamSocket(AcceptedSocket);
            break;
        }
        ALG_REFERENCE_INTERFACE(Interfacep);
        ALG_REFERENCE_INTERFACE(Interfacep);
        LeaveCriticalSection(&AlgInterfaceLock);
        ACQUIRE_LOCK(Interfacep);

        //
        // Process the accept-completion.
        // First look for an error code. If an error occurred
        // and the interface is no longer active, end the completion-handling.
        // Otherwise, attempt to reissue the accept-request.
        //

        if (ErrorCode) {
            NhTrace(
                TRACE_FLAG_IO,
                "AlgAcceptCompletionRoutine: error %d for interface %d",
                ErrorCode, Interfacep->Index
                );

            //
            // See if the interface is still active and, if so, reissue
            // the accept-request. Since we will not be creating an active
            // endpoint, we won't need the second reference to the interface.
            //

            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);

            if (!ALG_INTERFACE_ACTIVE(Interfacep)) {
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhReleaseBuffer(Bufferp);
                NhDeleteStreamSocket(AcceptedSocket);
            } else {

                //
                // Reissue the accept-request. Note that the callee is now
                // responsible for the reference we made to the interface.
                //

                Error =
                    AlgAcceptConnectionInterface(
                        Interfacep,
                        ListeningSocket,
                        AcceptedSocket,
                        Bufferp,
                        NULL
                        );
                RELEASE_LOCK(Interfacep);
                if (Error) {
                    NhReleaseBuffer(Bufferp);
                    NhDeleteStreamSocket(AcceptedSocket);
                    NhTrace(
                        TRACE_FLAG_IO,
                        "AlgAcceptCompletionRoutine: error %d reissuing accept",
                        Error
                        );
                }
            }

            break;
        }

        //
        // Now see if the interface is operational.
        // If it isn't, we need to destroy the accepted socket
        // and return control.
        //

        if (!ALG_INTERFACE_ACTIVE(Interfacep)) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhReleaseBuffer(Bufferp);
            NhDeleteStreamSocket(AcceptedSocket);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgAcceptCompletionRoutine: interface %d inactive",
                Interfacep->Index
                );
            InterlockedIncrement(
                reinterpret_cast<LPLONG>(&AlgStatistics.ConnectionsDropped)
                );
            break;
        }

        //
        // We now create a 'ALG_CONNECTION' for the new connection,
        // in the process launching operations for the connection.
        // The connection management module will handle the accepted socket
        // from here onward, and is responsible for the references to the
        // interface that were made above.
        //

        NhTrace(
            TRACE_FLAG_IO,
            "AlgAcceptCompletionRoutine: socket %d accepting connection",
            ListeningSocket
            );
        Error =
            AlgCreateConnection(
                Interfacep,
                ListeningSocket,
                AcceptedSocket,
                Bufferp->Buffer,
                &Connectionp
                );
        if (Error) {
            InterlockedIncrement(
                reinterpret_cast<LPLONG>(&AlgStatistics.ConnectionsDropped)
                );
        } else {
            InterlockedIncrement(
                reinterpret_cast<LPLONG>(&AlgStatistics.ConnectionsAccepted)
                );
        }

        //
        // Finally, issue an accept operation for the next connection-request
        // on the listening socket. Note that the callee is responsible
        // for releasing the reference to the interface in case of a failure.
        //

        Error =
            AlgAcceptConnectionInterface(
                Interfacep,
                ListeningSocket,
                INVALID_SOCKET,
                Bufferp,
                NULL
                );
        RELEASE_LOCK(Interfacep);
        if (Error) { NhReleaseBuffer(Bufferp); }

    } while(FALSE);

    ALG_DEREFERENCE_INTERFACE(Interfacep);
    DEREFERENCE_ALG();
} // AlgAcceptCompletionRoutine


VOID
AlgCloseEndpointNotificationRoutine(
    ULONG ErrorCode,
    ULONG BytesTransferred,
    PNH_BUFFER Bufferp
    )

/*++

Routine Description:

    This routine is invoked upon notification of a close operation
    on a ALG transparent proxy stream socket.

Arguments:

    ErrorCode - Win32 status code for the I/O operation

    BytesTransferred - number of bytes in 'Bufferp'

    Bufferp - holds context information for the closed socket.
        Note that we are not allowed to release this buffer here.

Return Value:

    none.

Environment:

    Runs in the context of a wait-thread.
    A reference to the component will have been made on our behalf
    by 'NhAcceptStreamSocket' or 'NhConnectStreamSocket'.
    A reference to the interface will have been made on our behalf
    by whoever issued the I/O request.
    Both of these references are released here.

--*/

{
    SOCKET ClosedSocket;
    ULONG EndpointId;
    PALG_INTERFACE Interfacep;
    PROFILE("AlgCloseEndpointNotificationRoutine");
    do {
        ClosedSocket = (SOCKET)Bufferp->Socket;
        Interfacep = (PALG_INTERFACE)Bufferp->Context;
        EndpointId = PtrToUlong(Bufferp->Context2);
        NhTrace(
            TRACE_FLAG_IO,
            "AlgCloseEndpointNotificationRoutine: endpoint %d socket %d "
            "closed, error %d",
            EndpointId, ClosedSocket, ErrorCode
            );

#if 0
        PALG_ENDPOINT Endpointp;

        //
        // Lock the interface, and retrieve the endpoint whose socket has
        // been closed.
        //

        ACQUIRE_LOCK(Interfacep);
        Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);
        if (Endpointp) {
            AlgCloseActiveEndpoint(Endpointp, ClosedSocket);
        }
        RELEASE_LOCK(Interfacep);
#endif
    } while(FALSE);

    ALG_DEREFERENCE_INTERFACE(Interfacep);
    DEREFERENCE_ALG();
} // AlgCloseEndpointNotificationRoutine


VOID
AlgConnectEndpointCompletionRoutine(
    ULONG ErrorCode,
    ULONG BytesTransferred,
    PNH_BUFFER Bufferp
    )

/*++

Routine Description:

    This routine is invoked upon completion of a connect operation
    on a ALG transparent proxy stream socket.

Arguments:

    ErrorCode - Win32 status code for the I/O operation

    BytesTransferred - number of bytes in 'Bufferp'

    Bufferp - holds the context information for the endpoint.
        Note that we are not allowed to release this buffer here.

Return Value:

    none.

Environment:

    Runs in the context of a wait-thread.
    A reference to the component will have been made on our behalf
    by 'NhConnectStreamSocket'.
    A reference to the interface will have been made on our behalf
    by whoever issued the I/O request.
    Neither of these references may be released here; they are both
    released in the close-notification routine, which we are guaranteed
    will be invoked. (Eventually.)

--*/

{
    SOCKET ConnectedSocket;
    ULONG EndpointId;
    PALG_ENDPOINT Endpointp;
    ULONG Error;
    PALG_INTERFACE Interfacep;
    PROFILE("AlgConnectEndpointCompletionRoutine");
    do {
        ConnectedSocket = (SOCKET)Bufferp->Socket;
        Interfacep = (PALG_INTERFACE)Bufferp->Context;
        EndpointId = PtrToUlong(Bufferp->Context2);

        //
        // Acquire two additional references to the interface
        // for the endpoint-activation that we will initiate below,
        // lock the interface, and retrieve the endpoint.
        //

        EnterCriticalSection(&AlgInterfaceLock);
        if (!ALG_REFERENCE_INTERFACE(Interfacep)) {
            LeaveCriticalSection(&AlgInterfaceLock);
            break;
        }
        ALG_REFERENCE_INTERFACE(Interfacep);
        LeaveCriticalSection(&AlgInterfaceLock);
        ACQUIRE_LOCK(Interfacep);
        Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);

        //
        // First look for an error code.
        // If an error occurred and the interface is still active,
        // destroy the endpoint.
        // If the interface is inactive, we're done, since the endpoint
        // will have already been destroyed.
        // If the interface is active but the endpoint has already
        // been destroyed, end this connection-attempt.
        //

        if (ErrorCode) {
            if (Endpointp) {
                NhTrace(
                    TRACE_FLAG_IO,
                    "AlgConnectEndpointCompletionRoutine: deleting endpoint %d "
                    "on error %d", EndpointId, ErrorCode
                    );
                AlgDeleteActiveEndpoint(Endpointp);
            }
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            break;
        } else if (!ALG_INTERFACE_ACTIVE(Interfacep)) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgConnectEndpointCompletionRoutine: interface %d inactive",
                Interfacep->Index
                );
            break;
        } else if (!Endpointp) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgConnectEndpointCompletionRoutine: endpoint %d removed",
                EndpointId
                );
            break;
        }

        //
        // We now activate the endpoint, beginning data transfer.
        // Note that it is the caller's responsibility to release
        // the two new references to the interface if an error occurs.
        //

        NhTrace(
            TRACE_FLAG_IO,
            "AlgConnectEndpointCompletionRoutine: endpoint %d socket %d "
            "connected", EndpointId, ConnectedSocket
            );
        Error = AlgActivateActiveEndpoint(Interfacep, Endpointp);
        RELEASE_LOCK(Interfacep);

    } while(FALSE);

} // AlgConnectEndpointCompletionRoutine


VOID
AlgReadEndpointCompletionRoutine(
    ULONG ErrorCode,
    ULONG BytesTransferred,
    PNH_BUFFER Bufferp
    )

/*++

Routine Description:

    This routine is invoked upon completion of a read operation
    on a ALG transparent proxy stream socket.

    The contexts for all reads are the interface and endpoint-identifier
    corresponding to the socket, stored in 'Context' and 'Context2',
    respectively.

Arguments:

    ErrorCode - Win32 status code for the I/O operation

    BytesTransferred - number of bytes in 'Bufferp'

    Bufferp - holds data read from the socket

Return Value:

    none.

Environment:

    Runs in the context of a worker-thread which has just dequeued an
    I/O completion packet from the common I/O completion port with which
    our stream sockets are associated.
    A reference to the component will have been made on our behalf
    by 'NhReadStreamSocket'.
    A reference to the interface will have been made on our behalf
    by whoever issued the I/O request.

--*/

{
    ULONG EndpointId;
    PALG_ENDPOINT Endpointp;
    ULONG Error;
    PALG_INTERFACE Interfacep;
    PROFILE("AlgReadEndpointCompletionRoutine");
    do {
        Interfacep = (PALG_INTERFACE)Bufferp->Context;
        EndpointId = PtrToUlong(Bufferp->Context2);

        //
        // Acquire two additional references to the interface
        // for the followup requests that we will issue below,
        // lock the interface, and retrieve the endpoint.
        //

        EnterCriticalSection(&AlgInterfaceLock);
        if (!ALG_REFERENCE_INTERFACE(Interfacep)) {
            LeaveCriticalSection(&AlgInterfaceLock);
            NhReleaseBuffer(Bufferp);
            break;
        }
        ALG_REFERENCE_INTERFACE(Interfacep);
        LeaveCriticalSection(&AlgInterfaceLock);
        ACQUIRE_LOCK(Interfacep);
        Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);

        //
        // Process the read-completion. First we look for an error-code,
        // and if we find one, we decide whether to re-issue the read-request.
        // If the interface is still active, the error-code is non-fatal, and
        // the endpoint still exists, we reissue the read.
        //

        if (ErrorCode) {

            //
            // We won't be needing the second reference to the interface,
            // since we won't be calling 'AlgProcessMessage.
            //

            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgReadEndpointCompletionRoutine: error %d for endpoint %d",
                ErrorCode, EndpointId
                );
            if (!ALG_INTERFACE_ACTIVE(Interfacep) || !Endpointp) {
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhReleaseBuffer(Bufferp);
            } else if (NhIsFatalSocketError(ErrorCode)) {
                AlgDeleteActiveEndpoint(Endpointp);
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhReleaseBuffer(Bufferp);
                NhTrace(
                    TRACE_FLAG_IO,
                    "AlgReadEndpointCompletionRoutine: deleting endpoint %d "
                    "on fatal read-error %d", EndpointId, ErrorCode
                    );
            } else {

                //
                // We need to repost the buffer for another read operation,
                // so we now reissue a read for the same number of bytes as
                // before.
                //

                Error =
                    NhReadStreamSocket(
                        &AlgComponentReference,
                        Bufferp->Socket,
                        Bufferp,
                        Bufferp->BytesToTransfer,
                        Bufferp->TransferOffset,
                        AlgReadEndpointCompletionRoutine,
                        Bufferp->Context,
                        Bufferp->Context2
                        );
                if (Error) {
                    AlgDeleteActiveEndpoint(Endpointp);
                    RELEASE_LOCK(Interfacep);
                    ALG_DEREFERENCE_INTERFACE(Interfacep);
                    NhTrace(
                        TRACE_FLAG_IO,
                        "AlgReadEndpointCompletionRoutine: deleting endpoint "
                        "%d, NhReadStreamSocket=%d", EndpointId, Error
                        );
                    if (Error != ERROR_NETNAME_DELETED) {
                        NhWarningLog(
                            IP_ALG_LOG_RECEIVE_FAILED,
                            Error,
                            "%I",
                            NhQueryAddressSocket(Bufferp->Socket)
                            );
                    }
                    NhReleaseBuffer(Bufferp);
                    break;
                }

                RELEASE_LOCK(Interfacep);
            }

            break;
        } else if (!BytesTransferred) {

            //
            // Zero bytes were read from the endpoint's socket.
            // This indicates that the sender has closed the socket.
            // We now propagate the closure to the alternate socket
            // for the endpoint. When the 'other' sender is done,
            // this endpoint will be removed altogether.
            //

            NhTrace(
                TRACE_FLAG_IO,
                "AlgReadEndpointCompletionRoutine: endpoint %d socket %d "
                "closed", EndpointId, Bufferp->Socket
                );
            if (Endpointp) {
                AlgCloseActiveEndpoint(Endpointp, Bufferp->Socket);
            }
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhReleaseBuffer(Bufferp);
            break;
        }

        //
        // The original request completed successfully.
        // Now see if the interface and endpoint are operational and,
        // if not, return control.
        //

        if (!ALG_INTERFACE_ACTIVE(Interfacep)) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhReleaseBuffer(Bufferp);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgReadEndpointCompletionRoutine: interface %d inactive",
                Interfacep->Index
                );
            break;
        } else if (!Endpointp) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhReleaseBuffer(Bufferp);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgReadEndpointCompletionRoutine: endpoint %d not found",
                EndpointId
                );
            break;
        }

        //
        // Record the number of bytes read, and issue a read-request
        // for the remainder if necessary. Otherwise, process the completed
        // message.
        //

        NhTrace(
            TRACE_FLAG_IO,
            "AlgReadEndpointCompletionRoutine: endpoint %d socket %d read %d "
            "bytes", EndpointId, Bufferp->Socket, BytesTransferred
            );
        ASSERT(BytesTransferred <= Bufferp->BytesToTransfer);
        Bufferp->BytesToTransfer -= BytesTransferred;
        Bufferp->TransferOffset += BytesTransferred;

        if (Bufferp->BytesToTransfer > 0 &&
            AlgIsFullMessage(
                reinterpret_cast<CHAR*>(Bufferp->Buffer),
                Bufferp->TransferOffset
                ) == NULL) {

            //
            // Read the remainder of the message, after releasing
            // the second reference to the interface, which is needed
            // only when we call 'AlgProcessMessage'.
            //

            ALG_DEREFERENCE_INTERFACE(Interfacep);

            Error =
                NhReadStreamSocket(
                    &AlgComponentReference,
                    Bufferp->Socket,
                    Bufferp,
                    Bufferp->BytesToTransfer,
                    Bufferp->TransferOffset,
                    AlgReadEndpointCompletionRoutine,
                    Bufferp->Context,
                    Bufferp->Context2
                    );
            if (Error) {
                AlgDeleteActiveEndpoint(Endpointp);
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhTrace(
                    TRACE_FLAG_IO,
                    "AlgReadEndpointCompletionRoutine: deleting endpoint "
                    "%d, NhReadStreamSocket=%d", EndpointId, Error
                    );
                if (Error != ERROR_NETNAME_DELETED) {
                    NhWarningLog(
                        IP_ALG_LOG_RECEIVE_FAILED,
                        Error,
                        "%I",
                        NhQueryAddressSocket(Bufferp->Socket)
                        );
                }
                NhReleaseBuffer(Bufferp);
                break;
            }
        } else {

            //
            // We've finished reading something. Process it.
            //

            AlgProcessMessage(Interfacep, Endpointp, Bufferp);
        }

        RELEASE_LOCK(Interfacep);

    } while(FALSE);

    ALG_DEREFERENCE_INTERFACE(Interfacep);
    DEREFERENCE_ALG();

} // AlgReadEndpointCompletionRoutine


VOID
AlgWriteEndpointCompletionRoutine(
    ULONG ErrorCode,
    ULONG BytesTransferred,
    PNH_BUFFER Bufferp
    )

/*++

Routine Description:

    This routine is invoked upon completion of a write-operation
    on a stream socket for a ALG control-channel connection.

    The contexts for all writes are the interface and endpoint-identifier
    corresponding to the socket, stored in 'Context' and 'Context2',
    respectively.

Arguments:

    ErrorCode - Win32 status code for the I/O operation

    BytesTransferred - number of bytes in 'Bufferp'

    Bufferp - holds data read from the stream socket

Return Value:

    none.

Environment:

    Runs in the context of a worker-thread which has just dequeued an
    I/O completion packet from the common I/O completion port with which our
    stream sockets are associated.
    A reference to the component will have been made on our behalf
    by 'NhWriteStreamSocket'.
    A reference to the interface will have been made on our behalf
    by whoever issued the I/O request.

--*/

{
    ULONG Error;
    ULONG EndpointId;
    PALG_ENDPOINT Endpointp;
    PALG_INTERFACE Interfacep;
    PROFILE("AlgWriteEndpointCompletionRoutine");
    do {
        Interfacep = (PALG_INTERFACE)Bufferp->Context;
        EndpointId = PtrToUlong(Bufferp->Context2);

        //
        // Acquire an additional reference to the interface
        // for the followup requests that we will issue below,
        // lock the interface, and retrieve the endpoint.
        //

        EnterCriticalSection(&AlgInterfaceLock);
        if (!ALG_REFERENCE_INTERFACE(Interfacep)) {
            LeaveCriticalSection(&AlgInterfaceLock);
            NhReleaseBuffer(Bufferp);
            break;
        }
        LeaveCriticalSection(&AlgInterfaceLock);
        ACQUIRE_LOCK(Interfacep);
        Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);

        //
        // Process the write-completion. First we look for an error-code,
        // and if we find one, we decide whether to re-issue the write-request.
        // If the interface is still active, the error-code is non-fatal, and
        // the endpoint still exists, we reissue the write.
        //

        if (ErrorCode) {
            NhTrace(
                TRACE_FLAG_IO,
                "AlgWriteEndpointCompletionRoutine: error %d for endpoint %d",
                ErrorCode, EndpointId
                );
            if (!ALG_INTERFACE_ACTIVE(Interfacep) || !Endpointp) {
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhReleaseBuffer(Bufferp);
            } else if (NhIsFatalSocketError(ErrorCode)) {
                AlgDeleteActiveEndpoint(Endpointp);
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhReleaseBuffer(Bufferp);
                NhTrace(
                    TRACE_FLAG_IO,
                    "AlgWriteEndpointCompletionRoutine: deleting endpoint %d "
                    "on fatal write-error %d", EndpointId, ErrorCode
                    );
            } else {

                //
                // We need to repost the buffer for another write operation,
                // so we now reissue a write for the same number of bytes
                // as before.
                //

                Error =
                    NhWriteStreamSocket(
                        &AlgComponentReference,
                        Bufferp->Socket,
                        Bufferp,
                        Bufferp->BytesToTransfer,
                        Bufferp->TransferOffset,
                        AlgWriteEndpointCompletionRoutine,
                        Bufferp->Context,
                        Bufferp->Context2
                        );
                if (Error) {
                    AlgDeleteActiveEndpoint(Endpointp);
                    RELEASE_LOCK(Interfacep);
                    ALG_DEREFERENCE_INTERFACE(Interfacep);
                    NhTrace(
                        TRACE_FLAG_IO,
                        "AlgWriteEndpointCompletionRoutine: deleting endpoint "
                        "%d, NhWriteStreamSocket=%d", EndpointId, Error
                        );
                    NhWarningLog(
                        IP_ALG_LOG_SEND_FAILED,
                        Error,
                        "%I",
                        NhQueryAddressSocket(Bufferp->Socket)
                        );
                    NhReleaseBuffer(Bufferp);
                    break;
                }

                RELEASE_LOCK(Interfacep);
            }

            break;
        }

        //
        // The original request completed successfully.
        // Now see if the interface and endpoint are operational and,
        // if not, return control.
        //

        if (!ALG_INTERFACE_ACTIVE(Interfacep)) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhReleaseBuffer(Bufferp);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgWriteEndpointCompletionRoutine: interface %d inactive",
                Interfacep->Index
                );
            break;
        } else if (!Endpointp) {
            RELEASE_LOCK(Interfacep);
            ALG_DEREFERENCE_INTERFACE(Interfacep);
            NhReleaseBuffer(Bufferp);
            NhTrace(
                TRACE_FLAG_IO,
                "AlgWriteEndpointCompletionRoutine: endpoint %d not found",
                EndpointId
                );
            break;
        }

        //
        // Record the number of bytes written, and issue a write-request
        // for the remainder if necessary. Otherwise, we are done,
        // and we return to reading from the 'other' socket for the
        // control-channel.
        //

        NhTrace(
            TRACE_FLAG_IO,
            "AlgWriteEndpointCompletionRoutine: endpoint %d socket %d wrote %d "
            "bytes", EndpointId, Bufferp->Socket, BytesTransferred
            );

        ASSERT(BytesTransferred <= Bufferp->BytesToTransfer);
        Bufferp->BytesToTransfer -= BytesTransferred;
        Bufferp->TransferOffset += BytesTransferred;
        if (Bufferp->BytesToTransfer) {

            //
            // Write the remainder of the message
            //

            Error =
                NhWriteStreamSocket(
                    &AlgComponentReference,
                    Bufferp->Socket,
                    Bufferp,
                    Bufferp->BytesToTransfer,
                    Bufferp->TransferOffset,
                    AlgWriteEndpointCompletionRoutine,
                    Bufferp->Context,
                    Bufferp->Context2
                    );
            if (Error) {
                AlgDeleteActiveEndpoint(Endpointp);
                RELEASE_LOCK(Interfacep);
                ALG_DEREFERENCE_INTERFACE(Interfacep);
                NhTrace(
                    TRACE_FLAG_IO,
                    "AlgWriteEndpointCompletionRoutine: deleting endpoint %d, "
                    "NhWriteStreamSocket=%d", EndpointId, Error
                    );
                NhWarningLog(
                    IP_ALG_LOG_SEND_FAILED,
                    Error,
                    "%I",
                    NhQueryAddressSocket(Bufferp->Socket)
                    );
                NhReleaseBuffer(Bufferp);
                break;
            }
        } else {
            SOCKET Socket;
            ULONG UserFlags;

            //
            // We now go back to reading from the other socket of the
            // endpoint, by issuing the next read on the endpoint's other
            // socket. Note that it is the responsibility of the callee
            // to release the reference to the interface if a failure occurs.
            //

            UserFlags = Bufferp->UserFlags;
            if (UserFlags & ALG_BUFFER_FLAG_FROM_ACTUAL_CLIENT) {
                Socket = Endpointp->HostSocket;
                UserFlags &= ~(ULONG)ALG_BUFFER_FLAG_CONTINUATION;
                UserFlags |= ALG_BUFFER_FLAG_FROM_ACTUAL_CLIENT;
            } else {
                Socket = Endpointp->ClientSocket;
                UserFlags &= ~(ULONG)ALG_BUFFER_FLAG_CONTINUATION;
                UserFlags |= ALG_BUFFER_FLAG_FROM_ACTUAL_HOST;
            }
            NhReleaseBuffer(Bufferp);
            Error =
                AlgReadActiveEndpoint(
                    Interfacep,
                    Endpointp,
                    Socket,
                    UserFlags
                    );
            if (Error) {
                NhTrace(
                    TRACE_FLAG_IO,
                    "AlgWriteEndpointCompletionRoutine: deleting endpoint %d, "
                    "AlgReadActiveEndpoint=%d", EndpointId, Error
                    );
                AlgDeleteActiveEndpoint(Endpointp);
                RELEASE_LOCK(Interfacep);
                NhWarningLog(
                    IP_ALG_LOG_RECEIVE_FAILED,
                    Error,
                    "%I",
                    NhQueryAddressSocket(Socket)
                    );
                break;
            }
        }

        RELEASE_LOCK(Interfacep);

    } while(FALSE);

    ALG_DEREFERENCE_INTERFACE(Interfacep);
    DEREFERENCE_ALG();

} // AlgWriteEndpointCompletionRoutine