mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1719 lines
56 KiB
1719 lines
56 KiB
/*++
|
|
|
|
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 an address object (in tdiaddr.c).
|
|
|
|
This file represents the 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
|
|
John Ludeman (JohnL) 04-10-93 - Rewrote for VXD
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "nbtprocs.h"
|
|
#include "ctemacro.h"
|
|
|
|
//
|
|
// The event receive buffer takes a pointer to a flags variable that will
|
|
// always be the same, so just use the same variable
|
|
//
|
|
static USHORT usFlags = TDI_RECEIVE_NORMAL ;
|
|
|
|
VOID
|
|
AcceptCompletionRoutine(
|
|
IN PVOID pContext,
|
|
IN uint tdistatus,
|
|
IN uint extra
|
|
);
|
|
VOID
|
|
NewSessionCompletionRoutine (
|
|
IN PVOID pContext,
|
|
IN uint tdistatus,
|
|
IN uint extra
|
|
);
|
|
NTSTATUS
|
|
Reindicate(
|
|
IN PVOID ReceiveEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID pTsdu
|
|
);
|
|
|
|
//
|
|
// 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));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
TDI_STATUS
|
|
TdiReceiveHandler (
|
|
IN PVOID ReceiveEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT EventRcvBuffer * pevrcvbuf
|
|
)
|
|
/*++
|
|
|
|
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 EvenRcvBuffer *ppBuffer - Receive buffer to fill if set
|
|
|
|
|
|
Return Value:
|
|
|
|
TDI_STATUS - Status of receive operation
|
|
|
|
--*/
|
|
|
|
{
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnectEle;
|
|
PRCV_CONTEXT prcvCont = NULL ;
|
|
NTSTATUS status;
|
|
ULONG PduSize;
|
|
ULONG RemainingPdu;
|
|
|
|
DbgPrint("TRH Entered (ConnectionContext = 0x") ;
|
|
DbgPrintNum( (ULONG) ConnectionContext) ; DbgPrint(")\r\n") ;
|
|
|
|
pLowerConn = (tLOWERCONNECTION *)ConnectionContext;
|
|
|
|
pLowerConn->BytesRcvd += BytesAvailable;
|
|
|
|
//
|
|
// check if this is another part of a session pdu
|
|
//
|
|
if (pLowerConn->State == NBT_SESSION_UP)
|
|
{
|
|
DbgPrint("\tTRH: Session status is UP, Bytes: available, indicated: 0x") ;
|
|
DbgPrintNum( BytesAvailable ) ; DbgPrint(" 0x") ;
|
|
DbgPrintNum( BytesIndicated ) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
pConnectEle = pLowerConn->pUpperConnection;
|
|
pConnectEle->BytesInXport = BytesAvailable ;
|
|
*BytesTaken = 0 ;
|
|
|
|
DbgPrint("\tTRH: BytesInXport: 0x") ;
|
|
DbgPrintNum( pConnectEle->BytesInXport ) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
|
|
//
|
|
// ** RECEIVING A PDU STATE **
|
|
//
|
|
|
|
switch (pLowerConn->StateRcv)
|
|
{
|
|
case NORMAL:
|
|
//
|
|
// check indication and if less than the session header,
|
|
// copy to the session header buffer and go to Indic_buffer state
|
|
// and wait for the next indication. This is a rare case (and a pain
|
|
// in the butt).
|
|
//
|
|
if (BytesIndicated < sizeof(tSESSIONHDR))
|
|
{
|
|
ASSERT( pLowerConn->BytesInHdr == 0 ) ;
|
|
DbgPrint("\tTRH - NORMAL case: Not enough for session header, requesting 0x") ;
|
|
DbgPrintNum( sizeof( pLowerConn->Hdr ) ) ;
|
|
DbgPrint(" bytes\r\n") ;
|
|
|
|
|
|
if ( !GetRcvContext( &prcvCont))
|
|
return STATUS_INSUFFICIENT_RESOURCES ;
|
|
|
|
InitRcvContext( prcvCont, pLowerConn, NULL ) ;
|
|
prcvCont->usFlags = TDI_RECEIVE_NORMAL;
|
|
pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ;
|
|
pevrcvbuf->erb_rtn = (CTEReqCmpltRtn) NewSessionCompletionRoutine ;
|
|
pevrcvbuf->erb_size = sizeof( pLowerConn->Hdr ) ;
|
|
pevrcvbuf->erb_context = prcvCont ;
|
|
pevrcvbuf->erb_flags = &usFlags ;
|
|
InitNDISBuff( pevrcvbuf->erb_buffer,
|
|
&pLowerConn->Hdr,
|
|
sizeof(pLowerConn->Hdr),
|
|
NULL ) ;
|
|
|
|
pLowerConn->StateRcv = INDICATE_BUFFER;
|
|
|
|
//
|
|
// Okay to use pIrpRcv here as it will only be completed if
|
|
// the state is FILL_IRP
|
|
//
|
|
pConnectEle->pIrpRcv = (PCTE_IRP) prcvCont ;
|
|
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
|
|
PduSize = myntohl(((tSESSIONHDR *)pTsdu)->UlongLength)
|
|
+ sizeof(tSESSIONHDR);
|
|
DbgPrint("\tTRH: New message; Opcode, PduSize + hdr : 0x") ;
|
|
DbgPrintNum( ((tSESSIONHDR *)pTsdu)->Type ) ; DbgPrint(" 0x") ;
|
|
DbgPrintNum( PduSize ) ; DbgPrint("\r\n") ;
|
|
|
|
//
|
|
// Indicate to the client
|
|
//
|
|
ASSERT( PduSize >= sizeof(tSESSIONHDR)) ;
|
|
status = RcvHandlrNotOs(
|
|
ReceiveEventContext,
|
|
ConnectionContext,
|
|
ReceiveFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
BytesTaken,
|
|
pTsdu,
|
|
&prcvCont
|
|
);
|
|
|
|
ASSERT( *BytesTaken <= pConnectEle->BytesInXport ) ;
|
|
ASSERT( *BytesTaken <= BytesIndicated);
|
|
pConnectEle->BytesInXport -= *BytesTaken;
|
|
BytesIndicated -= *BytesTaken;
|
|
BytesAvailable -= *BytesTaken;
|
|
((BYTE*)pTsdu) += *BytesTaken ;
|
|
|
|
DbgPrint("\tTRH: RcvHandlrNotOs returned, BytesTaken: 0x") ;
|
|
DbgPrintNum( *BytesTaken ) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
DbgPrint("\tTRH: RcvHandlrNotOs status, prcvCont: 0x") ;
|
|
DbgPrintNum( status ) ; DbgPrint(" 0x") ;
|
|
DbgPrintNum( (ULONG)prcvCont ) ; DbgPrint("\r\n") ;
|
|
|
|
if ( prcvCont )
|
|
{
|
|
ULONG BytesToCopy ;
|
|
ASSERT( status == STATUS_MORE_PROCESSING_REQUIRED );
|
|
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
|
|
|
|
//
|
|
// Record which session satisfied the request
|
|
//
|
|
prcvCont->pLowerConnId = pLowerConn ;
|
|
REQUIRE( !VxdFindLSN( pConnectEle->pClientEle->pDeviceContext,
|
|
pConnectEle,
|
|
&prcvCont->pNCB->ncb_lsn )) ;
|
|
//
|
|
// New message so strip session header
|
|
//
|
|
PduSize -= *BytesTaken ;
|
|
DbgPrint("\tTRH: Remaining PduSize = 0x") ;
|
|
DbgPrintNum( PduSize ) ; DbgPrint("\r\n") ;
|
|
|
|
DbgPrint("\tTRH: TotalPcktLen = 0x") ;
|
|
DbgPrintNum( pConnectEle->TotalPcktLen ) ; DbgPrint("\r\n") ;
|
|
|
|
BytesToCopy = min( pConnectEle->TotalPcktLen,
|
|
prcvCont->ndisBuff.Length ) ;
|
|
|
|
DbgPrint("\tTRH: BytesToCopy = 0x") ;
|
|
DbgPrintNum( BytesToCopy ) ; DbgPrint("\r\n") ;
|
|
|
|
//
|
|
// pIrpRcv is set to NULL while the request is in the
|
|
// transport. This prevents two completions if an error
|
|
// occurs
|
|
//
|
|
pLowerConn->StateRcv = FILL_IRP ;
|
|
pConnectEle->pIrpRcv = NULL ;
|
|
pConnectEle->OffsetFromStart = 0 ;
|
|
|
|
//
|
|
// If the data is available, then just grab it now
|
|
// (also, if flag is set to TDI_RECEIVE_NO_RESPONSE_EXP, we
|
|
// need to give hint to the xport, so let xport do the copying)
|
|
//
|
|
if ( ( BytesIndicated >= BytesToCopy ) &&
|
|
( prcvCont->usFlags == TDI_RECEIVE_NORMAL ) )
|
|
{
|
|
CTEMemCopy( prcvCont->ndisBuff.VirtualAddress,
|
|
pTsdu,
|
|
BytesToCopy ) ;
|
|
*BytesTaken += BytesToCopy ;
|
|
CompletionRcv( prcvCont, STATUS_SUCCESS, BytesToCopy ) ;
|
|
return STATUS_SUCCESS ;
|
|
}
|
|
|
|
pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ;
|
|
pevrcvbuf->erb_rtn = CompletionRcv ;
|
|
pevrcvbuf->erb_size = BytesToCopy ;
|
|
pevrcvbuf->erb_context = prcvCont ;
|
|
if (prcvCont->usFlags == TDI_RECEIVE_NO_RESPONSE_EXP)
|
|
pevrcvbuf->erb_flags = &prcvCont->usFlags ;
|
|
else
|
|
pevrcvbuf->erb_flags = &usFlags ;
|
|
|
|
DbgPrint("\tTRH: Rcv Dest Buff: 0x") ;
|
|
DbgPrintNum((ULONG)pevrcvbuf->erb_buffer->VirtualAddress) ; DbgPrint("\r\n") ;
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
else
|
|
{
|
|
// the client received some, all or none of the data
|
|
// For Keep Alives the PduSize is zero so this check
|
|
// will work correctly and go to the else
|
|
// Also, if we were attempting to complete a zero-len message
|
|
// but failed because there was no receive pending then we
|
|
// must go in PARTIAL_RCV state
|
|
//
|
|
if ( (*BytesTaken < PduSize) ||
|
|
((status == STATUS_DATA_NOT_ACCEPTED) &&
|
|
(pConnectEle->TotalPcktLen == 0) &&
|
|
(pConnectEle->state == NBT_SESSION_UP)) )
|
|
{
|
|
//
|
|
// took some of the data, so keep track of the
|
|
// rest of the data left here by going to the PARTIALRCV
|
|
// state.
|
|
//
|
|
pLowerConn->StateRcv = PARTIAL_RCV;
|
|
InsertTailList( &pLowerConn->pDeviceContext->PartialRcvHead,
|
|
&pLowerConn->PartialRcvList ) ;
|
|
pLowerConn->fOnPartialRcvList = TRUE;
|
|
|
|
DbgPrint("TdiReceiveHandler:Switch to Partial Rcv Indicated\r\n") ;
|
|
|
|
return STATUS_SUCCESS ;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Must have taken all of the pdu data, so check for
|
|
// more data available - if so then reindicate ourselves.
|
|
// Note that TDI will pickup the bytes taken before any posted
|
|
// receives are performed.
|
|
//
|
|
status = STATUS_SUCCESS ;
|
|
|
|
//
|
|
// The next bytes in the transport will be the
|
|
// beginning of a Session Header so leave the state
|
|
// as NORMAL
|
|
//
|
|
|
|
if (BytesAvailable > *BytesTaken)
|
|
{
|
|
ULONG ClientBytesTaken = 0 ;
|
|
|
|
//
|
|
// we already added the bytes available the first time
|
|
// TdiReceiveHandler got called. They will get added
|
|
// again, so subtract now!
|
|
//
|
|
pLowerConn->BytesRcvd -= BytesAvailable;
|
|
|
|
status = TdiReceiveHandler( ReceiveEventContext,
|
|
ConnectionContext,
|
|
ReceiveFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
&ClientBytesTaken,
|
|
pTsdu,
|
|
pevrcvbuf ) ;
|
|
|
|
*BytesTaken += ClientBytesTaken ;
|
|
//
|
|
// status will be more processing if pervrcvbuf
|
|
// was setup, else it will be success if
|
|
// bytes were taken. Note that BytesInXport
|
|
// is adjusted automatically in the call to
|
|
// TdiReceiveHandler
|
|
//
|
|
}
|
|
}
|
|
}
|
|
return status ;
|
|
|
|
case FILL_IRP:
|
|
{
|
|
NCB * pNCB = pConnectEle->pIrpRcv ;
|
|
ULONG BuffAvailable = pNCB->ncb_length - pConnectEle->OffsetFromStart ;
|
|
ULONG BytesToCopy ;
|
|
|
|
// 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.
|
|
prcvCont = *((PRCV_CONTEXT*)&pNCB->ncb_reserve) ;
|
|
ASSERT( prcvCont->Signature = RCVCONT_SIGN ) ;
|
|
|
|
//
|
|
// too much data may have arrived... i.e. part of the next session pdu..
|
|
// so check and set the receive length accordingly
|
|
//
|
|
RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd;
|
|
BytesToCopy = min( RemainingPdu, BuffAvailable ) ;
|
|
pConnectEle->pIrpRcv = NULL ; // Buffer in the transport
|
|
|
|
DbgPrint("\tTRH - FILL_IRP case: Requesting 0x") ;
|
|
DbgPrintNum( pevrcvbuf->erb_size ) ; DbgPrint("\r\n") ;
|
|
|
|
//
|
|
// Append the new data onto the existing data
|
|
//
|
|
((BYTE*)prcvCont->ndisBuff.VirtualAddress) =
|
|
pNCB->ncb_buffer + pConnectEle->OffsetFromStart ;
|
|
prcvCont->ndisBuff.Length =
|
|
pNCB->ncb_length - pConnectEle->OffsetFromStart ;
|
|
|
|
//
|
|
// If the data is available, then just grab it now
|
|
//
|
|
if ( BytesIndicated >= BytesToCopy )
|
|
{
|
|
CTEMemCopy( prcvCont->ndisBuff.VirtualAddress,
|
|
pTsdu,
|
|
BytesToCopy ) ;
|
|
*BytesTaken += BytesToCopy ;
|
|
CompletionRcv( prcvCont, STATUS_SUCCESS, BytesToCopy ) ;
|
|
return STATUS_SUCCESS ;
|
|
}
|
|
|
|
//
|
|
// Have to post a buffer since the data isn't available
|
|
// We also have a new offset for the *next* indication
|
|
//
|
|
pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ;
|
|
pevrcvbuf->erb_rtn = CompletionRcv ;
|
|
pevrcvbuf->erb_size = BytesToCopy ;
|
|
|
|
pevrcvbuf->erb_context = prcvCont ;
|
|
pevrcvbuf->erb_flags = &usFlags ;
|
|
ASSERT( pConnectEle->OffsetFromStart <= pNCB->ncb_length ) ;
|
|
|
|
DbgPrint("\tTRH: offset, address: 0x") ;
|
|
DbgPrintNum( pConnectEle->OffsetFromStart ) ; DbgPrint(", 0x") ;
|
|
DbgPrintNum( (ULONG)prcvCont->ndisBuff.VirtualAddress ) ; DbgPrint("\r\n") ;
|
|
|
|
//
|
|
// State remains in FILL_IRP (only goes to PARTIAL_RCV when no
|
|
// NCBs are actively receiving and only part of a PDU has been
|
|
// picked up).
|
|
//
|
|
// BytesInXport adjusted in CompletinRcv
|
|
//
|
|
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
break ;
|
|
|
|
case INDICATE_BUFFER:
|
|
{
|
|
DbgPrint("\tTRH: Hit INDICATE_BUFFER state, bytes in hdr: 0x") ;
|
|
DbgPrintNum( pLowerConn->BytesInHdr ) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
//
|
|
// Our context is still setup so adjust things such that
|
|
// the location to start copying the new data into is right
|
|
// after the existing data in the session header buffer
|
|
//
|
|
prcvCont = (PRCV_CONTEXT) pConnectEle->pIrpRcv ;
|
|
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
|
|
((BYTE*)prcvCont->ndisBuff.VirtualAddress) =
|
|
((BYTE*)&pLowerConn->Hdr) + pLowerConn->BytesInHdr ;
|
|
prcvCont->ndisBuff.Length =
|
|
sizeof( pLowerConn->Hdr ) - pLowerConn->BytesInHdr ;
|
|
pevrcvbuf->erb_size = min( BytesAvailable,
|
|
sizeof(pLowerConn->Hdr) - pLowerConn->BytesInHdr) ;
|
|
pevrcvbuf->erb_buffer = &prcvCont->ndisBuff ;
|
|
pevrcvbuf->erb_rtn = NewSessionCompletionRoutine ;
|
|
pevrcvbuf->erb_context = prcvCont ;
|
|
pevrcvbuf->erb_flags = &usFlags ;
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
|
|
case PARTIAL_RCV:
|
|
//
|
|
// If we get indicated in this state, then the client doesn't have
|
|
// any receive buffers posted and we are in the middle of a
|
|
// PDU, so just track the new byte count and continue waiting
|
|
// for the client
|
|
//
|
|
DbgPrint("\tTRH: Indicated in Partial_Rcv state\r\n") ;
|
|
return STATUS_SUCCESS ;
|
|
|
|
default:
|
|
ASSERT( FALSE ) ;
|
|
break;
|
|
}
|
|
}
|
|
else if ( pLowerConn->State == NBT_SESSION_INBOUND )
|
|
{
|
|
status = Inbound(
|
|
ReceiveEventContext,
|
|
ConnectionContext,
|
|
ReceiveFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
BytesTaken,
|
|
pTsdu,
|
|
&prcvCont
|
|
);
|
|
}
|
|
else if ( pLowerConn->State == NBT_SESSION_OUTBOUND )
|
|
{
|
|
status = Outbound(
|
|
ReceiveEventContext,
|
|
ConnectionContext,
|
|
ReceiveFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
BytesTaken,
|
|
pTsdu,
|
|
&prcvCont
|
|
);
|
|
}
|
|
|
|
//
|
|
// maybe disconnect is going on: reject the data
|
|
//
|
|
else
|
|
{
|
|
*BytesTaken = BytesAvailable;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Client should *never* pass back a completion buffer (only used by
|
|
// event handler which isn't supported in a VXD
|
|
//
|
|
ASSERT( prcvCont == NULL ) ;
|
|
|
|
return status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
TDI_STATUS
|
|
ReceiveAnyHandler ( // Handles NCBRCVANY commands, is
|
|
IN PVOID ReceiveEventContext, // called after all other receive
|
|
IN PVOID ConnectionContext, // handlers
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID Data,
|
|
PVOID * ppBuffer // Pointer to RCV_CONTEXT
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles any data not processed by TdiReceiveHandler and
|
|
RcvHandlrNotOs. It processes ReceiveAny NCBs.
|
|
|
|
Note that TdiReceiveHandler calls RcvHandlrNotOs which may call
|
|
this routine (i.e., this is only called from RcvHandlrNotOs).
|
|
|
|
Arguments:
|
|
|
|
--*/
|
|
{
|
|
TDI_STATUS tdistatus ;
|
|
tCLIENTELE * pClientEle;
|
|
PLIST_ENTRY pEntry ;
|
|
PRCV_CONTEXT prcvCont ;
|
|
|
|
DbgPrint("ReceiveAnyHandler Entered \r\n") ;
|
|
*ppBuffer = NULL ;
|
|
|
|
pClientEle = (tCLIENTELE*) ReceiveEventContext ;
|
|
|
|
ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ) ;
|
|
|
|
//
|
|
// Are there any ReceiveAny NCBs queued for this connection or for
|
|
// any connection?
|
|
//
|
|
if ( !IsListEmpty( &pClientEle->RcvAnyHead ))
|
|
{
|
|
pEntry = RemoveHeadList( &pClientEle->RcvAnyHead ) ;
|
|
DbgPrint("ReceiveAnyHandler - Found Receive Any buffer\r\n") ;
|
|
}
|
|
else if ( !IsListEmpty( &pClientEle->pDeviceContext->RcvAnyFromAnyHead ))
|
|
{
|
|
pEntry = RemoveHeadList( &pClientEle->pDeviceContext->RcvAnyFromAnyHead ) ;
|
|
DbgPrint("ReceiveAnyHandler - Found Receive Any from Any buffer\r\n") ;
|
|
}
|
|
else
|
|
return STATUS_SUCCESS ;
|
|
//
|
|
// Found one
|
|
//
|
|
prcvCont = *ppBuffer = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
|
|
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
CompletionRcv(
|
|
IN PVOID pContext,
|
|
IN uint tdistatus,
|
|
IN uint BytesRcvd )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes TdiVxdReceive. The NCB is completed or further
|
|
receives are performed.
|
|
|
|
Arguments:
|
|
|
|
pContext - Pointer to a RCV_CONTEXT structure
|
|
tdistatus - Completion status
|
|
BytesRcvd - Bytes copied to the destination buffer
|
|
|
|
--*/
|
|
{
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnectEle;
|
|
PRCV_CONTEXT prcvcont = (PRCV_CONTEXT) pContext ;
|
|
|
|
DbgPrint("CompletionRcv Entered (BytesRcvd: 0x") ;
|
|
DbgPrintNum( BytesRcvd ) ; DbgPrint(")\r\n") ;
|
|
|
|
ASSERT( prcvcont->Signature == RCVCONT_SIGN ) ;
|
|
ASSERT( tdistatus ||
|
|
(!tdistatus && ((prcvcont->pLowerConnId != NULL) &&
|
|
(prcvcont->pLowerConnId->pUpperConnection != NULL))) ) ;
|
|
|
|
//
|
|
// If an error occurred, bail
|
|
//
|
|
if ( tdistatus && tdistatus != TDI_BUFFER_OVERFLOW )
|
|
{
|
|
DbgPrint("CompletionRcv: error occurred, status: 0x") ;
|
|
DbgPrintNum( tdistatus ) ; DbgPrint("\n\r") ;
|
|
|
|
//
|
|
// Make sure the receive IRP doesn't get completed twice if the
|
|
// connection is still up
|
|
//
|
|
if ( prcvcont->pLowerConnId &&
|
|
prcvcont->pLowerConnId->pUpperConnection )
|
|
{
|
|
prcvcont->pLowerConnId->pUpperConnection->pIrpRcv = NULL ;
|
|
}
|
|
|
|
CTEIoComplete( prcvcont->pNCB, tdistatus, 0 ) ;
|
|
return ;
|
|
}
|
|
|
|
pLowerConn = prcvcont->pLowerConnId ;
|
|
pConnectEle = pLowerConn->pUpperConnection;
|
|
|
|
ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION ) ;
|
|
pConnectEle->BytesRcvd += BytesRcvd;
|
|
pConnectEle->OffsetFromStart += BytesRcvd ;
|
|
|
|
//
|
|
// Since we request NCB buffer sizes, we can get more bytes then what
|
|
// was shown as available. If that happens then we've already consumed
|
|
// all transport bytes so reset to 0.
|
|
//
|
|
if ( pConnectEle->BytesInXport <= BytesRcvd )
|
|
pConnectEle->BytesInXport = 0 ;
|
|
else
|
|
pConnectEle->BytesInXport -= BytesRcvd ;
|
|
|
|
//
|
|
// this case handles when all bytes in a session pdu have arrived...
|
|
//
|
|
if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen)
|
|
{
|
|
//
|
|
// we have received all of the data for this message
|
|
// so complete back to the client
|
|
//
|
|
DbgPrint("CompletionRcv: PDU Receive done, about to complete client with 0x") ;
|
|
DbgPrintNum( pConnectEle->OffsetFromStart ) ; DbgPrint(" of total PDU length: 0x") ;
|
|
DbgPrintNum( pConnectEle->TotalPcktLen ) ; DbgPrint("\r\n") ;
|
|
|
|
pConnectEle->pIrpRcv = NULL ;
|
|
|
|
//
|
|
// change the state before completing the ncb because our client can
|
|
// turn around and post a hangup rightaway (in fact, net send does that!)
|
|
//
|
|
pLowerConn->StateRcv = NORMAL;
|
|
CTEIoComplete( prcvcont->pNCB, STATUS_SUCCESS, pConnectEle->OffsetFromStart ) ;
|
|
|
|
//
|
|
// Freed by CTEIoComplete, make sure we don't use it again
|
|
//
|
|
prcvcont = NULL ;
|
|
|
|
pConnectEle->OffsetFromStart = 0;
|
|
pConnectEle->BytesRcvd = 0; // reset for the next session pdu
|
|
pLowerConn->BytesInHdr = 0 ;
|
|
|
|
//
|
|
// Check if there is still more data in the transport and reindicate
|
|
// if there is.
|
|
//
|
|
if (pConnectEle->BytesInXport)
|
|
{
|
|
ULONG BytesTaken = 0 ;
|
|
DbgPrint("Nbt:ComplRcv - Bytes left in Xport after completing a receive, BytesInXport= 0x") ;
|
|
DbgPrintNum(pConnectEle->BytesInXport) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
//
|
|
// The next thing to do is copy the session header into
|
|
// pLowerConn->Hdr which this will do.
|
|
//
|
|
tdistatus = Reindicate( NULL,
|
|
pLowerConn,
|
|
0, // Rcv flags
|
|
0, // Bytes Indicated
|
|
pConnectEle->BytesInXport, // Bytes Avail
|
|
&BytesTaken,
|
|
NULL ) ; // tsdu
|
|
}
|
|
|
|
}
|
|
else
|
|
if (pConnectEle->BytesRcvd > pConnectEle->TotalPcktLen)
|
|
{
|
|
DbgPrint("CompletionRcv: Too Many Bytes Rcvd!! Rcvd 0x") ;
|
|
DbgPrintNum( pConnectEle->BytesRcvd ) ;
|
|
DbgPrint(" Total message length:0x") ;
|
|
DbgPrintNum( pConnectEle->TotalPcktLen ) ;
|
|
//DbgPrint(" NCB Buffer size: 0x") ;
|
|
//DbgPrintNum( prcvcont->pNCB->ncb_length ) ;
|
|
DbgPrint("\r\n") ;
|
|
ASSERT(FALSE);
|
|
|
|
pConnectEle->pIrpRcv = NULL ;
|
|
pLowerConn->StateRcv = NORMAL;
|
|
pConnectEle->BytesRcvd = 0; // reset for the next session pdu
|
|
pLowerConn->BytesInHdr = 0 ;
|
|
CTEIoComplete( prcvcont->pNCB, TDI_INVALID_STATE, 0 ) ;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( prcvcont->pNCB ) ;
|
|
|
|
//
|
|
// Haven't received all of the data for this PDU yet, check to
|
|
// see how much room the NCB has left in it.
|
|
//
|
|
if ( pConnectEle->OffsetFromStart >= prcvcont->pNCB->ncb_length )
|
|
{
|
|
// If OffsetFromStart is greater then the buffer size then we
|
|
// have went beyond the end of the buffer.
|
|
ASSERT( pConnectEle->OffsetFromStart == prcvcont->pNCB->ncb_length ) ;
|
|
|
|
DbgPrint("CompletionRcv: Completed NCB but more data available, Total BytesRecieved: 0x") ;
|
|
DbgPrintNum( pConnectEle->BytesRcvd ) ;
|
|
DbgPrint("\r\n Ncb buffer size completed: 0x") ;
|
|
DbgPrintNum( prcvcont->pNCB->ncb_length ) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
pConnectEle->pIrpRcv = NULL ;
|
|
pConnectEle->OffsetFromStart = 0;
|
|
pLowerConn->StateRcv = PARTIAL_RCV ;
|
|
InsertTailList( &pLowerConn->pDeviceContext->PartialRcvHead,
|
|
&pLowerConn->PartialRcvList ) ;
|
|
pLowerConn->fOnPartialRcvList = TRUE;
|
|
|
|
//
|
|
// Done with this NCB, set the status appropriately and hope
|
|
// the client will submit another NCB to pick up the rest of
|
|
// the PDU. prcvcont freed by the completion.
|
|
//
|
|
CTEIoComplete( prcvcont->pNCB,
|
|
TDI_BUFFER_OVERFLOW, //translates to: NRC_INCOMP
|
|
prcvcont->pNCB->ncb_length ) ;
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Room still left in the NCB so wait for the transport to
|
|
// indicate when more data is available (OffsetFromStart has
|
|
// already been adjusted)
|
|
//
|
|
pConnectEle->pIrpRcv = prcvcont->pNCB ;
|
|
pLowerConn->StateRcv = FILL_IRP ;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
NewSessionCompletionRoutine (
|
|
IN PVOID pContext,
|
|
IN uint tdistatus,
|
|
IN uint BytesReceived
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
Called when a partial message header is received - puts the data back
|
|
on the
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
pConnectionContext - connection context returned to the transport(connection to use)
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
|
|
{
|
|
PRCV_CONTEXT prcvCont = pContext ;
|
|
NTSTATUS status;
|
|
ULONG BytesTaken = 0;
|
|
ULONG BytesAvailable ;
|
|
tCONNECTELE *pConnEle;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
|
|
DbgPrint("NewSessionCompletionRoutine Entered\r\n") ;
|
|
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
|
|
|
|
pLowerConn = prcvCont->pLowerConnId ;
|
|
pConnEle = pLowerConn->pUpperConnection ;
|
|
|
|
//
|
|
// If an error occurred just drop the PDU on the floor (though an
|
|
// error really shouldn't happen)
|
|
//
|
|
if ( tdistatus != TDI_SUCCESS )
|
|
{
|
|
FreeRcvContext( prcvCont ) ;
|
|
pLowerConn->StateRcv = NORMAL ;
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Adjust BytesInXport if there were too few bytes for the
|
|
// session header
|
|
//
|
|
if ( BytesReceived > pConnEle->BytesInXport )
|
|
pConnEle->BytesInXport = BytesReceived ;
|
|
|
|
//
|
|
// there may be data still in the indication buffer,
|
|
// so add that amount to what we just received. The transport
|
|
// has already copied the data into the pLowerConn->Hdr so we
|
|
// just need to update the state.
|
|
//
|
|
ASSERT( BytesReceived <= sizeof( pLowerConn->Hdr ) ) ;
|
|
pLowerConn->BytesInHdr += BytesReceived ;
|
|
|
|
//
|
|
// If we have a full header, process it
|
|
//
|
|
if ( pLowerConn->BytesInHdr == sizeof(pLowerConn->Hdr) )
|
|
{
|
|
ULONG BytesTaken = 0 ;
|
|
pLowerConn->StateRcv = NORMAL ; // New session header
|
|
pLowerConn->BytesInHdr = 0 ;
|
|
FreeRcvContext( prcvCont ) ;
|
|
|
|
//
|
|
// We indicate just the session header bytes, this will force
|
|
// the client to post a receive if they are interested in the
|
|
// rest of the data
|
|
//
|
|
status = Reindicate(NULL,
|
|
pLowerConn,
|
|
0, // rcv flags
|
|
sizeof( pLowerConn->Hdr ),
|
|
pConnEle->BytesInXport,
|
|
&BytesTaken,
|
|
&pLowerConn->Hdr ) ;
|
|
ASSERT( BytesTaken <= sizeof( pLowerConn->Hdr ) ) ;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We *still* don't have the full session header so
|
|
// wait for reindication
|
|
//
|
|
return ;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS
|
|
Reindicate(
|
|
IN PVOID ReceiveEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN USHORT ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID pTsdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies data from the Indicate buffer to a 128 byte buffer and
|
|
then fills this with data from the current indication.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
NTSTATUS - Status of receive operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS ;
|
|
tLOWERCONNECTION *pLowerConn;
|
|
tCONNECTELE *pConnEle;
|
|
EventRcvBuffer evrcvbuf ;
|
|
|
|
DbgPrint("Reindicated Entered\r\n") ;
|
|
DbgPrint("\tReindicate: Bytes: available, indicated: 0x") ;
|
|
DbgPrintNum( BytesAvailable ) ; DbgPrint(" 0x") ;
|
|
DbgPrintNum( BytesIndicated ) ;
|
|
DbgPrint("\r\n") ;
|
|
pLowerConn = (tLOWERCONNECTION *)ConnectionContext;
|
|
pConnEle = pLowerConn->pUpperConnection;
|
|
|
|
//
|
|
// we already added the bytes available the first time TdiReceiveHandler
|
|
// got called. They will get added again, so subtract now!
|
|
//
|
|
pLowerConn->BytesRcvd -= BytesAvailable;
|
|
|
|
status = TdiReceiveHandler(NULL,
|
|
pLowerConn,
|
|
0, // rcv flags
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
BytesTaken,
|
|
pTsdu,
|
|
&evrcvbuf );
|
|
|
|
//
|
|
// If a receive context was returned, then post the receive, if bytes
|
|
// were taken, the state was updated in the receive handler
|
|
//
|
|
if ( status == TDI_MORE_PROCESSING )
|
|
{
|
|
TDI_REQUEST Request ;
|
|
PRCV_CONTEXT prcvCont = evrcvbuf.erb_context ;
|
|
ULONG cbRcvLength = evrcvbuf.erb_size ;
|
|
|
|
ASSERT( (evrcvbuf.erb_rtn == CompletionRcv) ||
|
|
(evrcvbuf.erb_rtn == NewSessionCompletionRoutine))
|
|
Request.RequestNotifyObject = evrcvbuf.erb_rtn ;
|
|
Request.RequestContext = prcvCont ;
|
|
Request.Handle.ConnectionContext = pLowerConn->pFileObject ;
|
|
|
|
status = TdiVxdReceive( &Request,
|
|
&usFlags,
|
|
&cbRcvLength,
|
|
&prcvCont->ndisBuff ) ;
|
|
if ( status != TDI_PENDING )
|
|
{
|
|
DbgPrint("Reindicate: Error returned from TdiVxdReceive - 0x") ;
|
|
DbgPrintNum( status ) ;
|
|
DbgPrint("\r\n") ;
|
|
CTEIoComplete( prcvCont->pNCB, status, 0 ) ;
|
|
}
|
|
else
|
|
{
|
|
status = TDI_SUCCESS ;
|
|
}
|
|
}
|
|
|
|
return status ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
TDI_STATUS
|
|
TdiConnectHandler (
|
|
IN PVOID pConnectEventContext,
|
|
IN int RemoteAddressLength,
|
|
IN PVOID pRemoteAddress,
|
|
IN int UserDataLength,
|
|
IN PVOID pUserData,
|
|
IN int OptionsLength,
|
|
IN PVOID pOptions,
|
|
IN PVOID * AcceptingID,
|
|
ConnectEventInfo * pEventInfo //OUT CONNECTION_CONTEXT *pConnectionContext
|
|
)
|
|
/*++
|
|
|
|
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;
|
|
tDEVICECONTEXT * pDeviceContext;
|
|
tLOWERCONNECTION * pLowerConn ;
|
|
PTDI_CONNECTION_INFO pConnInfo ;
|
|
|
|
DbgPrint("TdiConnectHandler: Entered\r\n") ;
|
|
|
|
// convert the context value into the device context record ptr
|
|
pDeviceContext = (tDEVICECONTEXT *)pConnectEventContext;
|
|
|
|
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 = ConnectHndlrNotOs(
|
|
pConnectEventContext,
|
|
RemoteAddressLength,
|
|
pRemoteAddress,
|
|
UserDataLength,
|
|
pUserData,
|
|
&pLowerConn );
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DbgPrint("TdiConnectHandler: NO FREE CONNECTIONS in connect handler\r\n");
|
|
return STATUS_DATA_NOT_ACCEPTED ;
|
|
}
|
|
|
|
//
|
|
// Fill in the completion information
|
|
//
|
|
pEventInfo->cei_rtn = AcceptCompletionRoutine ;
|
|
pEventInfo->cei_context = pLowerConn ;
|
|
pEventInfo->cei_acceptinfo = NULL ;
|
|
pEventInfo->cei_conninfo = NULL ;
|
|
*AcceptingID = pLowerConn ; // Connection Context
|
|
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
AcceptCompletionRoutine(
|
|
IN PVOID pContext,
|
|
IN uint tdistatus,
|
|
IN uint extra
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the completion of an Accept to the transport.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - success or not
|
|
|
|
--*/
|
|
{
|
|
tLOWERCONNECTION *pLowerConn;
|
|
|
|
DbgPrint("AcceptCompletionRoutine: Entered\r\n") ;
|
|
pLowerConn = (tLOWERCONNECTION *)pContext;
|
|
|
|
if (!NT_SUCCESS(tdistatus) &&
|
|
(pLowerConn->State == NBT_SESSION_INBOUND))
|
|
{
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
DbgPrint("AcceptCompletionRoutine - Error returned: 0x") ;
|
|
DbgPrintNum( tdistatus ) ;
|
|
DbgPrint("\r\n") ;
|
|
|
|
// the connection setup failed, so put the lower connection block
|
|
// back on the free list
|
|
pLowerConn->State = NBT_IDLE;
|
|
pDeviceContext = pLowerConn->pDeviceContext;
|
|
|
|
//
|
|
// First remove it from pDeviceContext->LowerConnection
|
|
//
|
|
RemoveEntryList( &pLowerConn->Linkage ) ;
|
|
CTEInterlockedDecrementLong(&pLowerConn->RefCount);
|
|
InsertHeadList(&pDeviceContext->LowerConnFreeHead,
|
|
&pLowerConn->Linkage);
|
|
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
TDI_STATUS
|
|
TdiDisconnectHandler (
|
|
PVOID EventContext,
|
|
PVOID ConnectionContext,
|
|
ULONG DisconnectDataLength,
|
|
PVOID pDisconnectData,
|
|
ULONG DisconnectInformationLength,
|
|
PVOID pDisconnectInformation,
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
TDI_STATUS status ;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
|
|
DbgPrint("TdiDisconnectHandler: Entered\r\n") ;
|
|
|
|
pDeviceContext = (tDEVICECONTEXT *)EventContext;
|
|
ASSERTMSG("Bad Device context passed to the Disconnect Event Handler",
|
|
pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT);
|
|
|
|
status = DisconnectHndlrNotOs(
|
|
EventContext,
|
|
ConnectionContext,
|
|
DisconnectDataLength,
|
|
pDisconnectData,
|
|
DisconnectInformationLength,
|
|
pDisconnectInformation,
|
|
DisconnectIndicators);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DbgPrint("NO FREE CONNECTIONS in connect handler\n\r");
|
|
status = TDI_CONN_REFUSED ; // return(STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
DbgPrint("TdiDisconnectHandler: returning\r\n") ;
|
|
return status ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
TDI_STATUS
|
|
VxdDisconnectHandler ( // Cleans up Netbios stuff for remote
|
|
IN PVOID DisconnectEventContext, // disconnects
|
|
IN PVOID ConnectionContext,
|
|
IN PVOID DisconnectData,
|
|
IN ULONG DisconnectInformationLength,
|
|
IN PVOID pDisconnectInformation,
|
|
IN ULONG DisconnectIndicators
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up open Netbios stuff.
|
|
|
|
Note this routine gets called from the NCB hangup completion also.
|
|
|
|
Arguments:
|
|
|
|
--*/
|
|
{
|
|
TDI_STATUS tdistatus ;
|
|
tCLIENTELE * pClientEle = (tCLIENTELE*) DisconnectEventContext ;
|
|
tCONNECTELE * pConnEle = (tCONNECTELE*) ConnectionContext ;
|
|
tDEVICECONTEXT * pDeviceContext = pClientEle->pDeviceContext ;
|
|
tLOWERCONNECTION * pLowerConn;
|
|
TDI_REQUEST Request ;
|
|
NCBERR errNCB ;
|
|
UCHAR lsn ;
|
|
BOOL fNotified ;
|
|
|
|
DbgPrint("VxdDisconnectHandler Entered \r\n") ;
|
|
ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ) ;
|
|
ASSERT( (pConnEle->Verify == NBT_VERIFY_CONNECTION) ||
|
|
(pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ;
|
|
|
|
//
|
|
// Session is dead, kill off everything
|
|
//
|
|
|
|
if ( errNCB = VxdFindLSN( pDeviceContext,
|
|
pConnEle,
|
|
&lsn ))
|
|
{
|
|
//
|
|
// This shouldn't happen but watch for it in case we get in a
|
|
// weird situation
|
|
//
|
|
DbgPrint("VxdDisconnectHandler - Warning: VxdFindLsn failed\r\n") ;
|
|
}
|
|
|
|
REQUIRE( !VxdCompleteSessionNcbs( pDeviceContext,
|
|
pConnEle )) ;
|
|
|
|
pLowerConn = pConnEle->pLowerConnId ;
|
|
if ( pLowerConn &&
|
|
(pLowerConn->fOnPartialRcvList == TRUE) &&
|
|
pLowerConn->StateRcv == PARTIAL_RCV )
|
|
{
|
|
RemoveEntryList( &pLowerConn->PartialRcvList ) ;
|
|
pLowerConn->fOnPartialRcvList = FALSE;
|
|
InitializeListHead(&pLowerConn->PartialRcvList);
|
|
}
|
|
|
|
//
|
|
// The close may free the connection so check if the client has been
|
|
// notified now.
|
|
//
|
|
fNotified = !!(pConnEle->Flags & NB_CLIENT_NOTIFIED) ;
|
|
Request.Handle.ConnectionContext = pConnEle ;
|
|
tdistatus = NbtCloseConnection( &Request,
|
|
NULL,
|
|
pDeviceContext,
|
|
NULL ) ;
|
|
if ( tdistatus )
|
|
{
|
|
DbgPrint("VxdDisconnectHandler: NbtCloseConnection returned 0x") ;
|
|
DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ;
|
|
}
|
|
|
|
tdistatus = NbtDisassociateAddress( &Request ) ;
|
|
if ( tdistatus )
|
|
{
|
|
DbgPrint("VxdDisconnectHandler: NbtDisassociateAddress returned 0x") ;
|
|
DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ;
|
|
}
|
|
|
|
if ( !errNCB && fNotified )
|
|
{
|
|
REQUIRE( NBUnregister( pDeviceContext,
|
|
lsn,
|
|
NB_SESSION )) ;
|
|
}
|
|
|
|
//
|
|
// If this name has been deleted, check to see if this is the last session,
|
|
// if so, delete the name
|
|
//
|
|
if ( pClientEle->fDeregistered && !ActiveSessions(pClientEle) )
|
|
{
|
|
UCHAR NameNum ;
|
|
if ( VxdFindNameNum( pDeviceContext, pClientEle->pAddress, &NameNum ))
|
|
{
|
|
ASSERT( FALSE ) ;
|
|
return STATUS_UNSUCCESSFUL ;
|
|
}
|
|
|
|
(void) VxdCleanupAddress( pDeviceContext,
|
|
NULL,
|
|
pClientEle,
|
|
NameNum,
|
|
TRUE ) ;
|
|
}
|
|
|
|
return STATUS_SUCCESS ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
// This structure is the context that is passed to the datagram completion
|
|
// routine when we want TDI to fill in the NCB's buffer
|
|
//
|
|
typedef struct _RCV_DG_COMP_CONTEXT
|
|
{
|
|
EventRcvBuffer evrcvbuf ;
|
|
NCB * pncb ;
|
|
tCLIENTLIST * pClientList ;
|
|
NDIS_BUFFER ndisRcvBuf ;
|
|
} RCV_DG_COMP_CONTEXT, *PRCV_DG_COMP_CONTEXT ;
|
|
|
|
TDI_STATUS
|
|
TdiRcvDatagramHandler(
|
|
IN PVOID pDgramEventContext,
|
|
IN int SourceAddressLength,
|
|
IN PVOID pSourceAddress,
|
|
IN int OptionsLength,
|
|
IN PVOID pOptions,
|
|
IN UINT Flags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT ULONG *pBytesTaken,
|
|
IN PVOID pData,
|
|
OUT EventRcvBuffer * * ppBuffer
|
|
)
|
|
/*++
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
TDI_STATUS tdistatus ;
|
|
tDEVICECONTEXT * pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext;
|
|
tCLIENTLIST * pClientList = NULL ;
|
|
NCB * pncb = NULL ;
|
|
|
|
//
|
|
// Tell TDI we don't want anything unless we change our minds down below
|
|
//
|
|
*ppBuffer = 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
|
|
tdistatus = DgramHndlrNotOs(
|
|
pDgramEventContext,
|
|
SourceAddressLength,
|
|
pSourceAddress,
|
|
OptionsLength,
|
|
pOptions,
|
|
0, // Receive data flags
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
pBytesTaken,
|
|
pData,
|
|
&pncb,
|
|
&pClientList ) ;
|
|
if ( !NT_SUCCESS( tdistatus ) )
|
|
{
|
|
// fail the request back to the transport provider since we
|
|
// could not find a receive buffer or receive handler.
|
|
return(tdistatus);
|
|
|
|
}
|
|
else
|
|
{
|
|
PRCV_DG_COMP_CONTEXT prcvdgContext = NULL ;
|
|
|
|
prcvdgContext = CTEAllocMem( sizeof( RCV_DG_COMP_CONTEXT ) ) ;
|
|
if ( !prcvdgContext )
|
|
return STATUS_DATA_NOT_ACCEPTED ;
|
|
|
|
//
|
|
// ncb_callname filled in by the NotOs event handler
|
|
//
|
|
|
|
prcvdgContext->evrcvbuf.erb_rtn = CompletionRcvDgram ;
|
|
prcvdgContext->evrcvbuf.erb_size = BytesAvailable - *pBytesTaken ;
|
|
prcvdgContext->evrcvbuf.erb_context = prcvdgContext ;
|
|
prcvdgContext->evrcvbuf.erb_buffer = &prcvdgContext->ndisRcvBuf ;
|
|
prcvdgContext->pncb = pncb ;
|
|
prcvdgContext->evrcvbuf.erb_flags = NULL ;
|
|
prcvdgContext->pClientList = pClientList ;
|
|
InitNDISBuff( prcvdgContext->evrcvbuf.erb_buffer,
|
|
pncb->ncb_buffer,
|
|
pncb->ncb_length,
|
|
NULL ) ;
|
|
*ppBuffer = &prcvdgContext->evrcvbuf ;
|
|
return TDI_MORE_PROCESSING ;
|
|
}
|
|
|
|
//
|
|
// Transport will complete the processing of the request, we don't
|
|
// want the datagram.
|
|
//
|
|
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
TDI_STATUS
|
|
TdiRcvNameSrvHandler(
|
|
IN PVOID pDgramEventContext,
|
|
IN int SourceAddressLength,
|
|
IN PVOID pSourceAddress,
|
|
IN int OptionsLength,
|
|
IN PVOID pOptions,
|
|
IN UINT Flags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT ULONG *pBytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT EventRcvBuffer * * ppBuffer
|
|
)
|
|
/*++
|
|
|
|
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;
|
|
TDI_STATUS tdistatus ;
|
|
tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext;
|
|
tNAMEHDR *pNameSrv = (tNAMEHDR *)pTsdu;
|
|
|
|
//
|
|
// No receive buffer
|
|
//
|
|
*ppBuffer = NULL ;
|
|
|
|
ASSERTMSG("NBT:The Device Context does not have the correct Verification value!!\n",
|
|
pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT );
|
|
|
|
|
|
// call a non-OS specific routine to decide what to do with the datagrams
|
|
status = NameSrvHndlrNotOs(
|
|
pDeviceContext,
|
|
pSourceAddress,
|
|
pNameSrv,
|
|
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 );
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
CompletionRcvDgram(
|
|
IN PVOID Context,
|
|
IN UINT tdistatus,
|
|
IN UINT RcvdSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes the receive datagram NCB. If multiple clients
|
|
were listenning then the largest NCB is used as a template and the
|
|
rest are completed from it.
|
|
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to a RCV_DG_COMP_CONTEXT structure set above
|
|
tdistatus - Completion status
|
|
Rcvdsize - Number of bytes copied
|
|
|
|
--*/
|
|
{
|
|
PRCV_DG_COMP_CONTEXT prcvdgContext = Context ;
|
|
NCB * pncb = prcvdgContext->pncb ;
|
|
tCLIENTLIST * pClientList = prcvdgContext->pClientList;
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pEntry;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Check to see if our buffer was big enough to get all the data
|
|
//
|
|
if ( !tdistatus &&
|
|
prcvdgContext->evrcvbuf.erb_size > pncb->ncb_length )
|
|
{
|
|
tdistatus = STATUS_BUFFER_OVERFLOW ;
|
|
}
|
|
|
|
//
|
|
// there may be several clients that want to see this datagram so check
|
|
// the client list to see...
|
|
//
|
|
if ( pClientList )
|
|
{
|
|
pHead = &pClientList->pAddress->ClientHead;
|
|
pEntry = pHead->Flink;
|
|
|
|
#ifdef PROXY_NODE
|
|
if (!pClientList->fProxy)
|
|
{
|
|
#endif
|
|
// *** Client Has posted a receive Buffer, rather than using
|
|
// *** receive handler - VXD case!
|
|
// ***
|
|
while (pEntry != pHead)
|
|
{
|
|
tCLIENTELE * pClientEle;
|
|
NCB * pncbDest ;
|
|
|
|
pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage);
|
|
|
|
CTEInterlockedIncrementLong(&pClientEle->RefCount);
|
|
|
|
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.
|
|
//
|
|
}
|
|
else
|
|
if (!IsListEmpty(&pClientEle->RcvDgramHead))
|
|
{
|
|
PLIST_ENTRY pRcvEntry;
|
|
tRCVELE * pRcvEle;
|
|
TDI_STATUS tdistatusTmp ;
|
|
UINT BytesToCopy = 0 ;
|
|
|
|
pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead);
|
|
pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage);
|
|
pncbDest = (NCB*) pRcvEle->pIrp ;
|
|
ASSERT( pncbDest ) ;
|
|
|
|
//
|
|
// Only copy the data and call name if we successfully
|
|
// completed the datagram
|
|
//
|
|
if ( !tdistatus || tdistatus == STATUS_BUFFER_OVERFLOW )
|
|
{
|
|
BytesToCopy = min( pncbDest->ncb_length, RcvdSize ) ;
|
|
|
|
CTEMemCopy( pncbDest->ncb_buffer,
|
|
pncb->ncb_buffer,
|
|
BytesToCopy ) ;
|
|
CTEMemCopy( pncbDest->ncb_callname,
|
|
pncb->ncb_callname,
|
|
NETBIOS_NAME_SIZE ) ;
|
|
|
|
if ( pncbDest->ncb_length < RcvdSize ||
|
|
(pncbDest->ncb_length == RcvdSize &&
|
|
tdistatus == STATUS_BUFFER_OVERFLOW) )
|
|
{
|
|
tdistatusTmp = STATUS_BUFFER_OVERFLOW ;
|
|
}
|
|
else
|
|
tdistatusTmp = STATUS_SUCCESS ;
|
|
}
|
|
else
|
|
{
|
|
tdistatusTmp = tdistatus ;
|
|
}
|
|
|
|
CTEIoComplete( pncbDest, tdistatusTmp, BytesToCopy ) ;
|
|
|
|
// free the receive block
|
|
CTEMemFree((PVOID)pRcvEle);
|
|
|
|
}
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
CTEInterlockedDecrementLong(&pClientEle->RefCount);
|
|
|
|
|
|
} // of while(pEntry != pHead)
|
|
|
|
//
|
|
// 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
|
|
//
|
|
status = ProxyDoDgramDist(
|
|
(tDGRAMHDR *)pncb->ncb_buffer,
|
|
RcvdSize,
|
|
(tNAMEADDR *)pClientList->pAddress, //NameAddr
|
|
pClientList->pRemoteAddress //device context
|
|
);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Free the buffers allocated
|
|
//
|
|
if (!pClientList->fProxy)
|
|
{
|
|
CTEMemFree(pClientList->pRemoteAddress);
|
|
}
|
|
CTEMemFree(Context);
|
|
}
|
|
|
|
//
|
|
// Finally complete our template NCB (or only NCB if single receive)
|
|
//
|
|
CTEIoComplete( pncb, tdistatus, RcvdSize ) ;
|
|
CTEMemFree( prcvdgContext ) ;
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
TDI_STATUS
|
|
TdiErrorHandler (
|
|
IN PVOID Context,
|
|
IN ULONG 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
|
|
|
|
--*/
|
|
|
|
{
|
|
DbgPrint("Nbt: Error Event HAndler hit unexpectedly\r\n");
|
|
return TDI_INVALID_REQUEST ;
|
|
}
|