windows-nt/Source/XPSP1/NT/net/netbeui/sys/action.c

663 lines
16 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
Module Name:
action.c
Abstract:
This module contains support for the TdiAction handler.
Author:
David Beaver (dbeaver) 2-July-1991
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
typedef struct _QUERY_INDICATION {
UCHAR Command;
USHORT Data2;
UCHAR DestinationName[16];
UCHAR SourceName[16];
} QUERY_INDICATION, *PQUERY_INDICATION;
typedef struct _ACTION_QUERY_INDICATION {
TDI_ACTION_HEADER Header;
QUERY_INDICATION QueryIndication;
} ACTION_QUERY_INDICATION, *PACTION_QUERY_INDICATION;
typedef struct _DATAGRAM_INDICATION {
UCHAR DestinationName[16];
UCHAR SourceName[16];
USHORT DatagramBufferLength;
UCHAR DatagramBuffer[1];
} DATAGRAM_INDICATION, *PDATAGRAM_INDICATION;
typedef struct _ACTION_DATAGRAM_INDICATION {
TDI_ACTION_HEADER Header;
DATAGRAM_INDICATION DatagramIndication;
} ACTION_DATAGRAM_INDICATION, *PACTION_DATAGRAM_INDICATION;
#define QUERY_INDICATION_CODE 1
#define DATAGRAM_INDICATION_CODE 2
VOID
NbfCancelAction(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
NbfTdiAction(
IN PDEVICE_CONTEXT DeviceContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine performs the TdiAction request for the transport
provider.
Arguments:
DeviceContext - The device context for the operation
Irp - the Irp for the requested operation.
Return Value:
NTSTATUS - status of operation.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
PTDI_ACTION_HEADER ActionHeader;
LARGE_INTEGER timeout = {0,0};
PTP_REQUEST tpRequest;
KIRQL oldirql, cancelirql;
ULONG BytesRequired;
//
// what type of status do we want?
//
irpSp = IoGetCurrentIrpStackLocation (Irp);
if ((!Irp->MdlAddress) ||
(MmGetMdlByteCount(Irp->MdlAddress) < sizeof(TDI_ACTION_HEADER))) {
return STATUS_INVALID_PARAMETER;
}
ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
if (!ActionHeader) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Make sure we have required number of bytes for this type of request
//
switch (ActionHeader->ActionCode) {
case QUERY_INDICATION_CODE:
BytesRequired = sizeof(ACTION_QUERY_INDICATION);
break;
case DATAGRAM_INDICATION_CODE:
BytesRequired = sizeof(ACTION_DATAGRAM_INDICATION);
break;
default:
return STATUS_NOT_IMPLEMENTED;
}
if (MmGetMdlByteCount(Irp->MdlAddress) < BytesRequired) {
return STATUS_INVALID_PARAMETER;
}
//
// Here the request is one of QUERY_INDICATION or DATAGRAM_INDICATION
//
//
// These two requests are sent by RAS to "MABF"
//
if (!RtlEqualMemory ((PVOID)(&ActionHeader->TransportId), "MABF", 4)) {
return STATUS_NOT_SUPPORTED;
}
//
// They should be sent on the control channel
//
if (irpSp->FileObject->FsContext2 != UlongToPtr(NBF_FILE_TYPE_CONTROL)) {
return STATUS_NOT_SUPPORTED;
}
//
// Create a request to describe this.
//
status = NbfCreateRequest (
Irp, // IRP for this request.
DeviceContext, // context.
REQUEST_FLAGS_DC, // partial flags.
Irp->MdlAddress,
MmGetMdlByteCount(Irp->MdlAddress),
timeout,
&tpRequest);
if (NT_SUCCESS (status)) {
NbfReferenceDeviceContext ("Action", DeviceContext, DCREF_REQUEST);
tpRequest->Owner = DeviceContextType;
tpRequest->FrameContext = (USHORT)irpSp->FileObject->FsContext;
IoAcquireCancelSpinLock(&cancelirql);
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql);
//
// Disallow these requests on a stopping device.
//
if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) {
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql);
IoReleaseCancelSpinLock(cancelirql);
NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0);
} else {
if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) {
InsertTailList (
&DeviceContext->QueryIndicationQueue,
&tpRequest->Linkage);
} else {
InsertTailList (
&DeviceContext->DatagramIndicationQueue,
&tpRequest->Linkage);
}
DeviceContext->IndicationQueuesInUse = TRUE;
//
// If this IRP has been cancelled, then call the
// cancel routine.
//
if (Irp->Cancel) {
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql);
Irp->CancelIrql = cancelirql;
NbfCancelAction((PDEVICE_OBJECT)DeviceContext, Irp);
return STATUS_PENDING;
}
IoSetCancelRoutine(Irp, NbfCancelAction);
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql);
IoReleaseCancelSpinLock(cancelirql);
}
status = STATUS_PENDING;
}
return status;
}
VOID
NbfCancelAction(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to cancel an Action.
What is done to cancel it is specific to each action.
NOTE: This routine is called with the CancelSpinLock held and
is responsible for releasing it.
Arguments:
DeviceObject - Pointer to the device object for this driver.
Irp - Pointer to the request packet representing the I/O request.
Return Value:
none.
--*/
{
KIRQL oldirql;
PIO_STACK_LOCATION IrpSp;
PTP_REQUEST Request;
PLIST_ENTRY p;
BOOLEAN Found;
PTDI_ACTION_HEADER ActionHeader;
PLIST_ENTRY QueueHead, QueueEnd;
PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject;
//
// Get a pointer to the current stack location in the IRP. This is where
// the function codes and parameters are stored.
//
IrpSp = IoGetCurrentIrpStackLocation (Irp);
ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
(IrpSp->MinorFunction == TDI_ACTION));
ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
if (!ActionHeader) {
return;
}
switch (ActionHeader->ActionCode) {
case QUERY_INDICATION_CODE:
case DATAGRAM_INDICATION_CODE:
//
// Scan through the appropriate queue, looking for this IRP.
// If we find it, we just remove it from the queue; there
// is nothing else involved in cancelling.
//
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) {
QueueHead = DeviceContext->QueryIndicationQueue.Flink;
QueueEnd = &DeviceContext->QueryIndicationQueue;
} else {
QueueHead = DeviceContext->DatagramIndicationQueue.Flink;
QueueEnd = &DeviceContext->DatagramIndicationQueue;
}
Found = FALSE;
for (p = QueueHead; p != QueueEnd; p = p->Flink) {
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
if (Request->IoRequestPacket == Irp) {
//
// Found it, remove it from the list here.
//
RemoveEntryList (p);
Found = TRUE;
break;
}
}
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
IoReleaseCancelSpinLock (Irp->CancelIrql);
if (Found) {
NbfCompleteRequest (Request, STATUS_CANCELLED, 0);
} else {
#if DBG
DbgPrint("NBF: Tried to cancel action %lx on %lx, not found\n",
Irp, DeviceContext);
#endif
}
break;
default:
IoReleaseCancelSpinLock (Irp->CancelIrql);
break;
}
}
VOID
NbfStopControlChannel(
IN PDEVICE_CONTEXT DeviceContext,
IN USHORT ChannelIdentifier
)
/*++
Routine Description:
This routine is called when an MJ_CLEANUP IRP is received
on a control channel. It walks the device context's list of
pending action requests and cancels those associated with
this channel (as identified by ChannelIdentifier.
Arguments:
DeviceContext - Pointer to our device context.
ChannelIdentifier - The identifier for this open of the control
channel, which is stored in Request->FrameContext for requests
made on this channel.
Return Value:
None
--*/
{
KIRQL oldirql, cancelirql;
PTP_REQUEST Request;
PLIST_ENTRY p;
UINT i;
BOOLEAN FoundRequest;
PLIST_ENTRY QueueHead, QueueEnd;
//
// Scan both queues, looking for requests. Since the list
// may change, we scan until we find one, then remove it
// and complete it. We then start scanning at the beginning
// again. We continue until we find none on the queue that
// belong to this control channel.
//
// The outer loop only runs twice; the first time it
// processes QueryIndicationQueue, the second time
// DatagramIndicationQueue.
//
for (i = 0; i < 2; i++) {
do {
//
// Loop until we do not find a request on this
// pass through the queue.
//
FoundRequest = FALSE;
IoAcquireCancelSpinLock(&cancelirql);
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
if (i == 0) {
QueueHead = DeviceContext->QueryIndicationQueue.Flink;
QueueEnd = &DeviceContext->QueryIndicationQueue;
} else {
QueueHead = DeviceContext->DatagramIndicationQueue.Flink;
QueueEnd = &DeviceContext->DatagramIndicationQueue;
}
//
// Scan the appropriate queue for a request on this
// channel.
//
for (p = QueueHead; p != QueueEnd; p = p->Flink) {
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
if (Request->FrameContext == ChannelIdentifier) {
//
// Found it, remove it from the list here.
//
IoSetCancelRoutine(Request->IoRequestPacket, NULL);
RemoveEntryList (p);
FoundRequest = TRUE;
break;
}
}
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
IoReleaseCancelSpinLock(cancelirql);
//
// If we found a request, then complete it and loop
// back to the top of the while loop to rescan the
// list. If not, then we will exit the while loop
// now.
//
if (FoundRequest) {
NbfCompleteRequest (Request, STATUS_CANCELLED, 0);
}
} while (FoundRequest);
}
}
VOID
NbfActionQueryIndication(
IN PDEVICE_CONTEXT DeviceContext,
IN PNBF_HDR_CONNECTIONLESS UiFrame
)
/*++
Routine Description:
This routine is called after a UI frame of type NAME_QUERY,
ADD_NAME_QUERY, or ADD_GROUP_NAME_QUERY has been processed.
It checks if there is a QUERY.INDICATION IRP waiting to
be completed, and if so completes it.
Arguments:
DeviceContext - Pointer to our device context.
UiFrame - Pointer to the incoming frame. The first byte of
information is the first byte of the NetBIOS connectionless
header.
Return Value:
None
--*/
{
KIRQL oldirql, cancelirql;
PTP_REQUEST Request;
PLIST_ENTRY p;
PMDL Mdl;
PACTION_QUERY_INDICATION ActionHeader;
PQUERY_INDICATION QueryIndication;
IoAcquireCancelSpinLock (&cancelirql);
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
if (!IsListEmpty (&DeviceContext->QueryIndicationQueue)) {
p = RemoveHeadList (&DeviceContext->QueryIndicationQueue);
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
IoSetCancelRoutine(Request->IoRequestPacket,NULL);
IoReleaseCancelSpinLock(cancelirql);
Mdl = Request->Buffer2;
ActionHeader = (PACTION_QUERY_INDICATION)
(MmGetSystemAddressForMdl(Mdl));
QueryIndication = &ActionHeader->QueryIndication;
//
// Copy over data from frame (note that dest and source
// address are copied with one call).
//
QueryIndication->Command = UiFrame->Command;
RtlCopyMemory ((PUCHAR)(&QueryIndication->Data2), (PUCHAR)(&UiFrame->Data2Low), 2);
RtlCopyMemory ((PUCHAR)(QueryIndication->DestinationName),
(PUCHAR)(UiFrame->DestinationName),
2 * NETBIOS_NAME_LENGTH);
NbfCompleteRequest (Request, STATUS_SUCCESS, sizeof(ACTION_QUERY_INDICATION));
} else {
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
IoReleaseCancelSpinLock(cancelirql);
}
}
VOID
NbfActionDatagramIndication(
IN PDEVICE_CONTEXT DeviceContext,
IN PNBF_HDR_CONNECTIONLESS UiFrame,
IN ULONG Length
)
/*++
Routine Description:
This routine is called after a datagram frame has been
received. It checks if there is a DATAGRAM.INDICATION IRP
waiting to be completed, and if so completes it.
Arguments:
DeviceContext - Pointer to our device context.
UiFrame - Pointer to the incoming frame. The first byte of
information is the first byte of the NetBIOS connectionless
header.
Length - The length of the frame starting at UiFrame.
Return Value:
None
--*/
{
KIRQL oldirql, cancelirql;
PTP_REQUEST Request;
PLIST_ENTRY p;
PACTION_DATAGRAM_INDICATION ActionHeader;
PDATAGRAM_INDICATION DatagramIndication;
ULONG CopyLength;
PMDL Mdl;
NTSTATUS Status;
IoAcquireCancelSpinLock (&cancelirql);
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
if (!IsListEmpty (&DeviceContext->DatagramIndicationQueue)) {
p = RemoveHeadList (&DeviceContext->DatagramIndicationQueue);
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
IoSetCancelRoutine(Request->IoRequestPacket, NULL);
IoReleaseCancelSpinLock(cancelirql);
Mdl = Request->Buffer2;
ActionHeader = (PACTION_DATAGRAM_INDICATION)
(MmGetSystemAddressForMdl(Mdl));
DatagramIndication = &ActionHeader->DatagramIndication;
//
// Copy over data from frame (note that dest and source
// address are copied with one call).
//
RtlCopyMemory ((PUCHAR)(DatagramIndication->DestinationName),
(PUCHAR)(UiFrame->DestinationName),
2 * NETBIOS_NAME_LENGTH);
if ((Length-sizeof(NBF_HDR_CONNECTIONLESS)) <=
(ULONG)DatagramIndication->DatagramBufferLength) {
CopyLength = Length - sizeof(NBF_HDR_CONNECTIONLESS);
Status = STATUS_SUCCESS;
} else {
CopyLength = DatagramIndication->DatagramBufferLength;
Status = STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(
(PUCHAR)DatagramIndication->DatagramBuffer,
((PUCHAR)UiFrame) + sizeof(NBF_HDR_CONNECTIONLESS),
CopyLength);
DatagramIndication->DatagramBufferLength = (USHORT)CopyLength;
NbfCompleteRequest (Request, Status, CopyLength +
FIELD_OFFSET (ACTION_DATAGRAM_INDICATION, DatagramIndication.DatagramBuffer[0]));
} else {
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
IoReleaseCancelSpinLock(cancelirql);
}
}