1113 lines
29 KiB
C
1113 lines
29 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
receive.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains code which processes all read NCB's including
|
|||
|
both session and datagram based transfers.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Colin Watson (ColinW) 13-Mar-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "nb.h"
|
|||
|
|
|||
|
PDNCB
|
|||
|
FindReceive (
|
|||
|
IN PCB pcb
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ReturnDatagram(
|
|||
|
IN PAB pab,
|
|||
|
IN PVOID SourceAddress,
|
|||
|
IN PDNCB pdncb,
|
|||
|
IN BOOL MultipleReceive
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbCompletionBroadcast(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbReceive(
|
|||
|
IN PDNCB pdncb,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PIO_STACK_LOCATION IrpSp,
|
|||
|
IN ULONG Buffer2Length,
|
|||
|
IN BOOLEAN Locked,
|
|||
|
IN KIRQL LockedIrql
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to read a buffer of data.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pdncb - Pointer to the NCB.
|
|||
|
|
|||
|
Irp - Pointer to the request packet representing the I/O request.
|
|||
|
|
|||
|
IrpSp - Pointer to current IRP stack frame.
|
|||
|
|
|||
|
Buffer2Length - Length of user provided buffer for data.
|
|||
|
|
|||
|
Locked - TRUE if the spinlock is already held.
|
|||
|
|
|||
|
LockedIrql - OldIrql if Locked == TRUE.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|||
|
PCB pcb;
|
|||
|
PPCB ppcb;
|
|||
|
NTSTATUS Status;
|
|||
|
KIRQL OldIrql; // Used when SpinLock held.
|
|||
|
|
|||
|
if ( Locked != TRUE ) {
|
|||
|
LOCK( pfcb, OldIrql );
|
|||
|
} else {
|
|||
|
OldIrql = LockedIrql;
|
|||
|
}
|
|||
|
|
|||
|
ppcb = FindCb( pfcb, pdncb, FALSE);
|
|||
|
|
|||
|
pdncb->irp = Irp;
|
|||
|
pdncb->pfcb = pfcb;
|
|||
|
|
|||
|
if ( ppcb == NULL ) {
|
|||
|
// FindCb has put the error in the NCB
|
|||
|
|
|||
|
UNLOCK( pfcb, OldIrql );
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive on invalid connection\n" ));
|
|||
|
}
|
|||
|
|
|||
|
if ( pdncb->ncb_retcode == NRC_SCLOSED ) {
|
|||
|
// Tell dll to hangup the connection.
|
|||
|
return STATUS_HANGUP_REQUIRED;
|
|||
|
} else {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
pcb = *ppcb;
|
|||
|
pdncb->tick_count = pcb->ReceiveTimeout;
|
|||
|
|
|||
|
if ( (pcb->DeviceObject == NULL) || (pcb->ConnectionObject == NULL)) {
|
|||
|
|
|||
|
UNLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
NCB_COMPLETE( pdncb, NRC_SCLOSED );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
if ( pcb->ReceiveIndicated == 0 ) {
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive, queue receive pcb: %lx, pdncb: %lx\n", pcb, pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
// Note: QueueRequest UNLOCKS the fcb.
|
|||
|
QueueRequest(&pcb->ReceiveList, pdncb, Irp, pfcb, OldIrql, FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
PDEVICE_OBJECT DeviceObject;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive, submit receive pcb: %lx, pdncb: %lx\n", pcb, pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
pcb->ReceiveIndicated = 0;
|
|||
|
|
|||
|
TdiBuildReceive (Irp,
|
|||
|
pcb->DeviceObject,
|
|||
|
pcb->ConnectionObject,
|
|||
|
NbCompletionPDNCB,
|
|||
|
pdncb,
|
|||
|
Irp->MdlAddress,
|
|||
|
0,
|
|||
|
Buffer2Length);
|
|||
|
|
|||
|
// Save the DeviceObject before pcb gets released by UNLOCK
|
|||
|
|
|||
|
DeviceObject = pcb->DeviceObject;
|
|||
|
|
|||
|
UNLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
IoCallDriver (DeviceObject, Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Transport will complete the request. Return pending so that
|
|||
|
// netbios does not complete as well.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_PENDING;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receive: %X, %X\n", Status, Irp->IoStatus.Status ));
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
UNREFERENCED_PARAMETER( Buffer2Length );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbReceiveAny(
|
|||
|
IN PDNCB pdncb,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PIO_STACK_LOCATION IrpSp,
|
|||
|
IN ULONG Buffer2Length
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to read a buffer of data from any session on
|
|||
|
a particular address, provided there is not a read on that address
|
|||
|
already.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pdncb - Pointer to the NCB.
|
|||
|
|
|||
|
Irp - Pointer to the request packet representing the I/O request.
|
|||
|
|
|||
|
IrpSp - Pointer to current IRP stack frame.
|
|||
|
|
|||
|
Buffer2Length - Length of user provided buffer for data.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|||
|
PPCB ppcb;
|
|||
|
PPAB ppab;
|
|||
|
KIRQL OldIrql; // Used when SpinLock held.
|
|||
|
|
|||
|
LOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
ppab = FindAbUsingNum( pfcb, pdncb, pdncb->ncb_num );
|
|||
|
|
|||
|
if ( ppab == NULL ) {
|
|||
|
UNLOCK( pfcb, OldIrql );
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive any on invalid connection\n" ));
|
|||
|
}
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
pdncb->irp = Irp;
|
|||
|
pdncb->pfcb = pfcb;
|
|||
|
|
|||
|
//
|
|||
|
// If there is already a receive any on the address block then add
|
|||
|
// this request to the tail of the queue. If the list is empty then
|
|||
|
// look for a connection on this address flagged as having a receive
|
|||
|
// indicated. Either queue the request if there are no indications or
|
|||
|
// satisfy the indicated receive any with this request.
|
|||
|
//
|
|||
|
|
|||
|
if ( !IsListEmpty( &(*ppab)->ReceiveAnyList )) {
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive any with receive any list non empty\n" ));
|
|||
|
ppcb = FindReceiveIndicated( pfcb, pdncb, ppab );
|
|||
|
if ( ppcb != NULL ) {
|
|||
|
NbPrint(( " ppcb: %lx has a receive indicated( %lx )!\n",
|
|||
|
ppcb,
|
|||
|
(*ppcb)->ReceiveIndicated));
|
|||
|
ASSERT( FALSE );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Note: QueueRequest UNLOCKS the fcb.
|
|||
|
QueueRequest(&(*ppab)->ReceiveAnyList, pdncb, Irp, pfcb, OldIrql, FALSE);
|
|||
|
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find either a connection with a receive indicated or one that has been
|
|||
|
// disconnected but not reported yet.
|
|||
|
//
|
|||
|
|
|||
|
ppcb = FindReceiveIndicated( pfcb, pdncb, ppab );
|
|||
|
|
|||
|
if ( ppcb == NULL ) {
|
|||
|
// No connections with receive indications set.
|
|||
|
|
|||
|
// Note: QueueRequest UNLOCKS the fcb.
|
|||
|
QueueRequest(&(*ppab)->ReceiveAnyList, pdncb, Irp, pfcb, OldIrql, FALSE);
|
|||
|
|
|||
|
return STATUS_PENDING;
|
|||
|
} else {
|
|||
|
// FindReceiveIndicated has set the LSN appropriately in the NCB
|
|||
|
|
|||
|
// Note : NbReceive will unlock the spinlock & resource
|
|||
|
return NbReceive( pdncb, Irp, IrpSp, Buffer2Length, TRUE, OldIrql );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbTdiReceiveHandler (
|
|||
|
IN PVOID ReceiveEventContext,
|
|||
|
IN PVOID ConnectionContext,
|
|||
|
IN USHORT ReceiveFlags,
|
|||
|
IN ULONG BytesIndicated,
|
|||
|
IN ULONG BytesAvailable,
|
|||
|
OUT PULONG BytesTaken,
|
|||
|
IN PVOID Tsdu,
|
|||
|
OUT PIRP *IoRequestPacket
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the receive event indication handler.
|
|||
|
|
|||
|
It is called when an NCB arrives from the network, it will look for a
|
|||
|
connection for this address with an appropriate read outstanding.
|
|||
|
The connection that has the read associated with it is indicated by the
|
|||
|
context parameter.
|
|||
|
|
|||
|
If it finds an appropriate read it processes the NCB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PVOID ReceiveEventContext - Context provided for this event - pab
|
|||
|
IN PVOID ConnectionContext - Connection Context - pcb
|
|||
|
IN USHORT ReceiveFlags - Flags describing the message
|
|||
|
IN ULONG BytesIndicated - Number of bytes available at indication time
|
|||
|
IN ULONG BytesAvailable - Number of bytes available to receive
|
|||
|
OUT PULONG BytesTaken - Number of bytes consumed by redirector.
|
|||
|
IN PVOID Tsdu - Data from remote machine.
|
|||
|
OUT PIRP *IoRequestPacket - I/O request packet filled in if received data
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of receive operation
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL OldIrql; // Used when SpinLock held.
|
|||
|
PCB pcb = *(PPCB)ConnectionContext;
|
|||
|
PAB pab = *(pcb->ppab);
|
|||
|
PFCB pfcb = pab->pLana->pFcb;
|
|||
|
PDNCB pdncb;
|
|||
|
|
|||
|
*IoRequestPacket = NULL;
|
|||
|
|
|||
|
LOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
if (( pcb == NULL ) ||
|
|||
|
( pcb->Status != SESSION_ESTABLISHED )) {
|
|||
|
|
|||
|
//
|
|||
|
// The receive indication came in after we had an
|
|||
|
// allocation error on the Irp to be used for orderly disconnect.
|
|||
|
// If the Irp allocation fails then we should ignore receives
|
|||
|
// since we are in the process of putting down a ZwClose on this
|
|||
|
// connection.
|
|||
|
//
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
return STATUS_DATA_NOT_ACCEPTED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
pdncb = FindReceive( pcb );
|
|||
|
|
|||
|
if ( pdncb == NULL ) {
|
|||
|
|
|||
|
pcb->ReceiveIndicated = 1;
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
return STATUS_DATA_NOT_ACCEPTED;
|
|||
|
}
|
|||
|
|
|||
|
pcb->ReceiveIndicated = 0;
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// If this is the simple case where all the data required has been
|
|||
|
// indicated and it all fits in the buffer then copy the packet
|
|||
|
// contents directly into the users buffer rather than returning the
|
|||
|
// Irp. This should always be faster than returning an Irp to the
|
|||
|
// transport.
|
|||
|
//
|
|||
|
|
|||
|
if (( BytesAvailable <= pdncb->ncb_length ) &&
|
|||
|
( BytesAvailable == BytesIndicated ) &&
|
|||
|
( ReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE )) {
|
|||
|
|
|||
|
PIRP Irp = pdncb->irp;
|
|||
|
|
|||
|
if ( BytesAvailable != 0 ) {
|
|||
|
|
|||
|
PUCHAR UsersBuffer = MmGetSystemAddressForMdlSafe(
|
|||
|
Irp->MdlAddress, NormalPagePriority);
|
|||
|
|
|||
|
if (UsersBuffer == NULL) {
|
|||
|
pcb->ReceiveIndicated = 1;
|
|||
|
|
|||
|
return STATUS_DATA_NOT_ACCEPTED;
|
|||
|
}
|
|||
|
|
|||
|
TdiCopyLookaheadData(
|
|||
|
UsersBuffer,
|
|||
|
Tsdu,
|
|||
|
BytesAvailable,
|
|||
|
ReceiveFlags);
|
|||
|
}
|
|||
|
|
|||
|
*BytesTaken = BytesAvailable;
|
|||
|
|
|||
|
pdncb->ncb_length = (WORD)BytesAvailable;
|
|||
|
|
|||
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|||
|
|
|||
|
Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|||
|
|
|||
|
NbCompleteRequest(Irp,STATUS_SUCCESS);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
TdiBuildReceive (pdncb->irp,
|
|||
|
pcb->DeviceObject,
|
|||
|
pcb->ConnectionObject,
|
|||
|
NbCompletionPDNCB,
|
|||
|
pdncb,
|
|||
|
pdncb->irp->MdlAddress,
|
|||
|
0,
|
|||
|
pdncb->ncb_length);
|
|||
|
|
|||
|
IoSetNextIrpStackLocation( pdncb->irp );
|
|||
|
|
|||
|
*IoRequestPacket = pdncb->irp;
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( ReceiveEventContext );
|
|||
|
UNREFERENCED_PARAMETER( Tsdu );
|
|||
|
}
|
|||
|
|
|||
|
PIRP
|
|||
|
BuildReceiveIrp (
|
|||
|
IN PCB pcb
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the receive event indication handler.
|
|||
|
|
|||
|
It is called when an NCB arrives from the network and also when
|
|||
|
a receive completes with STATUS_BUFFER_OVERFLOW.
|
|||
|
|
|||
|
If no Irp is available then this routine sets ReceiveIndicated so
|
|||
|
that the next appropriate receive will be passed to the transport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PCB pcb - Supplies the connection which should put a receive Irp
|
|||
|
down if it has one available.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PDNCB to be satisfied by this receive request
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDNCB pdncb = FindReceive( pcb );
|
|||
|
|
|||
|
if ( pdncb == NULL ) {
|
|||
|
|
|||
|
pcb->ReceiveIndicated = 1;
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
TdiBuildReceive (pdncb->irp,
|
|||
|
pcb->DeviceObject,
|
|||
|
pcb->ConnectionObject,
|
|||
|
NbCompletionPDNCB,
|
|||
|
pdncb,
|
|||
|
pdncb->irp->MdlAddress,
|
|||
|
0,
|
|||
|
pdncb->ncb_length);
|
|||
|
|
|||
|
pcb->ReceiveIndicated = 0;
|
|||
|
|
|||
|
return pdncb->irp;
|
|||
|
}
|
|||
|
|
|||
|
PDNCB
|
|||
|
FindReceive (
|
|||
|
IN PCB pcb
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
It is called when an NCB arrives from the network and also when
|
|||
|
a receive completes with STATUS_BUFFER_OVERFLOW.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PCB pcb - Supplies the connection which should put a receive Irp
|
|||
|
down if it has one available.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PDNCB to be satisfied by this receive request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAB pab;
|
|||
|
PFCB pfcb;
|
|||
|
PDNCB pdncb;
|
|||
|
|
|||
|
pab = *(pcb->ppab);
|
|||
|
pfcb = pab->pLana->pFcb;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive handler pcb: %lx\n", pcb ));
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( pcb->Signature == CB_SIGNATURE );
|
|||
|
|
|||
|
//
|
|||
|
// If there is a receive in the list then hand over the data.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if ( (pdncb = DequeueRequest( &pcb->ReceiveList)) != NULL ) {
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receive handler pcb: %lx, ncb: %lx\n", pcb, pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
return pdncb;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// No receives on this connection. Is there a receive any for this
|
|||
|
// address?
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( pab != NULL );
|
|||
|
|
|||
|
if ( (pdncb = DequeueRequest( &pab->ReceiveAnyList)) != NULL ) {
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receiveANY handler pcb: %lx, ncb: %lx\n", pcb, pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
pdncb->ncb_num = pab->NameNumber;
|
|||
|
pdncb->ncb_lsn = pcb->SessionNumber;
|
|||
|
|
|||
|
return pdncb;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// No receives on this connection. Is there a receive any for any
|
|||
|
// address on this adapter?
|
|||
|
//
|
|||
|
|
|||
|
pab = pcb->Adapter->AddressBlocks[MAXIMUM_ADDRESS];
|
|||
|
|
|||
|
ASSERT( pab != NULL );
|
|||
|
|
|||
|
if ( (pdncb = DequeueRequest( &pab->ReceiveAnyList)) != NULL ) {
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receiveANYANY handler pcb: %lx, ncb: %lx\n", pcb, pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
pdncb->ncb_num = pab->NameNumber;
|
|||
|
pdncb->ncb_lsn = pcb->SessionNumber;
|
|||
|
|
|||
|
return pdncb;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Transport will complete the processing of the request, we don't
|
|||
|
// want the data yet.
|
|||
|
//
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receive handler ignored receive, pcb: %lx\n", pcb ));
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbReceiveDatagram(
|
|||
|
IN PDNCB pdncb,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PIO_STACK_LOCATION IrpSp,
|
|||
|
IN ULONG Buffer2Length
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to read a buffer of data.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pdncb - Pointer to the NCB.
|
|||
|
|
|||
|
Irp - Pointer to the request packet representing the I/O request.
|
|||
|
|
|||
|
IrpSp - Pointer to current IRP stack frame.
|
|||
|
|
|||
|
Buffer2Length - Length of user provided buffer for data.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|||
|
NTSTATUS Status;
|
|||
|
PPAB ppab;
|
|||
|
KIRQL OldIrql; // Used when SpinLock held.
|
|||
|
|
|||
|
LOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
pdncb->irp = Irp;
|
|||
|
pdncb->pfcb = pfcb;
|
|||
|
|
|||
|
ppab = FindAbUsingNum( pfcb, pdncb, pdncb->ncb_num );
|
|||
|
|
|||
|
if ( ppab == NULL ) {
|
|||
|
UNLOCK( pfcb, OldIrql );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
// Build the ReceiveInformation datastructure in the DNCB.
|
|||
|
|
|||
|
|
|||
|
if ( (pdncb->ncb_command & ~ASYNCH) == NCBDGRECVBC ) {
|
|||
|
//
|
|||
|
// Receive broadcast commands can be requested on any valid
|
|||
|
// name number but once accepted, they are treated seperately
|
|||
|
// from the name. To implement this, the driver queues the
|
|||
|
// receives on address 255.
|
|||
|
//
|
|||
|
|
|||
|
ppab = FindAbUsingNum( pfcb, pdncb, MAXIMUM_ADDRESS );
|
|||
|
|
|||
|
if ((ppab == NULL) || (pdncb->ncb_num == MAXIMUM_ADDRESS) ) {
|
|||
|
|
|||
|
NCB_COMPLETE( pdncb, NRC_ILLNN );
|
|||
|
UNLOCK( pfcb, OldIrql );
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB Bdatagram receive, queue ppab: %lx, pab: %lx, pdncb: %lx\n",
|
|||
|
ppab, (*ppab), pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
if ( (*ppab)->ReceiveDatagramRegistered == FALSE) {
|
|||
|
|
|||
|
(*ppab)->ReceiveDatagramRegistered = TRUE;
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql);
|
|||
|
|
|||
|
Status = NbSetEventHandler( (*ppab)->DeviceObject,
|
|||
|
(*ppab)->AddressObject,
|
|||
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|||
|
(PVOID)NbTdiDatagramHandler,
|
|||
|
(*ppab));
|
|||
|
|
|||
|
if (Status != STATUS_SUCCESS)
|
|||
|
{
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
LOCK_SPINLOCK( pfcb, OldIrql);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// When one receive broadcast is received, we must satisfy all the receive
|
|||
|
// broadcasts. To do this, the largest receive is placed at the head of the queue.
|
|||
|
// When a datagram is received, this receive is given to the transport to fill in
|
|||
|
// with data. In the completion routine this driver propogates the same data to
|
|||
|
// the other receive datagram requests.
|
|||
|
//
|
|||
|
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
if ( !IsListEmpty( &(*ppab)->ReceiveBroadcastDatagramList) ) {
|
|||
|
PDNCB pdncbHead = CONTAINING_RECORD( &(*ppab)->ReceiveBroadcastDatagramList.Flink , DNCB, ncb_next);
|
|||
|
if ( pdncb->ncb_length >= pdncbHead->ncb_length ) {
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB Bdatagram receive, Head of queue ppab: %lx, pab: %lx, pdncb: %lx\n",
|
|||
|
ppab, (*ppab), pdncb ));
|
|||
|
}
|
|||
|
// Note: QueueRequest UNLOCKS the fcb.
|
|||
|
QueueRequest(&(*ppab)->ReceiveBroadcastDatagramList, pdncb, Irp, pfcb, OldIrql, TRUE);
|
|||
|
} else {
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB Bdatagram receive, Tail of queue ppab: %lx, pab: %lx, pdncb: %lx\n",
|
|||
|
ppab, (*ppab), pdncb ));
|
|||
|
}
|
|||
|
QueueRequest(&(*ppab)->ReceiveBroadcastDatagramList, pdncb, Irp, pfcb, OldIrql, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB Bdatagram receive, Tail2 of queue ppab: %lx, pab: %lx, pdncb: %lx\n",
|
|||
|
ppab, (*ppab), pdncb ));
|
|||
|
}
|
|||
|
QueueRequest(&(*ppab)->ReceiveBroadcastDatagramList, pdncb, Irp, pfcb, OldIrql, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB datagram receive, queue ppab: %lx, pab: %lx, pdncb: %lx\n",
|
|||
|
ppab, (*ppab), pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
QueueRequest(&(*ppab)->ReceiveDatagramList, pdncb, Irp, pfcb, OldIrql, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_PENDING;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB datagram receive: %X, %X\n", Status, Irp->IoStatus.Status ));
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
UNREFERENCED_PARAMETER( Buffer2Length );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbTdiDatagramHandler(
|
|||
|
IN PVOID TdiEventContext, // the event context - pab
|
|||
|
IN int SourceAddressLength, // length of the originator of the datagram
|
|||
|
IN PVOID SourceAddress, // string describing the originator of the datagram
|
|||
|
IN int OptionsLength, // options for the receive
|
|||
|
IN PVOID Options, //
|
|||
|
IN ULONG ReceiveDatagramFlags, //
|
|||
|
IN ULONG BytesIndicated, // number of bytes this indication
|
|||
|
IN ULONG BytesAvailable, // number of bytes in complete Tsdu
|
|||
|
OUT ULONG *BytesTaken, // number of bytes used
|
|||
|
IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
|
|||
|
OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the receive datagram event indication handler.
|
|||
|
|
|||
|
It is called when an NCB arrives from the network, it will look for a
|
|||
|
the address with an appropriate read datagram outstanding.
|
|||
|
The address that has the read associated with it is indicated by the
|
|||
|
context parameter.
|
|||
|
|
|||
|
If it finds an appropriate read it processes the NCB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PVOID TdiEventContext - Context provided for this event - pab
|
|||
|
IN int SourceAddressLength, // length of the originator of the datagram
|
|||
|
IN PVOID SourceAddress, // string describing the originator of the datagram
|
|||
|
IN int OptionsLength, // options for the receive
|
|||
|
IN PVOID Options, //
|
|||
|
IN ULONG BytesIndicated, // number of bytes this indication
|
|||
|
IN ULONG BytesAvailable, // number of bytes in complete Tsdu
|
|||
|
OUT ULONG *BytesTaken, // number of bytes used
|
|||
|
IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
|
|||
|
OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of receive operation
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAB pab = (PAB)TdiEventContext;
|
|||
|
PAB pab255;
|
|||
|
|
|||
|
PDNCB pdncb;
|
|||
|
PFCB pfcb;
|
|||
|
KIRQL OldIrql; // Used when SpinLock held.
|
|||
|
|
|||
|
pfcb = pab->pLana->pFcb;
|
|||
|
LOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "NB receive datagram handler pfcb: %lx, pab: %lx\n", pfcb, pab ));
|
|||
|
}
|
|||
|
|
|||
|
*IoRequestPacket = NULL;
|
|||
|
|
|||
|
ASSERT( pab->Signature == AB_SIGNATURE );
|
|||
|
|
|||
|
// If its address 255 then we are receiving a broadcast datagram.
|
|||
|
|
|||
|
if ( pab->NameNumber == MAXIMUM_ADDRESS ) {
|
|||
|
|
|||
|
if ( (pdncb = DequeueRequest( &pab->ReceiveBroadcastDatagramList)) != NULL ) {
|
|||
|
|
|||
|
ReturnDatagram(
|
|||
|
pab,
|
|||
|
SourceAddress,
|
|||
|
pdncb,
|
|||
|
!IsListEmpty( &pab->ReceiveBroadcastDatagramList));
|
|||
|
|
|||
|
*IoRequestPacket = pdncb->irp;
|
|||
|
|
|||
|
IoSetNextIrpStackLocation( pdncb->irp );
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Transport will complete the processing of the request, we don't
|
|||
|
// want the datagram.
|
|||
|
//
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receive BD handler ignored receive, pab: %lx\n", pab ));
|
|||
|
}
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
return STATUS_DATA_NOT_ACCEPTED;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check the address block looking for a Receive Datagram.
|
|||
|
//
|
|||
|
|
|||
|
if ( (pdncb = DequeueRequest( &pab->ReceiveDatagramList)) != NULL ) {
|
|||
|
|
|||
|
ReturnDatagram(
|
|||
|
pab,
|
|||
|
SourceAddress,
|
|||
|
pdncb,
|
|||
|
FALSE);
|
|||
|
|
|||
|
*IoRequestPacket = pdncb->irp;
|
|||
|
|
|||
|
IoSetNextIrpStackLocation( pdncb->irp );
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if there is a receive any datagram.
|
|||
|
//
|
|||
|
|
|||
|
// look at the list on address 255.
|
|||
|
|
|||
|
pab255 = pab->pLana->AddressBlocks[MAXIMUM_ADDRESS];
|
|||
|
|
|||
|
if ( (pdncb = DequeueRequest( &pab255->ReceiveDatagramList)) != NULL ) {
|
|||
|
|
|||
|
ReturnDatagram(
|
|||
|
pab255,
|
|||
|
SourceAddress,
|
|||
|
pdncb,
|
|||
|
FALSE);
|
|||
|
|
|||
|
pdncb->ncb_num = pab->NameNumber;
|
|||
|
|
|||
|
*IoRequestPacket = pdncb->irp;
|
|||
|
|
|||
|
IoSetNextIrpStackLocation( pdncb->irp );
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Transport will complete the processing of the request, we don't
|
|||
|
// want the datagram.
|
|||
|
//
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB receive datagram handler ignored receive, pab: %lx\n", pab ));
|
|||
|
}
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|||
|
|
|||
|
return STATUS_DATA_NOT_ACCEPTED;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( SourceAddressLength );
|
|||
|
UNREFERENCED_PARAMETER( BytesIndicated );
|
|||
|
UNREFERENCED_PARAMETER( BytesAvailable );
|
|||
|
UNREFERENCED_PARAMETER( BytesTaken );
|
|||
|
UNREFERENCED_PARAMETER( Tsdu );
|
|||
|
UNREFERENCED_PARAMETER( OptionsLength );
|
|||
|
UNREFERENCED_PARAMETER( Options );
|
|||
|
UNREFERENCED_PARAMETER( ReceiveDatagramFlags );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ReturnDatagram(
|
|||
|
IN PAB pab,
|
|||
|
IN PVOID SourceAddress,
|
|||
|
IN PDNCB pdncb,
|
|||
|
IN BOOL MultipleReceive
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to provide the Irp for a receive datagram to
|
|||
|
the transport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PAB pab - Supplies the address block associated with the NCB.
|
|||
|
IN PVOID SourceAddress - Supplies the sender of the datagram.
|
|||
|
IN PDNCB pdncb - Supplies the NCB to be satisfied.
|
|||
|
IN BOOL MultipleReceive - True if the special Receive Broadcast datagram completion
|
|||
|
handler is to be used.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP Irp = pdncb->irp;
|
|||
|
PIO_COMPLETION_ROUTINE CompletionRoutine;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_RECEIVE) {
|
|||
|
NbPrint(( "\n NB BDatagramreceive handler pab: %lx, ncb: %lx\n",
|
|||
|
pab, pdncb ));
|
|||
|
}
|
|||
|
|
|||
|
// Copy the name into the NCB for return to the application.
|
|||
|
RtlMoveMemory(
|
|||
|
pdncb->ncb_callname,
|
|||
|
((PTA_NETBIOS_ADDRESS)SourceAddress)->Address[0].Address[0].NetbiosName,
|
|||
|
NCBNAMSZ
|
|||
|
);
|
|||
|
|
|||
|
// Tell TDI we do not want to specify any filters.
|
|||
|
pdncb->Information.RemoteAddress = 0;
|
|||
|
pdncb->Information.RemoteAddressLength = 0;
|
|||
|
pdncb->Information.UserData = NULL;
|
|||
|
pdncb->Information.UserDataLength = 0;
|
|||
|
pdncb->Information.Options = NULL;
|
|||
|
pdncb->Information.OptionsLength = 0;
|
|||
|
|
|||
|
// Tell TDI we do not want any more information on the remote name.
|
|||
|
pdncb->ReturnInformation.RemoteAddress = 0;
|
|||
|
pdncb->ReturnInformation.RemoteAddressLength = 0;
|
|||
|
pdncb->ReturnInformation.UserData = NULL;
|
|||
|
pdncb->ReturnInformation.UserDataLength = 0;
|
|||
|
pdncb->ReturnInformation.Options = NULL;
|
|||
|
pdncb->ReturnInformation.OptionsLength = 0;
|
|||
|
|
|||
|
CompletionRoutine = ( MultipleReceive == FALSE ) ? NbCompletionPDNCB: NbCompletionBroadcast;
|
|||
|
|
|||
|
ASSERT(Irp->MdlAddress != NULL);
|
|||
|
|
|||
|
TdiBuildReceiveDatagram (Irp,
|
|||
|
pab->DeviceObject,
|
|||
|
pab->AddressObject,
|
|||
|
CompletionRoutine,
|
|||
|
pdncb,
|
|||
|
Irp->MdlAddress,
|
|||
|
pdncb->ncb_length,
|
|||
|
&pdncb->Information,
|
|||
|
&pdncb->ReturnInformation,
|
|||
|
0);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NbCompletionBroadcast(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine takes the completed datagram receive and copies the data in the buffer
|
|||
|
to all the other receive broadcast datagram requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - unused.
|
|||
|
|
|||
|
Irp - Supplies Irp that the transport has finished processing.
|
|||
|
|
|||
|
Context - Supplies the NCB associated with the Irp.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The final status from the operation (success or an exception).
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDNCB pdncb = (PDNCB) Context;
|
|||
|
KIRQL OldIrql; // Used when SpinLock held.
|
|||
|
PUCHAR pData;
|
|||
|
UCHAR NcbStatus;
|
|||
|
PAB pab;
|
|||
|
PDNCB pdncbNext;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_COMPLETE) {
|
|||
|
NbPrint( ("NbCompletionBroadcast pdncb: %lx, Status: %X, Length %lx\n",
|
|||
|
Context,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information ));
|
|||
|
}
|
|||
|
|
|||
|
// Tell application how many bytes were transferred
|
|||
|
pdncb->ncb_length = (unsigned short)Irp->IoStatus.Information;
|
|||
|
|
|||
|
if ( NT_SUCCESS(Irp->IoStatus.Status) ) {
|
|||
|
NcbStatus = NRC_GOODRET;
|
|||
|
} else {
|
|||
|
NcbStatus = NbMakeNbError( Irp->IoStatus.Status );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Tell IopCompleteRequest how much to copy back when the request
|
|||
|
// completes.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|||
|
|
|||
|
pData = MmGetSystemAddressForMdlSafe (Irp->MdlAddress, NormalPagePriority);
|
|||
|
|
|||
|
if (pData != NULL) {
|
|||
|
|
|||
|
LOCK_SPINLOCK( pdncb->pfcb, OldIrql );
|
|||
|
|
|||
|
pab = *(FindAbUsingNum( pdncb->pfcb, pdncb, MAXIMUM_ADDRESS ));
|
|||
|
|
|||
|
//
|
|||
|
// For each request on the queue, copy the data, update the NCb and complete the IRP.
|
|||
|
//
|
|||
|
|
|||
|
while ( (pdncbNext = DequeueRequest( &pab->ReceiveBroadcastDatagramList)) != NULL ) {
|
|||
|
PUCHAR pNextData;
|
|||
|
WORD Length;
|
|||
|
|
|||
|
IF_NBDBG (NB_DEBUG_COMPLETE) {
|
|||
|
NbPrint( ("NbCompletionBroadcast pdncb: %lx, Length %lx\n",
|
|||
|
pdncbNext,
|
|||
|
Irp->IoStatus.Information ));
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(pdncbNext->irp->MdlAddress != NULL);
|
|||
|
|
|||
|
if (pdncbNext->irp->MdlAddress != NULL ) {
|
|||
|
pNextData = MmGetSystemAddressForMdlSafe(
|
|||
|
pdncbNext->irp->MdlAddress, NormalPagePriority
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if ((pdncbNext->irp->MdlAddress == NULL) ||
|
|||
|
(pNextData == NULL)) {
|
|||
|
Length = 0;
|
|||
|
}
|
|||
|
|
|||
|
else {
|
|||
|
Length = min( pdncb->ncb_length, pdncbNext->ncb_length);
|
|||
|
pdncbNext->ncb_length = Length;
|
|||
|
RtlMoveMemory( pNextData, pData, Length );
|
|||
|
}
|
|||
|
|
|||
|
if (( Length != pdncb->ncb_length ) &&
|
|||
|
( NcbStatus == NRC_GOODRET )) {
|
|||
|
if (Length == 0) {
|
|||
|
NCB_COMPLETE( pdncbNext, NRC_NORES );
|
|||
|
}
|
|||
|
else {
|
|||
|
NCB_COMPLETE( pdncbNext, NRC_INCOMP );
|
|||
|
}
|
|||
|
} else {
|
|||
|
NCB_COMPLETE( pdncbNext, NcbStatus );
|
|||
|
}
|
|||
|
pdncbNext->irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|||
|
NbCompleteRequest(pdncbNext->irp, STATUS_SUCCESS );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
UNLOCK_SPINLOCK( pdncb->pfcb, OldIrql );
|
|||
|
}
|
|||
|
|
|||
|
NCB_COMPLETE( pdncb, NcbStatus );
|
|||
|
|
|||
|
//
|
|||
|
// Must return a non-error status otherwise the IO system will not copy
|
|||
|
// back the NCB into the users buffer.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|||
|
}
|