diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/nbt/vxd/vxdisol.c | 4042 |
1 files changed, 4042 insertions, 0 deletions
diff --git a/private/ntos/nbt/vxd/vxdisol.c b/private/ntos/nbt/vxd/vxdisol.c new file mode 100644 index 000000000..7c5e6dfe1 --- /dev/null +++ b/private/ntos/nbt/vxd/vxdisol.c @@ -0,0 +1,4042 @@ +/**********************************************************************/ +/** 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 ; +} |