windows-nt/Source/XPSP1/NT/net/netbios/sys/receive.c

1113 lines
29 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 );
}