587 lines
13 KiB
C
587 lines
13 KiB
C
/*++
|
||
|
||
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
|
||
|
||
|
||
|