Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4042 lines
124 KiB

/**********************************************************************/
/** Microsoft Windows/NT **/
/** Copyright(c) Microsoft Corp., 1993 **/
/**********************************************************************/
/*
VxdIsol.c
This file roughly corresponds to ntisol.c and contains VxD specific
portions of the NBT driver
FILE HISTORY:
Johnl 15-Apr-1993 Created
*/
#include <nbtprocs.h>
#include <nbtioctl.h>
//
// Used by VxdFindClientElement
//
enum CLIENT_TYPE
{
CLIENT_BC,
CLIENT_LOCAL
} ;
//
// Counts the number of items in the list Head
//
// Assumes pEntry is defined in the procedure
//
#define COUNT_ELEMENTS( Head, Count ) \
for ( pEntry = (Head).Flink ; \
pEntry != &(Head); \
pEntry = pEntry->Flink ) \
{ \
(Count)++ ; \
}
extern BOOLEAN CachePrimed;
extern BOOL fInInit;
//
// this is used for AdapterStatus and FindName calls because we need to retain
// the info, so can't have it as a local var (both these calls are synchronous
// so need not worry about stomping on this memory)
//
TA_NETBIOS_ADDRESS tanb_global ;
NCBERR VxdNameToClient( tDEVICECONTEXT * pDeviceContext,
CHAR * pName,
UCHAR * pNameNum,
tCLIENTELE * * ppClientEle ) ;
//-------------------------------------------------------------------------
//
// Allocates and frees the SESS_SETUP_CONTEXT contents (not the context
// itself).
//
TDI_STATUS AllocSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext,
BOOL fListenOnStar ) ;
void FreeSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext ) ;
NCBERR VxdInitSessionSetup( tDEVICECONTEXT * pDeviceContext,
TDI_REQUEST * pRequest,
PSESS_SETUP_CONTEXT * ppSessSetupContext,
NCB * pNCB ) ;
BOOL VxdCopySessionStatus( tDEVICECONTEXT * pDeviceContext,
tCLIENTELE * pClientEle,
PSESSION_HEADER pSessionHeader,
PSESSION_BUFFER * ppSessionBuff,
ULONG * pRemainingSize ) ;
//-------------------------------------------------------------------------
//
// NCB Reset context structures
//
//
typedef struct
{
//
// Number of active sessions we are waiting on to close
//
int cActiveSessions ;
//
// Number of active names we have to wait to finish deregistering
//
int cActiveNames ;
//
// NCB Error if reset failed (failed to resize session table for example)
//
UCHAR errncb ;
} RESET_CONTEXT, *PRESET_CONTEXT ;
NCBERR VxdResetContinue( tDEVICECONTEXT * pDeviceContext, NCB * pNCB ) ;
#define DISCONNECT_TIMEOUT 15000
//-------------------------------------------------------------------------
//
// Last valid NCB name and logical session number
//
#define MAX_NCB_NUMS 254
#define ANY_NAME 255
NTSTATUS
PostInit_Proc();
//******************* Pageable Routine Declarations ****************
#ifdef ALLOC_PRAGMA
#pragma CTEMakePageable(PAGE, PostInit_Proc)
#endif
//******************* Pageable Routine Declarations ****************
/*******************************************************************
NAME: VxdDgramSend
SYNOPSIS: Vxd specific Send Datagram code
ENTRY: pDeviceContext - Device to send the datagram on
pNCB - NCB that contains the datagram data/dest
RETURNS: NT_SUCCESS if successful, error otherwise
NOTES:
HISTORY:
Johnl 19-Apr-1993 Created
********************************************************************/
NCBERR VxdDgramSend( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
NTSTATUS status;
LONG lSentLength;
TDI_REQUEST Request;
tDGRAMHDR *pDgramHdr;
tCLIENTELE *pClientEle;
char *pName ;
NCBERR errNCB ;
TDI_CONNECTION_INFORMATION SendInfo ;
TA_NETBIOS_ADDRESS tanb ;
errNCB = VxdFindClientElement( pDeviceContext,
pNCB->ncb_num,
&pClientEle,
CLIENT_LOCAL ) ;
if ( errNCB )
{
DbgPrint("VxdDgramSend: VxdFindClientElement Failed\r\n") ;
return errNCB ;
}
ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ) ;
if ( pClientEle->fDeregistered )
return NRC_NOWILD ;
//
// If broadcast, then use "*" for destination name
//
if ( (pNCB->ncb_command & ~ASYNCH) == NCBDGSENDBC )
{
//
// Name must stay valid after call
//
static char BcastName[NCBNAMSZ] = "* " ;
pName = BcastName ;
}
else
{
pName = pNCB->ncb_callname ;
}
//
// Initialize the transport address
//
// It's Ok to pass automatic variables to NbtSendDatagram,
// the necessary data is copied out before returning
//
InitNBTDIConnectInfo( &SendInfo, &tanb, pName ) ;
Request.Handle.AddressHandle = pClientEle;
status = NbtSendDatagram(
&Request,
&SendInfo,
pNCB->ncb_length,
&lSentLength,
pNCB->ncb_buffer, // user data
pDeviceContext,
(PIRP) pNCB );
errNCB = MapTDIStatus2NCBErr( status ) ;
if ( errNCB != NRC_GOODRET && errNCB != NRC_PENDING)
{
DbgPrint("VxdDgramSend - returning ncb status 0x" ) ;
DbgPrintNum( (ULONG) errNCB ) ;
DbgPrint("\r\n") ;
}
else
{
//
// Since NbtSendDatagram always buffers datagram sends, we need to
// complete the NCB here since NbtSendDatagram will not complete
// the data gram
//
CTEIoComplete(pNCB,status,pNCB->ncb_length);
}
return errNCB ;
}
/*******************************************************************
NAME: VxdDgramReceive
SYNOPSIS: Vxd specific Datagram Receive code
ENTRY: pDeviceContext - Device to send the datagram on
pNCB - NCB that contains the datagram data/dest
RETURNS: NT_SUCCESS if successful, error otherwise
NOTES: For a receive datagram, the name number is who we want to
receive to, and the call name will be set to who we
received from. The name number may be 0xff to indicate
receive to any name from anyone.
HISTORY:
Johnl 19-Apr-1993 Created
********************************************************************/
NCBERR VxdDgramReceive( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
TDI_REQUEST Request;
ULONG ReceivedLength;
tCLIENTELE * pClientEle ;
NCBERR errNCB ;
TDI_CONNECTION_INFORMATION RcvInfo ;
TDI_CONNECTION_INFORMATION SendInfo ;
NTSTATUS status ;
if ( pNCB->ncb_num != ANY_NAME )
{
//
// For the RcvBC case, this just confirms the ncb_num is valid, the
// pClientEle is replaced with the broadcast client element (mif
// tests invalid ncb_nums with broadcasts).
//
if ( errNCB = VxdFindClientElement( pDeviceContext,
pNCB->ncb_num,
&pClientEle,
CLIENT_LOCAL ) )
{
return errNCB ;
}
if ( pClientEle->fDeregistered )
return NRC_NOWILD ;
if ( (pNCB->ncb_command & ~ASYNCH) == NCBDGRECVBC )
{
if ( errNCB = VxdFindClientElement( pDeviceContext,
pNCB->ncb_num,
&pClientEle,
CLIENT_BC ) )
if ( errNCB )
return NRC_NAMERR ;
}
Request.Handle.AddressHandle = pClientEle ;
status = NbtReceiveDatagram(
&Request,
NULL, //pTdiRequest->ReceiveDatagramInformation,
NULL, //pTdiRequest->ReturnDatagramInformation,
pNCB->ncb_length,
&ReceivedLength,
pNCB->ncb_buffer, // user data
pDeviceContext,
pNCB );
if ( status == STATUS_PENDING )
return NRC_PENDING ;
}
else
{
tRCVELE * pRcvEle = (tRCVELE *)CTEAllocMem(sizeof(tRCVELE));
if (!pRcvEle)
return NRC_NORES ;
pRcvEle->pIrp = pNCB ;
pRcvEle->ReceiveInfo = NULL ;
pRcvEle->ReturnedInfo = NULL;
pRcvEle->RcvLength = pNCB->ncb_length ;
pRcvEle->pRcvBuffer = pNCB->ncb_buffer ;
InsertTailList( &pDeviceContext->RcvDGAnyFromAnyHead,
&pRcvEle->Linkage );
return NRC_PENDING ;
}
//
// Status should always be pending or error
//
ASSERT( status != TDI_SUCCESS ) ;
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdCall
SYNOPSIS: Attempts to setup a session with the corresponding listen
ENTRY: pDeviceContext - Adapter to call on
pNCB - NCB that contains the call command
RETURNS:
NOTES: Before we can do the listen we must first open the
connection and associate the address.
The reserve field of the NCB is used as a SESS_SETUP_CONTEXT
structure
HISTORY:
Johnl 14-May-1993 Created
********************************************************************/
NCBERR VxdCall( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
NTSTATUS status;
NCBERR errNCB ;
TDI_REQUEST Request;
PSESS_SETUP_CONTEXT pSessSetupContext = NULL ;
if ( ( pNCB->ncb_name[0] == '*' )
|| ( pNCB->ncb_callname[0] == '*' ) )
{
return NRC_NOWILD ;
}
if ( errNCB = VxdInitSessionSetup( pDeviceContext,
&Request,
&pSessSetupContext,
pNCB ))
{
return errNCB ;
}
status = NbtConnect( &Request,
0, // Use system timeout
pSessSetupContext->pRequestConnect,
pSessSetupContext->pReturnConnect,
pNCB
);
if ( !NT_SUCCESS(status) )
{
VxdTearDownSession( pDeviceContext,
(tCONNECTELE*)Request.Handle.ConnectionContext,
pSessSetupContext,
NULL ) ;
}
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdSend
SYNOPSIS: Sends a netbios request
ENTRY: pDeviceContext - Adapter to call on
pNCB - NCB that contains the send command
EXIT:
RETURNS:
NOTES: pNCB->ncb_lsn - Session number to Send on
pNCB->ncb_reserved will contain a pointer to a tSESSIONHDR
for this NCB (freed in VxdIoComplete).
No-ack sends are treated like normal sends.
HISTORY:
Johnl 8-Jun-1993 Created
********************************************************************/
NCBERR VxdSend( tDEVICECONTEXT *pDeviceContext, NCB * pNCB )
{
NTSTATUS status ;
NCBERR errNCB ;
tCONNECTELE * pConnEle;
CTELockHandle OldIrq;
tLOWERCONNECTION * pLowerConn;
tSESSIONHDR * pHdr=NULL;
tBUFFERCHAINSEND SendBuff ;
TDI_REQUEST Request ;
ULONG SentSize ;
ULONG SendFlags = 0 ;
PSEND_CONTEXT pSendCont = (PSEND_CONTEXT) pNCB->ncb_reserve ;
ASSERT( sizeof(SEND_CONTEXT) <=
sizeof(pNCB->ncb_reserve)+sizeof(pNCB->ncb_event)) ;
if ( errNCB = VxdFindConnectElement( pDeviceContext,
pNCB,
&pConnEle ))
{
return errNCB ;
}
ASSERT( pConnEle->Verify == NBT_VERIFY_CONNECTION ) ;
pLowerConn = (tLOWERCONNECTION *)pConnEle->pLowerConnId ;
// check the state of the connection
if (pConnEle->state == NBT_SESSION_UP)
{
if ( GetSessionHdr( &pHdr ))
{
//
// If this is part of a chain send, set up the 2nd buffer
//
if ( ((pNCB->ncb_command & ~ASYNCH) == NCBCHAINSEND) ||
((pNCB->ncb_command & ~ASYNCH) == NCBCHAINSENDNA) )
{
SendBuff.Length2 = *((WORD*)pNCB->ncb_callname) ;
SendBuff.pBuffer2 = *((PUCHAR*)(pNCB->ncb_callname+2)) ;
SendFlags |= CHAIN_SEND_FLAG ;
DbgPrint("VxdSend - Doing chain send\r\n") ;
}
else
{
SendBuff.Length2 = 0 ;
}
pHdr->Type = NBT_SESSION_MESSAGE ;
pHdr->Flags = NBT_SESSION_FLAGS ;
pHdr->UlongLength = htonl(pNCB->ncb_length + SendBuff.Length2) ;
pSendCont->pHdr = pHdr ;
//
// Only sends that can time out are put on the timeout list
//
if ( (pSendCont->STO = pConnEle->STO) != NCB_INFINITE_TIME_OUT )
{
InsertTailList( &NbtConfig.SendTimeoutHead, &pSendCont->ListEntry ) ;
}
SendBuff.tBuff.pDgramHdr = pHdr ;
SendBuff.tBuff.HdrLength = sizeof( *pHdr ) ;
SendBuff.tBuff.pBuffer = pNCB->ncb_buffer ;
SendBuff.tBuff.Length = pNCB->ncb_length ;
#ifdef DEBUG
if ( !pNCB->ncb_length ) // Make sure 0 length buffers do
SendBuff.tBuff.pBuffer = NULL ; // the right thing
#endif
Request.RequestNotifyObject = VxdIoComplete ;
Request.RequestContext = pNCB ;
Request.Handle.ConnectionContext = pConnEle->pLowerConnId->pFileObject ;
status = TdiSend( &Request,
0,
(USHORT) SendBuff.tBuff.HdrLength +
SendBuff.tBuff.Length + SendBuff.Length2,
&SentSize,
(tBUFFER*) &SendBuff,
SendFlags ) ;
ASSERT( !NT_SUCCESS( status ) ||
(SentSize == (SendBuff.tBuff.HdrLength +
SendBuff.tBuff.Length + SendBuff.Length2)) ) ;
pLowerConn->BytesSent += SentSize;
return NRC_PENDING ;
//
// if TdiSend fails, it will call the completion routine (directly
// or eventually, Vxdiocomplete) which will remove this from the list
// so, don't remove it here also or we overwrite redir's code segment!!
//
// //
// // Remove from the timeout list if an error occurred
// //
// if ( !NT_SUCCESS( status ) &&
// pConnEle->STO != NCB_INFINITE_TIME_OUT )
// {
// RemoveEntryList( &pSendCont->ListEntry ) ;
// }
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES ;
goto ErrorExit ;
}
}
else
{
status = TDI_INVALID_CONNECTION ;
}
if ( !NT_SUCCESS( status ) )
goto ErrorExit ;
return MapTDIStatus2NCBErr( status ) ;
ErrorExit:
if ( pHdr )
FreeSessionHdr( pHdr ) ;
DbgPrint("VxdSend returning NCB error: 0x") ;
DbgPrintNum( MapTDIStatus2NCBErr( status ) ) ; DbgPrint("\r\n") ;
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdReceiveAny
SYNOPSIS: Handles a request to accept data from any open session
ENTRY: pDeviceContext - Adapter to call on
pNCB - NCB that contains the receive command
EXIT:
RETURNS:
NOTES: pNCB->ncb_lsn - Session set to who to receive from if
an indication is found
The most common case (for WFW rdr) is to Receive on
a particular name number w/o any waiting connections
HISTORY:
Johnl 8-Jun-1993 Created
********************************************************************/
NCBERR VxdReceiveAny( tDEVICECONTEXT *pDeviceContext, NCB * pNCB )
{
NTSTATUS status;
NCBERR errNCB ;
tLOWERCONNECTION * pLowerConn;
tCLIENTELE * pClientEle = NULL ;
PLIST_ENTRY pEntry, pHead ;
#ifdef DEBUG
DbgPrint("VxdReceiveAny posted: Ncb length, Rcv Buff Address: 0x") ;
DbgPrintNum( pNCB->ncb_length ) ; DbgPrint(", 0x") ;
DbgPrintNum( (ULONG) pNCB->ncb_buffer ) ; DbgPrint("\r\n") ;
#endif
//
// If they've given us a name number to receive on, find it
//
if ( pNCB->ncb_num != ANY_NAME )
{
if ( errNCB = VxdFindClientElement( pDeviceContext,
pNCB->ncb_num,
&pClientEle,
CLIENT_LOCAL ) )
{
DbgPrint("VxdReceiveAny - Couldn't find name number\r\n") ;
return errNCB ;
}
if ( !pClientEle->fDeregistered )
{
if ( IsListEmpty( &pDeviceContext->PartialRcvHead ) )
{
goto QueueRcv ;
}
}
else
{
return NRC_NOWILD ;
}
}
else
{
if ( IsListEmpty( &pDeviceContext->PartialRcvHead ) )
{
goto QueueRcv ;
}
}
//
// Scan for all active sessions looking for one that has indicated
// data that will satisfy this ReceiveAny
//
pHead = &pDeviceContext->PartialRcvHead ;
pEntry = pHead->Flink ;
ASSERT( pEntry );
while ( pEntry != pHead )
{
DbgPrint("VxdReceiveAny: scanning lower connections for partial receive\r\n") ;
pLowerConn = CONTAINING_RECORD( pEntry, tLOWERCONNECTION, PartialRcvList ) ;
ASSERT( pLowerConn->State < NBT_DISCONNECTING );
//
// If Receive any from any, then the first one we find
// will work, otherwise compare the names
//
if ( pNCB->ncb_num == ANY_NAME )
break ;
else
{
if ( CTEMemCmp( pClientEle->pAddress->pNameAddr->Name,
pLowerConn->pUpperConnection->pClientEle->
pAddress->pNameAddr->Name,
NETBIOS_NAME_SIZE ) == NETBIOS_NAME_SIZE )
{
break ;
}
}
pEntry = pEntry->Flink ;
}
if ( pEntry != pHead )
{
DbgPrint("VxdReceiveAny: Found partial receive, calling VxdReceive\r\n") ;
ASSERT (pLowerConn->fOnPartialRcvList == TRUE);
RemoveEntryList( &pLowerConn->PartialRcvList ) ;
pLowerConn->fOnPartialRcvList = FALSE;
InitializeListHead(&pLowerConn->PartialRcvList);
//
// Now find the session number this receive is taking place on
//
if ( errNCB = VxdFindLSN( pDeviceContext,
pLowerConn->pUpperConnection,
&pNCB->ncb_lsn ))
{
return errNCB ;
}
return VxdReceive( pDeviceContext, pNCB, FALSE ) ;
}
else
{
//
// Nothing active so queue it
//
PRCV_CONTEXT prcvCont ;
QueueRcv:
if ( !GetRcvContext( &prcvCont ))
return NRC_NORESOURCES ;
InitRcvContext( prcvCont, NULL, pNCB ) ;
InitNDISBuff( &prcvCont->ndisBuff,
pNCB->ncb_buffer,
pNCB->ncb_length,
NULL ) ;
prcvCont->usFlags = TDI_RECEIVE_NORMAL;
*((PRCV_CONTEXT*)&pNCB->ncb_reserve) = prcvCont ;
if ( pNCB->ncb_num != ANY_NAME )
{
ASSERT( pClientEle != NULL ) ;
InsertTailList( &pClientEle->RcvAnyHead,
&prcvCont->ListEntry ) ;
return NRC_PENDING ;
}
else
{
InsertTailList( &pDeviceContext->RcvAnyFromAnyHead,
&prcvCont->ListEntry ) ;
}
}
return NRC_PENDING ;
}
/*******************************************************************
NAME: VxdReceive
SYNOPSIS: Worker for VxdReceive and VxdReceiveAny
ENTRY: pDeviceContext - Adapter to call on
pNCB - NCB that contains the receive command
fReceive - TRUE if we got here via a Receive ncb
- FALSE if we got here via a ReceiveAny ncb
EXIT:
RETURNS:
NOTES: pNCB->ncb_reserved will contain a pointer to a RCV_CONTEXT
for this NCB.
VxdReceiveAny calls this on an element where state==NBT_SESSION_UP
and StateRcv == PARTIAL_RCV, thus the receive context should
never be added to pConnele->RcvHead.
HISTORY:
Johnl 8-Jun-1993 Created
********************************************************************/
NCBERR VxdReceive( tDEVICECONTEXT * pDeviceContext, NCB * pNCB, BOOL fReceive )
{
NTSTATUS status;
NCBERR errNCB ;
tCONNECTELE * pConnEle;
CTELockHandle OldIrq;
tLOWERCONNECTION * pLowerConn;
PRCV_CONTEXT prcvCont ;
if ( errNCB = VxdFindConnectElement( pDeviceContext,
pNCB,
&pConnEle ))
{
return errNCB ;
}
ASSERT( pConnEle->Verify == NBT_VERIFY_CONNECTION ) ;
pLowerConn = pConnEle->pLowerConnId;
DbgPrint("VxdReceive posted: Ncb length, Rcv buff Address: 0x") ;
DbgPrintNum( pNCB->ncb_length ) ; DbgPrint(", 0x") ;
DbgPrintNum( (ULONG) pNCB->ncb_buffer ) ; DbgPrint("\r\n") ;
//
// Setup the receive context tracker
//
if ( GetRcvContext( &prcvCont ))
{
InitRcvContext( prcvCont, pLowerConn, pNCB ) ;
InitNDISBuff( &prcvCont->ndisBuff,
pNCB->ncb_buffer,
pNCB->ncb_length,
NULL ) ;
prcvCont->RTO = pConnEle->RTO ;
prcvCont->usFlags = TDI_RECEIVE_NORMAL;
*((PRCV_CONTEXT*)&pNCB->ncb_reserve) = prcvCont ;
//
// If data is not available, queue the request, otherwise get the
// data
//
if ( pLowerConn->StateRcv != PARTIAL_RCV )
{
//
// Make sure a RcvAny didn't get to here
//
ASSERT( (pNCB->ncb_command & ~ASYNCH)== NCBRECV ) ;
if ( !pConnEle->Orig && fReceive )
{
prcvCont->usFlags = TDI_RECEIVE_NO_RESPONSE_EXP ;
}
InsertTailList(&pConnEle->RcvHead,
&prcvCont->ListEntry);
return NRC_PENDING ;
}
else
{
TDI_REQUEST Request ;
UINT cbReceiveLength ;
static USHORT usFlags = TDI_RECEIVE_NORMAL ;
DbgPrint("VxdReceive:A Rcv Buffer posted when data in the transport, InXport= 0x") ;
DbgPrintNum( pConnEle->BytesInXport ) ;
DbgPrint("\r\n") ;
pConnEle->OffsetFromStart = 0 ;
Request.RequestNotifyObject = CompletionRcv ;
Request.RequestContext = prcvCont ;
Request.Handle.ConnectionContext = pLowerConn->pFileObject ;
pConnEle->pIrpRcv = NULL ; // Buffer in the transport
pLowerConn->StateRcv = FILL_IRP ;
RemoveEntryList( &pLowerConn->PartialRcvList ) ;
pLowerConn->fOnPartialRcvList = FALSE;
InitializeListHead(&pLowerConn->PartialRcvList);
cbReceiveLength = min( pNCB->ncb_length,
pConnEle->TotalPcktLen-pConnEle->BytesRcvd ) ;
//
// Don't pass zero length buffers to the transport
//
if ( !cbReceiveLength )
{
CompletionRcv( prcvCont, STATUS_SUCCESS, 0 ) ;
return NRC_GOODRET ;
}
//
// if it's an incoming session and this is a receive (as opp. to
// receive-any) then give transport a hint that there is no response
// coming back (trying to solve the raw-write-to-smb-server-perf problem)
//
if ( !pConnEle->Orig && fReceive )
{
usFlags = TDI_RECEIVE_NO_RESPONSE_EXP ;
}
status = TdiVxdReceive( &Request,
&usFlags,
&cbReceiveLength,
&prcvCont->ndisBuff ) ;
if ( status == STATUS_PENDING )
return NRC_PENDING ;
//
// Should always get pending unless a real error occurs
//
if ( !NT_SUCCESS(status) )
{
DbgPrint("VxdReceive - TdiReceive failed, error 0x") ;
DbgPrintNum( status ) ;
DbgPrint("\r\n") ;
CTEIoComplete( pNCB, status, 0 ) ;
}
}
}
else
return NRC_NORESOURCES ;
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdHangup
SYNOPSIS: Sets up a session Hangup
ENTRY: pDeviceContext -
pNCB - NCB that contains Hangup command
RETURNS:
NOTES: The code is similar to VxdDisconnectHandler. If this
changes, the VxdDisconnectHandler will probably have to
change
HISTORY:
Johnl 12-Jul-1993 Created
********************************************************************/
NCBERR VxdHangup( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
TDI_STATUS tdistatus ;
tCONNECTELE * pConnEle ;
NCBERR errNCB ;
TDI_REQUEST Request ;
ULONG TimeOut = DISCONNECT_TIMEOUT ;
tCLIENTELE * pClientEle ;
tLOWERCONNECTION * pLowerConn;
if ( errNCB = VxdFindConnectElement( pDeviceContext,
pNCB,
&pConnEle ))
{
//
// If the session was already closed but the client hasn't been
// notified, notify them now
//
if ( errNCB == NRC_SCLOSED )
{
CTEIoComplete( pNCB, STATUS_SUCCESS, 0 ) ;
errNCB = NRC_GOODRET ;
}
return errNCB ;
}
ASSERT( (pConnEle->Verify == NBT_VERIFY_CONNECTION) ||
(pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ;
if ( tdistatus = VxdCompleteSessionNcbs( pDeviceContext, pConnEle ) )
{
DbgPrint("VxdHangup: Error return from VxdCompleteSessionNcbs\r\n") ;
}
if ( pClientEle = pConnEle->pClientEle )
{
ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ||
pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN ) ;
}
pLowerConn = pConnEle->pLowerConnId ;
if ( pLowerConn &&
(pLowerConn->fOnPartialRcvList == TRUE) &&
pLowerConn->StateRcv == PARTIAL_RCV )
{
RemoveEntryList( &pLowerConn->PartialRcvList ) ;
pLowerConn->fOnPartialRcvList = FALSE;
InitializeListHead(&pLowerConn->PartialRcvList);
}
Request.Handle.ConnectionContext = pConnEle ;
tdistatus = NbtDisconnect( &Request,
&TimeOut,
TDI_DISCONNECT_RELEASE,
NULL,
NULL,
NULL ) ;
if ( tdistatus && tdistatus != TDI_PENDING )
{
DbgPrint("VxdHangup: Warning: NbtDisconnect returned error\r\n") ;
}
tdistatus = NbtCloseConnection( &Request,
NULL,
pDeviceContext,
NULL ) ;
if ( tdistatus && tdistatus != TDI_PENDING )
{
DbgPrint("VxdHangup: Warning: NbtCloseConnection returned error\r\n") ;
}
tdistatus = NbtDisassociateAddress( &Request ) ;
if ( tdistatus )
{
DbgPrint("VxdHangup: NbtDisassociateAddress returned 0x") ;
DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ;
}
REQUIRE( NBUnregister( pDeviceContext, pNCB->ncb_lsn, NB_SESSION )) ;
//
// If this name has been deleted but there were active sessions, check
// to see if this is the last session, if so, delete the name
//
if ( pClientEle &&
pClientEle->fDeregistered &&
!ActiveSessions(pClientEle) )
{
UCHAR NameNum ;
if ( !VxdFindNameNum( pDeviceContext, pClientEle->pAddress, &NameNum ))
{
(void) VxdCleanupAddress( pDeviceContext,
NULL,
pClientEle,
NameNum,
TRUE ) ;
}
}
CTEIoComplete( pNCB, STATUS_SUCCESS, 0 ) ;
return NRC_GOODRET ;
}
/*******************************************************************
NAME: VxdListen
SYNOPSIS: Sets up a session listen
ENTRY: pDeviceContext -
pNCB - NCB that contains listen command
RETURNS:
NOTES: Before we can do the listen we must first open the
connection and associate the address.
The reserve field of the NCB is used as a SESS_SETUP_CONTEXT
structure
HISTORY:
Johnl 14-May-1993 Created
********************************************************************/
NCBERR VxdListen( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
NTSTATUS status;
NCBERR errNCB ;
TDI_REQUEST Request;
PSESS_SETUP_CONTEXT pSessSetupContext = NULL ;
if ( errNCB = VxdInitSessionSetup( pDeviceContext,
&Request,
&pSessSetupContext,
pNCB ))
{
return errNCB ;
}
status = NbtListen( &Request,
TDI_QUERY_ACCEPT,
*pNCB->ncb_callname != '*' ?
pSessSetupContext->pRequestConnect : NULL,
pSessSetupContext->pReturnConnect,
pNCB
);
if ( !NT_SUCCESS( status ) )
{
VxdTearDownSession( pDeviceContext,
Request.Handle.ConnectionContext,
pSessSetupContext,
NULL ) ;
}
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdOpenName
SYNOPSIS: Creates an Address object in response to AddName or
AddGroupName.
ENTRY: pDeviceContext - Device name is being added to
pNCB - NCB AddName submission
RETURNS: STATUS_SUCCESS if successful, error code otherwise
NOTES:
HISTORY:
Johnl 20-Apr-1993 Created
********************************************************************/
NCBERR VxdOpenName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
NTSTATUS status ;
TDI_REQUEST tdiRequest ;
TDI_ADDRESS_NETBIOS tdiaddr ;
if ( pNCB->ncb_name[0] == '*' ||
pNCB->ncb_name[0] == '\0' )
{
return NRC_NOWILD ;
}
//
// Fill in the TDI structures appropriately
//
switch ( pNCB->ncb_command & ~ASYNCH )
{
case NCBADDGRNAME:
tdiaddr.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP ;
break ;
case NCBADDNAME:
tdiaddr.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE ;
break ;
default:
ASSERTMSG("VxdOpenName: Unexpected command type!\n", FALSE ) ;
return NRC_SYSTEM ;
}
CTEMemCopy( tdiaddr.NetbiosName,
pNCB->ncb_name,
sizeof(pNCB->ncb_name) ) ;
status = NbtOpenAddress( &tdiRequest,
&tdiaddr,
pDeviceContext->IpAddress,
NULL, // Security descriptor
pDeviceContext,
pNCB ) ;
if ( NT_SUCCESS( status ))
{
//
// Set our event handler to catch "Receive Any" and "Receve
// Any From Any" NCBs
//
REQUIRE( !NbtSetEventHandler( (tCLIENTELE*)tdiRequest.Handle.AddressHandle,
TDI_EVENT_RECEIVE,
ReceiveAnyHandler,
(tCLIENTELE*)tdiRequest.Handle.AddressHandle )) ;
//
// Set an event handler to cleanup up Netbios specific stuff on
// disconnect
//
REQUIRE( !NbtSetEventHandler( (tCLIENTELE*)tdiRequest.Handle.AddressHandle,
TDI_EVENT_DISCONNECT,
VxdDisconnectHandler,
(tCLIENTELE*)tdiRequest.Handle.AddressHandle)) ;
}
//
// If we open a non-unique name twice (such as a group name) then
// NbtOpenAddress doesn't complete the IRP it just returns success.
//
if ( status == TDI_SUCCESS )
{
CTEIoComplete( pNCB, status, (ULONG) tdiRequest.Handle.AddressHandle ) ;
}
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdCloseName
SYNOPSIS: Called in response to a Netbios Delete Name request
ENTRY: pDeviceContext - Device name should be deleted from
pNCB - Netbios Delete name submission
RETURNS: STATUS_SUCCESS if successful, error code otherwise
NOTES:
HISTORY:
Johnl 23-Apr-1993 Created
********************************************************************/
NCBERR VxdCloseName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
tCLIENTELE * pClientEle ;
TDI_STATUS tdistatus ;
UCHAR NameNum ;
NCBERR errNCB ;
if ( pNCB->ncb_name[0] == '*' ||
pNCB->ncb_name[0] == '\0' )
{
return NRC_NOWILD ;
}
if ( errNCB = VxdNameToClient( pDeviceContext,
pNCB->ncb_name,
&NameNum,
&pClientEle ))
{
return errNCB ;
}
//
// If any sessions are open on this name, delay deletion till last name is
// closed
//
if ( ActiveSessions( pClientEle ) )
{
VxdCleanupAddress( pDeviceContext, pNCB, pClientEle, NameNum, FALSE ) ;
CTEIoComplete( pNCB, STATUS_NRC_ACTSES, 0 ) ;
return NRC_GOODRET ;
}
//
// No open sessions so blow away the name
//
return VxdCleanupAddress( pDeviceContext, pNCB, pClientEle, NameNum, TRUE ) ;
}
/*******************************************************************
NAME: ActiveSessions
SYNOPSIS: Returns TRUE if pClientEle has any active sessions
ENTRY: pClientEle - Client element to check
********************************************************************/
BOOL ActiveSessions( tCLIENTELE * pClientEle )
{
PLIST_ENTRY pHead, pEntry ;
pHead = &pClientEle->ConnectActive ;
pEntry = pClientEle->ConnectActive.Flink ;
while ( pHead != pEntry )
{
tCONNECTELE * pConnEle = CONTAINING_RECORD( pEntry, tCONNECTELE, Linkage ) ;
if ( pConnEle->state > NBT_ASSOCIATED )
{
return TRUE ;
}
pEntry = pEntry->Flink ;
}
return FALSE ;
}
/*******************************************************************
NAME: VxdCleanupAddress
SYNOPSIS: Prepares a name for deletion and optionally deletes it
ENTRY: pDeviceContext - Adapter we are dealing with
pNCB - Delete name NCB
pClientEle - Client of address element to delete
NameNum - Name number in table we are deleting
fDeleteAddress - TRUE if address should be deleted
EXIT: The address element will be marked as deregistered and all
non-session NCBs will be completed. The address element
may optionally be deleted also.
NOTES: This routine will complete pNCB as appropriate.
HISTORY:
Johnl 22-Sep-1993 Created
********************************************************************/
NCBERR VxdCleanupAddress( tDEVICECONTEXT * pDeviceContext,
NCB * pNCB,
tCLIENTELE * pClientEle,
UCHAR NameNum,
BOOL fDeleteAddress )
{
TDI_REQUEST Request ;
NCBERR errNCB ;
tCLIENTELE * pClientEleBcast ;
TDI_STATUS tdistatus ;
USHORT NameType ;
PLIST_ENTRY pHead, pEntry ;
PLIST_ENTRY pNextEntry;
tLISTENREQUESTS * pListen ;
PRCV_CONTEXT prcvCont ;
tRCVELE * prcvEle ;
pClientEle->fDeregistered = TRUE ;
//
// Delete all outstanding listens on this name
//
while ( !IsListEmpty( &pClientEle->ListenHead ))
{
pEntry = RemoveHeadList( &pClientEle->ListenHead ) ;
pListen = CONTAINING_RECORD( pEntry, tLISTENREQUESTS, Linkage ) ;
CTEIoComplete( pListen->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ;
CTEMemFree( pListen ) ;
}
//
// Delete all outstanding datagram receives on this name
//
while ( !IsListEmpty( &pClientEle->RcvDgramHead ))
{
pEntry = RemoveHeadList( &pClientEle->RcvDgramHead ) ;
prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ;
CTEIoComplete( prcvEle->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ;
CTEMemFree( prcvEle ) ;
}
//
// Delete all outstanding datagram broadcast receives on this name number
//
errNCB = VxdFindClientElement( pDeviceContext,
0,
&pClientEleBcast,
CLIENT_BC ) ;
if ( !errNCB )
{
//
// Scan the NCBs looking for a receive on this name number
//
pHead = &pClientEleBcast->RcvDgramHead ;
pEntry = pClientEleBcast->RcvDgramHead.Flink ;
while ( pEntry != pHead )
{
prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ;
pNextEntry = pEntry->Flink ;
if ( ((NCB*)prcvEle->pIrp)->ncb_num == NameNum )
{
RemoveEntryList( pEntry ) ;
CTEIoComplete( prcvEle->pIrp, STATUS_NETWORK_NAME_DELETED, 0 ) ;
CTEMemFree( prcvEle ) ;
}
pEntry = pNextEntry ;
}
}
//
// Delete all outstanding Receive Anys on this name
//
while ( !IsListEmpty( &pClientEle->RcvAnyHead ))
{
pEntry = RemoveHeadList( &pClientEle->RcvAnyHead ) ;
prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
CTEIoComplete( prcvCont->pNCB, STATUS_NETWORK_NAME_DELETED, 0 ) ;
}
tdistatus = TDI_SUCCESS;
if ( fDeleteAddress )
{
Request.Handle.ConnectionContext = pClientEle ;
tdistatus = NbtCloseAddress( &Request,
NULL, //&RequestStatus,
pDeviceContext,
pNCB ) ;
if ( (tdistatus != TDI_PENDING) && pNCB )
CTEIoComplete( pNCB, tdistatus, 0 ) ;
REQUIRE( NBUnregister( pDeviceContext, NameNum, NB_NAME )) ;
DbgPrint("VxdCloseName: NBUnregistered:NameNum = 0x") ;
DbgPrintNum( NameNum ) ;
DbgPrint(" ClientEle = 0x") ;
DbgPrintNum( pClientEle ) ;
DbgPrint("\r\n") ;
if ( !NT_SUCCESS( tdistatus ))
{
DbgPrint("VxdCloseName: NbtCloseAddress failed with status 0x") ;
DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ;
}
}
return MapTDIStatus2NCBErr( tdistatus ) ;
}
/*******************************************************************
NAME: VxdAccept
SYNOPSIS: Accepts an indicated listen
ENTRY: pConnectElem - Upper part of connection we're about to
setup
pNCB - Original Listen request
RETURNS: TDI_SUCCESS if successful error code otherwise
NOTES:
HISTORY:
Johnl 27-May-1993 Created
********************************************************************/
TDI_STATUS VxdAccept( tCONNECTELE * pConnectElem, NCB * pNCB )
{
TDI_REQUEST Request ;
PSESS_SETUP_CONTEXT pSessSetupCont = (PSESS_SETUP_CONTEXT) pNCB->ncb_reserve ;
TDI_STATUS status ;
Request.Handle.ConnectionContext = pConnectElem ;
status = NbtAccept( &Request,
pSessSetupCont->pRequestConnect,
pSessSetupCont->pReturnConnect,
NULL ) ;
if ( !NT_SUCCESS(status) )
{
DbgPrint( "VxdAccept: NbtAccept returned " ) ;
DbgPrintNum( status ) ;
DbgPrint("\r\n") ;
}
//
// It's OK if the accept is pending because it's just the
// session setup acknowledgement
//
if ( status == TDI_PENDING )
status = TDI_SUCCESS ;
return status ;
}
/*******************************************************************
NAME: VxdAdapterStatus
SYNOPSIS: Gets the requested adapter status
ENTRY: pDeviceContext - Adapter status to get
pNCB - Pointer to requesting NCB
EXIT:
NOTES:
HISTORY:
Johnl 10-Aug-1993 Created
********************************************************************/
NCBERR VxdAdapterStatus( tDEVICECONTEXT * pDeviceContext,
NCB * pNCB,
ULONG Ipaddr
)
{
TDI_STATUS status ;
PADAPTER_STATUS pAdapterStatus ;
ULONG ActualSize;
ULONG Size = pNCB->ncb_length ;
//
// Ipaddr will always be 0 except in one case: if we came here
// via nbtstat -A
//
if ( !Ipaddr && *pNCB->ncb_callname == '*' )
{
//
// Get the local adapter status
//
DbgPrint("VxdAdapterStatus: AStat for local (*)\r\n") ;
status = NbtQueryAdapterStatus( pDeviceContext,
&pAdapterStatus,
&Size ) ;
if ( !status || status == TDI_BUFFER_OVERFLOW )
{
ActualSize = min( pNCB->ncb_length, Size ) ;
CTEMemCopy( pNCB->ncb_buffer,
pAdapterStatus,
ActualSize) ;
pNCB->ncb_length = ActualSize;
CTEFreeMem( pAdapterStatus ) ;
CTEIoComplete( pNCB, status, 0 ) ;
//
// Return a successful status (buffer overflow denoted
// in NCB)
//
status = NRC_GOODRET ;
}
}
else
{
ULONG IpAddrsList[2];
IpAddrsList[0] = Ipaddr;
IpAddrsList[1] = 0;
status = NbtSendNodeStatus( pDeviceContext,
pNCB->ncb_callname,
pNCB,
IpAddrsList,
0,
NodeStatusDone);
}
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdFindName
SYNOPSIS: Gets the requested adapter status
ENTRY: pDeviceContext - Adapter status to get
pNCB - Pointer to requesting NCB
EXIT:
NOTES:
HISTORY:
Johnl 04-Oct-1993 Created
********************************************************************/
NCBERR VxdFindName( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
TDI_STATUS status ;
TDI_CONNECTION_INFORMATION RequestInfo ;
DbgPrint("VxdFindName: Entered\r\n") ;
InitNBTDIConnectInfo( &RequestInfo, &tanb_global, pNCB->ncb_callname ) ;
status = NbtQueryFindName( &RequestInfo,
pDeviceContext,
pNCB,
FALSE ) ;
if ( status == STATUS_SUCCESS )
{
CTEIoComplete( pNCB, STATUS_SUCCESS, 0xffffffff ) ;
return STATUS_SUCCESS ;
}
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdSessionStatus
SYNOPSIS: Gets the requested Session status
ENTRY: pDeviceContext - Session status to get
pNCB - Pointer to requesting NCB
EXIT:
NOTES: VxdCopySessionStatus will automatically complete the NCB
if the buffer overflows. Otherwise we will.
HISTORY:
Johnl 23-Aug-1993 Created
********************************************************************/
NCBERR VxdSessionStatus( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
TDI_STATUS status = STATUS_SUCCESS ;
PSESSION_HEADER pSessionHeader = (PSESSION_HEADER) pNCB->ncb_buffer ;
PSESSION_BUFFER pSessionBuff ;
ULONG RemainingSize = pNCB->ncb_length ;
tNAMEADDR * pNameAddr = NULL ;
tCLIENTELE * pClientEle = NULL ;
tCLIENTELE * pClientEleBcast = NULL ;
USHORT NameType ;
PLIST_ENTRY pEntry ;
UCHAR i ;
NCBERR errNCB ;
if ( RemainingSize < sizeof(SESSION_HEADER) )
{
CTEIoComplete( pNCB, STATUS_INVALID_BUFFER_SIZE, 0 ) ;
return NRC_GOODRET ;
}
pSessionHeader->sess_name = 0 ;
pSessionHeader->num_sess = 0 ;
pSessionHeader->rcv_dg_outstanding = 0 ;
pSessionHeader->rcv_any_outstanding = 0 ;
//
// For broadcast datagram statistics
//
errNCB = VxdFindClientElement( pDeviceContext,
0,
&pClientEleBcast,
CLIENT_BC ) ;
if ( errNCB )
return errNCB ;
//
// Get all sessions?
//
if ( pNCB->ncb_name[0] == '*' )
{
for ( i = 1 ; i <= pDeviceContext->cMaxSessions ; i++ )
{
if ( pDeviceContext->pSessionTable[i] != NULL )
{
pClientEle = pDeviceContext->pSessionTable[i]->pClientEle ;
//
// Both normal receives and broadcast receives are
// kept on the same list
//
COUNT_ELEMENTS( pClientEle->RcvDgramHead,
pSessionHeader->rcv_dg_outstanding ) ;
COUNT_ELEMENTS( pClientEle->RcvAnyHead,
pSessionHeader->rcv_any_outstanding ) ;
}
}
//
// Only one broadcast client element per adapter
//
COUNT_ELEMENTS( pClientEleBcast->RcvDgramHead,
pSessionHeader->rcv_dg_outstanding ) ;
COUNT_ELEMENTS( pDeviceContext->RcvDGAnyFromAnyHead,
pSessionHeader->rcv_dg_outstanding ) ;
pSessionHeader->sess_name = 0xff ;
RemainingSize -= sizeof( SESSION_HEADER ) ;
pSessionBuff = (PSESSION_BUFFER) (pSessionHeader + 1) ;
//
// From this device context, traverse all of the Address elements
// and all of its Client elements and all of its Connect Elements
//
for ( pEntry = NbtConfig.AddressHead.Flink ;
pEntry != &NbtConfig.AddressHead && !status ;
pEntry = pEntry->Flink )
{
PLIST_ENTRY pEntryClient ;
tADDRESSELE * pAddrEle = CONTAINING_RECORD( pEntry,
tADDRESSELE,
Linkage ) ;
ASSERT( pAddrEle->Verify == NBT_VERIFY_ADDRESS ) ;
//
// Only get addresses for this adapter
//
if ( pAddrEle->pDeviceContext != pDeviceContext )
continue ;
for ( pEntryClient = pAddrEle->ClientHead.Flink ;
pEntryClient != &pAddrEle->ClientHead ;
pEntryClient = pEntryClient->Flink )
{
tCLIENTELE * pClientEle = CONTAINING_RECORD( pEntryClient,
tCLIENTELE,
Linkage ) ;
PLIST_ENTRY pEntryConn ;
ASSERT( pClientEle->Verify == NBT_VERIFY_CLIENT ||
pClientEle->Verify == NBT_VERIFY_CLIENT_DOWN ) ;
if (!VxdCopySessionStatus( pDeviceContext,
pClientEle,
pSessionHeader,
&pSessionBuff,
&RemainingSize ))
{
status = STATUS_BUFFER_OVERFLOW ;
break ;
}
}
}
}
else
{
if ( errNCB = VxdNameToClient( pDeviceContext,
pNCB->ncb_name,
&pSessionHeader->sess_name,
&pClientEle ))
{
return errNCB ;
}
COUNT_ELEMENTS( pClientEle->RcvDgramHead,
pSessionHeader->rcv_dg_outstanding ) ;
COUNT_ELEMENTS( pClientEleBcast->RcvDgramHead,
pSessionHeader->rcv_dg_outstanding ) ;
COUNT_ELEMENTS( pDeviceContext->RcvDGAnyFromAnyHead,
pSessionHeader->rcv_dg_outstanding ) ;
COUNT_ELEMENTS( pClientEle->RcvAnyHead,
pSessionHeader->rcv_any_outstanding ) ;
RemainingSize -= sizeof( SESSION_HEADER ) ;
pSessionBuff = (PSESSION_BUFFER) (pSessionHeader + 1) ;
if ( !VxdCopySessionStatus( pDeviceContext,
pClientEle,
pSessionHeader,
&pSessionBuff,
&RemainingSize ))
{
status = STATUS_BUFFER_OVERFLOW ;
}
}
CTEIoComplete( pNCB,
status,
sizeof(SESSION_HEADER) +
pSessionHeader->num_sess * sizeof(SESSION_BUFFER) ) ;
return NRC_GOODRET ;
}
/*******************************************************************
NAME: VxdCopySessionStatus
SYNOPSIS: Copies all of the sessions associated with pClientEle
ENTRY: pDeviceContext - Adapter to use
pClientEle - Client to retrieve all sessions for
pSessionHeader - Session status header
pSessionBuff - Pointer to beginning of session buffers
pRemainingSize - Remaining size of buffer
RETURNS: TRUE if all session information was transferred,
FALSE if we ran out of buffer space
NOTES:
HISTORY:
Johnl 23-Aug-1993 Created
********************************************************************/
BOOL VxdCopySessionStatus( tDEVICECONTEXT * pDeviceContext,
tCLIENTELE * pClientEle,
PSESSION_HEADER pSessionHeader,
PSESSION_BUFFER * ppSessionBuff,
ULONG * pRemainingSize )
{
PLIST_ENTRY pEntryConn ;
tCONNECTELE * pConnectEle ;
for ( pEntryConn = pClientEle->ConnectActive.Flink ;
pEntryConn != &pClientEle->ConnectActive ;
pEntryConn = pEntryConn->Flink )
{
PLIST_ENTRY pEntry ;
BOOL fFillRemote = FALSE ;
pConnectEle = CONTAINING_RECORD( pEntryConn,
tCONNECTELE,
Linkage ) ;
ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION ||
pConnectEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ;
if ( *pRemainingSize < sizeof(SESSION_BUFFER) )
{
return FALSE ;
}
*pRemainingSize -= sizeof(SESSION_BUFFER) ;
pSessionHeader->num_sess++ ;
(*ppSessionBuff)->rcvs_outstanding = 0 ;
(*ppSessionBuff)->sends_outstanding = 0 ; // Always 0
REQUIRE( !VxdFindLSN( pDeviceContext, pConnectEle, &(*ppSessionBuff)->lsn )) ;
COUNT_ELEMENTS( pConnectEle->RcvHead,
(*ppSessionBuff)->rcvs_outstanding ) ;
//
// Set the session state
//
switch ( pConnectEle->state )
{
case NBT_CONNECTING: // establishing Transport connection
if ( pConnectEle->Orig )
(*ppSessionBuff)->state = CALL_PENDING ;
else
(*ppSessionBuff)->state = LISTEN_OUTSTANDING ;
break ;
case NBT_SESSION_INBOUND: // waiting for a session request after tcp connectio
case NBT_SESSION_WAITACCEPT: // waiting for accept after a listen has been satis
(*ppSessionBuff)->state = LISTEN_OUTSTANDING ;
break ;
case NBT_SESSION_OUTBOUND: // waiting for a session response after tcp connecti
fFillRemote = TRUE ;
(*ppSessionBuff)->state = CALL_PENDING ;
case NBT_SESSION_UP: // got positive response
fFillRemote = TRUE ;
(*ppSessionBuff)->state = SESSION_ESTABLISHED ;
break ;
case NBT_DISCONNECTING: // sent a disconnect down to Tcp, but it hasn't comp
(*ppSessionBuff)->state = HANGUP_PENDING;
break ;
case NBT_DISCONNECTED: // a session has been disconnected but not closed wit
(*ppSessionBuff)->state = HANGUP_COMPLETE;
break ;
case NBT_IDLE: // Shouldn't be on ConnectActive list
case NBT_ASSOCIATED:
default:
ASSERT( FALSE ) ;
(*ppSessionBuff)->state = SESSION_ABORTED ;
break ;
}
//
// Copy local and/or remote name
//
CTEMemCopy( (*ppSessionBuff)->local_name,
pClientEle->pAddress->pNameAddr->Name,
NCBNAMSZ ) ;
if ( fFillRemote )
{
CTEMemCopy( (*ppSessionBuff)->remote_name,
pConnectEle->RemoteName,
NCBNAMSZ ) ;
}
(*ppSessionBuff)++ ;
}
return TRUE ;
}
/*******************************************************************
NAME: VxdReset
SYNOPSIS: Clears out the name tables and completes all outstanding NCBs
ENTRY: pDeviceContext - Adapter status to get
pNCB - Pointer to requesting NCB
EXIT:
NOTES: If a session is active then we have to wait till we disconnect
the connection before deleting the name. We keep count of the
active sessions and call the VxdResetContinue function after
all session disconnects have been completed.
It is assumed this is made as a "wait" call.
HISTORY:
Johnl 16-Aug-1993 Created
********************************************************************/
NCBERR VxdReset( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
UCHAR i ;
TDI_STATUS tdistatus ;
PLIST_ENTRY pHead, pEntry ;
PRESET_CONTEXT pRstCont = (PRESET_CONTEXT) pNCB->ncb_reserve ;
pRstCont->cActiveSessions = 0 ;
pRstCont->cActiveNames = 0 ;
pRstCont->errncb = NRC_GOODRET ;
//
// Kill off all of the Receive any from any NCBs
//
while ( !IsListEmpty(&pDeviceContext->RcvAnyFromAnyHead))
{
PRCV_CONTEXT prcvCont ;
pEntry = RemoveHeadList( &pDeviceContext->RcvAnyFromAnyHead ) ;
prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
CTEIoComplete( prcvCont->pNCB,
STATUS_CONNECTION_DISCONNECTED,
0 ) ;
}
//
// Kill off all of the Receive any datagrams from any
//
while ( !IsListEmpty(&pDeviceContext->RcvDGAnyFromAnyHead))
{
tRCVELE * pRcvEle ;
pEntry = RemoveHeadList( &pDeviceContext->RcvDGAnyFromAnyHead ) ;
pRcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ;
CTEIoComplete( pRcvEle->pIrp,
STATUS_NETWORK_NAME_DELETED, // NRC_NAMERR
0 ) ;
CTEMemFree( pRcvEle ) ;
}
//
// Disconnect all sessions
//
for ( i = 1 ; i <= pDeviceContext->cMaxSessions ; i++ )
{
//
// This will also prevent any listens from being accepted on the
// connection
//
if ( pDeviceContext->pSessionTable[i] != NULL )
{
TDI_REQUEST Request ;
ULONG TimeOut = DISCONNECT_TIMEOUT ;
tCONNECTELE * pConnEle= pDeviceContext->pSessionTable[i] ;
Request.Handle.ConnectionContext = pConnEle ;
pRstCont->cActiveSessions++ ;
tdistatus = NbtDisconnect( &Request,
&TimeOut,
TDI_DISCONNECT_RELEASE,
NULL,
NULL,
pNCB ) ;
if ( tdistatus != TDI_PENDING )
{
pRstCont->cActiveSessions-- ;
tdistatus = NbtCloseConnection( &Request,
NULL,
pDeviceContext,
NULL ) ;
REQUIRE( NBUnregister( pDeviceContext, i, NB_SESSION )) ;
}
}
}
//
// If no active sessions, then go ahead and delete all the names
//
if ( !pRstCont->cActiveSessions )
{
pRstCont->cActiveSessions = -1 ;
return VxdResetContinue( pDeviceContext, pNCB ) ;
}
return NRC_GOODRET ;
}
/*******************************************************************
NAME: VxdResetContinue
SYNOPSIS: Finishes the reset after all sessions have been successfully
shutdown
ENTRY: pDeviceContext - Adapter status to get
pNCB - Pointer to requesting NCB
EXIT:
NOTES:
HISTORY:
Johnl 16-Aug-1993 Created
********************************************************************/
NCBERR VxdResetContinue( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
UCHAR i ;
TDI_STATUS tdistatus ;
PLIST_ENTRY pHead, pEntry ;
PRESET_CONTEXT pRstCont = (PRESET_CONTEXT) pNCB->ncb_reserve ;
PNCB pNCBPerm ;
DbgPrint("VxdResetContinue entered\r\n") ;
//
// Now that all of the sessions have been disconnected, close each
// connection
//
for ( i = 1 ; i <= pDeviceContext->cMaxSessions ; i++ )
{
if ( pDeviceContext->pSessionTable[i] != NULL )
{
TDI_REQUEST Request ;
tCONNECTELE * pConnEle = pDeviceContext->pSessionTable[i] ;
Request.Handle.ConnectionContext = pConnEle ;
tdistatus = NbtCloseConnection( &Request,
NULL,
pDeviceContext,
NULL ) ;
REQUIRE( NBUnregister( pDeviceContext, i, NB_SESSION )) ;
}
}
//
// Delete all the names (including the permanent name)
//
for ( i = 0 ; i <= pDeviceContext->cMaxNames ; i++ )
{
if ( pDeviceContext->pNameTable[i] != NULL )
{
TDI_REQUEST Request ;
Request.Handle.ConnectionContext = pDeviceContext->pNameTable[i] ;
pRstCont->cActiveNames++ ;
tdistatus = NbtCloseAddress( &Request,
NULL, //&RequestStatus,
pDeviceContext,
pNCB ) ;
if ( tdistatus != TDI_PENDING )
pRstCont->cActiveNames-- ;
//
// Go ahead and remove the name from the table since nobody
// will be able to re-register with it since this is a "wait" cmd
//
REQUIRE( NBUnregister( pDeviceContext, i, NB_NAME )) ;
}
}
//
// Resize the session table If an error occurs, keep the old
// session table.
//
if ( pNCB->ncb_lsn != pDeviceContext->cMaxSessions )
{
UCHAR MaxSess = (UCHAR) pNCB->ncb_lsn ? pNCB->ncb_lsn : 6 ;
PVOID pSess = CTEAllocMem((USHORT)((MaxSess+1)*sizeof(tCONNECTELE*))) ;
if ( !pSess )
{
pRstCont->errncb = NRC_NORESOURCES ;
}
else
{
CTEFreeMem( pDeviceContext->pSessionTable ) ;
pDeviceContext->cMaxSessions = MaxSess ;
pDeviceContext->pSessionTable = pSess ;
CTEZeroMemory( &pDeviceContext->pSessionTable[0],
(pDeviceContext->cMaxSessions+1)*sizeof(tCONNECTELE*) ) ;
}
}
//
// Set current session/name numbers back to 1
//
pDeviceContext->iNcbNum = 1 ;
pDeviceContext->iLSNum = 1 ;
//
// re-add the permanent name for this adapter, non-fatal if it fails
//
if ( !NT_SUCCESS( NbtAddPermanentName( pDeviceContext )))
{
CDbgPrint( DBGFLAG_ERROR,
("VxdResetContinue: Warning - Failed to add permanent name")) ;
}
if ( !pRstCont->cActiveNames )
CTEIoComplete( pNCB, NRC_GOODRET, 0 ) ;
return NRC_GOODRET ;
}
/*******************************************************************
NAME: VxdCancel
SYNOPSIS: Attempts to cancel the NCB pointed at by ncb_buffer
ENTRY: pDeviceContext - Adapter status to get
pNCB - Pointer to requesting NCB
EXIT:
NOTES:
HISTORY:
Johnl 18-Aug-1993 Created
********************************************************************/
NCBERR VxdCancel( tDEVICECONTEXT * pDeviceContext, NCB * pNCB )
{
NCB * pNCBCancelled = (NCB*) pNCB->ncb_buffer ;
tCONNECTELE * pConnEle ;
tCLIENTELE * pClientEle ;
NCBERR errNCB = NRC_GOODRET ;
TDI_STATUS tdistatus ;
PLIST_ENTRY pHead, pEntry ;
USHORT NameType ;
tNAMEADDR * pNameAddr ;
tLISTENREQUESTS * pListen ;
PRCV_CONTEXT prcvCont ; // Used for session receives
tRCVELE * prcvEle ; // Used for Datagram receives
if ( pNCB->ncb_lana_num != pNCBCancelled->ncb_lana_num )
{
DbgPrint("VxdCancel: Attempt to cancel NCB w/ different lana\r\n") ;
return NRC_BRIDGE ;
}
if ( pNCB->ncb_retcode != NRC_PENDING )
return NRC_CANOCCR ;
switch ( pNCBCancelled->ncb_command & ~ASYNCH )
{
case NCBSEND:
case NCBSENDNA:
case NCBCHAINSEND:
case NCBCHAINSENDNA:
case NCBRECV:
//
// Cancelling a session NCB automatically closes the session
//
if ( VxdFindConnectElement( pDeviceContext,
pNCBCancelled,
&pConnEle ))
{
DbgPrint("VxdCancel: Attempted to cancel send NCB on non-existent session\r\n") ;
break ;
}
if ( (pNCBCancelled->ncb_command & ~ASYNCH) == NCBRECV )
{
errNCB = NRC_CANOCCR ;
for ( pEntry = pConnEle->RcvHead.Flink ;
pEntry != &pConnEle->RcvHead ;
pEntry = pEntry->Flink )
{
prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
if ( prcvCont->pNCB == pNCBCancelled )
{
RemoveEntryList( pEntry ) ;
CTEIoComplete( prcvCont->pNCB, STATUS_CANCELLED, 0 ) ;
errNCB = NRC_GOODRET ;
break ;
}
}
}
else
{
//
// Sends are immediately submitted to the transport, tell
// caller it's too late to cancel. The transport will complete
// the NCB when we close the connection below.
//
errNCB = NRC_CANOCCR ;
}
REQUIRE( !VxdCompleteSessionNcbs( pDeviceContext, pConnEle )) ;
VxdTearDownSession( pDeviceContext,
pConnEle,
NULL,
NULL ) ;
//
// Only remove from table if we've told the client
//
if ( pConnEle->Flags & NB_CLIENT_NOTIFIED )
{
REQUIRE( NBUnregister( pDeviceContext,
pNCBCancelled->ncb_lsn,
NB_SESSION )) ;
}
break ;
case NCBCANCEL:
errNCB = NRC_CANCEL ; // Can't cancel a cancel
break ;
case NCBLISTEN:
//
// Lookup the Client Element associated with this name, then scan
// the listen NCBs for one that matches the one being cancelled
//
if ( errNCB = VxdNameToClient( pDeviceContext,
pNCBCancelled->ncb_name,
NULL,
&pClientEle ))
{
DbgPrint("VxdCancel: Tried to cancel listen on non-existent name\r\n") ;
errNCB = NRC_CANOCCR ;
break ;
}
errNCB = NRC_CANOCCR ;
for ( pEntry = pClientEle->ListenHead.Flink ;
pEntry != &pClientEle->ListenHead ;
pEntry = pEntry->Flink )
{
pListen = CONTAINING_RECORD( pEntry, tLISTENREQUESTS, Linkage ) ;
if ( pListen->pIrp == pNCBCancelled )
{
DbgPrint("VxdCancel: Cancelling NCB 0x") ;
DbgPrintNum( (ULONG) pNCBCancelled ) ; DbgPrint("\r\n") ;
RemoveEntryList( &pListen->Linkage ) ;
CTEIoComplete( pNCBCancelled, STATUS_CANCELLED, 0 ) ;
CTEMemFree( pListen ) ;
errNCB = NRC_GOODRET ;
break ;
}
}
break ;
case NCBCALL:
//
// Search the ConnectActive list for our NCB and cleanup that
// connection
//
if ( errNCB = VxdNameToClient( pDeviceContext,
pNCBCancelled->ncb_name,
NULL,
&pClientEle ))
{
DbgPrint("VxdCancel: Tried to cancel call on non-existent name\r\n") ;
errNCB = NRC_CANOCCR ;
break ;
}
errNCB = NRC_CANOCCR ;
for ( pEntry = pClientEle->ConnectActive.Flink ;
pEntry != &pClientEle->ConnectActive ;
pEntry = pEntry->Flink )
{
pConnEle = CONTAINING_RECORD( pEntry, tCONNECTELE, Linkage ) ;
if ( pConnEle->pIrp == pNCBCancelled )
{
tDGRAM_SEND_TRACKING * pTracker = (tDGRAM_SEND_TRACKING*)
pConnEle->pIrpRcv ;
//
// if it's too late, just say we can't cancel it
//
if (pConnEle->state >= NBT_SESSION_OUTBOUND)
{
errNCB = NRC_CANOCCR ;
break;
}
//
// yes, we can cancel it. we just mark the tracker to say
// this call is cancelled: both the original ncb and this
// cancel ncb will get completed at some stage.
//
DbgPrint("VxdCancel: Cancelling NCB 0x") ;
DbgPrintNum( (ULONG) pNCBCancelled ) ; DbgPrint("\r\n") ;
pTracker->Flags |= TRACKER_CANCELLED;
pConnEle->pIrpDisc = pNCB;
return NRC_GOODRET ;
}
}
break ;
case NCBDGRECV:
if ( pNCBCancelled->ncb_num == ANY_NAME )
{
pHead = &pDeviceContext->RcvDGAnyFromAnyHead ;
}
else
{
if ( errNCB = VxdFindClientElement( pDeviceContext,
pNCBCancelled->ncb_num,
&pClientEle,
CLIENT_LOCAL ) )
{
ASSERT( FALSE ) ;
break ;
}
pHead = &pClientEle->RcvDgramHead ;
}
errNCB = NRC_CANOCCR ;
for ( pEntry = pHead->Flink ;
pEntry != pHead ;
pEntry = pEntry->Flink )
{
prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ;
if ( prcvEle->pIrp == pNCBCancelled )
{
RemoveEntryList( pEntry ) ;
CTEIoComplete( pNCBCancelled, STATUS_CANCELLED, 0 ) ;
CTEMemFree( prcvEle ) ;
errNCB = NRC_GOODRET ;
break ;
}
}
break ;
case NCBDGRECVBC:
//
// For receive broadcast datagrams, we have to look through the list
// of clients on the Broadcast Address.
//
errNCB = VxdFindClientElement( pDeviceContext,
0,
&pClientEle,
CLIENT_BC ) ;
if ( !errNCB )
{
errNCB = NRC_CANOCCR ;
for ( pEntry = pClientEle->RcvDgramHead.Flink ;
pEntry != &pClientEle->RcvDgramHead ;
pEntry = pEntry->Flink )
{
prcvEle = CONTAINING_RECORD( pEntry, tRCVELE, Linkage ) ;
if ( prcvEle->pIrp == pNCBCancelled )
{
RemoveEntryList( pEntry ) ;
CTEMemFree( prcvEle ) ;
CTEIoComplete( pNCBCancelled, STATUS_CANCELLED, 0 ) ;
errNCB = NRC_GOODRET ;
break ;
}
}
}
break ;
case NCBRECVANY:
if ( pNCBCancelled->ncb_num == ANY_NAME )
pHead = &pDeviceContext->RcvAnyFromAnyHead ;
else
{
if ( errNCB = VxdFindClientElement( pDeviceContext,
pNCBCancelled->ncb_num,
&pClientEle,
CLIENT_LOCAL ) )
{
ASSERT( FALSE ) ;
break ;
}
pHead = &pClientEle->RcvAnyHead ;
}
errNCB = NRC_CANOCCR ;
pEntry = pHead->Flink ;
while ( pEntry != pHead )
{
prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
if ( prcvCont->pNCB == pNCBCancelled )
{
RemoveEntryList( pEntry ) ;
CTEIoComplete( prcvCont->pNCB, STATUS_CANCELLED, 0 ) ;
errNCB = NRC_GOODRET ;
break ;
}
pEntry = pEntry->Flink ;
}
break ;
default:
errNCB = NRC_CANCEL ;
}
CTEIoComplete( pNCB, errNCB, 0 ) ;
//
// No, no! Don't touch that ncb after completing it!
//
//pNCB->ncb_retcode = errNCB ;
//pNCB->ncb_cmd_cplt = errNCB ;
return errNCB ;
}
/*******************************************************************
NAME: VxdIoComplete
SYNOPSIS: Let's the NCB know that all processing is done by setting
the command completion fields and calling the post routine
if available.
ENTRY: pirp - Pointer to the NCB to notify that we are done
(or NULL if this didn't come from the Netbios I/F
status - Status of the completion
ulExtra - Extra parameter
NOTES: This is the procedure that CTEIoComplete maps to and is
roughly equivilent to "completing" an IRP.
HISTORY:
Johnl 27-Apr-1993 Created
********************************************************************/
VOID VxdIoComplete( PCTE_IRP pirp,
NTSTATUS status,
ULONG ulExtra )
{
NCB * pNCB = pirp ;
NCBERR errNCB = NRC_GOODRET ;
PSESS_SETUP_CONTEXT pSessSetupCont ;
BOOL fAsync ;
tDEVICECONTEXT * pDeviceContext ;
PRESET_CONTEXT pRstCont ;
PSEND_CONTEXT pSendCont ;
tCONNECTELE * pConnEle ;
DbgPrint("VxdIoComplete: Completing NCB; Cmd, Addr, TDI status: 0x") ;
if ( pNCB )
{
DbgPrintNum( pNCB->ncb_command ) ;
DbgPrint(" 0x") ; DbgPrintNum( (ULONG) pNCB ) ;
DbgPrint(" 0x") ; DbgPrintNum( status ) ;
DbgPrint("\r\n") ;
}
else
DbgPrint("NULL\r\n") ;
//
// If no NCB to complete then we're done
//
if ( !pNCB )
return ;
fAsync = !!(pNCB->ncb_command & ASYNCH) ;
pDeviceContext = GetDeviceContext( pNCB ) ;
ASSERT(pDeviceContext);
//
// Note that we drop through the below case statement even if an error
// occurred because some commands need to free stuff before completing
// the NCB.
//
if ( status != STATUS_SUCCESS &&
( pNCB->ncb_command & ~ASYNCH) != NCBCANCEL )
{
errNCB = MapTDIStatus2NCBErr( status ) ;
}
//
// Fill in any items in the NCB struct if necessary
//
switch( pNCB->ncb_command & ~ASYNCH )
{
case NCBRECVANY: // lsn was set when the receive was posted
case NCBRECV:
FreeRcvContext( *((PRCV_CONTEXT*)&pNCB->ncb_reserve) ) ;
if ( errNCB && errNCB != NRC_INCOMP )
{
break ;
}
ASSERT( ulExtra <= 0xffff ) ;
ASSERT( pNCB->ncb_length >= ulExtra ) ;
pNCB->ncb_length = (WORD) ulExtra ;
DbgPrint("\tSetting length to 0x") ;
DbgPrintNum( ulExtra ) ;
DbgPrint("\r\n") ;
break ;
case NCBSSTAT:
case NCBDGRECV:
case NCBDGRECVBC:
if ( errNCB && errNCB != NRC_INCOMP )
break ;
ASSERT( ulExtra <= 0xffff ) ;
ASSERT( pNCB->ncb_length >= ulExtra ) ;
pNCB->ncb_length = (WORD) ulExtra ;
DbgPrint("\tSetting length to 0x") ;
DbgPrintNum( ulExtra ) ;
DbgPrint("\r\n") ;
break ;
case NCBASTAT:
case NCBFINDNAME:
if ( errNCB && errNCB != NRC_INCOMP )
break ;
if ( ulExtra != 0xffffffff ) // Means buffer length already set
pNCB->ncb_length = (WORD) ulExtra ;
DbgPrint("\tAStat/Findname length is 0x") ;
DbgPrintNum( (ULONG) pNCB->ncb_length ) ;
DbgPrint("\r\n") ;
break ;
case NCBSEND:
case NCBSENDNA:
case NCBCHAINSEND:
case NCBCHAINSENDNA:
pSendCont = (PSEND_CONTEXT) pNCB->ncb_reserve ;
if ( errNCB )
{
//
// Sends are immediately given to the transport, so if a
// timeout occurs, we'll first be completed by the timeout
// code, then we'll be completed by the transport closing
// the connection.
//
if ( errNCB != NRC_CMDTMO &&
pSendCont->STO == NCB_TIMED_OUT )
{
//
// The transport has completed this NCB in response to the
// Close connection because of a send timeout. Map the
// error to timeout and complete back to the client. The
// session is dead so don't disconnect it again. The send
// has already been removed from the timeout list.
//
errNCB = NRC_CMDTMO ;
}
else
{
BOOL fTimedOutNCB = pSendCont->STO == NCB_TIMED_OUT ;
//
// Remove from timeout list
//
if ( pSendCont->STO != NCB_INFINITE_TIME_OUT )
RemoveEntryList( &pSendCont->ListEntry ) ;
//
// Kill the session
//
if ( VxdFindConnectElement( pDeviceContext,
pNCB,
&pConnEle ))
{
//
// There maybe multiple sends on this session, only the
// first should disconnect
//
CTEFreeMem( pSendCont->pHdr ) ;
DbgPrint("VxdIoComplete: Error occurred on non-existent session\r\n") ;
break ;
}
REQUIRE( !VxdCompleteSessionNcbs( pDeviceContext, pConnEle )) ;
//
// Only remove from table if we've told the client
//
if ( pConnEle->Flags & NB_CLIENT_NOTIFIED )
{
REQUIRE( NBUnregister( pDeviceContext,
pNCB->ncb_lsn,
NB_SESSION )) ;
}
VxdTearDownSession( pDeviceContext,
pConnEle,
NULL,
NULL ) ;
if ( fTimedOutNCB ) // pSendCont may already have been freed
{
//
// The Close Connection above will cause the transport to
// complete this send with a session closed error, wait
// for that before completing back to the client.
//
return ;
}
}
}
else
{
if ( pSendCont->STO != NCB_INFINITE_TIME_OUT )
RemoveEntryList( &pSendCont->ListEntry ) ;
}
FreeSessionHdr( pSendCont->pHdr ) ;
break ;
case NCBDGSEND:
case NCBDGSENDBC:
//
// Nothing to do
//
break ;
//
// Need to set the ncb_num field for the following commands. Note
// that the ulExtra parameter will contain a pointer to the address
// element that was just added for this name.
//
case NCBADDNAME:
case NCBADDGRNAME:
if ( errNCB )
break ;
if ( !NBRegister( pDeviceContext,
&pNCB->ncb_num,
(tCLIENTELE *) ulExtra,
NB_NAME ))
{
TDI_REQUEST Request ;
TDI_STATUS tdistatus ;
errNCB = NRC_NAMTFUL ;
Request.Handle.ConnectionContext = (tCLIENTELE *) ulExtra ;
tdistatus = NbtCloseAddress( &Request,
NULL, //&RequestStatus,
pDeviceContext,
NULL ) ;
ASSERT( NT_SUCCESS( tdistatus )) ;
}
else
{
DbgPrint("\tRegistered Name number ") ;
DbgPrintNum( pNCB->ncb_num ) ;
DbgPrint(" for Address Element ") ;
DbgPrintNum( ulExtra ) ;
DbgPrint("\r\n") ;
}
break ;
#if 0
//
// Private NBT NCB type for processing the permanent name
//
case NCBADD_PERMANENT_NAME:
CTEFreeMem( pNCB ) ;
if ( errNCB )
{
DbgPrint("VxdIoComplete: Failed to add permanent name!\r\n") ;
}
else
{
ASSERT( pDeviceContext->pNameTable[0] == NULL ) ;
pDeviceContext->pNameTable[0] = (tCLIENTELE *) ulExtra ;
}
//
// Don't do any further processing of this NCB. Not only did
// we just free it, nobody is looking for it.
//
return ;
#endif
case NCBCALL:
case NCBLISTEN:
pSessSetupCont = (PSESS_SETUP_CONTEXT) pNCB->ncb_reserve ;
if ( errNCB )
{
VxdTearDownSession( pDeviceContext,
pSessSetupCont->pConnEle,
pSessSetupCont,
NULL ) ;
break ;
}
//
// Put the connection in our LSN table and copy out the connecting
// name if necessary
//
if ( !NBRegister( pDeviceContext,
&pNCB->ncb_lsn,
(tCONNECTELE *) ulExtra,
NB_SESSION ))
{
VxdTearDownSession( pDeviceContext,
(tCONNECTELE *) ulExtra,
NULL,
NULL ) ;
errNCB = NRC_LOCTFUL ;
}
else
{
tCONNECTELE * pConnEle = (tCONNECTELE*) ulExtra ;
//
// Were we listenning for '*'? If so, copy out the connecting
// name.
//
if ( pSessSetupCont->fIsWorldListen )
{
DbgPrint( "VxdIoComplete: World listen accepted \"" ) ;
DbgPrint( pConnEle->RemoteName ) ;
DbgPrint("\" for connection endpoint\r\n") ;
CTEMemCopy( pNCB->ncb_callname,
pConnEle->RemoteName,
NCBNAMSZ ) ;
}
((tCONNECTELE *)ulExtra)->RTO = pNCB->ncb_rto ;
((tCONNECTELE *)ulExtra)->STO = pNCB->ncb_sto ;
((tCONNECTELE *)ulExtra)->Flags = 0 ;
//
// Don't delete the connection element until the client has been
// notified that the connection is down
//
((tCONNECTELE *)ulExtra)->RefCount++ ;
DbgPrint("\tRegistered Session number ") ;
DbgPrintNum( pNCB->ncb_lsn ) ;
DbgPrint(" for Connection Element ") ;
DbgPrintNum( (ULONG) pConnEle ) ;
DbgPrint("\r\n") ;
}
FreeSessSetupContext( pSessSetupCont ) ;
//
// If we're in WAITACCEPT, then this is a Listen that needs to
// be accepted
//
if ( ((tCONNECTELE *)ulExtra)->state == NBT_SESSION_WAITACCEPT )
{
//
// Accept the connection
//
VxdAccept( (tCONNECTELE *) ulExtra, NULL ) ;
}
break ;
case NCBRESET:
pRstCont = (PRESET_CONTEXT) pNCB->ncb_reserve ;
if ( pRstCont->cActiveSessions != -1 ||
pRstCont->cActiveNames )
{
DbgPrint("VxdIoComplete: Disconnect/Name de-reg completed from Reset, remaining disconnects, names: ") ;
DbgPrintNum( pRstCont->cActiveSessions ) ;
DbgPrintNum( pRstCont->cActiveNames ) ; DbgPrint("\r\n") ;
//
// Only complete the Reset NCB after all session have been
// disconnected and the names have been released on the network
//
if ( pRstCont->cActiveSessions != -1 )
{
if ( --pRstCont->cActiveSessions == 0 )
{
//
// Starts the name deletion process
//
pRstCont->cActiveSessions = -1 ;
VxdResetContinue( pDeviceContext, pNCB ) ;
}
return ;
}
if ( --pRstCont->cActiveNames != 0 )
{
return ;
}
}
if ( !errNCB )
errNCB = pRstCont->errncb ;
// Fall through
case NCBUNLINK:
pNCB->ncb_retcode = errNCB ;
pNCB->ncb_cmd_cplt = errNCB ;
goto SkipPost ;
case NCBCANCEL:
pNCB->ncb_retcode = errNCB ;
pNCB->ncb_cmd_cplt = errNCB ;
break;
case NCBHANGUP:
case NCBDELNAME:
case NCBTRACE:
break ;
default:
DbgPrint("VxdIoComplete: Unexpected NCB command: 0x") ;
DbgPrintNum( pNCB->ncb_command ) ; DbgPrint("\r\n") ;
break ;
}
if ( pNCB->ncb_retcode == NRC_PENDING )
{
pNCB->ncb_retcode = errNCB ;
pNCB->ncb_cmd_cplt = errNCB ;
}
else
{
if ( (pNCB->ncb_command & ~ASYNCH) != NCBCANCEL )
{
CTEPrint("VxdIoComplete: ncb_retcode already set!\r\n") ;
CTEPrint("\tCommand: 0x") ; DbgPrintNum(pNCB->ncb_command) ;
CTEPrint(" NCB Address: 0x") ; DbgPrintNum( (ULONG) pNCB ) ;
}
goto SkipPost ;
}
//
// call the post-routine only if this was a no-wait call and if
// the post-routine has been specified!
//
if ( fAsync && pNCB->ncb_post )
{
typedef void (CALLBACK * VXDNCBPost )( void ) ;
VXDNCBPost ncbpost = (VXDNCBPost) pNCB->ncb_post ;
//
// Clients are expecting EBX to point to the NCB (instead of
// pushing it on the stack...). The post routine may trash
// ebp also, so save it.
//
_asm push ebp ;
_asm mov ebx, pNCB ;
ncbpost() ;
_asm pop ebp ;
}
SkipPost:
//
// Now that we've completed the NCB, unblock if it was a Wait NCB
//
if ( !fAsync )
{
PBLOCKING_NCB_CONTEXT pBlkNcbContext;
PLIST_ENTRY pHead, pEntry ;
//
// find the blocking ncb context from the list corresponding to this ncb
//
pHead = &NbtConfig.BlockingNcbs;
pEntry = pHead->Flink;
while( pEntry != pHead )
{
pBlkNcbContext = CONTAINING_RECORD( pEntry, BLOCKING_NCB_CONTEXT, Linkage ) ;
if (pBlkNcbContext->pNCB == pNCB)
break;
else
pBlkNcbContext = NULL;
pEntry = pEntry->Flink;
}
if (pBlkNcbContext)
{
ASSERT(pBlkNcbContext->Verify == NBT_VERIFY_BLOCKING_NCB);
//
// if the ncb is blocked for completion, remove the context from
// the list first (important!) and then signal the thread that we
// are done. Then free the memory.
//
if ( pBlkNcbContext->fBlocked )
{
RemoveEntryList(&pBlkNcbContext->Linkage);
CTESignal( pBlkNcbContext->pWaitNCBBlock, 0 ) ;
CTEFreeMem(pBlkNcbContext->pWaitNCBBlock);
CTEFreeMem(pBlkNcbContext);
}
else
{
pBlkNcbContext->fNCBCompleted = TRUE;
}
}
else
{
DbgPrint("VxdIoComplete: didn't find blocking ncb context\r\n") ;
DbgPrint("for NCB Address: 0x") ; DbgPrintNum( (ULONG) pNCB ) ;
}
}
}
/*******************************************************************
NAME: VxdInitSessionSetup
SYNOPSIS: Common initialization required for Call and Listen
ENTRY: pDeviceContext - Adapter to setup on
pRequest - Request to fill in if successful
ppSessSetupContext - Context to be filled
pNCB - NCB doing the call/listen
EXIT:
RETURNS: NRC_GOODRET if successful, error code otherwise
NOTES:
HISTORY:
Johnl 26-May-1993 Created
********************************************************************/
NCBERR VxdInitSessionSetup( tDEVICECONTEXT * pDeviceContext,
TDI_REQUEST * pRequest,
PSESS_SETUP_CONTEXT * ppSessSetupContext,
NCB * pNCB )
{
NTSTATUS status;
NCBERR errNCB ;
TDI_REQUEST_STATUS RequestStatus ;
tCLIENTELE * pClientEle ;
tCONNECTELE * pConnEle ;
tNAMEADDR * pNameAddr ;
USHORT NameType ;
BOOL fIsListen = ((pNCB->ncb_command & ~ASYNCH)
== NCBLISTEN) ;
*ppSessSetupContext = NULL ;
//
// Lookup the Client Element associated with this name and verify
// it's valid
//
if ( errNCB = VxdNameToClient( pDeviceContext,
pNCB->ncb_name,
NULL,
&pClientEle ))
{
return errNCB ;
}
if ( pClientEle->fDeregistered )
return NRC_NOWILD ;
//
// Request.Handle.ConnectionContext will contain Connection
// element after we open the connection
//
if ( status = NbtOpenConnection( pRequest,
NULL, //ConnectionContext, // Passed to connect and disconnect handlers
pDeviceContext ) )
{
return MapTDIStatus2NCBErr( status ) ;
}
//
// Initialize the connection context (used by Vxd disconnect handler)
//
pConnEle = (tCONNECTELE *) pRequest->Handle.ConnectionContext ;
pConnEle->ConnectContext = pConnEle ;
if ( status = NbtAssociateAddress( pRequest,
pClientEle,
NULL ))
{
goto ErrorExit1 ;
}
ASSERT( sizeof( SESS_SETUP_CONTEXT ) <= (sizeof( pNCB->ncb_reserve ) +
sizeof( pNCB->ncb_event )) ) ;
*ppSessSetupContext = (PSESS_SETUP_CONTEXT) pNCB->ncb_reserve ;
if ( status = AllocSessSetupContext( *ppSessSetupContext,
*pNCB->ncb_callname == '*' ) )
goto ErrorExit0 ;
//
// Listen for '*' uses a NULL Request remote address
//
if ( *pNCB->ncb_callname != '*' )
{
InitNBTDIConnectInfo( (*ppSessSetupContext)->pRequestConnect,
(*ppSessSetupContext)->pRequestConnect->RemoteAddress,
pNCB->ncb_callname ) ;
}
InitNBTDIConnectInfo( (*ppSessSetupContext)->pReturnConnect,
(*ppSessSetupContext)->pReturnConnect->RemoteAddress,
pNCB->ncb_name ) ;
(*ppSessSetupContext)->fIsWorldListen = (pNCB->ncb_callname[0] == '*') ;
(*ppSessSetupContext)->pConnEle = pConnEle ;
return NRC_GOODRET ;
ErrorExit0:
if ( !(NbtDisassociateAddress( pRequest ) == TDI_SUCCESS))
CTEPrint("VxdInitSessionSetup: AllocSesssetupContext failed and DisassociateAddress failed\r\n") ;
ErrorExit1:
REQUIRE( NbtCloseConnection( pRequest,
&RequestStatus,
pDeviceContext,
NULL ) == TDI_SUCCESS ) ;
return MapTDIStatus2NCBErr( status ) ;
}
/*******************************************************************
NAME: VxdFindClientElement
SYNOPSIS: Finds the appropriate client element
ENTRY: pDeviceContext - Device to search on
ncbnum - NCB Name Number
ppClientEle - Receives result of search
Type - If CLIENT_BC (broadcast), then the Broadcast client
element for the pDeviceContext adapter is returned
RETURNS: STATUS_SUCCESS if the name is found, error code otherwise
NOTES: The device context points to a list of Address elements (one
address element for each Netbios name in the system). Each
address element has a Client Element list hanging off of it.
We return the first client element off of the Address Element
as there shouldn't be more then one client (is this true?).
HISTORY:
Johnl 23-Apr-1993 Created
********************************************************************/
NCBERR VxdFindClientElement( tDEVICECONTEXT * pDeviceContext,
UCHAR ncbnum,
tCLIENTELE * * ppClientEle,
enum CLIENT_TYPE Type )
{
ASSERT( pDeviceContext != NULL ) ;
if ( !pDeviceContext )
return NRC_SYSTEM ;
if ( Type != CLIENT_BC )
{
if ( ncbnum > pDeviceContext->cMaxNames || !pDeviceContext->pNameTable[ncbnum] )
return NRC_ILLNN ;
*ppClientEle = (tCLIENTELE *) pDeviceContext->pNameTable[ncbnum] ;
return NRC_GOODRET ;
}
else
{
NTSTATUS status;
tCLIENTELE * pClientEleBcast ;
UCHAR pName[NETBIOS_NAME_SIZE];
tNAMEADDR * pNameAddr;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
//
// find the * name in the local hash table
//
CTEZeroMemory(pName,NETBIOS_NAME_SIZE);
pName[0] = '*';
status = FindInHashTable(NbtConfig.pLocalHashTbl,
pName,
NbtConfig.pScope,
&pNameAddr);
if (NT_SUCCESS(status))
{
pHead = &pNameAddr->pAddressEle->ClientHead;
pEntry = pHead->Flink;
while ( pEntry != pHead )
{
pClientEleBcast = CONTAINING_RECORD( pEntry, tCLIENTELE, Linkage ) ;
if ( pClientEleBcast->pDeviceContext == pDeviceContext )
{
*ppClientEle = pClientEleBcast ;
break ;
}
pEntry = pEntry->Flink ;
}
}
else
{
return(NRC_ILLNN);
}
if ( pEntry == pHead )
return NRC_ILLNN ;
}
return NRC_GOODRET ;
}
/*******************************************************************
NAME: VxdFindConnectElement
SYNOPSIS: Finds the appropriate connect element from the
session number
ENTRY: pDeviceContext - Device to search on
lsn - NCB LS Number
ppConnectEle - Receives result of search
RETURNS: NRC_GOODRET if successful, error otherwise
NOTES: LSN 0 will be disallowed because pSessionTable[0] is always
NULL
HISTORY:
Johnl 23-Apr-1993 Created
********************************************************************/
NCBERR VxdFindConnectElement( tDEVICECONTEXT * pDeviceContext,
NCB * pNCB,
tCONNECTELE * * ppConnectEle )
{
UCHAR lsn ;
ASSERT( pNCB != NULL ) ;
ASSERT( pDeviceContext != NULL ) ;
if ( !pDeviceContext || !pNCB )
return NRC_SYSTEM ;
lsn = pNCB->ncb_lsn ;
if ( lsn > pDeviceContext->cMaxSessions || !pDeviceContext->pSessionTable[lsn] )
return NRC_SNUMOUT ;
*ppConnectEle = pDeviceContext->pSessionTable[lsn] ;
//
// Check to see if the connection is down but the NB client hasn't been
// notified, if so notify them and remove the session from the table
//
if ( ( (*ppConnectEle)->state == NBT_ASSOCIATED ||
(*ppConnectEle)->state == NBT_IDLE ) &&
(*ppConnectEle)->RefCount == 1 )
{
DbgPrint("VxdFindConnectElement: Deleting connection element\r\n") ;
NbtDereferenceConnection( *ppConnectEle ) ;
REQUIRE( NBUnregister( pDeviceContext, lsn, NB_SESSION )) ;
return NRC_SCLOSED ;
}
return NRC_GOODRET ;
}
/*******************************************************************
NAME: VxdFindLSN
SYNOPSIS: Finds a session number from its tCONNECTELE *.
ENTRY: pDeviceContext - Device to search on
pConnectEle - Connect element to find
plsn - Index pConnectEle was found at
NOTES:
HISTORY:
Johnl 07-Jul-1993 Created
********************************************************************/
NCBERR VxdFindLSN( tDEVICECONTEXT * pDeviceContext,
tCONNECTELE * pConnectEle,
UCHAR * plsn )
{
ASSERT( (pDeviceContext != NULL) && (pConnectEle != NULL) && (plsn != NULL)) ;
ASSERT( pConnectEle->Verify == NBT_VERIFY_CONNECTION ||
pConnectEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ;
for ( *plsn = 0 ; *plsn <= pDeviceContext->cMaxSessions ; (*plsn)++ )
{
if ( pDeviceContext->pSessionTable[*plsn] == pConnectEle )
return NRC_GOODRET ;
}
ASSERT( FALSE ) ;
return NRC_SNUMOUT ;
}
/*******************************************************************
NAME: VxdFindNameNum
SYNOPSIS: Finds a name number from its tADDRESSELE *.
ENTRY: pDeviceContext - Device to search on
pAddressEle - Address element to find
pNum - Index pAddressEle was found at
NOTES:
HISTORY:
Johnl 07-Jul-1993 Created
********************************************************************/
NCBERR VxdFindNameNum( tDEVICECONTEXT * pDeviceContext,
tADDRESSELE * pAddressEle,
UCHAR * pNum )
{
tCLIENTELE *pClientEle;
ASSERT( (pDeviceContext != NULL) && (pAddressEle != NULL) && (pNum != NULL)) ;
ASSERT( pAddressEle->Verify == NBT_VERIFY_ADDRESS ) ;
for ( *pNum = 0 ; *pNum <= pDeviceContext->cMaxNames ; (*pNum)++ )
{
pClientEle = pDeviceContext->pNameTable[*pNum];
if ( (pClientEle) && (pClientEle->pAddress == pAddressEle ) )
return NRC_GOODRET ;
}
return NRC_ILLNN ;
}
/*******************************************************************
NAME: VxdNameToClient
SYNOPSIS: Converts a ncb_callname to the corresponding client element
in the name table
ENTRY: pDeviceContext - Device to search on
pchName - Name to find
pNameNum - Index into table name number is at (Optional)
ppClientEle - Client element in the name table
NOTES:
HISTORY:
Johnl 15-Oct-1993 Created
********************************************************************/
NCBERR VxdNameToClient( tDEVICECONTEXT * pDeviceContext,
CHAR * pName,
UCHAR * pNameNum,
tCLIENTELE * * ppClientEle )
{
USHORT NameType ;
tNAMEADDR * pNameAddr ;
UCHAR NameNum ;
NTSTATUS status;
//
// Lookup the Client Element associated with this name
//
if ( pName[0] == '*' )
{
return NRC_NOWILD ; // Also means name not found
}
status = FindInHashTable(
NbtConfig.pLocalHashTbl,
pName,
NbtConfig.pScope,
&pNameAddr);
if (!NT_SUCCESS(status))
{
return NRC_NOWILD ; // Also means name not found
}
//
// if the name is not registered on the adapter (provided by the client)
// tell the client so!
//
if ( pNameAddr->AdapterMask &&
!(pNameAddr->AdapterMask & pDeviceContext->AdapterNumber) )
{
DbgPrint("VxdNameToClient: wrong DeviceContext element\r\n") ;
return NRC_NOWILD ;
}
if ( VxdFindNameNum( pDeviceContext, pNameAddr->pAddressEle, &NameNum ))
{
ASSERT( FALSE ) ;
return NRC_NOWILD ;
}
REQUIRE( !VxdFindClientElement( pDeviceContext,
NameNum,
ppClientEle,
CLIENT_LOCAL )) ;
ASSERT( (*ppClientEle)->Verify == NBT_VERIFY_CLIENT ) ;
if ( pNameNum != NULL )
*pNameNum = NameNum ;
return NRC_GOODRET ;
}
/*******************************************************************
NAME: NBRegister
SYNOPSIS: Finds the next available slot in apElem and assigns
pElem to that slot according to Netbios rules
ENTRY: pDeviceContext - Adapter we are adding name to
pNCBNum - Receives the found free slot
pElem - Element we are registering
NbTable - Indicates the Name table or session table
EXIT: *pNCBNum will point to the found slot and
apElem[*pNCBNum] will point to pElem
RETURNS: TRUE if we found a free slot, FALSE if the table was
full.
NOTES: The Netbios spec states that returned NCB nums and Logical
Session numbers increase to 254 until they wrap to 1 (0
is reserved for the adapter name).
HISTORY:
Johnl 28-Apr-1993 Created
********************************************************************/
BOOL NBRegister( tDEVICECONTEXT * pDeviceContext,
UCHAR * pNCBNum,
PVOID pElem,
NB_TABLE_TYPE NbTable )
{
UCHAR i ;
BOOL fFound = FALSE ;
BOOL fPassTwo = FALSE ;
UCHAR MaxNCBNum ;
UCHAR * piCurrent ;
PVOID * apElem ;
ASSERT( pElem != NULL ) ;
if ( NbTable == NB_NAME )
{
MaxNCBNum = pDeviceContext->cMaxNames ;
apElem = pDeviceContext->pNameTable ;
piCurrent = &pDeviceContext->iNcbNum ;
}
else
{
MaxNCBNum = pDeviceContext->cMaxSessions ;
apElem = pDeviceContext->pSessionTable ;
piCurrent = &pDeviceContext->iLSNum ;
}
//
// Find the next free name number and store it in pNCBNum
//
for ( i = *piCurrent ; ; i++ )
{
if ( i > MaxNCBNum )
i = 1 ;
if ( !apElem[i] )
{
fFound = TRUE ;
break ;
}
//
// Second time we hit *piCurrent means there are no free slots
//
if ( i == *piCurrent)
{
if ( fPassTwo )
break ;
else
fPassTwo = TRUE ;
}
}
if ( fFound )
{
apElem[i] = pElem ;
*pNCBNum = *piCurrent = i ;
(*piCurrent)++ ;
if ( *piCurrent > MaxNCBNum )
*piCurrent = 1 ;
}
return fFound ;
}
/*******************************************************************
NAME: NBUnregister
SYNOPSIS: Invalidates the passed netbios number
ENTRY: NCBNum - Name number to unregister
EXIT: The name number entry will be set to NULL
RETURNS: TRUE if we freed the slot, FALSE if the name wasn't
registered in the first place or it's out of range
NOTES:
HISTORY:
Johnl 05-May-1993 Created
********************************************************************/
BOOL NBUnregister( tDEVICECONTEXT * pDeviceContext,
UCHAR NCBNum,
NB_TABLE_TYPE NbTable )
{
UCHAR MaxNCBNum ;
PVOID * apElem ;
if ( NbTable == NB_NAME )
{
MaxNCBNum = pDeviceContext->cMaxNames ;
apElem = pDeviceContext->pNameTable ;
}
else
{
MaxNCBNum = pDeviceContext->cMaxSessions ;
apElem = pDeviceContext->pSessionTable ;
}
if ( NCBNum > MaxNCBNum || apElem[NCBNum] == NULL )
{
return FALSE ;
}
apElem[NCBNum] = NULL ;
return TRUE ;
}
/*******************************************************************
NAME: VxdCompleteSessionNcbs
SYNOPSIS: Finds all NCBs attached to a session and completes them
ENTRY: pDeviceContext - Device we are on
pConnEle - Session connection element to complete NCBs on
NOTES:
HISTORY:
Johnl 16-Aug-1993 Broke out as common code
********************************************************************/
TDI_STATUS VxdCompleteSessionNcbs( tDEVICECONTEXT * pDeviceContext,
tCONNECTELE * pConnEle )
{
PLIST_ENTRY pHead, pEntry ;
PRCV_CONTEXT prcvCont ;
BOOL fCompleteToClient = TRUE ;
UCHAR lsn ;
NCBERR errNCB ;
BOOL fAnyFound = FALSE ;
ASSERT( pConnEle != NULL ) ;
ASSERT( pConnEle->Verify == NBT_VERIFY_CONNECTION ||
pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN ) ;
if ( errNCB = VxdFindLSN( pDeviceContext,
pConnEle,
&lsn ))
{
//
// This shouldn't happen but watch for it in case we get in a
// weird situation
//
DbgPrint("VxdCompleteSessionNCBs - Warning: VxdFindLsn failed\r\n") ;
return STATUS_UNSUCCESSFUL ;
}
//
// Complete the first RcvAny
//
if ( pConnEle->pClientEle &&
!IsListEmpty( &pConnEle->pClientEle->RcvAnyHead ))
{
pEntry = RemoveHeadList( &pConnEle->pClientEle->RcvAnyHead ) ;
prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
//
// Set the session number so the client knows which session is going
// away.
//
prcvCont->pNCB->ncb_lsn = lsn ;
CTEIoComplete( prcvCont->pNCB,
STATUS_CONNECTION_DISCONNECTED,
0 ) ;
fAnyFound = TRUE ;
}
//
// Now kill all of the outstanding receives. Sends are completed as
// they are submitted so nothing to kill.
//
while ( !IsListEmpty( &pConnEle->RcvHead ))
{
pEntry = RemoveHeadList( &pConnEle->RcvHead ) ;
prcvCont = CONTAINING_RECORD( pEntry, RCV_CONTEXT, ListEntry ) ;
ASSERT( prcvCont->Signature == RCVCONT_SIGN ) ;
CTEIoComplete( prcvCont->pNCB,
STATUS_CONNECTION_DISCONNECTED,
0 ) ;
fAnyFound = TRUE ;
}
//
// Once the client has been notified, deref the connection
// element so the memory will be deleted when the connection is
// closed. If the client wasn't notified, then the connection remains
// in our table until the next NCB on this session.
//
if ( fAnyFound &&
!(pConnEle->Flags & NB_CLIENT_NOTIFIED) )
{
DbgPrint("CompleteSessionNcbs - Marking connection as notified\r\n") ;
pConnEle->Flags |= NB_CLIENT_NOTIFIED ;
NbtDereferenceConnection( pConnEle ) ;
}
return TDI_SUCCESS ;
}
/*******************************************************************
NAME: VxdTearDownSession
SYNOPSIS: Closes a session and deletes its session context
ENTRY: pConnEle - Pointer to connection session element to close
pCont - Session context to delete (or NULL to ignore)
pSessSetupContext - Session context to delete if non-NULL
pNCB - NCB to complete after disconnect finishes
NOTES:
HISTORY:
Johnl 16-Aug-1993 Commonized
********************************************************************/
void VxdTearDownSession( tDEVICECONTEXT * pDeviceContext,
tCONNECTELE * pConnEle,
PSESS_SETUP_CONTEXT pSessSetupContext,
NCB * pNCB )
{
TDI_STATUS tdistatus ;
TDI_REQUEST Request ;
if ( pConnEle != NULL )
{
ASSERT((pConnEle->Verify == NBT_VERIFY_CONNECTION) ||
(pConnEle->Verify == NBT_VERIFY_CONNECTION_DOWN)) ;
Request.Handle.ConnectionContext = pConnEle ;
tdistatus = NbtDisconnect( &Request, 0, TDI_DISCONNECT_ABORT, NULL, NULL, NULL ) ;
if ( tdistatus && tdistatus != TDI_PENDING )
{
DbgPrint("VxdTearDownSession - NbtDisconnect returned error " ) ;
DbgPrintNum( tdistatus ) ;
DbgPrint("\r\n") ;
}
tdistatus = NbtCloseConnection( &Request,
NULL,
pDeviceContext,
NULL ) ;
if ( tdistatus && tdistatus != TDI_PENDING )
{
DbgPrint("VxdTearDownSession - NbtCloseConnection returned error " ) ;
DbgPrintNum( tdistatus ) ;
DbgPrint("\r\n") ;
}
}
if ( pSessSetupContext )
FreeSessSetupContext( pSessSetupContext ) ;
}
/*******************************************************************
NAME: AllocSessSetupContext
SYNOPSIS: Allocates and initializes a listen context structure
ENTRY: pSessSetupContext - Pointer to structure
fListenOnStar - TRUE if the request remote address should
be left as NULL
NOTES:
HISTORY:
Johnl 19-May-1993 Created
********************************************************************/
TDI_STATUS AllocSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext,
BOOL fListenOnStar )
{
CTEZeroMemory( pSessSetupContext, sizeof( SESS_SETUP_CONTEXT ) ) ;
if ( !(pSessSetupContext->pRequestConnect =
CTEAllocMem( sizeof( TDI_CONNECTION_INFORMATION ))) ||
!(pSessSetupContext->pReturnConnect =
CTEAllocMem( sizeof( TDI_CONNECTION_INFORMATION))) )
{
goto ErrorExit1 ;
}
pSessSetupContext->pRequestConnect->RemoteAddress = NULL ;
pSessSetupContext->pReturnConnect->RemoteAddress = NULL ;
if ( !(pSessSetupContext->pReturnConnect->RemoteAddress =
CTEAllocMem( sizeof( TA_NETBIOS_ADDRESS ))) ||
(!fListenOnStar &&
!(pSessSetupContext->pRequestConnect->RemoteAddress =
CTEAllocMem( sizeof( TA_NETBIOS_ADDRESS )))) )
{
goto ErrorExit0 ;
}
return TDI_SUCCESS ;
ErrorExit0:
if ( pSessSetupContext->pRequestConnect->RemoteAddress)
CTEFreeMem( pSessSetupContext->pRequestConnect->RemoteAddress ) ;
if ( pSessSetupContext->pReturnConnect->RemoteAddress)
CTEFreeMem( pSessSetupContext->pReturnConnect->RemoteAddress ) ;
ErrorExit1:
if ( pSessSetupContext->pRequestConnect)
CTEFreeMem( pSessSetupContext->pRequestConnect ) ;
if ( pSessSetupContext->pReturnConnect)
CTEFreeMem( pSessSetupContext->pReturnConnect ) ;
return TDI_NO_RESOURCES ;
}
/*******************************************************************
NAME: FreeSessSetupContext
SYNOPSIS: Frees a successfully initialized listen context
ENTRY: pSessSetupContext - Context to be freed
HISTORY:
Johnl 19-May-1993 Created
********************************************************************/
void FreeSessSetupContext( PSESS_SETUP_CONTEXT pSessSetupContext )
{
if ( pSessSetupContext->pRequestConnect->RemoteAddress )
CTEFreeMem( pSessSetupContext->pRequestConnect->RemoteAddress ) ;
CTEFreeMem( pSessSetupContext->pReturnConnect->RemoteAddress ) ;
CTEFreeMem( pSessSetupContext->pRequestConnect ) ;
CTEFreeMem( pSessSetupContext->pReturnConnect ) ;
}
/*******************************************************************
NAME: DelayedSessEstablish
SYNOPSIS: This routine is called by VxdScheduleDelayedEvent.
After name query is successful, we typically make a tcp
connection. We delay that step until later so that stack
usage is reduced. (yes, there is only 4k of stack on chicago!)
ENTRY: pContext - context that contains the actual parms
RETURNS: Nothing
HISTORY:
Koti Dec. 19, 94
********************************************************************/
VOID DelayedSessEstablish( PVOID pContext )
{
tDGRAM_SEND_TRACKING *pTracker;
NTSTATUS status;
COMPLETIONCLIENT pClientCompletion;
//
// get our parameters out
//
pTracker = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pTracker;
status = (NTSTATUS)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext;
pClientCompletion = ((NBT_WORK_ITEM_CONTEXT *)pContext)->ClientCompletion;
CTEMemFree(pContext);
CompleteClientReq(pClientCompletion,
pTracker,
status);
}
/*******************************************************************
NAME: VxdApiWorker
SYNOPSIS: When clients such as another vxd or a V86 app (such as
nbtstat.exe) make requests for information or some service,
this is the routine that gets called.
ENTRY: OpCode - what info or service is being requested
ClientBuffer - buffer in which to pass info
ClientBufLen - how big is the buffer
RETURNS: ErrorCode from the operation (0 if success)
HISTORY:
Koti 16-Jun-1994 Created
********************************************************************/
NTSTATUS
VxdApiWorker(
DWORD Ioctl,
PVOID ClientOutBuffer,
DWORD ClientOutBufLen,
PVOID ClientInBuffer,
DWORD ClientInBufLen,
DWORD fOkToTrashInputBuffer
)
{
NTSTATUS status;
USHORT OpCode;
int i;
USHORT NumLanas;
PCHAR pchBuffer;
DWORD dwSize;
DWORD dwBytesToCopy;
PULONG pIpAddr;
PLIST_ENTRY pEntry,pHead;
tDEVICECONTEXT *pDeviceContext;
NCB ncb;
UCHAR retcode;
tIPANDNAMEINFO *pIpAndNameInfo;
tIPCONFIG_INFO *pIpCfg;
status = STATUS_SUCCESS;
dwSize = ClientOutBufLen;
// always use the first adapter on the list
pDeviceContext = CONTAINING_RECORD(NbtConfig.DeviceContexts.Flink,tDEVICECONTEXT,Linkage);
OpCode = (USHORT)Ioctl;
switch (OpCode)
{
// nbtstat -<any option>
case IOCTL_NETBT_GET_IP_ADDRS :
if (ClientOutBufLen < sizeof(ULONG)*(NbtConfig.AdapterCount + 1))
{
return( STATUS_BUFFER_OVERFLOW );
}
if (!ClientOutBuffer)
{
return( STATUS_INVALID_PARAMETER );
}
pIpAddr = (PULONG )ClientOutBuffer;
pEntry = pHead = &NbtConfig.DeviceContexts;
while ((pEntry = pEntry->Flink) != pHead)
{
pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
if (pDeviceContext->IpAddress)
{
*pIpAddr = pDeviceContext->IpAddress;
pIpAddr++;
}
}
//
// put a 0 address on the end
//
*pIpAddr = 0;
status = STATUS_SUCCESS;
break;
// nbtstat -n (or -N)
case IOCTL_NETBT_GET_LOCAL_NAMES :
// nbtstat -c
case IOCTL_NETBT_GET_REMOTE_NAMES :
if (!ClientOutBuffer || ClientOutBufLen == 0)
return (STATUS_INSUFFICIENT_RESOURCES);
if (OpCode == IOCTL_NETBT_GET_REMOTE_NAMES )
{
// make this null, so NbtQueryAda..() knows this is for remote
pDeviceContext = NULL;
}
// return an array of netbios names that are registered
status = NbtQueryAdapterStatus(pDeviceContext,
&pchBuffer,
&dwSize);
break;
// nbtstat -r
case IOCTL_NETBT_GET_BCAST_NAMES :
// return an array of netbios names that are registered
status = NbtQueryBcastVsWins(pDeviceContext,&pchBuffer,&dwSize);
break;
// nbtstat -R
case IOCTL_NETBT_PURGE_CACHE :
status = NbtResyncRemoteCache();
break;
// nbtstat -s, nbtstat -S
case IOCTL_NETBT_GET_CONNECTIONS :
// return an array of netbios names that are registered
status = NbtQueryConnectionList(NULL,
&pchBuffer,
&dwSize);
break;
// nbtstat -a, nbtstat -A
case IOCTL_NETBT_ADAPTER_STATUS:
if (!ClientOutBuffer)
{
return( STATUS_INVALID_PARAMETER );
}
CTEZeroMemory( &ncb, sizeof(NCB) );
ncb.ncb_command = NCBASTAT;
ncb.ncb_buffer = ClientOutBuffer;
ncb.ncb_length = ClientOutBufLen;
ncb.ncb_lana_num = pDeviceContext->iLana;
if (!ClientInBuffer)
{
return( STATUS_INVALID_PARAMETER );
}
pIpAndNameInfo = (tIPANDNAMEINFO *)ClientInBuffer;
//
// see if Ipaddress is specified: if yes, use it
//
if ( pIpAndNameInfo->IpAddress )
{
ncb.ncb_callname[0] = '*';
retcode = VNBT_NCB_X( &ncb, 0, &pIpAndNameInfo->IpAddress, 0, 0 );
}
//
// no ipaddress: use the name that's given to us
//
else
{
CTEMemCopy(
&ncb.ncb_callname[0],
&(pIpAndNameInfo->NetbiosAddress.Address[0].Address[0].NetbiosName[0]),
NCBNAMSZ );
retcode = VNBT_NCB_X( &ncb, 0, 0, 0, 0 );
}
status = STATUS_UNSUCCESSFUL;
if (!retcode)
{
if (ncb.ncb_retcode == NRC_GOODRET)
status = STATUS_SUCCESS;
else if (ncb.ncb_retcode == NRC_INCOMP)
status = TDI_BUFFER_OVERFLOW;
}
break;
// ipconfig queries us for nodetype and scope
case IOCTL_NETBT_IPCONFIG_INFO:
dwBytesToCopy = sizeof(tIPCONFIG_INFO) +
NbtConfig.ScopeLength;
if ( !ClientOutBuffer || ClientOutBufLen < dwBytesToCopy )
{
status = STATUS_BUFFER_OVERFLOW;
break;
}
pIpCfg = (tIPCONFIG_INFO *)ClientOutBuffer;
NumLanas = 0;
for ( i = 0; i < NBT_MAX_LANAS; i++)
{
if (LanaTable[i].pDeviceContext != NULL)
{
pDeviceContext = LanaTable[i].pDeviceContext;
pIpCfg->LanaInfo[NumLanas].LanaNumber = pDeviceContext->iLana;
pIpCfg->LanaInfo[NumLanas].IpAddress = pDeviceContext->IpAddress;
pIpCfg->LanaInfo[NumLanas].NameServerAddress = pDeviceContext->lNameServerAddress;
pIpCfg->LanaInfo[NumLanas].BackupServer = pDeviceContext->lBackupServer;
pIpCfg->LanaInfo[NumLanas].lDnsServerAddress = pDeviceContext->lDnsServerAddress;
pIpCfg->LanaInfo[NumLanas].lDnsBackupServer = pDeviceContext->lDnsBackupServer;
NumLanas++;
}
}
pIpCfg->NumLanas = NumLanas;
pIpCfg->NodeType = NodeType;
pIpCfg->ScopeLength = NbtConfig.ScopeLength;
CTEMemCopy( &pIpCfg->szScope[0],
NbtConfig.pScope,
NbtConfig.ScopeLength );
status = STATUS_SUCCESS;
break;
default:
status = STATUS_NOT_SUPPORTED;
break;
}
//
// Copy the output into user's buffer
//
if ( (OpCode == IOCTL_NETBT_GET_LOCAL_NAMES) ||
(OpCode == IOCTL_NETBT_GET_REMOTE_NAMES) ||
(OpCode == IOCTL_NETBT_GET_CONNECTIONS) ||
(OpCode == IOCTL_NETBT_GET_BCAST_NAMES) )
{
if ( NT_SUCCESS(status) || (status == STATUS_BUFFER_OVERFLOW))
{
if ( status == STATUS_BUFFER_OVERFLOW )
{
dwBytesToCopy = ClientOutBufLen;
}
else
{
dwBytesToCopy = dwSize;
status = STATUS_SUCCESS;
}
CTEMemCopy( ClientOutBuffer, pchBuffer, dwBytesToCopy ) ;
CTEMemFree((PVOID)pchBuffer);
}
}
//
// we may be called either through the vxd entry point which 16 bit apps
// will do (for now, only nbtstat.exe), or through the file system api's
// which 32 bit apps will do via CreateFile and ioctl.
// If we came here through file system (i.e.VNBT_DeviceIoControl called us)
// then don't trash the input buffer since the status gets passed back as
// it is. For 16 bit apps (i.e.VNBT_Api_Handler called us), the only way
// we can pass status back (without major changes all over) is through the
// input buffer.
//
if ( ClientInBuffer && fOkToTrashInputBuffer )
{
*(NTSTATUS *)ClientInBuffer = status;
}
return( status );
}
/*******************************************************************
NAME: PostInit_Proc
SYNOPSIS: After the whole system is initialized, we get the
Sys_Vm_Init message and that's when this routine gets called.
This can be used for any post-processing, but for now we
only use it to load lmhosts (this way, we can load all the
#INCLUDE files which have UNC's in them, since now we know
the net is up).
RETURNS: ErrorCode from the operation (0 if success)
HISTORY:
Koti 12-Jul-1994 Created
********************************************************************/
NTSTATUS
PostInit_Proc()
{
LONG lRetcode;
CachePrimed = FALSE;
CTEPagedCode();
lRetcode = PrimeCache( NbtConfig.pLmHosts,
NULL,
TRUE,
NULL) ;
if (lRetcode != -1)
{
CachePrimed = TRUE ;
}
}
/*******************************************************************
NAME: CTEAllocInitMem
SYNOPSIS: Allocates memory during driver initialization
NOTES: If first allocation fails, we refill the heap spare and
try again. We can only do this during driver initialization
because the act of refilling may yield the current
thread.
HISTORY:
Johnl 27-Aug-1993 Created
********************************************************************/
PVOID CTEAllocInitMem( ULONG cbBuff )
{
PVOID pv = CTEAllocMem( cbBuff ) ;
if ( pv )
{
return pv ;
}
else if ( fInInit )
{
DbgPrint("CTEAllocInitMem: Failed allocation, trying again\r\n") ;
CTERefillMem() ;
pv = CTEAllocMem( cbBuff ) ;
}
return pv ;
}