|
|
/*++
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
|