mirror of https://github.com/lianthony/NT4.0
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.
672 lines
19 KiB
672 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Tdihndlr.c
|
|
|
|
Abstract:
|
|
|
|
This file contains code relating to manipulation of address objects
|
|
that is specific to the NT operating system. It creates address endpoints
|
|
with the transport provider.
|
|
|
|
Author:
|
|
|
|
Jim Stewart (Jimst) 10-2-92
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "nbtprocs.h"
|
|
|
|
//******************* Pageable Routine Declarations ****************
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma CTEMakePageable(PAGE, NbtTdiOpenAddress)
|
|
#pragma CTEMakePageable(PAGE, NbtTdiOpenControl)
|
|
#pragma CTEMakePageable(PAGE, SetEventHandler)
|
|
#pragma CTEMakePageable(PAGE, SubmitTdiRequest)
|
|
#endif
|
|
//******************* Pageable Routine Declarations ****************
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtTdiOpenAddress (
|
|
OUT PHANDLE pHandle,
|
|
OUT PDEVICE_OBJECT *ppDeviceObject,
|
|
OUT PFILE_OBJECT *ppFileObject,
|
|
IN tDEVICECONTEXT *pDeviceContext,
|
|
IN USHORT PortNumber,
|
|
IN ULONG IpAddress,
|
|
IN ULONG Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Note: This synchronous call may take a number of seconds. It runs in
|
|
the context of the caller. The code Opens an Address object with the
|
|
transport provider and then sets up event handlers for Receive,
|
|
Disconnect, Datagrams and Errors.
|
|
|
|
THIS ROUTINE MUST BE CALLED IN THE CONTEXT OF THE FSP (I.E.
|
|
PROBABLY AN EXECUTIVE WORKER THREAD).
|
|
|
|
The address data structures are found in tdi.h , but they are rather
|
|
confusing since the definitions have been spread across several data types.
|
|
This section shows the complete data type for Ip address:
|
|
|
|
typedef struct
|
|
{
|
|
int TA_AddressCount;
|
|
struct _TA_ADDRESS
|
|
{
|
|
USHORT AddressType;
|
|
USHORT AddressLength;
|
|
struct _TDI_ADDRESS_IP
|
|
{
|
|
USHORT sin_port;
|
|
USHORT in_addr;
|
|
UCHAR sin_zero[8];
|
|
} TDI_ADDRESS_IP
|
|
|
|
} TA_ADDRESS[AddressCount];
|
|
|
|
} TRANSPORT_ADDRESS
|
|
|
|
An EA buffer is allocated (for the IRP), with an EA name of "TransportAddress"
|
|
and value is a structure of type TRANSPORT_ADDRESS.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
OBJECT_ATTRIBUTES AddressAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PFILE_FULL_EA_INFORMATION EaBuffer;
|
|
NTSTATUS status;
|
|
PWSTR pNameTcp=L"Tcp";
|
|
PWSTR pNameUdp=L"Udp";
|
|
UNICODE_STRING ucDeviceName;
|
|
PTRANSPORT_ADDRESS pTransAddressEa;
|
|
PTRANSPORT_ADDRESS pTransAddr;
|
|
TDI_ADDRESS_IP IpAddr;
|
|
BOOLEAN Attached = FALSE;
|
|
PFILE_OBJECT pFileObject;
|
|
HANDLE FileHandle;
|
|
|
|
CTEPagedCode();
|
|
*ppFileObject = NULL;
|
|
*ppDeviceObject = NULL;
|
|
// copy device name into the unicode string - either Udp or Tcp
|
|
//
|
|
if (Flags & TCP_FLAG)
|
|
status = CreateDeviceString(pNameTcp,&ucDeviceName);
|
|
else
|
|
status = CreateDeviceString(pNameUdp,&ucDeviceName);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return(status);
|
|
}
|
|
EaBuffer = NbtAllocMem(
|
|
sizeof(FILE_FULL_EA_INFORMATION) - 1 +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TRANSPORT_ADDRESS) +
|
|
sizeof(TDI_ADDRESS_IP),NBT_TAG('j'));
|
|
|
|
if (EaBuffer == NULL)
|
|
{
|
|
ASSERTMSG(
|
|
(PCHAR)"Unable to get memory for an Eabuffer to open an address",
|
|
(PVOID)EaBuffer);
|
|
CTEMemFree(ucDeviceName.Buffer);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
EaBuffer->NextEntryOffset = 0;
|
|
EaBuffer->Flags = 0;
|
|
EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
|
|
|
|
EaBuffer->EaValueLength = sizeof(TRANSPORT_ADDRESS) -1
|
|
+ sizeof(TDI_ADDRESS_IP);
|
|
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("EaValueLength = %d\n",EaBuffer->EaValueLength));
|
|
|
|
// put "TransportAddress" into the name
|
|
//
|
|
RtlMoveMemory(
|
|
EaBuffer->EaName,
|
|
TdiTransportAddress,
|
|
EaBuffer->EaNameLength + 1);
|
|
|
|
// fill in the IP address and Port number
|
|
//
|
|
pTransAddressEa = (TRANSPORT_ADDRESS *)&EaBuffer->EaName[EaBuffer->EaNameLength+1];
|
|
|
|
|
|
// allocate Memory for the transport address
|
|
//
|
|
pTransAddr = NbtAllocMem(
|
|
sizeof(TDI_ADDRESS_IP)+sizeof(TRANSPORT_ADDRESS),NBT_TAG('k'));
|
|
|
|
pTransAddr->TAAddressCount = 1;
|
|
pTransAddr->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP);
|
|
pTransAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
|
|
|
IpAddr.sin_port = htons(PortNumber); // put in network order
|
|
IpAddr.in_addr = htonl(IpAddress);
|
|
|
|
// zero fill the last component of the IP address
|
|
//
|
|
RtlFillMemory((PVOID)&IpAddr.sin_zero,
|
|
sizeof(IpAddr.sin_zero),
|
|
0);
|
|
|
|
// copy the ip address to the end of the structure
|
|
//
|
|
RtlMoveMemory(pTransAddr->Address[0].Address,
|
|
(CONST PVOID)&IpAddr,
|
|
sizeof(IpAddr));
|
|
|
|
// copy the ip address to the end of the name in the EA structure
|
|
//
|
|
RtlMoveMemory((PVOID)pTransAddressEa,
|
|
(CONST PVOID)pTransAddr,
|
|
sizeof(TDI_ADDRESS_IP) + sizeof(TRANSPORT_ADDRESS)-1);
|
|
|
|
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("creating Address named %ws\n",ucDeviceName.Buffer));
|
|
|
|
InitializeObjectAttributes(
|
|
&AddressAttributes,
|
|
&ucDeviceName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = ZwCreateFile(
|
|
&FileHandle,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&AddressAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
0,
|
|
(PVOID)EaBuffer,
|
|
sizeof(FILE_FULL_EA_INFORMATION) - 1 +
|
|
EaBuffer->EaNameLength + 1 +
|
|
EaBuffer->EaValueLength);
|
|
|
|
CTEMemFree((PVOID)pTransAddr);
|
|
CTEMemFree((PVOID)EaBuffer);
|
|
CTEMemFree(ucDeviceName.Buffer);
|
|
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("NBT:Failed Create (address) File, status = %X\n",status ));
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// if the ZwCreate passed set the status to the IoStatus
|
|
status = IoStatusBlock.Status;
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint(("Nbt:Failed to Open the Address to the transport, status = %X\n",
|
|
status));
|
|
|
|
return(status);
|
|
}
|
|
|
|
// dereference the file object to keep the device ptr around to avoid
|
|
// this dereference at run time
|
|
//
|
|
status = ObReferenceObjectByHandle(
|
|
FileHandle,
|
|
(ULONG)0,
|
|
0,
|
|
KernelMode,
|
|
(PVOID *)&pFileObject,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// return the handle to the caller
|
|
//
|
|
*pHandle = FileHandle;
|
|
//
|
|
// return the parameter to the caller
|
|
//
|
|
*ppFileObject = pFileObject;
|
|
|
|
*ppDeviceObject = IoGetRelatedDeviceObject(*ppFileObject);
|
|
|
|
status = SetEventHandler(
|
|
*ppDeviceObject,
|
|
*ppFileObject,
|
|
TDI_EVENT_ERROR,
|
|
(PVOID)TdiErrorHandler,
|
|
(PVOID)pDeviceContext);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// if this is a TCP address being opened, then create different
|
|
// event handlers for connections
|
|
//
|
|
if (Flags & TCP_FLAG)
|
|
{
|
|
status = SetEventHandler(
|
|
*ppDeviceObject,
|
|
*ppFileObject,
|
|
TDI_EVENT_RECEIVE,
|
|
(PVOID)TdiReceiveHandler,
|
|
(PVOID)pDeviceContext);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = SetEventHandler(
|
|
*ppDeviceObject,
|
|
*ppFileObject,
|
|
TDI_EVENT_DISCONNECT,
|
|
(PVOID)TdiDisconnectHandler,
|
|
(PVOID)pDeviceContext);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// only set a connect handler if the session flag is set.
|
|
// In this case the address being opened is the Netbios session
|
|
// port 139
|
|
//
|
|
if (Flags & SESSION_FLAG)
|
|
{
|
|
status = SetEventHandler(
|
|
*ppDeviceObject,
|
|
*ppFileObject,
|
|
TDI_EVENT_CONNECT,
|
|
(PVOID)TdiConnectHandler,
|
|
(PVOID)pDeviceContext);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
return(status);
|
|
}
|
|
|
|
}
|
|
else
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// Datagram ports only need this event handler
|
|
if (PortNumber == NBT_DATAGRAM_UDP_PORT)
|
|
{
|
|
// Datagram Udp Handler
|
|
status = SetEventHandler(
|
|
*ppDeviceObject,
|
|
*ppFileObject,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
(PVOID)TdiRcvDatagramHandler,
|
|
(PVOID)pDeviceContext);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
return(status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Name Service Udp handler
|
|
status = SetEventHandler(
|
|
*ppDeviceObject,
|
|
*ppFileObject,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
(PVOID)TdiRcvNameSrvHandler,
|
|
(PVOID)pDeviceContext);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
return(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ERROR Case
|
|
//
|
|
ObDereferenceObject(pFileObject);
|
|
ZwClose(FileHandle);
|
|
|
|
return(status);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("Failed Open Address (Dereference Object) status = %X\n",
|
|
status));
|
|
|
|
ZwClose(FileHandle);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return(status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
NbtTdiOpenControl (
|
|
IN tDEVICECONTEXT *pDeviceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens a control object with the transport. It is very similar
|
|
to opening an address object, above.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PWSTR pName=L"Tcp";
|
|
PFILE_FULL_EA_INFORMATION EaBuffer;
|
|
UNICODE_STRING DeviceName;
|
|
BOOLEAN Attached = FALSE;
|
|
|
|
|
|
CTEPagedCode();
|
|
// copy device name into the unicode string
|
|
Status = CreateDeviceString(pName,&DeviceName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
InitializeObjectAttributes (
|
|
&ObjectAttributes,
|
|
&DeviceName,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer));
|
|
|
|
EaBuffer = NULL;
|
|
|
|
Status = ZwCreateFile (
|
|
(PHANDLE)&pDeviceContext->hControl,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&ObjectAttributes, // object attributes.
|
|
&IoStatusBlock, // returned status information.
|
|
NULL, // block size (unused).
|
|
FILE_ATTRIBUTE_NORMAL, // file attributes.
|
|
0,
|
|
FILE_CREATE,
|
|
0, // create options.
|
|
(PVOID)EaBuffer, // EA buffer.
|
|
0); // Ea length
|
|
|
|
|
|
CTEMemFree(DeviceName.Buffer);
|
|
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint( ("OpenControl CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status));
|
|
|
|
if ( NT_SUCCESS( Status ))
|
|
{
|
|
// if the ZwCreate passed set the status to the IoStatus
|
|
Status = IoStatusBlock.Status;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("Nbt:Failed to Open the control connection to the transport, status = %X\n",
|
|
Status));
|
|
|
|
}
|
|
else
|
|
{
|
|
// get a reference to the file object and save it since we can't
|
|
// dereference a file handle at DPC level so we do it now and keep
|
|
// the ptr around for later.
|
|
Status = ObReferenceObjectByHandle(
|
|
pDeviceContext->hControl,
|
|
0L,
|
|
NULL,
|
|
KernelMode,
|
|
(PVOID *)&pDeviceContext->pControlFileObject,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ZwClose(pDeviceContext->hControl);
|
|
}
|
|
else
|
|
pDeviceContext->pControlDeviceObject =
|
|
IoGetRelatedDeviceObject(pDeviceContext->pControlFileObject);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
KdPrint(("Nbt:Failed to Open the control connection to the transport, status1 = %X\n",
|
|
Status));
|
|
|
|
// set control file object ptr to null so we know that we didnot open
|
|
// the control point.
|
|
//
|
|
pDeviceContext->pControlFileObject = NULL;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} /* NbtTdiOpenConnection */
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
CompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does not complete the Irp. It is used to signal to a
|
|
synchronous part of the NBT driver that it can proceed (i.e.
|
|
to allow some code that is waiting on a "KeWaitForSingleObject" to
|
|
proceeed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - Supplies Irp that the transport has finished processing.
|
|
|
|
Context - Supplies the event associated with the Irp.
|
|
|
|
Return Value:
|
|
|
|
The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
|
|
processing Irp stack locations at this point.
|
|
|
|
--*/
|
|
{
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint( ("Completion event: %X, Irp: %X, DeviceObject: %X\n",
|
|
Context,
|
|
Irp,
|
|
DeviceObject));
|
|
|
|
KeSetEvent((PKEVENT )Context, 0, FALSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
SetEventHandler (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG EventType,
|
|
IN PVOID EventHandler,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers an event handler with a TDI transport provider.
|
|
|
|
Arguments:
|
|
|
|
IN PDEVICE_OBJECT DeviceObject - Supplies the device object of the transport provider.
|
|
IN PFILE_OBJECT FileObject - Supplies the address object's file object.
|
|
IN ULONG EventType, - Supplies the type of event.
|
|
IN PVOID EventHandler - Supplies the event handler.
|
|
IN PVOID Context - Supplies the context passed into the event handler when it runs
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the set event operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
|
|
CTEPagedCode();
|
|
Irp = IoAllocateIrp(IoGetRelatedDeviceObject(FileObject)->StackSize, FALSE);
|
|
|
|
if (Irp == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
TdiBuildSetEventHandler(Irp, DeviceObject, FileObject,
|
|
NULL, NULL,
|
|
EventType, EventHandler, Context);
|
|
|
|
Status = SubmitTdiRequest(FileObject, Irp);
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
SubmitTdiRequest (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine submits a request to TDI and waits for it to complete.
|
|
|
|
Arguments:
|
|
|
|
IN PFILE_OBJECT FileObject - Connection or Address handle for TDI request
|
|
IN PIRP Irp - TDI request to submit.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of request.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
|
|
|
|
CTEPagedCode();
|
|
KeInitializeEvent (&Event, NotificationEvent, FALSE);
|
|
|
|
// set the address of the routine to be executed when the IRP
|
|
// finishes. This routine signals the event and allows the code
|
|
// below to continue (i.e. KeWaitForSingleObject)
|
|
//
|
|
IoSetCompletionRoutine(Irp,
|
|
(PIO_COMPLETION_ROUTINE)CompletionRoutine,
|
|
(PVOID)&Event,
|
|
TRUE, TRUE, TRUE);
|
|
|
|
CHECK_COMPLETION(Irp);
|
|
Status = IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp);
|
|
|
|
//
|
|
// If it failed immediately, return now, otherwise wait.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KdPrint(("Failed to Submit Tdi Request, status = %X\n",Status));
|
|
return Status;
|
|
}
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
|
|
Status = KeWaitForSingleObject((PVOID)&Event, // Object to wait on.
|
|
Executive, // Reason for waiting
|
|
KernelMode, // Processor mode
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KdPrint(("Failed on return from KeWaitForSingleObj in Set Event Handler, status = %X\n",
|
|
Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
IF_DBG(NBT_DEBUG_TDIADDR)
|
|
KdPrint(("Io Status from setting event = %X\n",Status));
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|