diff options
Diffstat (limited to 'private/ntos/nbt/nt/tdihndlr.c')
-rw-r--r-- | private/ntos/nbt/nt/tdihndlr.c | 6286 |
1 files changed, 6286 insertions, 0 deletions
diff --git a/private/ntos/nbt/nt/tdihndlr.c b/private/ntos/nbt/nt/tdihndlr.c new file mode 100644 index 000000000..bcd515d97 --- /dev/null +++ b/private/ntos/nbt/nt/tdihndlr.c @@ -0,0 +1,6286 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdihndlr.c + +Abstract: + + + This file contains the TDI handlers that are setup for Connects, + Receives, Disconnects, and Errors on various objects such as connections + and udp endpoints . + + This file represents the inbound TDI interface on the Bottom of NBT. Therefore + the code basically decodes the incoming information and passes it to + a non-Os specific routine to do what it can. Upon return from that + routine additional Os specific work may need to be done. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "ctemacro.h" + +// this macro checks that the types field is always zero in the Session +// Pdu +// +#if DBG +#define CHECK_PDU( _Size,_Offset) \ + if (_Size > 1) \ + ASSERT(((PUCHAR)pTsdu)[_Offset] == 0) +#else +#define CHECK_PDU( _Size,_Offset ) +#endif + +#if DBG +UCHAR pLocBuff[256]; +UCHAR CurrLoc; + +ULONG R1; +ULONG R2; +ULONG R3; +ULONG R4; + +ULONG C1; +ULONG C2; +ULONG C3; +ULONG C4; + + +#define INCR_COUNT(_Count) _Count++ +#else +#define INCR_COUNT(_Count) +#endif + + +// +// This ntohl swaps just three bytes, since the 4th byte could be a session +// keep alive message type. +// +__inline long +myntohl(long x) +{ + return((((x) >> 24) & 0x000000FFL) | + (((x) >> 8) & 0x0000FF00L) | + (((x) << 8) & 0x00FF0000L)); +} + +NTSTATUS +LessThan4BytesRcvd( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + OUT PVOID *ppIrp + ); +NTSTATUS +ClientTookSomeOfTheData( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + IN ULONG BytesTaken, + IN ULONG PduSize + ); +NTSTATUS +MoreDataRcvdThanNeeded( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +NotEnoughDataYet( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN ULONG PduSize, + OUT PVOID *ppIrp + ); +NTSTATUS +ProcessIrp( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrp, + IN PVOID pBuffer, + IN PULONG BytesTaken, + IN ULONG BytesIndicted, + IN ULONG BytesAvailable + ); + +NTSTATUS +NtBuildIndicateForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ); + +NTSTATUS +AcceptCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +VOID +DpcNextOutOfRsrcKill( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +DpcGetRestOfIndication( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +ClientBufferOverFlow( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG BytesRcvd + ); +VOID +DpcHandleNewSessionPdu ( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); +VOID +HandleNewSessionPdu ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Offset, + IN ULONG ToGet + ); +NTSTATUS +NewSessionCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +BuildIrpForNewSessionInIndication ( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrpIn, + IN ULONG BytesAvailable, + IN ULONG RemainingPdu, + OUT PIRP *ppIrp + ); +VOID +TrackIndicatedBytes( + IN ULONG BytesIndicated, + IN ULONG BytesTaken, + IN tCONNECTELE *pConnEle + ); + +__inline +VOID +DerefLowerConnFast ( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN CTELockHandle OldIrq + ); + +NTSTATUS +CopyDataandIndicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ); + +VOID +SumMdlLengths ( + IN PMDL pMdl, + IN ULONG BytesAvailable, + IN tCONNECTELE *pConnectEle + ); + + + +NTSTATUS +RsrcKillCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +VOID +FillIrpCancelRoutine( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NTSTATUS +NameSrvCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); +//---------------------------------------------------------------------------- +__inline +NTSTATUS +Normal( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + ASSERTMSG("Should not execute this procedure",0); + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +LessThan4BytesRcvd( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + NTSTATUS status; + + // for short indications less than 4 bytes we can't determine + // the pdu size so just get the header first then get the + // whole pdu next. + + status = NtBuildIrpForReceive(pLowerConn, + sizeof(tSESSIONHDR), + (PVOID *)ppIrp); + + pConnectEle = pLowerConn->pUpperConnection; + + pConnectEle->BytesInXport = BytesAvailable; + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return( STATUS_DATA_NOT_ACCEPTED); + } + // + // set the irp mdl length to size of session hdr so that + // we don't get more than one session pdu into the buffer + // + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + *BytesTaken = 0; + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to Ind Buff(<4 bytes), Avail = %X\n", + BytesAvailable)); + + PUSH_LOCATION(0); + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +ClientTookSomeOfTheData( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + IN ULONG BytesTaken, + IN ULONG PduSize + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + the client has not taken all of the data indicated to it. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + + // + // took some of the data, so keep track of the + // rest of the data left here by going to the PARTIALRCV + // state. + // + PUSH_LOCATION(0x5); + + pLowerConn->StateRcv = PARTIAL_RCV; + pLowerConn->CurrentStateProc = PartialRcv; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switch to Partial Rcv Indicated=%X, PduSize=%X\n", + BytesIndicated,PduSize-4)); + + // Note: PduSize must include the 4 byte session header for this to + // work correctly. + // + pConnectEle = pLowerConn->pUpperConnection; + // + // We always indicate the whole Pdu size to the client, so the amount + // indicated is that minus what was taken - typically the 4 byte + // session hdr + // + pConnectEle->ReceiveIndicated = PduSize - BytesTaken; + ASSERT(pConnectEle->ReceiveIndicated <= 0x20000); + + // amount left in the transport... + pConnectEle->BytesInXport = BytesAvailable - BytesTaken; + + // need to return this status since we took the 4 bytes + // session header at least, even if the client took none. + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +MoreDataRcvdThanNeeded( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + ULONG Length; + ULONG Remaining; + ULONG PduSize; + NTSTATUS status; + tSESSIONHDR UNALIGNED *pSessionHdr; + + + PUSH_LOCATION(0x6); + // + // there is too much data, so keep track of the + // fact that there is data left in the transport + // and get it with the indicate buffer + // + pLowerConn->StateRcv = INDICATE_BUFFER; + + + ASSERT(pLowerConn->BytesInIndicate == 0); +#if DBG + if (pLowerConn->BytesInIndicate) + { + KdPrint(("Nbt:Bytes in indicate should be ZERO, but are = %X\n", + pLowerConn->BytesInIndicate)); + } +#endif + pLowerConn->CurrentStateProc = IndicateBuffer; + pConnectEle = pLowerConn->pUpperConnection; + + pConnectEle->BytesInXport = BytesAvailable - *BytesTaken; + + // + // for short indications less than 4 bytes we can't determine + // the pdu size so just get the header first then get the + // whole pdu next. + // + Remaining = BytesIndicated - *BytesTaken; + if (Remaining < sizeof(tSESSIONHDR)) + { + status = NtBuildIrpForReceive(pLowerConn,sizeof(tSESSIONHDR),(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + // this is a serious error - we must + // kill of the connection and let the + // redirector restart it + KdPrint(("Nbt:Unable to get an Irp for RCv - Closing Connection!! %X\n",pLowerConn)); + CTESpinFreeAtDpc(pLowerConn); + + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_DATA_NOT_ACCEPTED); + } + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:< 4 Bytes,BytesTaken=%X,Avail=%X,Ind=%X,Remain=%X\n", + *BytesTaken,BytesAvailable,BytesIndicated, + Remaining)); + + // DEBUG + CTEZeroMemory(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl), + NBT_INDICATE_BUFFER_SIZE); + + PUSH_LOCATION(0x7); + status = STATUS_MORE_PROCESSING_REQUIRED; + } + else + { + // if we get to here there are enough bytes left to determine + // the next pdu size...so we can determine how much + // data to get for the indicate buffer + // + pSessionHdr = (tSESSIONHDR UNALIGNED *)((PUCHAR)pTsdu + *BytesTaken); + + PduSize = myntohl(pSessionHdr->UlongLength) + sizeof(tSESSIONHDR); + + + Length = (PduSize > NBT_INDICATE_BUFFER_SIZE) ? + NBT_INDICATE_BUFFER_SIZE : PduSize; + + // + // The NewSessionCompletion routine recalculates + // what is left in the transport when the + // irp completes + // + status = NtBuildIrpForReceive(pLowerConn,Length,(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + // this is a serious error - we must + // kill of the connection and let the + // redirector restart it + KdPrint(("Nbt:Unable to get an Irp for RCV(2) - Closing Connection!! %X\n",pLowerConn)); + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return(STATUS_DATA_NOT_ACCEPTED); + } + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switch to Ind Buff, InXport = %X, Pdusize=%X,ToGet=%X\n", + pConnectEle->BytesInXport,PduSize-4,Length)); + + } + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NotEnoughDataYet( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN ULONG PduSize, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnectEle; + ULONG Length; + + PUSH_LOCATION(0x9); + // + // not enough data indicated, so use the indicate buffer + // + Length = (PduSize > NBT_INDICATE_BUFFER_SIZE) ? + NBT_INDICATE_BUFFER_SIZE : PduSize; + + status = NtBuildIrpForReceive(pLowerConn,Length,(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return(STATUS_DATA_NOT_ACCEPTED); + } + + *BytesTaken = 0; + + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + pConnectEle = pLowerConn->pUpperConnection; + pConnectEle->BytesInXport = BytesAvailable; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Not Enough data indicated in Tdihndlr, using indic. buffer Indicated = %X,Avail=%X,PduSize= %X\n", + BytesIndicated, BytesAvailable,PduSize-4)); + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +FillIrp( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + ASSERTMSG("Should not execute this procedure",0); + return(STATUS_SUCCESS); + // do nothing + +} +//---------------------------------------------------------------------------- +NTSTATUS +IndicateBuffer( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles reception of data while in the IndicateBuffer state. + In this state the indicate buffer is receiveing data until at least + 128 bytes have been receive, or a whole pdu has been received. + + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + NTSTATUS status; + ULONG PduSize; + ULONG ToCopy; + PVOID pIndicateBuffer; + ULONG Taken; + + // + // there is data in the indicate buffer and we got a new + // indication, so copy some or all of the indication to the + // indicate buffer + // + PVOID pDest; + ULONG RemainPdu; + ULONG SpaceLeft; + ULONG TotalBytes; + ULONG ToCopy1=0; + + INCR_COUNT(R3); + PUSH_LOCATION(0xe); + pConnectEle = pLowerConn->pUpperConnection; + ASSERT(pLowerConn->StateRcv == INDICATE_BUFFER); + // + // The indicate buffer always starts with a pdu + // + pIndicateBuffer = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + // the location to start copying the new data into is right + // after the existing data in the buffer + // + pDest = (PVOID)((PUCHAR)pIndicateBuffer + pLowerConn->BytesInIndicate); + + // + // the session header may not be all into the indicate + // buffer yet, so check that before getting the pdu length. + // + if (pLowerConn->BytesInIndicate < sizeof(tSESSIONHDR)) + { + PUSH_LOCATION(0xe); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Too Few in Indicate Buff, Adding InIndicate %X\n", + pLowerConn->BytesInIndicate)); + + ToCopy1 = sizeof(tSESSIONHDR) - pLowerConn->BytesInIndicate; + if (ToCopy1 > BytesIndicated) + { + ToCopy1 = BytesIndicated; + } + CTEMemCopy(pDest,pTsdu,ToCopy1); + + pDest = (PVOID)((PUCHAR)pDest + ToCopy1); + pTsdu = (PVOID)((PUCHAR)pTsdu + ToCopy1); + + pLowerConn->BytesInIndicate += (USHORT)ToCopy1; + + *BytesTaken = ToCopy1; + } + + // now check again, and pass down an irp to get more data if necessary + // + if (pLowerConn->BytesInIndicate < sizeof(tSESSIONHDR)) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:< 4 Bytes in IndicBuff, BytesinInd= %X, BytesIndicated=%x\n", + pLowerConn->BytesInIndicate,BytesIndicated)); + + PUSH_LOCATION(0xF); + + // + // the data left in the transport is what was Available + // minus what we just copied to the indicate buffer + // + pConnectEle->BytesInXport = BytesAvailable - ToCopy1; + + if (pConnectEle->BytesInXport) + { + PUSH_LOCATION(0x10); + // + // pass the indicate buffer down to get some more data + // to fill out to the end of the session hdr + // + NtBuildIndicateForReceive(pLowerConn, + sizeof(tSESSIONHDR)-pLowerConn->BytesInIndicate, + (PVOID *)ppIrp); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:INDIC_BUF...need more data for hdr Avail= %X, InXport = %X\n", + BytesAvailable,pConnectEle->BytesInXport,pLowerConn)); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // if we get to here there isn't 4 bytes in the indicate buffer and + // there is no more data in the Transport, so just wait for the next + // indication. + // + return(STATUS_SUCCESS); + } + + PduSize = myntohl(((tSESSIONHDR *)pIndicateBuffer)->UlongLength) + + sizeof(tSESSIONHDR); + + // copy up to 132 bytes or the whole pdu to the indicate buffer + // + RemainPdu = PduSize - pLowerConn->BytesInIndicate; + + SpaceLeft = NBT_INDICATE_BUFFER_SIZE - pLowerConn->BytesInIndicate; + + if (RemainPdu < SpaceLeft) + ToCopy = RemainPdu; + else + ToCopy = SpaceLeft; + + if (ToCopy > (BytesIndicated-ToCopy1)) + { + ToCopy = (BytesIndicated - ToCopy1); + } + + // + // Copy the indication or part of it to the indication + // buffer + // + CTEMemCopy(pDest,pTsdu,ToCopy); + + pLowerConn->BytesInIndicate += (USHORT)ToCopy; + + TotalBytes = pLowerConn->BytesInIndicate; + + // the amount of data taken is the amount copied to the + // indicate buffer + // + *BytesTaken = ToCopy + ToCopy1; + +#if DBG + { + tSESSIONHDR UNALIGNED *pSessionHdr; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pIndicateBuffer; + ASSERT((pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) || + (pSessionHdr->Type == NBT_SESSION_MESSAGE)); + } +#endif + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:INDIC_BUFF, TotalBytes= %X, InIndic=%X, Copied(0/1)= %X %X Avail %X\n", + TotalBytes,pLowerConn->BytesInIndicate,ToCopy,ToCopy1,BytesAvailable)); + + + // the data left in the transport is what was Available + // minus what we just copied to the indicate buffer + // + pConnectEle->BytesInXport = BytesAvailable - *BytesTaken; + + // now check if we have a whole pdu or 132 bytes, either way + // enough to indicate to the client. + // + ASSERT(TotalBytes <= NBT_INDICATE_BUFFER_SIZE); + + if ((TotalBytes == NBT_INDICATE_BUFFER_SIZE) || + (TotalBytes == PduSize)) + { + + status = CopyDataandIndicate( + ReceiveEventContext, + (PVOID)pLowerConn, + ReceiveFlags, + TotalBytes, + pConnectEle->BytesInXport + TotalBytes, + &Taken, + pIndicateBuffer, + (PIRP *)ppIrp); + + } + else + { + + // not enough data in the indicate buffer yet + // NOTE: *BytesTaken should be set correctly above... + // = ToCopy + ToCopy1; + + PUSH_LOCATION(0x11); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Not Enough data indicated(INDICBUFF state), Indicated = %X,PduSize= %X,InIndic=%X\n", + BytesIndicated, PduSize, pLowerConn->BytesInIndicate)); + + + status = STATUS_SUCCESS; + } + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +PartialRcv( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + // + // the data for the client may be in the indicate buffer and + // in this case the transport could indicate us with more data. Therefore + // track the number of bytes available in the transport which + // we will get when the client finally posts a buffer. + // This state could also happen on a zero length Rcv when the + // client does not accept the data, and later posts a rcv + // buffer for the zero length rcv. + // + INCR_COUNT(R4); + PUSH_LOCATION(0x13); + ASSERT(pLowerConn->StateRcv == PARTIAL_RCV); + pConnectEle = pLowerConn->pUpperConnection; + +// ASSERT(pConnectEle->BytesInXport == 0); +#if DBG + if (pConnectEle->BytesInXport != 0) + { + KdPrint(("Netbt!PartialRcv: pConnectEle->BytesInXport != 0 Avail %X, InIndicate=%X,InXport %X %X\n", + BytesAvailable,pLowerConn->BytesInIndicate, + pConnectEle->BytesInXport,pLowerConn)); + } +#endif // DBG + pConnectEle->BytesInXport = BytesAvailable; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got Indicated while in PartialRcv state Avail %X, InIndicate=%X,InXport %X %X\n", + BytesAvailable,pLowerConn->BytesInIndicate, + pConnectEle->BytesInXport,pLowerConn)); + + *BytesTaken = 0; + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiReceiveHandler ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + IN PVOID ReceiveEventContext - Context provided for this event when event set + IN PVOID ConnectionContext - Connection Context, (pLowerConnection) + 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 pTsdu - Data from remote machine. + OUT PIRP *ppIrp - I/O request packet filled in if received data + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + register tLOWERCONNECTION *pLowerConn; + PIRP pIrp; + CTELockHandle OldIrq; + NTSTATUS status; + tCONNECTELE *pConnEle; + ULONG BTaken; + + *ppIrp = NULL; + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + + // NOTE: + // Access is synchronized through the spin lock on pLowerConn for all + // Session related stuff. This includes the case where the client + // posts another Rcv Buffer in NTReceive. - so there is no need to get the + // pConnEle Spin lock too. + // + + CTESpinLock(pLowerConn,OldIrq); +// pLowerConn->InRcvHandler = TRUE; + pLowerConn->RefCount++; + + // save this on the stack in case we need to dereference it below. + pConnEle = pLowerConn->pUpperConnection; + + // call the correct routine depending on the state of the connection + // Normal/FillIrp/PartialRcv/IndicateBuffer/Inbound/OutBound + // + if ((pLowerConn->State == NBT_SESSION_UP) && + (pLowerConn->StateRcv == FILL_IRP)) + { + PIO_STACK_LOCATION pIrpSp; + PMDL pNewMdl; + PFILE_OBJECT pFileObject; + ULONG RemainingPdu; + PVOID NewAddress; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + KIRQL OldIrq2; + ULONG RcvLength; + + + PUSH_LOCATION(0xa); + // we are still waiting for the rest of the session pdu so + // do not call the RcvHandlrNotOs, since we already have the buffer + // to put this data in. + // too much data may have arrived... i.e. part of the next session pdu.. + // so check and set the receive length accordingly + // + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + RcvLength = RemainingPdu; + // + // try high runner case first + // + if (BytesAvailable <= RemainingPdu) + { + PUSH_LOCATION(0xb); + // + // if the client buffer is too small to take all of the rest of the + // data, shorten the receive length and keep track of how many + // bytes are left in the transport. ReceiveIndicated should have + // been set when the irp was passed down originally. + // + if (BytesAvailable > pConnEle->FreeBytesInMdl) + { + + PUSH_LOCATION(0xb); + + RcvLength = pConnEle->FreeBytesInMdl; + pConnEle->BytesInXport = BytesAvailable - RcvLength; + } + } + else + { + // + // start of session pdu in the middle of the indication + // + PUSH_LOCATION(0xc); + // + // It is possible that the client buffer is too short, so check + // for that case. + // + if (RemainingPdu > pConnEle->FreeBytesInMdl) + { + RcvLength = pConnEle->FreeBytesInMdl; + PUSH_LOCATION(0xd); + } + /* Remember how much data is left in the transport + when this irp passes through the completionrcv routine + it will pass the indication buffer back to the transport + to get at least 4 bytes of header information so we + can determine the next session pdu's size before receiving + it. The trick is to avoid having more than one session + pdu in the buffer at once. + */ + pConnEle->BytesInXport = BytesAvailable - RcvLength; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:End of FILL_IRP, found new Pdu BytesInXport=%X\n", + pConnEle->BytesInXport)); + + + } + + pIrp = pConnEle->pIrpRcv; + + // if the transport has all of the data it says is available, then + // do the copy here ( if the client buffer is large enough - checked + // by !ReceiveIndicated) + // + if ((BytesAvailable == BytesIndicated) && + (RcvLength >= BytesIndicated) && + !pConnEle->ReceiveIndicated) + { + ULONG BytesCopied; + ULONG TotalBytes; + + PUSH_LOCATION(0x70); + + if (RcvLength > BytesIndicated) + RcvLength = BytesIndicated; + + status = TdiCopyBufferToMdl( + pTsdu, + 0, + RcvLength, + pConnEle->pNextMdl, + pConnEle->OffsetFromStart, + &BytesCopied); + + // + // if the irp is not yet full, or the free bytes have not + // been exhausted by this copy, then adjust some counts and return + // quickly, otherwise call the completion rcv routine as if the + // irp has completed normally from the transport - + // + TotalBytes = pConnEle->BytesRcvd + BytesCopied; + + if ((TotalBytes < pConnEle->TotalPcktLen) && + (BytesCopied < pConnEle->FreeBytesInMdl)) + { + PMDL pMdl; + + // + // take the short cut and do not call completion rcv since we + // are still waiting for more data + // + PUSH_LOCATION(0x81); + pConnEle->BytesRcvd += BytesCopied; + pConnEle->FreeBytesInMdl -= BytesCopied; + + // clean up the partial mdl. + // + pMdl = pConnEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + // set where the next rcvd data will start, by setting the pNextMdl and + // offset from start. + // + pMdl = pConnEle->pNextMdl; + if ((BytesCopied + pConnEle->OffsetFromStart) < MmGetMdlByteCount(pMdl)) + { + PUSH_LOCATION(0x82); + // + // All of this data will fit into the current Mdl, and + // the next data will start in the same Mdl (if there is more data) + // + pConnEle->OffsetFromStart += BytesCopied; + } + else + { + PUSH_LOCATION(0x83) + SumMdlLengths(pMdl, + pConnEle->OffsetFromStart + BytesCopied, + pConnEle); + } + *BytesTaken = BytesCopied; + status = STATUS_SUCCESS; + + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("I")); + goto ExitRoutine; + } + else + { + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("i")); + CTESpinFree(pLowerConn,OldIrq); + // + // the values are set to this so that when Completion Rcv is + // called it will increment the BytesRcvd by BytesCopied. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = BytesCopied; + + // + // now call the irp completion routine, shorting out the io + // subsystem - to process the irp + // + status = CompletionRcv(NULL,pIrp,(PVOID)pLowerConn); + // + // complete the irp back to the client if required + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + IoAcquireCancelSpinLock(&OldIrq2); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq2); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + } + // + // tell the transport we took all the data that we did take. + // Since CompletionRcv has unlocked the spin lock and decremented + // the refcount, return here. + // + *BytesTaken = BytesCopied; + return(STATUS_SUCCESS); + } + else + { + // + // Either BytesIndicated != BytesAvailable or the RcvBuffer + // is too short, so make up an Irp with a partial Mdl and pass it + // to the transport. + // + PUSH_LOCATION(0x71); + + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pConnEle->pNextMdl) + + pConnEle->OffsetFromStart); + + /* create a partial MDL so that the new data is copied after the existing data + in the MDL. Use the pNextMdl field stored in the pConnEle + that was set up during the last receive.( since at that time + we knew the BytesAvailable then). Without this we would have to + traverse the list of Mdls for each receive. + + 0 for length means map the rest of the buffer + */ + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pConnEle->pNextMdl,pNewMdl,NewAddress,0); + // + // hook the new partial mdl to the front of the MDL chain + // + pNewMdl->Next = pConnEle->pNextMdl->Next; + + pIrp->MdlAddress = pNewMdl; + ASSERT(pNewMdl); + + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + IoAcquireCancelSpinLock(&OldIrq2); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq2); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + /* this code is sped up somewhat by expanding the code here rather than calling + the TdiBuildReceive macro + + make the next stack location the current one. Normally IoCallDriver + would do this but we are not going through IoCallDriver here, since the + Irp is just passed back with RcvIndication. + */ + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + pIrpSp->CompletionRoutine = CompletionRcv; + + pParams->ReceiveLength = RcvLength; + + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pLowerConn; + + /* set flags so the completion routine is always invoked. + */ + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + + pFileObject = pLowerConn->pFileObject; + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + /* + pass the Irp back to the transport + */ + *ppIrp = (PVOID)pIrp; + *BytesTaken = 0; + + status = STATUS_MORE_PROCESSING_REQUIRED; + } + + + } + else + if ((pLowerConn->State == NBT_SESSION_UP) && + (pLowerConn->StateRcv == NORMAL)) + { + ULONG PduSize; + UCHAR Passit; + + INCR_COUNT(R1); + /* + check indication and if less than 1 pdu or 132 bytes then + copy to the indicate buffer and go to Indic_buffer state + The while loop allows us to indicate multiple Pdus to the + client in the event that several indications arrive in one + indication from the transport + NOTE: + It is possible to get an indication that occurs in the middle + of the pdu if the client took the first indication rather + than passing an irp back, and thence going to the FILL_IRP + state. So check if BytesRcvd is zero, meaning that we are + expecting a new PDU. + */ + ASSERT(pConnEle->BytesInXport == 0); + ASSERT(pLowerConn->StateRcv == NORMAL); + + if (pConnEle->BytesRcvd == 0) + { + if (BytesIndicated >= sizeof(tSESSIONHDR)) + { + PduSize = myntohl(((tSESSIONHDR UNALIGNED *)pTsdu)->UlongLength) + + sizeof(tSESSIONHDR); + Passit = FALSE; + + } + else + { + status = LessThan4BytesRcvd(pLowerConn, + BytesAvailable, + BytesTaken, + ppIrp); + goto ExitRoutine; + } + + } + else + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got rest of PDU in indication BytesInd %X, BytesAvail %X\n", + BytesIndicated, BytesAvailable)); + + /* This is the remaining pdu size + */ + PduSize = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + /* a flag to pass the if below, since we are passing the + remaining data of a pdu to the client and we do not have + to adhere to the 128 bytes restriction. + */ + PUSH_LOCATION(0x1); + if (pConnEle->JunkMsgFlag) + { + // + // in this case the client has indicated that it took the + // entire message on the previous indication, so don't + // indicate any more to it. + // + PUSH_LOCATION(0x1); + + if (BytesAvailable < PduSize) + { + BTaken = BytesAvailable; + } + else + { + BTaken = PduSize; + } + pConnEle->BytesRcvd += BTaken; + if (pConnEle->BytesRcvd == pConnEle->TotalPcktLen) + { + PUSH_LOCATION(0x1); + pConnEle->BytesRcvd = 0; // reset for the next session pdu + pConnEle->JunkMsgFlag = FALSE; + } + status = STATUS_SUCCESS; + goto SkipIndication; + } + Passit = TRUE; + + } + /* + be sure that there is at least 132 bytes or a whole pdu + Since a keep alive has a zero length byte, we check for + that because the 4 byte session hdr is added to the 0 length + giving 4, so a 4 byte Keep Alive pdu will pass this test. + */ + if ((BytesIndicated >= NBT_INDICATE_BUFFER_SIZE) || + (BytesIndicated >= PduSize) || Passit ) + { + + PUSH_LOCATION(0x2); + + /* + // Indicate to the client + */ + status = RcvHandlrNotOs( + ReceiveEventContext, + (PVOID)pLowerConn, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &BTaken, + pTsdu, + (PVOID)&pIrp + ); + + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ULONG RemainingPdu; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // check if we can copy to the client's irp directly - meaning + // that we have received the whole pdu in this indication and + // the client's buffer is large enough, and there is no more + // data in the transport. + // + + if ((RemainingPdu == (BytesIndicated - BTaken)) && + (BytesIndicated == BytesAvailable) && + (pClientParams->ReceiveLength >= RemainingPdu) && + pIrp->MdlAddress) + { + ULONG BytesCopied; + + PUSH_LOCATION(0x88); + + status = TdiCopyBufferToMdl( + (PVOID)((PUCHAR)pTsdu + BTaken), + 0, + RemainingPdu, + pIrp->MdlAddress, + 0, + &BytesCopied); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Copy to client Buffer RcvLen=%X,StateRcv=%X\n", + RemainingPdu,pLowerConn->StateRcv)); + + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // reset a few things since this pdu has been fully recv'd + // + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // + // tell the transport we took all the data that we did take. + // + *BytesTaken = BytesCopied + BTaken; + + // + // complete the irp back to the client if required + // + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("F")); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + pLowerConn->BytesRcvd += BytesCopied; + + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + return(STATUS_SUCCESS); + } + else + { + PUSH_LOCATION(0x3); + + status = ProcessIrp(pLowerConn, + pIrp, + pTsdu, + &BTaken, + BytesIndicated, + BytesAvailable); + + *BytesTaken = BTaken; + ASSERT(*BytesTaken <= (pConnEle->TotalPcktLen + sizeof(tSESSIONHDR)) ); + if (status == STATUS_RECEIVE_EXPEDITED) + { + // in this case the processirp routine has completed the + // irp, so just return since the completion routine will + // have adjusted the RefCount and InRcvHandler flag + // + *ppIrp = NULL; + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_SUCCESS); + } + else + if (status == STATUS_SUCCESS) + { + *ppIrp = NULL; + + } + else + { + *ppIrp = (PVOID)pIrp; + } + } + + + } + else + { + // for the skip indication case the client has told us it + // does not want to be indicated with any more of the data + // + SkipIndication: + // + // the client received some, all or none of the data + // For Keep Alives the PduSize is 4 and BytesTaken = 4 + // so this check and return status success + // + *BytesTaken = BTaken; + + pLowerConn->BytesRcvd += BTaken - sizeof(tSESSIONHDR); + + // + // if the connection has disonnected, then just return + // + if (!pLowerConn->pUpperConnection) + { + *BytesTaken = BytesAvailable; + status = STATUS_SUCCESS; + } + else + if (BTaken > BytesAvailable) + { + // + // in this case the client has taken all of the message + // which could be larger than the available because + // we set bytesavail to the message length. So set a flag + // that tells us to discard the rest of the message as + // it comes in. + // + pConnEle->JunkMsgFlag = TRUE; + pConnEle->BytesRcvd = BytesAvailable - sizeof(tSESSIONHDR); + *BytesTaken = BytesAvailable; + + } + else + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // this may be a zero length send -that the client has + // decided not to accept. If so then the state will be set + // to PartialRcv. In this case do NOT go down to the transport + // and get the rest of the data, but wait for the client + // to post a rcv buffer. + // + + // amount left in the transport... + pConnEle->BytesInXport = BytesAvailable - BTaken; + status = STATUS_SUCCESS; + } + else + if (BTaken == PduSize) + { + /* + Must have taken all of the pdu data, so check for + more data available - if so send down the indicate + buffer to get it. + */ + ASSERT(BTaken <= BytesIndicated); + if (BytesAvailable <= BTaken) + { + /* FAST PATH + */ + PUSH_LOCATION(0x8); + + status = STATUS_SUCCESS; + + } + else + { + /* + get remaining data with the indicate buffer + */ + status = MoreDataRcvdThanNeeded(pLowerConn, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + ppIrp); + } + } + else + { + // + // the client may have taken all the data in the + // indication!!, in which case return status success + // Note: that we check bytes available here not bytes + // indicated - since the client could take all indicated + // data but still leave data in the transport. + // + if (BTaken == BytesAvailable) + { + PUSH_LOCATION(0x4); + status = STATUS_SUCCESS; + + } + else + { + PUSH_LOCATION(0x87); + if (BTaken > PduSize) + { +#ifndef VXD +#if DBG + DbgBreakPoint(); +#endif +#endif + // + // the client took more than a PDU size worth, + // which is odd.... + // + PUSH_LOCATION(0x87); + ASSERT(BTaken <= PduSize); + + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + status = STATUS_SUCCESS; + + } + else + { + // + // otherwise the client did not take all of the data, + // which can mean that + // the client did not take all that it could, so + // go to the partial rcv state to keep track of it. + // + status = ClientTookSomeOfTheData(pLowerConn, + BytesIndicated, + BytesAvailable, + *BytesTaken, + PduSize); + } + } + } + + } + + } + else + { + status = NotEnoughDataYet(pLowerConn, + BytesIndicated, + BytesAvailable, + BytesTaken, + PduSize, + (PVOID *)ppIrp); + } + } + else + { + status = (*pLowerConn->CurrentStateProc)(ReceiveEventContext, + pLowerConn, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + ppIrp); + } + + // + // in the IndicateBuffer state we have sent the indicate buffer + // down the the transport and expect it to come back in + // NewSessionCompletionRoutine. Therefore do not dereference the lower + // connection and do not change the InRcvHandler flag. + + // If an Irp + // is returned, then do not undo the reference - but rather + // wait for CompletionRcv to be called. + // +ExitRoutine: + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection + // + PUSH_LOCATION(0x50); + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + } + else + CTESpinFree(pLowerConn,OldIrq); + + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ProcessIrp( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrp, + IN PVOID pBuffer, + IN PULONG BytesTaken, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable + ) +/*++ + +Routine Description: + + This routine handles a Receive Irp that the client has returned on an + indication. The idea here is to check the Irp's MDL length to be + sure the pdu fits into the MDL, and also keep track of the situation where + more than one data is required to fill the pdu. + +Arguments: + + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnectEle; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + ULONG RemainingPdu; + PMDL pMdl; + PFILE_OBJECT pFileObject; + ULONG ReceiveLength; + BOOLEAN QuickRoute; + BOOLEAN FromCopyData; + + pConnectEle = pLowerConn->pUpperConnection; + + status = STATUS_SUCCESS; + + // subtract session header and any bytes that the client took + // + BytesAvailable -= *BytesTaken; + + // + // put together an Irp stack location to process the receive and pass down + // to the transport. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // + // check if this will be a multiple rcv session pdu. If it is then + // allocate a partial MDL to be used for mapping part of the first + // MDL in each chunk received + // + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + ReceiveLength = RemainingPdu; + PUSH_LOCATION(0x19); + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + // this code should not be hit if called by CopyDataandIndicate + // which is in the indicate buffer state since it adjusts the bytesInXport + // which is also set by the code in TdiReceiveHndlr in the INDICATE_BUFFER + // state before calling CopyDataandIndicate. Also, CopyDataandIndicate + // does not want this routine to set the state to fillIrp when Bytes + // Available < RemainingPdu + // + FromCopyData = (pLowerConn->StateRcv == INDICATE_BUFFER); + if (!FromCopyData) + { + + QuickRoute = TRUE; + // we need this code within the check since this routine is also called by the + // HandleNewSessionPdu routine, which calls IoCallDriver, which + // increments the stack location itself. + // + ASSERT(pIrp->CurrentLocation > 1); + + if (BytesAvailable == RemainingPdu) + { + if (pClientParams->ReceiveLength >= BytesAvailable) + { + // *** FAST PATH CASE **** + goto ExitCode; + } + } + else + if (BytesAvailable < RemainingPdu ) // need more data from transport + { + PUSH_LOCATION(0x14); + // it is possible for the client to pass down an irp with no + // MDL in it, so we check for that here + // + if (pIrp->MdlAddress) + { + PUSH_LOCATION(0x14); + + // + // save the client's irp address since the session pdu will arrive + // in several chunks, and we need to continually pass the irp to the + // transport for each chunk. + // + //pConnectEle->pIrpRcv = pIrp; + // NOTE: the pIrp is NOT saved here because the irp is about + // to be passed back to the transport. Hence we do not want + // to accidently complete it in DisconnectHandlrNotOs + // if a disconnect comes in while the irp is in the transport. + // pIrpRcv is set to pIrp in Completion Rcv while we have + // the irp in our possession. + + // + // keep the initial Mdl(chain) since we need to + // to copy new data after the existing data, when the session pdu arrives + // as several chunks from TCP. Keeping the Mdl around allows us to + // reconstruct the original Mdl chain when we are all done. + // + pLowerConn->pMdl = pIrp->MdlAddress; + // + // this call maps the client's Mdl so that on each partial Mdl creation + // we don't go through a mapping and unmapping (when MmPrepareMdlForReuse) + // is called in the completion routine. + // + (PVOID)MmGetSystemAddressForMdl(pIrp->MdlAddress); + + pMdl = pIrp->MdlAddress; + + // the nextmdl is setup to allow us to create a partial Mdl starting + // from the next one. CompletionRcv will adjust this if it needs to. + // + pConnectEle->pNextMdl = pMdl; + + // need more data from the transport to fill this + // irp + // + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + } + + status = STATUS_MORE_PROCESSING_REQUIRED; + + // if the client buffer is big enough, increment to the next + // io stack location and jump to the code that sets up the + // irp, since we always want to pass it to the transport in this + // case because the transport will hold onto the irp till it is full + // if it can. (faster) + // + if (pClientParams->ReceiveLength >= RemainingPdu) + { + // *** FAST PATH CASE **** + IoSetNextIrpStackLocation(pIrp); + pConnectEle->FreeBytesInMdl = ReceiveLength; + pConnectEle->CurrentRcvLen = RemainingPdu; + goto ExitCode2; + } + + // + // if there is no mdl then we want to be able to go through the + // quick route below to return the null mdl right away, so + // don't set Quickroute false here. + // + + + } + else + if (BytesAvailable > RemainingPdu) + { + PUSH_LOCATION(0x15); + // + // there is too much data, so keep track of the + // fact that there is data left in the transport + // and get it when the irp completes through + // completion recv. + // + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + // this calculation may have to be adjusted below if the client's + // buffer is too short. NOTE: BytesTaken have already been subtracted + // from BytesAvailable (above). + // + pConnectEle->BytesInXport = BytesAvailable - RemainingPdu; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to Indicate Buff(Irp), Indic = %X, Pdusize=%X\n", + BytesIndicated,pConnectEle->TotalPcktLen)); + + + status = STATUS_DATA_NOT_ACCEPTED; + } + + // DEBUG* + //IoSetNextIrpStackLocation(pIrp); + } + else + { + QuickRoute = FALSE; + } + + // + // if the receive buffer is too short then flag it so when the client + // passes another buffer to NBT, nbt will pass it to the transport + // + //if (BytesAvailable > pClientParams->ReceiveLength ) + { + + // so just check for too short of a client buffer. + // + if (RemainingPdu > pClientParams->ReceiveLength) + { + PUSH_LOCATION(0x17); + + ReceiveLength = pClientParams->ReceiveLength; + // + // Adjust the number of bytes left in the transport up by the number of + // bytes not taken by the client. Be sure not to add in the number + // of bytes in the transport twice, since it could have been done + // above where the state is set to INDICATE_BUFFER + // + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // BytesInXport was already incremented to account for any + // amount over remainingPdu, so just add the amount that the + // client buffer is short of RemainingPdu + // + PUSH_LOCATION(0x18); + if (BytesAvailable > ReceiveLength ) + { + pConnectEle->BytesInXport += (RemainingPdu - ReceiveLength); + } + // the client has not taken all of the data , but has returned + // a buffer that is ReceiveLength long, therefore the amount + // that the client needs to take is just the total pdu - rcvlength. + // + pConnectEle->ReceiveIndicated = (RemainingPdu - + ReceiveLength); + } + else + { + // + // BytesInXport has not been incremented yet so add the entire + // amount that the client buffer is too short by. Check if + // the client's buffer will take all of the data. + // + if (BytesAvailable > ReceiveLength ) + { + pConnectEle->BytesInXport += (BytesAvailable - ReceiveLength); + } + // the client has not taken all of the data , but has returned + // a buffer that is ReceiveLength long, therefore the amount + // that the client needs to take is just what was indicated + // to the client - recvlength. + // + pConnectEle->ReceiveIndicated = (RemainingPdu - + ReceiveLength); + + } + + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to PartialRcv for Irp. RecvInd. =%X, RemainPdu %X Avail %X\n", + pConnectEle->ReceiveIndicated,RemainingPdu,BytesAvailable)); + } + + } + +ExitCode: + + // keep track of data in MDL so we know when it is full and we need to + // return it to the user. CurrentRcvLen tells us how many bytes the current + // Irp can have max when the Mdl is full. + // + pConnectEle->FreeBytesInMdl = ReceiveLength; + pConnectEle->CurrentRcvLen = ReceiveLength; + if (ReceiveLength > RemainingPdu) + { + pConnectEle->CurrentRcvLen = RemainingPdu; + } + if (QuickRoute) + { + // + // check if we can copy the data to the client's MDL + // right here. If the indication is too short pass an Irp down + // to the transport. + // + BytesIndicated -= *BytesTaken; + + if ((ReceiveLength <= BytesIndicated)) + { + ULONG BytesCopied; + + PUSH_LOCATION(0x76); + + if (pIrp->MdlAddress) + { + + status = TdiCopyBufferToMdl( + (PVOID)((PUCHAR)pBuffer + *BytesTaken), + 0, + ReceiveLength, + pIrp->MdlAddress, + 0, + &BytesCopied); + + } + else + { + // + // No Mdl, so just return the irp to the client, and then + // return success to the caller so we tell the transport that + // we took only BytesTaken + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:No MDL, so complete Irp\n")); + + + PUSH_LOCATION(0x77); + BytesCopied = 0; + } + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Copy to client Buffer RcvLen=%X,StateRcv=%X\n", + ReceiveLength,pLowerConn->StateRcv)); + + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + // + // now call the irp completion routine, shorting out the io + // subsystem - to process the irp + // + CTESpinFreeAtDpc(pLowerConn); + status = CompletionRcv(NULL,pIrp,(PVOID)pLowerConn); + + // + // tell the transport we took all the data that we did take. + // + *BytesTaken += BytesCopied; + + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("f")); + // + // complete the irp back to the client if required + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + PUSH_LOCATION(0x76); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Completing Irp Quickly\n")); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + + // since we have called CompletionRcv, that routine has + // adjusted the refcount and InRcvHandlr flag, so return this + // status to cause the caller to return directly + CTESpinLockAtDpc(pLowerConn); + return(STATUS_RECEIVE_EXPEDITED); + + } + else + { + // + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + // + IoSetNextIrpStackLocation(pIrp); + } + + } +ExitCode2: + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pLowerConn; + + // set Control flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + + pFileObject = pLowerConn->pFileObject; + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + // Set the correct receive length in the irp in case the client has + // passed one down that is larger than the message + // + pParams->ReceiveLength = ReceiveLength; + + // + // just check for a zero length send, where the client has + // passed down an Irp with a null mdl, or the pdu size is zero. We don't want to pass + // that to the transport because it will hold onto it till the next + // pdu comes in from the wire - we want to complete the irp when this routine + // returns. When this is called from CopyDataAndIndicate don't + // to this because copydataandindicate does all the checks. + // + if (!FromCopyData) + { + if ((RemainingPdu == 0) || !pIrp->MdlAddress) + { + // + // the call to IoCompleteRequest will call completionRcv which will + // decrement the RefCount. Similarly returning status success will + // cause the caller to decrement the ref count, so increment one + // more time here to account for this second decrement. + // + pLowerConn->RefCount++; + CTESpinFreeAtDpc(pLowerConn); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTESpinLockAtDpc(pLowerConn); + + status = STATUS_SUCCESS; + } + else + status = STATUS_MORE_PROCESSING_REQUIRED; + } + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ClientBufferOverFlow( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG BytesRcvd + ) +/*++ + +Routine Description: + + This routine completes the Irp by tracking the number of bytes received + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pLowerConn - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + + // *TODO* + +// ASSERT(0); + + switch (pLowerConn->StateRcv) + { + case PARTIAL_RCV: + + + case FILL_IRP: + + + case NORMAL: + + + + case INDICATE_BUFFER: + + + default: + ; + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRcv( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine completes the Irp by tracking the number of bytes received + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pLowerConn - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + register tCONNECTELE *pConnectEle; + NTSTATUS status; + ULONG BytesRcvd; + tLOWERCONNECTION *pLowerConn; + PKDPC pDpc; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + PMDL pMdl; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + BOOLEAN AllowDereference=TRUE; + + // + // Do some checking to keep the Io system happy - propagate the pending + // bit up the irp stack frame.... if it was set by the driver below then + // it must be set by me + // + if (Irp->PendingReturned) + { + IoMarkIrpPending(Irp); + } + + // check the bytes recvd + pLowerConn = (tLOWERCONNECTION *)Context; + // + // if the link has disconnected, do not process the irp, just pass it + // up the chain. + // + CTESpinLock(pLowerConn,OldIrq); + if (!NT_SUCCESS(Irp->IoStatus.Status) || !pLowerConn->pUpperConnection) + { + PUSH_LOCATION(0x1); + if (pLowerConn->StateRcv == FILL_IRP) + { + PUSH_LOCATION(0x1); + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + } + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + // + // the rcv failed so kill the connection since + // we can't keep track of message boundaries any more. + // + CTESpinFree(pLowerConn,OldIrq); + OutOfRsrcKill(pLowerConn); + CTESpinLock(pLowerConn,OldIrq); + + status = STATUS_SUCCESS; + goto ExitCode; + } + + pConnectEle = pLowerConn->pUpperConnection; + + // keep track of how many bytes have been received + // + BytesRcvd = Irp->IoStatus.Information; + pConnectEle->BytesRcvd += BytesRcvd; + // + // subtract the number of bytes rcvd from the length of the client + // buffer + // so when more data arrives we can determine if we are going to + // overflow the client buffer. + // + pConnectEle->FreeBytesInMdl -= BytesRcvd; + + pIrpSp = IoGetCurrentIrpStackLocation(Irp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + pLowerConn->BytesRcvd += BytesRcvd; + + CHECK_PTR(pConnectEle); + if (Irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW) + { + // + // the client's buffer was too short - probably because he said it + // was longer than it really was + // + PUSH_LOCATION(0x1a); + KdPrint(("Nbt:Client Buffer Too short on CompletionRcv\n")); + + if (pLowerConn->StateRcv == FILL_IRP) + { + PUSH_LOCATION(0x1a); + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + + } + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + status = ClientBufferOverFlow(pLowerConn,pConnectEle,Irp,BytesRcvd); + + // + // the client's buffer was too short so kill the connection since + // we can't keep track of message boundaries any more. + // + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + CTESpinFree(pLowerConn,OldIrq); + OutOfRsrcKill(pLowerConn); + CTESpinLock(pLowerConn,OldIrq); + + goto ExitCode; + } + else + if ((pConnectEle->FreeBytesInMdl == 0) || + (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen)) + { + INCR_COUNT(C1); + // + // this case handles when the Irp MDL is full or the whole pdu has been + // received. + // + + // + // reset the MDL fields back to where they were + // if this was a multi-rcv session pdu + // + // + if (pLowerConn->StateRcv == FILL_IRP) + { + + INCR_COUNT(C2); + PUSH_LOCATION(0x1b); + + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + // + // allow the MDL to be used again for the next session PDU + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + pConnectEle->OffsetFromStart = 0; + + } + // + // The client may have passed down a too short irp which we are + // tracking with ReceiveIndicated, so set state to partialrcv if + // necessary. + // + if (pConnectEle->ReceiveIndicated == 0) + { + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + else + { + PUSH_LOCATION(0x26); + // + // there may still be data left in the transport + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Short Rcv, still data indicated to client\n")); + + pLowerConn->StateRcv = PARTIAL_RCV; + pLowerConn->CurrentStateProc = PartialRcv; + } + + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + // + // we have received all of the data + // so complete back to the client + // + status = STATUS_SUCCESS; + // + // the amount of data in this irp is the CurrentRcvLen which + // could be less than BytesRcvd when the client passes down + // short rcv buffers. + // + Irp->IoStatus.Information = pConnectEle->CurrentRcvLen; + + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + Irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + PUSH_LOCATION(0x27); + // + // this MDL must be too short to take the whole pdu, so set the + // status to buffer overflow. + // + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + + // + // Check if there is still more data in the transport or if the client + // has been indicated with more data and has subsequently posted a rcv + // which we must get now and pass to the transport. + // + if ((pConnectEle->BytesInXport) || (pLowerConn->StateRcv == PARTIAL_RCV)) + { + INCR_COUNT(C3); + // + // send down another + // irp to get the data and complete the client's current irp. + // + PUSH_LOCATION(0x1c); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:ComplRcv BytesInXport= %X, %X\n",pConnectEle->BytesInXport, + pLowerConn)); + + if (pLowerConn->StateRcv != PARTIAL_RCV) + { + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + pLowerConn->BytesInIndicate = 0; + } + + CTESpinFree(pLowerConn,OldIrq); + + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(Irp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + // Complete the current Irp + IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); + + CTESpinLock(pLowerConn,OldIrq); + + // rather than call HandleNewSessionPdu directly, we queue a + // Dpc since streams does not currently expect to get a recv + // posted while it is processing an indication response. The + // Dpc will run when streams is all done, and it should handle + // this posted receive ok. + + + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // + // check if the client has passed down another rcv buffer + // and if so, start a Dpc which will pass down the client's + // buffer. + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('p')); + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcGetRestOfIndication, + (PVOID)pLowerConn); + + KeInsertQueueDpc(pDpc,NULL,NULL); + // + // we don't want to dereference pLowerConn at the end + // since we will use it in the DPC routine. + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + + } + } + else + { + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('q')); + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + // + // just get the session hdr to start with so we know how large + // the pdu is, then get the rest of the pdu after that completes. + // + KeInsertQueueDpc(pDpc,NULL,(PVOID)sizeof(tSESSIONHDR)); + // + // we don't want to dereference pLowerConn at the end + // since we will use it in the DPC routine. + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + } + + status = STATUS_MORE_PROCESSING_REQUIRED; + goto ExitCode; + } + } + else + if (pConnectEle->BytesRcvd < pConnectEle->TotalPcktLen) + { + ULONG Bytes; + + INCR_COUNT(C4); + PUSH_LOCATION(0x1d); + // + // in this case we have not received all of the data from the transport + // for this session pdu, so tell the io subystem not to finish processing + // the irp yet if it is not a partial Rcv. + // + status = STATUS_MORE_PROCESSING_REQUIRED; + + // clean up the partial mdl. + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + // set where the next rcvd data will start, by setting the pNextMdl and + // offset from start. + // + pMdl = pConnectEle->pNextMdl; + ASSERT(pMdl); + + Bytes = BytesRcvd + pConnectEle->OffsetFromStart; + if (Bytes < MmGetMdlByteCount(pMdl)) + { + PUSH_LOCATION(0x74); + // + // All of this data will fit into the current Mdl, and + // the next data will start in the same Mdl (if there is more data) + // + pConnectEle->OffsetFromStart += BytesRcvd; + + IF_DBG(NBT_DEBUG_FILLIRP) + KdPrint(("~")); + } + else + { + // + // sum the Mdl lengths until we find enough space for the data + // to fit into. + // + IF_DBG(NBT_DEBUG_FILLIRP) + KdPrint(("^")); + PUSH_LOCATION(0x75); + + SumMdlLengths(pMdl,Bytes,pConnectEle); + + } + + // since we are holding on to the rcv Irp, set up a cancel routine + IoAcquireCancelSpinLock(&OldIrq2); + + // if the session was disconnected while the transport had the + // irp, then cancel the irp now... + // + if ((pConnectEle->state != NBT_SESSION_UP) || Irp->Cancel) + { + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + + + IoReleaseCancelSpinLock(OldIrq2); + CTESpinFree(pLowerConn,OldIrq); + + // since the irp has been cancelled, don't touch it. + // return status success so the IO subsystem passes the irp + // back to the owner. + // + status = STATUS_SUCCESS; + +// Irp->IoStatus.Status = STATUS_CANCELLED; +// IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); + + // the irp is being cancelled in mid session pdu. We can't + // recover since we have given the client only part of a pdu, + // therefore disconnect the connection. + + OutOfRsrcKill(pLowerConn); + + CTESpinLock(pLowerConn,OldIrq); + + } + else + { + // setup the cancel routine + IoSetCancelRoutine(Irp,FillIrpCancelRoutine); + + // the pIrpRcv value is set to Zero when the irp is in the + // tranport, so we can't accidently complete it twice in + // disconnectHandlrNotOs when a disconnect occurs and the + // transport has the irp. So here we save the value again so FillIrp + // will work correctly. + // + pConnectEle->pIrpRcv = Irp; + // set the irp mdl back to its original so that a cancel will + // find the irp in the right state + // + Irp->MdlAddress = pLowerConn->pMdl; + + IoReleaseCancelSpinLock(OldIrq2); + + } + } + else + { + + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d,NewBytes =%d,%X\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen, + Irp->IoStatus.Information,pLowerConn)); + ASSERT(0); + // this status will return the irp to the user + // + status = STATUS_SUCCESS; + if (pLowerConn->StateRcv == FILL_IRP) + { + + PUSH_LOCATION(0x1f); + + Irp->MdlAddress = pLowerConn->pMdl; + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = 0; + + // + // allow the MDL to be used again for the next session PDU + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + + } + pConnectEle->OffsetFromStart = 0; + pConnectEle->BytesRcvd = 0; + + pLowerConn->StateRcv = NORMAL; + + //WHAT ELSE TO DO HERE OTHER THAN KILL THE CONNECTION, SINCE WE ARE + // PROBABLY OFF WITH RESPECT TO THE SESSION HDR.... + // ....RESET THE CONNECTION ???? + + CTESpinFree(pLowerConn,OldIrq); + + OutOfRsrcKill(pLowerConn); + + CTESpinLock(pLowerConn,OldIrq); + + } + +ExitCode: + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection - this function is __inline!! + // + PUSH_LOCATION(0x52); + DerefLowerConnFast(pLowerConn,pConnectEle,OldIrq); + + return(status); + + UNREFERENCED_PARAMETER( DeviceObject ); +} +//---------------------------------------------------------------------------- + +__inline +NTSTATUS +RcvHandlrNotOs ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *RcvBuffer + + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network, when the + session has already been established (NBT_SESSION_UP state). The routine + looks for a receive buffer first and failing that looks for a receive + indication handler to pass the message to. + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + PLIST_ENTRY pRcv; + PVOID pRcvElement; + tCLIENTELE *pClientEle; + tSESSIONHDR UNALIGNED *pSessionHdr; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + PIRP pIrp; + ULONG ClientBytesTaken; + BOOLEAN DebugMore; + ULONG RemainingPdu; + +//******************************************************************** +//******************************************************************** +// +// NOTE: A copy of this procedure is in Tdihndlr.c - it is inlined for +// the NT case. Therefore, only change this procedure and then +// copy the procedure body to Tdihndlr.c +// +// +//******************************************************************** +//******************************************************************** + + // get the ptr to the lower connection, and from that get the ptr to the + // upper connection block + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu; + + // + // Session ** UP ** processing + // + *BytesTaken = 0; + + pConnectEle = pLowerConn->pUpperConnection; + + ASSERT(pConnectEle->pClientEle); + + ASSERT(BytesIndicated >= sizeof(tSESSIONHDR)); + + // this routine can get called by the next part of a large pdu, so that + // we don't always started at the begining of a pdu. The Bytes Rcvd + // value is set to zero in CompletionRcv when a new pdu is expected + // + if (pConnectEle->BytesRcvd == 0) + { + + if (pSessionHdr->Type == NBT_SESSION_MESSAGE) + { + + // + // expecting the start of a new session Pkt, so get the length out + // of the pTsdu passed in + // + pConnectEle->TotalPcktLen = myntohl(pSessionHdr->UlongLength); + + // remove the Session header by adjusting the data pointer + pTsdu = (PVOID)((PUCHAR)pTsdu + sizeof(tSESSIONHDR)); + + // shorten the number of bytes since we have stripped off the + // session header + BytesIndicated -= sizeof(tSESSIONHDR); + BytesAvailable -= sizeof(tSESSIONHDR); + *BytesTaken = sizeof(tSESSIONHDR); + } + // + // Session Keep Alive + // + else + if (pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) + { + // session keep alives are simply discarded, since the act of sending + // a keep alive indicates the session is still alive, otherwise the + // transport would report an error. + + // tell the transport that we took the Pdu + *BytesTaken = sizeof(tSESSIONHDR); + return(STATUS_SUCCESS); + + } + else + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Unexpected Session Pdu received: type = %X\n", + pSessionHdr->Type)); + + ASSERT(0); + *BytesTaken = BytesIndicated; + return(STATUS_SUCCESS); + + } + } + + // + // check if there are any receive buffers queued against this connection + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + // get the first buffer off the receive list + pRcv = RemoveHeadList(&pConnectEle->RcvHead); +#ifndef VXD + pRcvElement = CONTAINING_RECORD(pRcv,IRP,Tail.Overlay.ListEntry); + + // the cancel routine was set when this irp was posted to Nbt, so + // clear it now, since the irp is being passed to the transport + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine((PIRP)pRcvElement,NULL); + IoReleaseCancelSpinLock(OldIrq); + +#else + pRcvElement = CONTAINING_RECORD(pRcv, RCV_CONTEXT, ListEntry ) ; +#endif + + // + // this buffer is actually an Irp, so pass it back to the transport + // as a return parameter + // + *RcvBuffer = pRcvElement; + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // + // No receives on this connection. Is there a receive event handler for this + // address? + // + pClientEle = pConnectEle->pClientEle; + +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + if (pClientEle->evReceive) +#endif + { + + + // check that we have not received more data than we should for + // this session Pdu. i.e. part of the next session pdu. BytesRcvd may + // have a value other than zero if the pdu has arrived in two chunks + // and the client has taken the previous one in the indication rather + // than passing back an Irp. + // +#if DBG + DebugMore = FALSE; +#endif + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + if (BytesAvailable >= RemainingPdu) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:More Data Recvd than expecting! Avail= %X,TotalLen= %X,state=%x\n", + BytesAvailable,pConnectEle->TotalPcktLen,pLowerConn->StateRcv)); +#if DBG + DebugMore =TRUE; +#endif + // shorten the indication to the client so that they don't + // get more data than the end of the pdu + // + BytesAvailable = RemainingPdu; + if (BytesIndicated > BytesAvailable) + { + BytesIndicated = BytesAvailable; + } + // + // We always indicated at raised IRQL since we call freelockatdispatch + // below + // + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE | TDI_RECEIVE_AT_DISPATCH_LEVEL; + } + else + { + // the transport may have has this flag on. We need to + // turn it off if the entire message is not present, where entire + // message means within the bytesAvailable length. We deliberately + // use bytesavailable so that Rdr/Srv can know that the next + // indication will be a new message if they set bytestaken to + // bytesavailable. + // + ReceiveFlags &= ~TDI_RECEIVE_ENTIRE_MESSAGE; + ReceiveFlags |= TDI_RECEIVE_AT_DISPATCH_LEVEL; +#ifndef VXD + BytesAvailable = RemainingPdu; +#endif + } + + // + // NT-specific code locks pLowerConn before calling this routine, + // + CTESpinFreeAtDpc(pLowerConn); + + // call the Client Event Handler + ClientBytesTaken = 0; + status = (*pClientEle->evReceive)( + pClientEle->RcvEvContext, + pConnectEle->ConnectContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &ClientBytesTaken, + pTsdu, + &pIrp); + + CTESpinLockAtDpc(pLowerConn); +#if DBG + if (DebugMore) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(( "Client TOOK %X bytes, pIrp = %X,status =%X\n", + ClientBytesTaken,pIrp,status)); + } +#endif + if (!pLowerConn->pUpperConnection) + { + // the connection was disconnected in the interim + // so do nothing. + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + } + else + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ASSERT(pIrp); + // + // the client may pass back a receive in the pIrp. + // In this case pIrp is a valid receive request Irp + // and the status is MORE_PROCESSING + // + + // don't put these lines outside the if incase the client + // does not set ClientBytesTaken when it returns an error + // code... we don't want to use the value then + // + // count the bytes received so far. Most of the bytes + // will be received in the CompletionRcv handler in TdiHndlr.c + pConnectEle->BytesRcvd += ClientBytesTaken; + + // The client has taken some of the data at least... + *BytesTaken += ClientBytesTaken; + + *RcvBuffer = pIrp; + + // ** FAST PATH ** + return(status); + } + else + // + // no irp was returned... the client just took some of the bytes.. + // + if (status == STATUS_SUCCESS) + { + + // count the bytes received so far. + pConnectEle->BytesRcvd += ClientBytesTaken; + *BytesTaken += ClientBytesTaken; + + // + // look at how much data was taken and adjust some counts + // + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + // ** FAST PATH ** + CHECK_PTR(pConnectEle); + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + return(status); + } + else + if (pConnectEle->BytesRcvd > pConnectEle->TotalPcktLen) + { + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen)); + + ASSERTMSG("Nbt:Client Took Too Much Data!!!\n",0); + + // + // try to recover by saying that the client took all of the + // data so at least the transport is not confused too + // + *BytesTaken = BytesIndicated; + + } + else + // the client did not take all of the data so + // keep track of the fact + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT:Client took Indication BytesRcvd=%X, TotalLen=%X BytesAvail %X ClientTaken %X\n", + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen, + BytesAvailable, + ClientBytesTaken)); + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement the + // ReceiveIndicated counter which is set in Tdihndlr.c + + } + } + else + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // client has not taken ANY data... + // + // In this case the *BytesTaken is set to 4, the session hdr. + // since we really have taken that data to setup the PduSize + // in the pConnEle structure. + // + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT: Status DATA NOT ACCEPTED returned from client Avail %X %X\n", + BytesAvailable,pConnectEle)); + + // the code in tdihndlr.c normally looks after incrementing + // the ReceiveIndicated count for data that is not taken by + // the client, but if it is a zero length send that code cannot + // detect it, so we put code here to handle that case + // + // It is possible for the client to do a disconnect after + // we release the spin lock on pLowerConn to call the Client's + // disconnect indication. If that occurs, do not overwrite + // the StateProc with PartialRcv + // + if ((pConnectEle->TotalPcktLen == 0) && + (pConnectEle->state == NBT_SESSION_UP)) + { + pLowerConn->StateRcv = PARTIAL_RCV; + SetStateProc( pLowerConn, PartialRcv ) ; + CHECK_PTR(pConnectEle); + pConnectEle->ReceiveIndicated = 0; // zero bytes waiting for client + } + else + { + // + // if any bytes were taken (i.e. the session hdr) then + // return status success. (otherwise the status is + // statusNotAccpeted). + // + if (*BytesTaken) + { + status = STATUS_SUCCESS; + } + } + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement this + // counter. + } + else + ASSERT(0); + + + return(status); + + } +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + else + { + // + // there is no client buffer to pass the data to, so keep + // track of the fact so when the next client buffer comes down + // we can get the data from the transport. + // + KdPrint(("NBT:Client did not have a Buffer posted, rcvs indicated =%X,BytesRcvd=%X, TotalLen=%X\n", + pConnectEle->ReceiveIndicated, + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen)); + + // the routine calling this one increments ReceiveIndicated and sets the + // state to PartialRcv to keep track of the fact that there is data + // waiting in the transport + // + return(STATUS_DATA_NOT_ACCEPTED); + } +#endif +} + +//---------------------------------------------------------------------------- +__inline +VOID +DerefLowerConnFast( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine dereferences the lower connection and if someone has + tried to do that during the execution of the routine that called + this one, the pConnEle is dereferenced too. + +Arguments: + + +Return Value: + + +--*/ + +{ + // NOTE: we do not coordinate with the pConnEle using InRcvHandler any + // more- we just check if pUpperconnection is null or not. + // + //if (pLowerConn->InRcvHandler) + { + // pLowerConn->InRcvHandler = FALSE; + if (pLowerConn->RefCount > 1) + { + // This is the FAST PATH + pLowerConn->RefCount--; + CTESpinFree(pLowerConn,OldIrq); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + NbtDereferenceLowerConnection(pLowerConn); + + } + } +} +//---------------------------------------------------------------------------- +VOID +DpcGetRestOfIndication( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine is called when the client has been indicated with more + data than they will take and there is a rcv buffer on their RcvHead + list when completion rcv runs. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + tCONNECTELE *pConnEle; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + tLOWERCONNECTION *pLowerConn=(tLOWERCONNECTION *)Context; + PLIST_ENTRY pEntry; + + CTEMemFree((PVOID)pDpc); + + CTESpinLockAtDpc(&NbtConfig.JointLock); + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + if (!pLowerConn->pUpperConnection || pLowerConn->StateRcv != PARTIAL_RCV) + { + PUSH_LOCATION(0xA4); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + KdPrint(("Nbt:Unable to get an Irp - Closing Connection!!\n",0)); + status = OutOfRsrcKill(pLowerConn); + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + CTESpinLockAtDpc(pLowerConn); + + pConnEle = (tCONNECTELE *)pLowerConn->pUpperConnection; + + if (!IsListEmpty(&pConnEle->RcvHead)) + { + PUSH_LOCATION(0xA5); + pEntry = RemoveHeadList(&pConnEle->RcvHead); + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + // + // call the same routine that the client would call to post + // a recv buffer, except now we are in the PARTIAL_RCV state + // and the buffer will be passed to the transport. + // + status = NTReceive(pLowerConn->pDeviceContext,pIrp); + + + } + else + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + PUSH_LOCATION(0xA6); + } + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + +} + +//---------------------------------------------------------------------------- +VOID +DpcHandleNewSessionPdu ( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls HandleNewSessionPdu from a Dpc started in + NewSessionCompletionRoutine. + +Arguments: + + +Return Value: + + +--*/ + +{ + CTEMemFree((PVOID)pDpc); + + + HandleNewSessionPdu((tLOWERCONNECTION *)Context,(ULONG)SystemArgument1, + (ULONG)SystemArgument2); + +} + +//---------------------------------------------------------------------------- +VOID +HandleNewSessionPdu ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Offset, + IN ULONG ToGet + ) +/*++ + +Routine Description: + + This routine handles the case when a session pdu starts in the middle of + a data indication from the transport. It gets an Irp from the free list + and formulates a receive to pass to the transport to get that data. The + assumption is that the client has taken all data preceding the next session + pdu. If the client hasn't then this routine should not be called yet. + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + ULONG BytesTaken; + PIRP pIrp; + PFILE_OBJECT pFileObject; + PMDL pMdl; + ULONG BytesToGet; + tCONNECTELE *pConnEle; + + pIrp = NULL; + BytesTaken = 0; + + // we grab the joint lock because it is needed to separate the lower and + // upper connections, so with it we can check if they have been separated. + // + CTESpinLockAtDpc(&NbtConfig.JointLock); + pConnEle = pLowerConn->pUpperConnection; + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + if (!pLowerConn->pUpperConnection) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + KdPrint(("Nbt:Unable to get an Irp - Closing Connection!!\n",0)); + status = OutOfRsrcKill(pLowerConn); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + CTESpinLockAtDpc(pLowerConn); + // + // be sure the connection has not disconnected in the meantime... + // + if (pLowerConn->State != NBT_SESSION_UP) + { + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + pFileObject = pLowerConn->pFileObject; + + // use the indication buffer for the receive. + pMdl = pLowerConn->pIndicateMdl; + + // this flag is set below so we know if there is data in the indicate buffer + // or not. + if (Offset) + { + PVOID NewAddress; + PMDL pNewMdl; + + // there is still data in the indication buffer ,so only + // fill the empty space. This means adjusting the Mdl to + // to only map the last portion of the Indication Buffer + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pMdl) + + Offset); + + // create a partial MDL so that the new data is copied after the existing data + // in the MDL. + // + // 0 for length means map the rest of the buffer + // + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pMdl,pNewMdl,NewAddress,0); + + pMdl = pNewMdl; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Mapping IndicBuffer to partial Mdl Offset=%X, ToGet=%X %X\n", + Offset,ToGet, + pLowerConn)); + } + else + { + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + } + + // + // Only get the amount of data specified, which is either the 4 byte header + // or the rest of the pdu so that we never have + // more than one session pdu in the indicate buffer. + // + BytesToGet = ToGet; + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pFileObject), + pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pMdl, + (ULONG)TDI_RECEIVE_NORMAL, + BytesToGet); // only ask for the number of bytes left and no more + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pFileObject),pIrp); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NewSessionCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of the receive to get the remaining + data left in the transport when a session PDU starts in the middle of + an indication from the transport. This routine is run as the completion + of a recv Irp passed to the transport by NBT, to get the remainder of the + data in the transport. + + The routine then calls the normal receive handler, which can either + consume the data or pass back an Irp. If an Irp is passed back then + the data is copied into that irp in this routine. + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + ULONG BytesTaken; + tCONNECTELE *pConnEle; + PVOID pData; + KIRQL OldIrq; + PMDL pMdl; + ULONG BytesIndicated; + ULONG BytesAvailable; + PKDPC pDpc; + tLOWERCONNECTION *pLowerConn; + ULONG Length; + ULONG PduLen; + PIRP pRetIrp; + + // we grab the joint lock because it is needed to separate the lower and + // upper connections, so with it we can check if they have been separated. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pLowerConn = (tLOWERCONNECTION *)pContext; + pConnEle = pLowerConn->pUpperConnection; + + CTESpinLockAtDpc(pLowerConn); + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + // + if (!pConnEle) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + status = STATUS_UNSUCCESSFUL; + goto ExitRoutine; + } + + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + + BytesTaken = 0; + + pMdl = pLowerConn->pIndicateMdl; + + pData = MmGetMdlVirtualAddress(pMdl); + + // + // The Indication buffer may have more data in it than what we think + // was left in the transport, because the transport may have received more + // data in the intervening time. Check for this case. + // + if (pIrp->IoStatus.Information > pConnEle->BytesInXport) + { + // no data left in transport + // + CHECK_PTR(pConnEle); + pConnEle->BytesInXport = 0; + } + else + { + // + // subtract what we just retrieved from the transport, from the count + // of data left in the transport + // + pConnEle->BytesInXport -= pIrp->IoStatus.Information; + } + // put the irp back on its free list + CHECK_PTR(pIrp); + pIrp->MdlAddress = NULL; + + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + + // + // there may be data still in the indication buffer, + // so add that amount to what we just received. + // + pLowerConn->BytesInIndicate += (USHORT)pIrp->IoStatus.Information; + BytesIndicated = pLowerConn->BytesInIndicate; + // + // we need to set the bytes available to be the data in the Xport + the + // bytes in the indicate buffer, so that + // ReceiveIndicated gets set to the correct value if the client does + // not take all of data + // + BytesAvailable = pConnEle->BytesInXport + BytesIndicated; + pRetIrp = NULL; + + // if the number of bytes is 4 then we just have the header and must go + // back to the transport for the rest of the pdu, or we have a keep + // alive pdu... + // + // + // This could be a session keep alive pdu so check the pdu type. Keep + // alives just go to the RcvHndlrNotOs routine and return, doing nothing. + // They have a length of zero, so the overall length is 4 and they could + // be confused for session pdus otherwise. + // + status = STATUS_SUCCESS; + if (BytesIndicated == sizeof(tSESSIONHDR)) + { + + PUSH_LOCATION(0x1e) + if (((tSESSIONHDR UNALIGNED *)pData)->Type == NBT_SESSION_MESSAGE) + { + // if there is still data in the transport we must send down an + // irp to get the data, however, if there is no data left in + // the transport, then the data will come up on its own, into + // the indicate_buffer case in the main Receivehandler. + // + if (pConnEle->BytesInXport) + { + PUSH_LOCATION(0x1e); + + // tell the DPC routine to get the data at an offset of 4 for length Length + + // + // this is the first indication to find out how large the pdu is, so + // get the length and go get the rest of the pdu. + // + Length = myntohl(((tSESSIONHDR UNALIGNED *)pData)->UlongLength); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got Pdu Hdr in sessioncmplionroutine, PduLen =%X\n",Length)); + + // it is possible to get a zero length pdu, in which case we + // do NOT need to go to the transport to get more data + // + if (Length) + { + PUSH_LOCATION(0x1e); + // + // now go get this amount of data and add it to the header + // + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('r')); + + ASSERT(pDpc); + + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + + + CTESpinFree(pLowerConn,OldIrq); + + // check that the pdu is not going to overflow the indicate buffer. + // + if (Length > NBT_INDICATE_BUFFER_SIZE - sizeof(tSESSIONHDR)) + { + Length = NBT_INDICATE_BUFFER_SIZE - sizeof(tSESSIONHDR); + } + + ASSERTMSG("Nbt:Getting ZERO bytes from Xport!!\n",Length); + + KeInsertQueueDpc(pDpc,(PVOID)sizeof(tSESSIONHDR),(PVOID)Length); + + // clean up the partial mdl since we are going to turn around and reuse + // it in HandleNewSessionPdu above.. + // + // THIS CALL SHOULD NOT BE NEEDED SINCE THE INDICATE BUFFER IS NON_PAGED + // POOL +// MmPrepareMdlForReuse(pConnEle->pNewMdl); + + // return this status to stop to tell the io subsystem to stop processing + // this irp when we return it. + // + return(STATUS_MORE_PROCESSING_REQUIRED); + } + } + + + } + } + + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:NewSessComplRcv BytesinXport= %X,InIndicate=%X Indic. %X,Avail=%X %X\n", + pConnEle->BytesInXport,pLowerConn->BytesInIndicate,BytesIndicated, + BytesAvailable,pConnEle->pLowerConnId)); + + + if (!NT_SUCCESS(pIrp->IoStatus.Status)) + { + ASSERTMSG("Nbt:Not Expecting a Bad Status Code\n",0); + goto ExitRoutine; + } + + // + // check if we have a whole pdu in the indicate buffer or not. IF not + // then just return and wait for more data to hit the TdiReceiveHandler + // code. This check passes KeepAlives correctly since they have a pdu + // length of 0, and adding the header gives 4, their overall length. + // + PduLen = myntohl(((tSESSIONHDR UNALIGNED *)pData)->UlongLength); + if ((BytesIndicated < PduLen + sizeof(tSESSIONHDR)) && + (BytesIndicated != NBT_INDICATE_BUFFER_SIZE)) + + { + PUSH_LOCATION(0x1f); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Returning in NewSessionCompletion BytesIndicated = %X\n", + BytesIndicated)); + } + else + { + PUSH_LOCATION(0x20); + + status = CopyDataandIndicate( + NULL, + (PVOID)pLowerConn, + 0, // rcv flags + BytesIndicated, + BytesAvailable, + &BytesTaken, + pData, + (PVOID)&pRetIrp); + +// status = STATUS_MORE_PROCESSING_REQUIRED; + + } + +ExitRoutine: + // + // check if an irp is passed back, so we don't Deref in that case. + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection + // + PUSH_LOCATION(0x51); + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + } + + + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NtBuildIndicateForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine sets up the indicate buffer to get data from the transport + when the indicate buffer already has some data in it. A partial MDL is + built and the attached to the irp. + before we indicate. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PIRP pIrp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + PMDL pNewMdl; + PVOID NewAddress; + + // + // get an Irp from the list + // + + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Unable to get Irp, Kill connection\n")); + + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pConnEle= pLowerConn->pUpperConnection; + + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl) + + pLowerConn->BytesInIndicate); + + // create a partial MDL so that the new data is copied after the existing data + // in the MDL. + // + // 0 for length means map the rest of the buffer + // + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pLowerConn->pIndicateMdl,pNewMdl,NewAddress,0); + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pLowerConn->pFileObject), + pLowerConn->pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pNewMdl, + (ULONG)TDI_RECEIVE_NORMAL, + Length); + + // + // we need to set the next Irp stack location because this irp is returned + // as a return parameter rather than being passed through IoCallDriver + // which increments the stack location itself + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + *ppIrp = (PVOID)pIrp; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NtBuildIrpForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine gets an Irp to be used to receive data and hooks the indication + Mdl to it, so we can accumulate at least 128 bytes of data for the client + before we indicate. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PIRP pIrp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Unable to get Irp, Kill connection\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pLowerConn->pFileObject), + pLowerConn->pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pLowerConn->pIndicateMdl, + (ULONG)TDI_RECEIVE_NORMAL, + Length); + + // + // we need to set the next Irp stack location because this irp is returned + // as a return parameter rather than being passed through IoCallDriver + // which increments the stack location itself + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + *ppIrp = (PVOID)pIrp; + + return(STATUS_SUCCESS); +} + +#pragma inline_depth(0) +//---------------------------------------------------------------------------- +NTSTATUS +CopyDataandIndicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + + This routine combines data indicated with the indicate buffer to + indicate the total to the client. Any bytes Indicated are those bytes + in the indicate buffer. Bytes available adds in any bytes in the transport. + + The idea here is to copy as much as possible from the indicate buffer and + then pass back an irp if there is still more data in the transport. If + no data left in the transport, this routine completes the client irp and + returns STATUS_SUCCESS. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnEle; + ULONG BytesCopied; + ULONG Indicated; + ULONG Available; + ULONG Taken; + ULONG AmountAlreadyInIndicateBuffer; + PVOID pBuffer; + PIRP pIrp; + BOOLEAN bReIndicate=FALSE; + ULONG RemainingPdu; + ULONG ToCopy; + PKDPC pDpc; + ULONG SaveInXport; + ULONG PduSize; + + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pConnEle = pLowerConn->pUpperConnection; + + AmountAlreadyInIndicateBuffer = pLowerConn->BytesInIndicate; + + // + // set the parameters for the call to the TdiReceiveHandler routine + // + + Indicated = BytesIndicated; + Available = BytesAvailable; + Taken = 0; + + + ASSERT(pLowerConn->StateRcv == INDICATE_BUFFER); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Amount In Indicate = %X\n",AmountAlreadyInIndicateBuffer)); + + // now that we have 128 bytes (plus the session hdr = 132 total) we + // can indicate to the client + + pBuffer = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:FromCopyData, BytesAvail= %X,BytesInd= %X,BytesRcvd= %X,Amount=%X, %X,state=%X,RcvEC=%X\n", + Available,Indicated,pConnEle->BytesRcvd, + AmountAlreadyInIndicateBuffer,pLowerConn,pLowerConn->StateRcv, + ReceiveEventContext)); + + pIrp = NULL; + + // + // Reset this count so that the routine processes the Session header correctly + // + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + PUSH_LOCATION(0x21); + status = RcvHandlrNotOs( + NULL, + ConnectionContext, + ReceiveFlags, + Indicated, + Available, + &Taken, + pBuffer, + (PVOID)&pIrp + ); + + // + // if the connection has disonnected, then just return + // + if (!pLowerConn->pUpperConnection) + { + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + + // do not use pConnEle->TotalPcktLen here becauase it won't be set for + // keep alives - must use actual buffer to get length. + PduSize = myntohl(((tSESSIONHDR UNALIGNED *)pBuffer)->UlongLength) + sizeof(tSESSIONHDR); + + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + + if (Taken <= pLowerConn->BytesInIndicate) + { + pLowerConn->BytesInIndicate -= (USHORT)Taken; + } + else + { + pLowerConn->BytesInIndicate = 0; + } + + if (pIrp) + { + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + ULONG ClientRcvLen; + + PUSH_LOCATION(0x22); + // + // BytesInXport will be recalculated by ProcessIrp based on BytesAvailable + // and the ClientRcvLength, so set it to 0 here. + // + SaveInXport = pConnEle->BytesInXport; + CHECK_PTR(pConnEle); + pConnEle->BytesInXport = 0; + status = ProcessIrp(pLowerConn, + pIrp, + pBuffer, + &Taken, + Indicated, + Available); + + // + // copy the data in the indicate buffer that was not taken by the client + // into the MDL and then update the bytes taken and pass the irp on downwar + // to the transport + // + ToCopy = Indicated - Taken; + + // the Next stack location has the correct info in it because we + // called TdiRecieveHandler with a null ReceiveEventContext, + // so that routine does not increment the stack location + // + pIrpSp = IoGetNextIrpStackLocation(pIrp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + ClientRcvLen = pParams->ReceiveLength; + + // did the client's Pdu fit entirely into the indication buffer? + // + if (ClientRcvLen <= ToCopy) + { + PUSH_LOCATION(0x23); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Took some(or all) RemainingPdu= %X, ClientRcvLen= %X,InXport=%X %X\n", + RemainingPdu,ClientRcvLen,pConnEle->BytesInXport,pLowerConn)); + + // if ProcessIrp has recalculated the bytes in the Xport + // then set it back to where it should be, Since ProcessIrp will + // put all not taken bytes as bytes in the transport - but some + // of the bytes are still in the indicate buffer. + // + pConnEle->BytesInXport = SaveInXport; + + // it could be a zero length send where the client returns a null + // mdl, or the client returns an mdl and the RcvLen is really zero. + // + if (pIrp->MdlAddress && ClientRcvLen) + { + TdiCopyBufferToMdl(pBuffer, // indicate buffer + Taken, // src offset + ClientRcvLen, + pIrp->MdlAddress, + 0, // dest offset + &BytesCopied); + } + else + BytesCopied = 0; + + // + // check for data still in the transport - subtract data copied to + // Irp, since Taken was already subtracted. + // + pLowerConn->BytesInIndicate -= (USHORT)BytesCopied; + + *BytesTaken = Taken + BytesCopied; + ASSERT(BytesCopied == ClientRcvLen); + + // the client has received all of the data, so complete his irp + // + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // since we are completing it and TdiRcvHandler did not set the next + // one. + // + ASSERT(pIrp->CurrentLocation > 1); + + // since we are completing the irp here, no need to call + // this, because it will complete through completionrcv. + IoSetNextIrpStackLocation(pIrp); + + // there should not be any data in the indicate buffer since it + // only holds either 132 bytes or a whole pdu unless the client + // receive length is too short... + // + if (pLowerConn->BytesInIndicate) + { + PUSH_LOCATION(0x23); + // when the irp goes through completionRcv it should set the + // state to PartialRcv and the next posted buffer from + // the client should pickup this data. + CopyToStartofIndicate(pLowerConn,(Taken+BytesCopied)); + } + else + { + // + // this will complete through CompletionRcv and for that + // reason it will get any more data left in the transport. The + // Completion routine will set the correct state for the rcv when + // it processes this Irp ( to INDICATED, if needed). ProcessIrp + // may have set ReceiveIndicated, so that CompletionRcv will + // set the state to PARTIAL_RCV when it runs. + // + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + + CTESpinFreeAtDpc(pLowerConn); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTESpinLockAtDpc(pLowerConn); + // + // this was undone by CompletionRcv, so redo them, since the + // caller will undo them again. + // + pLowerConn->RefCount++; + + return(STATUS_SUCCESS); + } + else + { + + PUSH_LOCATION(0x24); + // + // there is still data that we need to get to fill the PDU. There + // may be more data left in the transport or not after the irp is + // filled. + // In either case the Irps' Mdl must be adjusted to account for + // filling part of it. + // + TdiCopyBufferToMdl(pBuffer, // IndicateBuffer + Taken, // src offset + ToCopy, + pIrp->MdlAddress, + 0, // dest offset + &BytesCopied); + + // + // save the Mdl so we can reconstruct things later + // + pLowerConn->pMdl = pIrp->MdlAddress; + pConnEle->pNextMdl = pIrp->MdlAddress; + ASSERT(pIrp->MdlAddress); + // + // The irp is being passed back to the transport, so we NULL + // our ptr to it so we don't try to cancel it on a disconnect + // + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // Adjust the number of bytes in the Mdl chain so far since the + // completion routine will only count the bytes filled in by the + // transport + // + pConnEle->BytesRcvd += BytesCopied; + + *BytesTaken = BytesIndicated; + + // + // clear the number of bytes in the indicate buffer since the client + // has taken more than the data left in the Indicate buffer + // + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + + // decrement the client rcv len by the amount already put into the + // client Mdl + // + ClientRcvLen -= BytesCopied; + // + // if ProcessIrp did recalculate the bytes in the transport + // then set back to what it was. Process irp will do this + // recalculation if the clientrcv buffer is too short only. + // + pConnEle->BytesInXport = SaveInXport; + + // + // adjust the number of bytes downward due to the client rcv + // buffer + // + if (ClientRcvLen < SaveInXport) + { + PUSH_LOCATION(0x24); + pConnEle->BytesInXport -= ClientRcvLen; + } + else + { + pConnEle->BytesInXport = 0; + } + + // ProcessIrp will set bytesinXport and ReceiveIndicated - since + // the indicate buffer is empty that calculation of BytesInXport + // will be correct. + // + + // We MUST set the state to FILL_IRP so that completion Rcv + // undoes the partial MDL stuff - i.e. it puts the original + // MdlAddress in the Irp, rather than the partial Mdl address. + // CompletionRcv will set the state to partial Rcv if ReceiveIndicated + // is not zero. + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + + // the client is going to take more data from the transport with + // this Irp. Set the new Rcv Length that accounts for the data just + // copied to the Irp. + // + pParams->ReceiveLength = ClientRcvLen; + + // keep track of data in MDL so we know when it is full and we need to + // return it to the user - ProcessIrp set it to ClientRcvLen, so + // shorten it here. + // + pConnEle->FreeBytesInMdl -= BytesCopied; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:ClientRcvLen = %X, LeftinXport= %X RemainingPdu= %X %X\n",ClientRcvLen, + pConnEle->BytesInXport,RemainingPdu,pLowerConn)); + + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + MakePartialMdl(pConnEle,pIrp,BytesCopied); + + *ppIrp = pIrp; + + // increments the stack location, since TdiReceiveHandler did not. + // + if (ReceiveEventContext) + { + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + // pass the Irp to the transport since we were called from + // NewSessionCompletionRoutine + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Calling IoCallDriver\n")); + ASSERT(pIrp->CurrentLocation > 1); + + CTESpinFreeAtDpc(pLowerConn); + CHECK_COMPLETION(pIrp); + IoCallDriver(IoGetRelatedDeviceObject(pLowerConn->pFileObject),pIrp); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + + } + } + else + { + PUSH_LOCATION(0x54); + // + // no Irp passed back, the client just took some or all of the data + // + *BytesTaken = Taken; + pLowerConn->BytesRcvd += Taken - sizeof(tSESSIONHDR); + + ASSERT(*BytesTaken < 0x7FFFFFFF ); + + // + // if more than the indicate buffer is taken, then the client + // is probably trying to say it doesn't want any more of the + // message. + // + if (Taken > BytesIndicated) + { + // + // in this case the client has taken more than the indicated. + // We set bytesavailable to the message length in RcvHndlrNotOs, + // so the client has probably said BytesTaken=BytesAvailable. + // So kill the connection + // because we have no way of handling this case here, since + // part of the message may still be in the transport, and we + // might have to send the indicate buffer down there multiple + // times to get all of it...a mess! The Rdr only sets bytestaken = + // bytesAvailable under select error conditions anyway. + // + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + *BytesTaken = BytesAvailable; + + } + else + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // this may be a zero length send -that the client has + // decided not to accept. If so then the state will be set + // to PartialRcv. In this case do NOT go down to the transport + // and get the rest of the data, but wait for the client + // to post a rcv buffer. + // + PUSH_LOCATION(0x54); + return(STATUS_SUCCESS); + } + else + if (Taken == PduSize) + { + // + // Must have taken all of the pdu data, so check for + // more data available - if so send down the indicate + // buffer to get it. + // + if (pConnEle->BytesInXport) + { + PUSH_LOCATION(0x28); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:CopyData BytesInXport= %X, %X\n",pConnEle->BytesInXport, + pLowerConn)); + + // + // there is still data in the transport so Q a Dpc to use + // the indicate buffer to get the data + // + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('s')); + + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + // get just the header first to see how large the pdu is + // + pLowerConn->RefCount++; + KeInsertQueueDpc(pDpc,NULL,(PVOID)sizeof(tSESSIONHDR)); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + + } + else + { + PUSH_LOCATION(0x29); + // + // clear the flag saying that we are using the indicate buffer + // + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + + } + + PUSH_LOCATION(0x2a); + return(STATUS_SUCCESS); + + } + else + { + // + // the client may have taken all the data in the + // indication!!, in which case return status success + // Note: that we check bytes available here not bytes + // indicated - since the client could take all indicated + // data but still leave data in the transport. If the client + // got told there was more available but only took the indicated, + // the we need to do the else and track ReceiveIndicated, but if + // Indicated == Available, then we take the if and wait for + // another indication from the transport. + // + if (Taken == BytesAvailable) + { + PUSH_LOCATION(0x4); + status = STATUS_SUCCESS; + + } + else + { + + // did not take all of the data in the Indication + // + + PUSH_LOCATION(0x2b); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Took Part of indication... BytesRemaining= %X, LeftInXport= %X, %X\n", + pLowerConn->BytesInIndicate,pConnEle->BytesInXport,pLowerConn)); + + // + // The amount of data Indicated to the client should not exceed + // the Pdu size, so check that, since this routine could get + // called with bytesAvailable > than the Pdu size. + // + // That is checked above where we check if Taken > BytesIndicated. + + SaveInXport = pConnEle->BytesInXport; + ASSERT(Taken <= PduSize); + status = ClientTookSomeOfTheData(pLowerConn, + Indicated, + Available, + Taken, + PduSize); + + // + // Since the data may be divided between some in the transport + // and some in the indicate buffer do not let ClientTookSomeOf... + // recalculate the amount in the transport, since it assumes all + // untaken data is in the transport. Since the client did not + // take of the indication, the Bytes in Xport have not changed. + // + pConnEle->BytesInXport = SaveInXport; + // + // need to move the data forward in the indicate buffer so that + // it begins at the start of the buffer + // + if (Taken) + { + CopyToStartofIndicate(pLowerConn,Taken); + } + + } + } + + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiConnectHandler ( + IN PVOID pConnectEventContext, + IN int RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID UNALIGNED pUserData, + IN int OptionsLength, + IN PVOID pOptions, + OUT CONNECTION_CONTEXT *pConnectionContext, + OUT PIRP *ppAcceptIrp + ) +/*++ + +Routine Description: + + This routine is connect event handler. It is invoked when a request for + a connection has been received by the provider. NBT accepts the connection + on one of its connections in its LowerConnFree list + + Initially a TCP connection is setup with this port. Then a Session Request + packet is sent across the connection to indicate the name of the destination + process. This packet is received in the RcvHandler. + +Arguments: + + pConnectEventContext - the context passed to the transport when this event was setup + RemoteAddressLength - the length of the source address (4 bytes for IP) + pRemoteAddress - a ptr to the source address + UserDataLength - the number of bytes of user data - includes the session Request hdr + pUserData - ptr the the user data passed in + OptionsLength - number of options to pass in + pOptions - ptr to the options + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT pFileObject; + PIRP pRequestIrp; + CONNECTION_CONTEXT pConnectionId; + tDEVICECONTEXT *pDeviceContext; + + *pConnectionContext = NULL; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)pConnectEventContext; + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("pDeviceContxt = %X ConnectEv = %X",pDeviceContext,pConnectEventContext)); + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // call the non-OS specific routine to find a free connection. + + status = ConnectHndlrNotOs( + pConnectEventContext, + RemoteAddressLength, + pRemoteAddress, + UserDataLength, + pUserData, + &pConnectionId); + + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("NO FREE CONNECTIONS in connect handler\n")); + + // put the Irp back on its free list + // + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_DATA_NOT_ACCEPTED); + } + + pFileObject = ((tLOWERCONNECTION *)pConnectionId)->pFileObject; + + TdiBuildAccept( + pRequestIrp, + IoGetRelatedDeviceObject(pFileObject), + pFileObject, + AcceptCompletionRoutine, + (PVOID)pConnectionId, + NULL, + NULL); + + // we need to null the MDL address because the transport KEEPS trying to + // release buffers!! which do not exist!!! + // + CHECK_PTR(pRequestIrp); + pRequestIrp->MdlAddress = NULL; + + + // return the connection id to accept the connect indication on. + *pConnectionContext = (CONNECTION_CONTEXT)pConnectionId; + *ppAcceptIrp = pRequestIrp; + // + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with Connect Indication. + // + ASSERT(pRequestIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pRequestIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +AcceptCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of an Accept to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + tDEVICECONTEXT *pDeviceContext; + + pLowerConn = (tLOWERCONNECTION *)pContext; + pDeviceContext = pLowerConn->pDeviceContext; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pLowerConn); + // + // if the connection disconnects before the connect accept irp (this irp) + // completes do not put back on the free list here but let nbtdisconnect + // handle it. + // (i.e if the state is no longer INBOUND, then don't touch the connection + // + if ((!NT_SUCCESS(pIrp->IoStatus.Status)) && + (pLowerConn->State == NBT_SESSION_INBOUND)) + { + + // + // the accept failed, so close the connection and create + // a new one to be sure all activity is run down on the connection. + // + + pLowerConn->State = NBT_IDLE; + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + KdPrint(("AcceptCompletionRoutine: error: %lx\n", pIrp->IoStatus.Status)); + + CTEQueueForNonDispProcessing( + NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pDeviceContext); + + PUSH_LOCATION(0x93); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + // put the Irp back on its free list + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.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 +TdiDisconnectHandler ( + IN PVOID EventContext, + IN PVOID ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID pDisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID pDisconnectInformation, + IN ULONG DisconnectIndicators + ) +/*++ + +Routine Description: + + This routine is called when a session is disconnected from a remote + machine. + +Arguments: + + IN PVOID EventContext, + IN PCONNECTION_CONTEXT ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID DisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectIndicators + +Return Value: + + NTSTATUS - Status of event indicator + +--*/ + +{ + + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)EventContext; + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("pDeviceContxt = %X ConnectEv = %X\n",pDeviceContext,ConnectionContext)); + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // call the non-OS specific routine to find a free connection. + + status = DisconnectHndlrNotOs( + EventContext, + ConnectionContext, + DisconnectDataLength, + pDisconnectData, + DisconnectInformationLength, + pDisconnectInformation, + DisconnectIndicators); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("NO FREE CONNECTIONS in connect handler\n")); + return(STATUS_DATA_NOT_ACCEPTED); + } + + + return status; + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiRcvDatagramHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT PIRP *pIoRequestPacket + ) +/*++ + +Routine Description: + + This routine is the receive datagram event indication handler. + + It is called when an Datagram arrives from the network, it will look for a + the address with an appropriate read datagram outstanding or a Datagrm + Event handler setup. + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tDGRAMHDR UNALIGNED *pDgram = (tDGRAMHDR UNALIGNED *)pTsdu; + PIRP pIrp = NULL; + ULONG lBytesTaken; + tCLIENTLIST *pClientList; + + + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT receive datagram handler pDeviceContext: %X\n", + pDeviceContext )); + + *pIoRequestPacket = NULL; + + ASSERTMSG("NBT:Invalid Device Context passed to DgramRcv Handler!!\n", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + // call a non-OS specific routine to decide what to do with the datagrams + pIrp = NULL; + pClientList = NULL; + status = DgramHndlrNotOs( + pDgramEventContext, + SourceAddressLength, + pSourceAddress, + OptionsLength, + pOptions, + ReceiveDatagramFlags, + BytesIndicated, + BytesAvailable, + &lBytesTaken, + pTsdu, + (PVOID *)&pIrp, + &pClientList); + + + if ( !NT_SUCCESS(status) ) + { + // fail the request back to the transport provider since we + // could not find a receive buffer or receive handler or the + // data was taken in the indication handler. + // + return(STATUS_DATA_NOT_ACCEPTED); + + } + else + { + // a rcv buffer was returned, so use it for the receive.(an Irp) + PTDI_REQUEST_KERNEL_RECEIVEDG pParams; + PIO_STACK_LOCATION pIrpSp; + ULONG lRcvLength; + ULONG lRcvFlags; + + // When the client list is returned, we need to make up an irp to + // send down to the transport, which we will use in the completion + // routine to copy the data to all clients, ONLY if we are not + // using a client buffer, so check that flag first. + // + if (pClientList && !pClientList->fUsingClientBuffer) + { + PMDL pMdl; + PVOID pBuffer; + + // + // get an irp to do the receive with and attach + // a buffer to it. + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + return(STATUS_DATA_NOT_ACCEPTED); + + // + // make an Mdl for a buffer to get all of the data from + // the transprot + // + pBuffer = NbtAllocMem(BytesAvailable,NBT_TAG('t')); + + pMdl = NULL; + if (pBuffer) + { + // Allocate a MDL and set the header sizes correctly + pMdl = IoAllocateMdl( + pBuffer, + BytesAvailable, + FALSE, + FALSE, + NULL); + + } + if (!pMdl) + { + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + if (pBuffer) + CTEMemFree(pBuffer); + + //ASSERT(pMdl); + KdPrint(("Nbt:Unable to get Mdl, Kill Connection\n")); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + pIrp->MdlAddress = pMdl; + ASSERT(pMdl); + + lRcvFlags = 0; + + lRcvLength = BytesAvailable; + } + else + { + ASSERT(pIrp); + // *TODO* may have to keep track of the case where the + // client returns a buffer that is not large enough for all of the + // data indicated. So the next posting of a buffer gets passed + // directly to the transport. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + lRcvFlags = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters)->ReceiveFlags; + + lRcvLength = ((PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters)->ReceiveLength; + + if (lRcvLength < BytesIndicated - lBytesTaken) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("Nbt:Clients Buffer is too short on Rcv Dgram size= %X, needed = %X\n", + lRcvLength, BytesIndicated-lBytesTaken)); + } + } + + + // this code is sped up somewhat by expanding the code here rather than calling + // the TdiBuildReceive macro + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->CompletionRoutine = CompletionRcvDgram; + + // pass the ClientList to the completion routine so it can + // copy the datagram to several clients that may be listening on the + // same name + // + pIrpSp->Context = (PVOID)pClientList; + CHECK_PTR(pIrpSp); + pIrpSp->Flags = 0; + + // set flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE_DATAGRAM; + pIrpSp->DeviceObject = pDeviceContext->pDgramDeviceObject; + pIrpSp->FileObject = pDeviceContext->pDgramFileObject; + + pParams = (PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters; + pParams->ReceiveFlags = lRcvFlags; + pParams->ReceiveLength = lRcvLength; + + // pass back the irp to the transport provider and increment the stack + // location so it can write to the irp if it needs to. + *pIoRequestPacket = pIrp; + *pBytesTaken = lBytesTaken; + + return(STATUS_MORE_PROCESSING_REQUIRED); + + } + + // + // Transport will complete the processing of the request, we don't + // want the datagram. + // + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT receive datagram handler ignored receive, pDeviceContext: %X\n", + pDeviceContext )); + + return STATUS_DATA_NOT_ACCEPTED; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiRcvNameSrvHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT PIRP *pIoRequestPacket + ) +/*++ + +Routine Description: + + This routine is the Name Service datagram event indication handler. + It gets all datagrams destined for UDP port 137 + + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tNAMEHDR UNALIGNED *pNameSrv = (tNAMEHDR UNALIGNED *)pTsdu; + USHORT OpCode; + + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT: NAMEHDR datagram handler pDeviceContext: %X\n", + pDeviceContext )); + + *pIoRequestPacket = NULL; + // + // check if the whole datagram has arrived yet + // + if (BytesIndicated != BytesAvailable) + { + PIRP pIrp; + PVOID pBuffer; + PMDL pMdl; + ULONG Length; + + // + // get an irp to do the receive with and attach + // a buffer to it. + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + return(STATUS_DATA_NOT_ACCEPTED); + + // + // make an Mdl for a buffer to get all of the data from + // the transprot + // + Length = BytesAvailable + SourceAddressLength + sizeof(ULONG); + Length = ((Length + 3)/sizeof(ULONG)) * sizeof(ULONG); + pBuffer = NbtAllocMem(Length,NBT_TAG('u')); + if (pBuffer) + { + PVOID pSrcAddr; + + // + // save the source address and length in the buffer for later + // indication back to this routine. + // + *(ULONG UNALIGNED *)((PUCHAR)pBuffer + BytesAvailable) = SourceAddressLength; + pSrcAddr = (PVOID)((PUCHAR)pBuffer + BytesAvailable + sizeof(ULONG)); + + CTEMemCopy(pSrcAddr, + pSourceAddress, + SourceAddressLength); + + // Allocate a MDL and set the header sizes correctly + pMdl = IoAllocateMdl( + pBuffer, + BytesAvailable, + FALSE, + FALSE, + NULL); + + if (pMdl) + { + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + pIrp->MdlAddress = pMdl; + ASSERT(pMdl); + + TdiBuildReceive( + pIrp, + &pDeviceContext->DeviceObject, + pDeviceContext->pNameServerFileObject, + NameSrvCompletionRoutine, + (PVOID)BytesAvailable, + pMdl, + (ULONG)TDI_RECEIVE_NORMAL, + BytesAvailable); + + *pBytesTaken = 0; + + *pIoRequestPacket = pIrp; + + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + + } + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + return(STATUS_DATA_NOT_ACCEPTED); + + } + + if (pWinsInfo) + { + USHORT TransactionId; + ULONG SrcAddress; + + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&((PTRANSPORT_ADDRESS)pSourceAddress)->Address[0].Address[0])->in_addr); + // + // Pass To Wins if: + // + // 1) It is a response pdu with the transaction id in the WINS range + // that is not a WACK... OR + // 2) It is a request that is NOT broadcast....and... + // 2) It is a name query(excluding node status requests), + // Allowing queries from other netbt clients + // allowing queries from anyone not on this machine OR + // 3) It is a name release request. OR + // 4) It is a name refresh OR + // 5) It is a name registration + // + OpCode = pNameSrv->OpCodeFlags; + TransactionId = ntohs(pNameSrv->TransactId); + + if (((OpCode & OP_RESPONSE) && + (TransactionId <= WINS_MAXIMUM_TRANSACTION_ID) && + (OpCode != OP_WACK)) + || + (!(OpCode & (OP_RESPONSE | FL_BROADCAST)) && + ( + (((OpCode & NM_FLAGS_MASK) == OP_QUERY) && + (OpCode & FL_RECURDESIRE) && // not node status request + ((TransactionId > WINS_MAXIMUM_TRANSACTION_ID) || + ( !SrcIsUs(SrcAddress) ))) +// (SrcAddress != pDeviceContext->lNameServerAddress ))) + || + (OpCode & (OP_RELEASE | OP_REFRESH)) + || + (OpCode & OP_REGISTRATION) )) ) + { + status = PassNamePduToWins( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated); + +// NbtConfig.DgramBytesRcvd += BytesIndicated; + + // + // if WINS took the data then tell the transport to dump the data + // since we have buffered it already. Otherwise, let nbt take + // a look at the data + // + if (NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + + { + + // DO a quick check of the name to see if it is in the local name table + // and reject it otherwise - for name queries only, if not the proxy + // + if ((BytesIndicated >= NBT_MINIMUM_QUERY) && !(NodeType & PROXY)) + { + ULONG UNALIGNED *pHdr; + ULONG i,lValue; + UCHAR pNameStore[NETBIOS_NAME_SIZE]; + UCHAR *pName; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + + // it must be a name query request, not a response, and not a + // node status request, to enter this special check + // + OpCode = pNameSrv->OpCodeFlags; + if (((OpCode & NM_FLAGS_MASK) == OP_QUERY) && + (!(OpCode & OP_RESPONSE)) && + (OpCode & FL_RECURDESIRE)) // not node status request + { + pHdr = (ULONG UNALIGNED *)pNameSrv->NameRR.NetBiosName; + pName = pNameStore; + + // the Half Ascii portion of the netbios name is always 32 bytes long + for (i=0; i < NETBIOS_NAME_SIZE*2 ;i +=4 ) + { + lValue = *pHdr - 0x41414141; // four A's + pHdr++; + lValue = ((lValue & 0x0F000000) >> 16) + + ((lValue & 0x0F0000) >> 4) + + ((lValue & 0x0F00) >> 8) + + ((lValue & 0x0F) << 4); + *(PUSHORT)pName = (USHORT)lValue; + ((PUSHORT)pName)++; + + } + CTESpinLock(&NbtConfig.JointLock,OldIrq); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pNameStore, + NULL, + &pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (!NT_SUCCESS(status)) + { + *pBytesTaken = BytesIndicated; + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + + + // call a non-OS specific routine to decide what to do with the datagrams + status = NameSrvHndlrNotOs( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated, + (BOOLEAN)((ReceiveDatagramFlags & TDI_RECEIVE_BROADCAST) != 0)); + +// NbtConfig.DgramBytesRcvd += BytesIndicated + + } + + return status; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NameSrvCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the case when a name service datagram is too + short and and Irp has to be passed back to the transport to get the + rest of the datagram. The irp completes through here when full. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PIRP pIoRequestPacket; + ULONG BytesTaken; + ULONG Offset = (ULONG)Context; + PVOID pBuffer; + ULONG SrcAddressLength; + PVOID pSrcAddress; + + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(("NameSrvCompletionRoutine pRcvBuffer: %X, Status: %X Length %X\n", + Context, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information )); + + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + SrcAddressLength = *(ULONG UNALIGNED *)((PUCHAR)pBuffer + Offset); + pSrcAddress = (PVOID)((PUCHAR)pBuffer + Offset + sizeof(ULONG)); + // + // just call the regular indication routine as if UDP had done it. + // + TdiRcvNameSrvHandler( + DeviceObject, + SrcAddressLength, + pSrcAddress, + 0, + NULL, + TDI_RECEIVE_NORMAL, + pIrp->IoStatus.Information, + pIrp->IoStatus.Information, + &BytesTaken, + pBuffer, + &pIoRequestPacket ); + + CTEMemFree(MmGetSystemAddressForMdl(pIrp->MdlAddress)); + + IoFreeMdl(pIrp->MdlAddress); + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRcvDgram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine completes the Irp by removing the Rcv Element off the queue + and putting it back on the free list. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PVOID pRemoteAddress; + ULONG RemoteAddressLength; + ULONG BytesCopied; + PVOID pTsdu; + ULONG ReceiveFlags; + tCLIENTLIST *pClientList; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + ULONG ClientBytesTaken; + ULONG DataLength; + tADDRESSELE *pAddress; + tRCVELE *pRcvEle; + PLIST_ENTRY pRcvEntry; + tDEVICECONTEXT *pDeviceContext; + CTEULONGLONG AdapterNumber; + + + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(("CompletionRcvDgram pRcvBuffer: %X, Status: %X Length %X\n", + Context, + Irp->IoStatus.Status, + Irp->IoStatus.Information )); + + + // there may be several clients that want to see this datagram so check + // the client list to see... + // + if (Context) + { + tCLIENTELE *pClientPrev = NULL; + + DataLength = Irp->IoStatus.Information; + + pTsdu = MmGetSystemAddressForMdl(Irp->MdlAddress); + + pClientList = (tCLIENTLIST *)Context; + + // for the multihomed host, we only want to distribute the inbound + // datagram to clients on this same adapter, to avoid giving the + // datagram to the same client several times, once for each adapter + // it is bound to. + // + pDeviceContext = pClientList->pClientEle->pDeviceContext; + AdapterNumber = pDeviceContext->AdapterNumber; + + pAddress = pClientList->pAddress; + pRemoteAddress = pClientList->pRemoteAddress; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pHead = &pClientList->pAddress->ClientHead; + pEntry = pHead->Flink; + RemoteAddressLength = pClientList->RemoteAddressLength; + ReceiveFlags = pClientList->ReceiveDatagramFlags; + +#ifdef PROXY_NODE + if (!pClientList->fProxy) + { +#endif + if (!pClientList->fUsingClientBuffer) + { + + while (pEntry != pHead) + { + PTDI_IND_RECEIVE_DATAGRAM EvRcvDgram; + PVOID RcvDgramEvContext; + tCLIENTELE *pClientEle; + PIRP pRcvIrp; + + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // for multihomed hosts only distribute the datagram to + // clients hooked to this device context to avoid duplicate + // indications + // + if (pClientEle->pDeviceContext->AdapterNumber == AdapterNumber) + { + InterlockedIncrement(&pClientEle->RefCount); + + EvRcvDgram = pClientEle->evRcvDgram; + RcvDgramEvContext = pClientEle->RcvDgramEvContext; + + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + // dereference the previous client in the list + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + + pRcvIrp = NULL; + + ClientBytesTaken = 0; + + status = (*EvRcvDgram)(RcvDgramEvContext, + RemoteAddressLength, + pRemoteAddress, + 0, + NULL, + #ifndef VXD + ReceiveFlags, + #endif + DataLength, + DataLength, + &ClientBytesTaken, + pTsdu, + &pRcvIrp); + + if (!pRcvIrp) + { + // if no buffer is returned, then the client is done + // with the data so go to the next client ...since it may + // be possible to process all clients in this loop without + // ever sending an irp down to the transport + // free the remote address mem block + + status = STATUS_DATA_NOT_ACCEPTED; + } + else + { + + // the client has passed back an irp so + // copy the data to the client's Irp + // + TdiCopyBufferToMdl(pTsdu, + ClientBytesTaken, + DataLength - ClientBytesTaken, + pRcvIrp->MdlAddress, + 0, + &BytesCopied); + + // length is copied length (since the MDL may be + // too short to take it all) + // + if (BytesCopied < (DataLength-ClientBytesTaken)) + { + pRcvIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + else + { + pRcvIrp->IoStatus.Status = STATUS_SUCCESS; + } + + pRcvIrp->IoStatus.Information = BytesCopied; + + IoCompleteRequest(pRcvIrp,IO_NETWORK_INCREMENT); + } + + CTESpinLock(&NbtConfig.JointLock, OldIrq); + + + pClientPrev = pClientEle; + } + // this code is protected from a client removing itself + // from the list of clients attached to an address by + // referencing the client prior to releasing the spin lock + // on the address. The client element does not get + // removed from the address list until its ref count goes + // to zero. We must hold the joint spin lock to prevent the + // next client from deleting itself from the list before we + // can increment its reference count. + // + pEntry = pEntry->Flink; + + + } // of while(pEntry != pHead) + + + } + else + { + // *** Client Has posted a receive Buffer, rather than using + // *** receive handler - VXD case! + // *** + while (pEntry != pHead) + { + tCLIENTELE *pClientEle; + PIRP pRcvIrp; + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // for multihomed hosts only distribute the datagram to + // clients hooked to this device context to avoid duplicate + // indications + // + if (pClientEle->pDeviceContext->AdapterNumber == AdapterNumber) + { + InterlockedIncrement(&pClientEle->RefCount); + + // check for datagrams posted to this name + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // undo the InterlockedIncrement on the refcount + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + pClientPrev = NULL; + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pClientEle == pClientList->pClientEle) + { + // this is the client whose buffer we are using - it is + // passed up to the client after all other clients + // have been processed. + // + InterlockedDecrement(&pClientEle->RefCount); + pEntry = pEntry->Flink; + continue; + } + else + if (!IsListEmpty(&pClientEle->RcvDgramHead)) + { + + pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead); + pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage); + pRcvIrp = pRcvEle->pIrp; + + // + // copy the data to the client's Irp + // + TdiCopyBufferToMdl(pTsdu, + 0, + DataLength, + pRcvIrp->MdlAddress, + 0, + &BytesCopied); + + // length is copied length (since the MDL may be too short to take it all) + if (BytesCopied < DataLength) + { + pRcvIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + else + { + pRcvIrp->IoStatus.Status = STATUS_SUCCESS; + } + + pRcvIrp->IoStatus.Information = BytesCopied; + + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + IoCompleteRequest(pRcvIrp,IO_NETWORK_INCREMENT); + + + // free the receive block + CTEMemFree((PVOID)pRcvEle); + + CTESpinLock(&NbtConfig.JointLock, OldIrq); + } + + pEntry = pEntry->Flink; + + pClientPrev = pClientEle; + } + + } // of while(pEntry != pHead) + + // free the remote address structure and the client list + // allocated in DgramHndlrNotOs + // + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + // undo the InterlockedIncrement on the refcount + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + CTEMemFree(pClientList->pRemoteAddress); + + // + // The address was referenced in DgramRcvNotOs to be sure it did not + // disappear until this dgram rcv was done, which is now. + // + NbtDereferenceAddress(pClientList->pAddress); + + CTEMemFree(Context); + + + // returning success allows the IO subsystem to complete the + // irp that we used to get the data - i.e. one client's + // buffer + // + return(STATUS_SUCCESS); + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // dereference the previous client in the list from the RcvHANDLER + // case a page or so above... + // + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + + // + // The address was referenced in DgramRcvNotOs to be sure it did not + // disappear until this dgram rcv was done, which is now. + // + NbtDereferenceAddress(pClientList->pAddress); + + +#ifdef PROXY_NODE + } + else + { + // + // Call the ProxyDoDgramDist after freeing the jointlock. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = ProxyDoDgramDist( + pTsdu, + DataLength, + (tNAMEADDR *)pClientList->pAddress, //NameAddr + pClientList->pRemoteAddress //device context + ); + + } +#endif + + + // + // Free the buffers allocated + // + CTEMemFree(pTsdu); + if (!pClientList->fProxy) + { + CTEMemFree(pClientList->pRemoteAddress); + } + CTEMemFree(Context); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("****Freeing Mdl: Irp = %X Mdl = %X\n",Irp,Irp->MdlAddress)); + IoFreeMdl(Irp->MdlAddress); + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&Irp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &Irp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_MORE_PROCESSING_REQUIRED); + + } + + // for the single receive case this passes the rcv up to the client + // + return(STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiErrorHandler ( + IN PVOID Context, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called on any error indications passed back from the + transport. It implements LAN_STATUS_ALERT. + +Arguments: + + Context - Supplies the pfcb for the address. + + Status - Supplies the error. + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + + // debug *JS + KdPrint(("Nbt: Error Event HAndler hit unexpectedly\n")); + return(STATUS_DATA_NOT_ACCEPTED); + + return STATUS_SUCCESS; +} + + +//---------------------------------------------------------------------------- +VOID +SumMdlLengths ( + IN PMDL pMdl, + IN ULONG BytesCopied, + IN tCONNECTELE *pConnectEle + ) + +/*++ + +Routine Description: + + This routine is called to sum the lengths of MDLs in a chain. + +Arguments: + + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + ULONG TotalLength; + + TotalLength = 0; + + do + { + if ((TotalLength + MmGetMdlByteCount(pMdl)) > BytesCopied) + { + pConnectEle->OffsetFromStart = BytesCopied - TotalLength; + pConnectEle->pNextMdl = pMdl; + break; + } + else + { + TotalLength += MmGetMdlByteCount(pMdl); + } + } + while (pMdl=(PMDL)pMdl->Next); + + return; +} + +//---------------------------------------------------------------------------- +NTSTATUS +AllocateMdl ( + IN tCONNECTELE *pConnEle + ) + +/*++ + +Routine Description: + + This routine is called to allocate a Mdl for the Connection. + +Arguments: + + pConnEle - ptr to the connection element + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + PMDL pNewMdl; + PVOID NewAddress; + + // + // Allocate an extra MDL for this connection if there isn't one + // already allocated. + // + if (pConnEle->pNewMdl == NULL ) + { + // The length of the + // Mdl is set to 64K(MAXUSHORT) so that there are enough pfns in the + // Mdl to map a large buffer. + // + // use the pConnEle as the Virtual address, since it doesn't matter + // because it will be overwritten when the partial Mdl is created. + // + NewAddress = pConnEle; + pNewMdl = IoAllocateMdl( + NewAddress, + MAXUSHORT, + FALSE, + FALSE,NULL); + if (!pNewMdl) + { + ASSERTMSG("Nbt:Unable to allocate a MDL!!\n",0); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pConnEle->pNewMdl = pNewMdl; + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +MakePartialMdl ( + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG ToCopy + ) + +/*++ + +Routine Description: + + This routine is called to build a partial Mdl that accounts for ToCopy + bytes of data being copied to the start of the Client's Mdl. + +Arguments: + + pConnEle - ptr to the connection element + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + PMDL pNewMdl; + PVOID NewAddress; + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + SumMdlLengths(pIrp->MdlAddress,ToCopy,pConnEle); + + // this routine has set the Mdl that the next data starts at and + // the offset from the start of that Mdl, so create a partial Mdl + // to map that buffer and tack it on the mdl chain instead of the + // original + // + pNewMdl = pConnEle->pNewMdl; + NewAddress = (PVOID)((PUCHAR)MmGetMdlVirtualAddress(pConnEle->pNextMdl) + + pConnEle->OffsetFromStart); + IoBuildPartialMdl(pConnEle->pNextMdl,pNewMdl,NewAddress,0); + + // hook the new partial mdl to the front of the MDL chain + // + pNewMdl->Next = pConnEle->pNextMdl->Next; + + pIrp->MdlAddress = pNewMdl; + ASSERT(pNewMdl); +} +//---------------------------------------------------------------------------- +VOID +CopyToStartofIndicate ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG DataTaken + ) + +/*++ + +Routine Description: + + This routine is called to copy data remaining in the indicate buffer to + the head of the indicate buffer. + +Arguments: + + pLowerConn - ptr to the lower connection element + +Return Value: + + none + +--*/ + +{ + PVOID pSrc; + ULONG DataLeft; + PVOID pMdl; + + + DataLeft = pLowerConn->BytesInIndicate; + + pMdl = (PVOID)MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + pSrc = (PVOID)( (PUCHAR)pMdl + DataTaken); + + CTEMemCopy(pMdl,pSrc,DataLeft); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTProcessAcceptIrp( + IN PIRP pIrp, + OUT tCONNECTELE **ppConnEle) + +/*++ +Routine Description: + + This Routine handles a Client's AcceptIrp, extracting the pConnEle and + returning it. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + tCONNECTELE *pConnEle; + + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + *ppConnEle = pConnEle; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- + +NTSTATUS +OutOfRsrcKill( + OUT tLOWERCONNECTION *pLowerConn) + +/*++ +Routine Description: + + This Routine handles killing a connection when an out of resource condition + occurs. It uses a special Irp that it has saved away, and a linked list + if that irp is currently in use. + +Arguments: + + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + PIRP pIrp; + PFILE_OBJECT pFileObject; + PDEVICE_OBJECT pDeviceObject; + tDEVICECONTEXT *pDeviceContext = pLowerConn->pDeviceContext; + + + CTESpinLock(pDeviceContext,OldIrq); + CTESpinLock(&NbtConfig,OldIrq1); + + InterlockedIncrement(&pLowerConn->RefCount); + if (NbtConfig.OutOfRsrc.pIrp) + { + // get an Irp to send the message in + pIrp = NbtConfig.OutOfRsrc.pIrp; + NbtConfig.OutOfRsrc.pIrp = NULL; + + pFileObject = pLowerConn->pFileObject; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + CTESpinFree(&NbtConfig,OldIrq1); + CTESpinFree(pDeviceContext,OldIrq); + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildDisconnect( + pIrp, + pDeviceObject, + pFileObject, + RsrcKillCompletion, + pLowerConn, //context value passed to completion routine + NULL, // Timeout... + TDI_DISCONNECT_ABORT, + NULL, // send connection info + NULL); // return connection info + + CHECK_PTR(pIrp); + pIrp->MdlAddress = NULL; + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(pDeviceObject,pIrp); + + IF_DBG(NBT_DEBUG_REF) + KdPrint(("Nbt:Out of Resource, Kill connection, %X\n",pLowerConn)); + + return(status); + + } + else + { + // + // The lower conn could get removed here, then get dequed from the ConnectionHead and come here + // (via DpcNextOutOfRsrcKill), and fail to get an Irp; we re-enque it into the OutOfRsrc list, + // but should not try to deque it here. + // + if (!pLowerConn->OutOfRsrcFlag) { + + RemoveEntryList(&pLowerConn->Linkage); + + // + // The lower conn gets removed from the inactive list here and again when CleanupAfterDisconnect + // calls NbtDeleteLowerConn. In order to prevent the second deque, we set a flag here and test + // for it in NbtDeleteLowerConn. + // + pLowerConn->OutOfRsrcFlag = TRUE; + } + + pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (struct _LIST_ENTRY * volatile)0x00006041; + InsertTailList(&NbtConfig.OutOfRsrc.ConnectionHead,&pLowerConn->Linkage); + + CTESpinFree(&NbtConfig,OldIrq1); + CTESpinFree(pDeviceContext,OldIrq); + } + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +RsrcKillCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a disconnect to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + KIRQL OldIrq; + PLIST_ENTRY pEntry; + tLOWERCONNECTION *pLowerConn; + PKDPC pDpc; + + + + pLowerConn = (tLOWERCONNECTION *)pContext; + + // this call will indicate the disconnect to the client and clean up + // abit. + // + status = DisconnectHndlrNotOs ( + NULL, + (PVOID)pLowerConn, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT); + + NbtDereferenceLowerConnection(pLowerConn); + + CTESpinLock(&NbtConfig,OldIrq); + NbtConfig.OutOfRsrc.pIrp = pIrp; + + if (!IsListEmpty(&NbtConfig.OutOfRsrc.ConnectionHead)) + { + if (NbtConfig.OutOfRsrc.pDpc) + { + pDpc = NbtConfig.OutOfRsrc.pDpc; + NbtConfig.OutOfRsrc.pDpc = NULL; + + pEntry = RemoveHeadList(&NbtConfig.OutOfRsrc.ConnectionHead); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + + pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (struct _LIST_ENTRY * volatile)0x00006109; + KeInitializeDpc(pDpc, + DpcNextOutOfRsrcKill, + (PVOID)pLowerConn); + + KeInsertQueueDpc(pDpc,NULL,NULL); + + CTESpinFree(&NbtConfig,OldIrq); + + } + else + CTESpinFree(&NbtConfig,OldIrq); + } + else + { + CTESpinFree(&NbtConfig,OldIrq); + } + + + + // + // 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); +} + + +//---------------------------------------------------------------------------- +VOID +DpcNextOutOfRsrcKill( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls OutOfRsrcKill from a Dpc started in + RsrcKillCompletion. + +Arguments: + + +Return Value: +--*/ +{ + + KIRQL OldIrq; + tLOWERCONNECTION *pLowerConn; + + + pLowerConn = (tLOWERCONNECTION *)Context; + + CTESpinLock(&NbtConfig,OldIrq); + NbtConfig.OutOfRsrc.pDpc = pDpc; + CTESpinFree(&NbtConfig,OldIrq); + + OutOfRsrcKill(pLowerConn); + + // + // to remove the extra reference put on pLowerConn when OutOfRsrc called + // + NbtDereferenceLowerConnection(pLowerConn); + +} + + +//---------------------------------------------------------------------------- +VOID +FillIrpCancelRoutine( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Receive Irp that has been saved + during the FILL_IRP state. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + KIRQL OldIrq; + KIRQL OldIrq1; + KIRQL OldIrq2; + PIO_STACK_LOCATION pIrpSp; + tLOWERCONNECTION *pLowerConn; + BOOLEAN CompleteIt = FALSE; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got a Receive Cancel Irp !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + // now look for an Irp to cancel + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + CTESpinLock(pConnEle,OldIrq); + + + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + CTESpinLock(pLowerConn,OldIrq2); + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + + } + + + CHECK_PTR(pConnEle); + + pConnEle->pIrpRcv = NULL; + + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq2); + } + + CTESpinFree(pConnEle,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + if (pLowerConn) + { + // + // Cancelling a Rcv Irp in the fill irp state will cause netbt + // to lose track of where it is in the message so it must kill + // the connection. + // + OutOfRsrcKill(pLowerConn); + } + return; + +} + + |