windows-nt/Source/XPSP1/NT/net/netbt/sys/nt/tdiout.c

749 lines
23 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
Tdiout.c
Abstract:
This file represents the TDI interface on the bottom edge of NBT.
The procedures herein conform to the TDI I/F spec. and then convert
the information to NT specific Irps etc. This implementation can be
changed out to run on another OS.
Author:
Jim Stewart (Jimst) 10-2-92
Revision History:
--*/
#include "precomp.h" // procedure headings
// function prototypes for completion routines used in this file
NTSTATUS
TdiSendDatagramCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pSendbufferMdl
);
NTSTATUS
TcpConnectComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pContext
);
NTSTATUS
TcpDisconnectComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pContext
);
NTSTATUS
SendSessionCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pContext
);
// DEBUG
VOID
CheckIrpList(
);
//----------------------------------------------------------------------------
NTSTATUS
TdiSendDatagram(
IN PTDI_REQUEST pRequestInfo,
IN PTDI_CONNECTION_INFORMATION pSendDgramInfo,
IN ULONG SendLength,
OUT PULONG pSentSize,
IN tDGRAM_SEND_TRACKING *pDgramTracker
)
/*++
Routine Description:
This routine sends a datagram to the transport
Arguments:
pSendBuffer - this is really an Mdl in NT land. It must be tacked on
the end of the Mdl created for the Nbt datagram header.
Return Value:
The function value is the status of the operation.
--*/
{
NTSTATUS status;
PIRP pRequestIrp;
PMDL pMdl;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject;
PVOID pCompletionRoutine;
tBUFFER *pSendBuffer = &pDgramTracker->SendBuffer;
// get an Irp to send the message in
pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
status = GetIrp(&pRequestIrp); // get an Irp from the list
if (NT_SUCCESS(status))
{
pRequestIrp->CancelRoutine = NULL;
// set up the completion routine passed in from Udp Send using the APC
// fields in the Irp that would normally be used to complete the request
// back to the client - although we are really the client here so we can
// use these fields our self!
pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
(PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = (PVOID)pRequestInfo->RequestContext;
// Allocate a MDL and set the head sizes correctly
if (!(pMdl = IoAllocateMdl (pSendBuffer->pDgramHdr, pSendBuffer->HdrLength, FALSE, FALSE, NULL)))
{
REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pRequestIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
IF_DBG(NBT_DEBUG_TDIOUT)
KdPrint(("Nbt.TdiSendDatagram: Failed to get an Irp"));
}
// tack the client's send buffer (MDL) onto the end of the datagram header
// Mdl, and then pass the irp on downward to the transport
if (NT_SUCCESS(status) && pSendBuffer->pBuffer) {
pMdl->Next = IoAllocateMdl (pSendBuffer->pBuffer, pSendBuffer->Length, FALSE, FALSE, NULL);
if (pMdl->Next == NULL) {
REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pRequestIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
status = STATUS_INSUFFICIENT_RESOURCES;
IoFreeMdl(pMdl);
pMdl = NULL;
}
}
if (!NT_SUCCESS(status))
{
if (pRequestInfo->RequestNotifyObject) // call the completion routine (if there is one)
{
NBT_DEREFERENCE_DEVICE (pDgramTracker->pDeviceContext, REF_DEV_UDP_SEND, FALSE);
(*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
((PVOID)pRequestInfo->RequestContext,
STATUS_INSUFFICIENT_RESOURCES,
0L);
}
return(STATUS_PENDING); // so the Irp is not completed twice.
}
// Map the pages in memory...
ASSERT(!pSendBuffer->pBuffer || pMdl->Next);
MmBuildMdlForNonPagedPool(pMdl);
if (pMdl->Next) {
MmBuildMdlForNonPagedPool(pMdl->Next);
}
pCompletionRoutine = TdiSendDatagramCompletion;
// store some context stuff in the Irp stack so we can call the completion
// routine set by the Udpsend code...
TdiBuildSendDatagram (pRequestIrp,
pDeviceObject,
pFileObject,
pCompletionRoutine,
(PVOID)pMdl->Next, // The completion routine will know that we have allocated an extra MDL
pMdl,
SendLength,
pSendDgramInfo);
CHECK_COMPLETION(pRequestIrp);
status = IoCallDriver(pDeviceObject,pRequestIrp);
*pSentSize = SendLength; // Fill in the SentSize
// The transport always completes the IRP, so as long as the irp made it
// to the transport it got completed. The return code from the transport
// does not indicate if the irp was completed or not. The real status
// of the operation is in the Irp Iostatus return code.
// What we need to do is make sure NBT does not complete the irp AND the
// transport complete the Irp. Therefore this routine returns
// status pending if the Irp was passed to the transport, regardless of
// the return code from the transport. This return code signals the caller
// that the irp will be completed via the completion routine and the
// actual status of the send can be found in the Irpss IoStatus.Status
// variable.
//
// If the Caller of this routine gets a bad return code, they can assume
// that this routine failed to give the Irp to the transport and it
// is safe for them to complete the Irp themselves.
//
// If the Completion routine is set to null, then there is no danger
// of the irp completing twice and this routine will return the transport
// return code in that case.
if (pRequestInfo->RequestNotifyObject)
{
return(STATUS_PENDING);
}
else
{
return(status);
}
}
//----------------------------------------------------------------------------
NTSTATUS
TdiSendDatagramCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pSendbufferMdl
)
/*++
Routine Description:
This routine handles the completion of a datagram send to the transport.
It must call the client completion routine and free the Irp and Mdl.
Arguments:
Return Value:
NTSTATUS - success or not
--*/
{
KIRQL OldIrq;
tDGRAM_SEND_TRACKING *pTracker = pIrp->Overlay.AsynchronousParameters.UserApcContext;
tDEVICECONTEXT *pDeviceContext;
// check for a completion routine of the clients to call...
if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine)
{
//
// The Tracker can be free'ed in the routine below, so save the Device ptr
//
pDeviceContext = pTracker->pDeviceContext;
(*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine))
((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext,
pIrp->IoStatus.Status,
(ULONG)pIrp->IoStatus.Information); // sent length
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_UDP_SEND, FALSE);
}
// Don't depend on pIrp->MdlAddress->Next which may occassionally changed by others
ASSERT((PMDL)pSendbufferMdl == pIrp->MdlAddress->Next);
if (pSendbufferMdl) {
IoFreeMdl((PMDL)pSendbufferMdl);
}
// deallocate the MDL.. this is done by the IO subsystem in IoCompleteRequest
pIrp->MdlAddress->Next = NULL;
IoFreeMdl(pIrp->MdlAddress);
REMOVE_FROM_LIST(&pIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
// return this status to stop the IO subsystem from further processing the
// IRP - i.e. trying to complete it back to the initiating thread! -since
// there is no initiating thread - we are the initiator
return(STATUS_MORE_PROCESSING_REQUIRED);
}
//----------------------------------------------------------------------------
PIRP
NTAllocateNbtIrp(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine allocates an irp by calling the IO system, and then it
undoes the queuing of the irp to the current thread, since these are
NBTs own irps, and should not be attached to a thread.
Arguments:
Return Value:
NTSTATUS - success or not
--*/
{
PIRP pIrp;
// call the IO subsystem to allocate the irp
pIrp = IoAllocateIrp(DeviceObject->StackSize,FALSE);
if (!pIrp)
{
return(NULL);
}
//
// Simply return a pointer to the packet.
//
return pIrp;
}
//----------------------------------------------------------------------------
NTSTATUS
TdiConnect(
IN PTDI_REQUEST pRequestInfo,
IN ULONG_PTR lTimeout,
IN PTDI_CONNECTION_INFORMATION pSendInfo,
IN PIRP pClientIrp
)
/*++
Routine Description:
This routine sends a connect request to the tranport provider, to setup
a connection to the other side...
Arguments:
Return Value:
The function value is the status of the operation.
--*/
{
NTSTATUS status;
PIRP pRequestIrp;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject;
// get an Irp to send the message in
pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
// get an Irp from the list
status = GetIrp(&pRequestIrp);
if (!NT_SUCCESS(status))
{
IF_DBG(NBT_DEBUG_TDIOUT)
KdPrint(("Nbt.TdiConnect: Failed to get an Irp"));
// call the completion routine with this status
(*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
((PVOID)pRequestInfo->RequestContext,
STATUS_INSUFFICIENT_RESOURCES,
0L);
return(STATUS_PENDING);
}
pRequestIrp->CancelRoutine = NULL;
// set up the completion routine passed in from Tcp SessionStart using the APC
// fields in the Irp that would normally be used to complete the request
// back to the client - although we are really the client here so we can
// use these fields ourselves
pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
(PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
pRequestIrp->Overlay.AsynchronousParameters.UserApcContext =
(PVOID)pRequestInfo->RequestContext;
// store some context stuff in the Irp stack so we can call the completion
// routine set by the Udpsend code...
TdiBuildConnect(
pClientIrp,
pDeviceObject,
pFileObject,
TcpConnectComplete,
(PVOID)pRequestIrp, //context value passed to completion routine
lTimeout, // use timeout on connect
pSendInfo,
NULL);
pRequestIrp->MdlAddress = NULL;
CHECK_COMPLETION(pClientIrp);
status = IoCallDriver(pDeviceObject,pClientIrp);
// the transport always completes the IRP, so we always return status pending
return(STATUS_PENDING);
}
//----------------------------------------------------------------------------
NTSTATUS
TdiDisconnect(
IN PTDI_REQUEST pRequestInfo,
IN PVOID lTimeout,
IN ULONG Flags,
IN PTDI_CONNECTION_INFORMATION pSendInfo,
IN PCTE_IRP pClientIrp,
IN BOOLEAN Wait
)
/*++
Routine Description:
This routine sends a connect request to the tranport provider, to setup
a connection to the other side...
Arguments:
pClientIrp - this is the irp that the client used when it issued an
NbtDisconnect. We pass this irp to the transport so that
the client can do a Ctrl C and cancel the irp if the
disconnect takes too long.
Return Value:
The function value is the status of the operation.
--*/
{
NTSTATUS status;
PIRP pRequestIrp;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject;
// get an Irp to send the message in
pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
status = GetIrp(&pRequestIrp);
if (!NT_SUCCESS(status))
{
IF_DBG(NBT_DEBUG_TDIOUT)
KdPrint(("Nbt.TdiDisconnect: Failed to get an Irp"));
// call the completion routine will this status
(*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
((PVOID)pRequestInfo->RequestContext,
STATUS_INSUFFICIENT_RESOURCES,
0L);
return(STATUS_PENDING);
}
if (!pClientIrp)
{
// if no client irp was passed in, then just use our Irp
pClientIrp = pRequestIrp;
}
pRequestIrp->CancelRoutine = NULL;
// set up the completion routine passed in from Tcp SessionStart using the APC
// fields in the Irp that would normally be used to complete the request
// back to the client - although we are really the client here so we can
// use these fields ourselves
pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
(PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
pRequestIrp->Overlay.AsynchronousParameters.UserApcContext =
(PVOID)pRequestInfo->RequestContext;
// store some context stuff in the Irp stack so we can call the completion
// routine set by the Udpsend code...
// Note that pRequestIrp is passed to the completion routine as a context
// value so we will know the routine to call for the client's completion.
TdiBuildDisconnect(
pClientIrp,
pDeviceObject,
pFileObject,
TcpConnectComplete,
(PVOID)pRequestIrp, //context value passed to completion routine
lTimeout,
Flags,
NULL, // send connection info
NULL); // return connection info
pRequestIrp->MdlAddress = NULL;
// if Wait is set, then this means do a synchronous disconnect and block
// until the irp is returned.
//
if (Wait)
{
status = SubmitTdiRequest(pFileObject,pClientIrp);
if (!NT_SUCCESS(status))
{
IF_DBG(NBT_DEBUG_TDIOUT)
KdPrint (("Nbt.TdiDisconnect: ERROR -- SubmitTdiRequest returned <%x>\n", status));
}
//
// return the irp to its pool
//
REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pRequestIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
return(status);
}
else
{
CHECK_COMPLETION(pClientIrp);
status = IoCallDriver(pDeviceObject,pClientIrp);
// the transport always completes the IRP, so we always return status pending
return(STATUS_PENDING);
}
}
//----------------------------------------------------------------------------
NTSTATUS
TcpConnectComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
/*++
Routine Description:
This routine handles the completion of a TCP session setup. The TCP
connection is either setup or not depending on the status returned here.
It must called the clients completion routine (in udpsend.c). Which should
look after sending the NetBios sesion startup pdu across the TCP connection.
The pContext value is actually one of NBTs irps that is JUST used to store
the calling routines completion routine. The real Irp used is the original
client's irp. This is done so that IoCancelIrp will cancel the connect
properly.
Arguments:
Return Value:
NTSTATUS - success or not
--*/
{
KIRQL OldIrq;
PIRP pMyIrp;
pMyIrp = (PIRP)pContext;
// check for a completion routine of the clients to call...
if (pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine)
{
(*((NBT_COMPLETION)pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine))
((PVOID)pMyIrp->Overlay.AsynchronousParameters.UserApcContext,
pIrp->IoStatus.Status,
0L);
}
REMOVE_FROM_LIST(&pMyIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pMyIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
// return this status to stop the IO subsystem from further processing the
// IRP - i.e. trying to complete it back to the initiating thread! -since
// there is not initiating thread - we are the initiator
return(STATUS_MORE_PROCESSING_REQUIRED);
}
//----------------------------------------------------------------------------
NTSTATUS
TdiSend(
IN PTDI_REQUEST pRequestInfo,
IN USHORT sFlags,
IN ULONG SendLength,
OUT PULONG pSentSize,
IN tBUFFER *pSendBuffer,
IN ULONG Flags
)
/*++
Routine Description:
This routine sends a packet to the transport on a TCP connection
Arguments:
pSendBuffer - this is really an Mdl in NT land. It must be tacked on
the end of the Mdl created for the Nbt datagram header.
Return Value:
The function value is the status of the operation.
--*/
{
NTSTATUS status;
PIRP pRequestIrp;
PMDL pMdl;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject;
// get an Irp to send the message in
pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
// get an Irp from the list
status = GetIrp(&pRequestIrp);
if (!NT_SUCCESS(status))
{
IF_DBG(NBT_DEBUG_TDIOUT)
KdPrint(("Nbt.TdiSend: Failed to get an Irp"));
// call the completion routine with this status
if (pRequestInfo->RequestNotifyObject)
{
(*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
((PVOID)pRequestInfo->RequestContext,
STATUS_INSUFFICIENT_RESOURCES,
0L);
}
return(STATUS_INSUFFICIENT_RESOURCES);
}
pRequestIrp->CancelRoutine = NULL;
// set up the completion routine passed in from Udp Send using the APC
// fields in the Irp that would normally be used to complete the request
// back to the client - although we are really the client here so we can
// use these fields our self!
pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
(PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
pRequestIrp->Overlay.AsynchronousParameters.UserApcContext =
(PVOID)pRequestInfo->RequestContext;
// get the MDL that is currently linked to the IRP (i.e. created at the
// same time that we created the IRP list. Set the sizes correctly in
// the MDL header.
pMdl = IoAllocateMdl(
pSendBuffer->pDgramHdr,
pSendBuffer->HdrLength,
FALSE,
FALSE,
NULL);
if (!pMdl)
{
REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pRequestIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
// call the completion routine will this status
if (pRequestInfo->RequestNotifyObject)
{
(*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
((PVOID)pRequestInfo->RequestContext,
STATUS_INSUFFICIENT_RESOURCES,
0L);
}
return(STATUS_INSUFFICIENT_RESOURCES);
}
// Map the pages in memory...
MmBuildMdlForNonPagedPool(pMdl);
TdiBuildSend(
pRequestIrp,
pDeviceObject,
pFileObject,
SendSessionCompletionRoutine,
NULL, //context value passed to completion routine
pMdl,
sFlags,
SendLength); // include session hdr length (ULONG)
//
// tack the Client's buffer on the end, if there is one
//
if (pSendBuffer->Length)
{
pMdl->Next = pSendBuffer->pBuffer;
}
CHECK_COMPLETION(pRequestIrp);
status = IoCallDriver(pDeviceObject,pRequestIrp);
*pSentSize = SendLength; // the size we attempted to send
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
SendSessionCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID pContext
)
/*++
Routine Description:
This routine handles the completion of a send to the transport.
It must call any client supplied completion routine and free the Irp
and Mdl back to its pool.
Arguments:
Return Value:
NTSTATUS - success or not
--*/
{
KIRQL OldIrq;
//
// check for a completion routine of the clients to call...
//
if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine)
{
(*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine))
((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext,
pIrp->IoStatus.Status,
(ULONG)pIrp->IoStatus.Information); // sent length
}
IoFreeMdl(pIrp->MdlAddress);
REMOVE_FROM_LIST(&pIrp->ThreadListEntry);
ExInterlockedInsertTailList(&NbtConfig.IrpFreeList,
&pIrp->Tail.Overlay.ListEntry,
&NbtConfig.LockInfo.SpinLock);
//
// return this status to stop the IO subsystem from further processing the
// IRP - i.e. trying to complete it back to the initiating thread! -since
// there is no initiating thread - we are the initiator
//
return(STATUS_MORE_PROCESSING_REQUIRED);
}