/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    loopsub.c

Abstract:

    This module implements common functions for the loopback Transport
    Provider driver for NT LAN Manager.

Author:

    Chuck Lenzmeier (chuckl)    15-Aug-1991

Revision History:

--*/

#include "loopback.h"


VOID
LoopCopyData (
    IN PMDL Destination,
    IN PMDL Source,
    IN ULONG Length
    )

/*++

Routine Description:

    This routine copies data from the storage described by one MDL chain
    into the storage described by another MDL chain.

Arguments:

    Destination - Pointer to first MDL in Destination chain

    Source - Pointer to first MDL in Source chain

    Length - Amount of data to copy.  Caller must ensure that the Source
        and Destination chains are at least this long.

Return Value:

    None.

--*/

{
    PCHAR sourceAddress;
    ULONG sourceLength;

    PCHAR destinationAddress;
    ULONG destinationLength;

    ULONG copyLength;

    //
    // Get the virtual address of the first source buffer, mapping it
    // if necessary.  Also get the length of the buffer.
    //

    sourceAddress = MmGetSystemAddressForMdl( Source );
    sourceLength = MmGetMdlByteCount( Source );

    //
    // Get the virtual address of the first destination buffer, mapping
    // it if necessary.  Also get the length of the buffer.
    //

    destinationAddress = MmGetSystemAddressForMdl( Destination );
    destinationLength = MmGetMdlByteCount( Destination );

    //
    // Loop copying data.
    //

    do {

        //
        // The amount to copy in this pass is the minimum of 1) the
        // amount remaining in the current source buffer, 2) the amount
        // remaining in the current destination buffer, and 3) the
        // amount remaining in the overall copy operation.
        //

        copyLength = sourceLength;
        if ( copyLength > destinationLength ) copyLength = destinationLength;
        if ( copyLength > Length ) copyLength = Length;

        //
        // Copy from the source buffer into the destination buffer.
        //

#ifndef TIMING
        IF_DEBUG(LOOP4) {
            DbgPrint( "      copying %lx bytes from %lx to %lx\n",
                        copyLength, sourceAddress, destinationAddress );
            DbgPrint( "      source data: %lx, %lx\n",
                        *(PULONG)sourceAddress, *((PULONG)sourceAddress + 1) );
        }
        RtlMoveMemory( destinationAddress, sourceAddress, copyLength );
#else
        if ( (NtGlobalFlag & 0x20000000) == 0 ) {
            RtlMoveMemory( destinationAddress, sourceAddress, copyLength );
        } else {
            RtlMoveMemory(
                destinationAddress,
                sourceAddress,
                (copyLength > 200) ? 200 : copyLength
                );
        }
#endif

        //
        // If all of the requested data has been copied, leave.
        //

        Length -= copyLength;

        if ( Length == 0 ) {

            return;

        }

        //
        // If we have used up all of the current source buffer, move to
        // the next buffer.  Get the virtual address of the next buffer,
        // mapping it if necessary.  Also get the length of the buffer.
        // If we haven't used up the current source buffer, simply
        // update the source pointer and the remaining length.
        //

        if ( copyLength == sourceLength ) {

            Source = Source->Next;

            sourceAddress = MmGetSystemAddressForMdl( Source );
            sourceLength = MmGetMdlByteCount( Source );

        } else {

            sourceAddress += copyLength;
            sourceLength -= copyLength;

        }

        //
        // If we have used up all of the current destination buffer,
        // move to the next buffer.  Get the virtual address of the next
        // buffer, mapping it if necessary.  Also get the length of the
        // buffer.  If we haven't used up the current destination
        // buffer, simply update the destination pointer and the
        // remaining length.
        //

        if ( copyLength == destinationLength ) {

            Destination = Destination->Next;

            destinationAddress = MmGetSystemAddressForMdl( Destination );
            destinationLength = MmGetMdlByteCount( Destination );

        } else {

            destinationAddress += copyLength;
            destinationLength -= copyLength;

        }

    } while ( TRUE );

    //
    // Can't get here.
    //

    ASSERTMSG( FALSE, "Can't get here!" );

} // LoopCopyData


PVOID
LoopGetConnectionContextFromEa (
    PFILE_FULL_EA_INFORMATION Ea
    )

/*++

Routine Description:

    This routine returns the connection context specified in an EA.

Arguments:

    Ea - Pointer to EA buffer

Return Value:

    PVOID - Connection context

--*/

{
    PVOID ctx;

    RtlMoveMemory( &ctx, &Ea->EaName[Ea->EaNameLength + 1], sizeof(PVOID) );

    return ctx;

} // LoopGetConnectionContextFromEa


NTSTATUS
LoopGetEndpointTypeFromEa (
    PFILE_FULL_EA_INFORMATION Ea,
    PBLOCK_TYPE Type
    )

/*++

Routine Description:

    This routine determines whether an EA describes an address or a
    connection.

Arguments:

    Ea - Pointer to EA buffer

    Type - Returns block type

Return Value:

    NTSTATUS - STATUS_INVALID_PARAMETER if EA is not valid

--*/

{
    //
    // First check for address type.
    //

    if ( (Ea->EaNameLength == TDI_TRANSPORT_ADDRESS_LENGTH) &&
         (strcmp( Ea->EaName, TdiTransportAddress ) == 0) ) {
        *Type = BlockTypeLoopEndpoint;
        return STATUS_SUCCESS;
    }

    //
    // Next check for connection type.
    //

    if ( (Ea->EaNameLength == TDI_CONNECTION_CONTEXT_LENGTH) &&
         (strcmp( Ea->EaName, TdiConnectionContext ) == 0) ) {
        *Type = BlockTypeLoopConnection;
        return STATUS_SUCCESS;
    }

    //
    // Invalid type.
    //

    return STATUS_INVALID_PARAMETER;

} // LoopGetEndpointTypeFromEa


NTSTATUS
LoopParseAddress (
    IN PTA_NETBIOS_ADDRESS Address,
    OUT PCHAR NetbiosName
    )

/*++

Routine Description:

    This routine parses the input AddressString according to conventions
    defined for transport address strings as defined in the TDI
    specification.  It converts the name from that form into a "standard"
    NetBIOS name.

Arguments:

    Address - Pointer to a transport address in the TDI format.

    NetbiosName - A 16-character space into which the NULL-terminated
        NetBIOS name is written.

Return Value:

    NTSTATUS - Indicates whether the address string was valid.

--*/

{
    //
    // If the input address is not a single unique address in NetBIOS
    // format, reject it.
    //

    if ( (Address->TAAddressCount != 1) ||
         (Address->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS) ||
         (Address->Address[0].AddressLength != sizeof(TDI_ADDRESS_NETBIOS)) ||
         (Address->Address[0].Address[0].NetbiosNameType !=
                                    TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) ) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Copy the name into the output buffer.
    //

    RtlMoveMemory(
        NetbiosName,
        Address->Address[0].Address[0].NetbiosName,
        NETBIOS_NAME_LENGTH
        );

    return STATUS_SUCCESS;

} // LoopParseAddress


NTSTATUS
LoopParseAddressFromEa (
    IN PFILE_FULL_EA_INFORMATION Ea,
    OUT PCHAR NetbiosName
    )

/*++

Routine Description:

    This routine parses the input EA according to conventions defined
    for transport address strings as defined in the TDI specification.
    It converts the name from that form into a "standard" NetBIOS name.

Arguments:

    Ea - Pointer to an EA in the TDI format.

    NetbiosName - A 16-character space into which the NULL-terminated
        NetBIOS name is written.

Return Value:

    NTSTATUS - Indicates whether the address string was valid.

--*/

{
    TA_NETBIOS_ADDRESS nbAddress;

    if ( Ea->EaValueLength != sizeof(TA_NETBIOS_ADDRESS) ) {
        return STATUS_INVALID_PARAMETER;
    }

    RtlMoveMemory(
        &nbAddress,
        &Ea->EaName[Ea->EaNameLength + 1],
        sizeof(TA_NETBIOS_ADDRESS)
        );

    //
    // Pass the value portion of the EA, which is a TRANSPORT_ADDRESS,
    // to LoopParseAddress.
    //

    return LoopParseAddress( &nbAddress, NetbiosName );

} // LoopParseAddressFromEa