Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

561 lines
15 KiB

/*++
Copyright (c) 1999, Microsoft Corporation
Module Name:
portapi.c
Abstract:
This module contains code for API routines which provide port-reservation
functionality to user-mode clients of TCP/IP. This functionality allows
applications to 'reserve' blocks of TCP/UDP port-numbers for private use,
preventing any other processes from binding to the reserved port-numbers.
Author:
Abolade Gbadegesin (aboladeg) 25-May-1999
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <ipnatapi.h>
#include <ntddtcp.h>
//
// PRIVATE STRUCTURE DECLARATIONS
//
typedef struct _NAT_PORT_RESERVATION {
CRITICAL_SECTION Lock;
HANDLE TcpipHandle;
USHORT BlockSize;
USHORT PortBlockSize;
LIST_ENTRY PortBlockList;
} NAT_PORT_RESERVATION, *PNAT_PORT_RESERVATION;
typedef struct _NAT_PORT_BLOCK {
LIST_ENTRY Link;
ULONG StartHandle;
RTL_BITMAP Bitmap;
ULONG BitmapBuffer[0];
} NAT_PORT_BLOCK, *PNAT_PORT_BLOCK;
//
// FORWARD DECLARATIONS
//
ULONG
NatpCreatePortBlock(
PNAT_PORT_RESERVATION PortReservation,
PNAT_PORT_BLOCK* PortBlockCreated
);
VOID
NatpDeletePortBlock(
PNAT_PORT_RESERVATION PortReservation,
PNAT_PORT_BLOCK PortBlock
);
ULONG
NatAcquirePortReservation(
HANDLE ReservationHandle,
USHORT PortCount,
OUT PUSHORT ReservedPortBase
)
/*++
Routine Description:
This routine is called to reserve one or more contiguous port-numbers
from the port-reservation handle supplied.
Arguments:
ReservationHandle - supplies a port-reservation handle from which to
acquire port-numbers
PortCount - specifies the number of port-numbers required
ReservedPortBase - receives the first port-number reserved,
in network-order.
Return Value:
ULONG - Win32 status code.
--*/
{
ULONG Error;
ULONG Index;
PLIST_ENTRY Link;
PNAT_PORT_BLOCK PortBlock;
PNAT_PORT_RESERVATION PortReservation =
(PNAT_PORT_RESERVATION)ReservationHandle;
NTSTATUS Status;
//
// Fail immediately if the caller has requested more port-numbers
// than would exist in a completely unallocated block.
// Otherwise, traverse the list of port-blocks to see if any of the blocks
// have enough contiguous port-numbers to satisfy the caller's request.
//
if (PortCount > PortReservation->BlockSize) {
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&PortReservation->Lock);
for (Link = PortReservation->PortBlockList.Flink;
Link != &PortReservation->PortBlockList; Link = Link->Flink) {
PortBlock = CONTAINING_RECORD(Link, NAT_PORT_BLOCK, Link);
Index = RtlFindClearBitsAndSet(&PortBlock->Bitmap, PortCount, 0);
if (Index != (ULONG)-1) {
*ReservedPortBase =
RtlUshortByteSwap((USHORT)(PortBlock->StartHandle + Index));
LeaveCriticalSection(&PortReservation->Lock);
return NO_ERROR;
}
}
//
// No port-block had the required number of contiguous port-numbers.
// Attempt to create a new port-block, and if that succeeds use it
// to satisfy the caller's request.
//
Error = NatpCreatePortBlock(PortReservation, &PortBlock);
if (NO_ERROR != Error) {
LeaveCriticalSection(&PortReservation->Lock);
return Error;
}
Index = RtlFindClearBitsAndSet(&PortBlock->Bitmap, PortCount, 0);
*ReservedPortBase =
RtlUshortByteSwap((USHORT)(PortBlock->StartHandle + Index));
LeaveCriticalSection(&PortReservation->Lock);
return NO_ERROR;
} // NatAcquirePortReservation
ULONG
NatInitializePortReservation(
USHORT BlockSize,
OUT PHANDLE ReservationHandle
)
/*++
Routine Description:
This routine is called to initialize a handle to the port-reservation
module. The resulting handle is used to acquire and release ports
from the dynamically-allocated block.
Arguments:
BlockSize - indicates the number of ports to request each time
an additional block is requested from the TCP/IP driver.
ReservationHandle - on output, receives a handle to be used for
acquiring and releasing ports.
Return Value:
ULONG - Win32 status code.
--*/
{
ULONG BitmapSize;
IO_STATUS_BLOCK IoStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
PNAT_PORT_RESERVATION PortReservation;
NTSTATUS Status;
HANDLE TcpipHandle;
UNICODE_STRING UnicodeString;
do {
//
// Open a handle to the TCP/IP driver.
// This handle will later be used to issue reservation-requests.
//
RtlInitUnicodeString(&UnicodeString, DD_TCP_DEVICE_NAME);
InitializeObjectAttributes(
&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL
);
Status =
NtCreateFile(
&TcpipHandle,
SYNCHRONIZE|FILE_READ_DATA|FILE_WRITE_DATA,
&ObjectAttributes,
&IoStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0
);
if (!NT_SUCCESS(Status)) { break; }
//
// Allocate and initialize a port-reservation context block.
//
PortReservation = MALLOC(sizeof(*PortReservation));
if (!PortReservation) { Status = STATUS_NO_MEMORY; break; }
if (FALSE == InitializeCriticalSectionAndSpinCount(&PortReservation->Lock, 0)) {
Status = STATUS_NO_MEMORY;
break;
}
PortReservation->TcpipHandle = TcpipHandle;
PortReservation->BlockSize = BlockSize;
BitmapSize = (BlockSize + sizeof(ULONG) * 8 - 1) / (sizeof(ULONG) * 8);
PortReservation->PortBlockSize =
(USHORT)FIELD_OFFSET(NAT_PORT_BLOCK, BitmapBuffer[BitmapSize]);
InitializeListHead(&PortReservation->PortBlockList);
*ReservationHandle = (HANDLE)PortReservation;
return NO_ERROR;
} while(FALSE);
if (TcpipHandle) { NtClose(TcpipHandle); }
if (PortReservation) { FREE(PortReservation); }
return RtlNtStatusToDosError(Status);
} // NatInitializePortReservation
ULONG
NatpCreatePortBlock(
PNAT_PORT_RESERVATION PortReservation,
PNAT_PORT_BLOCK* PortBlockCreated
)
/*++
Routine Description:
This routine is called to create a new port-block when the existing
port-numbers have been exhausted.
Arguments:
PortReservation - the reservation to which the port-block should be added
PortBlockCreated - on output, receives the new port-block
Return Value:
ULONG - Win32 error code; return NO_ERROR if successful.
Environment:
PortReservation->Lock must be held by the caller.
--*/
{
IO_STATUS_BLOCK IoStatus;
PLIST_ENTRY Link;
PNAT_PORT_BLOCK PortBlock;
TCP_BLOCKPORTS_REQUEST Request;
ULONG StartHandle;
NTSTATUS Status;
HANDLE WaitEvent;
//
// Allocate memory for the new port-block and its bitmap of free ports
//
PortBlock = MALLOC(PortReservation->PortBlockSize);
if (!PortBlock) { return ERROR_NOT_ENOUGH_MEMORY; }
//
// Request a new block of ports from the TCP/IP driver
//
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent == NULL) {
FREE(PortBlock);
return ERROR_NOT_ENOUGH_MEMORY;
}
Request.ReservePorts = TRUE;
Request.NumberofPorts = PortReservation->BlockSize;
Status =
NtDeviceIoControlFile(
PortReservation->TcpipHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_TCP_BLOCK_PORTS,
&Request,
sizeof(Request),
&StartHandle,
sizeof(StartHandle)
);
if (Status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
Status = IoStatus.Status;
}
CloseHandle(WaitEvent);
if (!NT_SUCCESS(Status)) {
FREE(PortBlock); return RtlNtStatusToDosError(Status);
}
//
// Initialize the new port-block, and insert it in the list of ports.
//
PortBlock->StartHandle = StartHandle;
RtlInitializeBitMap(
&PortBlock->Bitmap,
PortBlock->BitmapBuffer,
PortReservation->BlockSize
);
RtlClearAllBits(&PortBlock->Bitmap);
for (Link = PortReservation->PortBlockList.Flink;
Link != &PortReservation->PortBlockList; Link = Link->Flink) {
PNAT_PORT_BLOCK Temp = CONTAINING_RECORD(Link, NAT_PORT_BLOCK, Link);
if (PortBlock->StartHandle > Temp->StartHandle) {
continue;
} else {
break;
}
ASSERTMSG("NatpCreatePortBlock: duplicate port range\n", TRUE);
}
InsertTailList(Link, &PortBlock->Link);
if (PortBlockCreated) { *PortBlockCreated = PortBlock; }
return NO_ERROR;
} // NatpCreatePortBlock
VOID
NatpDeletePortBlock(
PNAT_PORT_RESERVATION PortReservation,
PNAT_PORT_BLOCK PortBlock
)
/*++
Routine Description:
This routine is called to delete a port-block when the port-numbers
it contains have been released, or when the port-reservation is cleaned up.
Arguments:
PortReservation - the reservation to which the port-block belongs
PortBlock - the port block to be deleted
Return Value:
none.
Environment:
PortReservation->Lock must be held by the caller.
--*/
{
IO_STATUS_BLOCK IoStatus;
TCP_BLOCKPORTS_REQUEST Request;
NTSTATUS Status;
HANDLE WaitEvent;
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent == NULL) {
return;
}
//
// Release the block of ports to the TCP/IP driver
//
Request.ReservePorts = FALSE;
Request.StartHandle = PortBlock->StartHandle;
Status =
NtDeviceIoControlFile(
PortReservation->TcpipHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_TCP_BLOCK_PORTS,
&Request,
sizeof(Request),
NULL,
0
);
if (Status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
Status = IoStatus.Status;
}
RemoveEntryList(&PortBlock->Link);
FREE(PortBlock);
CloseHandle(WaitEvent);
} // NatpDeletePortBlock
ULONG
NatReleasePortReservation(
HANDLE ReservationHandle,
USHORT ReservedPortBase,
USHORT PortCount
)
/*++
Routine Description:
This routine is called to release all contiguous port-numbers obtained
in a previous acquisition from the port-reservation handle supplied.
Arguments:
ReservationHandle - supplies a port-reservation handle to which to
release port-numbers
ReservedPortBase - receives the first port-number reserved,
in network-order.
PortCount - specifies the number of port-numbers acquired
Return Value:
ULONG - Win32 status code.
--*/
{
PLIST_ENTRY Link;
USHORT PortBase;
PNAT_PORT_BLOCK PortBlock;
PNAT_PORT_RESERVATION PortReservation =
(PNAT_PORT_RESERVATION)ReservationHandle;
EnterCriticalSection(&PortReservation->Lock);
//
// Convert the caller's port-base into host-order,
// and search the sorted list of port-blocks for the entry
// from which the acquisition was made.
//
PortBase = RtlUshortByteSwap(ReservedPortBase);
for (Link = PortReservation->PortBlockList.Flink;
Link != &PortReservation->PortBlockList; Link = Link->Flink) {
PortBlock = CONTAINING_RECORD(Link, NAT_PORT_BLOCK, Link);
if (PortBase < PortBlock->StartHandle) {
break;
} else if (PortBase <
(PortBlock->StartHandle + PortReservation->BlockSize)) {
//
// This should be the block from which the caller's port-numbers
// were acquired. For good measure, check that the end of the
// callers range also falls within this block.
//
if ((PortBase + PortCount - 1) >=
(USHORT)(PortBlock->StartHandle + PortReservation->BlockSize)) {
//
// The caller has probably supplied an incorrect length,
// or is releasing an allocation twice, or something.
//
LeaveCriticalSection(&PortReservation->Lock);
return ERROR_INVALID_PARAMETER;
} else {
//
// This is the caller's range. Clear the bits corresponding
// to the caller's acquisition, and then see if there are
// any bits left in the bitmap. If not, and if there are
// other port-blocks, delete this port-block altogether.
//
RtlClearBits(
&PortBlock->Bitmap,
PortBase - PortBlock->StartHandle,
PortCount
);
if (RtlFindSetBits(&PortBlock->Bitmap, 1, 0) == (ULONG)-1 &&
(PortBlock->Link.Flink != &PortReservation->PortBlockList ||
PortBlock->Link.Blink != &PortReservation->PortBlockList)
) {
NatpDeletePortBlock(PortReservation, PortBlock);
}
LeaveCriticalSection(&PortReservation->Lock);
return NO_ERROR;
}
} else {
continue;
}
}
LeaveCriticalSection(&PortReservation->Lock);
//
// We could not find the port-block from which the caller
// allegedly acquired this range of port-numbers.
//
return ERROR_CAN_NOT_COMPLETE;
} // NatReleasePortReservation
VOID
NatShutdownPortReservation(
HANDLE ReservationHandle
)
/*++
Routine Description:
This routine is called to clean up a handle to the port-reservation module.
It releases all reservations acquired, and closes the handle to the TCP/IP
driver.
Arguments:
ReservationHandle - the handle to be cleaned up
Return Value:
none.
--*/
{
PNAT_PORT_BLOCK PortBlock;
PNAT_PORT_RESERVATION PortReservation =
(PNAT_PORT_RESERVATION)ReservationHandle;
while (!IsListEmpty(&PortReservation->PortBlockList)) {
PortBlock =
CONTAINING_RECORD(
PortReservation->PortBlockList.Flink, NAT_PORT_BLOCK, Link
);
NatpDeletePortBlock(PortReservation, PortBlock);
}
NtClose(PortReservation->TcpipHandle);
DeleteCriticalSection(&PortReservation->Lock);
FREE(PortReservation);
}