//================================================================================
// Copyright (C) 1997 Microsoft Corporation
// Author: RameshV
// Description: implements the basic structures for managing (multicast) scopes
// ThreadSafe: no
// Locks: none
// Please read stdinfo.txt for programming style.
//================================================================================
#include    <mm.h>
#include    <array.h>
#include    <opt.h>
#include    <optl.h>
#include    <optclass.h>
#include    <bitmask.h>
#include    <range.h>
#include    <reserve.h>
#include    <dhcp.h>
#include    <winnls.h>

//BeginExport(typedef)
typedef struct _M_SUBNET {
    LPVOID                         ServerPtr;     // Ptr to Server object
    union {
        struct {                                  // for normal subnet.
            DWORD                      Address;
            DWORD                      Mask;
            DWORD                      SuperScopeId;  // unused for MCAST scopes
        };
        struct {                                  // for multicast scope
            DWORD                      MScopeId;
            LPWSTR                     LangTag;       // the language tag for multicast scope
            BYTE                       TTL;
        };
    };
    DWORD                          fSubnet;       // TRUE => Subnet, FALSE => MSCOPE
    DWORD                          State;
    DWORD                          Policy;
    DATE_TIME                      ExpiryTime;     // Scope Lifetime. Currently used for MCast only.
    M_OPTCLASS                     Options;
    ARRAY                          Ranges;
    ARRAY                          Exclusions;
    M_RESERVATIONS                 Reservations;
    ARRAY                          Servers;       // future use, Server-Server protocol
    LPWSTR                         Name;
    LPWSTR                         Description;
} M_SUBNET, *PM_SUBNET, *LPM_SUBNET;
//EndExport(typedef)

// the following are the flags bits used for subnet object.
#define DEFAULT_SCOPE   0x01
#define IS_DEFAULT_SCOPE( _subnet )     ((_subnet)->Flags & DEFAULT_SCOPE == DEFAULT_SCOPE )
#define SET_DEFAULT_SCOPE( _subnet )    ((_subnet)->Flags |= DEFAULT_SCOPE )
#define RESET_DEFAULT_SCOPE( _subnet )  ((_subnet)->Flags &= ~DEFAULT_SCOPE)


//BeginExport(enum)
enum /* Anonymous */ {
    AddressPolicyNone = 0,
    AddressPolicySequential,
    AddressPolicyRoundRobin
};
//EndExport(enum)

//BeginExport(function)
DWORD
MemSubnetInit(
    OUT     PM_SUBNET             *pSubnet,
    IN      DWORD                  Address,
    IN      DWORD                  Mask,
    IN      DWORD                  State,
    IN      DWORD                  SuperScopeId,
    IN      LPWSTR                 Name,
    IN      LPWSTR                 Description
) //EndExport(function)
{
    DWORD                          Error;
    DWORD                          Size;
    PM_SUBNET                      Subnet;

    AssertRet(pSubnet, ERROR_INVALID_PARAMETER);
    AssertRet( !(CLASSD_HOST_ADDR(Address)||CLASSE_HOST_ADDR(Address)),
               ERROR_INVALID_PARAMETER );
    Require((Address&Mask));

    *pSubnet = NULL;

    Size = ROUND_UP_COUNT(sizeof(*Subnet), ALIGN_WORST);
    Size += sizeof(WCHAR) * (Name?(1+wcslen(Name)):0);
    Size += sizeof(WCHAR) * (Description?(1+wcslen(Description)):0);

    Subnet = MemAlloc(Size);
    if( NULL == Subnet) return ERROR_NOT_ENOUGH_MEMORY;

    Size = ROUND_UP_COUNT(sizeof(*Subnet), ALIGN_WORST);

    Subnet->Name = Subnet->Description = NULL;
    if( Name ) {
        Subnet->Name = (LPWSTR)(Size + (LPBYTE)Subnet);
        wcscpy(Subnet->Name, Name);
        Size += sizeof(WCHAR) * ( 1 + wcslen(Name));
    }

    if( Description ) {
        Subnet->Description = (LPWSTR)( Size + (LPBYTE)Subnet );
        wcscpy(Subnet->Description, Description);
    }

    Subnet->ServerPtr    = NULL;
    Subnet->Address      = Address;
    Subnet->Mask         = Mask;
    Subnet->State        = State;
    Subnet->SuperScopeId = SuperScopeId;
    Subnet->fSubnet      = TRUE;
    Subnet->Policy       = AddressPolicyNone;

    Error = MemOptClassInit(&Subnet->Options);
    if( ERROR_SUCCESS != Error ) { MemFree(Subnet); return Error; }

    Error = MemArrayInit(&Subnet->Ranges);
    if( ERROR_SUCCESS != Error ) { MemFree(Subnet); return Error; }

    Error = MemArrayInit(&Subnet->Exclusions);
    if( ERROR_SUCCESS != Error ) { MemFree(Subnet); return Error; }

    Error = MemArrayInit(&Subnet->Servers);
    if( ERROR_SUCCESS != Error ) { MemFree(Subnet); return Error; }

    Error = MemReserveInit(&Subnet->Reservations);
    if( ERROR_SUCCESS != Error ) { MemFree(Subnet); return Error; }

    *pSubnet = Subnet;
    return ERROR_SUCCESS;
}

VOID
GetLangTag(
    WCHAR LangTag[]
    )
{
    WCHAR b1[8], b2[8];

    b1[0] = b2[0] = L'\0';
    GetLocaleInfoW(
        LOCALE_SYSTEM_DEFAULT, LOCALE_SISO639LANGNAME,
        b1, sizeof(b1)/sizeof(b1[0])
        );
    
    GetLocaleInfoW(
        LOCALE_SYSTEM_DEFAULT, LOCALE_SISO3166CTRYNAME,
        b2, sizeof(b2)/sizeof(b2[0])
        );
    
    if (_wcsicmp(b1, b2))
        wsprintf(LangTag, L"%s-%s", b1, b2);
    else
        wcscpy(LangTag, b1);
}

//BeginExport(function)
DWORD
MemMScopeInit(
    OUT     PM_SUBNET             *pMScope,
    IN      DWORD                  MScopeId,
    IN      DWORD                  State,
    IN      DWORD                  AddressPolicy,
    IN      BYTE                   TTL,
    IN      LPWSTR                 Name,
    IN      LPWSTR                 Description,
    IN      LPWSTR                 LangTag,
    IN      DATE_TIME              ExpiryTime
) //EndExport(function)
{
    DWORD                          Error;
    DWORD                          Size;
    PM_SUBNET                      MScope;
    WCHAR                          DummyLangTag[100];
    
    AssertRet(pMScope, ERROR_INVALID_PARAMETER);
    //AssertRet(MScopeId, ERROR_INVALID_PARAMETER);
    Require(LangTag);

    if( NULL == LangTag ) {
        LangTag = DummyLangTag;
        GetLangTag(DummyLangTag);
    }
    
    *pMScope = NULL;

    Size = ROUND_UP_COUNT(sizeof(*MScope), ALIGN_WORST);
    Size += sizeof(WCHAR) * (Name?(1+wcslen(Name)):0);
    Size += sizeof(WCHAR) * (Description?(1+wcslen(Description)):0);
    Size += sizeof(WCHAR) * (1+wcslen(LangTag));

    MScope = MemAlloc(Size);
    if( NULL == MScope) return ERROR_NOT_ENOUGH_MEMORY;

    Size = ROUND_UP_COUNT(sizeof(*MScope), ALIGN_WORST);

    MScope->Name = MScope->Description = MScope->LangTag = NULL;

    if( Name ) {
        MScope->Name = (LPWSTR)(Size + (LPBYTE)MScope);
        wcscpy(MScope->Name, Name);
        Size += sizeof(WCHAR) * ( 1 + wcslen(Name));
    }

    if( Description ) {
        MScope->Description = (LPWSTR)( Size + (LPBYTE)MScope );
        wcscpy(MScope->Description, Description);
        Size += sizeof(WCHAR) * ( 1 + wcslen(Description));
    }

    MScope->LangTag = (LPWSTR)( Size + (LPBYTE)MScope );
    wcscpy(MScope->LangTag, LangTag);

    MScope->ServerPtr    = NULL;
    MScope->MScopeId     = MScopeId;
    MScope->State        = State;
    MScope->TTL          = TTL;
    MScope->fSubnet      = FALSE;
    MScope->Policy       = AddressPolicy;
    MScope->ExpiryTime   = ExpiryTime;

    Error = MemOptClassInit(&MScope->Options);
    if( ERROR_SUCCESS != Error ) { MemFree(MScope); return Error; }

    Error = MemArrayInit(&MScope->Ranges);
    if( ERROR_SUCCESS != Error ) { MemFree(MScope); return Error; }

    Error = MemArrayInit(&MScope->Exclusions);
    if( ERROR_SUCCESS != Error ) { MemFree(MScope); return Error; }

    Error = MemArrayInit(&MScope->Servers);
    if( ERROR_SUCCESS != Error ) { MemFree(MScope); return Error; }

    Error = MemReserveInit(&MScope->Reservations);
    if( ERROR_SUCCESS != Error ) { MemFree(MScope); return Error; }

    *pMScope = MScope;
    return ERROR_SUCCESS;
}

//BeginExport(inline)
DWORD       _inline
MemSubnetCleanup(
    IN OUT  PM_SUBNET              Subnet
)
{
    DWORD                          Error;

    AssertRet(Subnet, ERROR_INVALID_PARAMETER);
    Require(Subnet->Address&Subnet->Mask);

    Error = MemOptClassCleanup(&Subnet->Options);
    if( ERROR_SUCCESS != Error ) return Error;

    Error = MemArrayCleanup(&Subnet->Ranges);
    if( ERROR_SUCCESS != Error ) return Error;

    Error = MemArrayCleanup(&Subnet->Exclusions);
    if( ERROR_SUCCESS != Error ) return Error;

    Error = MemArrayCleanup(&Subnet->Servers);
    if( ERROR_SUCCESS != Error ) return Error;

    Error = MemReserveCleanup(&Subnet->Reservations);
    if( ERROR_SUCCESS != Error ) return Error;

    MemFree(Subnet);
    return ERROR_SUCCESS;
}
//EndExport(inline)

//BeginExport(function)
DWORD                                             // SUCCESS if either of Excl or Range get filled, else FILE_NOT_FOUND
MemSubnetGetAddressInfo(
    IN      PM_SUBNET              Subnet,
    IN      DWORD                  Address,
    OUT     PM_RANGE              *Range,         // OPTIONAL -- filled if a range could be found -- even if excluded
    OUT     PM_EXCL               *Excl,          // OPTIONAL -- filled if an exclusion could be found
    OUT     PM_RESERVATION        *Reservation    // OPTIONAL -- filled with  a matching reservation, if found
) //EndExport(function)
{
    ARRAY_LOCATION                 Location;
    DWORD                          Error;
    DWORD                          RetError;
    PM_RANGE                       ThisRange;
    PM_EXCL                        ThisExcl;

    AssertRet(Subnet && (Range || Excl || Reservation), ERROR_INVALID_PARAMETER );

    if( Subnet->fSubnet && (Address & Subnet->Mask) != Subnet->Address )
        return ERROR_FILE_NOT_FOUND;              // it is ok for MSCOPE objects, as Address refers to ScopeId

    RetError = ERROR_FILE_NOT_FOUND;
    if( Range ) {
        *Range = NULL;
        Error = MemArrayInitLoc(&Subnet->Ranges, &Location);
        while( ERROR_FILE_NOT_FOUND != Error ) {
            Require(ERROR_SUCCESS == Error);

            Error = MemArrayGetElement(&Subnet->Ranges, &Location, (LPVOID *)&ThisRange);
            Require(ERROR_SUCCESS == Error && ThisRange);

            if( ThisRange->Start <= Address && Address <= ThisRange->End ) {
                *Range = ThisRange;
                RetError = ERROR_SUCCESS;
                break;
            }

            Error = MemArrayNextLoc(&Subnet->Ranges, &Location);
        }
    }

    if( Excl ) {
        *Excl = NULL;
        Error = MemArrayInitLoc(&Subnet->Exclusions, &Location);
        while( ERROR_FILE_NOT_FOUND != Error ) {
            Require(ERROR_SUCCESS == Error);

            Error = MemArrayGetElement(&Subnet->Exclusions, &Location, (LPVOID *)&ThisExcl);
            Require(ERROR_SUCCESS == Error && ThisExcl);

            if( ThisExcl->Start <= Address && Address <= ThisExcl->End ) {
                *Excl = ThisExcl;
                RetError = ERROR_SUCCESS;
                break;
            }

            Error = MemArrayNextLoc(&Subnet->Exclusions, &Location);
        }
    }

    if( Reservation ) {
        *Reservation = NULL;

        Error = MemReserveFindByAddress(&Subnet->Reservations, Address, Reservation);
        if( ERROR_SUCCESS == Error ) RetError = ERROR_SUCCESS;
    }

    return RetError;
}

//BeginExport(function)
DWORD                                             // ERROR_SUCCESS on finding a collition, else ERROR_FILE_NOT_FOUND
MemSubnetFindCollision(
    IN OUT  PM_SUBNET              Subnet,
    IN      DWORD                  Start,
    IN      DWORD                  End,
    OUT     PM_RANGE              *Range,         // OPTIONAL
    OUT     PM_EXCL               *Excl           // OPTIONAL
) //EndExport(function)
{
    ARRAY_LOCATION                 Location;
    DWORD                          Error;
    DWORD                          RetError;
    DWORD                          Cond;
    PM_RANGE                       ThisRange;
    PM_EXCL                        ThisExcl;

    Require(Subnet && (Range || Excl));
    if( Subnet->fSubnet ) {                       // checks ommitted for MCAST scopes.
        if( (Start & Subnet->Mask) != (End & Subnet->Mask) )
            return ERROR_INVALID_PARAMETER;
        if( (Start & Subnet->Mask) != (Subnet->Address & Subnet->Mask) )
            return ERROR_INVALID_PARAMETER;
    }

    RetError = ERROR_FILE_NOT_FOUND;
    if(Range) {
        *Range = NULL;
        Error = MemArrayInitLoc(&Subnet->Ranges, &Location);
        while( ERROR_FILE_NOT_FOUND != Error ) {
            Require(ERROR_SUCCESS == Error);

            Error = MemArrayGetElement(&Subnet->Ranges, &Location, (LPVOID *)&ThisRange);
            Require(ERROR_SUCCESS == Error && ThisRange);

            Cond = MemRangeCompare(Start,End, ThisRange->Start, ThisRange->End);
            if( Cond != X_LESSTHAN_Y && Cond != Y_LESSTHAN_X ) {
                // Collision has occured
                *Range = ThisRange;
                RetError = ERROR_SUCCESS;
                break;
            }

            Error = MemArrayNextLoc(&Subnet->Ranges, &Location);
        }
    }

    if( Excl ) {
        *Excl = NULL;
        Error = MemArrayInitLoc(&Subnet->Exclusions, &Location);
        while( ERROR_FILE_NOT_FOUND != Error ) {
            Require(ERROR_SUCCESS == Error);

            Error = MemArrayGetElement(&Subnet->Exclusions, &Location, (LPVOID *)&ThisExcl);
            Require(ERROR_SUCCESS == Error && ThisExcl);

            Cond = MemRangeCompare(Start,End, ThisExcl->Start, ThisExcl->End);
            if( Cond != X_LESSTHAN_Y && Cond != Y_LESSTHAN_X ) {
                *Excl = ThisExcl;
                RetError = ERROR_SUCCESS;
                break;
            }

            Error = MemArrayNextLoc(&Subnet->Exclusions, &Location);
        }
    }

    return RetError;
}



//BeginExport(function)
DWORD                                             // ERROR_OBJECT_ALREADY_EXISTS on collision
MemSubnetAddRange(                                // check if the range is valid, and only then add it
    IN OUT  PM_SUBNET              Subnet,
    IN      DWORD                  Start,
    IN      DWORD                  End,
    IN      DWORD                  State,
    IN      ULONG                  BootpAllocated,
    IN      ULONG                  MaxBootpAllowed,
    OUT     PM_RANGE              *OverlappingRange
) //EndExport(function)
{
    DWORD                          Error;
    DWORD                          LocalError;
    PM_RANGE                       NewRange;

    AssertRet(Subnet && OverlappingRange, ERROR_INVALID_PARAMETER);

    if( Subnet->fSubnet ) {
        if( (Subnet->Address & Subnet->Mask) != (Start & Subnet->Mask) ||
            (Start & Subnet->Mask)  != (End & Subnet->Mask) )
            return ERROR_INVALID_PARAMETER;
    } else {
        if (!CLASSD_HOST_ADDR(Start) || !CLASSD_HOST_ADDR(End)) {
            return ERROR_INVALID_PARAMETER;
        }
    }

    if( Start > End ) return ERROR_INVALID_PARAMETER;

    *OverlappingRange = NULL;
    Error = MemSubnetFindCollision(
        Subnet,
        Start,
        End,
        OverlappingRange,
        NULL
    );
    if(ERROR_FILE_NOT_FOUND != Error ) {          // collision with a range?
        Require(ERROR_SUCCESS == Error);
        return ERROR_OBJECT_ALREADY_EXISTS;
    }

    NewRange = MemAlloc(sizeof(*NewRange));
    if( NULL == NewRange ) return ERROR_NOT_ENOUGH_MEMORY;

    Error = MemRangeInit(
        NewRange, Start, End, Subnet->fSubnet ? Subnet->Mask : 0, State,
        BootpAllocated, MaxBootpAllowed
        );
    if( ERROR_SUCCESS != Error ) {
        MemFree(NewRange);
        return Error;
    }

    Error = MemArrayAddElement(
        &Subnet->Ranges,
        NewRange
    );

    if( ERROR_SUCCESS != Error ) {
        LocalError = MemRangeCleanup(NewRange);
        Require(LocalError == ERROR_SUCCESS);
        MemFree(NewRange);
    }

    return Error;
}

//BeginExport(function)
DWORD
MemSubnetAddRangeExpandOrContract(
    IN      PM_SUBNET              Subnet,
    IN      DWORD                  StartAddress,
    IN      DWORD                  EndAddress,
    OUT     DWORD                 *OldStartAddress,
    OUT     DWORD                 *OldEndAddress
) //EndExport(function)
{
    DWORD                          Error;
    DWORD                          LocalError;
    DWORD                          Cond;
    DWORD                          nAddresses;
    BOOL                           fExtend;
    PM_RANGE                       OldRange;
    PM_RANGE                       ThisRange;
    PARRAY                         Ranges;
    ARRAY_LOCATION                 Loc;

    Ranges = &Subnet->Ranges;

    *OldStartAddress = *OldEndAddress = 0;
    OldRange = NULL;
    Error = MemArrayInitLoc(Ranges, &Loc);
    while( ERROR_FILE_NOT_FOUND != Error ) {
        Require(ERROR_SUCCESS == Error);

        Error = MemArrayGetElement(Ranges, &Loc, (LPVOID *)&ThisRange);
        Require(ERROR_SUCCESS == Error && ThisRange);

        Cond = MemRangeCompare(StartAddress, EndAddress, ThisRange->Start, ThisRange->End);
        if( Cond != X_LESSTHAN_Y && Cond != Y_LESSTHAN_X ) {
            if( OldRange ) return ERROR_OBJECT_ALREADY_EXISTS;
            if( X_IN_Y != Cond && Y_IN_X != Cond )
                return ERROR_OBJECT_ALREADY_EXISTS;
            OldRange = ThisRange;
        }

        Error = MemArrayNextLoc(Ranges, &Loc);
    }

    if( NULL == OldRange ) return ERROR_FILE_NOT_FOUND;

    *OldStartAddress = OldRange->Start;
    *OldEndAddress = OldRange->End;

    if( OldRange->Start < StartAddress ) {
        fExtend = FALSE;
        nAddresses = StartAddress - OldRange->Start;
    } else {
        fExtend = TRUE;
        nAddresses = OldRange->Start - StartAddress;
    }

    Error = ERROR_SUCCESS;
    if( nAddresses ) Error = MemRangeExtendOrContract(
        OldRange,
        nAddresses,
        fExtend,
        FALSE
    );
    if( ERROR_SUCCESS != Error ) return Error;

    if( OldRange->End < EndAddress ) {
        fExtend = TRUE;
        nAddresses = EndAddress - OldRange->End;
    } else {
        fExtend = FALSE;
        nAddresses = OldRange->End - EndAddress;
    }

    if( nAddresses ) Error = MemRangeExtendOrContract(
        OldRange,
        nAddresses,
        fExtend,
        TRUE
    );
    return Error;
}

//BeginExport(function)
DWORD
MemSubnetAddExcl(
    IN OUT  PM_SUBNET              Subnet,
    IN      DWORD                  Start,
    IN      DWORD                  End,
    OUT     PM_EXCL               *OverlappingExcl
) //EndExport(function)
{
    DWORD                          Error;
    DWORD                          LocalError;
    PM_EXCL                        NewExcl;

    AssertRet(Subnet && OverlappingExcl, ERROR_INVALID_PARAMETER);

    if( Subnet->fSubnet ) {
        if( (Subnet->Address & Subnet->Mask) != (Start & Subnet->Mask) ||
            (Start & Subnet->Mask)  != (End & Subnet->Mask) )
            return ERROR_INVALID_PARAMETER;
    }

    if( Start > End ) return ERROR_INVALID_PARAMETER;

    *OverlappingExcl = NULL;
    Error = MemSubnetFindCollision(
        Subnet,
        Start,
        End,
        NULL,
        OverlappingExcl
    );
    if(ERROR_FILE_NOT_FOUND != Error ) {          // collision with a range?
        Require(ERROR_SUCCESS == Error);
        return ERROR_OBJECT_ALREADY_EXISTS;
    }

    NewExcl = MemAlloc(sizeof(*NewExcl));
    if( NULL == NewExcl ) return ERROR_NOT_ENOUGH_MEMORY;

    NewExcl->Start = Start;
    NewExcl->End = End;

    Error = MemArrayAddElement(
        &Subnet->Exclusions,
        NewExcl
    );

    if( ERROR_SUCCESS != Error ) {
        MemFree(NewExcl);
    }

    return Error;
}

//BeginExport(function)
DWORD
MemSubnetDelRange(
    IN OUT  PM_SUBNET              Subnet,
    IN      DWORD                  Start
) //EndExport(function)
{
    DWORD                          Error;
    PM_RANGE                       ThisRange;
    ARRAY_LOCATION                 Location;

    Error = MemArrayInitLoc(&Subnet->Ranges, &Location);
    while( ERROR_FILE_NOT_FOUND != Error ) {
        Require(ERROR_SUCCESS == Error);

        Error = MemArrayGetElement(&Subnet->Ranges, &Location, (LPVOID *)&ThisRange);
        Require(ERROR_SUCCESS == Error && ThisRange);

        if( ThisRange->Start == Start ) {         // Collision has occured
            Error = MemRangeCleanup(ThisRange);
            Require(ERROR_SUCCESS == Error);
            MemFree(ThisRange);

            Error = MemArrayDelElement(&Subnet->Ranges, &Location, (LPVOID *)&ThisRange);
            return ERROR_SUCCESS;
        }

        Error = MemArrayNextLoc(&Subnet->Ranges, &Location);
    }
    return ERROR_FILE_NOT_FOUND;
}

//BeginExport(function)
DWORD
MemSubnetDelExcl(
    IN OUT  PM_SUBNET              Subnet,
    IN      DWORD                  Start
) //EndExport(function)
{
    DWORD                          Error;
    PM_EXCL                        ThisExcl;
    ARRAY_LOCATION                 Location;

    Error = MemArrayInitLoc(&Subnet->Exclusions, &Location);
    while( ERROR_FILE_NOT_FOUND != Error ) {
        Require(ERROR_SUCCESS == Error);

        Error = MemArrayGetElement(&Subnet->Exclusions, &Location, (LPVOID *)&ThisExcl);
        Require(ERROR_SUCCESS == Error && ThisExcl);

        if( ThisExcl->Start == Start ) {
            Error = MemArrayDelElement(&Subnet->Exclusions, &Location, (LPVOID *)&ThisExcl);
            MemFree(ThisExcl);
            return ERROR_SUCCESS;
        }

        Error = MemArrayNextLoc(&Subnet->Exclusions, &Location);
    }
    return ERROR_FILE_NOT_FOUND;
}

//BeginExport(function)
DWORD
MemSubnetExtendOrContractRange(
    IN OUT  PM_SUBNET              Subnet,
    IN OUT  PM_RANGE               Range,
    IN      DWORD                  nAddresses,    // how many addresses to extend by
    IN      BOOL                   fExtend,       // is this an EXTEND? or a CONTRACT?
    IN      BOOL                   fEnd           // is this operation to be done to END of range or START?
) //EndExport(function)
{
    DWORD                          Error;
    PM_RANGE                       CollidedRange;

    AssertRet(Subnet && Range, ERROR_INVALID_PARAMETER);

    if( Subnet->fSubnet ) {                       // for real subnets (non-multicast-scopes) do sanity check
        if( fExtend ) {
            if( fEnd ) {
                if( ((Range->End + nAddresses) & Subnet->Mask) != (Range->Start & Subnet->Mask) )
                    return ERROR_INVALID_PARAMETER;

                Error = MemSubnetFindCollision(
                    Subnet,
                    Range->End +1,
                    Range->End +nAddresses,
                    &CollidedRange,
                    NULL
                );
                if( ERROR_SUCCESS == Error && NULL != CollidedRange)
                    return ERROR_OBJECT_ALREADY_EXISTS;
            }  else {
                if( ((Range->Start - nAddresses) & Subnet->Mask) != (Range->End & Subnet->Mask) )
                    return ERROR_INVALID_PARAMETER;

                Error = MemSubnetFindCollision(
                    Subnet,
                    Range->Start - nAddresses,
                    Range->Start - 1,
                    &CollidedRange,
                    NULL
                );
                if( ERROR_SUCCESS == Error && NULL != CollidedRange)
                    return ERROR_OBJECT_ALREADY_EXISTS;
            }
        }
    }

    if( !fExtend && nAddresses >  Range->End - Range->Start )
        return ERROR_INVALID_PARAMETER;

    Error = MemRangeExtendOrContract(
        Range,
        nAddresses,
        fExtend,
        fEnd
    );

    return Error;
}


//BeginExport(function)
DWORD
MemSubnetExtendOrContractExcl(
    IN OUT  PM_SUBNET              Subnet,
    IN OUT  PM_EXCL                Excl,
    IN      DWORD                  nAddresses,    // how many addresses to extend by
    IN      BOOL                   fExtend,       // is this an EXTEND? or a CONTRACT?
    IN      BOOL                   fEnd           // is this operation to be done to END of range or START?
) //EndExport(function)
{
    DWORD                          Error;
    PM_EXCL                        CollidedExcl;

    AssertRet(Subnet && Excl, ERROR_INVALID_PARAMETER);

    if( Subnet->fSubnet ) {                       // for real subnets (non-multicast-scopes) do sanity check
        if( fExtend ) {
            if( fEnd ) {
                if( ((Excl->End + nAddresses) & Subnet->Mask) != (Excl->Start & Subnet->Mask) )
                    return ERROR_INVALID_PARAMETER;

                Error = MemSubnetFindCollision(
                    Subnet,
                    Excl->End +1,
                    Excl->End +nAddresses,
                    NULL,
                    &CollidedExcl
                );
                if( ERROR_SUCCESS == Error && NULL != CollidedExcl)
                    return ERROR_OBJECT_ALREADY_EXISTS;
            }  else {
                if( ((Excl->Start - nAddresses) & Subnet->Mask) != (Excl->End & Subnet->Mask) )
                    return ERROR_INVALID_PARAMETER;

                Error = MemSubnetFindCollision(
                    Subnet,
                    Excl->Start - nAddresses,
                    Excl->Start - 1,
                    NULL,
                    &CollidedExcl
                );
                if( ERROR_SUCCESS == Error && NULL != CollidedExcl)
                    return ERROR_OBJECT_ALREADY_EXISTS;
            }
        }
    }

    if( !fExtend && nAddresses >  Excl->End - Excl->Start )
        return ERROR_INVALID_PARAMETER;

    if( fExtend )
        if( fEnd )
            Excl->End += nAddresses;
        else
            Excl->Start -= nAddresses;
    else
        if( fEnd )
            Excl->End -= nAddresses;
        else
            Excl->Start += nAddresses;

    return NO_ERROR;
}


//================================================================================
//  Multicast Scopes implementation
//================================================================================

//BeginExport(typedef)
typedef     M_SUBNET               M_MSCOPE;      // same structure for Multicast Scopes and Subnets
typedef     PM_SUBNET              PM_MSCOPE;     // still, use the correct functions for MScope
typedef     LPM_SUBNET             LPM_MSCOPE;
//EndExport(typedef)

//BeginExport(inline)
DWORD       _inline
MemMScopeCleanup(
    IN      PM_MSCOPE              MScope
) {
    return MemSubnetCleanup(MScope);
}
//EndExport(inline)

//BeginExport(decl)
#define     MemMScopeGetAddressInfo               MemSubnetGetAddressInfo
#define     MemMScopeFindCollision                MemSubnetFindCollision
#define     MemMScopeAddExcl                      MemSubnetAddExcl
#define     MemMScopeDelRange                     MemSubnetDelRange
#define     MemMScopeDelExcl                      MemSubnetDelExcl
#define     MemMScopeExtendOrContractRange        MemSubnetExtendOrContractRange
#define     MemMScopeExtendOrContractExcl         MemSubnetExtendOrContractExcl

DWORD
MemMScopeGetAddressInfo(
    IN      PM_MSCOPE              MScope,
    IN      DWORD                  MCastAddress,
    OUT     PM_RANGE              *Range,         // OPTIONAL -- filled if a range could be found -- even if excluded
    OUT     PM_EXCL               *Excl,          // OPTIONAL -- filled if an exclusion could be found
    OUT     PM_RESERVATION        *Reservation    // OPTIONAL -- filled with  a matching reservation, if found
);

DWORD                                             // ERROR_SUCCESS on finding a collition, else ERROR_FILE_NOT_FOUND
MemMScopeFindCollision(
    IN OUT  PM_MSCOPE              Subnet,
    IN      DWORD                  MCastStart,
    IN      DWORD                  MCastEnd,
    OUT     PM_RANGE              *Range,         // OPTIONAL
    OUT     PM_EXCL               *Excl           // OPTIONAL
);

//EndExport(decl)

//BeginExport(inline)
DWORD       _inline                               // ERROR_OBJECT_ALREADY_EXISTS on collision
MemMScopeAddRange(                                // check if the range is valid, and only then add it
    IN OUT  PM_MSCOPE              Subnet,
    IN      DWORD                  MCastStart,
    IN      DWORD                  MCastEnd,
    IN      DWORD                  State,
    OUT     PM_RANGE              *OverlappingRange
)
{
    return MemSubnetAddRange(Subnet,MCastStart, MCastEnd, State, 0, 0, OverlappingRange);
}


DWORD
MemMScopeAddExcl(
    IN OUT  PM_MSCOPE              Subnet,
    IN      DWORD                  MCastStart,
    IN      DWORD                  MCastEnd,
    OUT     PM_EXCL               *OverlappingExcl
);

DWORD
MemMScopeDelRange(
    IN OUT  PM_MSCOPE              Subnet,
    IN      DWORD                  MCastStart
);

DWORD
MemMScopeDelExcl(
    IN OUT  PM_MSCOPE              Subnet,
    IN      DWORD                  MCastStart
);

DWORD
MemMScopeExtendOrContractRange(
    IN OUT  PM_MSCOPE              Subnet,
    IN OUT  PM_RANGE               Range,
    IN      DWORD                  nAddresses,    // how many addresses to extend by
    IN      BOOL                   fExtend,       // is this an EXTEND? or a CONTRACT?
    IN      BOOL                   fEnd           // is this operation to be done to END of range or START?
);

DWORD
MemMScopeExtendOrContractExcl(
    IN OUT  PM_MSCOPE              Subnet,
    IN OUT  PM_EXCL                Excl,
    IN      DWORD                  nAddresses,    // how many addresses to extend by
    IN      BOOL                   fExtend,       // is this an EXTEND? or a CONTRACT?
    IN      BOOL                   fEnd           // is this operation to be done to END of range or START?
);
//EndExport(decl)

//================================================================================
// end of file
//================================================================================