windows-nt/Source/XPSP1/NT/net/rras/ip/nat/nbt.c

587 lines
13 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997, Microsoft Corporation
Module Name:
nbt.c
Abstract:
This module contains code for the NAT's NetBT support.
The support consists of an outbound-data handler for NetBT's datagram
service, which runs over UDP port 138.
Author:
Abolade Gbadegesin (t-abolag) 25-Aug-1997
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// Registration structure for NBT datagram-service editing
//
IP_NAT_REGISTER_EDITOR NbtRegisterEditor;
typedef struct _NBT_PSEUDO_HEADER {
PUCHAR MessageType;
PUCHAR Flags;
// PUSHORT DatagramId;
PULONG SourceAddress;
PUSHORT SourcePort;
// PUSHORT DatagramLength;
// PUSHORT PacketOffset;
PUCHAR SourceName;
PUCHAR DestinationName;
} NBT_PSEUDO_HEADER, *PNBT_PSEUDO_HEADER;
#define NBT_HEADER_FIELD(RecvBuffer, DataOffsetp, Header, Field, Type) \
FIND_HEADER_FIELD(RecvBuffer, DataOffsetp, Header, Field, NBT_HEADER, Type)
//
// NBT MAPPING MANAGEMENT ROUTINES (alphabetically)
//
NTSTATUS
NatCreateNbtMapping(
PNAT_INTERFACE Interfacep,
ULONG PrivateAddress,
ULONG PublicAddress,
UCHAR SourceName[],
PLIST_ENTRY InboundInsertionPoint,
PNAT_NBT_MAPPING* MappingCreated
)
/*++
Routine Description:
This routine is called to allocate and initialize an NBT mapping.
It assumes that the interface is locked.
Arguments:
PrivateAddress - the private address for the mapping
PublicAddress - the public address for the mapping
SourceName - the NetBIOS name for the mapping's private machine
InboundInsertionPoint - optionally supplies the point of insertion
in the list of NBT mappings
MappingCreated - receives the mapping created
Return Value:
NTSTATUS - success/failure code.
--*/
{
PNAT_NBT_MAPPING Mapping;
CALLTRACE(("NatCreateNbtMapping\n"));
*MappingCreated = NULL;
//
// Allocate space for the new mapping
//
Mapping = ALLOCATE_NBT_BLOCK();
if (!Mapping) {
TRACE(NBT, ("NatCreateNbtMapping: allocation failed\n"));
return STATUS_NO_MEMORY;
}
RtlZeroMemory(Mapping, sizeof(*Mapping));
Mapping->PrivateAddress = PrivateAddress;
Mapping->PublicAddress = PublicAddress;
RtlCopyMemory(Mapping->SourceName, SourceName, NBT_NAME_LENGTH);
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
//
// Find the insertion point if necessary
//
if (!InboundInsertionPoint &&
NatLookupInboundNbtMapping(
Interfacep,
PublicAddress,
SourceName,
&InboundInsertionPoint
)) {
TRACE(
NBT, ("NatCreateNbtMapping: duplicate %d.%d.%d.%d/%d.%d.%d.%d\n",
ADDRESS_BYTES(PublicAddress), ADDRESS_BYTES(PrivateAddress)
));
return STATUS_UNSUCCESSFUL;
}
//
// Insert the new mapping
//
InsertTailList(InboundInsertionPoint, &Mapping->Link);
*MappingCreated = Mapping;
return STATUS_SUCCESS;
} // NatCreateNbtMapping
PNAT_NBT_MAPPING
NatLookupInboundNbtMapping(
PNAT_INTERFACE Interfacep,
ULONG PublicAddress,
UCHAR SourceName[],
PLIST_ENTRY* InboundInsertionPoint
)
/*++
Routine Description:
This routine is invoked to search the list of mappings for a given entry.
Arguments:
Interfacep - the interface whose mapping-list is to be searched
PublicAddress - the public address of the mapping
SourceName - the private NBT endpoint's source-name
InboundInsertionPoint - optionally receives the insertion point
if the mapping is not found.
Return Value:
PNAT_NBT_MAPPING - the mapping if found, otherwise NULL
--*/
{
LONG cmp;
PLIST_ENTRY Link;
PNAT_NBT_MAPPING Mapping;
TRACE(PER_PACKET, ("NatLookupInboundNbtMapping\n"));
for (Link = Interfacep->NbtMappingList.Flink;
Link != &Interfacep->NbtMappingList;
Link = Link->Flink
) {
Mapping = CONTAINING_RECORD(Link, NAT_NBT_MAPPING, Link);
if (PublicAddress > Mapping->PublicAddress) { continue; }
else
if (PublicAddress < Mapping->PublicAddress) { break; }
cmp = memcmp(SourceName, Mapping->SourceName, NBT_NAME_LENGTH);
if (cmp > 0) { continue; }
else
if (cmp < 0) { break; }
//
// We've found the item.
//
return Mapping;
}
//
// The item wasn't found; store the insertion point if possible.
//
if (InboundInsertionPoint) { *InboundInsertionPoint = Link; }
return NULL;
} // NatLookupInboundNbtMapping
//
// NBT EDITOR HANDLER ROUTINES (alphabetically)
//
NTSTATUS
NatInitializeEditorNbt(
VOID
)
/*++
Routine Description:
This routine registers the NBT datagram-service editor with the NAT.
Arguments:
none.
Return Value:
NTSTATUS - indicates success/failure.
--*/
{
CALLTRACE(("NatInitializeEditorNbt\n"));
NbtRegisterEditor.Version = IP_NAT_VERSION;
NbtRegisterEditor.Flags = 0;
NbtRegisterEditor.Protocol = NAT_PROTOCOL_UDP;
NbtRegisterEditor.Port = NTOHS(NBT_DATAGRAM_PORT);
NbtRegisterEditor.Direction = NatOutboundDirection;
NbtRegisterEditor.EditorContext = NULL;
NbtRegisterEditor.CreateHandler = NULL;
NbtRegisterEditor.DeleteHandler = NULL;
NbtRegisterEditor.ForwardDataHandler = NatOutboundDataHandlerNbt;
NbtRegisterEditor.ReverseDataHandler = NULL;
return NatCreateEditor(&NbtRegisterEditor);
} // NatInitializeEditorNbt
NTSTATUS
NatOutboundDataHandlerNbt(
IN PVOID InterfaceHandle,
IN PVOID SessionHandle,
IN PVOID DataHandle,
IN PVOID EditorContext,
IN PVOID EditorSessionContext,
IN PVOID RecvBuffer,
IN ULONG DataOffset
)
/*++
Routine Description:
This routine is invoked for each datagram sent using NetBT's datagram
service. It replaces the address/port pair in the NetBT header with
the publicly visible address/port pair.
Arguments:
InterfaceHandle - handle to the outgoing NAT_INTERFACE
SessionHandle - the session's NAT_DYNAMIC_MAPPING
DataHandle - the packet's NAT_XLATE_CONTEXT
EditorContext - unused
EditorSessionContext - unused
RecvBuffer - contains the received packet
DataOffset - offset of the protocol data in 'RecvBuffer
Return Value:
NTSTATUS - indicates success/failure
--*/
{
#define RECVBUFFER ((IPRcvBuf*)RecvBuffer)
NBT_PSEUDO_HEADER Header;
PNAT_NBT_MAPPING Mapping;
LONG Offset = (LONG)DataOffset;
ULONG PrivateAddress;
ULONG PublicAddress;
USHORT PublicPort;
UCHAR SourceName[NBT_NAME_LENGTH];
NTSTATUS status;
CALLTRACE(("NatOutboundDataHandlerNbt\n"));
//
// Retrieve the message type
//
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, MessageType, PUCHAR);
if (!RecvBuffer) { return STATUS_UNSUCCESSFUL; }
//
// Only allow DIRECT_{UNIQUE|GROUP} messages, since they're the only ones
// that can be translated.
//
if (*Header.MessageType != NBT_MESSAGE_DIRECT_UNIQUE &&
*Header.MessageType != NBT_MESSAGE_DIRECT_GROUP
) {
return STATUS_UNSUCCESSFUL;
}
//
// Now retrieve the flags
//
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, Flags, PUCHAR);
if (!RecvBuffer) { return STATUS_UNSUCCESSFUL; }
//
// There's nothing to translate in datagram fragments,
// since they don't include the header.
//
if (!(*Header.Flags & NBT_FLAG_FIRST_FRAGMENT)) {
return STATUS_SUCCESS;
}
//
// Consult the NAT to get the public address/port info
//
NbtRegisterEditor.QueryInfoSession(
SessionHandle,
NULL,
NULL,
NULL,
NULL,
&PublicAddress,
&PublicPort,
NULL
);
//
// Retrieve the source-address and source-port fields
//
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, SourceAddress, PULONG);
if (!RecvBuffer) { return STATUS_UNSUCCESSFUL; }
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, SourcePort, PUSHORT);
if (!RecvBuffer) { return STATUS_UNSUCCESSFUL; }
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, SourceName, PUCHAR);
if (!RecvBuffer) { return STATUS_UNSUCCESSFUL; }
PrivateAddress = *Header.SourceAddress;
//
// Copy the SourceName to a local buffer
//
COPY_FROM_BUFFER(
SourceName,
RECVBUFFER,
NBT_NAME_LENGTH,
(ULONG)(Header.SourceName-RECVBUFFER->ipr_buffer)
);
//
// Attempt to create a mapping for the NBT datagram
//
status =
NatCreateNbtMapping(
InterfaceHandle,
PrivateAddress,
PublicAddress,
SourceName,
NULL,
&Mapping
);
if (!NT_SUCCESS(status)) {
//
// The mapping may already exist; be quiet if we can't create it.
//
return STATUS_SUCCESS;
}
//
// Translate the datagram-header source information
//
NatEditorEditLongSession(
DataHandle, Header.SourceAddress, PublicAddress
);
NatEditorEditShortSession(
DataHandle, Header.SourcePort, PublicPort
);
return STATUS_SUCCESS;
#undef RECVBUFFER
} // NatOutboundDataHandlerNbt
FORWARD_ACTION
NatTranslateNbt(
PNAT_INTERFACE Interfacep,
IP_NAT_DIRECTION Direction,
PNAT_XLATE_CONTEXT Contextp,
IPRcvBuf** InRecvBuffer,
IPRcvBuf** OutRecvBuffer
)
/*++
Routine Description:
This routine is called to translate an incoming NetBT datagram message,
by looking up the destination name in the interface's list of NBT mappings.
Arguments:
Interfacep - the boundary interface over which to translate.
Direction - the direction in which the packet is traveling
Contextp - initialized with context-information for the packet
InRecvBuffer - input buffer-chain
OutRecvBuffer - receives modified buffer-chain.
Return Value:
FORWARD_ACTION - indicates action to take on the packet.
--*/
{
#define RECVBUFFER ((IPRcvBuf*)RecvBuffer)
#define UDPHEADER ((PUDP_HEADER)Contextp->ProtocolHeader)
ULONG Checksum;
ULONG ChecksumDelta = 0;
UCHAR DestinationName[NBT_NAME_LENGTH];
NBT_PSEUDO_HEADER Header;
PNAT_NBT_MAPPING Mapping;
LONG Offset;
ULONG PublicAddress;
IPRcvBuf* RecvBuffer = Contextp->ProtocolRecvBuffer;
TRACE(PER_PACKET, ("NatTranslateNbt\n"));
//
// Initialize the context for accessing UDP data fields
//
Contextp->ProtocolDataOffset =
(ULONG)( (PUCHAR)UDPHEADER - Contextp->ProtocolRecvBuffer->ipr_buffer)
+ sizeof(UDP_HEADER);
Offset = (LONG)Contextp->ProtocolDataOffset;
//
// Retrieve the message type
//
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, MessageType, PUCHAR);
if (!RecvBuffer) { return FORWARD; }
//
// Only allow DIRECT_{UNIQUE|GROUP} messages, since they're the only ones
// that can be translated.
//
if (*Header.MessageType != NBT_MESSAGE_DIRECT_UNIQUE &&
*Header.MessageType != NBT_MESSAGE_DIRECT_GROUP
) {
return FORWARD;
}
//
// Now retrieve the flags
//
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, Flags, PUCHAR);
if (!RecvBuffer) { return FORWARD; }
//
// There's nothing to translate in datagram fragments,
// since they don't include the header.
//
if (!(*Header.Flags & NBT_FLAG_FIRST_FRAGMENT)) {
TRACE(NBT, ("NatTranslateNbt: NBT fragment ignored\n"));
return FORWARD;
}
//
// Retrieve the public address from the IP header
//
PublicAddress = Contextp->DestinationAddress;
//
// Get the destination name from within the NetBIOS header
//
NBT_HEADER_FIELD(RECVBUFFER, &Offset, &Header, DestinationName, PUCHAR);
if (!RecvBuffer) { return FORWARD; }
RtlCopyMemory(DestinationName, Header.DestinationName, NBT_NAME_LENGTH);
//
// Lookup an NBT mapping for the datagram,
// using the public address and the destination name.
//
Mapping =
NatLookupInboundNbtMapping(
Interfacep,
PublicAddress,
DestinationName,
NULL
);
if (!Mapping) { return FORWARD; }
//
// Translate the IP header
//
CHECKSUM_LONG(ChecksumDelta, ~PublicAddress);
Contextp->Header->DestinationAddress = Mapping->PrivateAddress;
CHECKSUM_LONG(ChecksumDelta, Contextp->Header->DestinationAddress);
CHECKSUM_UPDATE(Contextp->Header->Checksum);
if (UDPHEADER->Checksum) {
CHECKSUM_UPDATE(UDPHEADER->Checksum);
}
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
*OutRecvBuffer = *InRecvBuffer; *InRecvBuffer = NULL;
return FORWARD;
#undef RECVBUFFER
#undef UDPHEADER
} // NatTranslateNbt