/*
 * COPYRIGHT:   See COPYING in the top level directory
 * PROJECT:     ReactOS TCP/IP protocol driver
 * FILE:        tcpip/ports.c
 * PURPOSE:     Port allocation
 * PROGRAMMERS: arty (ayerkes@speakeasy.net)
 * REVISIONS:
 *   arty 20041114 Created
 */

#include "precomp.h"

VOID PortsStartup( PPORT_SET PortSet,
		   UINT StartingPort,
		   UINT PortsToManage ) {
    PortSet->StartingPort = StartingPort;
    PortSet->PortsToOversee = PortsToManage;
    PortSet->LastAllocatedPort = PortSet->StartingPort +
                                 PortSet->PortsToOversee - 1;
    PortSet->ProtoBitBuffer =
	PoolAllocateBuffer( (PortSet->PortsToOversee + 7) / 8 );
    RtlInitializeBitMap( &PortSet->ProtoBitmap,
			 PortSet->ProtoBitBuffer,
			 PortSet->PortsToOversee );
    RtlClearAllBits( &PortSet->ProtoBitmap );
    ExInitializeFastMutex( &PortSet->Mutex );
}

VOID PortsShutdown( PPORT_SET PortSet ) {
    PoolFreeBuffer( PortSet->ProtoBitBuffer );
}

VOID DeallocatePort( PPORT_SET PortSet, ULONG Port ) {
    Port = htons(Port);
    ASSERT(Port >= PortSet->StartingPort);
    ASSERT(Port < PortSet->StartingPort + PortSet->PortsToOversee);
    RtlClearBits( &PortSet->ProtoBitmap, Port - PortSet->StartingPort, 1 );
}

BOOLEAN AllocatePort( PPORT_SET PortSet, ULONG Port ) {
    BOOLEAN Clear;

    Port = htons(Port);
    ASSERT(Port >= PortSet->StartingPort);
    Port -= PortSet->StartingPort;

    ExAcquireFastMutex( &PortSet->Mutex );
    Clear = RtlAreBitsClear( &PortSet->ProtoBitmap, Port, 1 );
    if( Clear ) RtlSetBits( &PortSet->ProtoBitmap, Port, 1 );
    ExReleaseFastMutex( &PortSet->Mutex );

    return Clear;
}

ULONG AllocateAnyPort( PPORT_SET PortSet ) {
    ULONG AllocatedPort;
    ULONG Next;

    if (PortSet->StartingPort + PortSet->PortsToOversee <=
        PortSet->LastAllocatedPort + 1) {
	Next = PortSet->StartingPort;
    } else {
	Next = PortSet->LastAllocatedPort + 1;
    }
    Next -= PortSet->StartingPort;

    ExAcquireFastMutex( &PortSet->Mutex );
    AllocatedPort = RtlFindClearBits( &PortSet->ProtoBitmap, 1, 0 );
    if( AllocatedPort != (ULONG)-1 ) {
	RtlSetBit( &PortSet->ProtoBitmap, AllocatedPort );
	AllocatedPort += PortSet->StartingPort;
	PortSet->LastAllocatedPort = AllocatedPort;
    }
    ExReleaseFastMutex( &PortSet->Mutex );

    AllocatedPort = htons(AllocatedPort);

    return AllocatedPort;
}

ULONG AllocatePortFromRange( PPORT_SET PortSet, ULONG Lowest, ULONG Highest ) {
    ULONG AllocatedPort;
    ULONG Next;

    if (PortSet->StartingPort + PortSet->PortsToOversee <=
        PortSet->LastAllocatedPort + 1) {
	Next = PortSet->StartingPort;
    } else {
	Next = PortSet->LastAllocatedPort + 1;
    }
    if (Next < Lowest || Highest <= Next) {
	Next = Lowest;
    }
    Next -= PortSet->StartingPort;
    Lowest -= PortSet->StartingPort;
    Highest -= PortSet->StartingPort;

    ExAcquireFastMutex( &PortSet->Mutex );
    AllocatedPort = RtlFindClearBits( &PortSet->ProtoBitmap, 1, Next );
    if( AllocatedPort != (ULONG)-1 && AllocatedPort >= Lowest &&
        AllocatedPort <= Highest) {
	RtlSetBit( &PortSet->ProtoBitmap, AllocatedPort );
	AllocatedPort += PortSet->StartingPort;
	PortSet->LastAllocatedPort = AllocatedPort;
    }
    ExReleaseFastMutex( &PortSet->Mutex );

    AllocatedPort = htons(AllocatedPort);

    return AllocatedPort;
}
