1260 lines
28 KiB
C
1260 lines
28 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
editor.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code for editor management.
|
||
|
||
Author:
|
||
|
||
Abolade Gbadegesin (t-abolag) 14-July-1997
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
//
|
||
// GLOBAL DATA DEFINITIONS
|
||
//
|
||
|
||
//
|
||
// Count of NAT editors
|
||
//
|
||
|
||
ULONG EditorCount;
|
||
|
||
//
|
||
// List of NAT editors
|
||
//
|
||
|
||
LIST_ENTRY EditorList;
|
||
|
||
//
|
||
// Spin-lock controlling access to 'EditorList'.
|
||
//
|
||
|
||
KSPIN_LOCK EditorLock;
|
||
|
||
//
|
||
// Spin-lock controlling access to the 'MappingList' field of all editors.
|
||
//
|
||
|
||
KSPIN_LOCK EditorMappingLock;
|
||
|
||
|
||
VOID
|
||
NatCleanupEditor(
|
||
PNAT_EDITOR Editor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called to perform final cleanup for an editor.
|
||
|
||
Arguments:
|
||
|
||
Editor - the editor to be cleaned up.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'EditorLock' NOT held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
PLIST_ENTRY Link;
|
||
PNAT_DYNAMIC_MAPPING Mapping;
|
||
|
||
KeAcquireSpinLock(&EditorLock, &Irql);
|
||
KeAcquireSpinLockAtDpcLevel(&EditorMappingLock);
|
||
|
||
for (Link = Editor->MappingList.Flink; Link != &Editor->MappingList;
|
||
Link = Link->Flink) {
|
||
Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, EditorLink);
|
||
Link = Link->Blink;
|
||
NatMappingDetachEditor(Editor, Mapping);
|
||
}
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&EditorMappingLock);
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
|
||
ExFreePool(Editor);
|
||
|
||
} // NatCleanupEditor
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NatCreateEditor(
|
||
PIP_NAT_REGISTER_EDITOR RegisterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked when an editor attempts to register.
|
||
It handles creation of a context-block for the editor.
|
||
|
||
Arguments:
|
||
|
||
RegisterContext - information about the registering editor
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_EDITOR Editor;
|
||
PLIST_ENTRY InsertionPoint;
|
||
KIRQL Irql;
|
||
|
||
CALLTRACE(("NatCreateEditor\n"));
|
||
|
||
//
|
||
// Validate the registration information
|
||
//
|
||
|
||
if ((RegisterContext->Protocol != NAT_PROTOCOL_TCP &&
|
||
RegisterContext->Protocol != NAT_PROTOCOL_UDP) ||
|
||
(!RegisterContext->Port) ||
|
||
(RegisterContext->Direction != NatInboundDirection &&
|
||
RegisterContext->Direction != NatOutboundDirection) ||
|
||
(!RegisterContext->ForwardDataHandler &&
|
||
!RegisterContext->ReverseDataHandler)) {
|
||
ERROR(("NatCreateEditor: bad argument\n"));
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Allocate a new editor-struct
|
||
//
|
||
|
||
Editor =
|
||
ExAllocatePoolWithTag(NonPagedPool, sizeof(NAT_EDITOR), NAT_TAG_EDITOR);
|
||
|
||
if (!Editor) {
|
||
ERROR(("NatCreateEditor: allocation failed\n"));
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
KeInitializeSpinLock(&Editor->Lock);
|
||
Editor->ReferenceCount = 1;
|
||
Editor->Flags = RegisterContext->Flags;
|
||
Editor->Key =
|
||
MAKE_EDITOR_KEY(
|
||
RegisterContext->Protocol,
|
||
RegisterContext->Port,
|
||
RegisterContext->Direction
|
||
);
|
||
InitializeListHead(&Editor->MappingList);
|
||
|
||
Editor->Context = RegisterContext->EditorContext;
|
||
Editor->CreateHandler = RegisterContext->CreateHandler;
|
||
Editor->DeleteHandler = RegisterContext->DeleteHandler;
|
||
Editor->ForwardDataHandler = RegisterContext->ForwardDataHandler;
|
||
Editor->ReverseDataHandler = RegisterContext->ReverseDataHandler;
|
||
|
||
KeAcquireSpinLock(&EditorLock, &Irql);
|
||
if (NatLookupEditor(Editor->Key, &InsertionPoint)) {
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
ERROR(
|
||
("NatCreateEditor: duplicate editor %d/%d\n",
|
||
RegisterContext->Protocol, RegisterContext->Port)
|
||
);
|
||
ExFreePool(Editor);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
InsertTailList(InsertionPoint, &Editor->Link);
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
|
||
InterlockedIncrement(&EditorCount);
|
||
|
||
//
|
||
// Supply the caller with 'out' information
|
||
//
|
||
|
||
RegisterContext->EditorHandle = (PVOID)Editor;
|
||
RegisterContext->CreateTicket = NatEditorCreateTicket;
|
||
RegisterContext->DeleteTicket = NatEditorDeleteTicket;
|
||
RegisterContext->Deregister = NatEditorDeregister;
|
||
RegisterContext->DissociateSession = NatEditorDissociateSession;
|
||
RegisterContext->EditSession = NatEditorEditSession;
|
||
RegisterContext->QueryInfoSession = NatEditorQueryInfoSession;
|
||
RegisterContext->TimeoutSession = NatEditorTimeoutSession;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatCreateEditor
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NatDeleteEditor(
|
||
PNAT_EDITOR Editor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles editor deletion.
|
||
|
||
This routine assumes that EditorLock is NOT held by the caller.
|
||
|
||
Arguments:
|
||
|
||
Editor - specifies the editor to be deleted.
|
||
|
||
Return Value
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatDeleteEditor\n"));
|
||
|
||
InterlockedDecrement(&EditorCount);
|
||
|
||
//
|
||
// Remove the editor for the list
|
||
//
|
||
|
||
KeAcquireSpinLock(&EditorLock, &Irql);
|
||
Editor->Flags |= NAT_EDITOR_FLAG_DELETED;
|
||
RemoveEntryList(&Editor->Link);
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
|
||
//
|
||
// Drop its reference count and clean up if necessary
|
||
//
|
||
|
||
if (InterlockedDecrement(&Editor->ReferenceCount) > 0) {
|
||
return STATUS_PENDING;
|
||
}
|
||
NatCleanupEditor(Editor);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatDeleteEditor
|
||
|
||
|
||
VOID
|
||
NatInitializeEditorManagement(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine prepares the editor-management module for operation.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
CALLTRACE(("NatInitializeEditorManagement\n"));
|
||
EditorCount = 0;
|
||
KeInitializeSpinLock(&EditorLock);
|
||
InitializeListHead(&EditorList);
|
||
KeInitializeSpinLock(&EditorMappingLock);
|
||
} // NatInitializeEditorManagement
|
||
|
||
|
||
PNAT_EDITOR
|
||
NatLookupEditor(
|
||
ULONG Key,
|
||
PLIST_ENTRY* InsertionPoint
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to retrieve the editor corresponding to the given
|
||
key.
|
||
|
||
Arguments:
|
||
|
||
Key - the key for which an editor is to be found
|
||
|
||
InsertionPoint - receives the point at which the editor should be inserted
|
||
if not found
|
||
|
||
Return Value:
|
||
|
||
PNAT_EDITOR - the required editor, if found
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_EDITOR Editor;
|
||
PLIST_ENTRY Link;
|
||
for (Link = EditorList.Flink; Link != &EditorList; Link = Link->Flink) {
|
||
Editor = CONTAINING_RECORD(Link, NAT_EDITOR, Link);
|
||
if (Key > Editor->Key) {
|
||
continue;
|
||
} else if (Key < Editor->Key) {
|
||
break;
|
||
}
|
||
return Editor;
|
||
}
|
||
if (InsertionPoint) { *InsertionPoint = Link; }
|
||
return NULL;
|
||
} // NatLookupEditor
|
||
|
||
|
||
VOID
|
||
NatMappingAttachEditor(
|
||
PNAT_EDITOR Editor,
|
||
PNAT_DYNAMIC_MAPPING Mapping
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to attach a mapping to an editor.
|
||
It serves as a notification that there is one more mapping
|
||
associated with the editor.
|
||
|
||
Arguments:
|
||
|
||
Editor - the editor for the mapping
|
||
|
||
Mapping - the mapping to be attached.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Always invoked at dispatch level with 'EditorLock' and 'EditorMappingLock'
|
||
held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PrivateAddress;
|
||
USHORT PrivatePort;
|
||
ULONG PublicAddress;
|
||
USHORT PublicPort;
|
||
ULONG RemoteAddress;
|
||
USHORT RemotePort;
|
||
|
||
CALLTRACE(("NatMappingAttachEditor\n"));
|
||
if (Editor->CreateHandler) {
|
||
NatQueryInformationMapping(
|
||
Mapping,
|
||
NULL,
|
||
&PrivateAddress,
|
||
&PrivatePort,
|
||
&RemoteAddress,
|
||
&RemotePort,
|
||
&PublicAddress,
|
||
&PublicPort,
|
||
NULL
|
||
);
|
||
Editor->CreateHandler(
|
||
Editor->Context,
|
||
PrivateAddress,
|
||
PrivatePort,
|
||
RemoteAddress,
|
||
RemotePort,
|
||
PublicAddress,
|
||
PublicPort,
|
||
&Mapping->EditorContext
|
||
);
|
||
}
|
||
Mapping->Editor = Editor;
|
||
InsertTailList(&Editor->MappingList, &Mapping->EditorLink);
|
||
} // NatMappingAttachEditor
|
||
|
||
|
||
VOID
|
||
NatMappingDetachEditor(
|
||
PNAT_EDITOR Editor,
|
||
PNAT_DYNAMIC_MAPPING Mapping
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to detach a mapping from a editor.
|
||
It serves as a notification that there is one less mapping
|
||
associated with the editor.
|
||
|
||
Arguments:
|
||
|
||
Editor - editor to be detached
|
||
|
||
Mapping - the mapping to be detached
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Always invoked at dispatch level with 'EditorLock' and 'EditorMappingLock'
|
||
held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
if (Editor->DeleteHandler && Mapping->Interfacep) {
|
||
Editor->DeleteHandler(
|
||
Mapping->Interfacep,
|
||
Mapping,
|
||
Editor->Context,
|
||
Mapping->EditorContext
|
||
);
|
||
}
|
||
RemoveEntryList(&Mapping->EditorLink);
|
||
Mapping->Editor = NULL;
|
||
Mapping->EditorContext = NULL;
|
||
} // NatMappingDetachEditor
|
||
|
||
|
||
NTSTATUS
|
||
NatQueryEditorTable(
|
||
IN PIP_NAT_ENUMERATE_EDITORS InputBuffer,
|
||
IN PIP_NAT_ENUMERATE_EDITORS OutputBuffer,
|
||
IN PULONG OutputBufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used for enumerating the registered editors.
|
||
|
||
Arguments:
|
||
|
||
InputBuffer - supplies context information for the information
|
||
|
||
OutputBuffer - receives the result of the enumeration
|
||
|
||
OutputBufferLength - size of the i/o buffer
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Count;
|
||
ULONG i;
|
||
KIRQL Irql;
|
||
ULONG Key;
|
||
PLIST_ENTRY Link;
|
||
PNAT_EDITOR Editor;
|
||
NTSTATUS status;
|
||
PIP_NAT_EDITOR Table;
|
||
|
||
CALLTRACE(("NatQueryEditorTable\n"));
|
||
|
||
Key = InputBuffer->EnumerateContext;
|
||
KeAcquireSpinLock(&EditorLock, &Irql);
|
||
|
||
//
|
||
// See if this is a new enumeration or a continuation of an old one.
|
||
//
|
||
|
||
if (!Key) {
|
||
|
||
//
|
||
// This is a new enumeration. We start with the first item
|
||
// in the list of editors
|
||
//
|
||
|
||
Editor =
|
||
IsListEmpty(&EditorList)
|
||
? NULL
|
||
: CONTAINING_RECORD(EditorList.Flink, NAT_EDITOR, Link);
|
||
} else {
|
||
|
||
//
|
||
// This is a continuation. The context therefore contains
|
||
// the key for the next editor.
|
||
//
|
||
|
||
Editor = NatLookupEditor(Key, NULL);
|
||
}
|
||
|
||
if (!Editor) {
|
||
OutputBuffer->EnumerateCount = 0;
|
||
OutputBuffer->EnumerateContext = 0;
|
||
OutputBuffer->EnumerateTotalHint = EditorCount;
|
||
*OutputBufferLength =
|
||
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable);
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Compute the maximum number of entries we can store
|
||
//
|
||
|
||
Count =
|
||
*OutputBufferLength -
|
||
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable);
|
||
Count /= sizeof(IP_NAT_EDITOR);
|
||
|
||
//
|
||
// Walk the list storing mappings in the caller's buffer
|
||
//
|
||
|
||
Table = OutputBuffer->EnumerateTable;
|
||
|
||
for (i = 0, Link = &Editor->Link; i < Count && Link != &EditorList;
|
||
i++, Link = Link->Flink) {
|
||
Editor = CONTAINING_RECORD(Link, NAT_EDITOR, Link);
|
||
Table[i].Direction = EDITOR_KEY_DIRECTION(Editor->Key);
|
||
Table[i].Protocol = EDITOR_KEY_PROTOCOL(Editor->Key);
|
||
Table[i].Port = EDITOR_KEY_PORT(Editor->Key);
|
||
}
|
||
|
||
//
|
||
// The enumeration is over; update the output structure
|
||
//
|
||
|
||
*OutputBufferLength =
|
||
i * sizeof(IP_NAT_EDITOR) +
|
||
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable);
|
||
OutputBuffer->EnumerateCount = i;
|
||
OutputBuffer->EnumerateTotalHint = EditorCount;
|
||
if (Link == &EditorList) {
|
||
//
|
||
// We reached the end of the editor list
|
||
//
|
||
OutputBuffer->EnumerateContext = 0;
|
||
} else {
|
||
//
|
||
// Save the continuation context
|
||
//
|
||
OutputBuffer->EnumerateContext =
|
||
CONTAINING_RECORD(Link, NAT_EDITOR, Link)->Key;
|
||
}
|
||
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatQueryEditorTable
|
||
|
||
|
||
|
||
VOID
|
||
NatShutdownEditorManagement(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine shuts down the editor-management module.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_EDITOR Editor;
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatShutdownEditorManagement\n"));
|
||
|
||
KeAcquireSpinLock(&EditorLock, &Irql);
|
||
|
||
//
|
||
// Delete all editors
|
||
//
|
||
|
||
while (!IsListEmpty(&EditorList)) {
|
||
Editor = CONTAINING_RECORD(EditorList.Flink, NAT_EDITOR, Link);
|
||
RemoveEntryList(&Editor->Link);
|
||
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
||
NatCleanupEditor(Editor);
|
||
KeAcquireSpinLockAtDpcLevel(&EditorLock);
|
||
}
|
||
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
|
||
} // NatShutdownEditorManagement
|
||
|
||
|
||
//
|
||
// EDITOR HELPER ROUTINES
|
||
//
|
||
// These routines assume that references are held on the calling 'Interface',
|
||
// 'Editor' and 'Mapping' but that none are locked. The caller is assumed
|
||
// to be running at dispatch level, with the exception of the routine
|
||
// 'NatEditorDeregister' which may be invoked at lower IRQL.
|
||
//
|
||
|
||
NTSTATUS
|
||
NatEditorCreateTicket(
|
||
IN PVOID InterfaceHandle,
|
||
IN UCHAR Protocol,
|
||
IN ULONG PrivateAddress,
|
||
IN USHORT PrivatePort,
|
||
IN ULONG RemoteAddress OPTIONAL,
|
||
IN USHORT RemotePort OPTIONAL,
|
||
OUT PULONG PublicAddress,
|
||
OUT PUSHORT PublicPort
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by editors to dynamically establish mappings for
|
||
sessions.
|
||
|
||
Arguments:
|
||
|
||
InterfaceHandle - handle of interface over which the mapping is to be
|
||
established; would have been passed in a 'DataHandler' call.
|
||
|
||
Protocol - NAT_PROTOCOL_TCP or NAT_PROTOCOL_UDP
|
||
|
||
PrivateAddress - IP address of the session's private endpoint
|
||
|
||
PrivatePort - protocol port of the session's private endpoint
|
||
|
||
PublicAddress - receives the public address of the ticket created
|
||
|
||
PublicPort - receives the public port of the ticket created
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success/failure code.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
CALLTRACE(("NatEditorCreateTicket\n"));
|
||
|
||
//
|
||
// Lock the interface as expected by 'NatCreateTicket',
|
||
// and make the new ticket
|
||
//
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&((PNAT_INTERFACE)InterfaceHandle)->Lock);
|
||
|
||
status =
|
||
NatCreateTicket(
|
||
(PNAT_INTERFACE)InterfaceHandle,
|
||
Protocol,
|
||
PrivateAddress,
|
||
PrivatePort,
|
||
RemoteAddress,
|
||
RemotePort,
|
||
0,
|
||
NULL,
|
||
0,
|
||
PublicAddress,
|
||
PublicPort
|
||
);
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&((PNAT_INTERFACE)InterfaceHandle)->Lock);
|
||
|
||
return status;
|
||
|
||
} // NatEditorCreateTicket
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NatEditorDeleteTicket(
|
||
IN PVOID InterfaceHandle,
|
||
IN ULONG PublicAddress,
|
||
IN UCHAR Protocol,
|
||
IN USHORT PublicPort,
|
||
IN ULONG RemoteAddress OPTIONAL,
|
||
IN USHORT RemotePort OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deletes a ticket created by 'NatEditorDeleteTicket'.
|
||
|
||
Arguments:
|
||
|
||
InterfaceHandle - handle of interface on which the ticket was issued
|
||
|
||
Protocol - NAT_PROTOCOL_TCP or NAT_PROTOCOL_UDP
|
||
|
||
PublicAddress - address of the ticket's public endpoint
|
||
|
||
PublicPort - port of the ticket's public endpoint
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG64 Key;
|
||
ULONG64 RemoteKey;
|
||
NTSTATUS status;
|
||
|
||
CALLTRACE(("NatEditorDeleteTicket\n"));
|
||
|
||
//
|
||
// Lock the interface as expected by 'NatLookupAndDeleteTicket',
|
||
// and delete the ticket
|
||
//
|
||
|
||
Key = MAKE_TICKET_KEY(Protocol, PublicAddress, PublicPort);
|
||
RemoteKey = MAKE_TICKET_KEY(Protocol, RemoteAddress, RemotePort);
|
||
KeAcquireSpinLockAtDpcLevel(&((PNAT_INTERFACE)InterfaceHandle)->Lock);
|
||
status = NatLookupAndDeleteTicket(
|
||
(PNAT_INTERFACE)InterfaceHandle,
|
||
Key,
|
||
RemoteKey
|
||
);
|
||
KeReleaseSpinLockFromDpcLevel(&((PNAT_INTERFACE)InterfaceHandle)->Lock);
|
||
|
||
return status;
|
||
|
||
} // NatEditorDeleteTicket
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NatEditorDeregister(
|
||
IN PVOID EditorHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes an editor from the editor list,
|
||
and dissociates it from all sessions it is currently editing.
|
||
|
||
Arguments:
|
||
|
||
EditorHandle - handle of the editor to be removed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_EDITOR Editor = (PNAT_EDITOR)EditorHandle;
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatEditorDeregister\n"));
|
||
KeAcquireSpinLock(&EditorLock, &Irql);
|
||
Editor->Flags |= NAT_EDITOR_FLAG_DELETED;
|
||
RemoveEntryList(&Editor->Link);
|
||
KeReleaseSpinLock(&EditorLock, Irql);
|
||
if (InterlockedDecrement(&Editor->ReferenceCount) > 0) {
|
||
return STATUS_PENDING;
|
||
}
|
||
NatCleanupEditor(Editor);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatEditorDeregister
|
||
|
||
|
||
NTSTATUS
|
||
NatEditorDissociateSession(
|
||
IN PVOID EditorHandle,
|
||
IN PVOID SessionHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by an editor to dissociate itself from a specific
|
||
session.
|
||
|
||
Arguments:
|
||
|
||
EditorHandle - the editor which wishes to dissociate itself.
|
||
|
||
SessionHandle - the session from which the editor is disssociating itself.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure
|
||
|
||
Environment:
|
||
|
||
Invoked at dispatch level with neither 'EditorLock' nor 'EditorMappingLock'
|
||
held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PNAT_EDITOR Editor = (PNAT_EDITOR)EditorHandle;
|
||
PNAT_DYNAMIC_MAPPING Mapping = (PNAT_DYNAMIC_MAPPING)SessionHandle;
|
||
CALLTRACE(("NatEditorDissociateSession\n"));
|
||
KeAcquireSpinLockAtDpcLevel(&EditorLock);
|
||
if (Mapping->Editor != Editor) {
|
||
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
KeAcquireSpinLockAtDpcLevel(&EditorMappingLock);
|
||
NatMappingDetachEditor(Editor, Mapping);
|
||
KeReleaseSpinLockFromDpcLevel(&EditorMappingLock);
|
||
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatEditorDissociateSession
|
||
|
||
|
||
NTSTATUS
|
||
NatEditorEditSession(
|
||
IN PVOID DataHandle,
|
||
IN PVOID RecvBuffer,
|
||
IN ULONG OldDataOffset,
|
||
IN ULONG OldDataLength,
|
||
IN PUCHAR NewData,
|
||
IN ULONG NewDataLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by an editor to replace one range of bytes
|
||
in a packet with another range of bytes.
|
||
|
||
The routine makes the necessary adjustments to TCP sequence numbers
|
||
if the edition alters the size of a TCP segment.
|
||
|
||
Arguments:
|
||
|
||
EditorHandle - handle of the editor invoking this function.
|
||
|
||
SessionHandle - the session whose data is to be edited.
|
||
|
||
DataHandle - per-packet context passed to 'DataHandler'.
|
||
|
||
RecvBuffer - the 'RecvBuffer' argument to 'DataHandler'.
|
||
|
||
OldDataOffset - offset into 'RecvBuffer' of the range to be replaced
|
||
|
||
OldDataLength - length of range to be replaced
|
||
|
||
NewData - pointer to the bytes to serve as a replacement for 'OldData'
|
||
|
||
NewDataLength - number of bytes in the replacement range.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
#define XLATECONTEXT ((PNAT_XLATE_CONTEXT)DataHandle)
|
||
#define RECVBUFFER ((IPRcvBuf*)RecvBuffer)
|
||
|
||
LONG Diff;
|
||
IPRcvBuf* End;
|
||
ULONG EndOffset;
|
||
IPRcvBuf* NewEnd;
|
||
ULONG NewEndOffset;
|
||
BOOLEAN ResetIpHeader;
|
||
ULONG Size;
|
||
IPRcvBuf* Start;
|
||
ULONG StartOffset;
|
||
IPRcvBuf* Temp;
|
||
PUCHAR TempBuffer;
|
||
|
||
CALLTRACE(("NatEditorEditSession\n"));
|
||
|
||
ResetIpHeader =
|
||
((PUCHAR)XLATECONTEXT->Header == XLATECONTEXT->RecvBuffer->ipr_buffer);
|
||
|
||
//
|
||
// Find the buffer which contains the start of the range to be edited
|
||
//
|
||
|
||
for (Start = (IPRcvBuf*)RecvBuffer, StartOffset = 0;
|
||
Start && (StartOffset + Start->ipr_size) < OldDataOffset;
|
||
StartOffset += Start->ipr_size, Start = Start->ipr_next) { }
|
||
|
||
if (!Start) { return STATUS_INVALID_PARAMETER; }
|
||
|
||
StartOffset = OldDataOffset - StartOffset;
|
||
|
||
//
|
||
// Find the buffer which contains the end of the range to be edited
|
||
//
|
||
|
||
for (End = Start, EndOffset = OldDataLength + StartOffset;
|
||
End && EndOffset > End->ipr_size;
|
||
EndOffset -= End->ipr_size, End = End->ipr_next) { }
|
||
|
||
if (!End) { return STATUS_INVALID_PARAMETER; }
|
||
|
||
//
|
||
// Compute the change in length
|
||
//
|
||
|
||
Diff = NewDataLength - OldDataLength;
|
||
|
||
//
|
||
// If the length is decreasing, we MAY free some of the buffers.
|
||
// If the length is increasing, we WILL grow the last buffer.
|
||
//
|
||
|
||
if (Diff < 0) {
|
||
|
||
//
|
||
// See how many buffers we will need for the new length
|
||
//
|
||
|
||
for (NewEnd = Start, NewEndOffset = NewDataLength + StartOffset;
|
||
NewEnd && NewEndOffset > NewEnd->ipr_size;
|
||
NewEndOffset -= NewEnd->ipr_size, NewEnd = NewEnd->ipr_next) { }
|
||
|
||
//
|
||
// Free all the buffers we can
|
||
//
|
||
|
||
if (NewEnd != End) {
|
||
for (Temp = NewEnd->ipr_next; Temp != End; Temp = NewEnd->ipr_next) {
|
||
NewEnd->ipr_next = Temp->ipr_next;
|
||
Temp->ipr_next = NULL;
|
||
IPFreeBuff(Temp);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Copy over the remaining buffers
|
||
//
|
||
|
||
Size = min(NewDataLength, Start->ipr_size - StartOffset);
|
||
|
||
RtlCopyMemory(Start->ipr_buffer + StartOffset, NewData, Size);
|
||
|
||
NewData += Size;
|
||
NewDataLength -= Size;
|
||
|
||
for (Temp = Start->ipr_next; Temp != NewEnd->ipr_next;
|
||
Temp = Temp->ipr_next) {
|
||
Size = min(NewDataLength, Size);
|
||
RtlCopyMemory(Temp->ipr_buffer, NewData, Size);
|
||
NewData += Size;
|
||
NewDataLength -= Size;
|
||
}
|
||
|
||
//
|
||
// Now move up any data in the 'End' buffer
|
||
//
|
||
|
||
if (NewEnd == End) {
|
||
RtlMoveMemory(
|
||
End->ipr_buffer + NewEndOffset,
|
||
End->ipr_buffer + EndOffset,
|
||
End->ipr_size - EndOffset
|
||
);
|
||
End->ipr_size -= EndOffset - NewEndOffset;
|
||
} else {
|
||
NewEnd->ipr_size = NewEndOffset;
|
||
End->ipr_size -= EndOffset;
|
||
RtlMoveMemory(
|
||
End->ipr_buffer,
|
||
End->ipr_buffer + EndOffset,
|
||
End->ipr_size
|
||
);
|
||
}
|
||
}
|
||
else
|
||
if (Diff > 0) {
|
||
|
||
IPRcvBuf SavedRcvBuf;
|
||
|
||
//
|
||
// We will have to reallocate the last buffer;
|
||
// first save the old rcvbuf so we can free it
|
||
// once we've copied out the old data
|
||
//
|
||
|
||
SavedRcvBuf = *End;
|
||
SavedRcvBuf.ipr_next = NULL;
|
||
|
||
Size = End->ipr_size;
|
||
TempBuffer = End->ipr_buffer;
|
||
|
||
End->ipr_size += Diff;
|
||
|
||
if (!IPAllocBuff(End, End->ipr_size)) {
|
||
TRACE(EDIT, ("NatEditorEditSession: allocation failed\n"));
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
//
|
||
// If there's only one buffer, we have to copy any non-edited data
|
||
// at the start of the old buffer
|
||
//
|
||
|
||
if (Start == End && StartOffset) {
|
||
RtlCopyMemory(
|
||
Start->ipr_buffer,
|
||
TempBuffer,
|
||
StartOffset
|
||
);
|
||
}
|
||
|
||
//
|
||
// Copy any non-edited data that is at the end of the old buffer
|
||
//
|
||
|
||
if (Size != (EndOffset+1)) {
|
||
RtlCopyMemory(
|
||
End->ipr_buffer + EndOffset + Diff,
|
||
TempBuffer + EndOffset,
|
||
Size - EndOffset
|
||
);
|
||
}
|
||
|
||
FreeIprBuff(&SavedRcvBuf);
|
||
|
||
//
|
||
// Now copy over the buffers
|
||
//
|
||
|
||
Size = min(NewDataLength, Size);
|
||
|
||
RtlCopyMemory(Start->ipr_buffer + StartOffset, NewData, Size);
|
||
|
||
NewData += Size;
|
||
NewDataLength -= Size;
|
||
|
||
for (Temp = Start->ipr_next; Temp != End->ipr_next;
|
||
Temp = Temp->ipr_next) {
|
||
Size = min(NewDataLength, Size);
|
||
RtlCopyMemory(Temp->ipr_buffer, NewData, Size);
|
||
NewData += Size;
|
||
NewDataLength -= Size;
|
||
}
|
||
|
||
//
|
||
// Set up for checksum computation below
|
||
//
|
||
|
||
NewEnd = End;
|
||
NewEndOffset = EndOffset + Diff;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Equal length. We just walk through copying over existing data
|
||
//
|
||
|
||
Size = min(NewDataLength, Start->ipr_size - StartOffset);
|
||
|
||
RtlCopyMemory(Start->ipr_buffer + StartOffset, NewData, Size);
|
||
|
||
NewData += Size;
|
||
NewDataLength -= Size;
|
||
|
||
for (Temp = Start->ipr_next; Temp != End->ipr_next;
|
||
Temp = Temp->ipr_next) {
|
||
Size = min(NewDataLength, Size);
|
||
RtlCopyMemory(Temp->ipr_buffer, NewData, Size);
|
||
NewData += Size;
|
||
NewDataLength -= Size;
|
||
}
|
||
|
||
NewEnd = End;
|
||
NewEndOffset = EndOffset;
|
||
}
|
||
|
||
//
|
||
// Reset the 'Protocol' fields of the context which may be pointing
|
||
// to memory that was freed above.
|
||
//
|
||
|
||
if (ResetIpHeader) {
|
||
XLATECONTEXT->Header = (PIP_HEADER)XLATECONTEXT->RecvBuffer->ipr_buffer;
|
||
}
|
||
NAT_BUILD_XLATE_CONTEXT(
|
||
XLATECONTEXT,
|
||
XLATECONTEXT->Header,
|
||
XLATECONTEXT->DestinationType,
|
||
XLATECONTEXT->RecvBuffer,
|
||
XLATECONTEXT->SourceAddress,
|
||
XLATECONTEXT->DestinationAddress
|
||
);
|
||
|
||
//
|
||
// If this is a UDP packet, update the length field in the protocol header
|
||
//
|
||
|
||
if (Diff && XLATECONTEXT->Header->Protocol == NAT_PROTOCOL_UDP) {
|
||
PUDP_HEADER UdpHeader = (PUDP_HEADER)XLATECONTEXT->ProtocolHeader;
|
||
UdpHeader->Length = NTOHS(UdpHeader->Length);
|
||
UdpHeader->Length += (SHORT)Diff;
|
||
UdpHeader->Length = NTOHS(UdpHeader->Length);
|
||
}
|
||
|
||
//
|
||
// Update the packet's context to reflect the changes made
|
||
//
|
||
|
||
XLATECONTEXT->Flags |= NAT_XLATE_FLAG_EDITED;
|
||
XLATECONTEXT->Header->TotalLength =
|
||
NTOHS(XLATECONTEXT->Header->TotalLength) + (SHORT)Diff;
|
||
XLATECONTEXT->Header->TotalLength =
|
||
NTOHS(XLATECONTEXT->Header->TotalLength);
|
||
XLATECONTEXT->TcpSeqNumDelta += Diff;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
#undef XLATECONTEXT
|
||
#undef RECVBUFFER
|
||
|
||
} // NatEditorEditSession
|
||
|
||
|
||
|
||
VOID
|
||
NatEditorQueryInfoSession(
|
||
IN PVOID SessionHandle,
|
||
OUT PULONG PrivateAddress OPTIONAL,
|
||
OUT PUSHORT PrivatePort OPTIONAL,
|
||
OUT PULONG RemoteAddress OPTIONAL,
|
||
OUT PUSHORT RemotePort OPTIONAL,
|
||
OUT PULONG PublicAddress OPTIONAL,
|
||
OUT PUSHORT PublicPort OPTIONAL,
|
||
OUT PIP_NAT_SESSION_MAPPING_STATISTICS Statistics OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by editors to retrieve information about a session.
|
||
|
||
Arguments:
|
||
|
||
SessionHandle - handle of the session about which to retrieve information
|
||
|
||
PrivateAddress - receives the IP address of the session's private endpoint
|
||
|
||
PrivatePort - receives the protocol port of the session's private endpoint
|
||
|
||
RemoteAddress - receives the IP address of the session's remote endpoint
|
||
|
||
RemotePort - receives the protocol port of the session's remote endpoint
|
||
|
||
PublicAddress - receives the IP address of the session's public endpoint
|
||
|
||
PublicPort - receives the protocol port of the session's public endpoint
|
||
|
||
Statistics - receives any statistics for the mapping
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatEditorQueryInfoSession\n"));
|
||
KeAcquireSpinLock(&MappingLock, &Irql);
|
||
NatQueryInformationMapping(
|
||
(PNAT_DYNAMIC_MAPPING)SessionHandle,
|
||
NULL,
|
||
PrivateAddress,
|
||
PrivatePort,
|
||
RemoteAddress,
|
||
RemotePort,
|
||
PublicAddress,
|
||
PublicPort,
|
||
Statistics
|
||
);
|
||
KeReleaseSpinLock(&MappingLock, Irql);
|
||
} // NatEditorQueryInfoSession
|
||
|
||
|
||
|
||
VOID
|
||
NatEditorTimeoutSession(
|
||
IN PVOID EditorHandle,
|
||
IN PVOID SessionHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by an editor to indicate that a given session
|
||
should be timed out at the earliest opportunity.
|
||
|
||
Arguments:
|
||
|
||
EditorHandle - the editor requesting the timeout
|
||
|
||
SessionHandle - the session to be timed-out
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KeAcquireSpinLockAtDpcLevel(&((PNAT_DYNAMIC_MAPPING)SessionHandle)->Lock);
|
||
NatExpireMapping((PNAT_DYNAMIC_MAPPING)SessionHandle);
|
||
KeReleaseSpinLockFromDpcLevel(&((PNAT_DYNAMIC_MAPPING)SessionHandle)->Lock);
|
||
|
||
} // NatEditorTimeoutSession
|
||
|
||
|