diff options
Diffstat (limited to 'private/ntos/nbt/nt')
28 files changed, 23435 insertions, 0 deletions
diff --git a/private/ntos/nbt/nt/autodial.c b/private/ntos/nbt/nt/autodial.c new file mode 100644 index 000000000..9b83f526d --- /dev/null +++ b/private/ntos/nbt/nt/autodial.c @@ -0,0 +1,723 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + This file provides routines for interacting + with the automatic connection driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 9-6-95 + +Revision History: + +--*/ +#include "nbtprocs.h" // procedure headings + +#ifdef RASAUTODIAL + +#ifndef VXD +#include <nbtioctl.h> +#include <acd.h> +#include <acdapi.h> +#endif + +// +// Automatic connection global variables. +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbt '; + +// +// Imported routines. +// +VOID +CleanUpPartialConnection( + IN NTSTATUS status, + IN tCONNECTELE *pConnEle, + IN tDGRAM_SEND_TRACKING *pTracker, + IN PIRP pClientIrp, + IN CTELockHandle irqlJointLock, + IN CTELockHandle irqlConnEle + ); + +NTSTATUS +NbtConnectCommon( + IN TDI_REQUEST *pRequest, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp + ); + + + +VOID +NbtRetryPreConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnEle = pArgs[0]; + PVOID pTimeout = pArgs[1]; + PTDI_CONNECTION_INFORMATION pCallInfo = pArgs[2]; + PTDI_CONNECTION_INFORMATION pReturnInfo = pArgs[3]; + PIRP pIrp = pArgs[4]; + TDI_REQUEST request; + KIRQL irql; + CTELockHandle OldIrq; + +#ifdef notdef // DBG + DbgPrint( + "NbtRetryPreConnect: fSuccess=%d, pIrp=0x%x, pIrp->Cancel=%d, pConnEle=0x%x\n", + fSuccess, + pIrp, + pIrp->Cancel, + pConnEle); +#endif + request.Handle.ConnectionContext = pConnEle; + status = NTCancelCancelRoutine ( pIrp ); + if ( status != STATUS_CANCELLED ) + { + // + // We're done with the connection progress, + // so clear the fAutoConnecting flag. We + // set the fAutoConnected flag to prevent us + // from re-attempting another automatic + // connection on this connection. + // + CTESpinLock(pConnEle,OldIrq); + pConnEle->fAutoConnecting = FALSE; + pConnEle->fAutoConnected = TRUE; + CTESpinFree(pConnEle,OldIrq); + + status = fSuccess ? + NbtConnectCommon( + &request, + pTimeout, + pCallInfo, + pReturnInfo, + pIrp) : + STATUS_BAD_NETWORK_PATH; + // + // We are responsible for completing + // the irp. + // + if (status != STATUS_PENDING) { + // + // Clear out the Irp pointer in the Connection object so that we dont try to + // complete it again when we clean up the connection. Do this under the connection + // lock. + // + CTESpinLock(pConnEle,OldIrq); + + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle,OldIrq); + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + } +} // NbtRetryPreConnect + + + +BOOLEAN +NbtCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ + if (nArgs != 5) + return FALSE; + + return (pArgs[4] == pArg); +} // NbtCancelAutoDialRequest + + + +BOOLEAN +NbtCancelPreConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + KIRQL irql; + ACD_ADDR addr; + BOOLEAN fCanceled; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + if (pConnEle == NULL) + return FALSE; +#ifdef notdef // DBG + DbgPrint("NbtCancelPreConnect: pIrp=0x%x, pConnEle=0x%x\n", + pIrp, + pConnEle); +#endif + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnEle->RemoteName, 16); + // + // Cancel the autodial request. + // + fCanceled = (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbtCancelAutoDialRequest, + pIrp); + if (fCanceled) + IoSetCancelRoutine(pIrp, NULL); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + // + // If the request could not be found + // in the driver, then it has already + // been completed, so we simply return. + // + if (!fCanceled) + return FALSE; + + KeRaiseIrql(DISPATCH_LEVEL, &irql); + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + + { + // + // Clear out the Irp pointer in the Connection object so that we dont try to + // complete it again when we clean up the connection. Do this under the connection + // lock. + // + // BUGBUG[WISHLIST] This should not be needed since before we call NbtConnectCommon, the Cancel routine + // is NULLed out, so it cannot happen that the pIrp ptr in the connection is set to the + // Irp, and this cancel routine is called. + // + CTELockHandle OldIrq; + + CTESpinLock(pConnEle,OldIrq); + + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle,OldIrq); + } + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + KeLowerIrql(irql); + + return TRUE; +} // NbtCancelPreConnect + + + +VOID +NbtRetryPostConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnEle = pArgs[0]; + PVOID pTimeout = pArgs[1]; + PTDI_CONNECTION_INFORMATION pCallInfo = pArgs[2]; + PTDI_CONNECTION_INFORMATION pReturnInfo = pArgs[3]; + PIRP pIrp = pArgs[4]; + tDGRAM_SEND_TRACKING *pTracker = (tDGRAM_SEND_TRACKING *)pReturnInfo; + TDI_REQUEST request; + CTELockHandle irqlConnEle, irqlJointLock; + KIRQL irql; + +#ifdef notdef // DBG + DbgPrint( + "NbtRetryPostConnect: fSuccess=%d, pIrp=0x%x, pIrp->Cancel=%d, pConnEle=0x%x\n", + fSuccess, + pIrp, + pIrp->Cancel, + pConnEle); +#endif + if (fSuccess) { + // + // We set the state here to fool NbtConnect + // into doing a reconnect. + // + request.Handle.ConnectionContext = pConnEle; + pConnEle->state = NBT_ASSOCIATED; + status = NTCancelCancelRoutine ( pIrp ); + if ( status != STATUS_CANCELLED ) + { + status = NbtConnectCommon( + &request, + pTimeout, + pCallInfo, + pReturnInfo, + pIrp); + // + // We are responsible for completing + // the irp. + // + if (status != STATUS_PENDING) { + // + // Clear out the Irp pointer in the Connection object so that we dont try to + // complete it again when we clean up the connection. Do this under the connection + // lock. + // + CTELockHandle OldIrq; + + CTESpinLock(pConnEle,OldIrq); + + ASSERT(pIrp == pConnEle->pIrp); + pConnEle->pIrp = NULL; + + CTESpinFree(pConnEle, OldIrq); + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + } + } + else { + CTESpinLock(&NbtConfig.JointLock,irqlJointLock); + CTESpinLock(pConnEle,irqlConnEle); + CleanUpPartialConnection( + STATUS_BAD_NETWORK_PATH, + pConnEle, + pTracker, + pIrp, + irqlJointLock, + irqlConnEle); + } +} // NbtRetryPostConnect + + + +VOID +NbtCancelPostConnect( + IN PIRP pIrp + ) +{ + PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + tCONNECTELE *pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + ACD_ADDR addr; + + if (pConnEle == NULL) + return; +#ifdef notdef // DBG + DbgPrint( + "NbtCancelPostConnect: pIrp=0x%x, pConnEle=0x%x\n", + pIrp, + pConnEle); +#endif + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnEle->RemoteName, 15); + // + // Cancel the autodial request. + // + (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbtCancelAutoDialRequest, + pIrp); +} // NbtCancelPostConnect + + + +BOOLEAN +NbtAttemptAutoDial( + IN tCONNECTELE *pConnEle, + IN PVOID pTimeout, + IN PTDI_CONNECTION_INFORMATION pCallInfo, + IN PTDI_CONNECTION_INFORMATION pReturnInfo, + IN PIRP pIrp, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. The first five parameters are + used in the call to NbtConnect after the connection + completes successfully. + +Arguments: + + ... + + ulFlags - automatic connection flags + + pProc - callback procedure when the automatic connection completes + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + NTSTATUS status; + BOOLEAN fSuccess; + ACD_ADDR addr; + KIRQL irql; + PVOID pArgs[5]; + PTRANSPORT_ADDRESS pRemoteAddress; + PTA_NETBIOS_ADDRESS pRemoteNetBiosAddress; + PTA_NETBIOS_EX_ADDRESS pRemoteNetbiosExAddress; + ULONG tdiAddressType; + PCHAR pName; + ULONG ulcbName; + LONG lNameType; + + // + // If this connection has already been through the + // automatic connection process, don't do it again. + // + if (pCallInfo == NULL || pConnEle->fAutoConnected) + return FALSE; + // + // Get the address of the connection. + // + pRemoteAddress = (PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress; + tdiAddressType = pRemoteAddress->Address[0].AddressType; + if (tdiAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) { + PTDI_ADDRESS_NETBIOS pNetbiosAddress; + + pRemoteNetbiosExAddress = (PTA_NETBIOS_EX_ADDRESS)pRemoteAddress; + pNetbiosAddress = &pRemoteNetbiosExAddress->Address[0].Address[0].NetbiosAddress; + pName = pNetbiosAddress->NetbiosName; + lNameType = pNetbiosAddress->NetbiosNameType; + ulcbName = pRemoteNetbiosExAddress->Address[0].AddressLength - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress) - + FIELD_OFFSET(TDI_ADDRESS_NETBIOS,NetbiosName); + IF_DBG(NBT_DEBUG_NETBIOS_EX) { + KdPrint(( + "NetBt:NETBIOS address ulNameLen(%ld) Name %16s\n", + ulcbName, + pName)); + } + status = STATUS_SUCCESS; + } + else if (tdiAddressType == TDI_ADDRESS_TYPE_NETBIOS) { + pRemoteNetBiosAddress = (PTA_NETBIOS_ADDRESS)pRemoteAddress; + status = GetNetBiosNameFromTransportAddress( + pRemoteNetBiosAddress, + &pName, + &ulcbName, + &lNameType); + } + else + status = STATUS_INVALID_ADDRESS_COMPONENT; + // + // Save the address for pre-connect attempts, + // because if we have to cancel this irp, + // it is not saved anywhere else. + // + CTESpinLock(pConnEle, irql); + CTEMemCopy(pConnEle->RemoteName, pName, NETBIOS_NAME_SIZE); + CTESpinFree(pConnEle, irql); + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pName, NETBIOS_NAME_SIZE); +#ifdef DBG + DbgPrint("NbtAttemptAutodial: szAddr=%-15.15s\n", pName); +#endif + // + // Enqueue this request on the network + // connection pending queue. + // + pArgs[0] = pConnEle; + pArgs[1] = pTimeout; + pArgs[2] = pCallInfo; + pArgs[3] = pReturnInfo; + pArgs[4] = pIrp; + fSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 5, + pArgs); + if (fSuccess) { + // + // We set the automatic connection in progress + // flag so we know to clean up the connection + // block in the automatic connection driver if + // the request gets canceled. + // + CTESpinLock(pConnEle, irql); + pConnEle->fAutoConnecting = TRUE; + CTESpinFree(pConnEle, irql); + } + + return fSuccess; +} // NbtAttemptAutoDial + + + +VOID +NbtNoteNewConnection( + IN tCONNECTELE *pConnEle, + IN tNAMEADDR *pNameAddr + ) + +/*++ + +Routine Description: + + Inform the automatic connection driver of a + successful new connection. + +Arguments: + + pConnEle - a pointer to the upper connection + + pNameAddr - a pointer to the remote name + +Return Value: + None. + +--*/ + +{ + KIRQL irql; + ACD_ADDR addr; + ACD_ADAPTER adapter; + + if (pConnEle == NULL || pConnEle->pClientEle == NULL || + pConnEle->pClientEle->pDeviceContext == NULL) + { + return; + } + // + // Get the source IP address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pNameAddr->Name, 15); + adapter.fType = ACD_ADAPTER_IP; + adapter.ulIpaddr = 0; + CTESpinLock(pConnEle, irql); + adapter.ulIpaddr = htonl(pConnEle->pClientEle->pDeviceContext->IpAddress); + CTESpinFree(pConnEle, irql); + // + // If the connection did not have a + // TCP connection associated with it, + // then we're done. + // + if (adapter.ulIpaddr == 0) + return; + (*AcdDriverG.lpfnNewConnection)(&addr, &adapter); +} // NbtNoteNewConnection + + + +VOID +NbtAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbtAcdBind + + + +VOID +NbtAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbtAcdUnbind + +#endif // RASAUTODIAL diff --git a/private/ntos/nbt/nt/ctestuff.c b/private/ntos/nbt/nt/ctestuff.c new file mode 100644 index 000000000..0d9813a6d --- /dev/null +++ b/private/ntos/nbt/nt/ctestuff.c @@ -0,0 +1,90 @@ +// +// +// CTESTUFF.C +// +// This file contains Common Transport Environment code to handle +// OS dependent functions such as allocating memory etc. +// +// +#include "nbtprocs.h" + +// to convert a millisecond to a 100ns time +// +#define MILLISEC_TO_100NS 10000 + + +//---------------------------------------------------------------------------- +PVOID +CTEStartTimer( + IN CTETimer *pTimerIn, + IN ULONG DeltaTime, + IN CTEEventRtn TimerExpiry, + IN PVOID Context OPTIONAL + ) +/*++ +Routine Description: + + This Routine starts a timer. + +Arguments: + + Timer - Timer structure + TimerExpiry - completion routine + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +{ + LARGE_INTEGER Time; + + // + // initialize the DPC to have the correct completion routine and context + // + KeInitializeDpc(&pTimerIn->t_dpc, + (PVOID)TimerExpiry, // completion routine + Context); // context value + + // + // convert to 100 ns units by multiplying by 10,000 + // + Time.QuadPart = UInt32x32To64(DeltaTime,(LONG)MILLISEC_TO_100NS); + + // + // to make a delta time, negate the time + // + Time.QuadPart = -(Time.QuadPart); + + ASSERT(Time.QuadPart < 0); + + (VOID)KeSetTimer(&pTimerIn->t_timer,Time,&pTimerIn->t_dpc); + + return(NULL); +} +//---------------------------------------------------------------------------- +VOID +CTEInitTimer( + IN CTETimer *pTimerIn + ) +/*++ +Routine Description: + + This Routine initializes a timer. + +Arguments: + + Timer - Timer structure + TimerExpiry - completion routine + +Return Value: + + PVOID - a pointer to the memory or NULL if a failure + +--*/ + +{ + KeInitializeTimer(&pTimerIn->t_timer); +} + diff --git a/private/ntos/nbt/nt/dirs b/private/ntos/nbt/nt/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/nbt/nt/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/nbt/nt/driver.c b/private/ntos/nbt/nt/driver.c new file mode 100644 index 000000000..0294ea69b --- /dev/null +++ b/private/ntos/nbt/nt/driver.c @@ -0,0 +1,1228 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Driver.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for the + NBT Transport and other routines that are specific to the NT implementation + of a driver. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + + +#include "nbtprocs.h" +#include <nbtioctl.h> + +#if DBG +// allocate storage for the global debug flag NbtDebug +#ifdef _PNP_POWER +ULONG NbtDebug=NBT_DEBUG_PNP_POWER; // NT PNP debugging +#else // _PNP_POWER +ULONG NbtDebug=0x00000000; // disable all debugging +#endif // _PNP_POWER +#endif // DBG + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +NbtDispatchCleanup( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchClose( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchCreate( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ); + +NTSTATUS +NbtDispatchDevCtrl( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +NTSTATUS +NbtDispatchInternalCtrl( + IN PDEVICE_OBJECT device, + IN PIRP irp + ); + +PFILE_FULL_EA_INFORMATION +FindInEA( + IN PFILE_FULL_EA_INFORMATION start, + IN PCHAR wanted + ); + +VOID +ReturnIrp( + IN PIRP irp, + IN int status + ); + +VOID +MakePending( + IN PIRP pIrp + ); + +#ifdef RASAUTODIAL +VOID +NbtAcdBind(); +#endif // RASAUTODIAL + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(INIT, DriverEntry) +#ifdef RASAUTODIAL +#pragma CTEMakePageable(INIT, NbtAcdBind) +#endif // RASAUTODIAL +#pragma CTEMakePageable(PAGE, NbtDispatchCleanup) +#pragma CTEMakePageable(PAGE, NbtDispatchClose) +#pragma CTEMakePageable(PAGE, NbtDispatchCreate) +#pragma CTEMakePageable(PAGE, NbtDispatchDevCtrl) +#pragma CTEMakePageable(PAGE, FindInEA) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the NBT device driver. + This routine creates the device object for the NBT + device and calls a routine to perform other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS status; + tADDRARRAY *pAddr; + tDEVICES *pBindDevices=NULL; + tDEVICES *pExportDevices=NULL; + tADDRARRAY *pAddrArray=NULL; + +#ifndef _PNP_LATER + int i; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + NTSTATUS Locstatus; + tDEVICECONTEXT *pDeviceContext; + ULONG DevicesStarted; +#endif // _PNP_POWER + + CTEPagedCode(); + +#ifdef _PNP_POWER + TdiInitialize(); +#endif + + // + // get the file system process for NBT since we need to know this for + // allocating and freeing handles + // + NbtFspProcess =(PEPROCESS)PsGetCurrentProcess(); + + // + // read in registry configuration data + // + status = NbtReadRegistry(RegistryPath, + DriverObject, + &NbtConfig, + &pBindDevices, + &pExportDevices, + &pAddrArray); + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Fatal Error - Failed registry read! status = %X\n", + status)); + return(status); + } + + + // + // Initialize NBT global data. + // + status = InitNotOs() ; + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_NON_OS_INIT,status); + KdPrint(("NBT:OS Independent initialization failed! status = %X\n", + status)); + return(status); + } + + // + // Initialize the driver object with this driver's entry points. + // + DriverObject->MajorFunction[IRP_MJ_CREATE] = + (PDRIVER_DISPATCH)NbtDispatchCreate; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + (PDRIVER_DISPATCH)NbtDispatchDevCtrl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + (PDRIVER_DISPATCH)NbtDispatchInternalCtrl; + + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = + (PDRIVER_DISPATCH)NbtDispatchCleanup; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = + (PDRIVER_DISPATCH)NbtDispatchClose; + + DriverObject->DriverUnload = NULL; + +#ifndef _PNP_LATER + + // start some timers + status = InitTimersNotOs(); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_TIMERS,status); + KdPrint(("NBT:Failed to Initialize the Timers!,status = %X\n", + status)); + StopInitTimers(); + return(status); + } + +// #else // _PNP_POWER + + // ASSERT (status == STATUS_SUCCESS); + + NbtConfig.uDevicesStarted = 0; + +#endif // _PNP_POWER + + pNbtGlobConfig->iBufferSize[eNBT_FREE_SESSION_MDLS] = sizeof(tSESSIONHDR); + pNbtGlobConfig->iBufferSize[eNBT_DGRAM_MDLS] = DGRAM_HDR_SIZE + + (pNbtGlobConfig->ScopeLength << 1); + + // create some MDLs, for session sends to speed up the sends. + status = NbtInitMdlQ( + &NbtConfig.SessionMdlFreeSingleList, + eNBT_FREE_SESSION_MDLS); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Failed to Initialize the Session MDL Queue!,status = %X\n", + status)); + return(status); + } + // create some MDLs for datagram sends + status = NbtInitMdlQ( + &NbtConfig.DgramMdlFreeSingleList, + eNBT_DGRAM_MDLS); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Failed to Initialize the Dgram MDL Queue!,status = %X\n", + status)); + return(status); + } + +#ifndef _PNP_LATER + + // + // Create the NBT device object for each adapter configured + // + pAddr = pAddrArray; + + DevicesStarted = 0; + + for (i=0; i<pNbtGlobConfig->uNumDevices; i++ ) + { + + // this call ultimately allocates storage for the returned NameString + // that holds the Ipaddress + status = NbtCreateDeviceObject( + DriverObject, + pNbtGlobConfig, + &pBindDevices->Names[i], + &pExportDevices->Names[i], + pAddr, + RegistryPath, +#ifndef _IO_DELETE_DEVICE_SUPPORTED + FALSE, +#endif + &pDeviceContext); + + // for a Bnode there are no Wins server addresses, so this Ptr can + // be null. + if (pAddr) + { + pAddr++; + } + + // + // allow not having an address to succeed - DHCP will + // provide an address later + // + if (pDeviceContext != NULL) + { + if (status == STATUS_INVALID_ADDRESS) + { + // + // set to null so we know not to allow connections or dgram + // sends on this adapter + // + pDeviceContext->IpAddress = 0; + + DevicesStarted++; + + status = STATUS_SUCCESS; + + } + + + if ((NT_SUCCESS(status)) || + (status == STATUS_INVALID_ADDRESS_COMPONENT)) { + + status = STATUS_SUCCESS; + NbtConfig.uDevicesStarted++; + } + + + { + // + // We can tolerate the invalid address component failure since IP does not know of + // static addresses at this time. + // + if (!NT_SUCCESS(status)) + { + + pDeviceContext->RegistrationHandle = NULL; + + KdPrint((" Create Device Object Failed with status= %X, num devices = %X\n",status, + NbtConfig.uNumDevices)); + + NbtLogEvent(EVENT_NBT_CREATE_DEVICE,status); + // + // this device will not be started so decrement the count of started + // ones. + // + NbtConfig.AdapterCount--; + + // + // cleanup the mess and free the device object since we had some + // sort of failure. + // + pHead = &NbtConfig.DeviceContexts; + pEntry = RemoveTailList(pHead); + + ASSERT (pDeviceContext == CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage)); + + if (pDeviceContext->hNameServer) + { + ObDereferenceObject(pDeviceContext->pNameServerFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hNameServer); + KdPrint(("Close NameSrv File status = %X\n",Locstatus)); + } + if (pDeviceContext->hDgram) + { + ObDereferenceObject(pDeviceContext->pDgramFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hDgram); + KdPrint(("Close Dgram File status = %X\n",Locstatus)); + } + if (pDeviceContext->hSession) + { + ObDereferenceObject(pDeviceContext->pSessionFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hSession); + KdPrint(("Close Session File status = %X\n",Locstatus)); + } + if (pDeviceContext->hControl) + { + ObDereferenceObject(pDeviceContext->pControlFileObject); + Locstatus = NTZwCloseFile(pDeviceContext->hControl); + KdPrint(("Close Control File status = %X\n",Locstatus)); + } + + IoDeleteDevice((PDEVICE_OBJECT)pDeviceContext); + + } + else + { + // + // So we know that we need to register this device when an IP addres + // appears + // + pDeviceContext->RegistrationHandle = NULL; + DevicesStarted++; + pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + } + } + } + + } + // + // if no devices were created, then stop the timers and free the resources + // + if (DevicesStarted == 0) + { + ExDeleteResource(&NbtConfig.Resource); + StopInitTimers(); + } + else + { + // + // at least one device context was created successfully, so return success + // + status = STATUS_SUCCESS; + } + + if (NbtConfig.uNumDevices == 0) + { + NbtLogEvent(EVENT_NBT_NO_DEVICES,0); + } + +#endif // _PNP_POWER + + if (pBindDevices) + { + CTEMemFree((PVOID)pBindDevices->RegistrySpace); + CTEMemFree((PVOID)pBindDevices); + } + if (pExportDevices) + { + CTEMemFree((PVOID)pExportDevices->RegistrySpace); + CTEMemFree((PVOID)pExportDevices); + } + if (pAddrArray) + { + CTEMemFree((PVOID)pAddrArray); + } + +#ifndef _PNP_LATER + + // + // Get an Irp for the out of resource queue (used to disconnect sessions + // when really low on memory) + // + if (!IsListEmpty(&NbtConfig.DeviceContexts)) + { + pEntry = NbtConfig.DeviceContexts.Flink; + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + NbtConfig.OutOfRsrc.pIrp = NTAllocateNbtIrp(&pDeviceContext->DeviceObject); + + if (!NbtConfig.OutOfRsrc.pIrp) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // + // allocate a dpc structure and keep it: we might need if we hit an + // out-of-resource condition + // + NbtConfig.OutOfRsrc.pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('a')); + if (!NbtConfig.OutOfRsrc.pDpc) + { + IoFreeIrp(NbtConfig.OutOfRsrc.pIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +#ifdef RASAUTODIAL + // + // Get the automatic connection driver + // entry points. + // + if (status == STATUS_SUCCESS) + { + NbtAcdBind(); + } +#endif + +#ifdef _PNP_POWER + + (void)TdiRegisterAddressChangeHandler( + AddressArrival, + AddressDeletion, + &AddressChangeHandle + ); + +#ifdef WATCHBIND + (void)TdiRegisterNotificationHandler( + BindHandler, + UnbindHandler, + &BindingHandle + ); +#endif // WATCHBIND + +#endif // _PNP_POWER + } + else + { + KdPrint(("NetBT!DriverEntry: Huh? Started NetBT with no devices!")); + } + +#endif // _PNP_POWER + + NbtConfig.InterfaceIndex = 0; + + // + // Return to the caller. + // + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchCleanup( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for IRP_MJ_CLEANUP + requests. + + This function is called when the last reference to the handle is closed. + Hence, an NtClose() results in an IRP_MJ_CLEANUP first, and then an + IRP_MJ_CLOSE. This function runs down all activity on the object, and + when the close comes in the object is actually deleted. + +Arguments: + + device - ptr to device object for target device + irp - ptr to I/O request packet + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpsp; + tDEVICECONTEXT *pDeviceContext; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + irpsp = IoGetCurrentIrpStackLocation(irp); + + // check that we got the correct major function code + ASSERT(irpsp->MajorFunction == IRP_MJ_CLEANUP); + + // look at the context value that NBT put into the FSContext2 value to + // decide what to do + switch ((USHORT)irpsp->FileObject->FsContext2) + { + case NBT_ADDRESS_TYPE: + // the client is closing the address file, so we must cleanup + // and memory blocks associated with it. + status = NTCleanUpAddress(pDeviceContext,irp); + break; + + case NBT_CONNECTION_TYPE: + // the client is closing a connection, so we must clean up any + // memory blocks associated with it. + status = NTCleanUpConnection(pDeviceContext,irp); + break; + + case NBT_CONTROL_TYPE: + // there is nothing to do here.... + status = STATUS_SUCCESS; + break; + + default: + /* + * complete the i/o successfully. + */ + status = STATUS_SUCCESS; + break; + } + + // + // Complete the Irp + // + ReturnIrp(irp, status); + return(status); + + +} // DispatchCleanup + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchClose( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for IRP_MJ_CLOSE + requests. This is called after Cleanup (above) is called. + +Arguments: + + device - ptr to device object for target device + pIrp - ptr to I/O request packet + +Return Value: + + an NT status code. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpsp; + tDEVICECONTEXT *pDeviceContext; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + // + // close operations are synchronous. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = 0; + + irpsp = IoGetCurrentIrpStackLocation(pIrp); + ASSERT(irpsp->MajorFunction == IRP_MJ_CLOSE); + + switch ((ULONG)irpsp->FileObject->FsContext2) + { + case NBT_ADDRESS_TYPE: + status = NTCloseAddress(pDeviceContext,pIrp); + break; + + case NBT_CONNECTION_TYPE: + status = NTCloseConnection(pDeviceContext,pIrp); + break; + + case NBT_WINS_TYPE: + status = NTCloseWinsAddr(pDeviceContext,pIrp); + break; + + case NBT_CONTROL_TYPE: + // the client is closing the Control Object... + // there is nothing to do here.... + status = STATUS_SUCCESS; + break; + + default: + KdPrint(("Nbt:Close Received for unknown object type = %X\n", + irpsp->FileObject->FsContext2)); + status = STATUS_SUCCESS; + break; + } + + // NTCloseAddress can return Pending until the ref count actually gets + // to zero. + // + if (status != STATUS_PENDING) + { + ReturnIrp(pIrp, status); + } + + return(status); + +} // DispatchClose + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchCreate( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for IRP_MJ_CREATE + requests. It is called as a consequence of one of the following: + + a. TdiOpenConnection("\Device\Nbt_Elnkii0"), + b. TdiOpenAddress("\Device\Nbt_Elnkii0"), + +Arguments: + + Device - ptr to device object being opened + pIrp - ptr to I/O request packet + pIrp->Status => return status + pIrp->MajorFunction => IRP_MD_CREATE + pIrp->MinorFunction => not used + pIpr->FileObject => ptr to file obj created by I/O system. NBT fills in FsContext + pIrp->AssociatedIrp.SystemBuffer => ptr to EA buffer with address of obj to open(Netbios Name) + pIrp->Parameters.Create.EaLength => length of buffer specifying the Xport Addr. + +Return Value: + + STATUS_SUCCESS or STATUS_PENDING + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpsp; + PFILE_FULL_EA_INFORMATION ea; + tDEVICECONTEXT *pDeviceContext; + UCHAR IrpFlags; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + // + // If this device was destroyed, then reject all opens on it. + // Ideally we would like the IO sub-system to guarantee that no + // requests come down on IoDeleted devices, but..... + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + // IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Rejecting Create minor Func\n")); + + pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + pIrpsp = IoGetCurrentIrpStackLocation(pIrp); + ASSERT(pIrpsp->MajorFunction == IRP_MJ_CREATE); + IrpFlags = pIrpsp->Control; + + // + // set the pending flag here so that it is sure to be set BEFORE the + // completion routine gets hit. + // + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_PENDING; + IoMarkIrpPending(pIrp); + + IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Internal Ctrl minor Func = %X\n",pIrpsp->MinorFunction)); + + /* + * was this a TdiOpenConnection() or TdiOpenAddress()? + * Get the Extended Attribute pointer and look at the text + * value passed in for a match with "TransportAddress" or + * "ConnectionContext" (in FindEa) + */ + ea = (PFILE_FULL_EA_INFORMATION) pIrp->AssociatedIrp.SystemBuffer; + + if (!ea) + { + // a null ea means open the control object + status = NTOpenControl(pDeviceContext,pIrp); + } + else + if (FindInEA(ea, TdiConnectionContext)) + { + // not allowed to pass in both a Connect Request and a Transport Address + ASSERT(!FindInEA(ea, TdiTransportAddress)); + status = NTOpenConnection(pDeviceContext,pIrp); + } + else + if (FindInEA(ea, TdiTransportAddress)) + { + status = NTOpenAddr(pDeviceContext,pIrp); + } + else + if (FindInEA(ea, WINS_INTERFACE_NAME)) + { + status = NTOpenWinsAddr(pDeviceContext,pIrp); + } + else + { + status = STATUS_INVALID_EA_NAME; + pIrpsp->Control = IrpFlags; + ReturnIrp(pIrp, status); + return(status); + } + + // complete the irp if the status is anything EXCEPT status_pending + // since the name query completion routine NTCompletIO completes pending + // open addresses + + if (status != STATUS_PENDING) + { + +#if DBG + // *TODO* for debug... + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: error return status = %X\n",status)); + //ASSERTMSG("An error Status reported from NBT",0L); + } +#endif + + // reset the pending returned bit, since we are NOT returning pending + pIrpsp->Control = IrpFlags; + + ReturnIrp(pIrp,status); + + } + + + return(status); + + + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchDevCtrl( + IN PDEVICE_OBJECT Device, + IN PIRP irp + ) + +/*++ + +Routine Description: + + This is the NBT driver's dispatch function for all + IRP_MJ_DEVICE_CONTROL requests. + +Arguments: + + device - ptr to device object for target device + irp - ptr to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpsp; + tDEVICECONTEXT *pDeviceContext; + + CTEPagedCode(); + pDeviceContext = (tDEVICECONTEXT *)Device; + + irpsp = IoGetCurrentIrpStackLocation(irp); + + // + // If this device was destroyed, then reject all requests on it. + // Ideally we would like the IO sub-system to guarantee that no + // requests come down on IoDeleted devices, but..... + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + // IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Rejecting Dev Ctrl code = %X\n",irpsp->Parameters.DeviceIoControl.IoControlCode)); + irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + /* + * Initialize the I/O status block. + */ + irp->IoStatus.Status = STATUS_PENDING; + irp->IoStatus.Information = 0; + + ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + + IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt:DevCtrl hit with ControlCode == %X\n", + irpsp->Parameters.DeviceIoControl.IoControlCode)); + + if ((irpsp->Parameters.DeviceIoControl.IoControlCode >= IOCTL_NETBT_PURGE_CACHE) && + (irpsp->Parameters.DeviceIoControl.IoControlCode <IOCTL_NETBT_LAST_IOCTL)) + { + return(DispatchIoctls((tDEVICECONTEXT *)Device,irp, irpsp)); + } + else + { + /* + * if possible, convert the (external) device control into internal + * format, then treat it as if it had arrived that way. + */ + status = TdiMapUserRequest(Device, irp, irpsp); + + if (status == STATUS_SUCCESS) + { + return(NbtDispatchInternalCtrl(Device, irp)); + } + } + + ReturnIrp(irp, STATUS_INVALID_DEVICE_REQUEST); + return(STATUS_INVALID_DEVICE_REQUEST); + +} // NbtDispatchDevCtrl + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtDispatchInternalCtrl( + IN PDEVICE_OBJECT Device, + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This is the driver's dispatch function for all + IRP_MJ_INTERNAL_DEVICE_CONTROL requests. + +Arguments: + + device - ptr to device object for target device + irp - ptr to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + tDEVICECONTEXT *pDeviceContext; + PIO_STACK_LOCATION pIrpsp; + NTSTATUS status; + UCHAR IrpFlags; + + pDeviceContext = (tDEVICECONTEXT *)Device; + + pIrpsp = IoGetCurrentIrpStackLocation(pIrp); + + // + // If this device was destroyed, then reject all operations on it. + // Ideally we would like the IO sub-system to guarantee that no + // requests come down on IoDeleted devices, but..... + // + if (InterlockedExchangeAdd(&pDeviceContext->IsDestroyed, 0) != 0) { + // IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Rejecting Internal minor fn. = %X\n",pIrpsp->MinorFunction)); + pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + /* + * Initialize the I/O status block. + */ + + // + // this check if first to optimize the Send path + // + if (pIrpsp->MinorFunction ==TDI_SEND) + { + // + // this routine decides if it should complete the irp or not + // It never returns status pending, so we can turn off the + // pending bit + // + return( NTSend(pDeviceContext,pIrp) ); + + } + + IrpFlags = pIrpsp->Control; + + ASSERT(pIrpsp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); + + IF_DBG(NBT_DEBUG_DRIVER) + KdPrint(("Nbt Internal Ctrl minor Func = %X\n",pIrpsp->MinorFunction)); + + switch (pIrpsp->MinorFunction) + { + case TDI_ACCEPT: + MakePending(pIrp); + status = NTAccept(pDeviceContext,pIrp); + break; + + case TDI_ASSOCIATE_ADDRESS: + MakePending(pIrp); + status = NTAssocAddress(pDeviceContext,pIrp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + MakePending(pIrp); + status = NTDisAssociateAddress(pDeviceContext,pIrp); + break; + + case TDI_CONNECT: + MakePending(pIrp); + status = NTConnect(pDeviceContext,pIrp); + break; + + case TDI_DISCONNECT: + MakePending(pIrp); + status = NTDisconnect(pDeviceContext,pIrp); + break; + + case TDI_LISTEN: + status = NTListen(pDeviceContext,pIrp); + return(status); + break; + + case TDI_QUERY_INFORMATION: + status = NTQueryInformation(pDeviceContext,pIrp); +#if DBG + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: Bad status from Query Info = %X\n",status)); + } +#endif + return(status); + break; + + case TDI_RECEIVE: + status = NTReceive(pDeviceContext,pIrp); + return(status); + + break; + + case TDI_RECEIVE_DATAGRAM: + status = NTReceiveDatagram(pDeviceContext,pIrp); + return(status); + break; + + + case TDI_SEND_DATAGRAM: + + status = NTSendDatagram(pDeviceContext,pIrp); +#if DBG + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: Bad status from Dgram Send = %X\n",status)); + } +#endif + return(status); + break; + + case TDI_SET_EVENT_HANDLER: + MakePending(pIrp); + status = NTSetEventHandler(pDeviceContext,pIrp); + break; + + case TDI_SET_INFORMATION: + MakePending(pIrp); + status = NTSetInformation(pDeviceContext,pIrp); + break; + + #if DBG + // + // 0x7f is a request by the redirector to put a "magic bullet" out on + // the wire, to trigger the Network General Sniffer. + // + case 0x7f: + KdPrint(("NBT:DispatchInternalCtrl - 07f minor function code\n")); + ReturnIrp(pIrp, STATUS_NOT_SUPPORTED); + return(STATUS_NOT_SUPPORTED); + + #endif /* DBG */ + + default: + KdPrint(("NBT:Dispatch Internal Ctl - invalid minor function %X\n", + pIrpsp->MinorFunction)); + ReturnIrp(pIrp, STATUS_INVALID_DEVICE_REQUEST); + return(STATUS_INVALID_DEVICE_REQUEST); + } + + // if the returned status is pending, then we do not complete the IRP + // here since it will be completed elsewhere in the code... + // + if (status != STATUS_PENDING) + { +#if DBG + // *TODO* for debug... + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBT:error return status = %X,MinorFunc = %X\n",status,pIrpsp->MinorFunction)); +// ASSERTMSG("An error Status reported from NBT",0L); + } + +#endif + pIrpsp->Control = IrpFlags; + + ReturnIrp(pIrp,status); + + } + + return(status); + + +} // NbtDispatchInternalCtrl + + +//---------------------------------------------------------------------------- +PFILE_FULL_EA_INFORMATION +FindInEA( + IN PFILE_FULL_EA_INFORMATION start, + IN PCHAR wanted + ) + +/*++ + +Routine Description: + + This function check for the "Wanted" string in the Ea structure and + returns a pointer to the extended attribute structure + representing the given extended attribute name. + +Arguments: + + device - ptr to device object for target device + pIrp - ptr to I/O request packet + +Return Value: + + pointer to the extended attribute structure, or NULL if not found. + +--*/ + +{ + PFILE_FULL_EA_INFORMATION eabuf; + + CTEPagedCode(); + + for (eabuf = start; eabuf; eabuf += eabuf->NextEntryOffset) + { + + if (strncmp(eabuf->EaName,wanted,eabuf->EaNameLength) == 0) + { + return eabuf; + } + + if (eabuf->NextEntryOffset == 0) + { + return((PFILE_FULL_EA_INFORMATION) NULL); + } + + } + return((PFILE_FULL_EA_INFORMATION) NULL); + +} // FindEA + + + +//---------------------------------------------------------------------------- +VOID +ReturnIrp( + IN PIRP pIrp, + IN int status + ) + +/*++ + +Routine Description: + + This function completes an IRP, and arranges for return parameters, + if any, to be copied. + + Although somewhat a misnomer, this function is named after a similar + function in the SpiderSTREAMS emulator. + +Arguments: + + pIrp - pointer to the IRP to complete + status - completion status of the IRP + +Return Value: + + number of bytes copied back to the user. + +--*/ + +{ + KIRQL oldlevel; + CCHAR priboost; + + // + // pIrp->IoStatus.Information is meaningful only for STATUS_SUCCESS + // + + // set the Irps cancel routine to null or the system may bugcheck + // with a bug code of CANCEL_STATE_IN_COMPLETED_IRP + // + // refer to IoCancelIrp() ..\ntos\io\iosubs.c + // + IoAcquireCancelSpinLock(&oldlevel); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(oldlevel); + + pIrp->IoStatus.Status = status; + + priboost = (CCHAR) ((status == STATUS_SUCCESS) ? + IO_NETWORK_INCREMENT : IO_NO_INCREMENT); + + IoCompleteRequest(pIrp, priboost); + + return; + +} +//---------------------------------------------------------------------------- +VOID +MakePending( + IN PIRP pIrp + ) + +/*++ + +Routine Description: + + This function marks an irp pending and sets the correct status. + +Arguments: + + pIrp - pointer to the IRP to complete + status - completion status of the IRP + +Return Value: + + +--*/ + +{ + IoMarkIrpPending(pIrp); + pIrp->IoStatus.Status = STATUS_PENDING; + pIrp->IoStatus.Information = 0; + +} + diff --git a/private/ntos/nbt/nt/fileio.c b/private/ntos/nbt/nt/fileio.c new file mode 100644 index 000000000..5490c3acd --- /dev/null +++ b/private/ntos/nbt/nt/fileio.c @@ -0,0 +1,384 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fileio.c + +Abstract: + + This source implements a stdio-like facility. + +Author: + + Jim Stewart June 1993 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "hosts.h" +#include <string.h> + + +// +// Private Definitions +// + + + +// +// Local Variables +// + + + +// +// Local (Private) Functions +// +PUCHAR +LmpMapFile ( + IN HANDLE handle, + IN OUT int *pnbytes + ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, LmCloseFile) +#pragma CTEMakePageable(PAGE, LmFgets) +#pragma CTEMakePageable(PAGE, LmpMapFile) +#pragma CTEMakePageable(PAGE, LmOpenFile) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- + +NTSTATUS +LmCloseFile ( + IN PLM_FILE pfile + ) + +/*++ + +Routine Description: + + This function closes a file opened via LmOpenFile(), and frees its + LM_FILE object. + +Arguments: + + pfile - pointer to the LM_FILE object + +Return Value: + + An NTSTATUS value. + +--*/ + + +{ + NTSTATUS status; + + CTEPagedCode(); + CTEMemFree(pfile->f_buffer); + + status = ZwClose(pfile->f_handle); + + ASSERT(status == STATUS_SUCCESS); + + CTEMemFree(pfile); + + return(status); + +} // LmCloseFile + + + +//---------------------------------------------------------------------------- + +PUCHAR +LmFgets ( + IN PLM_FILE pfile, + OUT int *nbytes + ) + +/*++ + +Routine Description: + + This function is vaguely similar to fgets(3). + + Starting at the current seek position, it reads through a newline + character, or the end of the file. If a newline is encountered, it + is replaced with a NULL character. + +Arguments: + + pfile - file to read from + nbytes - the number of characters read, excluding the NULL character + +Return Value: + + A pointer to the beginning of the line, or NULL if we are at or past + the end of the file. + +--*/ + + +{ + PUCHAR endOfLine; + PUCHAR startOfLine; + size_t maxBytes; + + CTEPagedCode(); + startOfLine = pfile->f_current; + + if (startOfLine >= pfile->f_limit) + { + + return((PUCHAR) NULL); + } + + maxBytes = pfile->f_limit - pfile->f_current; + endOfLine = (PUCHAR) memchr(startOfLine, (UCHAR) '\n', maxBytes); + + if (!endOfLine) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: lmhosts file doesn't end in '\\n'")); + endOfLine = pfile->f_limit; + } + + *endOfLine = (UCHAR) NULL; + + pfile->f_current = endOfLine + 1; + (pfile->f_lineno)++; + ASSERT(pfile->f_current <= pfile->f_limit+1); + + *nbytes = endOfLine - startOfLine; + + return(startOfLine); + +} // LmFgets + + + +//---------------------------------------------------------------------------- + +PUCHAR +LmpMapFile ( + IN HANDLE handle, + IN OUT int *pnbytes + ) + +/*++ + +Routine Description: + + This function reads an entire file into memory. + +Arguments: + + handle - file handle + pnbytes - size of the whole file + + +Return Value: + + the buffer allocated, or NULL if unsuccessful. + +--*/ + + +{ + PUCHAR buffer; + NTSTATUS status; + IO_STATUS_BLOCK iostatus; + FILE_STANDARD_INFORMATION stdInfo; + LARGE_INTEGER offset ={0, 0}; + LARGE_INTEGER length ={0x7fffffff, 0x7fffffff}; + + CTEPagedCode(); + + + status = ZwQueryInformationFile( + handle, // FileHandle + &iostatus, // IoStatusBlock + (PVOID) &stdInfo, // FileInformation + sizeof(stdInfo), // Length + FileStandardInformation); // FileInformationClass + + if (status != STATUS_SUCCESS) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: ZwQueryInformationFile(std) = %X\n", status)); + return(NULL); + } + + length = stdInfo.EndOfFile; // structure copy + + if (length.HighPart) + { + return(NULL); + } + + buffer = ExAllocatePool(NonPagedPool, length.LowPart+2); + + if (buffer != NULL) + { + + status = ZwReadFile( + handle, // FileHandle + NULL, // Event + NULL, // ApcRoutine + NULL, // ApcContext + &iostatus, // IoStatusBlock + buffer, // Buffer + length.LowPart, // Length + &offset, // ByteOffset + NULL); // Key + + if (status != STATUS_SUCCESS) + { + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: ZwReadFile(std) = %X\n", status)); + } + + ASSERT(status != STATUS_PENDING); + + if (iostatus.Status != STATUS_SUCCESS || status != STATUS_SUCCESS) + { + CTEMemFree(buffer); + return(NULL); + } + + *pnbytes = length.LowPart; + } + return(buffer); + +} // LmpMapFile + + + +//---------------------------------------------------------------------------- + +PLM_FILE +LmOpenFile ( + IN PUCHAR path + ) + +/*++ + +Routine Description: + + This function opens a file for use by LmFgets(). + +Arguments: + + path - a fully specified, complete path to the file. + +Return Value: + + A pointer to an LM_FILE object, or NULL if unsuccessful. + +--*/ + + +{ + NTSTATUS status; + HANDLE handle; + PLM_FILE pfile; + IO_STATUS_BLOCK iostatus; + OBJECT_ATTRIBUTES attributes; + UNICODE_STRING ucPath; + PUCHAR start; + int nbytes; + OEM_STRING String; + PUCHAR LongerPath; + + + CTEPagedCode(); + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + status = LmGetFullPath(path,&LongerPath); + + if (NT_SUCCESS(status)) + { + RtlInitString(&String,LongerPath); + + status = RtlAnsiStringToUnicodeString(&ucPath,&String,TRUE); + + if (NT_SUCCESS(status)) + { + + InitializeObjectAttributes( + &attributes, // POBJECT_ATTRIBUTES + &ucPath, // ObjectName + OBJ_CASE_INSENSITIVE, // Attributes + (HANDLE) NULL, // RootDirectory + (PSECURITY_DESCRIPTOR) NULL); // SecurityDescriptor + + status = ZwCreateFile( + &handle, // FileHandle + SYNCHRONIZE | FILE_READ_DATA, // DesiredAccess + &attributes, // ObjectAttributes + &iostatus, // IoStatusBlock + 0, // AllocationSize + FILE_ATTRIBUTE_NORMAL, // FileAttributes + FILE_SHARE_READ | FILE_SHARE_WRITE, // ShareAccess + FILE_OPEN, // CreateDisposition + FILE_SYNCHRONOUS_IO_NONALERT, // OpenOptions + NULL, // EaBuffer + 0); // EaLength + + if (NT_SUCCESS(status)) + { + start = LmpMapFile(handle, &nbytes); + + if (start) + { + pfile = (PLM_FILE) ExAllocatePool(NonPagedPool, sizeof(LM_FILE)); + + if (pfile) + { + KeInitializeSpinLock(&(pfile->f_lock)); + + pfile->f_refcount = 1; + pfile->f_handle = handle; + pfile->f_lineno = 0; + pfile->f_fileOffset.HighPart = 0; + pfile->f_fileOffset.LowPart = 0; + + pfile->f_current = start; + pfile->f_buffer = start; + pfile->f_limit = pfile->f_buffer + nbytes; + + RtlFreeUnicodeString(&ucPath); + CTEMemFree(LongerPath); + + return(pfile); + } + + CTEMemFree(start); + } + + ZwClose(handle); + } + + RtlFreeUnicodeString(&ucPath); + + IF_DBG(NBT_DEBUG_LMHOST) + KdPrint(("NBT: ZwOpenFile(std) = %X\n", status)); + + } + + CTEMemFree(LongerPath); + } + + return((PLM_FILE) NULL); + +} // LmOpenFile + + diff --git a/private/ntos/nbt/nt/mp/makefile b/private/ntos/nbt/nt/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nt/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nt/mp/netbt.prf b/private/ntos/nbt/nt/mp/netbt.prf new file mode 100644 index 000000000..84914dcb9 --- /dev/null +++ b/private/ntos/nbt/nt/mp/netbt.prf @@ -0,0 +1,170 @@ +SendCompletion@12 +NbtDispatchInternalCtrl@8 +NTSend@8 +TdiReceiveHandler@32 +CompletionRcv@12 +ProcessIrp@24 +CTECountedAllocMem@8 +DgramHndlrNotOs@48 +ConvertToAscii@20 +UdpSendDatagram@28 +NbtDereferenceClient@4 +NTIoComplete@12 +MakeRemoteAddressStructure@16 +FindName@16 +GetIrp@4 +TdiSendDatagram@24 +DgramSendComplete@12 +FindInHashTable@16 +SendDgramCompletion@12 +CTECountedFreeMem@8 +DgramSendCleanupTracker@12 +NbtDereferenceName@4 +TdiRcvDatagramHandler@44 +GetNetBiosNameFromTransportAddress@12 +FindNameOrQuery@28 +NbtAllocTracker@0 +NbtDereferenceAddress@4 +NTSendDatagram@8 +NbtSendDatagram@28 +SendDgram@8 +ConvertToHalfAscii@16 +BuildSendDgramHdr@32 +RemoteHashTimeout@12 +TimerExpiry@16 +CTEStartTimer@16 +NTQueryInformation@8 +NTOpenConnection@8 +CreateDeviceString@8 +FreeTracker@8 +GetTracker@4 +SubmitTdiRequest@8 +TdiRcvNameSrvHandler@44 +GetEntry@12 +UdpSendNSBcast@36 +NDgramSendCompleted@12 +CreatePdu@32 +FindInEA@8 +MSnodeCompletion@12 +NbtOpenAndAssocConnection@8 +SrcIsUs@4 +AddToPendingList@8 +QueryNameOnNet@44 +NbtTdiAssociateConnection@8 +CompleteClientReq@12 +ClearCancelRoutine@4 +StartTimer@32 +StartLmHostTimer@8 +LmHostQueueRequest@16 +GetNameToFind@4 +RemoveNameAndCompleteReq@8 +RemoveName@4 +NameSrvHndlrNotOs@16 +ReturnIrp@8 +CheckRegistrationFromNet@16 +DereferenceTracker@4 +CTEInitTimer@4 +TimeoutQEntries@12 +LmHostTimeout@12 +GetContext@4 +MakePending@4 +FindNameRemoteThenLocal@8 +NbtOpenConnection@12 +NbtTdiOpenConnection@8 +NbtDispatchCreate@8 +SendDgramContinue@8 +CompletionRoutine@12 +AllocateMdl@4 +NTAssocAddress@8 +NbtAssociateAddress@12 +NTAllocateNbtIrp@4 +NbtGetMdl@8 +NbtListen@20 +NbtAllocateClientBlock@4 +NbtSetEventHandler@16 +InterlockedCallCompletion@8 +NTSetEventHandler@8 +FindInDomainList@8 +NbtRegisterName@32 +NTCheckSetCancelRoutine@12 +InitTimerQ@4 +CountLocalNames@4 +NbtInitConnQ@16 +AddToHashTable@28 +NbtInitTrackerQ@8 +NbtAppendString@12 +NbtQueryAdapterStatus@12 +QueryProviderCompletion@12 +NbtAddPermanentName@4 +ReadParameters2@8 +IncrementNameStats@8 +MSnodeRegCompletion@12 +NbtRegisterCompletion@8 +NbtOpenAddress@24 +InitQ@12 +NTSetFileObjectContexts@12 +NbtCreateDeviceObject@24 +CreateHashTable@12 +InitNotOs@0 +InitTimersNotOs@0 +TcpSendSessionResponse@12 +TcpSendSession@12 +Inbound@32 +CompleteSessionSetup@20 +SessionRespDone@12 +UdpSendResponse@32 +QueryRespDone@12 +SrcIsNameServer@8 +QueryFromNet@20 +NbtDispatchCleanup@8 +ConvertDottedDecimalToUlong@8 +NTGetLmHostPath@4 +NbtDispatchClose@8 +NbtDispatchDevCtrl@8 +NTOpenControl@8 +NbtCreateAddressObjects@12 +NTSetSharedAccess@12 +FindSessionEndPoint@24 +DispatchIoctls@12 +ReadParameters@8 +NTListen@8 +ConvertToUlong@8 +NbtInitMdlQ@8 +GetExtendedAttributes@4 +ReadNameServerAddresses@16 +GetServerAddress@12 +NbtReadRegistry@24 +GetIPFromRegistry@16 +NbtReadLinkageInformation@16 +NTReadIniString@12 +ReadElement@12 +NbtReadSingleParameter@16 +NbtOpenRegistry@12 +NbtFindLastSlash@12 +ReadStringRelative@16 +OpenAndReadElement@12 +SetEventHandler@20 +NbtTdiOpenAddress@28 +NbtTdiOpenControl@4 +SetWinsDownFlag@4 +LmGetFullPath@8 +TdiConnectHandler@36 +NTOpenAddr@8 +AcceptCompletionRoutine@12 +NTCheckSharedAccess@12 +LmOpenFile@4 +LmpBreakRecursion@8 +SendSessionCompletionRoutine@12 +ScanLmHostFile@4 +TdiSend@24 +NTReceive@8 +ClientTookSomeOfTheData@20 +LmGetIpAddr@16 +ConnectHndlrNotOs@24 +NTProcessAcceptIrp@8 +InitRemoteHashTable@12 +DriverEntry@8 +ReadLmHostFile@8 +ReadScope@8 +ClearConnStructures@8 +NbtQueryConnectionList@12 diff --git a/private/ntos/nbt/nt/mp/sources b/private/ntos/nbt/nt/mp/sources new file mode 100644 index 000000000..33e022722 --- /dev/null +++ b/private/ntos/nbt/nt/mp/sources @@ -0,0 +1,30 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib +TARGETLIBS=..\..\nbt\mp\obj\*\nbt.lib + +!include ..\sources.inc diff --git a/private/ntos/nbt/nt/netbt.rc b/private/ntos/nbt/nt/netbt.rc new file mode 100644 index 000000000..198701954 --- /dev/null +++ b/private/ntos/nbt/nt/netbt.rc @@ -0,0 +1,13 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "MBT Transport driver" + +#define VER_INTERNALNAME_STR "netbt.sys" +#define VER_ORIGINALFILENAME_STR "netbt.sys" + +#include <common.ver> + diff --git a/private/ntos/nbt/nt/netbtkd/kdextlib.c b/private/ntos/nbt/nt/netbtkd/kdextlib.c new file mode 100644 index 000000000..132d57b5d --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/kdextlib.c @@ -0,0 +1,843 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + kdextlib.c + +Abstract: + + Library routines for dumping data structures given a meta level descrioption + +Author: + + Balan Sethu Raman (SethuR) 11-May-1994 + +Notes: + The implementation tends to avoid memory allocation and deallocation as much as possible. + Therefore We have choosen an arbitrary length as the default buffer size. A mechanism will + be provided to modify this buffer length through the debugger extension commands. + +Revision History: + + 11-Nov-1994 SethuR Created + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include "ntverp.h" + +#define KDEXTMODE + +#include <windef.h> +#include <ntkdexts.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <kdextlib.h> + +PNTKD_OUTPUT_ROUTINE lpOutputRoutine; +PNTKD_GET_EXPRESSION lpGetExpressionRoutine; +PNTKD_GET_SYMBOL lpGetSymbolRoutine; +PNTKD_READ_VIRTUAL_MEMORY lpReadMemoryRoutine; + +#define PRINTF lpOutputRoutine +#define ERROR lpOutputRoutine + +#define NL 1 +#define NONL 0 + +#define SETCALLBACKS() \ + lpOutputRoutine = lpExtensionApis->lpOutputRoutine; \ + lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine; \ + lpGetSymbolRoutine = lpExtensionApis->lpGetSymbolRoutine; \ + lpReadMemoryRoutine = lpExtensionApis->lpReadVirtualMemRoutine; + +#define DEFAULT_UNICODE_DATA_LENGTH 512 +USHORT s_UnicodeStringDataLength = DEFAULT_UNICODE_DATA_LENGTH; +WCHAR s_UnicodeStringData[DEFAULT_UNICODE_DATA_LENGTH]; +WCHAR *s_pUnicodeStringData = s_UnicodeStringData; + +#define DEFAULT_ANSI_DATA_LENGTH 512 +USHORT s_AnsiStringDataLength = DEFAULT_ANSI_DATA_LENGTH; +CHAR s_AnsiStringData[DEFAULT_ANSI_DATA_LENGTH]; +CHAR *s_pAnsiStringData = s_AnsiStringData; + +// +// No. of columns used to display struct fields; +// + +ULONG s_MaxNoOfColumns = 3; +ULONG s_NoOfColumns = 1; + +/* + * Fetches the data at the given address + */ +BOOLEAN +GetData( DWORD dwAddress, PVOID ptr, ULONG size) +{ + BOOL b; + ULONG BytesRead; + + b = (lpReadMemoryRoutine)((LPVOID) dwAddress, ptr, size, &BytesRead ); + + + if (!b || BytesRead != size ) { + return FALSE; + } + + return TRUE; +} + +/* + * Fetch the null terminated ASCII string at dwAddress into buf + */ +BOOL +GetString( DWORD dwAddress, PSZ buf ) +{ + do { + if( !GetData( dwAddress,buf, 1) ) + return FALSE; + + dwAddress++; + buf++; + + } while( *buf != '\0' ); + + return TRUE; +} + +/* + * Displays a byte in hexadecimal + */ +VOID +PrintHexChar( UCHAR c ) +{ + PRINTF( "%c%c", "0123456789abcdef"[ (c>>4)&7 ], "0123456789abcdef"[ c&7 ] ); +} + +/* + * Displays a buffer of data in hexadecimal + */ +VOID +PrintHexBuf( PUCHAR buf, ULONG cbuf ) +{ + while( cbuf-- ) { + PrintHexChar( *buf++ ); + PRINTF( " " ); + } +} + +/* + * Displays a unicode string + */ +BOOL +PrintStringW(LPSTR msg, PUNICODE_STRING puStr, BOOL nl ) +{ + UNICODE_STRING UnicodeString; + ANSI_STRING AnsiString; + BOOLEAN b; + + if( msg ) + PRINTF( msg ); + + if( puStr->Length == 0 ) { + if( nl ) + PRINTF( "\n" ); + return TRUE; + } + + UnicodeString.Buffer = s_pUnicodeStringData; + UnicodeString.MaximumLength = s_UnicodeStringDataLength; + UnicodeString.Length = (puStr->Length > s_UnicodeStringDataLength) + ? s_UnicodeStringDataLength + : puStr->Length; + + b = (lpReadMemoryRoutine)( + (LPVOID) puStr->Buffer, + UnicodeString.Buffer, + UnicodeString.Length, + NULL); + + if (b) { + RtlUnicodeStringToAnsiString(&AnsiString, puStr, TRUE); + PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" ); + RtlFreeAnsiString(&AnsiString); + } + + return b; +} + +/* + * Displays a ANSI string + */ +BOOL +PrintStringA(LPSTR msg, PANSI_STRING pStr, BOOL nl ) +{ + ANSI_STRING AnsiString; + BOOLEAN b; + + if( msg ) + PRINTF( msg ); + + if( pStr->Length == 0 ) { + if( nl ) + PRINTF( "\n" ); + return TRUE; + } + + AnsiString.Buffer = s_pAnsiStringData; + AnsiString.MaximumLength = s_AnsiStringDataLength; + AnsiString.Length = (pStr->Length > (s_AnsiStringDataLength - 1)) + ? (s_AnsiStringDataLength - 1) + : pStr->Length; + + b = (lpReadMemoryRoutine)( + (LPVOID) pStr->Buffer, + AnsiString.Buffer, + AnsiString.Length, + NULL); + + if (b) { + AnsiString.Buffer[ AnsiString.Length ] = '\0'; + PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" ); + } + + return b; +} + +/* + * Displays all the fields of a given struct. This is the driver routine that is called + * with the appropriate descriptor array to display all the fields in a given struct. + */ + +char *NewLine = "\n"; +char *FieldSeparator = " "; +char *DotSeparator = "."; +#define NewLineForFields(FieldNo) \ + ((((FieldNo) % s_NoOfColumns) == 0) ? NewLine : FieldSeparator) +#define FIELD_NAME_LENGTH 30 + +VOID +PrintStructFields( DWORD dwAddress, VOID *ptr, FIELD_DESCRIPTOR *pFieldDescriptors ) +{ + int i; + int j; + BYTE ch; + + // Display the fields in the struct. + for( i=0; pFieldDescriptors->Name; i++, pFieldDescriptors++ ) { + + // Indentation to begin the struct display. + PRINTF( " " ); + + if( strlen( pFieldDescriptors->Name ) > FIELD_NAME_LENGTH ) { + PRINTF( "%-17s...%s ", pFieldDescriptors->Name, pFieldDescriptors->Name+strlen(pFieldDescriptors->Name)-10 ); + } else { + PRINTF( "%-30s ", pFieldDescriptors->Name ); + } + + PRINTF( "(0x%-2X) ", pFieldDescriptors->Offset ); + + switch( pFieldDescriptors->FieldType ) { + case FieldTypeByte: + case FieldTypeChar: + PRINTF( "%-16d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeBoolean: + PRINTF( "%-16s%s", + *(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE", + NewLineForFields(i)); + break; + + case FieldTypeBool: + PRINTF( "%-16s%s", + *(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE", + NewLineForFields(i)); + break; + + case FieldTypePointer: + PRINTF( "%-16X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeULongULong: + PRINTF( "%d%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + FieldSeparator ); + PRINTF( "%d%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset + sizeof(ULONG)), + NewLineForFields(i) ); + break; + + case FieldTypeListEntry: + + if ( (ULONG)(dwAddress + pFieldDescriptors->Offset) == + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset )) + { + PRINTF( "%s", "List Empty\n" ); + } + else + { + PRINTF( "%-8X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + FieldSeparator ); + PRINTF( "%-8X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset + sizeof(ULONG)), + NewLineForFields(i) ); + } + break; + + // Ip address: 4 bytes long + case FieldTypeIpAddr: + PRINTF( "%X%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + FieldSeparator ); + PRINTF( "(%d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 3), + DotSeparator ); + PRINTF( "%d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 2 ), + DotSeparator ); + PRINTF( "%d%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 1 ), + DotSeparator ); + PRINTF( "%d)%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + // Mac address: 6 bytes long + case FieldTypeMacAddr: + for (j=0; j<5; j++) + { + PRINTF( "%X%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j), + FieldSeparator ); + } + PRINTF( "%X%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 5), + NewLineForFields(i) ); + break; + + // Netbios name: 16 bytes long + case FieldTypeNBName: + // + // if first byte is printable, print the first 15 bytes as characters + // and 16th byte as a hex value. otherwise, print all the 16 bytes + // as hex values + // + ch = *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset); + if (ch >= 0x20 && ch <= 0x7e) + { + for (j=0; j<15; j++) + { + PRINTF( "%c", *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j)); + } + PRINTF( "<%X>%s", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 15), + NewLineForFields(i) ); + } + else + { + for (j=0; j<16; j++) + { + PRINTF( "%.2X", + *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j)); + } + PRINTF( "%s", NewLineForFields(i) ); + } + break; + + case FieldTypeULong: + case FieldTypeLong: + PRINTF( "%-16d%s", + *(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeShort: + PRINTF( "%-16X%s", + *(SHORT *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeUShort: + PRINTF( "%-16X%s", + *(USHORT *)(((char *)ptr) + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeUnicodeString: + PrintStringW( NULL, (UNICODE_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL ); + PRINTF( NewLine ); + break; + + case FieldTypeAnsiString: + PrintStringA( NULL, (ANSI_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL ); + PRINTF( NewLine ); + break; + + case FieldTypeSymbol: + { + UCHAR SymbolName[ 200 ]; + ULONG Displacement; + PVOID sym = (PVOID)(*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset )); + + lpGetSymbolRoutine( sym, SymbolName, &Displacement ); + PRINTF( "%-16s%s", + SymbolName, + NewLineForFields(i) ); + } + break; + + case FieldTypeEnum: + { + ULONG EnumValue; + ENUM_VALUE_DESCRIPTOR *pEnumValueDescr; + // Get the associated numericla value. + + EnumValue = *((ULONG *)((BYTE *)ptr + pFieldDescriptors->Offset)); + + if ((pEnumValueDescr = pFieldDescriptors->AuxillaryInfo.pEnumValueDescriptor) + != NULL) { + // + // An auxilary textual description of the value is + // available. Display it instead of the numerical value. + // + + LPSTR pEnumName = NULL; + + while (pEnumValueDescr->EnumName != NULL) { + if (EnumValue == pEnumValueDescr->EnumValue) { + pEnumName = pEnumValueDescr->EnumName; + break; + } + } + + if (pEnumName != NULL) { + PRINTF( "%-16s ", pEnumName ); + } else { + PRINTF( "%-4d (%-10s) ", EnumValue,"@$#%^&*"); + } + + } else { + // + // No auxilary information is associated with the ehumerated type + // print the numerical value. + // + PRINTF( "%-16d",EnumValue); + } + } + break; + + case FieldTypeStruct: + PRINTF( "@%-15X%s", + (dwAddress + pFieldDescriptors->Offset ), + NewLineForFields(i) ); + break; + + case FieldTypeLargeInteger: + case FieldTypeFileTime: + default: + ERROR( "Unrecognized field type %c for %s\n", pFieldDescriptors->FieldType, pFieldDescriptors->Name ); + break; + } + } +} + +LPSTR LibCommands[] = { + "dump <Struct Type Name>@<address expr> ", + "columns <d> -- controls the number of columns in the display ", + "logdump <Log Address> ", + 0 +}; + +BOOL +help( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + int i; + + SETCALLBACKS(); + + for( i=0; Extensions[i]; i++ ) + PRINTF( " %s\n", Extensions[i] ); + + for( i=0; LibCommands[i]; i++ ) + PRINTF( " %s\n", LibCommands[i] ); + + return TRUE; +} + + +BOOL +columns( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + ULONG NoOfColumns; + int i; + + SETCALLBACKS(); + + sscanf(lpArgumentString,"%ld",&NoOfColumns); + + if (NoOfColumns > s_MaxNoOfColumns) { + // PRINTF( "No. Of Columns exceeds maximum(%ld) -- directive Ignored\n", s_MaxNoOfColumns ); + } else { + s_NoOfColumns = NoOfColumns; + } + + PRINTF("Not Yet Implemented\n"); + + return TRUE; +} + + + +BOOL +globals( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + DWORD dwAddress; + CHAR buf[ 100 ]; + int i; + int c=0; + + SETCALLBACKS(); + + strcpy( buf, "srv!" ); + + for( i=0; GlobalBool[i]; i++, c++ ) { + BOOL b; + + strcpy( &buf[4], GlobalBool[i] ); + dwAddress = (lpGetExpressionRoutine) ( buf ); + if( dwAddress == 0 ) { + ERROR( "Unable to get address of %s\n", GlobalBool[i] ); + continue; + } + if( !GetData( dwAddress,&b, sizeof(b)) ) + return FALSE; + + PRINTF( "%s%-30s %10s%s", + c&1 ? " " : "", + GlobalBool[i], + b ? " TRUE" : "FALSE", + c&1 ? "\n" : "" ); + } + + for( i=0; GlobalShort[i]; i++, c++ ) { + SHORT s; + + strcpy( &buf[4], GlobalShort[i] ); + dwAddress = (lpGetExpressionRoutine) ( buf ); + if( dwAddress == 0 ) { + ERROR( "Unable to get address of %s\n", GlobalShort[i] ); + continue; + } + if( !GetData( dwAddress,&s,sizeof(s)) ) + return FALSE; + + PRINTF( "%s%-30s %10d%s", + c&1 ? " " : "", + GlobalShort[i], + s, + c&1 ? "\n" : "" ); + } + + for( i=0; GlobalLong[i]; i++, c++ ) { + LONG l; + + strcpy( &buf[4], GlobalLong[i] ); + dwAddress = (lpGetExpressionRoutine) ( buf ); + if( dwAddress == 0 ) { + ERROR( "Unable to get address of %s\n", GlobalLong[i] ); + continue; + } + if( !GetData( dwAddress,&l, sizeof(l)) ) + return FALSE; + + PRINTF( "%s%-30s %10d%s", + c&1 ? " " : "", + GlobalLong[i], + l, + c&1 ? "\n" : "" ); + } + + PRINTF( "\n" ); + + return TRUE; +} + + +BOOL +version +( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ +#if VER_DEBUG + char *kind = "checked"; +#else + char *kind = "free"; +#endif + + SETCALLBACKS(); + + PRINTF( "Redirector debugger Extension dll for %s build %u\n", kind, VER_PRODUCTBUILD ); + + return TRUE; +} + +#define NAME_DELIMITER '@' +#define NAME_DELIMITERS "@" +#define INVALID_INDEX 0xffffffff +#define MIN(x,y) ((x) < (y) ? (x) : (y)) + +ULONG SearchStructs(LPSTR lpArgument) +{ + ULONG i = 0; + STRUCT_DESCRIPTOR *pStructs = Structs; + ULONG NameIndex = INVALID_INDEX; + ULONG ArgumentLength = strlen(lpArgument); + BOOLEAN fAmbigous = FALSE; + + + while ((pStructs->StructName != 0)) { + int Result = _strnicmp(lpArgument, + pStructs->StructName, + MIN(strlen(pStructs->StructName),ArgumentLength)); + + if (Result == 0) { + if (NameIndex != INVALID_INDEX) { + // We have encountered duplicate matches. Print out the + // matching strings and let the user disambiguate. + fAmbigous = TRUE; + break; + } else { + NameIndex = i; + } + + } + pStructs++;i++; + } + + if (fAmbigous) { + PRINTF("Ambigous Name Specification -- The following structs match\n"); + PRINTF("%s\n",Structs[NameIndex].StructName); + PRINTF("%s\n",Structs[i].StructName); + while (pStructs->StructName != 0) { + if (_strnicmp(lpArgument, + pStructs->StructName, + MIN(strlen(pStructs->StructName),ArgumentLength)) == 0) { + PRINTF("%s\n",pStructs->StructName); + } + pStructs++; + } + PRINTF("Dumping Information for %s\n",Structs[NameIndex].StructName); + } + + return(NameIndex); +} + +VOID DisplayStructs() +{ + STRUCT_DESCRIPTOR *pStructs = Structs; + + PRINTF("The following structs are handled .... \n"); + while (pStructs->StructName != 0) { + PRINTF("\t%s\n",pStructs->StructName); + pStructs++; + } +} + +BOOL +dump( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + DWORD dwAddress; + + SETCALLBACKS(); + + if( lpArgumentString && *lpArgumentString ) { + // Parse the argument string to determine the structure to be displayed. + // Scan for the NAME_DELIMITER ( '@' ). + + LPSTR lpName = lpArgumentString; + LPSTR lpArgs = strpbrk(lpArgumentString, NAME_DELIMITERS); + ULONG Index; + + if (lpArgs) { + // + // The specified command is of the form + // dump <name>@<address expr.> + // + // Locate the matching struct for the given name. In the case + // of ambiguity we seek user intervention for disambiguation. + // + // We do an inplace modification of the argument string to + // facilitate matching. + // + *lpArgs = '\0'; + + Index = SearchStructs(lpName); + + // + // Let us restore the original value back. + // + + *lpArgs = NAME_DELIMITER; + + if (INVALID_INDEX != Index) { + BYTE DataBuffer[512]; + + dwAddress = (lpGetExpressionRoutine)( ++lpArgs ); + if (GetData(dwAddress,DataBuffer,Structs[Index].StructSize)) { + + PRINTF( + "++++++++++++++++ %s@%lx ++++++++++++++++\n", + Structs[Index].StructName, + dwAddress); + PrintStructFields( + dwAddress, + &DataBuffer, + Structs[Index].FieldDescriptors); + PRINTF( + "---------------- %s@%lx ----------------\n", + Structs[Index].StructName, + dwAddress); + } else { + PRINTF("Error reading Memory @ %lx\n",dwAddress); + } + } else { + // No matching struct was found. Display the list of + // structs currently handled. + + DisplayStructs(); + } + } else { + // + // The command is of the form + // dump <name> + // + // Currently we do not handle this. In future we will map it to + // the name of a global variable and display it if required. + // + + DisplayStructs(); + } + } else { + // + // display the list of structs currently handled. + // + + DisplayStructs(); + } + + return TRUE; +} + +#if 0 +BOOL +logdump( + DWORD dwCurrentPC, + PNTKD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString +) +{ + DWORD dwAddress; + BYTE DataBuffer[512]; + + SETCALLBACKS(); + + if( lpArgumentString && *lpArgumentString ) { + RX_LOG RxLog; + + dwAddress = (lpGetExpressionRoutine)(lpArgumentString); + if (GetData(dwAddress,&RxLog,sizeof(RX_LOG))) { + // Dump the log header followed by the log entries ... + ULONG dwCurEntry; + + PRINTF("s_RxLog.State %lx\n",RxLog.State); + PRINTF("s_RxLog.pHeadEntry %lx\n",RxLog.pHeadEntry); + PRINTF("s_RxLog.pTailEntry %lx\n",RxLog.pTailEntry); + PRINTF("s_RxLog.LogBufferSize %lx\n",RxLog.LogBufferSize); + PRINTF("s_RxLog.pLogBuffer %lx\n",RxLog.pLogBuffer); + PRINTF("s_RxLog.pWrapAroundPoint %lx\n",RxLog.pWrapAroundPoint); + PRINTF("s_RxLog.NumberOfEntriesIgnored %lx\n",RxLog.NumberOfEntriesIgnored); + PRINTF("s_RxLog.NumberOfLogWriteAttempts %lx\n",RxLog.NumberOfLogWriteAttempts); + + dwCurEntry = (DWORD)RxLog.pHeadEntry; + for (;;) { + PRX_LOG_ENTRY_HEADER pHeader; + ULONG LogRecordLength; + DWORD dwNextEntry; + + if (!GetData(dwCurEntry,DataBuffer,sizeof(RX_LOG_ENTRY_HEADER))) { + PRINTF("Error reading Memory @ %lx\n",dwAddress); + break; + } + + pHeader = (PRX_LOG_ENTRY_HEADER)DataBuffer; + LogRecordLength = pHeader->EntrySize - sizeof(RX_LOG_ENTRY_HEADER); + dwNextEntry = dwCurEntry + pHeader->EntrySize; + + if ((pHeader->EntrySize > 0) && + GetData((dwCurEntry + sizeof(RX_LOG_ENTRY_HEADER)), + DataBuffer, + LogRecordLength)) { + DataBuffer[LogRecordLength] = '\0'; + PRINTF("%s",DataBuffer); + } + + + if (RxLog.pTailEntry > RxLog.pHeadEntry) { + if (dwNextEntry > (DWORD)RxLog.pTailEntry) { + break; + } + } else { + if (dwNextEntry > (DWORD)RxLog.pHeadEntry) { + if ((dwNextEntry >= (DWORD)RxLog.pWrapAroundPoint) || + (dwNextEntry >= (DWORD)((PBYTE)RxLog.pLogBuffer + RxLog.LogBufferSize))) { + dwNextEntry = (DWORD)RxLog.pLogBuffer; + } + } else if (dwNextEntry > (DWORD)RxLog.pTailEntry) { + break; + } + } + + dwCurEntry = dwNextEntry; + } + } else { + PRINTF("Error reading Memory @ %lx\n",dwAddress); + } + } else { + PRINTF("usage: logdump <log address>\n"); + } + + return TRUE; +} +#endif + diff --git a/private/ntos/nbt/nt/netbtkd/kdextlib.h b/private/ntos/nbt/nt/netbtkd/kdextlib.h new file mode 100644 index 000000000..10bb0fbf9 --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/kdextlib.h @@ -0,0 +1,139 @@ + +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + rdr2kd.h + +Abstract: + + Redirector Kernel Debugger extension + +Author: + + Balan Sethu Raman (SethuR) 11-May-1994 + +Revision History: + + 11-Nov-1994 SethuR Created + +--*/ + +#ifndef _KDEXTLIB_H_ +#define _KDEXTLIB_H_ + +#include <windef.h> + +// +// The help strings printed out +// + +extern LPSTR Extensions[]; + +// +// The FIELD_DESCRIPTOR data structure is used to describe the field in a structure sufficiently +// for displaying information during debugging. The three pieces of information that are required +// are 1) the name of the field, 2) the offset in the corresponding structure and 3) a type descriptor. +// The type descriptor covers most primitive types. +// +// The task of generating these descriptors by augmenting the front end, but that will have to +// wait till we play around with these extensions and modify the data structures to meet most +// of the requirements. +// +// There are some types that can benefit from some auxillary information in the descriptors. A +// case in point is the "enum" defeinition. Merely printing out a numerical value for an enum +// type will invariably force the person using these extensions to refer to the corresponding +// include file. In order to avoid this we will accept an additional array for enum types that +// contains a textual description of the numerical value. +// +// There are certain conventions that have been adopted to ease the definition of the macros +// as well as facilitate the automation of the generation of these descriptors. +// These are as follows .... +// +// 1) All ENUM_VALUE_DESCRIPTOR definitions are named EnumValueDescrsOf_ENUMTYPENAME, where +// ENUMTYPENAME defines the corresponding enumerated type. +// + +typedef struct _ENUM_VALUE_DESCRIPTOR { + ULONG EnumValue; + LPSTR EnumName; +} ENUM_VALUE_DESCRIPTOR; + +typedef enum _FIELD_TYPE_CLASS { + FieldTypeByte, + FieldTypeChar, + FieldTypeBoolean, + FieldTypeBool, + FieldTypeULong, + FieldTypeLong, + FieldTypeUShort, + FieldTypeShort, + FieldTypePointer, + FieldTypeULongULong, + FieldTypeListEntry, + FieldTypeIpAddr, + FieldTypeMacAddr, + FieldTypeNBName, + FieldTypeUnicodeString, + FieldTypeAnsiString, + FieldTypeSymbol, + FieldTypeEnum, + FieldTypeByteBitMask, + FieldTypeWordBitMask, + FieldTypeDWordBitMask, + FieldTypeFloat, + FieldTypeDouble, + FieldTypeStruct, + FieldTypeLargeInteger, + FieldTypeFileTime +} FIELD_TYPE_CLASS, *PFIELD_TYPE_CLASS; + +typedef struct _FIELD_DESCRIPTOR_ { + FIELD_TYPE_CLASS FieldType; // The type of variable to be printed + LPSTR Name; // The name of the field + USHORT Offset; // The offset of the field in the structure + union { + ENUM_VALUE_DESCRIPTOR *pEnumValueDescriptor; // Auxillary information for enumerated types. + } AuxillaryInfo; +} FIELD_DESCRIPTOR; + +#define FIELD3(FieldType,StructureName, FieldName) \ + {FieldType, #FieldName , FIELD_OFFSET(StructureName,FieldName) ,NULL} + +#define FIELD4(FieldType, StructureName, FieldName, AuxInfo) \ + {FieldType, #FieldName , FIELD_OFFSET(StructureName,FieldName) ,AuxInfo} + +// +// The structs that are displayed by the debugger extensions are further +// described in another array. Each entry in the array contains the name of +// the structure and the associated Field descriptor list. +// + +typedef struct _STRUCT_DESCRITOR_ { + LPSTR StructName; + ULONG StructSize; + FIELD_DESCRIPTOR *FieldDescriptors; +} STRUCT_DESCRIPTOR; + +#define STRUCT(StructTypeName,FieldDescriptors) \ + { #StructTypeName,sizeof(StructTypeName),FieldDescriptors} + +// +// The array of structs handled by the debugger extension. +// + +extern STRUCT_DESCRIPTOR Structs[]; + +// +// Support for displaying global variables +// + +extern LPSTR GlobalBool[]; +extern LPSTR GlobalShort[]; +extern LPSTR GlobalLong[]; +extern LPSTR GlobalPtrs[]; + +#endif // _KDEXTLIB_H_ + diff --git a/private/ntos/nbt/nt/netbtkd/makefile b/private/ntos/nbt/nt/netbtkd/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nt/netbtkd/netbtkd.c b/private/ntos/nbt/nt/netbtkd/netbtkd.c new file mode 100644 index 000000000..6e1c60b41 --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/netbtkd.c @@ -0,0 +1,369 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + netbtkd.c + +Abstract: + + Netbt Kernel Debugger extension + +Author: + + Shirish Koti + +Revision History: + + 6-Jun-1991 Koti Created + +--*/ + +#include "types.h" + +#include <kdextlib.h> + +/* + * RDR2 global variables. + * + */ + +LPSTR GlobalBool[] = {0}; +LPSTR GlobalShort[] = {0}; +LPSTR GlobalLong[] = {0}; +LPSTR GlobalPtrs[] = {0}; + +LPSTR Extensions[] = { + "Netbt debugger extensions", + 0 +}; + +/* + * DeviceContext debugging. + * + */ + +FIELD_DESCRIPTOR DeviceContext[] = + { + FIELD3(FieldTypeStruct,tDEVICECONTEXT,DeviceObject), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,Linkage), + FIELD3(FieldTypePointer,tDEVICECONTEXT,SpinLock), + FIELD3(FieldTypePointer,tDEVICECONTEXT,Verify), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,UpConnectionInUse), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,LowerConnection), + FIELD3(FieldTypeListEntry,tDEVICECONTEXT,LowerConnFreeHead), + FIELD3(FieldTypeUnicodeString,tDEVICECONTEXT,BindName), + FIELD3(FieldTypeUnicodeString,tDEVICECONTEXT,ExportName), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,IpAddress), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,SubnetMask), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,BroadcastAddress), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,NetMask), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hNameServer), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pNameServerDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pNameServerFileObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hDgram), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pDgramDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pDgramFileObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hSession), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pSessionDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pSessionFileObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,hControl), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pControlDeviceObject), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pControlFileObject), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,lNameServerAddress), + FIELD3(FieldTypeIpAddr,tDEVICECONTEXT,lBackupServer), + FIELD3(FieldTypePointer,tDEVICECONTEXT,pPermClient), + FIELD3(FieldTypeULongULong,tDEVICECONTEXT,AdapterNumber), + FIELD3(FieldTypeMacAddr,tDEVICECONTEXT,MacAddress), + FIELD3(FieldTypeChar,tDEVICECONTEXT,LockNumber), + FIELD3(FieldTypeBoolean,tDEVICECONTEXT,RefreshToBackup), + FIELD3(FieldTypeBoolean,tDEVICECONTEXT,PointToPoint), + FIELD3(FieldTypeBoolean,tDEVICECONTEXT,WinsIsDown), + 0 + }; + +FIELD_DESCRIPTOR NameAddr[] = + { + FIELD3(FieldTypeListEntry,tNAMEADDR,Linkage), + FIELD3(FieldTypePointer,tNAMEADDR,pAddressEle), + FIELD3(FieldTypeIpAddr,tNAMEADDR,IpAddress), + FIELD3(FieldTypePointer,tNAMEADDR,pIpAddrsList), + FIELD3(FieldTypePointer,tNAMEADDR,pTracker), + FIELD3(FieldTypePointer,tNAMEADDR,pTimer), + FIELD3(FieldTypePointer,tNAMEADDR,Ttl), + FIELD3(FieldTypeULong,tNAMEADDR,RefCount), + FIELD3(FieldTypePointer,tNAMEADDR,NameTypeState), + FIELD3(FieldTypePointer,tNAMEADDR,Verify), + FIELD3(FieldTypeULongULong,tNAMEADDR,AdapterMask), + FIELD3(FieldTypeULongULong,tNAMEADDR,RefreshMask), + FIELD3(FieldTypeUShort,tNAMEADDR,TimeOutCount), + FIELD3(FieldTypeBoolean,tNAMEADDR,fProxyReq), +#ifdef PROXY_NODE + FIELD3(FieldTypeBoolean,tNAMEADDR,fPnode), +#endif + FIELD3(FieldTypeNBName,tNAMEADDR,Name), + 0 + }; + +FIELD_DESCRIPTOR AddressEle[] = + { + FIELD3(FieldTypeListEntry,tADDRESSELE,Linkage), + FIELD3(FieldTypePointer,tADDRESSELE,Verify), + FIELD3(FieldTypePointer,tADDRESSELE,SpinLock), + FIELD3(FieldTypeListEntry,tADDRESSELE,ClientHead), + FIELD3(FieldTypePointer,tADDRESSELE,pNameAddr), + FIELD3(FieldTypeULong,tADDRESSELE,RefCount), + FIELD3(FieldTypePointer,tADDRESSELE,pDeviceContext), + FIELD3(FieldTypePointer,tADDRESSELE,SecurityDescriptor), + FIELD3(FieldTypeUShort,tADDRESSELE,NameType), + FIELD3(FieldTypeChar,tADDRESSELE,LockNumber), + FIELD3(FieldTypeBoolean,tADDRESSELE,MultiClients), + 0 + }; + +FIELD_DESCRIPTOR ClientEle[] = + { + FIELD3(FieldTypeListEntry,tCLIENTELE,Linkage), + FIELD3(FieldTypePointer,tCLIENTELE,Verify), + FIELD3(FieldTypePointer,tCLIENTELE,pIrp), + FIELD3(FieldTypePointer,tCLIENTELE,SpinLock), + FIELD3(FieldTypePointer,tCLIENTELE,pAddress), + FIELD3(FieldTypeListEntry,tCLIENTELE,ConnectHead), + FIELD3(FieldTypeListEntry,tCLIENTELE,ConnectActive), + FIELD3(FieldTypeListEntry,tCLIENTELE,RcvDgramHead), + FIELD3(FieldTypeListEntry,tCLIENTELE,ListenHead), + FIELD3(FieldTypeListEntry,tCLIENTELE,SndDgrams), + FIELD3(FieldTypePointer,tCLIENTELE,evConnect), + FIELD3(FieldTypePointer,tCLIENTELE,ConEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evReceive), + FIELD3(FieldTypePointer,tCLIENTELE,RcvEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evDisconnect), + FIELD3(FieldTypePointer,tCLIENTELE,DiscEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evError), + FIELD3(FieldTypePointer,tCLIENTELE,ErrorEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evRcvDgram), + FIELD3(FieldTypePointer,tCLIENTELE,RcvDgramEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evRcvExpedited), + FIELD3(FieldTypePointer,tCLIENTELE,RcvExpedEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,evSendPossible), + FIELD3(FieldTypePointer,tCLIENTELE,SendPossEvContext), + FIELD3(FieldTypePointer,tCLIENTELE,pDeviceContext), + FIELD3(FieldTypeULong,tCLIENTELE,RefCount), + FIELD3(FieldTypeChar,tCLIENTELE,LockNumber), + FIELD3(FieldTypeBoolean,tCLIENTELE,WaitingForRegistration), + 0 + }; + + +FIELD_DESCRIPTOR ConnectEle[] = + { + FIELD3(FieldTypeListEntry,tCONNECTELE,Linkage), + FIELD3(FieldTypePointer,tCONNECTELE,Verify), + FIELD3(FieldTypePointer,tCONNECTELE,SpinLock), + FIELD3(FieldTypePointer,tCONNECTELE,pLowerConnId), + FIELD3(FieldTypePointer,tCONNECTELE,pClientEle), + FIELD3(FieldTypePointer,tCONNECTELE,ConnectContext), + FIELD3(FieldTypeNBName,tCONNECTELE,RemoteName), + FIELD3(FieldTypePointer,tCONNECTELE,pNewMdl), + FIELD3(FieldTypeULong,tCONNECTELE,CurrentRcvLen), + FIELD3(FieldTypeULong,tCONNECTELE,FreeBytesInMdl), + FIELD3(FieldTypeULong,tCONNECTELE,TotalPcktLen), + FIELD3(FieldTypeULong,tCONNECTELE,BytesInXport), + FIELD3(FieldTypeULong,tCONNECTELE,BytesRcvd), + FIELD3(FieldTypeULong,tCONNECTELE,ReceiveIndicated), + FIELD3(FieldTypePointer,tCONNECTELE,pNextMdl), + FIELD3(FieldTypeULong,tCONNECTELE,OffsetFromStart), + FIELD3(FieldTypePointer,tCONNECTELE,pIrp), + FIELD3(FieldTypePointer,tCONNECTELE,pIrpClose), + FIELD3(FieldTypePointer,tCONNECTELE,pIrpDisc), + FIELD3(FieldTypePointer,tCONNECTELE,pIrpRcv), + FIELD3(FieldTypeULong,tCONNECTELE,RefCount), + FIELD3(FieldTypeULong,tCONNECTELE,state), + FIELD3(FieldTypeBoolean,tCONNECTELE,Orig), + FIELD3(FieldTypeChar,tCONNECTELE,LockNumber), + FIELD3(FieldTypeChar,tCONNECTELE,SessionSetupCount), + FIELD3(FieldTypeChar,tCONNECTELE,DiscFlag), + FIELD3(FieldTypeBoolean,tCONNECTELE,JunkMsgFlag), + 0 + }; + +FIELD_DESCRIPTOR LowerConn[] = + { + FIELD3(FieldTypeListEntry,tLOWERCONNECTION,Linkage), + FIELD3(FieldTypePointer,tLOWERCONNECTION,Verify), + FIELD3(FieldTypePointer,tLOWERCONNECTION,SpinLock), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pUpperConnection), + FIELD3(FieldTypePointer,tLOWERCONNECTION,FileHandle), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pFileObject), + FIELD3(FieldTypePointer,tLOWERCONNECTION,AddrFileHandle), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pAddrFileObject), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pDeviceContext), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pIndicateMdl), + FIELD3(FieldTypeULongULong,tLOWERCONNECTION,BytesRcvd), + FIELD3(FieldTypeULongULong,tLOWERCONNECTION,BytesSent), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pMdl), + FIELD3(FieldTypeUShort,tLOWERCONNECTION,BytesInIndicate), + FIELD3(FieldTypeUShort,tLOWERCONNECTION,StateRcv), + FIELD3(FieldTypeIpAddr,tLOWERCONNECTION,SrcIpAddr), + FIELD3(FieldTypeULong,tLOWERCONNECTION,State), + FIELD3(FieldTypeULong,tLOWERCONNECTION,RefCount), + FIELD3(FieldTypePointer,tLOWERCONNECTION,pIrp), + FIELD3(FieldTypePointer,tLOWERCONNECTION,CurrentStateProc), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,bReceivingToIndicateBuffer), + FIELD3(FieldTypeChar,tLOWERCONNECTION,LockNumber), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,bOriginator), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,InRcvHandler), + FIELD3(FieldTypeBoolean,tLOWERCONNECTION,DestroyConnection), + 0 + }; + + +FIELD_DESCRIPTOR Tracker[] = + { + FIELD3(FieldTypeListEntry,tDGRAM_SEND_TRACKING,Linkage), + FIELD3(FieldTypeListEntry,tDGRAM_SEND_TRACKING,TrackerList), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,Verify), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pClientIrp), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pConnEle), + FIELD3(FieldTypeStruct,tDGRAM_SEND_TRACKING,SendBuffer), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pSendInfo), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pDeviceContext), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pTimer), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,RefCount), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pNameAddr), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,pTimeout), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,AllocatedLength), + FIELD3(FieldTypePointer,tDGRAM_SEND_TRACKING,CompletionRoutine), + FIELD3(FieldTypeUShort,tDGRAM_SEND_TRACKING,Flags), + FIELD3(FieldTypeListEntry,tDGRAM_SEND_TRACKING,DebugLinkage), + 0 + }; + +FIELD_DESCRIPTOR Nbt_Config[] = + { + FIELD3(FieldTypePointer,tNBTCONFIG,SpinLock), + FIELD3(FieldTypeULong,tNBTCONFIG,NumConnections), + FIELD3(FieldTypeULong,tNBTCONFIG,NumAddresses), + FIELD3(FieldTypeListEntry,tNBTCONFIG,DeviceContexts), + FIELD3(FieldTypeListEntry,tNBTCONFIG,DgramTrackerFreeQ), + FIELD3(FieldTypeListEntry,tNBTCONFIG,NodeStatusHead), + FIELD3(FieldTypeListEntry,tNBTCONFIG,AddressHead), + FIELD3(FieldTypeListEntry,tNBTCONFIG,PendingNameQueries), + FIELD3(FieldTypePointer,tNBTCONFIG,pControlObj), + FIELD3(FieldTypePointer,tNBTCONFIG,DriverObject), + FIELD3(FieldTypeListEntry,tNBTCONFIG,IrpFreeList), + FIELD3(FieldTypePointer,tNBTCONFIG,SessionMdlFreeSingleList), + FIELD3(FieldTypePointer,tNBTCONFIG,DgramMdlFreeSingleList), + FIELD3(FieldTypePointer,tNBTCONFIG,pTcpBindName), + FIELD3(FieldTypePointer,tNBTCONFIG,pLocalHashTbl), + FIELD3(FieldTypePointer,tNBTCONFIG,pRemoteHashTbl), + FIELD3(FieldTypeStruct,tNBTCONFIG,OutOfRsrc), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumDevices), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumLocalNames), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumRemoteNames), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumBucketsRemote), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumBucketsLocal), + FIELD3(FieldTypeUShort,tNBTCONFIG,TimerQSize), + FIELD3(FieldTypeULong,tNBTCONFIG,uBcastTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,uRetryTimeout), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumRetries), + FIELD3(FieldTypeUShort,tNBTCONFIG,uNumBcasts), + FIELD3(FieldTypeUShort,tNBTCONFIG,ScopeLength), + FIELD3(FieldTypeUShort,tNBTCONFIG,SizeTransportAddress), + FIELD3(FieldTypePointer,tNBTCONFIG,pScope), + FIELD3(FieldTypePointer,tNBTCONFIG,pBcastNetbiosName), + FIELD3(FieldTypeULong,tNBTCONFIG,MinimumTtl), + FIELD3(FieldTypeULong,tNBTCONFIG,RefreshDivisor), + FIELD3(FieldTypeULong,tNBTCONFIG,RemoteHashTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,WinsDownTimeout), + FIELD3(FieldTypePointer,tNBTCONFIG,pRefreshTimer), + FIELD3(FieldTypePointer,tNBTCONFIG,pSessionKeepAliveTimer), + FIELD3(FieldTypePointer,tNBTCONFIG,pRemoteHashTimer), + FIELD3(FieldTypeULong,tNBTCONFIG,InitialRefreshTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,KeepAliveTimeout), + FIELD3(FieldTypeULong,tNBTCONFIG,RegistryBcastAddr), + FIELD3(FieldTypeUShort,tNBTCONFIG,DhcpNumConnections), + FIELD3(FieldTypeUShort,tNBTCONFIG,CurrentHashBucket), + FIELD3(FieldTypeUShort,tNBTCONFIG,PduNodeType), + FIELD3(FieldTypeUShort,tNBTCONFIG,TransactionId), + FIELD3(FieldTypeUShort,tNBTCONFIG,NameServerPort), + FIELD3(FieldTypeUShort,tNBTCONFIG,sTimeoutCount), + FIELD3(FieldTypeStruct,tNBTCONFIG,JointLock), + FIELD3(FieldTypeChar,tNBTCONFIG,LockNumber), + FIELD3(FieldTypeUShort,tNBTCONFIG,RemoteTimeoutCount), + FIELD3(FieldTypeBoolean,tNBTCONFIG,UseRegistryBcastAddr), + FIELD3(FieldTypeULong,tNBTCONFIG,MaxDgramBuffering), + FIELD3(FieldTypeULong,tNBTCONFIG,LmHostsTimeout), + FIELD3(FieldTypePointer,tNBTCONFIG,pLmHosts), + FIELD3(FieldTypeULong,tNBTCONFIG,PathLength), + FIELD3(FieldTypeChar,tNBTCONFIG,AdapterCount), + FIELD3(FieldTypeBoolean,tNBTCONFIG,MultiHomed), + FIELD3(FieldTypeBoolean,tNBTCONFIG,SingleResponse), + FIELD3(FieldTypeBoolean,tNBTCONFIG,SelectAdapter), + FIELD3(FieldTypeBoolean,tNBTCONFIG,ResolveWithDns), + FIELD3(FieldTypeBoolean,tNBTCONFIG,EnableLmHosts), + FIELD3(FieldTypeBoolean,tNBTCONFIG,EnableProxyRegCheck), + FIELD3(FieldTypeBoolean,tNBTCONFIG,DoingRefreshNow), + FIELD3(FieldTypeChar,tNBTCONFIG,CurrProc), + FIELD3(FieldTypeUShort,tNBTCONFIG,OpRefresh), + 0 + }; + + +FIELD_DESCRIPTOR NbtWorkContext[] = + { + FIELD3(FieldTypeStruct,NBT_WORK_ITEM_CONTEXT,Item), + FIELD3(FieldTypePointer,NBT_WORK_ITEM_CONTEXT,pTracker), + FIELD3(FieldTypePointer,NBT_WORK_ITEM_CONTEXT,pClientContext), + FIELD3(FieldTypePointer,NBT_WORK_ITEM_CONTEXT,ClientCompletion), + FIELD3(FieldTypeBoolean,NBT_WORK_ITEM_CONTEXT,TimedOut), + 0 + }; + + +FIELD_DESCRIPTOR Timer_Entry[] = + { + FIELD3(FieldTypeStruct,tTIMERQENTRY,VxdTimer), + FIELD3(FieldTypeListEntry,tTIMERQENTRY,Linkage), + FIELD3(FieldTypePointer,tTIMERQENTRY,Context), + FIELD3(FieldTypePointer,tTIMERQENTRY,Context2), + FIELD3(FieldTypePointer,tTIMERQENTRY,CompletionRoutine), + FIELD3(FieldTypePointer,tTIMERQENTRY,ClientContext), + FIELD3(FieldTypePointer,tTIMERQENTRY,ClientCompletion), + FIELD3(FieldTypePointer,tTIMERQENTRY,pCacheEntry), + FIELD3(FieldTypeULong,tTIMERQENTRY,DeltaTime), + FIELD3(FieldTypeUShort,tTIMERQENTRY,Flags), + FIELD3(FieldTypeUShort,tTIMERQENTRY,Retries), + FIELD3(FieldTypeChar,tTIMERQENTRY,RefCount), + 0 + }; + +FIELD_DESCRIPTOR Dns_Queries[] = + { + FIELD3(FieldTypePointer,tDNS_QUERIES,QueryIrp), + FIELD3(FieldTypeListEntry,tDNS_QUERIES,ToResolve), + FIELD3(FieldTypePointer,tDNS_QUERIES,Context), + FIELD3(FieldTypeBoolean,tDNS_QUERIES,ResolvingNow), + 0 + }; + +// +// List of structs currently handled by the debugger extensions +// + +STRUCT_DESCRIPTOR Structs[] = + { + STRUCT(tDEVICECONTEXT,DeviceContext), + STRUCT(tNAMEADDR,NameAddr), + STRUCT(tADDRESSELE,AddressEle), + STRUCT(tCLIENTELE,ClientEle), + STRUCT(tCONNECTELE,ConnectEle), + STRUCT(tLOWERCONNECTION,LowerConn), + STRUCT(tDGRAM_SEND_TRACKING,Tracker), + STRUCT(tNBTCONFIG,Nbt_Config), + STRUCT(NBT_WORK_ITEM_CONTEXT,NbtWorkContext), + STRUCT(tTIMERQENTRY,Timer_Entry), + STRUCT(tDNS_QUERIES,Dns_Queries), + 0 + }; diff --git a/private/ntos/nbt/nt/netbtkd/netbtkd.def b/private/ntos/nbt/nt/netbtkd/netbtkd.def new file mode 100644 index 000000000..3e91e095e --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/netbtkd.def @@ -0,0 +1,8 @@ +LIBRARY NETBTKD +DESCRIPTION 'Netbt KD extensions' + +EXPORTS + help + dump + columns + diff --git a/private/ntos/nbt/nt/netbtkd/sources b/private/ntos/nbt/nt/netbtkd/sources new file mode 100644 index 000000000..f8bca6009 --- /dev/null +++ b/private/ntos/nbt/nt/netbtkd/sources @@ -0,0 +1,53 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=netbt + +TARGETNAME=netbtkd +TARGETPATH=obj +TARGETTYPE=DYNLINK +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\libc.lib \ + $(BASEDIR)\public\sdk\lib\*\user32.lib\ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib + +C_DEFINES=-DPROXY_NODE +INCLUDES=..;..\inc;..\..\inc;..\..\..\inc;..\..\..\..\inc + +!IFNDEF DISABLE_NET_UNICODE +UNICODE=1 +NET_C_DEFINES=-DUNICODE +!ENDIF + +DLLBASE=0x1010000 + +C_DEFINES=$(C_DEFINES) -DRDBSSDBG + +SOURCES=kdextlib.c \ + netbtkd.c + +UMTYPE=console +OPTIONAL_NTTEST= + + diff --git a/private/ntos/nbt/nt/ntisol.c b/private/ntos/nbt/nt/ntisol.c new file mode 100644 index 000000000..2f3e532cc --- /dev/null +++ b/private/ntos/nbt/nt/ntisol.c @@ -0,0 +1,4873 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Ntisol.h + +Abstract: + + + This file contains the interface between the TDI interface on the top + of NBT and the OS independent code. It takes the parameters out of the + irps and puts in into procedure calls for the OS independent code (which + is mostly in name.c). + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +Notes: + + The Nbt routines have been modified to include an additional parameter, i.e, + the transport type. This transport type is used primarily to distinguish the + NETBIOS over TCP/IP implementation from the Messaging Over TCP/IP implementation. + + The primary difference between the two being that the later uses the NETBT framing + without the associated NETBIOS name registartion/resolution. It primarily uses + DNS for name resolution. All the names that are registered for the new transport + are local names and are not defended on the network. + + The primary usage is in conjuntion with an extended NETBIOS address type defined + in tdi.h. The NETBIOS name resolution/registration traffic occurs in two phases. + The first phase contains all the broadcast traffic that ensues during NETBIOS + name registration. Subsequently the NETBT implementation queries the remote + adapter status to choose the appropriate called name. This approach results in + additional traffic for querying the remote adapter status. The new address type + defined in tdi.h enables the client of netbt to supply the name to be used in + NETBT session setup. This avoids the network traffic for querying the adapter + status. + + The original design which has not been fully implemented involved exposing two + device objects from the NetBt driver -- the NetBt device object which would be + the full implementation of NETBIOS over TCP/IP and the MoTcp device object which + would be the implementation of Messaging over TCP/IP. The MoTcp device object + would use the same port address as NetBt and use the same session setup protocol + to talk to remote machines running old NetBt drivers and machines running new + NetBt drivers. + + The transport type variations combined with the address type changes present us + with four different cases which need to be handled -- the NetBt transport being + presented with a TDI_ADDRESS_NETBIOS_EX structure, the NetBt transport being + prsented with a TDI_ADDRESS_NETBIOS structure and the same two cases for the + MoTcp transport. + +--*/ + +#include "types.h" +#include "nbtprocs.h" +#include "ntprocs.h" +#include <nbtioctl.h> +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL + +NTSTATUS +SendCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + + +NTSTATUS +NTSendCleanupConnection( + IN tCONNECTELE *pConnEle, + IN PVOID pCompletionRoutine, + IN PVOID Context, + IN PIRP pIrp); + +VOID +DpcSendSession( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NBT_WORK_ITEM_CONTEXT * +DnsIrpCancelPaged( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NBT_WORK_ITEM_CONTEXT * +FindCheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NTSTATUS +NTCancelCancelRoutine( + IN PIRP pIrp + ); + +#ifdef RASAUTODIAL +extern ACD_DRIVER AcdDriverG; + +BOOLEAN +NbtCancelPostConnect( + IN PIRP pIrp + ); +#endif // RASAUTODIAL + +NTSTATUS +NbtQueryGetAddressInfo( + IN PIO_STACK_LOCATION pIrpSp, + OUT PVOID *ppBuffer, + OUT ULONG *pSize +); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NTOpenControl) +#pragma CTEMakePageable(PAGE, NTOpenAddr) +#pragma CTEMakePageable(PAGE, NTCloseAddress) +#pragma CTEMakePageable(PAGE, NTOpenConnection) +#pragma CTEMakePageable(PAGE, NTAssocAddress) +#pragma CTEMakePageable(PAGE, NTCloseConnection) +#pragma CTEMakePageable(PAGE, NTSetSharedAccess) +#pragma CTEMakePageable(PAGE, NTCheckSharedAccess) +#pragma CTEMakePageable(PAGE, NTCleanUpConnection) +#pragma CTEMakePageable(PAGE, NTCleanUpAddress) +#pragma CTEMakePageable(PAGE, NTDisAssociateAddress) +#pragma CTEMakePageable(PAGE, NTListen) +// +// Should not be pageable since AFD can call us at raised Irql in case of AcceptEx. +// +// #pragma CTEMakePageable(PAGE, NTQueryInformation) +#pragma CTEMakePageable(PAGE, DispatchIoctls) +#endif +//******************* Pageable Routine Declarations **************** + + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenControl( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) +/*++ +Routine Description: + + This Routine handles opening the control object, which represents the + driver itself. For example QueryInformation uses the control object + as the destination of the Query message. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pIrpSp->FileObject->FsContext2 = (PVOID)(NBT_CONTROL_TYPE); + + // return a ptr the control endpoint + pIrpSp->FileObject->FsContext = (PVOID)pNbtGlobConfig->pControlObj; + + // + // the following call opens a control object with the transport below since + // several of the query information calls are passed directly on to the + // transport below. + // + if (!pDeviceContext->pControlFileObject) + { + status = NbtTdiOpenControl(pDeviceContext); + } + else + status = STATUS_SUCCESS; + + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) +/*++ +Routine Description: + + This Routine handles converting an Open Address Request from an IRP to + a procedure call so that NbtOpenAddress can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + TDI_REQUEST Request; + PVOID pSecurityDesc; + TRANSPORT_ADDRESS UNALIGNED *pTransportAddr; // structure containing counted array of TA_ADDRESS + TA_ADDRESS UNALIGNED *pAddress; + PTDI_ADDRESS_NETBIOS pNetbiosAddress; + PFILE_FULL_EA_INFORMATION ea; + int j; + NTSTATUS status=STATUS_INVALID_ADDRESS_COMPONENT; + + CTEPagedCode(); + + // make up the Request data structure from the IRP info + Request.Handle.AddressHandle = NULL; + + ea = (PFILE_FULL_EA_INFORMATION)pIrp->AssociatedIrp.SystemBuffer; + pTransportAddr = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + + pAddress = NULL; + + // loop through the addresses passed in until ONE is successfully used + // *TODO* is it really necessary to have this loop or can we just assume + // the name is at the start of the address buffer... + // *TODO does this need to handle multiple names?? + for (j=0;j < pTransportAddr->TAAddressCount ;j++ ) + { + // this includes the address type as well as the actual address + pAddress = &pTransportAddr->Address[j]; + switch (pAddress->AddressType) { + case TDI_ADDRESS_TYPE_NETBIOS: + { + if (pAddress->AddressLength == 0) + { + // zero length addresses mean the broadcast address + pAddress = NULL; + } + + // call the non-NT specific function to open an address + status = NbtOpenAddress(&Request, + pAddress, + pDeviceContext->IpAddress, + &pSecurityDesc, + pDeviceContext, + (PVOID)pIrp); + } + break; + case TDI_ADDRESS_TYPE_NETBIOS_EX: + { + + TDI_ADDRESS_NETBIOS NetbiosAddress; + PTDI_ADDRESS_NETBIOS_EX pNetbiosExAddress; + + pNetbiosExAddress = (PTDI_ADDRESS_NETBIOS_EX)pAddress->Address; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT..Opening NETBIOS_EX Address with Endpoint Name %16s\n",pNetbiosExAddress->EndpointName)); + + if (pAddress->AddressLength == 0) { + status = STATUS_INVALID_ADDRESS_COMPONENT; + } else { + // call the non-NT specific function to open an address + status = NbtOpenAddress(&Request, + pAddress, + pDeviceContext->IpAddress, + &pSecurityDesc, + pDeviceContext, + (PVOID)pIrp); + } + } + break; + default: + break; + } + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTCloseAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting a Close Address Request from an IRP to + a procedure call so that NbtCloseAddress can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + TDI_REQUEST_STATUS RequestStatus; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + + status = NbtCloseAddress( + &Request, + &RequestStatus, + pDeviceContext, + (PVOID)pIrp); + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting an Open Connection Request from an IRP to + a procedure call so that NbtOpenConnection can be called in an OS independent + manner. The connection must be associated with an address before it + can be used, except for in inbound call where the client returns the + connection ID in the accept. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + PFILE_FULL_EA_INFORMATION ea; + PIO_STACK_LOCATION pIrpSp; + CONNECTION_CONTEXT ConnectionContext; + NTSTATUS status; + PFILE_OBJECT pFileObject; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + + // make up the Request data structure from the IRP info + Request.Handle.ConnectionContext = NULL; + + // get the connection context out of the System buffer + ea = (PFILE_FULL_EA_INFORMATION)pIrp->AssociatedIrp.SystemBuffer; + + // the connection context value is stored in the string just after the + // name "connectionContext", and it is most likely unaligned, so just + // copy it out.( 4 bytes of copying ). + CTEMemCopy(&ConnectionContext, + (CONNECTION_CONTEXT)&ea->EaName[ea->EaNameLength+1], + sizeof(CONNECTION_CONTEXT)); + + // call the non-NT specific function to open an address + status = NbtOpenConnection( + &Request, + ConnectionContext, + pDeviceContext + ); + + pFileObject = pIrpSp->FileObject; + + if (!NT_SUCCESS(status)) + { + pFileObject->FsContext = NULL; + } + else + if (Request.Handle.ConnectionContext) + { + + // fill the IRP with successful completion information so we can + // find the connection object given the fileObject later. + pFileObject->FsContext = Request.Handle.ConnectionContext; + pFileObject->FsContext2 = (PVOID)(NBT_CONNECTION_TYPE); + status = STATUS_SUCCESS; + } + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTAssocAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting an Associate Address Request from an IRP to + a procedure call so that NbtAssociateAddress can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + PIO_STACK_LOCATION pIrpSp; + PVOID hAddress; + PFILE_OBJECT fileObject; + PTDI_REQUEST_KERNEL_ASSOCIATE parameters; // holds address handle + NTSTATUS status; + + CTEPagedCode(); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + // the address handle is buried in the Irp... + parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&pIrpSp->Parameters; + + // now get a pointer to the file object, which points to the address + // element by calling a kernel routine to convert this filehandle into + // a file pointer. + + status = ObReferenceObjectByHandle( + parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&fileObject, + NULL); + + if (NT_SUCCESS(status)) + { + hAddress = (PVOID)fileObject->FsContext; + // call the non-NT specific function to associate the address with + // the connection + status = NbtAssociateAddress( + &Request, + (tCLIENTELE *)hAddress, + (PVOID)pIrp); + + // we are done with the file object, so release the reference + ObDereferenceObject((PVOID)fileObject); + + return(status); + } + else + return(STATUS_INVALID_HANDLE); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCloseConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles converting a Close Connection Request from an IRP to + a procedure call so that NbtCloseConnection can be called in an OS independent + manner. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + TDI_REQUEST Request; + TDI_REQUEST_STATUS RequestStatus; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + + CTEPagedCode(); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + status = NbtCloseConnection( + &Request, + &RequestStatus, + pDeviceContext, + (PVOID)pIrp); + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +NTSetFileObjectContexts( + IN PIRP pIrp, + IN PVOID FsContext, + IN PVOID FsContext2) + +/*++ +Routine Description: + + This Routine handles fills in two context values in the Irp stack location, + that has to be done in an OS-dependent manner. This routine is called + from NbtOpenAddress() when a name is being registered on the network( i.e. + as a result of OpenAddress). + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + PIO_STACK_LOCATION pIrpSp; + PFILE_OBJECT pFileObject; + + // + // fill the IRP with context information so we can + // find the address object given the fileObject later. + // + // This must be done here, rather than after the call to NbtOpenAddress + // because that call can complete the Irp before it returns. Soooo, + // in the complete routine for the Irp, if the completion code is not + // good, it Nulls these two context values. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pFileObject = pIrpSp->FileObject; + pFileObject->FsContext = FsContext; + pFileObject->FsContext2 =FsContext2; + + +} + + +//---------------------------------------------------------------------------- +VOID +NTClearFileObjectContext( + IN PIRP pIrp + ) +/*++ +Routine Description: + + This Routine clears the context value in the file object when an address + object is closed. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + none + +--*/ + +{ + + PIO_STACK_LOCATION pIrpSp; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + CHECK_PTR(pIrpSp->FileObject); + pIrpSp->FileObject->FsContext = NULL; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTSetSharedAccess( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN tADDRESSELE *pAddress) + +/*++ +Routine Description: + + This Routine handles setting the shared access on the file object. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + PACCESS_STATE AccessState; + ULONG DesiredAccess; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + if ((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) + DesiredAccess = (ULONG)FILE_SHARE_READ; + + else + DesiredAccess = (ULONG)0; + + IoSetShareAccess( + FILE_READ_DATA, + DesiredAccess, + pIrpSp->FileObject, + &pAddress->ShareAccess); + + // assign the security descriptor ( need to to do this with the spinlock + // released because the descriptor is not mapped. Assign and CheckAccess + // are synchronized using a Resource. + + AccessState = pIrpSp->Parameters.Create.SecurityContext->AccessState; + + + status = SeAssignSecurity( + NULL, // Parent Descriptor + AccessState->SecurityDescriptor, + &pAddress->SecurityDescriptor, + FALSE, // is a directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) + { + + // + // Error, return status. + // + + IoRemoveShareAccess (pIrpSp->FileObject, &pAddress->ShareAccess); + + } + return status; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCheckSharedAccess( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN tADDRESSELE *pAddress) + +/*++ +Routine Description: + + This Routine handles setting the shared access on the file object. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + ULONG DesiredAccess; + PIO_STACK_LOCATION pIrpSp; + BOOLEAN duplicate=FALSE; + NTSTATUS status; + ULONG DesiredShareAccess; + static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + + if ((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) + DesiredAccess = (ULONG)FILE_SHARE_READ; + else + DesiredAccess = (ULONG)0; + + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + + AccessState = pIrpSp->Parameters.Create.SecurityContext->AccessState; + + status = STATUS_SUCCESS; + + // *TODO* check that this routine is doing the right thing... + // + AccessAllowed = SeAccessCheck( + pAddress->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + pIrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + pIrp->RequestorMode, + &GrantedAccess, + &status); + + + // use the status from the IoCheckShareAccess as the return access + // event if SeAccessCheck fails.... + + // + // BUGBUG: Compare DesiredAccess to GrantedAccess? + // + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + //ACQUIRE_SPIN_LOCK (&pDeviceContext->SpinLock, &oldirql); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredAccess, + pIrpSp->FileObject, + &pAddress->ShareAccess, + TRUE); + + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCleanUpAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles the first stage of releasing an address object. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + tCLIENTELE *pClientEle; + PIO_STACK_LOCATION pIrpSp; + + + CTEPagedCode(); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Cleanup Address Hit ***\n")); + + // + // Disconnect any active connections, and for each connection that is not + // in use, remove one from the free list to the transport below. + // + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status); + + status = NbtCleanUpAddress(pClientEle,pDeviceContext); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCleanUpConnection( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles running down a connection in preparation for a close + that will come in next. NtClose hits this entry first, and then it hits + the NTCloseConnection next. If the connection was outbound, then the + address object must be closed as well as the connection. This routine + mainly deals with the pLowerconn connection to the transport whereas + NbtCloseConnection deals with closing pConnEle, the connection to the client. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + + CTEPagedCode(); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + +#if DBG + if ((pConnEle->Verify != NBT_VERIFY_CONNECTION) && + (pConnEle->Verify != NBT_VERIFY_CONNECTION_DOWN)) + { + ASSERTMSG("Invalid Connection Handle passed to NtCleanupConnection\n",0); + return(STATUS_INVALID_HANDLE); + } +#endif + + //CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Cleanup Connection Hit state= %X\n",pConnEle->state)); + + status = NbtCleanUpConnection(pConnEle,pDeviceContext); + + return(status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTAccept( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles passing an accept for an inbound connect indication to + the OS independent code. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: ** Got an Accept from the Client **\n")); + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + TdiRequest.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + + status = NbtAccept( + &TdiRequest, + pRequest->RequestConnectionInformation, + pRequest->ReturnConnectionInformation, + pIrp); + + return(status); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTDisAssociateAddress( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + + +{ + NTSTATUS status; + TDI_REQUEST TdiRequest; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + + + CTEPagedCode(); + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + TdiRequest.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + status = NbtDisassociateAddress(&TdiRequest); + + return(status); + + +} + +NTSTATUS +NbtpConnectCompletionRoutine( + PDEVICE_OBJECT pDeviceObject, + PIRP pIrp, + PVOID pCompletionContext) + +/*++ +Routine Description: + + This Routine is the completion routine for local IRPS that are generated + to handle compound transport addresses + +Arguments: + + pDeviceObject - the device object + + pIrp - a ptr to an IRP + + pCompletionContext - the completion context + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + KEVENT *pEvent = pCompletionContext; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Completing local irp %lx\n",pIrp)); + KeSetEvent((PKEVENT )pEvent, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTConnect( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles calling the non OS specific code to open a session + connection to a destination. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + TDI_REQUEST Request; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS Status; + PTDI_REQUEST_KERNEL pRequestKernel; + PTDI_CONNECTION_INFORMATION pRequestConnectionInformation; + PTRANSPORT_ADDRESS pRemoteAddress; + tCONNECTELE *pConnEle; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + pConnEle = Request.Handle.ConnectionContext; + + pRequestConnectionInformation = pRequestKernel->RequestConnectionInformation; + pRemoteAddress = pRequestConnectionInformation->RemoteAddress; + + if (pRequestConnectionInformation->RemoteAddressLength < sizeof(TRANSPORT_ADDRESS)) { + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // The round about path of creating a Local IRP and processing the request is taken if + // we are either presented with a compound address, i.e., a transport address having + // multiple TA_ADDRESSes or if it is not a locally generated IRP(completion routine check) + // and the address type is not TDI_ADDRESS_TYPE_NETBIOS. + // + if ((pRemoteAddress->TAAddressCount > 1) || + ((pIrpSp->CompletionRoutine != NbtpConnectCompletionRoutine) && + (pRemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS))) { + PIRP pLocalIrp; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Taking the roundabout path\n")); + + pLocalIrp = IoAllocateIrp(pDeviceContext->DeviceObject.StackSize,FALSE); + if (pLocalIrp != NULL) { + TDI_CONNECTION_INFORMATION LocalConnectionInformation; + PTRANSPORT_ADDRESS pTransportAddress; + PCHAR pTaAddress; + USHORT TaAddressLength,TransportAddressLength,AddressIndex; + USHORT TaAddressType; + KEVENT IrpCompletionEvent; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Allocated local irp %lx\n",pLocalIrp)); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Compound Transport address %lx Count %lx\n",pRemoteAddress,pRemoteAddress->TAAddressCount)); + + TaAddressLength = 0; + pTaAddress = (PCHAR)&pRemoteAddress->Address[0] - FIELD_OFFSET(TA_ADDRESS,Address); + + for (AddressIndex = 0; + AddressIndex < pRemoteAddress->TAAddressCount; + AddressIndex++) { + pTaAddress = (pTaAddress + TaAddressLength + FIELD_OFFSET(TA_ADDRESS,Address)); + + RtlCopyMemory( + &TaAddressLength, + (pTaAddress + FIELD_OFFSET(TA_ADDRESS,AddressLength)), + sizeof(USHORT)); + + RtlCopyMemory( + &TaAddressType, + (pTaAddress + FIELD_OFFSET(TA_ADDRESS,AddressType)), + sizeof(USHORT)); + + if (pConnEle->RemoteNameDoesNotExistInDNS) { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Skipping address type %lx length %lx for nonexistent name, pIrp %lx\n",TaAddressType,TaAddressLength,pIrp)); + + // If the address type is such that we rely on DNS name resolution and + // if a prior attempt failed, there is no point in reissuing the request. + // We can fail them without having to go on the NET. + switch (TaAddressType) { + case TDI_ADDRESS_TYPE_NETBIOS: + if (TaAddressLength == TDI_ADDRESS_LENGTH_NETBIOS) { + Status = STATUS_SUCCESS; + break; + } + // lack of break intentional. + case TDI_ADDRESS_TYPE_NETBIOS_EX: + Status = STATUS_BAD_NETWORK_PATH; + break; + default: + Status = STATUS_INVALID_ADDRESS_COMPONENT; + } + + if (Status != STATUS_SUCCESS) { + continue; + } + } + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: pTaAddress %lx TaAddressLength %lx\n",pTaAddress,TaAddressLength)); + + // Allocate a buffer for copying the address and building a TRANSPORT_ADDRESS + // data structure. + TransportAddressLength = FIELD_OFFSET(TRANSPORT_ADDRESS,Address) + + FIELD_OFFSET(TA_ADDRESS,Address) + + TaAddressLength; + + pTransportAddress = NbtAllocMem(TransportAddressLength,NBT_TAG('b')); + if (pTransportAddress == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + pTransportAddress->TAAddressCount = 1; + + KeInitializeEvent(&IrpCompletionEvent, NotificationEvent, FALSE); + + RtlCopyMemory( + &pTransportAddress->Address[0], + pTaAddress, + (TaAddressLength + FIELD_OFFSET(TA_ADDRESS,Address))); + + pConnEle->AddressType = pTransportAddress->Address[0].AddressType; + + LocalConnectionInformation = *(pRequestKernel->RequestConnectionInformation); + LocalConnectionInformation.RemoteAddress = pTransportAddress; + LocalConnectionInformation.RemoteAddressLength = TransportAddressLength; + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: Building Connect Irp %lx\n",pLocalIrp)); + + TdiBuildConnect( + pLocalIrp, + &pDeviceContext->DeviceObject, + pIrpSp->FileObject, + NbtpConnectCompletionRoutine, + &IrpCompletionEvent, + pRequestKernel->RequestSpecific, + &LocalConnectionInformation, + pRequestKernel->ReturnConnectionInformation); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("Local IoCallDriver Invoked %lx %lx\n",pLocalIrp,pIrp)); + + Status = IoCallDriver(&pDeviceContext->DeviceObject,pLocalIrp); + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: IoCallDriver returned %lx\n",Status)); + + if (Status == STATUS_PENDING) { + // Await the completion of the Irp. + Status = KeWaitForSingleObject(&IrpCompletionEvent, // Object to wait on. + Executive, // Reason for waiting + KernelMode, // Processor mode + FALSE, // Alertable + NULL); // Timeout + + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: KeWiatForSingleObject returned %lx\n",Status)); + + // retrieve the completion status from the IRP. if it was successful exit, + // otherwise proceed to the next TA_ADDRESS in the transport address data + // structure. + Status = pLocalIrp->IoStatus.Status; + } + + if (Status != STATUS_SUCCESS) { + // Ensure that the original IRP was not cancelled before continuing. + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + + if (pIrp->Cancel) + { + Status = STATUS_CANCELLED; + } + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + } + + if (pTransportAddress != NULL) { + CTEFreeMem(pTransportAddress); + } + + if ((Status == STATUS_SUCCESS) || + (Status == STATUS_CANCELLED)) { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: exiting because of cancellation or success %lx\n",Status)); + break; + } else { + IF_DBG(NBT_DEBUG_NETBIOS_EX) + KdPrint(("NETBT: trying next component because of failure %lx\n",Status)); + } + } + + IoFreeIrp(pLocalIrp); + } else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + // call the non-NT specific function to setup the connection + Status = NbtConnect( + &Request, + pRequestKernel->RequestSpecific, // Ulong + pRequestKernel->RequestConnectionInformation, + pRequestKernel->ReturnConnectionInformation, + pIrp + ); + } + + return(Status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTDisconnect( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles calling the Non OS specific code to disconnect a + session. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + TDI_REQUEST Request; + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + PTDI_REQUEST_KERNEL pRequestKernel; + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + // call the non-NT specific function to setup the connection + status = NbtDisconnect( + &Request, + pRequestKernel->RequestSpecific, // Large Integer + pRequestKernel->RequestFlags, + pRequestKernel->RequestConnectionInformation, + pRequestKernel->ReturnConnectionInformation, + pIrp + ); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTListen( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + + NTSTATUS status; + TDI_REQUEST Request; + PTDI_REQUEST_KERNEL pRequestKernel; + PIO_STACK_LOCATION pIrpSp; + + CTEPagedCode(); + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a LISTEN !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext; + + // call the non-NT specific function to setup the connection + status = NbtListen( + &Request, + pRequestKernel->RequestFlags, // Ulong + pRequestKernel->RequestConnectionInformation, + pRequestKernel->ReturnConnectionInformation, + pIrp + ); + + + if (status != STATUS_PENDING) + { + NTIoComplete(pIrp,status,0); + } + return(status); + +} +//---------------------------------------------------------------------------- +VOID +NbtCancelListen( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a listen Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + tCLIENTELE *pClientEle; + KIRQL OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + tLISTENREQUESTS *pListenReq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a LISTEN Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + pClientEle = pConnEle->pClientEle; + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + + // now search the client's listen queue looking for this connection + // + CTESpinLock(pClientEle,OldIrq); + + pHead = &pClientEle->ListenHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pListenReq = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage); + if ((pListenReq->pConnectEle == pConnEle) && + (pListenReq->pIrp == pIrp)) + { + RemoveEntryList(pEntry); + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + + + CTESpinFree(pClientEle,OldIrq); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTEMemFree((PVOID)pListenReq); + + return; + + } + pEntry = pEntry->Flink; + + } + + + CTESpinFree(pClientEle,OldIrq); + + + return; + +} + +//---------------------------------------------------------------------------- +VOID +NTCancelSession( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a connect Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). It is called when + the session setup pdu has been sent, and the state is still outbound. + + The cancel routine is only setup when the timer is started to time + sending the session response pdu. + + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + KIRQL OldIrq; + PIO_STACK_LOCATION pIrpSp; + BOOLEAN DerefConnEle=FALSE; + tTIMERQENTRY *pTimer; + tDGRAM_SEND_TRACKING *pTracker; + COMPLETIONCLIENT pCompletion; + COMPLETIONROUTINE pCompletionRoutine; + PVOID pContext; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Connect Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + +#ifdef RASAUTODIAL + // + // Cancel the automatic connection if one's + // in progress. If we don't find the + // connection block in the automatic + // connection driver, then it's already + // been completed. + // + if (pConnEle->fAutoConnecting) { + if (!NbtCancelPostConnect(pIrp)) + return; + } +#endif // RASAUTODIAL + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // the irp could get completed between calling this cancel routine + // and this point in the code + // + if (pConnEle->pIrp) + { + pTracker = (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv; + if (pTracker) + { + pTimer = pTracker->Connect.pTimer; + pTracker->Connect.pTimer = NULL; + pTracker->Flags |= TRACKER_CANCELLED; + + if (pTimer) + { + // + // stop the timer and only continue if the timer was stopped before + // it expired + // + pCompletionRoutine = pTimer->CompletionRoutine; + StopTimer(pTimer,&pCompletion,&pContext); + + if (pCompletion) + { + + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + (*pCompletionRoutine)(pTracker,(PVOID)STATUS_CANCELLED,pTimer); + + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + else + if (pConnEle->state == NBT_SESSION_OUTBOUND) + { + // + // for some reason there is no timer, but the connection is still + // outbound, so call the timer completion routine to kill off + // the connection. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + SessionTimedOut(pTracker,(PVOID)STATUS_CANCELLED,(PVOID)1); + } else { + // + // Free the lock + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + return; + +} + +//---------------------------------------------------------------------------- +VOID +CheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a DNS name query Irp that is passed + down to NBT from Lmhsvc, for the purpose of resolving a name with DNS. + Nbt will complete this irp each time it has a name to resolve with DNS. + + This routine will get the Resource Lock, and Null the Irp ptr in the + DnsQueries structure and then return the irp. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + BOOLEAN DerefConnEle=FALSE; + KIRQL OldIrq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Dns Irp Cancel !!! *****************\n")); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (CheckAddr.QueryIrp) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + CheckAddr.QueryIrp = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + return; + +} + +//---------------------------------------------------------------------------- +VOID +DnsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a DNS name query Irp that is passed + down to NBT from Lmhsvc, for the purpose of resolving a name with DNS. + Nbt will complete this irp each time it has a name to resolve with DNS. + + This routine will get the Resource Lock, and Null the Irp ptr in the + DnsQueries structure and then return the irp. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + BOOLEAN DerefConnEle=FALSE; + KIRQL OldIrq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Dns Irp Cancel !!! *****************\n")); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (DnsQueries.QueryIrp) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + DnsQueries.QueryIrp = NULL; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + else + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + + return; + +} + +//---------------------------------------------------------------------------- +VOID +DiscWaitCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Disconnect Wait Irp - which has + been passed down by a client so that when a disconnect occurs this + irp will complete and inform the client. The action here is to simply + complete the irp with status cancelled. + down to NBT from Lmhsvc, for the purpose of resolving a name with DNS. + Nbt will complete this irp each time it has a name to resolve with DNS. + + This routine will get the Resource Lock, and Null the Irp ptr in the + DnsQueries structure and then return the irp. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + PIO_STACK_LOCATION pIrpSp; + CTELockHandle OldIrq; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Disc Wait Irp Cancel !!! *****************\n")); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(pConnEle,OldIrq); + + if (pConnEle->pIrpClose == pIrp) + { + pConnEle->pIrpClose = NULL; + } + + CTESpinFree(pConnEle,OldIrq); + + pIrp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + return; + +} + +//---------------------------------------------------------------------------- +VOID +WaitForDnsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to DNS, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + BOOLEAN FoundIt = FALSE; + NBT_WORK_ITEM_CONTEXT *Context; + CTELockHandle OldIrq; + tDGRAM_SEND_TRACKING *pTracker; + PVOID pClientCompletion; + PVOID pClientContext; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Wait For Dns Irp Cancel !!! *****************\n")); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + Context = DnsIrpCancelPaged(DeviceContext,pIrp); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Now complete the clients request to return the irp to the client + // + if (Context) + { + // + // this is the name Query tracker + // + pTracker = Context->pTracker; + pClientCompletion = Context->ClientCompletion; + pClientContext = Context->pClientContext; + + // for dns names (NameLen>16), pTracker would be NULL + if (pTracker) + { + // name did not resolve, so delete from table + RemoveName(pTracker->pNameAddr); + + DereferenceTracker(pTracker); + } + + // + // this should complete any name queries that are waiting on + // this first name query - i.e. queries to the resolving name + // + CompleteClientReq(pClientCompletion, + pClientContext, + STATUS_CANCELLED); + + } + +} + +//---------------------------------------------------------------------------- +NBT_WORK_ITEM_CONTEXT * +FindCheckAddrIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to LmHost, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + NBT_WORK_ITEM_CONTEXT *Context; + BOOLEAN FoundIt = FALSE; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + if (CheckAddr.ResolvingNow && CheckAddr.Context) + { + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)CheckAddr.Context)->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + + Context = (NBT_WORK_ITEM_CONTEXT *)CheckAddr.Context; + CheckAddr.Context = NULL; + FoundIt = TRUE; + + } + } + else + { + // + // go through the list of Queued requests to find the correct one + // and cancel it + // + pHead = pEntry = &CheckAddr.ToResolve; + + while ((pEntry = pEntry->Flink) != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + RemoveEntryList(pEntry); + FoundIt = TRUE; + break; + + } + } + } + + return( FoundIt ? Context : NULL ); +} + +//---------------------------------------------------------------------------- +NBT_WORK_ITEM_CONTEXT * +LmHostIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to LmHost, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + NBT_WORK_ITEM_CONTEXT *Context; + BOOLEAN FoundIt = FALSE; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + if (LmHostQueries.ResolvingNow && LmHostQueries.Context) + { + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context)->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + + Context = (NBT_WORK_ITEM_CONTEXT *)LmHostQueries.Context; + LmHostQueries.Context = NULL; + FoundIt = TRUE; + + } + } + else + { + // + // go through the list of Queued requests to find the correct one + // and cancel it + // + pHead = pEntry = &LmHostQueries.ToResolve; + + while ((pEntry = pEntry->Flink) != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + // this is the session setup tracker + // + pTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + if (pTracker->pClientIrp == pIrp) + { + RemoveEntryList(pEntry); + FoundIt = TRUE; + break; + + } + } + } + + return( FoundIt ? Context : NULL ); +} + +//---------------------------------------------------------------------------- +NBT_WORK_ITEM_CONTEXT * +DnsIrpCancelPaged( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Query to DNS, so that the client's + irp can be returned to the client. This cancellation is instigated + by the client (i.e. RDR). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pClientTracker; + NBT_WORK_ITEM_CONTEXT *Context; + BOOLEAN FoundIt = FALSE; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + // + // First check the lmhost list, then the Dns list + // + Context = LmHostIrpCancel(DeviceContext,pIrp); + + if (!Context) + { + + Context = FindCheckAddrIrpCancel(DeviceContext,pIrp); + + if (!Context) + { + + if (DnsQueries.ResolvingNow && DnsQueries.Context) + { + // + // this is the session setup tracker + // + pClientTracker = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context)->pClientContext; + if (pClientTracker->pClientIrp == pIrp) + { + + Context = (NBT_WORK_ITEM_CONTEXT *)DnsQueries.Context; + DnsQueries.Context = NULL; + FoundIt = TRUE; + + } + } + else + { + // + // go through the list of Queued requests to find the correct one + // and cancel it + // + pHead = &DnsQueries.ToResolve; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); + + // this is the session setup tracker + // + pClientTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext; + if (pClientTracker->pClientIrp == pIrp) + { + RemoveEntryList(pEntry); + FoundIt = TRUE; + break; + + } + pEntry = pEntry->Flink; + } + } + } else { + + // IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Found tracker in CheckAddr list: %lx\n", Context)); + FoundIt = TRUE; + } + } + else + { + FoundIt = TRUE; + } + + return( FoundIt ? Context : NULL ); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +QueryProviderCompletion( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the completion event when the Query Provider + Information completes. This routine must decrement the MaxDgramSize + and max send size by the respective NBT header sizes. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - not used + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + PTDI_PROVIDER_INFO pProvider; + ULONG HdrSize; + ULONG SubnetAddr; + ULONG ThisSubnetAddr; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pDeviceContext; + tDEVICECONTEXT *pDevContext; + + + if (NT_SUCCESS(Irp->IoStatus.Status)) + { + pProvider = (PTDI_PROVIDER_INFO)MmGetMdlVirtualAddress(Irp->MdlAddress); + + if (pProvider->MaxSendSize > sizeof(tSESSIONHDR)) + { + // + // Nbt has just a two byte + 1 bit session message length, so it + // can't have a send size larger than 1ffff + // + if (pProvider->MaxSendSize > (0x1FFFF + sizeof(tSESSIONHDR))) + { + pProvider->MaxSendSize = 0x1FFFF; + } + else + { + pProvider->MaxSendSize -= sizeof(tSESSIONHDR); + } + } + else + { + pProvider->MaxSendSize = 0; + } + + // subtract the datagram hdr size and the scope size (times 2) + HdrSize = DGRAM_HDR_SIZE + (NbtConfig.ScopeLength << 1); + + if (pProvider->MaxDatagramSize > HdrSize) + { + pProvider->MaxDatagramSize -= HdrSize; + if (pProvider->MaxDatagramSize > MAX_NBT_DGRAM_SIZE) + { + pProvider->MaxDatagramSize = MAX_NBT_DGRAM_SIZE; + } + } + else + { + pProvider->MaxDatagramSize = 0; + } + + + // + // Set the correct service flags to indicate what Netbt supports. + // + pProvider->ServiceFlags = TDI_SERVICE_MESSAGE_MODE | + TDI_SERVICE_CONNECTION_MODE | + TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_MULTICAST_SUPPORTED | + TDI_SERVICE_DELAYED_ACCEPTANCE | + TDI_SERVICE_ROUTE_DIRECTED; + + pProvider->MinimumLookaheadData = 128; + + // + // Check if any of the adapters with the same subnet address have + // the PointtoPoint bit set - and if so set it in the response. + // + pDeviceContext = (tDEVICECONTEXT *)DeviceContext; + SubnetAddr = pDeviceContext->IpAddress & pDeviceContext->SubnetMask; + + pEntry = pHead = &NbtConfig.DeviceContexts; + while ((pEntry = pEntry->Flink) != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + ThisSubnetAddr = pDevContext->IpAddress & pDevContext->SubnetMask; + + if ((SubnetAddr == ThisSubnetAddr) && + (pDevContext->PointToPoint)) + { + pProvider->ServiceFlags |= TDI_SERVICE_POINT_TO_POINT; + break; + } + } + } + + + // + // Must return a non-error status otherwise the IO system will not copy + // back into the users buffer. + // + + return(STATUS_SUCCESS); + + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTQueryInformation( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; + NTSTATUS status; + NTSTATUS Locstatus; + PVOID pBuffer; + LONG Size ; + PTA_NETBIOS_ADDRESS BroadcastAddress; + ULONG AddressLength; + ULONG BytesCopied; + PDEVICE_OBJECT pDeviceObject; + + // + // Should not be pageable since AFD can call us at raised Irql in case of AcceptEx. + // + // CTEPagedCode(); + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&pIrpSp->Parameters; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("the query type is %X\n",Query->QueryType)); + + switch( Query->QueryType) + { + case TDI_QUERY_BROADCAST_ADDRESS: + + // the broadcast address is the netbios name "*0000000..." + + BroadcastAddress = (PTA_NETBIOS_ADDRESS)NbtAllocMem( + sizeof(TA_NETBIOS_ADDRESS),NBT_TAG('b')); + + if (!BroadcastAddress) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + AddressLength = sizeof(TA_NETBIOS_ADDRESS); + + BroadcastAddress->TAAddressCount = 1; + BroadcastAddress->Address[0].AddressLength = NETBIOS_NAME_SIZE + + sizeof(USHORT); + BroadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + BroadcastAddress->Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; + + // the broadcast address to NetBios is "* 000000...", an * followed + // by 15 zeroes. + CTEZeroMemory(BroadcastAddress->Address[0].Address[0].NetbiosName, + NETBIOS_NAME_SIZE); + BroadcastAddress->Address[0].Address[0].NetbiosName[0] = '*'; + + + status = TdiCopyBufferToMdl ( + (PVOID)BroadcastAddress, + 0, + AddressLength, + pIrp->MdlAddress, + 0, + (PULONG)&pIrp->IoStatus.Information); + + CTEMemFree((PVOID)BroadcastAddress); + + break; + + + case TDI_QUERY_PROVIDER_INFO: + + // + // Simply pass the Irp on by to the Transport, and let it + // fill in the provider info + // + if (StreamsStack) + { + TdiBuildQueryInformation(pIrp, + pDeviceContext->pDgramDeviceObject, + pDeviceContext->pDgramFileObject, + QueryProviderCompletion, + NULL, + TDI_QUERY_PROVIDER_INFO, + pIrp->MdlAddress); + } + else + { + TdiBuildQueryInformation(pIrp, + pDeviceContext->pControlDeviceObject, + pDeviceContext->pControlFileObject, + QueryProviderCompletion, + NULL, + TDI_QUERY_PROVIDER_INFO, + pIrp->MdlAddress); + } + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(pDeviceContext->pControlDeviceObject,pIrp); + // + // we must return the next drivers ret code back to the IO subsystem + // + return(status); + + break; + + case TDI_QUERY_ADAPTER_STATUS: + + // + // check if it is a remote or local adapter status + // + if (Query->RequestConnectionInformation && + Query->RequestConnectionInformation->RemoteAddress) + { + PCHAR pName; + ULONG lNameType; + ULONG NameLen; + + // + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + + status = GetNetBiosNameFromTransportAddress( + Query->RequestConnectionInformation->RemoteAddress, + &pName, + &NameLen, + &lNameType); + + if ( NT_SUCCESS(status) && + (lNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) && + (NameLen <= NETBIOS_NAME_SIZE)) + { + status = NbtSendNodeStatus(pDeviceContext, + pName, + pIrp, + 0, + 0, + NodeStatusDone); + } + + // only complete the irp (below) for failure status's + if (status == STATUS_PENDING) + { + return(status); + } + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + } + else + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryAdapterStatus(pDeviceContext, + &pBuffer, + &Size); + + } + break; + + + + + case TDI_QUERY_CONNECTION_INFO: + { + tCONNECTELE *pConnectEle; + tLOWERCONNECTION *pLowerConn; + + // pass to transport to get the current throughput, delay and + // reliability numbers + // + + pConnectEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; +#if DBG + if (pConnectEle->Verify != NBT_VERIFY_CONNECTION) + { + status = STATUS_INVALID_HANDLE; + break; + } +#endif + pLowerConn = (tLOWERCONNECTION *)pConnectEle->pLowerConnId; + if (!pLowerConn) + { + status = STATUS_CONNECTION_INVALID; + break; + } + // + // Simply pass the Irp on by to the Transport, and let it + // fill in the info + // + pDeviceObject = IoGetRelatedDeviceObject( pLowerConn->pFileObject ); + + TdiBuildQueryInformation(pIrp, + pDeviceObject, + pLowerConn->pFileObject, + NULL, NULL, + TDI_QUERY_CONNECTION_INFO, + pIrp->MdlAddress); + + + status = IoCallDriver(pDeviceObject,pIrp); + + // + // we must return the next drivers ret code back to the IO subsystem + // + return(status); + + break; + } + + case TDI_QUERY_FIND_NAME: + // + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + status = NbtQueryFindName(Query->RequestConnectionInformation, + pDeviceContext, + pIrp, + FALSE); + + if (status == STATUS_PENDING) + { + return(status); + } + + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + break; + + case TDI_QUERY_ADDRESS_INFO: + status = NbtQueryGetAddressInfo( + pIrpSp, + &pBuffer, + &Size + ); + break; + + case TDI_QUERY_SESSION_STATUS: + default: + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt Query Info NOT SUPPORTED = %X\n",Query->QueryType)); + status = STATUS_NOT_SUPPORTED; + break; + + } + + BytesCopied = 0; + if (!NT_ERROR(status) && // allow buffer overflow to pass by + ((Query->QueryType == TDI_QUERY_ADAPTER_STATUS) || + (Query->QueryType == TDI_QUERY_ADDRESS_INFO))) + { + Locstatus = TdiCopyBufferToMdl( + pBuffer, + 0, + Size, + pIrp->MdlAddress, + 0, + &BytesCopied); + + if (Locstatus == STATUS_BUFFER_OVERFLOW) + { + status = STATUS_BUFFER_OVERFLOW; + } + CTEMemFree((PVOID)pBuffer); + } + // + // either Success or an Error + // so complete the irp + // + + NTIoComplete(pIrp,status,BytesCopied); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtQueryGetAddressInfo( + IN PIO_STACK_LOCATION pIrpSp, + OUT PVOID *ppBuffer, + OUT ULONG *pSize +) +{ + NTSTATUS status; + BOOLEAN IsGroup; + PLIST_ENTRY p; + tADDRESSELE *pAddressEle; + tNAMEADDR *pNameAddr; + tADDRESS_INFO *pAddressInfo; + tCLIENTELE *pClientEle; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + + pClientEle = pIrpSp->FileObject->FsContext; + if (pClientEle->Verify != NBT_VERIFY_CLIENT) + { + CTELockHandle OldIrq1; + pConnectEle = (tCONNECTELE *)pClientEle; + + // + // We crashed here since the pLowerConn was NULL below. + // Check the state of the connection, since it is possible that the connection + // was aborted and the disconnect indicated, but this query came in before the client + // got the disconnect indication. + // If the state is idle (in case of TDI_DISCONNECT_ABORT) or DISCONNECTED + // (TDI_DISCONNECT_RELEASE), error out. + // Also check for NBT_ASSOCIATED. + // + // NOTE: If NbtOpenConnection is unable to allocate the lower conn block (say, if the session fileobj + // has not been created yet), the state will be still be IDLE, so we are covered here. + // + CTESpinLock(pConnectEle,OldIrq1); + + if ((pConnectEle->Verify != NBT_VERIFY_CONNECTION) || + (pConnectEle->state <= NBT_ASSOCIATED) || // includes NBT_IDLE + (pConnectEle->state == NBT_DISCONNECTED)) + { + status = STATUS_INVALID_HANDLE; + } + else + { + // + // A TdiQueryInformation() call requesting TDI_QUERY_ADDRESS_INFO + // on a connection. Fill in a TDI_ADDRESS_INFO containing both the + // NetBIOS address and the IP address of the remote. Some of the + // fields are fudged. + // + + PNBT_ADDRESS_PAIR_INFO pAddressPairInfo; + pAddressPairInfo = NbtAllocMem(sizeof (NBT_ADDRESS_PAIR_INFO), NBT_TAG('c')); + + if (pAddressPairInfo) + { + memset ( pAddressPairInfo, 0, sizeof(NBT_ADDRESS_PAIR_INFO) ); + + pAddressPairInfo->ActivityCount = 1; + + pAddressPairInfo->AddressPair.TAAddressCount = 2; + + pAddressPairInfo->AddressPair.AddressNetBIOS.AddressLength = + TDI_ADDRESS_LENGTH_NETBIOS; + + pAddressPairInfo->AddressPair.AddressNetBIOS.AddressType = + TDI_ADDRESS_TYPE_NETBIOS; + + pAddressPairInfo->AddressPair.AddressNetBIOS.Address.NetbiosNameType = + TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + memcpy( &pAddressPairInfo->AddressPair.AddressNetBIOS.Address.NetbiosName[0], + &pConnectEle->RemoteName[0], + 16 + ); + + pAddressPairInfo->AddressPair.AddressIP.AddressLength = + TDI_ADDRESS_LENGTH_IP; + + pAddressPairInfo->AddressPair.AddressIP.AddressType = + TDI_ADDRESS_TYPE_IP; + + // + // Check for NULL (should not be NULL here since we check for states above). + // + // BUGBUG: Remove this check once we are sure that we are not hitting this condition + // + if (pConnectEle->pLowerConnId) { + pAddressPairInfo->AddressPair.AddressIP.Address.in_addr = + pConnectEle->pLowerConnId->SrcIpAddr; + + *ppBuffer = (PVOID)pAddressPairInfo; + *pSize = sizeof(NBT_ADDRESS_PAIR_INFO); + status = STATUS_SUCCESS; + } else { + DbgPrint("pLowerConn NULL in pConnEle%lx, state: %lx\n", pConnectEle, pConnectEle->state); + status = STATUS_INVALID_HANDLE; + } + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + CTESpinFree(pConnectEle,OldIrq1); + } + else + { + pAddressInfo = NbtAllocMem(sizeof(tADDRESS_INFO),NBT_TAG('c')); + if (pAddressInfo) + { + // + // count the clients attached to this address + // We need to spinlock the address element, which + // is why this routine is not pageable + // + pAddressInfo->ActivityCount = 0; + pAddressEle = pClientEle->pAddress; + + CTESpinLock(pAddressEle,OldIrq); + + for (p = pAddressEle->ClientHead.Flink; + p != &pAddressEle->ClientHead; + p = p->Flink) { + ++pAddressInfo->ActivityCount; + } + + CTESpinFree(pAddressEle,OldIrq); + + pNameAddr = pAddressEle->pNameAddr; + + IsGroup = (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ? + FALSE : TRUE; + + TdiBuildNetbiosAddress((PUCHAR)pNameAddr->Name, + IsGroup, + &pAddressInfo->NetbiosAddress); + + *ppBuffer = (PVOID)pAddressInfo; + *pSize = sizeof(tADDRESS_INFO); + status = STATUS_SUCCESS; + + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + return status; +} + +//---------------------------------------------------------------------------- +NTSTATUS +DispatchIoctls( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp) + +/*++ +Routine Description: + + This Routine handles calling the OS independent routine depending on + the Ioctl passed in. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status=STATUS_UNSUCCESSFUL; + NTSTATUS Locstatus; + ULONG ControlCode; + ULONG Size; + PVOID pBuffer; + + CTEPagedCode(); + + ControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Ioctl Value is %X\n",ControlCode)); + + switch (ControlCode) + { + case IOCTL_NETBT_PURGE_CACHE: + { + + status = NbtResyncRemoteCache(); + + break; + } + break; + case IOCTL_NETBT_GET_CONNECTIONS: + { + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryConnectionList(NULL, + &pBuffer, + &Size); + } + break; + } + + case IOCTL_NETBT_ADAPTER_STATUS: + + if (pIrp->MdlAddress) + { + PIO_STACK_LOCATION pIrpSp; + tIPANDNAMEINFO *pIpAndNameInfo; + PCHAR pName; + ULONG lNameType; + ULONG NameLen; + ULONG IpAddrsList[2]; + + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIpAndNameInfo = pIrp->AssociatedIrp.SystemBuffer; + + // this routine gets a ptr to the netbios name out of the wierd + // TDI address syntax. + status = GetNetBiosNameFromTransportAddress( + &pIpAndNameInfo->NetbiosAddress, + &pName, + &NameLen, + &lNameType); + + if ( NT_SUCCESS(status) && + (lNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) && + (NameLen <= NETBIOS_NAME_SIZE)) + { + // + // Nbtstat sends down * in the first byte on Nbtstat -A <IP address> + // Make sure we let that case go ahead. + // + if ((pName[0] == '*') && + (pIpAndNameInfo->IpAddress == 0)) { + + status = STATUS_BAD_NETWORK_PATH; + } else { + IpAddrsList[0] = pIpAndNameInfo->IpAddress; + IpAddrsList[1] = 0; + status = NbtSendNodeStatus(pDeviceContext, + pName, + pIrp, + &IpAddrsList[0], + 0, + NodeStatusDone); + } + + } + // only complete the irp (below) for failure status's + if (status == STATUS_PENDING) + { + return(status); + } + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + } + break; + + case IOCTL_NETBT_GET_REMOTE_NAMES: + + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryAdapterStatus(NULL, + &pBuffer, + &Size); + } + break; + + case IOCTL_NETBT_GET_BCAST_NAMES: + { + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + + // return an array of netbios names that are registered + status = NbtQueryBcastVsWins(pDeviceContext,&pBuffer,&Size); + } + break; + } + + case IOCTL_NETBT_REREAD_REGISTRY: + + status = NTReReadRegistry(pDeviceContext); + + break; + + case IOCTL_NETBT_ENABLE_EXTENDED_ADDR: { + // + // Enable extended addressing - pass up IP addrs on Datagram Recvs. + // + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + tCLIENTELE *pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + status = STATUS_SUCCESS; + + if (pIrpSp->FileObject->FsContext2 != (PVOID)NBT_ADDRESS_TYPE) { + status = STATUS_INVALID_ADDRESS; + } else { + pClientEle->ExtendedAddress = TRUE; + } + break; + } + + case IOCTL_NETBT_DISABLE_EXTENDED_ADDR: { + // + // Disnable extended addressing - dont pass up IP addrs on Datagram Recvs. + // + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + tCLIENTELE *pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + status = STATUS_SUCCESS; + + if (pIrpSp->FileObject->FsContext2 != (PVOID)NBT_ADDRESS_TYPE) { + status = STATUS_INVALID_ADDRESS; + } else { + pClientEle->ExtendedAddress = FALSE; + } + break; + } + + case IOCTL_NETBT_NEW_IPADDRESS: + + { + + tNEW_IP_ADDRESS *pNewAddress = (tNEW_IP_ADDRESS *)pIrp->AssociatedIrp.SystemBuffer; + + status = NbtNewDhcpAddress(pDeviceContext, + pNewAddress->IpAddress, + pNewAddress->SubnetMask); + + break; + } + + case IOCTL_NETBT_ADD_INTERFACE: + // + // Creates a dummy devicecontext which can be primed by the layer above + // with a DHCP address. This is to support multiple IP addresses per adapter + // for the Clusters group; but can be used by any module that needs support + // for more than one IP address per adapter. This private interface hides the + // devices thus created from the setup/regisrty and that is fine since the + // component (say, the clusters client) takes the responsibility for ensuring + // that the server (above us) comes to know of this new device. + // + { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("Ioctl Value is %X (IOCTL_NETBT_ADD_INTERFACE)\n",ControlCode)); + pBuffer = pIrp->AssociatedIrp.SystemBuffer; + Size = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + // + // return the export string created. + // + status = NbtAddNewInterface(pIrp, pBuffer, Size); + + NTIoComplete(pIrp,status,(ULONG)-1); + return status; + } + + case IOCTL_NETBT_DELETE_INTERFACE: + { +#if 0 + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + // + // Validate input buffer size + // + Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; + if (Size < sizeof(NETBT_ADD_DEL_IF)) { + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("NbtAddNewInterface: Output buffer too small for struct\n")); + status = STATUS_INVALID_PARAMETER; + } else { + pBuffer = pIrp->AssociatedIrp.SystemBuffer; + status = NbtDestroyDeviceObject(pBuffer); + } +#endif + // + // Delete the device this came down on.. + // + ASSERT(!pDeviceContext->IsDestroyed); + ASSERT(pDeviceContext->IsDynamic); + + status = NbtDestroyDeviceObject(pDeviceContext); + + break; + } + + case IOCTL_NETBT_QUERY_INTERFACE_INSTANCE: + { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + + // + // Validate input/output buffer size + // + Size = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; + if (Size < sizeof(NETBT_ADD_DEL_IF)) { + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("NbtQueryInstance: Output buffer too small for struct\n")); + status = STATUS_INVALID_PARAMETER; + } else { + PNETBT_ADD_DEL_IF pAddDelIf = (PNETBT_ADD_DEL_IF)pIrp->AssociatedIrp.SystemBuffer; + status = STATUS_SUCCESS; + + ASSERT(pDeviceContext->IsDynamic); + pAddDelIf->InstanceNumber = pDeviceContext->InstanceNumber; + pAddDelIf->Status = status; + pIrp->IoStatus.Information = sizeof(NETBT_ADD_DEL_IF); + + NTIoComplete(pIrp,status,(ULONG)-1); + return status; + + } + break; + } + + case IOCTL_NETBT_SET_WINS_ADDRESS: { + // + // Sets the WINS addresses for a dynamic adapter + // + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp); + + // + // Validate input/output buffer size + // + Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; + if (Size < sizeof(NETBT_SET_WINS_ADDR)) { + // IF_DBG(NBT_DEBUG_PNP_POWER) + KdPrint(("NbtSetWinsAddr: Input buffer too small for struct\n")); + status = STATUS_INVALID_PARAMETER; + } else { + PNETBT_SET_WINS_ADDR pSetWinsAddr = (PNETBT_SET_WINS_ADDR)pIrp->AssociatedIrp.SystemBuffer; + status = STATUS_SUCCESS; + + ASSERT(pDeviceContext->IsDynamic); + + pDeviceContext->lNameServerAddress = pSetWinsAddr->PrimaryWinsAddr; + pDeviceContext->lBackupServer = pSetWinsAddr->SecondaryWinsAddr; + + pSetWinsAddr->Status = status; + pIrp->IoStatus.Information = sizeof(NETBT_SET_WINS_ADDR); + + NTIoComplete(pIrp,status,(ULONG)-1); + return status; + + } + } + + case IOCTL_NETBT_DNS_NAME_RESOLVE: + { + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + // return an array of netbios names that are registered + status = NtDnsNameResolve(pDeviceContext,pBuffer,Size,pIrp); + + return(status); + } + break; + } + + case IOCTL_NETBT_CHECK_IP_ADDR: { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Ioctl Value is %X (IOCTL_NETBT_CHECK_IP_ADDR)\n",ControlCode)); + + if (pIrp->MdlAddress) + { + Size = MmGetMdlByteCount( pIrp->MdlAddress ) ; + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + // return an array of netbios names that are registered + status = NtCheckForIPAddr(pDeviceContext,pBuffer,Size,pIrp); + + return(status); + } + break; + } + + case IOCTL_NETBT_FIND_NAME: + { + tIPADDR_BUFFER *pIpAddrBuffer; + + // + // in case the call results in a name query on the wire... + // + IoMarkIrpPending(pIrp); + + pIpAddrBuffer = pIrp->AssociatedIrp.SystemBuffer; + + status = NbtQueryFindName((PTDI_CONNECTION_INFORMATION)pIpAddrBuffer, + pDeviceContext, + pIrp, + TRUE); + + if (status == STATUS_PENDING) + { + return(status); + } + + // the request has been satisfied, so unmark the pending + // since we will return the irp below + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + break; + } + + case IOCTL_NETBT_GET_WINS_ADDR: + { + if (pIrp->MdlAddress) + { + tWINS_ADDRESSES *pBuffer; + + if( MmGetMdlByteCount( pIrp->MdlAddress ) >= sizeof(tWINS_ADDRESSES)) + { + pBuffer = (tWINS_ADDRESSES *)MmGetSystemAddressForMdl(pIrp->MdlAddress); + pBuffer->PrimaryWinsServer = pDeviceContext->lNameServerAddress; + pBuffer->BackupWinsServer = pDeviceContext->lBackupServer; + + status = STATUS_SUCCESS; + } + else + status = STATUS_BUFFER_OVERFLOW; + + break; + + } + break; + } + + case IOCTL_NETBT_GET_IP_ADDRS: + { + ULONG Length; + PULONG pIpAddr; + PLIST_ENTRY pEntry,pHead; + tDEVICECONTEXT *pDevContext; + + // + // return this devicecontext's ip address and all the other + // ip addrs after it. + // + if (pIrp->MdlAddress) + { + Length = MmGetMdlByteCount( pIrp->MdlAddress ); + + if (Length < sizeof(ULONG)) { + status = STATUS_BUFFER_TOO_SMALL; + } + else { + // + // Put this adapter first in the list + // + pIpAddr = (PULONG )MmGetSystemAddressForMdl(pIrp->MdlAddress); + *pIpAddr = pDeviceContext->IpAddress; + pIpAddr++; + Length -= sizeof(ULONG); + status = STATUS_SUCCESS; + + pEntry = pHead = &NbtConfig.DeviceContexts; + while ((pEntry = pEntry->Flink) != pHead) + { + if (Length < sizeof(ULONG)) { + status = STATUS_BUFFER_OVERFLOW; + break; + } + + pDevContext = CONTAINING_RECORD( + pEntry, + tDEVICECONTEXT, + Linkage + ); + + if ((pDevContext != pDeviceContext) && + (pDevContext->IpAddress)) + { + *pIpAddr = pDevContext->IpAddress; + pIpAddr++; + Length -= sizeof(ULONG); + } + } + + if (status == STATUS_SUCCESS) { + if (Length < sizeof(ULONG)) { + status = STATUS_BUFFER_OVERFLOW; + } + else { + // + // put a 0 address on the end + // + *pIpAddr = 0; + } + } + } + } + + break; + } + + case IOCTL_NETBT_GET_IP_SUBNET: + { + ULONG Length; + PULONG pIpAddr; + + // + // return this devicecontext's ip address and all the other + // ip addrs after it. + // + if (pIrp->MdlAddress) + { + Length = MmGetMdlByteCount( pIrp->MdlAddress ); + if (Length < 2*sizeof(ULONG)) + { + status = STATUS_BUFFER_OVERFLOW; + } + else + { + // + // Put this adapter first in the list + // + pIpAddr = (PULONG )MmGetSystemAddressForMdl(pIrp->MdlAddress); + *pIpAddr = pDeviceContext->IpAddress; + pIpAddr++; + *pIpAddr = pDeviceContext->SubnetMask; + + status = STATUS_SUCCESS; + } + } + } + break; + + case IOCTL_NETBT_WINS_RCV: + { + if (pIrp->MdlAddress) + { + status = RcvIrpFromWins(pDeviceContext,pIrp); + return(status); + + } + break; + } + case IOCTL_NETBT_WINS_SEND: + { + if (pIrp->MdlAddress) + { + BOOLEAN MustSend; + + status = WinsSendDatagram(pDeviceContext,pIrp,(MustSend = FALSE)); + return(status); + + break; + + } + break; + } + + } + + // + // copy the reponse to the client's Mdl + // + if (!NT_ERROR(status) && // allow buffer overflow to pass by + ((ControlCode == IOCTL_NETBT_GET_REMOTE_NAMES) || + (ControlCode == IOCTL_NETBT_GET_BCAST_NAMES) || + (ControlCode == IOCTL_NETBT_GET_CONNECTIONS)) ) + { + Locstatus = TdiCopyBufferToMdl( + pBuffer, + 0, + Size, + pIrp->MdlAddress, + 0, + (PULONG)&pIrp->IoStatus.Information); + + if (Locstatus == STATUS_BUFFER_OVERFLOW) + { + status = STATUS_BUFFER_OVERFLOW; + } + CTEMemFree((PVOID)pBuffer); + } + // + // either Success or an Error + // so complete the irp + // + NTIoComplete(pIrp,status,0); + + return(status); + +} +//---------------------------------------------------------------------------- +VOID +NTCancelReceive( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a listen Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + tLOWERCONNECTION *pLowerConn; + KIRQL OldIrq; + KIRQL OldIrq1; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + PIRP pRcvIrp; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Receive Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + CTESpinLock(pLowerConn,OldIrq); + } + + if (pConnEle->Verify == NBT_VERIFY_CONNECTION) + { + // now search the connection's receive queue looking for this Irp + // + pHead = &pConnEle->RcvHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pRcvIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + if (pRcvIrp == pIrp) + { + RemoveEntryList(pEntry); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + return; + + } + pEntry = pEntry->Flink; + + } + + } + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq); + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + return; + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTReceive( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles Queuing a receive buffer on a connection or passing + the recieve buffer to the transport if there is outstanding data waiting + to be received on the connection. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status=STATUS_UNSUCCESSFUL; + PTDI_REQUEST_KERNEL pRequestKernel; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + KIRQL OldIrq; + ULONG ToCopy; + ULONG ClientRcvLen; + tLOWERCONNECTION *pLowerConn; + ULONG RemainingPdu; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; + + pConnEle = pIrpSp->FileObject->FsContext; + + PUSH_LOCATION(0x30); + + // be sure we have not been passed some bogus ptr + // +#if DBG + if (pConnEle->Verify != NBT_VERIFY_CONNECTION) + { + status = STATUS_INVALID_HANDLE; + NTIoComplete(pIrp,status,0); + return(status); + + } +#endif + if (pConnEle->state == NBT_SESSION_UP) + { + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + ULONG BytesCopied; + + PUSH_LOCATION(0x31); + + pLowerConn = pConnEle->pLowerConnId; + + CTESpinLock(pLowerConn,OldIrq); + + if (pLowerConn->StateRcv != PARTIAL_RCV) + { + // **** Fast Path Code **** + // + // Queue this receive buffer on to the Rcv Head + // + PUSH_LOCATION(0x46); + InsertTailList(&pConnEle->RcvHead, + &pIrp->Tail.Overlay.ListEntry); + + status = NTCheckSetCancelRoutine(pIrp,(PVOID)NTCancelReceive,pDeviceContext); + + if (!NT_SUCCESS(status)) + { + RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); + CTESpinFree(pLowerConn,OldIrq); + NTIoComplete(pIrp,status,0); + return(status); + } + else + { + // + // if the irp is not cancelled, returning pending + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_PENDING); + } + + + } + else + { + + // ***** Partial Rcv - Data Still in Transport ***** + + BOOLEAN ZeroLengthSend; + + PUSH_LOCATION(0x32); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:A Rcv Buffer posted data in Xport,InXport= %X,InIndic %X RcvIndicated %X\n", + pConnEle->BytesInXport,pLowerConn->BytesInIndicate, + pConnEle->ReceiveIndicated)); + + + // get the MDL chain length + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // Reset the Irp pending flag + pIrpSp->Control &= ~SL_PENDING_RETURNED; + + // fill in the next irp stack location with our completion routine. + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pConnEle->pLowerConnId; + pIrpSp->Flags = 0; + + // set flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pConnEle->pLowerConnId->pFileObject); + pIrpSp->FileObject = pConnEle->pLowerConnId->pFileObject; + + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + // Since this irp is going to traverse through CompletionRcv, we + // need to set the following, since it undoes this stuff. + // This also prevents the LowerConn from being blown away before + // the irp has returned from the transport + // + pLowerConn->RefCount++; + // + // pass the receive buffer directly to the transport, decrementing + // the number of receive bytes that have been indicated + // + ASSERT(pConnEle->TotalPcktLen >= pConnEle->BytesRcvd); + if (pClientParams->ReceiveLength > (pConnEle->TotalPcktLen - pConnEle->BytesRcvd)) + { + pParams->ReceiveLength = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + } + else + { + pParams->ReceiveLength = pClientParams->ReceiveLength; + } + + ClientRcvLen = pParams->ReceiveLength; + // + // Set the amount of data that we will receive so when the + // irp completes in completionRcv, we can fill in that + // info in the Irp + // + pConnEle->CurrentRcvLen = ClientRcvLen; + + // if a zero length send occurs, then ReceiveIndicated is set + // to zero with the state set to RcvPartial. Or, the client may + // pass down an Irp with no MDL in it!! - stupid but true + // + if ((pConnEle->ReceiveIndicated == 0) || !pIrp->MdlAddress) + { + ZeroLengthSend = TRUE; + } + else + ZeroLengthSend = FALSE; + + // calculate how many bytes are still remaining for the client. + ASSERT(pConnEle->ReceiveIndicated <= 0x20000); + if (pConnEle->ReceiveIndicated > ClientRcvLen) + { + PUSH_LOCATION(0x40); + pConnEle->ReceiveIndicated -= ClientRcvLen; + } + else + { + pConnEle->ReceiveIndicated = 0; + } + + if (pLowerConn->BytesInIndicate || ZeroLengthSend) + { + PMDL Mdl; + + PUSH_LOCATION(0x33); + if (ClientRcvLen > pLowerConn->BytesInIndicate) + { + ToCopy = pLowerConn->BytesInIndicate; + } + else + { + PUSH_LOCATION(0x41); + ToCopy = ClientRcvLen; + } + + // copy data from the indicate buffer to the client's buffer, + // remembering that there is a session header in the indicate + // buffer at the start of it... so skip that. The + // client can pass down a null Mdl address for a zero length + // rcv so check for that. + // + Mdl = pIrp->MdlAddress; + + if (Mdl) + { + TdiCopyBufferToMdl(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl), + 0, // src offset + ToCopy, + Mdl, + 0, // dest offset + &BytesCopied); + } + else + { + BytesCopied = 0; + } + + // client's MDL is too short... + if (BytesCopied != ToCopy) + { + PUSH_LOCATION(0x42); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Receive Buffer too short for Indicate buff BytesCopied %X, ToCopy %X\n", + BytesCopied, ToCopy)); + +// ToCopy = BytesCopied; + + // so the irp will be completed, below + ClientRcvLen = BytesCopied; + } + + pLowerConn->BytesInIndicate -= (USHORT)BytesCopied; + + // this case is only if the irp is full and should be returned + // now. + if (BytesCopied == ClientRcvLen) + { + PUSH_LOCATION(0x34); + // check if the indicate buffer is empty now. If not, then + // move the data forward to the start of the buffer. + // + if (pLowerConn->BytesInIndicate) + { + PUSH_LOCATION(0x43); + CopyToStartofIndicate(pLowerConn,BytesCopied); + } + // + // the irp is full so complete it + // + // the client MDL is full, so complete his irp + // CompletionRcv increments the number of bytes rcvd + // for this session pdu (pConnEle->BytesRcvd). + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // since we are completing it and TdiRcvHandler did not set the next + // one. + // + ASSERT(pIrp->CurrentLocation > 1); + + IoSetNextIrpStackLocation(pIrp); + + // we need to track how much of the client's MDL has filled + // up to know when to return it. CompletionRcv subtracts + // from this value as it receives bytes. + pConnEle->FreeBytesInMdl = ClientRcvLen; + pConnEle->CurrentRcvLen = ClientRcvLen; + + // + // this will complete through CompletionRcv... and for that + // reason it will get any more data left in the transport. The + // Completion routine will set the correct state for the rcv when + // it processes this Irp ( to INDICATED, if needed). + // + if (pConnEle->ReceiveIndicated == 0) + { + PUSH_LOCATION(0x44); + ASSERT(pLowerConn->BytesInIndicate == 0); + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + + } + CTESpinFree(pLowerConn,OldIrq); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + return(STATUS_SUCCESS); + } + else + { + PUSH_LOCATION(0x35); + // + // clear the number of bytes in the indicate buffer since the client + // has taken more than the data left in the Indicate buffer + // + pLowerConn->BytesInIndicate = 0; + + // decrement the client rcv len by the amount already put into the + // client Mdl + // + ClientRcvLen -= BytesCopied; + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt: Pass Client Irp to Xport BytesinXport %X, ClientRcvLen %X\n", + pConnEle->BytesInXport,ClientRcvLen)); + // + // Set the amount left inthe transport after this irp + // completes + if (pConnEle->BytesInXport < ClientRcvLen ) + { + pConnEle->BytesInXport = 0; + } + else + { + PUSH_LOCATION(0x45); + pConnEle->BytesInXport -= ClientRcvLen; + + } + + // Adjust the number of bytes in the Mdl chain so far since the + // completion routine will only count the bytes filled in by the + // transport + pConnEle->BytesRcvd += BytesCopied; + + // the client is going to take more data from the transport with + // this Irp. Set the new Rcv Length that accounts for the data just + // copied to the Irp. + // + pParams->ReceiveLength = ClientRcvLen; + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:ClientRcvLen = %X, LeftinXport= %X BytesCopied= %X %X\n",ClientRcvLen, + pConnEle->BytesInXport,BytesCopied,pLowerConn)); + + // set the state to this so we can undo the MDL footwork + // in completion rcv - since we have made a partial MDL and + // put that at the start of the chain. + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + + // Note that the Irp Mdl address changes below + // when MakePartialMdl is called so this line cannot + // be moved to the common code below!! + pLowerConn->pMdl = pIrp->MdlAddress; + + // setup the next MDL so we can create a partial mdl correctly + // in TdiReceiveHandler + // + pConnEle->pNextMdl = pIrp->MdlAddress; + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + // Force the system to map and lock the user buffer + MmGetSystemAddressForMdl(pIrp->MdlAddress); + MakePartialMdl(pConnEle,pIrp,BytesCopied); + + // pass the Irp to the transport + // + // + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:Calling IoCallDriver\n")); + ASSERT(pIrp->CurrentLocation > 1); + + } + + } + else + { + PUSH_LOCATION(0x36); + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt:Pass Irp To Xport Bytes in Xport %X, ClientRcvLen %X, RcvIndicated %X\n", + pConnEle->BytesInXport,ClientRcvLen,pConnEle->ReceiveIndicated)); + // + // there are no bytes in the indicate buffer, so just pass the + // irp on down to the transport + // + // + // Decide the next state depending on whether the transport currently + // has enough data for this irp + // + if (pConnEle->BytesInXport < ClientRcvLen) + { + PUSH_LOCATION(0x37); + pConnEle->BytesInXport = 0; + // + // to get to here, the implication is that ReceiveIndicated + // equals zero too!! Since ReceiveInd cannot be more than + // BytesInXport, so we can change the state to fill irp without + // worrying about overwriting PartialRcv + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + // setup the next MDL so we can create a partial mdl correctly + // in TdiReceiveHandler + // + pConnEle->pNextMdl = pIrp->MdlAddress; + + } + else + { + + PUSH_LOCATION(0x38); + pConnEle->BytesInXport -= ClientRcvLen; + + // set the state to this so we know what to do in completion rcv + // + if (pConnEle->ReceiveIndicated == 0) + { + PUSH_LOCATION(0x39); + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + } + + + // + // save the Irp so we can reconstruct things later + // + pLowerConn->pMdl = pIrp->MdlAddress; + + } + + // *** Common Code to passing irp to transport - when there is + // data in the indicate buffer and when there isn't + + // keep track of data in MDL so we know when it is full + // and we need to return it to the user + // + pConnEle->FreeBytesInMdl = pParams->ReceiveLength; + // Force the system to map and lock the user buffer + MmGetSystemAddressForMdl(pIrp->MdlAddress); + + // + // Null the Irp since we are passing it to the transport. + // + pConnEle->pIrpRcv = NULL; + CTESpinFree(pLowerConn,OldIrq); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pLowerConn->pFileObject),pIrp); + + } + + return(status); + } + + // + // session in wrong state so reject the buffer posting + // + + PUSH_LOCATION(0x47); + // + // complete the irp, since there must have been some sort of error + // to get to here + // + NTIoComplete(pIrp,STATUS_REMOTE_DISCONNECT,0); + + return(status); + + +} +//---------------------------------------------------------------------------- +VOID +NTCancelRcvDgram( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a listen Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCLIENTELE *pClientEle; + KIRQL OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + tRCVELE *pRcvEle; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a Rcv Dgram Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + if (pClientEle->Verify == NBT_VERIFY_CLIENT) + { + // now search the client's listen queue looking for this connection + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pHead = &pClientEle->RcvDgramHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pRcvEle = CONTAINING_RECORD(pEntry,tRCVELE,Linkage); + if (pRcvEle->pIrp == pIrp) + { + RemoveEntryList(pEntry); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTEMemFree((PVOID)pRcvEle); + + return; + + } + pEntry = pEntry->Flink; + + } + + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + return; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTReceiveDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles receiving a datagram by passing the datagram rcv + buffer to the non-OS specific code. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVEDG pTdiRequest; + TDI_REQUEST Request; + ULONG ReceivedLength; + tCLIENTELE *pClientEle; + + CTEPagedCode(); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("Nbt: Got a Receive datagram that NBT was NOT \n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + // get the sending information out of the irp + pTdiRequest = (PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters; + + Request.Handle.AddressHandle = pClientEle; + + status = NbtReceiveDatagram( + &Request, + pTdiRequest->ReceiveDatagramInformation, + pTdiRequest->ReturnDatagramInformation, + pTdiRequest->ReceiveLength, + &ReceivedLength, + (PVOID)pIrp->MdlAddress, // user data + (tDEVICECONTEXT *)pDeviceContext, + pIrp); + + if (status != STATUS_PENDING) + { + + NTIoComplete(pIrp,status,ReceivedLength); + + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTSend( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles sending session pdus across a connection. It is + all OS specific code. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + PTDI_REQUEST_KERNEL_SEND pTdiRequest; + PMDL pMdl; + PSINGLE_LIST_ENTRY pSingleListEntry; + tSESSIONHDR *pSessionHdr; + tCONNECTELE *pConnEle; + KIRQL OldIrq; + KIRQL OldIrq1; + PTDI_REQUEST_KERNEL_SEND pParams; + PFILE_OBJECT pFileObject; + tLOWERCONNECTION *pLowerConn; + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // get the sending information out of the irp + pTdiRequest = (PTDI_REQUEST_KERNEL_SEND)&pIrpSp->Parameters; + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + //ASSERT(pConnEle->Verify == NBT_VERIFY_CONNECTION); + + if (pConnEle) + { + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + // + // make sure lowerconn stays valid until the irp is done + // + CTESpinLock(pLowerConn,OldIrq1); + pLowerConn->RefCount++; + CTESpinFree(pLowerConn,OldIrq1); + } + else + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:attempting send when LowerConn has been freed!\n")); + + status = STATUS_INVALID_HANDLE; + + // to save on indent levels use a goto here + goto ErrorExit; + } + + CTESpinLock(pConnEle,OldIrq); + + // check the state of the connection + if (pConnEle->state == NBT_SESSION_UP) + { + // + // send the data on downward to tcp + // allocate an MDL to allow us to put the session hdr in first and then + // put the users buffer on after that, chained to the session hdr MDL. + // + CTESpinLockAtDpc(&NbtConfig); + + if (NbtConfig.SessionMdlFreeSingleList.Next) + { + pSingleListEntry = PopEntryList(&NbtConfig.SessionMdlFreeSingleList); + pMdl = CONTAINING_RECORD(pSingleListEntry,MDL,Next); + + ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) ); + + } + else + { + NbtGetMdl(&pMdl,eNBT_FREE_SESSION_MDLS); + + if (!pMdl) + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Unable to get an MDL for a session send!\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + CTESpinFreeAtDpc(&NbtConfig); + CTESpinFree(pConnEle,OldIrq); + + // to save on indent levels use a goto here + goto ErrorExit; + } + + } + + CTESpinFreeAtDpc(&NbtConfig); + + // get the session hdr address out of the MDL + pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl); + + // the type of PDU is always a session message, since the session + // request is sent when the client issues a "connect" rather than a send + // + pSessionHdr->UlongLength = htonl(pTdiRequest->SendLength); + + // get the device object and file object for the TCP transport underneath + // link the user buffer on the end of the session header Mdl on the Irp + // + pMdl->Next = pIrp->MdlAddress; + pIrp->MdlAddress = pMdl; + + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + pParams = (PTDI_REQUEST_KERNEL_SEND)&pIrpSp->Parameters; + pParams->SendFlags = pTdiRequest->SendFlags; + pParams->SendLength = pTdiRequest->SendLength + sizeof(tSESSIONHDR); + + + pIrpSp->CompletionRoutine = SendCompletion; + pIrpSp->Context = (PVOID)pLowerConn; + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_SEND; + + pFileObject = pLowerConn->pFileObject; + pLowerConn->BytesSent += pParams->SendLength; + + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + + CTESpinFree(pConnEle,OldIrq); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pFileObject),pIrp); + + return(status); + + }//correct state + else + { + CTESpinFree(pConnEle,OldIrq); + // + // Release pLowerConn->RefCount, grabbed above. + // + CTESpinLock(pLowerConn,OldIrq1); + pLowerConn->RefCount--; + CTESpinFree(pLowerConn,OldIrq1); + + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Invalid state for connection on an attempted send, %X\n", + pConnEle)); + status = STATUS_INVALID_HANDLE; + } + } + else // pConnEle + { + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:attempting send with NULL Connection element!\n")); + status = STATUS_INVALID_HANDLE; + } + + +ErrorExit: + + // + // Reset the Irp pending flag + // + pIrpSp->Control &= ~SL_PENDING_RETURNED; + // + // complete the irp, since there must have been some sort of error + // to get to here + // + NTIoComplete(pIrp,status,0); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +SendCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the completion event when the send completes with + the underlying transport. It must put the session hdr buffer back in + the correct free list and free the active q entry and put it back on + its free list. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + PMDL pMdl; + tLOWERCONNECTION *pLowerConn; + + // + // Do some checking to keep the Io system happy - propagate the pending + // bit up the irp stack frame.... if it was set by the driver below then + // it must be set by me + // + if (Irp->PendingReturned) + { + IoMarkIrpPending(Irp); + } + + // put the MDL we back on its free list and put the clients mdl back on the Irp + // as it was before the send + pMdl = Irp->MdlAddress; + Irp->MdlAddress = pMdl->Next; + + ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) ); + +#if DBG + IF_DBG(NBT_DEBUG_SEND) + { + PMDL pMdl1; + ULONG ulen1,ulen2,ulen3; + UCHAR uc; + tSESSIONHDR *pSessionHdr; + PSINGLE_LIST_ENTRY pSingleListEntry; + KIRQL OldIrq; + + pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl); + ulen1 = htonl ( pSessionHdr->UlongLength ); + + for ( ulen2 = 0 , pMdl1 = pMdl ; ( pMdl1 = pMdl1->Next ) != NULL ; ) { + ulen3 = MmGetMdlByteCount ( pMdl1 ); + ASSERT ( ulen3 > 0 ); + uc = ( ( UCHAR * ) MmGetMdlVirtualAddress ( pMdl1 ) ) [ ulen3 - 1 ]; + ulen2 += ulen3; + } + + ASSERT ( ulen2 == ulen1 ); + + CTESpinLock(&NbtConfig,OldIrq); + for ( pSingleListEntry = &NbtConfig.SessionMdlFreeSingleList ; + ( pSingleListEntry = pSingleListEntry->Next ) != NULL ; + ) + { + pMdl1 = CONTAINING_RECORD(pSingleListEntry,MDL,Next); + ASSERT ( pMdl1 != pMdl ); + } + CTESpinFree(&NbtConfig,OldIrq); + } +#endif // DBG + + ExInterlockedPushEntryList(&NbtConfig.SessionMdlFreeSingleList, + (PSINGLE_LIST_ENTRY)pMdl, + &NbtConfig.SpinLock); + + // fill in the sent size so that it substracts off the session header size + // + if (Irp->IoStatus.Information > sizeof(tSESSIONHDR)) + { + + Irp->IoStatus.Information -= sizeof(tSESSIONHDR); + } + else + { + // nothing was sent + Irp->IoStatus.Information = 0; + IF_DBG(NBT_DEBUG_SEND) + KdPrint(("Nbt:Zero Send Length for a session send!\n")); + } + + // + // we incremented this before the send: deref it now + // + pLowerConn = (tLOWERCONNECTION *)Context; +#if DBG + if (!pLowerConn || pLowerConn->Verify != NBT_VERIFY_LOWERCONN) + { + ASSERTMSG("Nbt: LowerConn is not valid!\n",0); + } +#endif + NbtDereferenceLowerConnection(pLowerConn); + + return(STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( DeviceObject ); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTSendDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles sending a datagram down to the transport. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + LONG lSentLength; + TDI_REQUEST Request; + PTDI_REQUEST_KERNEL_SENDDG pTdiRequest; + tCLIENTELE *pClientEle; + + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext; + + CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status); + + // get the sending information out of the irp + pTdiRequest = (PTDI_REQUEST_KERNEL_SENDDG)&pIrpSp->Parameters; + Request.Handle.AddressHandle = pClientEle; + + lSentLength = 0; + status = NbtSendDatagram( + &Request, + pTdiRequest->SendDatagramInformation, + pTdiRequest->SendLength, + &lSentLength, + (PVOID)pIrp->MdlAddress, // user data + (tDEVICECONTEXT *)pDeviceContext, + pIrp); + + + // + // either Success or an Error + // so complete the irp - PENDING is never returned!! + // + NTIoComplete(pIrp,status,lSentLength); + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTSetInformation( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles sets up event handlers that the client passes in. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + // *TODO* + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:************ Got a Set Information that was NOT expected *******\n")); + return(STATUS_SUCCESS); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTQueueToWorkerThread( + IN tDGRAM_SEND_TRACKING *pTracker, + IN PVOID pClientContext, + IN PVOID ClientCompletion, + IN PVOID CallBackRoutine, + IN PVOID pDeviceContext + ) +/*++ + +Routine Description: + + This routine simply queues a request on an excutive worker thread + for later execution. Scanning the LmHosts file must be down this way. + +Arguments: + pTracker - the tracker block for context + CallbackRoutine - the routine for the Workerthread to call + pDeviceContext - the device context which is this delayed event + pertains to. This could be NULL (meaning it's an event + pertaining to not any specific device context) + +Return Value: + + +--*/ + +{ + NTSTATUS status = STATUS_UNSUCCESSFUL ; + NBT_WORK_ITEM_CONTEXT *pContext; + + pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('e')); + if (pContext) + { + pContext->pTracker = pTracker; + pContext->pClientContext = pClientContext; + pContext->ClientCompletion = ClientCompletion; + + ExInitializeWorkItem(&pContext->Item,CallBackRoutine,pContext); + ExQueueWorkItem(&pContext->Item,DelayedWorkQueue); + status = STATUS_SUCCESS; + } + + return(status); + +} +//---------------------------------------------------------------------------- +VOID +SecurityDelete( + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles deleting a security context at non-dpc level. + +Arguments: + + +Return Value: + + none + +--*/ +{ + PSECURITY_CLIENT_CONTEXT pClientSecurity; + + pClientSecurity = (PSECURITY_CLIENT_CONTEXT)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; + SeDeleteClientSecurity(pClientSecurity); + CTEMemFree(pContext); +} + +//---------------------------------------------------------------------------- +VOID +NTSendSession( + IN tDGRAM_SEND_TRACKING *pTracker, + IN tLOWERCONNECTION *pLowerConn, + IN PVOID pCompletion) + +/*++ +Routine Description: + + This Routine handles seting up a DPC to send a session pdu so that the stack + does not get wound up in multiple sends for the keep alive timeout case. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ +{ + PKDPC pDpc; + + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('f')); + if (!pDpc) + { + return; + } + KeInitializeDpc(pDpc, + DpcSendSession, + (PVOID)pTracker); + + KeInsertQueueDpc(pDpc,(PVOID)pLowerConn,pCompletion); +} + +//---------------------------------------------------------------------------- +VOID +DpcSendSession( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls TcpSendSession from a Dpc started in + in NTSendSession (above). + +Arguments: + + +Return Value: + + +--*/ + +{ + CTEMemFree((PVOID)pDpc); + + + TcpSendSession((tDGRAM_SEND_TRACKING *)Context, + (tLOWERCONNECTION *)SystemArgument1, + (PVOID)SystemArgument2); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NTSetEventHandler( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tCLIENTELE *pClientEle; + PTDI_REQUEST_KERNEL_SET_EVENT pKeSetEvent; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientEle = pIrpSp->FileObject->FsContext; + pKeSetEvent = (PTDI_REQUEST_KERNEL_SET_EVENT)&pIrpSp->Parameters; + + // call the not NT specific routine to setup the event handler in the + // nbt data structures + status = NbtSetEventHandler( + pClientEle, + pKeSetEvent->EventType, + pKeSetEvent->EventHandler, + pKeSetEvent->EventContext); + + return(status); + +} + +//---------------------------------------------------------------------------- + +VOID +NTIoComplete( + IN PIRP pIrp, + IN NTSTATUS Status, + IN ULONG SentLength) + +/*++ +Routine Description: + + This Routine handles calling the NT I/O system to complete an I/O. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + KIRQL OldIrq; + +#if DBG + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt: NTIoComplete error return status = %X\n",Status)); +// ASSERTMSG("Nbt: Error Ret Code In IoComplete",0); + } +#endif + + pIrp->IoStatus.Status = Status; + + // use -1 as a flag to mean do not adjust the sent length since it is + // already set + if (SentLength != -1) + { + pIrp->IoStatus.Information = SentLength; + } + +#if DBG + if ( (Status != STATUS_SUCCESS) && + (Status != STATUS_PENDING) && + (Status != STATUS_INVALID_DEVICE_REQUEST) && + (Status != STATUS_INVALID_PARAMETER) && + (Status != STATUS_IO_TIMEOUT) && + (Status != STATUS_BUFFER_OVERFLOW) && + (Status != STATUS_BUFFER_TOO_SMALL) && + (Status != STATUS_INVALID_HANDLE) && + (Status != STATUS_INSUFFICIENT_RESOURCES) && + (Status != STATUS_CANCELLED) && + (Status != STATUS_DUPLICATE_NAME) && + (Status != STATUS_TOO_MANY_NAMES) && + (Status != STATUS_TOO_MANY_SESSIONS) && + (Status != STATUS_REMOTE_NOT_LISTENING) && + (Status != STATUS_BAD_NETWORK_PATH) && + (Status != STATUS_HOST_UNREACHABLE) && + (Status != STATUS_CONNECTION_REFUSED) && + (Status != STATUS_WORKING_SET_QUOTA) && + (Status != STATUS_REMOTE_DISCONNECT) && + (Status != STATUS_LOCAL_DISCONNECT) && + (Status != STATUS_LINK_FAILED) && + (Status != STATUS_SHARING_VIOLATION) && + (Status != STATUS_UNSUCCESSFUL) && + (Status != STATUS_ACCESS_VIOLATION) && + (Status != STATUS_NONEXISTENT_EA_ENTRY) ) + { + KdPrint(("Nbt: returning unusual status = %X\n",Status)); + } +#endif + + // set the Irps cancel routine to null or the system may bugcheck + // with a bug code of CANCEL_STATE_IN_COMPLETED_IRP + // + // refer to IoCancelIrp() ..\ntos\io\iosubs.c + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); +} +//---------------------------------------------------------------------------- + +NTSTATUS +NTGetIrpIfNotCancelled( + IN PIRP pIrp, + IN PIRP *ppIrpInStruct + ) +/*++ +Routine Description: + + This Routine gets the IOCancelSpinLock to coordinate with cancelling + irps It then returns STATUS_SUCCESS. It also nulls the irp in the structure + pointed to by the second parameter - so that the irp cancel routine + will not also be called. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + KIRQL OldIrq; + NTSTATUS status; + + IoAcquireCancelSpinLock(&OldIrq); + + // this nulls the irp in the datastructure - i.e. pConnEle->pIrp = NULL + *ppIrpInStruct = NULL; + + if (!pIrp->Cancel) + { + status = STATUS_SUCCESS; + } + else + { + status = STATUS_UNSUCCESSFUL; + } + IoSetCancelRoutine(pIrp,NULL); + + IoReleaseCancelSpinLock(OldIrq); + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTCheckSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine sets the cancel routine for an Irp. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + if (pIrp->Cancel) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + status = STATUS_CANCELLED; + + } + else + { + // setup the cancel routine + IoMarkIrpPending(pIrp); + IoSetCancelRoutine(pIrp,CancelRoutine); + status = STATUS_SUCCESS; + } + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + return(status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NTSetCancelRoutine( + IN PIRP pIrp, + IN PVOID CancelRoutine, + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine sets the cancel routine for an Irp. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + if (pIrp->Cancel) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + status = STATUS_CANCELLED; + + // + // Note the cancel spin lock is released by the Cancel routine + // + + (*(PDRIVER_CANCEL)CancelRoutine)((PDEVICE_OBJECT)pDeviceContext,pIrp); + + } + else + { + // setup the cancel routine and mark the irp pending + // + IoMarkIrpPending(pIrp); + IoSetCancelRoutine(pIrp,CancelRoutine); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + status = STATUS_SUCCESS; + } + return(status); + +} + +//---------------------------------------------------------------------------- +VOID +NTClearContextCancel( + IN NBT_WORK_ITEM_CONTEXT *pContext + ) +/*++ +Routine Description: + + This Routine sets the cancel routine for + ((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp + to NULL. + + NbtConfig.JointLock should be held when this routine is called. + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ +{ + NTSTATUS status; + status = NTCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp ); + ASSERT ( status != STATUS_CANCELLED ); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCancelCancelRoutine( + IN PIRP pIrp + ) + +/*++ +Routine Description: + + This Routine sets the cancel routine for an Irp to NULL + +Arguments: + + status - a completion status for the Irp + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + + if ( pIrp ) + { + // + // Check if the irp was cancelled yet and if not, then set the + // irp cancel routine. + // + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + + if (pIrp->Cancel) + { + status = STATUS_CANCELLED; + } + IoSetCancelRoutine(pIrp,NULL); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + } + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +FindNameCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a FindName Irp - which has + been passed down by a client (e.g. ping). Typically, when ping succeeds + on another adapter, it will issue this cancel. + On receiving the cancel, we stop any timer that is running in connection + with name query and then complete the irp with status_cancelled. + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tDGRAM_SEND_TRACKING *pTracker; + PIO_STACK_LOCATION pIrpSp; + + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got a FindName Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pTracker = pIrpSp->Parameters.Others.Argument4; + + // + // We want to ensure that the tracker supplied by FsContext + // is the right Tracker for this Irp + // + if (pTracker && (pIrp == pTracker->pClientIrp)) + { + // + // if pClientIrp still valid, completion routine hasn't run yet: go ahead + // and complete the irp here + // + pIrpSp->Parameters.Others.Argument4 = NULL; + pTracker->pClientIrp = NULL; + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + NTIoComplete(pIrp,STATUS_CANCELLED,(ULONG)-1); + + } else + { + // + // the completion routine has run. + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + } + + return; +} + diff --git a/private/ntos/nbt/nt/ntpnp.c b/private/ntos/nbt/nt/ntpnp.c new file mode 100644 index 000000000..20dbc90b5 --- /dev/null +++ b/private/ntos/nbt/nt/ntpnp.c @@ -0,0 +1,682 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + NTPNP.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for the + NBT Transport and other routines that are specific to the NT implementation + of a driver. + +Author: + + Earle R. Horton (earleh) 08-Nov-1995 + +Revision History: + +--*/ + + +#include "nbtprocs.h" + +#ifdef _PNP_POWER + +NTSTATUS +NbtNtPNPInit( + VOID + ) +/*++ + +Routine Description: + + Some general driver initialization that we postpone until we have + an actual IP address to which to bind. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operations. + +--*/ + +{ + NTSTATUS status; + + // start some timers + status = InitTimersNotOs(); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_TIMERS,status); + KdPrint(("NBT:Failed to Initialize the Timers!,status = %X\n", + status)); + StopInitTimers(); + return(status); + } + +} + +VOID +NbtFailedNtPNPInit( + VOID + ) +/*++ + +Routine Description: + + Undo some general driver initialization that we postpone until we have + an actual IP address to which to bind. Called after NbtAddressAdd() + failed to add the desired address, and no other addresses had been + added previously. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operations. + +--*/ + +{ + StopInitTimers(); +} + +NTSTATUS +NbtAddressAdd( + ULONG IpAddr, + PUNICODE_STRING pucBindString, + PUNICODE_STRING pucExportString, + PULONG Inst + ) +{ + NTSTATUS status = STATUS_SUCCESS; + NTSTATUS dontcarestatus; + tDEVICES *pBindDevices = NULL; + tDEVICES *pExportDevices = NULL; + tDEVICES BindDevices; + tDEVICES ExportDevices; + tADDRARRAY *pAddrArray = NULL; + + ULONG ulIpAddress; + ULONG ulSubnetMask; + + int i; + + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + tDEVICECONTEXT *pDeviceContext = NULL; + + BOOLEAN Attached; + + if (NbtConfig.uDevicesStarted == 0) + { + status = NbtNtPNPInit(); + if ( status != STATUS_SUCCESS ) { + KdPrint(("NetBT!NbtAddressAdd: Global driver initialization failed,\nfailing to add address %d%d%d%d.\n",IpAddr&0xFF,(IpAddr>>8)&0xFF,(IpAddr>>16)&0xFF,(IpAddr>>24)&0xFF)); + return status; + } + } + + if (IpAddr) { + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + // + // Find the bind and export devices to use from the device + // described in the registry that uses this address. + // + status = NbtReadRegistry( + &NbtConfig.pRegistry, + NULL, // Null Driver Object + &NbtConfig, + &pBindDevices, + &pExportDevices, + &pAddrArray); + + CTEExReleaseResource(&NbtConfig.Resource); + } else { + status = STATUS_SUCCESS; + ASSERT(pucBindString); + ASSERT(pucExportString); + + // + // Init the bind/export structs + // + BindDevices.RegistrySpace = pucBindString->Buffer; + BindDevices.Names[0] = *pucBindString; + + ExportDevices.RegistrySpace = pucExportString->Buffer; + ExportDevices.Names[0] = *pucExportString; + + pBindDevices = &BindDevices; + pExportDevices = &ExportDevices; + } + + if ( + ( status == STATUS_SUCCESS ) + && ( pBindDevices != NULL ) + ) + { + + for (i=0; i<pNbtGlobConfig->uNumDevices; i++ ) + { + BOOLEAN fStatic = TRUE; + + // + // Fetch a static IP address from the registry. + // + status = GetIPFromRegistry( + &NbtConfig.pRegistry, + &pBindDevices->Names[i], + &ulIpAddress, + &ulSubnetMask, + FALSE); + if ( status == STATUS_INVALID_ADDRESS ) + { + fStatic = FALSE; + + // + // This one doesn't have a valid static address. Try DHCP. + // + status = GetIPFromRegistry( + &NbtConfig.pRegistry, + &pBindDevices->Names[i], + &ulIpAddress, + &ulSubnetMask, + TRUE); + } + + if (((status == STATUS_SUCCESS) && ( ulIpAddress == IpAddr )) || + !IpAddr) + { + pDeviceContext = NbtFindBindName ( &pBindDevices->Names[i] ); + + if ( pDeviceContext != NULL ) + { + // + // Device already exists. Do something sensible with it. + // + + // + // For static addresses, open the addresses with the transports; add the permanent name + // + if (fStatic) { + +#ifdef _PNP_POWER + // + // these are passed into here in the reverse byte order, wrt to the IOCTL + // from DHCP. + // + (VOID)NbtNewDhcpAddress(pDeviceContext,htonl(ulIpAddress),htonl(ulSubnetMask)); +#endif // NOTYET_PNP + // + // Add the "permanent" name to the local name table. This is the IP + // address of the node padded out to 16 bytes with zeros. + // + status = NbtAddPermanentName(pDeviceContext); + } + + // + // If the device was not registered with TDI, do so now. + // + + // + // By-pass the TDI PnP mechanism for logical interfaces + // + if (IpAddr) { + if (pDeviceContext->RegistrationHandle == NULL) { + // pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + + status = TdiRegisterDeviceObject( + &pExportDevices->Names[i], + &pDeviceContext->RegistrationHandle); + + if (!NT_SUCCESS(status)) { + KdPrint(("Couldn't register device object\n")); + } + } + } else { + pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + } + } + else + { + BOOLEAN Attached = FALSE; + + // + // Attach to the system process so that all handles are created in the + // proper context. + // + CTEAttachFsp(&Attached); + + status = NbtCreateDeviceObject( + NbtConfig.DriverObject, + &NbtConfig, + &pBindDevices->Names[i], + &pExportDevices->Names[i], + &pAddrArray[i], + &NbtConfig.pRegistry, + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + (BOOLEAN)(IpAddr == 0), +#endif + &pDeviceContext); + + // + // allow not having an address to succeed - DHCP will + // provide an address later + // + if (pDeviceContext != NULL) + { + if ((status == STATUS_INVALID_ADDRESS) || + (!IpAddr && (status == STATUS_UNSUCCESSFUL))) + { + // + // set to null so we know not to allow connections or dgram + // sends on this adapter + // + pDeviceContext->IpAddress = 0; + + status = STATUS_SUCCESS; + + } + // + // Get an Irp for the out of resource queue (used to disconnect sessions + // when really low on memory) + // + if ( NT_SUCCESS(status) && !NbtConfig.OutOfRsrc.pIrp ) + { + pEntry = NbtConfig.DeviceContexts.Flink; + + ASSERT (pDeviceContext == CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage)); + + NbtConfig.OutOfRsrc.pIrp = NTAllocateNbtIrp(&pDeviceContext->DeviceObject); + + if (!NbtConfig.OutOfRsrc.pIrp) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // + // allocate a dpc structure and keep it: we might need if we hit an + // out-of-resource condition + // + NbtConfig.OutOfRsrc.pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('a')); + if (!NbtConfig.OutOfRsrc.pDpc) + { + IoFreeIrp(NbtConfig.OutOfRsrc.pIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + if (NT_SUCCESS(status)) + { + NbtConfig.uDevicesStarted++; + pDeviceContext->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; + + // + // By-pass the TDI PnP mechanism for logical interfaces + // + if (IpAddr) { + status = TdiRegisterDeviceObject( + &pExportDevices->Names[i], + &pDeviceContext->RegistrationHandle); + } + } + // + // Clean up code if device created but we could not use it + // for some reason. + // + if (!NT_SUCCESS(status)) + { + + pDeviceContext->RegistrationHandle = NULL; + + KdPrint((" Create Device Object Failed with status= %X, num devices = %X\n",status, + NbtConfig.uNumDevices)); + + NbtLogEvent(EVENT_NBT_CREATE_DEVICE,status); + // + // this device will not be started so decrement the count of started + // ones. + // + NbtConfig.AdapterCount--; + + pHead = &NbtConfig.DeviceContexts; + pEntry = RemoveTailList(pHead); + + ASSERT (pDeviceContext == CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage)); + + if (pDeviceContext->hNameServer) + { + ObDereferenceObject(pDeviceContext->pNameServerFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hNameServer); + KdPrint(("Close NameSrv File status = %X\n",dontcarestatus)); + } + if (pDeviceContext->hDgram) + { + ObDereferenceObject(pDeviceContext->pDgramFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hDgram); + KdPrint(("Close Dgram File status = %X\n",dontcarestatus)); + } + if (pDeviceContext->hSession) + { + ObDereferenceObject(pDeviceContext->pSessionFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hSession); + KdPrint(("Close Session File status = %X\n",dontcarestatus)); + } + if (pDeviceContext->hControl) + { + ObDereferenceObject(pDeviceContext->pControlFileObject); + dontcarestatus = NTZwCloseFile(pDeviceContext->hControl); + KdPrint(("Close Control File status = %X\n",dontcarestatus)); + } + + IoDeleteDevice((PDEVICE_OBJECT)pDeviceContext); + } + } + + CTEDetachFsp(Attached); + } + break; + } // ( (status == STATUS_SUCCESS) && ( ulIpAddress == IpAddr ) ) + } + } + + + if (NbtConfig.uDevicesStarted == 0) + { + NbtFailedNtPNPInit(); + } + + if (IpAddr) { + if (pBindDevices) + { + CTEMemFree((PVOID)pBindDevices->RegistrySpace); + CTEMemFree((PVOID)pBindDevices); + } + if (pExportDevices) + { + CTEMemFree((PVOID)pExportDevices->RegistrySpace); + CTEMemFree((PVOID)pExportDevices); + } + if (pAddrArray) + { + CTEMemFree((PVOID)pAddrArray); + } + } else { + if (pDeviceContext) { + *Inst = pDeviceContext->InstanceNumber; +#if DBG + pDeviceContext->IsDynamic = TRUE; +#endif + } + } + + return status; +} + +tDEVICECONTEXT * +NbtFindIPAddress( + ULONG IpAddr + ) +{ + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + tDEVICECONTEXT *pDeviceContext; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + if ( pDeviceContext->IpAddress == IpAddr ) + { + return pDeviceContext; + } + } + return (tDEVICECONTEXT *)NULL; +} + +tDEVICECONTEXT * +NbtFindBindName( + PUNICODE_STRING pucBindName + ) +{ + + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + + tDEVICECONTEXT *pDeviceContext; + + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pDeviceContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + + if ( RtlCompareUnicodeString( + pucBindName, + &pDeviceContext->BindName, + FALSE ) + == 0 ) + { + return pDeviceContext; + } + } + return (tDEVICECONTEXT *)NULL; +} + +VOID +NbtAddressDelete( + ULONG IpAddr + ) +{ + tDEVICECONTEXT *pDeviceContext; + + if ( ( pDeviceContext = NbtFindIPAddress ( IpAddr ) ) != NULL ) + { + (VOID)NbtNewDhcpAddress(pDeviceContext,0,0); + } +} + +HANDLE AddressChangeHandle; + +#ifdef WATCHBIND +HANDLE BindingHandle; +#endif // WATCHBIND + +//* AddressArrival - Handle an IP address arriving +// +// Called by TDI when an address arrives. +// +// Input: Addr - IP address that's coming. +// +// Returns: Nothing. +// +VOID +AddressArrival(PTA_ADDRESS Addr) +{ + ULONG IpAddr; + if (Addr->AddressType == TDI_ADDRESS_TYPE_IP){ + IpAddr = ntohl(((PTDI_ADDRESS_IP)&Addr->Address[0])->in_addr); + IF_DBG(NBT_DEBUG_PNP_POWER){ + KdPrint(("NetBT!AddressArrival: %d.%d.%d.%d\n",(IpAddr>>24)&0xFF,(IpAddr>>16)&0xFF,(IpAddr>>8)&0xFF,IpAddr&0xFF)); + } + if (IpAddr) + { + (VOID)NbtAddressAdd(IpAddr, NULL, NULL, NULL); + } + } +} + +//* AddressDeletion - Handle an IP address going away. +// +// Called by TDI when an address is deleted. If it's an address we +// care about we'll clean up appropriately. +// +// Input: Addr - IP address that's going. +// +// Returns: Nothing. +// +VOID +AddressDeletion(PTA_ADDRESS Addr) +{ + ULONG IpAddr; + if (Addr->AddressType == TDI_ADDRESS_TYPE_IP){ + IpAddr = ntohl(((PTDI_ADDRESS_IP)&Addr->Address[0])->in_addr); + IF_DBG(NBT_DEBUG_PNP_POWER){ + KdPrint(("NetBT!AddressDeletion: %d.%d.%d.%d\n",(IpAddr>>24)&0xFF,(IpAddr>>16)&0xFF,(IpAddr>>8)&0xFF,IpAddr&0xFF)); + } + if (IpAddr) + { + NbtAddressDelete(IpAddr); + } + } +} + +NTSTATUS +NbtAddNewInterface ( + IN PIRP pIrp, + IN PVOID *pBuffer, + IN ULONG Size + ) +/*++ + +Routine Description: + + Creates a device context by coming up with a unique export string to name + the device. + +Arguments: + +Return Value: + +Notes: + + +--*/ +{ + ULONG nextIndex = InterlockedIncrement(&NbtConfig.InterfaceIndex); + WCHAR Suffix[16]; + WCHAR Bind[60] = L"\\Device\\If"; + WCHAR Export[60] = L"\\Device\\NetBt_If"; + UNICODE_STRING ucSuffix; + UNICODE_STRING ucBindStr; + UNICODE_STRING ucExportStr; + NTSTATUS status; + ULONG OutSize; + ULONG Inst=0; + PNETBT_ADD_DEL_IF pAddDelIf = (PNETBT_ADD_DEL_IF)pBuffer; + + // + // Validate output buffer size + // + if (Size < sizeof(NETBT_ADD_DEL_IF)) { + KdPrint(("NbtAddNewInterface: Output buffer too small for struct\n")); + return(STATUS_INVALID_PARAMETER); + } + // + // Create the bind/export strings as: + // Bind: \Device\IF<1> Export: \Device\NetBt_IF<1> + // where 1 is a unique interface index. + // + ucSuffix.Buffer = Suffix; + ucSuffix.Length = 0; + ucSuffix.MaximumLength = sizeof(Suffix); + + RtlIntegerToUnicodeString(nextIndex, 10, &ucSuffix); + + RtlInitUnicodeString(&ucBindStr, Bind); + ucBindStr.MaximumLength = sizeof(Bind); + RtlInitUnicodeString(&ucExportStr, Export); + ucExportStr.MaximumLength = sizeof(Export); + + RtlAppendUnicodeStringToString(&ucBindStr, &ucSuffix); + RtlAppendUnicodeStringToString(&ucExportStr, &ucSuffix); + + OutSize = FIELD_OFFSET (NETBT_ADD_DEL_IF, IfName[0]) + + ucExportStr.Length + sizeof(UNICODE_NULL); + + if (Size < OutSize) { + KdPrint(("NbtAddNewInterface: Buffer too small for name\n")); + pAddDelIf->Length = ucExportStr.Length + sizeof(UNICODE_NULL); + pAddDelIf->Status = STATUS_BUFFER_TOO_SMALL; + pIrp->IoStatus.Information = sizeof(NETBT_ADD_DEL_IF); + return STATUS_SUCCESS; + } + + KdPrint(( + "Created: ucBindStr: %ws ucExportStr: %ws\n", + ucBindStr.Buffer, + ucExportStr.Buffer + )); + + status = NbtAddressAdd(0, &ucBindStr, &ucExportStr, &Inst); + + if (status == STATUS_SUCCESS) { + // + // Fill up the output buffer with the export name + // + RtlCopyMemory( + &pAddDelIf->IfName[0], + ucExportStr.Buffer, + ucExportStr.Length + sizeof(UNICODE_NULL) + ); + + pAddDelIf->InstanceNumber = Inst; + pAddDelIf->Length = ucExportStr.Length + sizeof(UNICODE_NULL); + pAddDelIf->Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = OutSize; + } + + return status; +} + +#ifdef WATCHBIND + +//* BindHandler - Handle a new transport device object. +// +// Called by TDI when a new transport device object is created. +// +// Input: DeviceName - Name of the new device object. +// +// Returns: Nothing. +// + +VOID +BindHandler(IN PUNICODE_STRING DeviceName) +{ +} + +//* UnbindHandler - Handle deletion of a transport device object. +// +// Called by TDI when a transport device object is deleted. +// +// Input: DeviceName - Name of the deleted device object. +// +// Returns: Nothing. +// + +VOID +UnbindHandler(IN PUNICODE_STRING DeviceName) +{ +} + +#endif // WATCHBIND + +#endif // _PNP_POWER diff --git a/private/ntos/nbt/nt/ntutil.c b/private/ntos/nbt/nt/ntutil.c new file mode 100644 index 000000000..4a5f200b8 --- /dev/null +++ b/private/ntos/nbt/nt/ntutil.c @@ -0,0 +1,2103 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Ntutil.c + +Abstract: + + This file contains a number of utility and support routines that are + NT specific. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "stdio.h" +#include <ntddtcp.h> +#undef uint // undef to avoid a warning where tdiinfo.h redefines it +#include <tdiinfo.h> +#include <ipinfo.h> + +NTSTATUS +CreateControlObject( + tNBTCONFIG *pConfig); + +NTSTATUS +IfNotAnyLowerConnections( + IN tDEVICECONTEXT *pDeviceContext + ); +NTSTATUS +NbtProcessDhcpRequest( + tDEVICECONTEXT *pDeviceContext); +VOID +GetExtendedAttributes( + tDEVICECONTEXT *pDeviceContext + ); + +PSTRM_PROCESSOR_LOG LogAlloc ; +PSTRM_PROCESSOR_LOG LogFree ; + + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#ifdef _PNP_POWER +#pragma CTEMakePageable(PAGE, NbtCreateDeviceObject) +#else // _PNP_POWER +#pragma CTEMakePageable(INIT, NbtCreateDeviceObject) +#endif // _PNP_POWER +#pragma CTEMakePageable(PAGE, CreateControlObject) +#pragma CTEMakePageable(PAGE, NbtInitConnQ) +#pragma CTEMakePageable(PAGE, NbtProcessDhcpRequest) +#pragma CTEMakePageable(PAGE, NbtCreateAddressObjects) +#pragma CTEMakePageable(PAGE, GetExtendedAttributes) +#pragma CTEMakePageable(PAGE, ConvertToUlong) +#pragma CTEMakePageable(PAGE, NbtInitMdlQ) +#pragma CTEMakePageable(PAGE, NTZwCloseFile) +#pragma CTEMakePageable(PAGE, NTReReadRegistry) +#pragma CTEMakePageable(PAGE, SaveClientSecurity) +#endif +//******************* Pageable Routine Declarations **************** + +ulong +GetUnique32BitValue( + void + ) + +/*++ + +Routine Description: + + Returns a reasonably unique 32-bit number based on the system clock. + In NT, we take the current system time, convert it to milliseconds, + and return the low 32 bits. + +Arguments: + + None. + +Return Value: + + A reasonably unique 32-bit value. + +--*/ + +{ + LARGE_INTEGER ntTime, tmpTime; + + KeQuerySystemTime(&ntTime); + + tmpTime = CTEConvert100nsToMilliseconds(ntTime); + + return(tmpTime.LowPart); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtCreateDeviceObject( + PDRIVER_OBJECT DriverObject, + tNBTCONFIG *pConfig, + PUNICODE_STRING pucBindName, + PUNICODE_STRING pucExportName, + tADDRARRAY *pAddrs, + PUNICODE_STRING pucRegistryPath, +#ifndef _IO_DELETE_DEVICE_SUPPORTED + BOOLEAN fReuse, +#endif + tDEVICECONTEXT **ppDeviceContext + ) + +/*++ + +Routine Description: + + This routine initializes a Driver Object from the device object passed + in and the name of the driver object passed in. After the Driver Object + has been created, clients can "Open" the driver by that name. + +Arguments: + + +Return Value: + + status - the outcome + +--*/ + +{ + + NTSTATUS status; + PDEVICE_OBJECT DeviceObject = NULL; + tDEVICECONTEXT *pDeviceContext; + ULONG LinkOffset; + ULONG ulIpAddress; + ULONG ulSubnetMask; +#ifdef _PNP_POWER + PUCHAR Buffer; +#endif // _PNP_POWER +#ifndef _IO_DELETE_DEVICE_SUPPORTED + BOOLEAN fIsItReused=FALSE; +#endif + CTELockHandle OldIrq1; + + CTEPagedCode(); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // + // If we can re-use some of the earlier devices, so be it.... + // This will go away when base supports IoDeleteDevice properly. + // + if (fReuse) { + LIST_ENTRY *pEntry; + + CTESpinLock(&NbtConfig, OldIrq1); + if (!IsListEmpty(&NbtConfig.FreeDevCtx)) { + pEntry = RemoveHeadList( &NbtConfig.FreeDevCtx); + + DeviceObject = (PDEVICE_OBJECT) CONTAINING_RECORD( pEntry, + tDEVICECONTEXT, + FreeLinkage); + + KdPrint(("Re-using device @ %lx, bind: %ws\n", DeviceObject, ((tDEVICECONTEXT *)DeviceObject)->BindName.Buffer)); + + // + // Re-use the name and update the value returned to the user - we shd decrement + // the global ptr too, but we might hit some other (valid) device the next time on. + // + pucBindName->MaximumLength = ((tDEVICECONTEXT *)DeviceObject)->BindName.MaximumLength; + RtlCopyUnicodeString(pucBindName, &((tDEVICECONTEXT *)DeviceObject)->BindName); + + pucExportName->MaximumLength = ((tDEVICECONTEXT *)DeviceObject)->ExportName.MaximumLength; + RtlCopyUnicodeString(pucExportName, &((tDEVICECONTEXT *)DeviceObject)->ExportName); + CTEMemFree( pDeviceContext->ExportName.Buffer ); + + CTESpinFree(&NbtConfig, OldIrq1); + + fIsItReused = TRUE; +#ifdef _PNP_POWER + Buffer = NbtAllocMem(pucExportName->MaximumLength+pucBindName->MaximumLength,NBT_TAG('w')); + + if ( Buffer == NULL ) + { + return STATUS_INSUFFICIENT_RESOURCES ; + } +#endif // _PNP_POWER + goto Reuse; + } + + fIsItReused = FALSE; + CTESpinFree(&NbtConfig, OldIrq1); + } +#endif + +#ifdef _PNP_POWER + Buffer = NbtAllocMem(pucExportName->MaximumLength+pucBindName->MaximumLength,NBT_TAG('w')); + + if ( Buffer == NULL ) + { + return STATUS_INSUFFICIENT_RESOURCES ; + } +#endif // _PNP_POWER + + + status = IoCreateDevice( + DriverObject, // Driver Object + sizeof(tDEVICECONTEXT) - sizeof(DRIVER_OBJECT), //Device Extension + pucExportName, // Device Name + FILE_DEVICE_NBT, // Device type 0x32 for now... + 0, //Device Characteristics + FALSE, //Exclusive + &DeviceObject ); + + if (!NT_SUCCESS( status )) + { + KdPrint(("Failed to create the Export Device, status=%X\n",status)); + *ppDeviceContext = NULL; +#ifdef _PNP_POWER + CTEMemFree ( Buffer ); +#endif // _PNP_POWER + return status; + } + +#ifndef _IO_DELETE_DEVICE_SUPPORTED +Reuse: +#endif + + *ppDeviceContext = pDeviceContext = (tDEVICECONTEXT *)DeviceObject; + + // + // zero out the data structure, beyond the OS specific part + // + LinkOffset = (ULONG)(&((tDEVICECONTEXT *)0)->Linkage); + CTEZeroMemory(&pDeviceContext->Linkage,sizeof(tDEVICECONTEXT) - LinkOffset); + +#ifdef _PNP_POWER + pDeviceContext->ExportName.MaximumLength = pucExportName->MaximumLength; + pDeviceContext->ExportName.Buffer = (PWSTR)Buffer; + + RtlCopyUnicodeString(&pDeviceContext->ExportName,pucExportName); + + pDeviceContext->BindName.MaximumLength = pucBindName->MaximumLength; + pDeviceContext->BindName.Buffer = (PWSTR)(Buffer+pucExportName->MaximumLength); + RtlCopyUnicodeString(&pDeviceContext->BindName,pucBindName); +#endif // _PNP_POWER + + // initialize the pDeviceContext data structure. There is one of + // these data structured tied to each "device" that NBT exports + // to higher layers (i.e. one for each network adapter that it + // binds to. + // The initialization sets the forward link equal to the back link equal + // to the list head + InitializeListHead(&pDeviceContext->UpConnectionInUse); + InitializeListHead(&pDeviceContext->LowerConnection); + InitializeListHead(&pDeviceContext->LowerConnFreeHead); + + // put a verifier value into the structure so that we can check that + // we are operating on the right data when the OS passes a device context + // to NBT + pDeviceContext->Verify = NBT_VERIFY_DEVCONTEXT; + + // setup the spin lock); + CTEInitLock(&pDeviceContext->SpinLock); + + pDeviceContext->LockNumber = DEVICE_LOCK; + // + // for a Bnode pAddrs is NULL + // + if (pAddrs) + { + pDeviceContext->lNameServerAddress = pAddrs->NameServerAddress; + pDeviceContext->lBackupServer = pAddrs->BackupServer; + // + // if the node type is set to Bnode by default then switch to Hnode if + // there are any WINS servers configured. + // + if ((NodeType & DEFAULT_NODE_TYPE) && + (pAddrs->NameServerAddress || pAddrs->BackupServer)) + { + NodeType = MSNODE | (NodeType & PROXY_NODE); + } + } + + // + // We need to acquire this lock since we can have multiple devices + // being added simultaneously and hence we will need to have a unique + // Adapter Number for each device + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + // keep a bit mask around to keep track of this adapter number so we can + // quickly find if a given name is registered on a particular adapter, + // by a corresponding bit set in the tNAMEADDR - local hash table + // entry + // + pDeviceContext->AdapterNumber = (CTEULONGLONG)1 << NbtConfig.AdapterCount; + NbtConfig.AdapterCount++; + + // add this new device context on to the List in the configuration + // data structure + InsertTailList(&pConfig->DeviceContexts,&pDeviceContext->Linkage); + + if (NbtConfig.AdapterCount > 1) + { + NbtConfig.MultiHomed = TRUE; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // increase the stack size of our device object, over that of the transport + // so that clients create Irps large enough + // to pass on to the transport below. + // In theory, we should just add 1 here, to account for out presence in the + // driver chain. + + status = NbtTdiOpenControl(pDeviceContext); + if (NT_SUCCESS(status)) + { + DeviceObject->StackSize = pDeviceContext->pControlDeviceObject->StackSize + 1; + } + else + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt!NbtTdiOpenControl returned status=%X\n",status)); + return(status); + } + + + // + // An instance number is assigned to each device so that the service which + // creates logical devices in Nbt can re-use these devices in case it fails + // to destroy them in a prev. instance. + // + pDeviceContext->InstanceNumber = GetUnique32BitValue(); + + // + // To create the address objects for this device we need an address for + // TCP port 139 (session services, UDP Port 138 (datagram services) + // and UDP Port 137 (name services). The IP addresses to use for these + // port number must be found by "groveling" the registry..i.e. looking + // under each adapter in the registry for a /parameters/tcpip section + // and then pulling the IP address out of that + // + status = GetIPFromRegistry( + pucRegistryPath, + pucBindName, + &ulIpAddress, + &ulSubnetMask, + FALSE); + +#ifdef _PNP_POWER +#ifdef NOTYET_PNP + if ( status == STATUS_INVALID_ADDRESS ) + { + // + // This one doesn't have a valid static address. Try DHCP. + // + status = GetIPFromRegistry( + pucRegistryPath, + pucBindName, + &ulIpAddress, + &ulSubnetMask, + TRUE); + } +#endif NOTYET_PNP +#endif // _PNP_POWER + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt!GetIPFromRegistry returned status=%X\n",status)); + return(status); + } + +#ifdef _PNP_POWER + + // + // Now, we create all devices up-front (in driverentry); so no need to open the addresses etc. + // + return(status); +#endif + + // get the ip address out of the registry and open the required address + // objects with the underlying transport provider + status = NbtCreateAddressObjects( + ulIpAddress, + ulSubnetMask, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + NbtLogEvent(EVENT_NBT_CREATE_ADDRESS,status); + + KdPrint(("Failed to create the Address Object, status=%X\n",status)); + + return(status); + } + + // + // Add the "permanent" name to the local name table. This is the IP + // address of the node padded out to 16 bytes with zeros. + // + status = NbtAddPermanentName(pDeviceContext); + + // this call must converse with the transport underneath to create + // connections and associate them with the session address object + status = NbtInitConnQ( + &pDeviceContext->LowerConnFreeHead, + sizeof(tLOWERCONNECTION), + NBT_NUM_INITIAL_CONNECTIONS, + pDeviceContext); + + if (!NT_SUCCESS(status)) + { + // NEED TO PUT CODE IN HERE TO RELEASE THE DEVICE OBJECT CREATED + // ABOVE AND LOG AN ERROR... + + NbtLogEvent(EVENT_NBT_CREATE_CONNECTION,status); + + KdPrint(("Failed to create the Connection Queue, status=%X\n",status)); + + return(status); + } + + return(STATUS_SUCCESS); +} + +#ifndef _IO_DELETE_DEVICE_SUPPORTED +/******************************************************************* + + NAME: NbtMarkHandlesAsStale + + SYNOPSIS: Marks all open handles on this device as stale + + ENTRY: DeviceContext ptr + + NOTE: Should be called with NbtConfig.JointLock held. + + HISTORY: + SanjayAn 11-Sept.-1996 Created + +********************************************************************/ +VOID +NbtMarkHandlesAsStale ( + IN tDEVICECONTEXT * pDeviceContext + ) +{ + CTELockHandle OldIrq1; + CTELockHandle OldIrq2; + CTELockHandle OldIrq3; + CTELockHandle OldIrq4; + PLIST_ENTRY pEntry; + PLIST_ENTRY pEntry1; + PLIST_ENTRY pEntry2; + PLIST_ENTRY pHead; + PLIST_ENTRY pHead1; + PLIST_ENTRY pHead2; + tADDRESSELE *pAddressEle; + tCONNECTELE *pConnEle; + tCLIENTELE *pClient; + + // go through the list of addresses, then the list of clients on each + // address and then the list of connection that are in use and those that + // are currently Listening. + // + pHead = &NbtConfig.AddressHead; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + + CTESpinLock(pAddressEle,OldIrq2); + + if (pAddressEle->pDeviceContext != pDeviceContext) { + CTESpinFree(pAddressEle,OldIrq2); + pEntry = pEntry->Flink; + continue; + } + + pHead1 = &pAddressEle->ClientHead; + pEntry1 = pHead1->Flink; + while (pEntry1 != pHead1) + { + pClient = CONTAINING_RECORD(pEntry1,tCLIENTELE,Linkage); + pEntry1 = pEntry1->Flink; + + CTESpinLock(pClient,OldIrq3); + + ASSERT(pClient->pDeviceContext == pDeviceContext); + + // + // Mark ClientEle as down so only a close is valid on it. + // + pClient->Verify = NBT_VERIFY_CLIENT_DOWN; + + pHead2 = &pClient->ConnectActive; + pEntry2 = pHead2->Flink; + while (pEntry2 != pHead2) + { + // + // Mark ConnEle as down so only a close is valid on it. + // + pConnEle = CONTAINING_RECORD(pEntry2,tCONNECTELE,Linkage); + + CTESpinLock(pConnEle,OldIrq4); + + ASSERT(pConnEle->pDeviceContext == pDeviceContext); + + pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN; + + CTESpinFree(pConnEle,OldIrq4); + + pEntry2 = pEntry2->Flink; + } + + pHead2 = &pClient->ConnectActive; + pEntry2 = pHead2->Flink; + while (pEntry2 != pHead2) + { + tLISTENREQUESTS *pListenReq; + + // + // Mark ConnEle as down so only a close is valid on it. + // + pListenReq = CONTAINING_RECORD(pEntry2,tLISTENREQUESTS,Linkage); + pConnEle = (tCONNECTELE *)pListenReq->pConnectEle; + + CTESpinLock(pConnEle,OldIrq4); + + ASSERT(pConnEle->pDeviceContext == pDeviceContext); + + pConnEle->Verify = NBT_VERIFY_CONNECTION_DOWN; + + CTESpinFree(pConnEle,OldIrq4); + + pEntry2 = pEntry2->Flink; + } + CTESpinFree(pClient,OldIrq3); + } + CTESpinFree(pAddressEle,OldIrq2); + pEntry = pEntry->Flink; + } +} +#endif + +/******************************************************************* + + NAME: NbtDestroyDeviceObject + + SYNOPSIS: Destroys the specified device + + ENTRY: pBuffer - name of the device/ device ptr + + HISTORY: + SanjayAn 11-Sept.-1996 Created + +********************************************************************/ + +NTSTATUS +NbtDestroyDeviceObject( +#if 0 + IN PVOID pBuffer +#endif + IN tDEVICECONTEXT * pDeviceContext + ) +{ + LIST_ENTRY * pEntry; + LIST_ENTRY * pHead; + tDEVICECONTEXT * pTmpDeviceContext; + tDEVICECONTEXT * pNextDeviceContext; + tCLIENTELE * pClientEle; + tADDRESSELE * pAddress; + tNAMEADDR * pNameAddr; + tCONNECTELE * pConnEle; + tLOWERCONNECTION * pLowerConn; + tTIMERQENTRY * pTimer; + COMPLETIONCLIENT pClientCompletion; + PVOID Context; + tDGRAM_SEND_TRACKING * pTracker; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + CTELockHandle OldIrq2; + int i; + tNBTCONFIG *pConfig = &NbtConfig; + WCHAR Buffer[MAX_PATH]; + UNICODE_STRING ucExportName; + PUNICODE_STRING pucExportName; + +#if 0 + tDEVICECONTEXT * pDeviceContext; + + ucExportName.MaximumLength = sizeof(Buffer); + ucExportName.Buffer = Buffer; + pucExportName = &ucExportName; + + RtlInitUnicodeString(pucExportName, &((PNETBT_ADD_DEL_IF)pBuffer)->IfName[0]); + + // + // Find which device is going away + // Also, find out a device object that is still active: we need that info + // to update some of the address ele's. + // + pDeviceContext = NULL; + pNextDeviceContext = NULL; + + for ( pEntry = pConfig->DeviceContexts.Flink; + pEntry != &pConfig->DeviceContexts; + pEntry = pEntry->Flink ) + { + pTmpDeviceContext = CONTAINING_RECORD( pEntry, tDEVICECONTEXT, Linkage); + if ( !RtlCompareUnicodeString ( + &pTmpDeviceContext->ExportName, + pucExportName, + FALSE)) + pDeviceContext = pTmpDeviceContext; + else + pNextDeviceContext = pTmpDeviceContext; + } +#endif + + if (pDeviceContext == NULL) + return STATUS_INVALID_PARAMETER; + + if (pDeviceContext->IpAddress != 0) { + (VOID)NbtNewDhcpAddress(pDeviceContext,0,0); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pDeviceContext,OldIrq1); + + if ( --NbtConfig.AdapterCount == 1) + NbtConfig.MultiHomed = FALSE; + + ASSERT(IsListEmpty(&pDeviceContext->LowerConnFreeHead)); + + // + // walk through all names and see if any is being registered on this + // device context: if so, stop and complete it! + // + for (i=0;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead->Flink; + while (pEntry != pHead) + { + pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); + pEntry = pEntry->Flink; + + if (pNameAddr->NameTypeState & STATE_RESOLVING) + { + pTimer = pNameAddr->pTimer; + + // + // if the name registration was started for this name on this device + // context, stop the timer. (Completion routine will take care of + // doing registration on other device contexts if applicable) + // + if (pTimer) + { + pTracker = pTimer->Context; + ASSERT(pTracker->pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + if (pTracker->pDeviceContext == pDeviceContext) + { + ASSERT(pTracker->pNameAddr == pNameAddr); + + pNameAddr->pTimer = NULL; + + StopTimer(pTimer,&pClientCompletion,&Context); + + if (pClientCompletion) + { + (*pClientCompletion)(Context,STATUS_NETWORK_NAME_DELETED); + } + + KdPrint(("DestroyDeviceObject: stopped name reg timer")) ; + } + } + + } + } + } + + // + // close all the TDI handles + // + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + CloseAddressesWithTransport(pDeviceContext); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pDeviceContext,OldIrq1); + + while (!IsListEmpty(&pDeviceContext->LowerConnection)) + { + pEntry = RemoveHeadList(&pDeviceContext->LowerConnection); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + CTEMemFree( pLowerConn ); + } + + RemoveEntryList( &pDeviceContext->Linkage); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // + // IoDeleteDevice on a device with open handles is not supported currently. + // Until the base guys support this feature, we hack Netbt to never call IoDeleteDevice; + // instead we mark it as down and re-use the block on a later open. + // We also mark all open handles as invalid so we fail any request directed at them. + // + CTESpinFree(pDeviceContext,OldIrq1); + NbtMarkHandlesAsStale(pDeviceContext); + CTESpinLock(pDeviceContext,OldIrq1); +#endif + + // + // Walk through the AddressHead list. If any addresses exist and they + // point to old device context, put the next device context. Also, update + // adapter mask to reflect that this device context is now gone. + // + KdPrint(("DestroyDeviceObject: setting AddrEle,NameAddr fields\r\n")); + pHead = pEntry = &NbtConfig.AddressHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pAddress = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage); + ASSERT (pAddress->Verify == NBT_VERIFY_ADDRESS); + if (pAddress->pDeviceContext == pDeviceContext) + { + if (!IsListEmpty(&pConfig->DeviceContexts)) { + pAddress->pDeviceContext = CONTAINING_RECORD( pConfig->DeviceContexts.Flink, tDEVICECONTEXT, Linkage); + } else { + pAddress->pDeviceContext = NULL; + } + } + + // + // Release the name on this adapter; but dont release on other adapters + // + // only release the name on the net if it was not in conflict first + // This prevents name releases going out for names that were not actually + // claimed. Also, quick add names are not released on the net either. + // + if (!(pNameAddr->NameTypeState & (STATE_CONFLICT | NAMETYPE_QUICK)) && + (pAddress->pNameAddr->Name[0] != '*') && + (pNameAddr->AdapterMask & pDeviceContext->AdapterNumber)) + { + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + (VOID)ReleaseNameOnNet(pAddress->pNameAddr, + NbtConfig.pScope, + pAddress, + NameReleaseDoneOnDynIf, // name released on dynamic if + NodeType, + pDeviceContext); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLock(pDeviceContext,OldIrq1); + } + } + + // + // Mark in the device extension that this is not a valid device. Default is FALSE... + // + InterlockedIncrement(&pDeviceContext->IsDestroyed); + +#ifndef _IO_DELETE_DEVICE_SUPPORTED + // + // Chain the device on the free list + // + ExInterlockedInsertTailList(&NbtConfig.FreeDevCtx, + &pDeviceContext->FreeLinkage, + &NbtConfig.SpinLock); + + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); +#else + + CTEMemFree( pDeviceContext->ExportName.Buffer ); + CTESpinFree(pDeviceContext,OldIrq1); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + IoDeleteDevice((PDEVICE_OBJECT)pDeviceContext); + +#endif + + KdPrint(("DestroyDeviceObject: deleted @ %lx\n", pDeviceContext)); + + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------- +NTSTATUS +CreateControlObject( + tNBTCONFIG *pConfig) + +/*++ + +Routine Description: + + This routine allocates memory for the provider info block, tacks it + onto the global configuration and sets default values for each item. + +Arguments: + + +Return Value: + + + NTSTATUS + +--*/ + +{ + tCONTROLOBJECT *pControl; + + + CTEPagedCode(); + pControl = (tCONTROLOBJECT *)ExAllocatePool( + NonPagedPool, + sizeof(tCONTROLOBJECT)); + if (!pControl) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pControl->Verify = NBT_VERIFY_CONTROL; + + // setup the spin lock); + CTEInitLock(&pControl->SpinLock); + + pControl->ProviderInfo.Version = 1; + pControl->ProviderInfo.MaxSendSize = 0; + pControl->ProviderInfo.MaxConnectionUserData = 0; + + // we need to get these values from the transport underneath...*TODO* + // since the RDR uses this value + pControl->ProviderInfo.MaxDatagramSize = 0; + + pControl->ProviderInfo.ServiceFlags = 0; +/* pControl->ProviderInfo.TransmittedTsdus = 0; + pControl->ProviderInfo.ReceivedTsdus = 0; + pControl->ProviderInfo.TransmissionErrors = 0; + pControl->ProviderInfo.ReceiveErrors = 0; +*/ + pControl->ProviderInfo.MinimumLookaheadData = 0; + pControl->ProviderInfo.MaximumLookaheadData = 0; +/* pControl->ProviderInfo.DiscardedFrames = 0; + pControl->ProviderInfo.OversizeTsdusReceived = 0; + pControl->ProviderInfo.UndersizeTsdusReceived = 0; + pControl->ProviderInfo.MulticastTsdusReceived = 0; + pControl->ProviderInfo.BroadcastTsdusReceived = 0; + pControl->ProviderInfo.MulticastTsdusTransmitted = 0; + pControl->ProviderInfo.BroadcastTsdusTransmitted = 0; + pControl->ProviderInfo.SendTimeouts = 0; + pControl->ProviderInfo.ReceiveTimeouts = 0; + pControl->ProviderInfo.ConnectionIndicationsReceived = 0; + pControl->ProviderInfo.ConnectionIndicationsAccepted = 0; + pControl->ProviderInfo.ConnectionsInitiated = 0; + pControl->ProviderInfo.ConnectionsAccepted = 0; +*/ + // put a ptr to this info into the pConfig so we can locate it + // when we want to cleanup + pConfig->pControlObj = pControl; + + /* KEEP THIS STUFF HERE SINCE WE MAY NEED TO ALSO CREATE PROVIDER STATS!! + *TODO* + DeviceList[i].ProviderStats.Version = 2; + DeviceList[i].ProviderStats.OpenConnections = 0; + DeviceList[i].ProviderStats.ConnectionsAfterNoRetry = 0; + DeviceList[i].ProviderStats.ConnectionsAfterRetry = 0; + DeviceList[i].ProviderStats.LocalDisconnects = 0; + DeviceList[i].ProviderStats.RemoteDisconnects = 0; + DeviceList[i].ProviderStats.LinkFailures = 0; + DeviceList[i].ProviderStats.AdapterFailures = 0; + DeviceList[i].ProviderStats.SessionTimeouts = 0; + DeviceList[i].ProviderStats.CancelledConnections = 0; + DeviceList[i].ProviderStats.RemoteResourceFailures = 0; + DeviceList[i].ProviderStats.LocalResourceFailures = 0; + DeviceList[i].ProviderStats.NotFoundFailures = 0; + DeviceList[i].ProviderStats.NoListenFailures = 0; + + DeviceList[i].ProviderStats.DatagramsSent = 0; + DeviceList[i].ProviderStats.DatagramBytesSent.HighPart = 0; + DeviceList[i].ProviderStats.DatagramBytesSent.LowPart = 0; + + DeviceList[i].ProviderStats.DatagramsReceived = 0; + DeviceList[i].ProviderStats.DatagramBytesReceived.HighPart = 0; + DeviceList[i].ProviderStats.DatagramBytesReceived.LowPart = 0; + + DeviceList[i].ProviderStats.PacketsSent = 0; + DeviceList[i].ProviderStats.PacketsReceived = 0; + + DeviceList[i].ProviderStats.DataFramesSent = 0; + DeviceList[i].ProviderStats.DataFrameBytesSent.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesSent.LowPart = 0; + + DeviceList[i].ProviderStats.DataFramesReceived = 0; + DeviceList[i].ProviderStats.DataFrameBytesReceived.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesReceived.LowPart = 0; + + DeviceList[i].ProviderStats.DataFramesResent = 0; + DeviceList[i].ProviderStats.DataFrameBytesResent.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesResent.LowPart = 0; + + DeviceList[i].ProviderStats.DataFramesRejected = 0; + DeviceList[i].ProviderStats.DataFrameBytesRejected.HighPart = 0; + DeviceList[i].ProviderStats.DataFrameBytesRejected.LowPart = 0; + + DeviceList[i].ProviderStats.ResponseTimerExpirations = 0; + DeviceList[i].ProviderStats.AckTimerExpirations = 0; + DeviceList[i].ProviderStats.MaximumSendWindow = 0; + DeviceList[i].ProviderStats.AverageSendWindow = 0; + DeviceList[i].ProviderStats.PiggybackAckQueued = 0; + DeviceList[i].ProviderStats.PiggybackAckTimeouts = 0; + + DeviceList[i].ProviderStats.WastedPacketSpace.HighPart = 0; + DeviceList[i].ProviderStats.WastedPacketSpace.LowPart = 0; + DeviceList[i].ProviderStats.WastedSpacePackets = 0; + DeviceList[i].ProviderStats.NumberOfResources = 0; + */ + return(STATUS_SUCCESS); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +IfNotAnyLowerConnections( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. If the DoDisable flag + is set the list head of free lower connections is returned and the + list in the Nbtconfig structure is made empty. + +Arguments: + +Return Value: + + none + +--*/ + +{ + CTELockHandle OldIrq; + + CTESpinLock(pDeviceContext,OldIrq); + if (!IsListEmpty(&pDeviceContext->LowerConnection)) + { + CTESpinFree(pDeviceContext,OldIrq); + return(STATUS_UNSUCCESSFUL); + } + CTESpinFree(pDeviceContext,OldIrq); + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +CloseAddressesWithTransport( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine checks each device context to see if there are any open + connections, and returns SUCCESS if there are. + +Arguments: + +Return Value: + + none + +--*/ + +{ + BOOLEAN Attached; + CTELockHandle OldIrq; + PFILE_OBJECT pNSFileObject, pSFileObject, pDGFileObject; + + CTEAttachFsp(&Attached); + + // + // Check for the existence of Objects under SpinLock and + // then Close them outside of the SpinLock + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pNSFileObject = pDeviceContext->pNameServerFileObject) + { + pDeviceContext->pNameServerFileObject = NULL; + } + if (pSFileObject = pDeviceContext->pSessionFileObject) + { + pDeviceContext->pSessionFileObject = NULL; + } + if (pDGFileObject = pDeviceContext->pDgramFileObject) + { + pDeviceContext->pDgramFileObject = NULL; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Now close all the necessary objects as appropriate + // + if (pNSFileObject) + { + ObDereferenceObject((PVOID *)pNSFileObject); + ZwClose(pDeviceContext->hNameServer); + } + if (pSFileObject) + { + ObDereferenceObject((PVOID *)pSFileObject); + ZwClose(pDeviceContext->hSession); + } + if (pDGFileObject) + { + ObDereferenceObject((PVOID *)pDGFileObject); + ZwClose(pDeviceContext->hDgram); + } + + CTEDetachFsp(Attached); + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtCreateAddressObjects( + IN ULONG IpAddress, + IN ULONG SubnetMask, + OUT tDEVICECONTEXT *pDeviceContext) + +/*++ + +Routine Description: + + This routine gets the ip address and subnet mask out of the registry + to calcuate the broadcast address. It then creates the address objects + with the transport. + +Arguments: + + pucRegistryPath - path to NBT config info in registry + pucBindName - name of the service to bind to. + pDeviceContext - ptr to the device context... place to store IP addr + and Broadcast address permanently + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + ULONG ValueMask; + UCHAR IpAddrByte; + + CTEPagedCode(); + // + // to get the broadcast address combine the IP address with the subnet mask + // to yield a value with 1's in the "local" portion and the IP address + // in the network portion + // + ValueMask = (SubnetMask & IpAddress) | (~SubnetMask & -1); + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Broadcastaddress = %X\n",ValueMask)); + + // + // the registry can be configured to set the subnet broadcast address to + // -1 rather than use the actual subnet broadcast address. This code + // checks for that and sets the broadcast address accordingly. + // + if (!NbtConfig.UseRegistryBcastAddr) + { + pDeviceContext->BroadcastAddress = ValueMask; + } + else + { + pDeviceContext->BroadcastAddress = NbtConfig.RegistryBcastAddr; + } + + pDeviceContext->IpAddress = IpAddress; + + pDeviceContext->SubnetMask = SubnetMask; + // + // get the network number by checking the top bits in the ip address, + // looking for 0 or 10 or 110 or 1110 + // + IpAddrByte = ((PUCHAR)&IpAddress)[3]; + if ((IpAddrByte & 0x80) == 0) + { + // class A address - one byte netid + IpAddress &= 0xFF000000; + } + else + if ((IpAddrByte & 0xC0) ==0x80) + { + // class B address - two byte netid + IpAddress &= 0xFFFF0000; + } + else + if ((IpAddrByte & 0xE0) ==0xC0) + { + // class C address - three byte netid + IpAddress &= 0xFFFFFF00; + } + + + pDeviceContext->NetMask = IpAddress; + + + // now create the address objects. + + // open the Ip Address for inbound Datagrams. + status = NbtTdiOpenAddress( + &pDeviceContext->hDgram, + &pDeviceContext->pDgramDeviceObject, + &pDeviceContext->pDgramFileObject, + pDeviceContext, + (USHORT)NBT_DATAGRAM_UDP_PORT, + pDeviceContext->IpAddress, + 0); // not a TCP port + + if (NT_SUCCESS(status)) + { + // open the Nameservice UDP port .. + status = NbtTdiOpenAddress( + &pDeviceContext->hNameServer, + &pDeviceContext->pNameServerDeviceObject, + &pDeviceContext->pNameServerFileObject, + pDeviceContext, + (USHORT)NBT_NAMESERVICE_UDP_PORT, + pDeviceContext->IpAddress, + 0); // not a TCP port + + if (NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt: Open Session port %X\n",pDeviceContext)); + + // Open the TCP port for Session Services + status = NbtTdiOpenAddress( + &pDeviceContext->hSession, + &pDeviceContext->pSessionDeviceObject, + &pDeviceContext->pSessionFileObject, + pDeviceContext, + (USHORT)NBT_SESSION_TCP_PORT, + pDeviceContext->IpAddress, + TCP_FLAG | SESSION_FLAG); // TCP port + + if (NT_SUCCESS(status)) + { + // + // This will get the MAC address for a RAS connection + // which is zero until there really is a connection to + // the RAS server + // + GetExtendedAttributes(pDeviceContext); + return(status); + } + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to Open Session address with TDI, status = %X\n",status)); + + // + // Ensure that the Object pointers are NULLed out! + // + pDeviceContext->pSessionFileObject = NULL; + + ObDereferenceObject(pDeviceContext->pNameServerFileObject); + pDeviceContext->pNameServerFileObject = NULL; + NTZwCloseFile(pDeviceContext->hNameServer); + + } + ObDereferenceObject(pDeviceContext->pDgramFileObject); + pDeviceContext->pDgramFileObject = NULL; + NTZwCloseFile(pDeviceContext->hDgram); + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to Open NameServer port with TDI, status = %X\n",status)); + } + + return(status); +} + +//---------------------------------------------------------------------------- +VOID +GetExtendedAttributes( + tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine converts a unicode dotted decimal to a ULONG + +Arguments: + + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + TCP_REQUEST_QUERY_INFORMATION_EX QueryReq; + UCHAR pBuffer[256]; + IO_STATUS_BLOCK IoStatus; + ULONG BufferSize = 256; + HANDLE event; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR pName=L"Tcp"; + PFILE_FULL_EA_INFORMATION EaBuffer; + UNICODE_STRING DeviceName; + BOOLEAN Attached = FALSE; + HANDLE hTcp; + + CTEPagedCode(); + + // + // Open a control channel to TCP for this IOCTL. + // + // NOTE: We cannot use the hControl in the DeviceContext since that was created in the context + // of the system process (address arrival from TCP/IP). Here, we are in the context of the service + // process (Ioctl down from DHCP) and so we need to open another control channel. + // + // NOTE: We still need to maintain the earlier call to create a control channel since that is + // used to submit TDI requests down to TCP/IP. + // + + // copy device name into the unicode string + Status = CreateDeviceString(pName,&DeviceName); + if (!NT_SUCCESS(Status)) + { + return; + } + InitializeObjectAttributes ( + &ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer)); + + EaBuffer = NULL; + + Status = ZwCreateFile ( + &hTcp, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + NULL, // block size (unused). + FILE_ATTRIBUTE_NORMAL, // file attributes. + 0, + FILE_CREATE, + 0, // create options. + (PVOID)EaBuffer, // EA buffer. + 0); // Ea length + + + CTEMemFree(DeviceName.Buffer); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint( ("OpenControl CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status)); + + if ( NT_SUCCESS( Status )) + { + // + // Initialize the TDI information buffers. + // + // + // pass in the ipaddress as the first ULONG of the context array + // + *(ULONG *)QueryReq.Context = htonl(pDeviceContext->IpAddress); + + QueryReq.ID.toi_entity.tei_entity = CL_NL_ENTITY; + QueryReq.ID.toi_entity.tei_instance = 0; + QueryReq.ID.toi_class = INFO_CLASS_PROTOCOL; + QueryReq.ID.toi_type = INFO_TYPE_PROVIDER; + QueryReq.ID.toi_id = IP_INTFC_INFO_ID; + + status = ZwCreateEvent( + &event, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE + ); + + if ( !NT_SUCCESS(status) ) + { + return; + + } + + // + // Make the actual TDI call + // + + status = ZwDeviceIoControlFile( + hTcp, + event, + NULL, + NULL, + &IoStatus, + IOCTL_TCP_QUERY_INFORMATION_EX, + &QueryReq, + sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), + pBuffer, + BufferSize + ); + + // + // If the call pended and we were supposed to wait for completion, + // then wait. + // + + if ( status == STATUS_PENDING ) + { + status = KeWaitForSingleObject( event, Executive, KernelMode, FALSE, NULL ); + + ASSERT( NT_SUCCESS(status) ); + } + + if ( NT_SUCCESS(status) ) + { + ULONG Length; + + pDeviceContext->PointToPoint = ((((IPInterfaceInfo *)pBuffer)->iii_flags & IP_INTFC_FLAG_P2P) != 0); + + // + // get the length of the mac address in case is is less than + // 6 bytes + // + Length = (((IPInterfaceInfo *)pBuffer)->iii_addrlength < sizeof(tMAC_ADDRESS)) + ? ((IPInterfaceInfo *)pBuffer)->iii_addrlength : sizeof(tMAC_ADDRESS); + + CTEZeroMemory(pDeviceContext->MacAddress.Address,sizeof(tMAC_ADDRESS)); + CTEMemCopy(&pDeviceContext->MacAddress.Address[0], + ((IPInterfaceInfo *)pBuffer)->iii_addr, + Length); + + } + + status = ZwClose( event ); + ASSERT( NT_SUCCESS(status) ); + + status = IoStatus.Status; + + // + // Close the handle to TCP since we dont need it anymore; all TDI requests go thru the + // Control handle in the DeviceContext. + // + status = ZwClose( hTcp ); + ASSERT( NT_SUCCESS(status) ); + } + else + { + KdPrint(("Nbt:Failed to Open the control connection to the transport, status1 = %X\n", + Status)); + + } + + return; +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ConvertToUlong( + IN PUNICODE_STRING pucAddress, + OUT ULONG *pulValue) + +/*++ + +Routine Description: + + This routine converts a unicode dotted decimal to a ULONG + +Arguments: + + +Return Value: + + none + +--*/ + +{ + NTSTATUS status; + OEM_STRING OemAddress; + + // create integer from unicode string + + CTEPagedCode(); + status = RtlUnicodeStringToAnsiString(&OemAddress, pucAddress, TRUE); + if (!NT_SUCCESS(status)) + { + return(status); + } + + status = ConvertDottedDecimalToUlong(OemAddress.Buffer,pulValue); + + RtlFreeAnsiString(&OemAddress); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("ERR: Bad Dotted Decimal Ip Address(must be <=255 with 4 dots) = %ws\n", + pucAddress->Buffer)); + + return(status); + } + + return(STATUS_SUCCESS); + + +} + + + +//---------------------------------------------------------------------------- +VOID +NbtGetMdl( + PMDL *ppMdl, + enum eBUFFER_TYPES eBuffType) + +/*++ + +Routine Description: + + This routine allocates an Mdl. + +Arguments: + + ppListHead - a ptr to a ptr to the list head to add buffer to + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + PMDL pMdl; + ULONG lBufferSize; + PVOID pBuffer; + + if (NbtConfig.iCurrentNumBuff[eBuffType] + >= NbtConfig.iMaxNumBuff[eBuffType]) + { + *ppMdl = NULL; + return; + } + + lBufferSize = NbtConfig.iBufferSize[eBuffType]; + + pBuffer = NbtAllocMem((USHORT)lBufferSize,NBT_TAG('g')); + + if (!pBuffer) + { + *ppMdl = NULL; + return; + } + + // allocate a MDL to hold the session hdr + pMdl = IoAllocateMdl( + (PVOID)pBuffer, + lBufferSize, + FALSE, // want this to be a Primary buffer - the first in the chain + FALSE, + NULL); + + *ppMdl = pMdl; + + if (!pMdl) + { + CTEMemFree(pBuffer); + return; + } + + // fill in part of the session hdr since it is always the same + if (eBuffType == eNBT_FREE_SESSION_MDLS) + { + ((tSESSIONHDR *)pBuffer)->Flags = NBT_SESSION_FLAGS; + ((tSESSIONHDR *)pBuffer)->Type = NBT_SESSION_MESSAGE; + } + else + if (eBuffType == eNBT_DGRAM_MDLS) + { + ((tDGRAMHDR *)pBuffer)->Flags = FIRST_DGRAM | (NbtConfig.PduNodeType >> 10); + ((tDGRAMHDR *)pBuffer)->PckOffset = 0; // not fragmented + + } + + // map the Mdl properly to fill in the pages portion of the MDL + MmBuildMdlForNonPagedPool(pMdl); + + NbtConfig.iCurrentNumBuff[eBuffType]++; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtInitMdlQ( + PSINGLE_LIST_ENTRY pListHead, + enum eBUFFER_TYPES eBuffType) + +/*++ + +Routine Description: + + This routine allocates Mdls for use later. + +Arguments: + + ppListHead - a ptr to a ptr to the list head to add buffer to + iNumBuffers - the number of buffers to add to the queue + +Return Value: + + none + +--*/ + +{ + int i; + PMDL pMdl; + + + CTEPagedCode(); + // Initialize the list head, so the last element always points to NULL + pListHead->Next = NULL; + + // create a small number first and then lis the list grow with time + for (i=0;i < NBT_INITIAL_NUM ;i++ ) + { + + NbtGetMdl(&pMdl,eBuffType); + if (!pMdl) + { + KdPrint(("NBT:Unable to allocate MDL at initialization time!!\n"));\ + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // put on free list + PushEntryList(pListHead,(PSINGLE_LIST_ENTRY)pMdl); + + } + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTZwCloseFile( + IN HANDLE Handle + ) + +/*++ +Routine Description: + + This Routine handles closing a handle with NT within the context of NBT's + file system process. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + BOOLEAN Attached = FALSE; + + CTEPagedCode(); + // + // Attach to NBT's FSP (file system process) to free the handle since + // the handle is only valid in that process. + // + if (PsGetCurrentProcess() != NbtFspProcess) + { + KeAttachProcess(&NbtFspProcess->Pcb); + Attached = TRUE; + } + + status = ZwClose(Handle); + + if (Attached) + { + // + // go back to the original process + // + KeDetachProcess(); + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTReReadRegistry( + IN tDEVICECONTEXT *pDeviceContext + ) + +/*++ +Routine Description: + + This Routine re-reads the registry values when DHCP issues the Ioctl + to do so. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + tADDRARRAY *pAddrArray=NULL; + tADDRARRAY *pAddr; + tDEVICES *pBindDevices=NULL; + tDEVICES *pExportDevices=NULL; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + tDEVICECONTEXT *pDevContext; + + CTEPagedCode(); + + CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); + + // + // BUGBUG [WishList]: We look at the whole registry in NbtAddressAdd too. + // + status = NbtReadRegistry( + &NbtConfig.pRegistry, + NULL, // Null Driver Object + &NbtConfig, + &pBindDevices, + &pExportDevices, + &pAddrArray); + + if (pAddrArray) + { + ULONG i; +#if DBG + { + BOOLEAN fFound=FALSE; + + // + // Loop thru the devicecontexts in the Config struct to ensure that this DeviceContext + // is actually valid. + // + // BUGBUG[WishList]:Would be good to have signatures in the structures. + // + pAddr = pAddrArray; + pHead = &NbtConfig.DeviceContexts; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage); + if (pDevContext == pDeviceContext) + { + fFound = TRUE; + break; + } + } + + ASSERT(fFound == TRUE); + } +#endif + + // + // Figure out the Address entry by matching the BindDevice names against the + // name in the DeviceContext passed in. + // + for (i=0; i<NbtConfig.uNumDevices; i++) { + + if (RtlCompareUnicodeString(&pDeviceContext->BindName, + &pBindDevices->Names[i], + FALSE) == 0) { + // + // We found a match + // + pDeviceContext->lNameServerAddress = pAddrArray[i].NameServerAddress; + pDeviceContext->lBackupServer = pAddrArray[i].BackupServer; + + // + // if the node type is set to Bnode by default then switch to Hnode if + // there are any WINS servers configured. + // + if ((NodeType & DEFAULT_NODE_TYPE) && + (pAddrArray[i].NameServerAddress || pAddrArray[i].BackupServer)) + { + NodeType = MSNODE | (NodeType & PROXY); + } + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("NBT:Found BindName: %lx, AddrArray: %lx, i: %lx\n", pBindDevices, pAddrArray, i)); + + break; + } + } + +#if DBG + if (i == NbtConfig.uNumDevices) { + KdPrint(("Nbt:Unable to find the entry corresp. to device %lx in the registry. BindDevices: %lx\n", + pDeviceContext, pBindDevices)); + DbgBreakPoint(); + } +#endif + } + + // + // Free Allocated memory + // + if (pBindDevices) + { + CTEMemFree(pBindDevices->RegistrySpace); + CTEMemFree((PVOID)pBindDevices); + } + if (pExportDevices) + { + CTEMemFree(pExportDevices->RegistrySpace); + CTEMemFree((PVOID)pExportDevices); + } + if (pAddrArray) + { + CTEMemFree((PVOID)pAddrArray); + } + + CTEExReleaseResource(&NbtConfig.Resource); + + if (pDeviceContext->IpAddress) + { + // + // Add the "permanent" name to the local name table. This is the IP + // address of the node padded out to 16 bytes with zeros. + // + status = NbtAddPermanentName(pDeviceContext); + + if (!(NodeType & BNODE)) + { + // Probably the Ip address just changed and Dhcp is informing us + // of a new Wins Server addresses, so refresh all the names to the + // new wins server + // + ReRegisterLocalNames(); + } + else + { + // + // no need to refresh + // on a Bnode + // + LockedStopTimer(&NbtConfig.pRefreshTimer); + } + } + + return(STATUS_SUCCESS); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtLogEvent( + IN ULONG EventCode, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This function allocates an I/O error log record, fills it in and writes it + to the I/O error log. + + +Arguments: + + EventCode - Identifies the error message. + Status - The status value to log: this value is put into the + data portion of the log message. + + +Return Value: + + STATUS_SUCCESS - The error was successfully logged.. + STATUS_BUFER_OVERFLOW - The error data was too large to be logged. + STATUS_INSUFFICIENT_RESOURCES - Unable to allocate memory. + + +--*/ + +{ + PIO_ERROR_LOG_PACKET ErrorLogEntry; + PVOID LoggingObject; + + LoggingObject = NbtConfig.DriverObject; + + ErrorLogEntry = IoAllocateErrorLogEntry(LoggingObject,sizeof(IO_ERROR_LOG_PACKET)); + + if (ErrorLogEntry == NULL) + { + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Unalbe to allocate Error Packet for Error logging\n")); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Fill in the necessary log packet fields. + // + ErrorLogEntry->UniqueErrorValue = 0; + ErrorLogEntry->ErrorCode = EventCode; + ErrorLogEntry->NumberOfStrings = 0; + ErrorLogEntry->StringOffset = 0; + ErrorLogEntry->DumpDataSize = (USHORT)sizeof(ULONG); + ErrorLogEntry->DumpData[0] = Status; + + IoWriteErrorLogEntry(ErrorLogEntry); + + return(STATUS_SUCCESS); +} +#ifdef DBGMEMNBT +VOID +PadEntry( + char *EntryPtr + ) +{ + char *Limit; + + // + // pad remainder of entry + // + Limit = EntryPtr + LOGWIDTH - 1; + ASSERT(LOGWIDTH >= (strlen(EntryPtr) + 1)); + for (EntryPtr += strlen(EntryPtr); + EntryPtr != Limit; + EntryPtr++ + ) { + *EntryPtr = ' '; + } + *EntryPtr = '\0'; +} +//---------------------------------------------------------------------------- +PVOID +CTEAllocMemDebug( + IN ULONG Size, + IN PVOID pBuffer, + IN UCHAR *File, + IN ULONG Line + ) + +/*++ + +Routine Description: + + This function logs getting and freeing memory. + +Arguments: + + +Return Value: + + +--*/ + +{ + CCHAR CurrProc; + UCHAR LockFree; + UCHAR *EntryPtr; + char *Limit; + PUCHAR pFile; + PVOID pMem; + PSTRM_PROCESSOR_LOG Log ; + + + if (!pBuffer) + { + if (!LogAlloc) + { + LogAlloc = ExAllocatePool(NonPagedPool,sizeof(STRM_PROCESSOR_LOG)); + LogAlloc->Index = 0; + } + Log = LogAlloc; + pMem = ExAllocatePool(NonPagedPool,Size); + } + else + { + if (!LogFree) + { + LogFree = ExAllocatePool(NonPagedPool,sizeof(STRM_PROCESSOR_LOG)); + LogFree->Index = 0; + } + Log = LogFree; + pMem = pBuffer; + ExFreePool(pBuffer); + } + + EntryPtr = Log->Log[Log->Index]; + + pFile = strrchr(File,'\\'); + + sprintf(EntryPtr,"%s %d %X",pFile, Line,pMem); + + PadEntry(EntryPtr); + + if (++(Log->Index) >= LOGSIZE) + { + Log->Index = 0; + } + // + // Mark next entry so we know where the log for this processor ends + // + EntryPtr = Log->Log[Log->Index]; + sprintf(EntryPtr, "*** Last Entry"); + + return(pMem); + +} +#endif + +#if DBG +//---------------------------------------------------------------------------- +VOID +AcquireSpinLockDebug( + IN PKSPIN_LOCK pSpinLock, + IN PKIRQL pOldIrq, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function gets the spin lock, and then sets the mask in Nbtconfig, per + processor. + + +Arguments: + + +Return Value: + + +--*/ + +{ + CCHAR CurrProc; + UCHAR LockFree; + + CTEGetLock(pSpinLock,pOldIrq); + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + NbtConfig.CurrProc = CurrProc; + + LockFree = (LockNumber > (UCHAR)NbtConfig.CurrentLockNumber[CurrProc]); + if (!LockFree) + { + KdPrint(("CurrProc = %X, CurrentLockNum = %X DataSTructLock = %X\n", + CurrProc,NbtConfig.CurrentLockNumber[CurrProc],LockNumber)); + } \ + + ASSERTMSG("Possible DeadLock, Getting SpinLock at a lower level\n",LockFree); + NbtConfig.CurrentLockNumber[CurrProc]|= LockNumber; + +} + +//---------------------------------------------------------------------------- +VOID +FreeSpinLockDebug( + IN PKSPIN_LOCK pSpinLock, + IN KIRQL OldIrq, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function clears the spin lock from the mask in Nbtconfig, per + processor and then releases the spin lock. + + +Arguments: + + +Return Value: + none + +--*/ + +{ + CCHAR CurrProc; + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + + NbtConfig.CurrentLockNumber[CurrProc] &= ~LockNumber; + CTEFreeLock(pSpinLock,OldIrq); + +} +//---------------------------------------------------------------------------- +VOID +AcquireSpinLockAtDpcDebug( + IN PKSPIN_LOCK pSpinLock, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function gets the spin lock, and then sets the mask in Nbtconfig, per + processor. + + +Arguments: + + +Return Value: + + +--*/ + +{ + CCHAR CurrProc; + UCHAR LockFree; + + CTEGetLockAtDPC(pSpinLock, 0); + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + NbtConfig.CurrProc = CurrProc; + + LockFree = (LockNumber > (UCHAR)NbtConfig.CurrentLockNumber[CurrProc]); + if (!LockFree) + { + KdPrint(("CurrProc = %X, CurrentLockNum = %X DataSTructLock = %X\n", + CurrProc,NbtConfig.CurrentLockNumber[CurrProc],LockNumber)); + } \ + + ASSERTMSG("Possible DeadLock, Getting SpinLock at a lower level\n",LockFree); + NbtConfig.CurrentLockNumber[CurrProc]|= LockNumber; + +} + +//---------------------------------------------------------------------------- +VOID +FreeSpinLockAtDpcDebug( + IN PKSPIN_LOCK pSpinLock, + IN UCHAR LockNumber + ) + +/*++ + +Routine Description: + + This function clears the spin lock from the mask in Nbtconfig, per + processor and then releases the spin lock. + + +Arguments: + + +Return Value: + none + +--*/ + +{ + CCHAR CurrProc; + + CurrProc = (CCHAR)KeGetCurrentProcessorNumber(); + + NbtConfig.CurrentLockNumber[CurrProc] &= ~LockNumber; + CTEFreeLockFromDPC(pSpinLock, 0); + +} +#endif //if Dbg + diff --git a/private/ntos/nbt/nt/registry.c b/private/ntos/nbt/nt/registry.c new file mode 100644 index 000000000..6bab53a34 --- /dev/null +++ b/private/ntos/nbt/nt/registry.c @@ -0,0 +1,1811 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + Registry.c + +Abstract: + + This contains all routines necessary to load device pathnames from the + registry. + +Author: + + Jim Stewart (Jimst) October 9 1992 + +Revision History: + + +Notes: + +--*/ + +#include "nbtprocs.h" +//#include <stdlib.h> + + +// +// Local functions used to access the registry. +// + +NTSTATUS +NbtOpenRegistry( + IN HANDLE NbConfigHandle, + IN PWSTR String, + OUT PHANDLE pHandle + ); + +VOID +NbtCloseRegistry( + IN HANDLE LinkageHandle, + IN HANDLE ParametersHandle + ); + +NTSTATUS +NbtReadLinkageInformation( + IN PWSTR pName, + IN HANDLE LinkageHandle, + OUT tDEVICES *pDevices, // place to put read in config data + OUT LONG *piNumDevice + ); + +NTSTATUS +OpenAndReadElement( + IN PUNICODE_STRING pucRootPath, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ); + +NTSTATUS +GetServerAddress ( + IN HANDLE ParametersHandle, + IN PWSTR KeyName, + OUT PULONG pIpAddr + ); + +NTSTATUS +NbtAppendString ( + IN PWSTR FirstString, + IN PWSTR SecondString, + OUT PUNICODE_STRING pucString + ); + +NTSTATUS +ReadStringRelative( + IN PUNICODE_STRING pRegistryPath, + IN PWSTR pRelativePath, + IN PWSTR pValueName, + OUT PUNICODE_STRING pOutString + ); + +VOID +NbtFindLastSlash( + IN PUNICODE_STRING pucRegistryPath, + OUT PWSTR *ppucLastElement, + IN int *piLength + ); + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtReadRegistry) +#pragma CTEMakePageable(PAGE, ReadNameServerAddresses) +#pragma CTEMakePageable(PAGE, GetServerAddress) +#pragma CTEMakePageable(PAGE, NTReadIniString) +#pragma CTEMakePageable(PAGE, GetIPFromRegistry) +#pragma CTEMakePageable(PAGE, NbtOpenRegistry) +#pragma CTEMakePageable(PAGE, NbtReadLinkageInformation) +#pragma CTEMakePageable(PAGE, NbtReadSingleParameter) +#pragma CTEMakePageable(PAGE, OpenAndReadElement) +#pragma CTEMakePageable(PAGE, ReadElement) +#pragma CTEMakePageable(PAGE, NTGetLmHostPath) +#pragma CTEMakePageable(PAGE, ReadStringRelative) +#pragma CTEMakePageable(PAGE, NbtFindLastSlash) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtReadRegistry( + IN PUNICODE_STRING RegistryPath, + IN PDRIVER_OBJECT DriverObject, + OUT tNBTCONFIG *pConfig, + OUT tDEVICES **ppBindDevices, + OUT tDEVICES **ppExportDevices, + OUT tADDRARRAY **ppAddrArray + + ) +/*++ + +Routine Description: + + This routine is called to get information from the registry, + starting at RegistryPath to get the parameters. + +Arguments: + + RegistryPath - Supplies RegistryPath. The name of Nbt's node in the + registry. + + pNbtConfig - ptr to global configuration strucuture for NBT + +Return Value: + + NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR BindName = NBT_BIND; + PWSTR ExportName = NBT_EXPORT; + PWSTR pwsNameServer = NBT_MAINNAME_SERVICE; + PWSTR pwsBackupNameServer = NBT_BACKUP_SERVER; + PWSTR pwsParmsKeyName = NBT_PARAMETERS; + NTSTATUS OpenStatus; + HANDLE LinkageHandle; + HANDLE ParametersHandle; + HANDLE NbtConfigHandle; + NTSTATUS Status; + ULONG Disposition; + OBJECT_ATTRIBUTES TmpObjectAttributes; + PWSTR LinkageString = L"Linkage"; + PWSTR ParametersString = L"Parameters"; + tDEVICES *pBindDevices; + tDEVICES *pExportDevices; + UNICODE_STRING ucString; + + CTEPagedCode(); + + *ppExportDevices = *ppBindDevices = NULL; + *ppAddrArray = NULL; + + // this procedure can be called from the DHCP activated code. In + // that case we just want to read the registry and not Zero the + // NbtConfig data structure. + if (DriverObject) + { + // + // Initialize the Configuration data structure + // + CTEZeroMemory(pConfig,sizeof(tNBTCONFIG)); + NbtConfig.TransactionId = WINS_MAXIMUM_TRANSACTION_ID + 1; + + // save the driver object for event logging purposes + // + NbtConfig.DriverObject = DriverObject; + + // save the registry path for later use when DHCP asks us + // to re-read the registry. + // + NbtConfig.pRegistry.Buffer = NbtAllocMem(RegistryPath->MaximumLength,NBT_TAG('i')); + NbtConfig.pRegistry.MaximumLength = (USHORT)RegistryPath->MaximumLength; + if (NbtConfig.pRegistry.Buffer) + { + RtlCopyUnicodeString(&NbtConfig.pRegistry,RegistryPath); + } + else + return(STATUS_INSUFFICIENT_RESOURCES); + + // clear the ptr to the broadcast netbios name. This ptr is an optimization + // that lets us resolve broadcast netbios name quickly + pConfig->pBcastNetbiosName = NULL; + } + // + // Open the registry. + // + + InitializeObjectAttributes( + &TmpObjectAttributes, + RegistryPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwCreateKey( + &NbtConfigHandle, + KEY_WRITE, + &TmpObjectAttributes, + 0, // title index + NULL, // class + 0, // create options + &Disposition); // disposition + + if (!NT_SUCCESS(Status)) + { + NbtLogEvent(EVENT_NBT_CREATE_DRIVER,Status); + + return STATUS_UNSUCCESSFUL; + } + + + OpenStatus = NbtOpenRegistry( + NbtConfigHandle, + LinkageString, + &LinkageHandle); + + if (NT_SUCCESS(OpenStatus)) + { + OpenStatus = NbtOpenRegistry( + NbtConfigHandle, + ParametersString, + &ParametersHandle); + + if (NT_SUCCESS(OpenStatus)) + { + // + // Read in the binding information (if none is present + // the array will be filled with all known drivers). + // + pBindDevices = NbtAllocMem(sizeof(tDEVICES),NBT_TAG('i')); + + if (pBindDevices) + { + pExportDevices = NbtAllocMem(sizeof(tDEVICES),NBT_TAG('i')); + if (pExportDevices) + { + ULONG NumDevices; + + // + // Read various parameters from the registry + // + ReadParameters(pConfig,ParametersHandle); + + Status = NbtReadLinkageInformation ( + BindName, + LinkageHandle, + pBindDevices, + (PLONG)&pConfig->uNumDevices); + + if ( Status == STATUS_ILL_FORMED_SERVICE_ENTRY ) + { + CTEMemFree(pBindDevices); + CTEMemFree(pExportDevices); + pBindDevices = pExportDevices = NULL; + pConfig->uNumDevices = 0; + } + else + { + if (!NT_SUCCESS(Status)) + { + NbtLogEvent(EVENT_NBT_READ_BIND,Status); + return(Status); + } + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Binddevice = %ws\n",pBindDevices->Names[0].Buffer)); + + // Read the EXPORT information as well. + Status = NbtReadLinkageInformation ( + ExportName, + LinkageHandle, + pExportDevices, + &NumDevices); + + // we want the lowest number for num devices in case there + // are more bindings than exports or viceversa + // + pConfig->uNumDevices = (USHORT)( pConfig->uNumDevices > NumDevices ? + NumDevices : pConfig->uNumDevices); + + if (!NT_SUCCESS(Status) || (pConfig->uNumDevices == 0)) + { + NbtLogEvent(EVENT_NBT_READ_EXPORT,Status); + if (NT_SUCCESS(Status)) + { + Status = STATUS_UNSUCCESSFUL; + } + return(Status); + } + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Exportdevice = %ws\n",pExportDevices->Names[0].Buffer)); + + // + // read in the NameServer IP address now + // + Status = ReadNameServerAddresses(NbtConfigHandle, + pBindDevices, + pConfig->uNumDevices, + ppAddrArray); + + if (!NT_SUCCESS(Status)) + { + if (!(NodeType & BNODE)) + { + NbtLogEvent(EVENT_NBT_NAME_SERVER_ADDRS,Status); + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt: Failed to Read the name Server Addresses!!, status = %X\n", + Status)); + } + // + // we don't fail startup if we can't read the name + // server addresses + // + Status = STATUS_SUCCESS; + } + else + { + // + // check if any WINS servers have been configured change + // to Hnode + // + if (NodeType & (BNODE | DEFAULT_NODE_TYPE)) + { + ULONG i; + for (i=0;i<pConfig->uNumDevices ;i++ ) + { + if (((*ppAddrArray)[i].NameServerAddress != LOOP_BACK) || + ((*ppAddrArray)[i].BackupServer != LOOP_BACK)) + { + NodeType = MSNODE | (NodeType & PROXY); + break; + } + } + } + } + } + // + // we have done the check for default node so turn off + // the flag + // + NodeType &= ~DEFAULT_NODE_TYPE; + // + // A Bnode cannot be a proxy too + // + if (NodeType & BNODE) + { + if (NodeType & PROXY) + { + NodeType &= ~PROXY; + } + } + + // keep the size around for allocating memory, so that when we run over + // OSI, only this value should change (in theory at least) + pConfig->SizeTransportAddress = sizeof(TDI_ADDRESS_IP); + + // fill in the node type value that is put into all name service Pdus + // that go out identifying this node type + switch (NodeType & NODE_MASK) + { + case BNODE: + pConfig->PduNodeType = 0; + break; + case PNODE: + pConfig->PduNodeType = 1 << 13; + break; + case MNODE: + pConfig->PduNodeType = 1 << 14; + break; + case MSNODE: + pConfig->PduNodeType = 3 << 13; + break; + + } + + // read the name of the transport to bind to + // + Status = ReadElement(ParametersHandle, + WS_TRANSPORT_BIND_NAME, + &ucString); + if (!NT_SUCCESS(Status)) + { + NbtConfig.pTcpBindName = NBT_TCP_BIND_NAME; + Status = STATUS_SUCCESS; + StreamsStack = TRUE; + } + else + { + UNICODE_STRING StreamsString; + + // + // if there is already a bind string, free it before + // allocating another + // + if (NbtConfig.pTcpBindName) + { + CTEMemFree(NbtConfig.pTcpBindName); + } + NbtConfig.pTcpBindName = ucString.Buffer; + + // ********** REMOVE LATER *********** + RtlInitUnicodeString(&StreamsString,NBT_TCP_BIND_NAME); + if (RtlCompareUnicodeString(&ucString,&StreamsString,FALSE)) + StreamsStack = FALSE; + else + StreamsStack = TRUE; + + } + ZwClose(LinkageHandle); + ZwClose (NbtConfigHandle); + ZwClose(ParametersHandle); + + *ppExportDevices = pExportDevices; + *ppBindDevices = pBindDevices; + return(Status); + } + + CTEMemFree(pBindDevices); + + } + ZwClose(ParametersHandle); + } + else + NbtLogEvent(EVENT_NBT_OPEN_REG_PARAMS,OpenStatus); + + ZwClose(LinkageHandle); + } + + ZwClose (NbtConfigHandle); + + NbtLogEvent(EVENT_NBT_OPEN_REG_LINKAGE,OpenStatus); + + return STATUS_UNSUCCESSFUL; + + +} +//---------------------------------------------------------------------------- +NTSTATUS +ReadNameServerAddresses ( + IN HANDLE NbtConfigHandle, + IN tDEVICES *BindDevices, + IN ULONG NumberDevices, + OUT tADDRARRAY **ppAddrArray + ) + +/*++ + +Routine Description: + + This routine is called to read the name server addresses from the registry. + It stores them in a data structure that it allocates. This memory is + subsequently freed in driver.c when the devices have been created. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ +#define ADAPTER_SIZE_MAX 200 + + UNICODE_STRING ucString; + NTSTATUS status = STATUS_UNSUCCESSFUL; + HANDLE Handle; + LONG i,j,Len; + PWSTR pwsNameServer = L"NameServer"; + PWSTR pwsDhcpNameServer = L"DhcpNameServer"; + PWSTR pwsBackup = L"NameServerBackup"; + PWSTR pwsDhcpBackup = L"DhcpNameServerBackup"; + PWSTR pwsAdapter = L"Adapters\\"; + PWSTR BackSlash = L"\\"; + tADDRARRAY *pAddrArray; + ULONG LenAdapter; + + CTEPagedCode(); + + + // this is large enough for 100 characters of adapter name. + ucString.Buffer = NbtAllocMem(ADAPTER_SIZE_MAX,NBT_TAG('i')); + + *ppAddrArray = NULL; + if (!ucString.Buffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pAddrArray = NbtAllocMem(sizeof(tADDRARRAY)*NumberDevices,NBT_TAG('i')); + CTEZeroMemory(pAddrArray,sizeof(tADDRARRAY)*NumberDevices); + + if (!pAddrArray) + { + CTEMemFree(ucString.Buffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + *ppAddrArray = pAddrArray; + + // get the adapter name out of the Bind string, and use it to open + // a key by the same name, to get the name server addresses + // + for (i = 0;i < (LONG)NumberDevices ;i ++ ) + { + WCHAR *pBuffer; + + Len = BindDevices->Names[i].Length/sizeof(WCHAR); + Len--; + // + // start at the end a work backwards looking for a '\' + // + j = Len; + pBuffer = &BindDevices->Names[i].Buffer[j]; + while (j) + { + if (*pBuffer != *BackSlash) + { + j--; + pBuffer--; + } + else + break; + } + + // if we don't find a backslash or at least one + // character name then continue around again, or the name + // is longer than the buffer, then go to the next device in the + // bind list + // + if ((j == 0) || + (j == Len) || + (j == Len -1) || + ((Len - j) > ADAPTER_SIZE_MAX)) + { + continue; + } + + // copy the string "Adapter\" to the buffer since the adapters all + // appear under this key in the registery + // + + LenAdapter = wcslen(pwsAdapter); + CTEMemCopy(ucString.Buffer, + pwsAdapter, + LenAdapter*sizeof(WCHAR)); + // copy just the adapter name from the Bind string, since that is + // the name of the key to open to find the name server ip addresses + // + CTEMemCopy(&ucString.Buffer[LenAdapter], + ++pBuffer, + (Len - j)*sizeof(WCHAR)); + + ucString.Buffer[Len - j + LenAdapter] = 0; + + status = NbtOpenRegistry( + NbtConfigHandle, + ucString.Buffer, + &Handle); + + pAddrArray->NameServerAddress = LOOP_BACK; + pAddrArray->BackupServer = LOOP_BACK; + + if (NT_SUCCESS(status)) + { + status = GetServerAddress(Handle, + pwsNameServer, + &pAddrArray->NameServerAddress); + // + // If there is no WINS addres in the registry or the address is + // null, which we map to Loop_Back, then try the Dhcp keys to see + // if Dhcp has written a value. + // + if (!NT_SUCCESS(status) || + (pAddrArray->NameServerAddress == LOOP_BACK)) + { + status = GetServerAddress(Handle, + pwsDhcpNameServer, + &pAddrArray->NameServerAddress); + + status = GetServerAddress(Handle, + pwsDhcpBackup, + &pAddrArray->BackupServer); + } + else + { + status = GetServerAddress(Handle, + pwsBackup, + &pAddrArray->BackupServer); + + } + + // don't want to fail this routine just because the + // name server address was not set + status = STATUS_SUCCESS; + + ZwClose(Handle); + } + pAddrArray++; + + } + + CTEMemFree(ucString.Buffer); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetServerAddress ( + IN HANDLE ParametersHandle, + IN PWSTR KeyName, + OUT PULONG pIpAddr + ) + +/*++ + +Routine Description: + + This routine is called to read the name server addresses from the registry. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + ULONG IpAddr; + PUCHAR NameServer; + + CTEPagedCode(); + + status = CTEReadIniString(ParametersHandle,KeyName,&NameServer); + + if (NT_SUCCESS(status)) + { + status = ConvertDottedDecimalToUlong(NameServer,&IpAddr); + if (NT_SUCCESS(status) && IpAddr) + { + *pIpAddr = IpAddr; + } + else + { + if (IpAddr != 0) + { + NbtLogEvent(EVENT_NBT_BAD_PRIMARY_WINS_ADDR,0); + } + *pIpAddr = LOOP_BACK; + } + + CTEMemFree((PVOID)NameServer); + + + } + else + { + *pIpAddr = LOOP_BACK; + } + + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NbtAppendString ( + IN PWSTR FirstString, + IN PWSTR SecondString, + OUT PUNICODE_STRING pucString + ) + +/*++ + +Routine Description: + + This routine is called to append the second string to the first string. + It allocates memory for this, so the caller must be sure to free it. + +Arguments: + + +Return Value: + + None. + +--*/ +{ + NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; + ULONG Length; + PWSTR pDhcpKeyName; + + CTEPagedCode(); + + Length = (wcslen(FirstString) + wcslen(SecondString) + 1)*sizeof(WCHAR); + pDhcpKeyName = NbtAllocMem(Length,NBT_TAG('i')); + if (pDhcpKeyName) + { + pucString->Buffer = pDhcpKeyName; + pucString->Length = (USHORT)0; + pucString->MaximumLength = (USHORT)Length; + pucString->Buffer[0] = UNICODE_NULL; + + status = RtlAppendUnicodeToString(pucString,FirstString); + if (NT_SUCCESS(status)) + { + status = RtlAppendUnicodeToString(pucString,SecondString); + if (NT_SUCCESS(status)) + { + return status; + } + } + CTEFreeMem(pDhcpKeyName); + + } + return(status); +} +//---------------------------------------------------------------------------- +NTSTATUS +NTReadIniString ( + IN HANDLE ParametersHandle, + IN PWSTR KeyName, + OUT PUCHAR *ppString + ) + +/*++ + +Routine Description: + + This routine is called to read a string of configuration information from + the registry. + +Arguments: + + ParametersHandle - handle to open key in registry + KeyName - key to read + ppString - returned string + +Return Value: + + None. + +--*/ +{ + UNICODE_STRING ucString; + STRING String; + NTSTATUS status; + PUCHAR pBuffer; + PWSTR Dhcp = L"Dhcp"; + + CTEPagedCode(); + // + // read in the Scope Id + // + status = ReadElement( + ParametersHandle, + KeyName, // Value to read + &ucString); // return value + + // + // if the key is not there or it is set to a null string try to read the + // dhcp key + // + if (!NT_SUCCESS(status) || (ucString.Length == 0)) + { + UNICODE_STRING String; + + // free the string allocated in ReadElement + if (NT_SUCCESS(status)) + { + CTEMemFree(ucString.Buffer); + } + // + // try to read a similar string that is prefixed with "DHCP" + // incase there is only the DHCP configuration information present + // and not overrides keys. + // + status = NbtAppendString(Dhcp,KeyName,&String); + if (NT_SUCCESS(status)) + { + status = ReadElement( + ParametersHandle, + String.Buffer, // Value to read + &ucString); // return value + + // free the buffer allocated in NbtAppendString + CTEFreeMem(String.Buffer); + } + } + // the scope must be less than + // 255-16 characters since the whole name is limited to 255 as per the + // RFC + // + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Nbt: ReadIniString = %ws\n",ucString.Buffer)); + + if (NT_SUCCESS(status)) + { + if ((ucString.Length > 0) && + (ucString.Length <= (255 - NETBIOS_NAME_SIZE)*sizeof(WCHAR))) + { + + pBuffer = NbtAllocMem(ucString.MaximumLength/sizeof(WCHAR),NBT_TAG('i')); + + if (pBuffer) + { + // convert to an ascii string and store in the config data structure + // increment pBuffer to leave room for the length byte + // + String.Buffer = pBuffer; + String.MaximumLength = ucString.MaximumLength/sizeof(WCHAR); + status = RtlUnicodeStringToAnsiString(&String, + &ucString, + FALSE); + + if (NT_SUCCESS(status)) + { + *ppString = pBuffer; + } + else + { + CTEMemFree(pBuffer); + } + } + else + status = STATUS_UNSUCCESSFUL; + + + } + else + if (NT_SUCCESS(status)) + { + // force the code to setup a null scope since the one in the + // registry is null + // + status = STATUS_UNSUCCESSFUL; + } + + // free the string allocated in ReadElement + CTEMemFree(ucString.Buffer); + } + + + return(status); +} + +VOID +NbtFreeRegistryInfo ( + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to free any storage that was allocated + by NbConfigureTransport in producing the specified CONFIG_DATA structure. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + +} + +//---------------------------------------------------------------------------- +NTSTATUS +GetIPFromRegistry( + IN PUNICODE_STRING pucRegistryPath, + IN PUNICODE_STRING pucBindDevice, + OUT PULONG pulIpAddress, + OUT PULONG pulSubnetMask, + IN BOOL fWantDhcpAddresses + ) +/*++ + +Routine Description: + + This routine is called to get the IP address of an adapter from the + Registry. The Registry path variable contains the path name + for NBT's registry entries. The last element of this path name is + removed to give the path to any card in the registry. + + The BindDevice path contains a Bind string for NBT. We remove the last + element of this path (which is the adapter name \Elnkii01) and tack it + onto the modified registry path from above. We then tack on + \Parameters which will give the full path to the Tcpip key, which + we open to get the Ip address. + + +Arguments: + + pucRegistryPath - Supplies pucRegistryPath. The name of Nbt's node in the + registry. + + pNbtConfig - ptr to global configuration strucuture for NBT + +Return Value: + + NTSTATUS - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR pwsIpAddressName = ( fWantDhcpAddresses ? L"DhcpIPAddress" : L"IPAddress" ); // value name to read + PWSTR pwsSubnetMask = ( fWantDhcpAddresses ? L"DhcpSubnetMask" : L"SubnetMask" ); // value name to read + PWSTR TcpParams = L"\\Parameters\\Tcpip"; // key to open + ULONG Len; + ULONG iBindPathLength; + PVOID pBuffer; + NTSTATUS Status; + PWSTR pwsString; + UNICODE_STRING Path; + UNICODE_STRING ucIpAddress; + UNICODE_STRING ucSubnetMask; + + + CTEPagedCode(); + + // now find the last back slash in the path name to the bind device + NbtFindLastSlash(pucBindDevice,&pwsString,&iBindPathLength); + if (pwsString) + { + // get the length of the adapter name (+1 for unicode null) + // + Len = (wcslen(pwsString) + wcslen(TcpParams) + 1) * sizeof(WCHAR); + pBuffer = NbtAllocMem(Len,NBT_TAG('i')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + Path.Buffer = pBuffer; + Path.MaximumLength = (USHORT)Len; + Path.Length = 0; + + // put adapter name in the Path string + Status = RtlAppendUnicodeToString(&Path,pwsString); + + if (NT_SUCCESS(Status)) + { + // put Tcpip\parameters on the end of the adapter name + Status = RtlAppendUnicodeToString(&Path,TcpParams); + + if (NT_SUCCESS(Status)) + { + Status = ReadStringRelative(&NbtConfig.pRegistry, + Path.Buffer, + pwsIpAddressName, + &ucIpAddress); + + if (NT_SUCCESS(Status)) + { + Status = ConvertToUlong(&ucIpAddress,pulIpAddress); + + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Convert Ipaddr string= %ws,ulong = %X\n",ucIpAddress.Buffer,*pulIpAddress)); + + // free the unicode string buffers allocated when the data was read + // from the registry + // + CTEMemFree(ucIpAddress.Buffer); + + // + // DHCP may put a 0 Ip address in the registry - we don't want to + // boot netbt under these conditions. + // + if (*pulIpAddress == 0) + { + Status = STATUS_INVALID_ADDRESS; + } + + if (NT_SUCCESS(Status)) + { + // read the broadcast address in now + Status = ReadStringRelative(&NbtConfig.pRegistry, + Path.Buffer, + pwsSubnetMask, + &ucSubnetMask); + + if (NT_SUCCESS(Status)) + { + // we must convert the Subnet mask to a broadcast address... + Status = ConvertToUlong(&ucSubnetMask,pulSubnetMask); + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to convert dotted decimal SubnetMask to ULONG string= %ws\n", + ucIpAddress.Buffer)); + + Status = STATUS_INVALID_ADDRESS; + } + + CTEMemFree(ucSubnetMask.Buffer); + } + else + { + Status = STATUS_INVALID_ADDRESS; + } + } + else + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("Unable to convert dotted decimal IpAddress to ULONG string= %ws\n", + ucIpAddress.Buffer)); + + Status = STATUS_INVALID_ADDRESS; + } + + } + } + + + } + // + // free the string with the path to the adapter in it + // + CTEMemFree(pBuffer); + } + else + Status = STATUS_UNSUCCESSFUL; + + return Status; + +} // GetIPFromRegistry + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtOpenRegistry( + IN HANDLE NbConfigHandle, + IN PWSTR String, + OUT PHANDLE pHandle + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to open the registry. If the registry + tree for Nbt exists, then it opens it and returns TRUE. If not, it + creates the appropriate keys in the registry, opens it, and + returns FALSE. + + +Arguments: + + NbConfigHandle - this is the root handle which String is relative to + String - the name of the key to open below the root handle + pHandle - returns the handle to the String key. + +Return Value: + + The status of the request. + +--*/ +{ + + NTSTATUS Status; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + CTEPagedCode(); + // + // Open the Nbt key. + // + + RtlInitUnicodeString (&KeyName, String); + + InitializeObjectAttributes( + &TmpObjectAttributes, + &KeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + NbConfigHandle, // root + NULL // security descriptor + ); + + Status = ZwOpenKey( + pHandle, + KEY_READ, + &TmpObjectAttributes); + + + return Status; + +} /* NbOpenRegistry */ + + +//---------------------------------------------------------------------------- +NTSTATUS +NbtReadLinkageInformation( + IN PWSTR pName, + IN HANDLE LinkageHandle, + OUT tDEVICES *pDevices, // place to put read in config data + OUT LONG *pNumDevices + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to read its linkage information + from the registry. If there is none present, then ConfigData + is filled with a list of all the adapters that are known + to Nbt. + +Arguments: + + RegistryHandle - A pointer to the open registry. + +Return Value: + + Status + +--*/ + +{ + UNICODE_STRING BindString; + NTSTATUS RegistryStatus; + + PKEY_VALUE_FULL_INFORMATION BindValue; + ULONG BytesWritten; + USHORT ConfigBindings = 0; + PWSTR CurBindValue; + ULONG Count; + PVOID pBuffer; + + + CTEPagedCode(); + // + // We read the parameters out of the registry + // linkage key. + // + RegistryStatus = STATUS_BUFFER_OVERFLOW; + Count = 1; + while ((RegistryStatus == STATUS_BUFFER_OVERFLOW) && (Count < 20)) + { + pBuffer = NbtAllocMem(REGISTRY_BUFF_SIZE*Count,NBT_TAG('i')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + BindValue = (PKEY_VALUE_FULL_INFORMATION)pBuffer; + + // copy "Bind" or "Export" into the unicode string + RtlInitUnicodeString (&BindString, pName); + + RegistryStatus = ZwQueryValueKey( + LinkageHandle, + &BindString, // string to retrieve + KeyValueFullInformation, + (PVOID)BindValue, // returned info + REGISTRY_BUFF_SIZE*Count, + &BytesWritten // # of bytes returned + ); + Count++; + if (RegistryStatus == STATUS_BUFFER_OVERFLOW) + { + CTEMemFree(pBuffer); + } + } + + if (!NT_SUCCESS(RegistryStatus) || + (RegistryStatus == STATUS_BUFFER_OVERFLOW)) + { + CTEMemFree(pBuffer); + return RegistryStatus; + } + + if ( BytesWritten == 0 ) + { + CTEMemFree(pBuffer); + return STATUS_ILL_FORMED_SERVICE_ENTRY; + } + + + // allocate memory for the unicode strings, currently in BindValue + // on the stack + pDevices->RegistrySpace = (PVOID)NbtAllocMem((USHORT)BytesWritten,NBT_TAG('i')); + + if ( pDevices->RegistrySpace == NULL ) + { + CTEMemFree(pBuffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlMoveMemory((PVOID)pDevices->RegistrySpace, (PVOID)BindValue, BytesWritten); + + // Point to the permanent location for the strings + BindValue = (PKEY_VALUE_FULL_INFORMATION)pDevices->RegistrySpace; + + CurBindValue = (PWCHAR)((PUCHAR)BindValue + BindValue->DataOffset); + + while ((*CurBindValue != 0) && + (ConfigBindings < NBT_MAXIMUM_BINDINGS)) + { + + // this sets the buffer ptr in Names to point to CurBindValue, so + // this value must be real memory and not stack, hence the need + // to allocate memory above... + RtlInitUnicodeString (&pDevices->Names[ConfigBindings], + (PCWSTR)CurBindValue); + + ++ConfigBindings; + + // + // Now advance the "Bind" value. + // + + // wcslen => wide character string length for a unicode string + CurBindValue += wcslen((PCWSTR)CurBindValue) + 1; + + } + *pNumDevices = ConfigBindings; + + CTEMemFree(pBuffer); + return STATUS_SUCCESS; + +} /* NbtReadLinkageInformation */ + +//---------------------------------------------------------------------------- +ULONG +NbtReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue, + IN ULONG MinimumValue + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to read a single parameter + from the registry. If the parameter is found it is stored + in Data. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to search for. + + DefaultValue - The default value. + +Return Value: + + The value to use; will be the default if the value is not + found or is not in the correct range. + +--*/ + +{ + static ULONG InformationBuffer[60]; + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + UNICODE_STRING ValueKeyName; + ULONG InformationLength; + ULONG ReturnValue=DefaultValue; + NTSTATUS Status; + ULONG Count=2; + PWSTR Dhcp = L"Dhcp"; + BOOLEAN FreeString = FALSE; + + CTEPagedCode(); + RtlInitUnicodeString (&ValueKeyName, ValueName); + + while (Count--) + { + + Status = ZwQueryValueKey( + ParametersHandle, + &ValueKeyName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + + if ((Status == STATUS_SUCCESS) && (Information->DataLength == sizeof(ULONG))) + { + + RtlMoveMemory( + (PVOID)&ReturnValue, + ((PUCHAR)Information) + Information->DataOffset, + sizeof(ULONG)); + + if (ReturnValue < MinimumValue) + { + ReturnValue = MinimumValue; + } + + } + else + { + // + // try to read the Dhcp key instead if the first read failed. + // + Status = STATUS_SUCCESS; + if (Count) + { + Status = NbtAppendString(Dhcp,ValueName,&ValueKeyName); + } + + if (!NT_SUCCESS(Status)) + { + Count = 0; + ReturnValue = DefaultValue; + } + else + FreeString = TRUE; + + + } + } // of while + + // nbt append string allocates memory. + if (FreeString) + { + CTEMemFree(ValueKeyName.Buffer); + + } + return ReturnValue; + +} /* NbtReadSingleParameter */ + + +//---------------------------------------------------------------------------- +NTSTATUS +OpenAndReadElement( + IN PUNICODE_STRING pucRootPath, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ) +/*++ + +Routine Description: + + This routine is called by Nbt to read in the Ip address appearing in the + registry at the path pucRootPath, with a key of pwsKeyName + +Arguments: + pucRootPath - the registry path to the key to read + pwsKeyName - the key to open (i.e. Tcpip) + pwsValueName- the name of the value to read (i.e. IPAddress) + +Return Value: + + pucString - the string returns the string read from the registry + +--*/ + +{ + + NTSTATUS Status; + HANDLE hRootKey; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + CTEPagedCode(); + + InitializeObjectAttributes( + &TmpObjectAttributes, + pucRootPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwOpenKey( + &hRootKey, + KEY_READ, + &TmpObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + return STATUS_UNSUCCESSFUL; + } + + Status = ReadElement(hRootKey,pwsValueName,pucString); + + ZwClose (hRootKey); + return(Status); + +} +//---------------------------------------------------------------------------- +NTSTATUS +ReadElement( + IN HANDLE HandleToKey, + IN PWSTR pwsValueName, + OUT PUNICODE_STRING pucString + ) +/*++ + +Routine Description: + + This routine is will read a string value given by pwsValueName, under a + given Key (which must be open) - given by HandleToKey. This routine + allocates memory for the buffer in the returned pucString, so the caller + must deallocate that. + +Arguments: + + pwsValueName- the name of the value to read (i.e. IPAddress) + +Return Value: + + pucString - the string returns the string read from the registry + +--*/ + +{ + ULONG ReadStorage[150]; // 600 bytes + ULONG BytesRead; + NTSTATUS Status; + PWSTR pwsSrcString; + PKEY_VALUE_FULL_INFORMATION ReadValue = + (PKEY_VALUE_FULL_INFORMATION)ReadStorage; + + CTEPagedCode(); + + // now put the name of the value to read into a unicode string + RtlInitUnicodeString(pucString,pwsValueName); + + // this read the value of IPAddress under the key opened above + Status = ZwQueryValueKey( + HandleToKey, + pucString, // string to retrieve + KeyValueFullInformation, + (PVOID)ReadValue, // returned info + sizeof(ReadStorage), + &BytesRead // # of bytes returned + ); + + if ( Status == STATUS_BUFFER_OVERFLOW ) + { + ReadValue = (PKEY_VALUE_FULL_INFORMATION) NbtAllocMem( BytesRead, NBT_TAG('i')); + if ( ReadValue == NULL ) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("ReadElement: failed to allocate %d bytes for element\n",BytesRead)); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto ReadElement_Return; + } + Status = ZwQueryValueKey( + HandleToKey, + pucString, // string to retrieve + KeyValueFullInformation, + (PVOID)ReadValue, // returned info + BytesRead, + &BytesRead // # of bytes returned + ); + } + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_NTUTIL) + KdPrint(("failed to Query Value Status = %X\n",Status)); + goto ReadElement_Return; + } + + if ( BytesRead == 0 ) + { + Status = STATUS_ILL_FORMED_SERVICE_ENTRY; + goto ReadElement_Return; + } + else + if (ReadValue->DataLength == 0) + { + Status = STATUS_UNSUCCESSFUL; + goto ReadElement_Return; + } + + + // create the pucString and copy the data returned to it + // assumes that the ReadValue string ends in a UNICODE_NULL + //bStatus = RtlCreateUnicodeString(pucString,pwSrcString); + pwsSrcString = (PWSTR)NbtAllocMem((USHORT)ReadValue->DataLength,NBT_TAG('i')); + if (!pwsSrcString) + { + ASSERTMSG((PVOID)pwsSrcString, + (PCHAR)"Unable to allocate memory for a Unicode string"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // move the read in data from the stack to the memory allocated + // from the nonpaged pool + RtlMoveMemory( + (PVOID)pwsSrcString, + ((PUCHAR)ReadValue) + ReadValue->DataOffset, + ReadValue->DataLength); + + RtlInitUnicodeString(pucString,pwsSrcString); + // if there isn't a null on the end of the pwsSrcString, then + // it will not work correctly. - a null string comes out with a + // length of 1!! since the null is counted therefore use + // rtlinitunicode string afterall. + // pucString->MaximumLength = ReadValue->DataLength; + // pucString->Length = ReadValue->DataLength; + // pucString->Buffer = pwsSrcString; + } + +ReadElement_Return: + + if ( ( ReadValue != (PKEY_VALUE_FULL_INFORMATION)ReadStorage ) + && ( ReadValue != NULL ) ) + { + CTEFreeMem(ReadValue); + } + return(Status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTGetLmHostPath( + OUT PUCHAR *ppPath + ) +/*++ + +Routine Description: + + This routine will read the DataBasePath from under + ...\tcpip\parameters\databasepath + +Arguments: + + pPath - ptr to a buffer containing the path name. + +Return Value: + + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING ucDataBase; + STRING StringPath; + STRING LmhostsString; + ULONG StringMax; + PWSTR LmHosts = L"lmhosts"; + PWSTR TcpIpParams = L"TcpIp\\Parameters"; + PWSTR TcpParams = L"Tcp\\Parameters"; + PWSTR DataBase = L"DataBasePath"; + PCHAR ascLmhosts="\\lmhosts"; + PCHAR pBuffer; + + CTEPagedCode(); + + status = ReadStringRelative(&NbtConfig.pRegistry, + TcpIpParams, + DataBase, + &ucDataBase); + + if (!NT_SUCCESS(status)) + { + // check for the new TCP stack which a slightly different registry + // key name. + // + status = ReadStringRelative(&NbtConfig.pRegistry, + TcpParams, + DataBase, + &ucDataBase); + if (!NT_SUCCESS(status)) + { + return STATUS_UNSUCCESSFUL; + } + } + + + StringMax = ucDataBase.Length/sizeof(WCHAR) + strlen(ascLmhosts) + 1; + pBuffer = NbtAllocMem(StringMax,NBT_TAG('i')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + StringPath.Buffer = (PCHAR)pBuffer; + StringPath.MaximumLength = (USHORT)StringMax; + StringPath.Length = (USHORT)StringMax; + + // convert to ascii from unicode + status = RtlUnicodeStringToAnsiString(&StringPath,&ucDataBase,FALSE); + + // this memory was allocated in OpenAndReadElement + // + CTEMemFree(ucDataBase.Buffer); + + if (!NT_SUCCESS(status)) + { + return(STATUS_UNSUCCESSFUL); + } + + // now put the "\lmhosts" name on the end of the string + // + RtlInitString(&LmhostsString,ascLmhosts); + + status = RtlAppendStringToString(&StringPath,&LmhostsString); + + if (NT_SUCCESS(status)) + { + // + // is the first part of the directory "%SystemRoot%" ? + // + // If so, it must be changed to "\\SystemRoot\\". + // + // 0123456789 123456789 1 + // %SystemRoot%\somewhere + // + // + if (strncmp(StringPath.Buffer, "%SystemRoot%", 12) == 0) + { + + StringPath.Buffer[0] = '\\'; + StringPath.Buffer[11] = '\\'; + if (StringPath.Buffer[12] == '\\') + { + ASSERT(StringPath.Length >= 13); + + if (StringPath.Length > 13) + { + RtlMoveMemory( // overlapped copy + &(StringPath.Buffer[12]), // Destination + &(StringPath.Buffer[13]), // Source + (ULONG) StringPath.Length - 13); // Length + + StringPath.Buffer[StringPath.Length - 1] = (CHAR) NULL; + } + + StringPath.Length--; + } + } + *ppPath = (PCHAR)StringPath.Buffer; + } + else + *ppPath = NULL; + + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +ReadStringRelative( + IN PUNICODE_STRING pRegistryPath, + IN PWSTR pRelativePath, + IN PWSTR pValueName, + OUT PUNICODE_STRING pOutString + ) + +/*++ + +Routine Description: + + This routine reads a string from a registry key parallel to the + Netbt key - such as ..\tcpip\parameters\database + +Arguments: + + pRegistryPath = ptr to the Netbt Registry path + pRelativePath = path to value relative to same root as nbt. + pValueName = value to read + + + +Return Value: + + The length of the path up to and including the last slash and a ptr + to the first character of the last element of the string. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING RegistryPath; + UNICODE_STRING RelativePath; + ULONG StringMax; + PVOID pBuffer; + PWSTR pLastElement; + ULONG Length; + + CTEPagedCode(); + + StringMax = (pRegistryPath->MaximumLength + + wcslen(pRelativePath)*sizeof(WCHAR)+2); + // + // allocate some memory for the registry path so that it is large enough + // to append a string on to, for the relative key to be read + // + pBuffer = NbtAllocMem(StringMax,NBT_TAG('i')); + + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RegistryPath.MaximumLength = (USHORT)StringMax; + RegistryPath.Buffer = pBuffer; + RtlCopyUnicodeString(&RegistryPath,pRegistryPath); + + // + // find the last backslash and truncate the string + NbtFindLastSlash(&RegistryPath,&pLastElement,&Length); + RegistryPath.Length = (USHORT)Length; + + if (pLastElement) + { + *pLastElement = UNICODE_NULL; + + RtlInitUnicodeString(&RelativePath,pRelativePath); + + status = RtlAppendUnicodeStringToString(&RegistryPath,&RelativePath); + + if (NT_SUCCESS(status)) + { + status = OpenAndReadElement(&RegistryPath,pValueName,pOutString); + + if (NT_SUCCESS(status)) + { + // free the registry path + // + CTEMemFree(pBuffer); + return(status); + } + } + } + CTEMemFree(pBuffer); + + return(status); +} +//---------------------------------------------------------------------------- +VOID +NbtFindLastSlash( + IN PUNICODE_STRING pucRegistryPath, + OUT PWSTR *ppucLastElement, + IN int *piLength + ) + +/*++ + +Routine Description: + + This routine is called by Nbt to find the last slash in a registry + path name. + +Arguments: + + +Return Value: + + The length of the path up to and including the last slash and a ptr + to the first character of the last element of the string. + +--*/ + +{ + int i; + PWSTR pwsSlash = L"\\"; + int iStart; + + CTEPagedCode(); + + // search starting at the end of the string for the last back slash + iStart = wcslen(pucRegistryPath->Buffer)-1; + for(i=iStart;i>=0 ;i-- ) + { + if (pucRegistryPath->Buffer[i] == *pwsSlash) + { + if (i==pucRegistryPath->Length-1) + { + // name ends a back slash... this is an error + break; + } + // increase one to allow for the slash + *piLength = (i+1)*sizeof(WCHAR); + if (ppucLastElement != NULL) + { + // want ptr to point at character after the slash + *ppucLastElement = &pucRegistryPath->Buffer[i+1]; + } + return; + } + } + + // null the pointer if one is passed in + if (ppucLastElement != NULL) + { + *ppucLastElement = NULL; + } + *piLength = 0; + return; +} diff --git a/private/ntos/nbt/nt/sources.inc b/private/ntos/nbt/nt/sources.inc new file mode 100644 index 000000000..4fa44b266 --- /dev/null +++ b/private/ntos/nbt/nt/sources.inc @@ -0,0 +1,71 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nbt + + + +NTPROFILEINPUT=yes + +TARGETNAME=netbt +TARGETTYPE=DRIVER + +TARGETLIBS=$(TARGETLIBS) \ + $(BASEDIR)\public\sdk\lib\*\tdi.lib + +INCLUDES=..\..\inc;..\..\..\inc;..\..\..\..\inc +!if "$(DS_BUILD)" == "1" +INCLUDES=$(BASEDIR)\private\dsinc;$(INCLUDES) +DSINC=.. +!endif + +C_DEFINES=$(C_DEFINES) -DWIN32 -DPROXY_NODE -D_NTDRIVER_ -DRASAUTODIAL -D_PNP_POWER=1 -D_IO_DELETE_DEVICE_SUPPORTED + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + ..\netbt.rc \ + ..\ctestuff.c \ + ..\driver.c \ + ..\ntisol.c \ + ..\ntutil.c \ + ..\registry.c \ + ..\tdiaddr.c \ + ..\tdicnct.c \ + ..\tdihndlr.c \ + ..\fileio.c \ + ..\winsif.c \ + ..\tdiout.c \ + ..\ntpnp.c \ + ..\autodial.c + + +PRECOMPILED_INCLUDE=..\..\nbtprocs.h +PRECOMPILED_PCH=nbtprocs.pch +PRECOMPILED_OBJ=nbtprocs.obj diff --git a/private/ntos/nbt/nt/tdiaddr.c b/private/ntos/nbt/nt/tdiaddr.c new file mode 100644 index 000000000..18dbff62f --- /dev/null +++ b/private/ntos/nbt/nt/tdiaddr.c @@ -0,0 +1,672 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdihndlr.c + +Abstract: + + This file contains code relating to manipulation of address objects + that is specific to the NT operating system. It creates address endpoints + with the transport provider. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtTdiOpenAddress) +#pragma CTEMakePageable(PAGE, NbtTdiOpenControl) +#pragma CTEMakePageable(PAGE, SetEventHandler) +#pragma CTEMakePageable(PAGE, SubmitTdiRequest) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenAddress ( + OUT PHANDLE pHandle, + OUT PDEVICE_OBJECT *ppDeviceObject, + OUT PFILE_OBJECT *ppFileObject, + IN tDEVICECONTEXT *pDeviceContext, + IN USHORT PortNumber, + IN ULONG IpAddress, + IN ULONG Flags + ) +/*++ + +Routine Description: + + Note: This synchronous call may take a number of seconds. It runs in + the context of the caller. The code Opens an Address object with the + transport provider and then sets up event handlers for Receive, + Disconnect, Datagrams and Errors. + + THIS ROUTINE MUST BE CALLED IN THE CONTEXT OF THE FSP (I.E. + PROBABLY AN EXECUTIVE WORKER THREAD). + + The address data structures are found in tdi.h , but they are rather + confusing since the definitions have been spread across several data types. + This section shows the complete data type for Ip address: + + typedef struct + { + int TA_AddressCount; + struct _TA_ADDRESS + { + USHORT AddressType; + USHORT AddressLength; + struct _TDI_ADDRESS_IP + { + USHORT sin_port; + USHORT in_addr; + UCHAR sin_zero[8]; + } TDI_ADDRESS_IP + + } TA_ADDRESS[AddressCount]; + + } TRANSPORT_ADDRESS + + An EA buffer is allocated (for the IRP), with an EA name of "TransportAddress" + and value is a structure of type TRANSPORT_ADDRESS. + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + + + OBJECT_ATTRIBUTES AddressAttributes; + IO_STATUS_BLOCK IoStatusBlock; + PFILE_FULL_EA_INFORMATION EaBuffer; + NTSTATUS status; + PWSTR pNameTcp=L"Tcp"; + PWSTR pNameUdp=L"Udp"; + UNICODE_STRING ucDeviceName; + PTRANSPORT_ADDRESS pTransAddressEa; + PTRANSPORT_ADDRESS pTransAddr; + TDI_ADDRESS_IP IpAddr; + BOOLEAN Attached = FALSE; + PFILE_OBJECT pFileObject; + HANDLE FileHandle; + + CTEPagedCode(); + *ppFileObject = NULL; + *ppDeviceObject = NULL; + // copy device name into the unicode string - either Udp or Tcp + // + if (Flags & TCP_FLAG) + status = CreateDeviceString(pNameTcp,&ucDeviceName); + else + status = CreateDeviceString(pNameUdp,&ucDeviceName); + + if (!NT_SUCCESS(status)) + { + return(status); + } + EaBuffer = NbtAllocMem( + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + + sizeof(TRANSPORT_ADDRESS) + + sizeof(TDI_ADDRESS_IP),NBT_TAG('j')); + + if (EaBuffer == NULL) + { + ASSERTMSG( + (PCHAR)"Unable to get memory for an Eabuffer to open an address", + (PVOID)EaBuffer); + CTEMemFree(ucDeviceName.Buffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + + EaBuffer->EaValueLength = sizeof(TRANSPORT_ADDRESS) -1 + + sizeof(TDI_ADDRESS_IP); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("EaValueLength = %d\n",EaBuffer->EaValueLength)); + + // put "TransportAddress" into the name + // + RtlMoveMemory( + EaBuffer->EaName, + TdiTransportAddress, + EaBuffer->EaNameLength + 1); + + // fill in the IP address and Port number + // + pTransAddressEa = (TRANSPORT_ADDRESS *)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + + + // allocate Memory for the transport address + // + pTransAddr = NbtAllocMem( + sizeof(TDI_ADDRESS_IP)+sizeof(TRANSPORT_ADDRESS),NBT_TAG('k')); + + pTransAddr->TAAddressCount = 1; + pTransAddr->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP); + pTransAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; + + IpAddr.sin_port = htons(PortNumber); // put in network order + IpAddr.in_addr = htonl(IpAddress); + + // zero fill the last component of the IP address + // + RtlFillMemory((PVOID)&IpAddr.sin_zero, + sizeof(IpAddr.sin_zero), + 0); + + // copy the ip address to the end of the structure + // + RtlMoveMemory(pTransAddr->Address[0].Address, + (CONST PVOID)&IpAddr, + sizeof(IpAddr)); + + // copy the ip address to the end of the name in the EA structure + // + RtlMoveMemory((PVOID)pTransAddressEa, + (CONST PVOID)pTransAddr, + sizeof(TDI_ADDRESS_IP) + sizeof(TRANSPORT_ADDRESS)-1); + + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("creating Address named %ws\n",ucDeviceName.Buffer)); + + InitializeObjectAttributes( + &AddressAttributes, + &ucDeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = ZwCreateFile( + &FileHandle, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &AddressAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + 0, + (PVOID)EaBuffer, + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + EaBuffer->EaNameLength + 1 + + EaBuffer->EaValueLength); + + CTEMemFree((PVOID)pTransAddr); + CTEMemFree((PVOID)EaBuffer); + CTEMemFree(ucDeviceName.Buffer); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("NBT:Failed Create (address) File, status = %X\n",status )); + + if (NT_SUCCESS(status)) + { + // if the ZwCreate passed set the status to the IoStatus + status = IoStatusBlock.Status; + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to Open the Address to the transport, status = %X\n", + status)); + + return(status); + } + + // dereference the file object to keep the device ptr around to avoid + // this dereference at run time + // + status = ObReferenceObjectByHandle( + FileHandle, + (ULONG)0, + 0, + KernelMode, + (PVOID *)&pFileObject, + NULL); + + if (NT_SUCCESS(status)) + { + // return the handle to the caller + // + *pHandle = FileHandle; + // + // return the parameter to the caller + // + *ppFileObject = pFileObject; + + *ppDeviceObject = IoGetRelatedDeviceObject(*ppFileObject); + + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_ERROR, + (PVOID)TdiErrorHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + // if this is a TCP address being opened, then create different + // event handlers for connections + // + if (Flags & TCP_FLAG) + { + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_RECEIVE, + (PVOID)TdiReceiveHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_DISCONNECT, + (PVOID)TdiDisconnectHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + // only set a connect handler if the session flag is set. + // In this case the address being opened is the Netbios session + // port 139 + // + if (Flags & SESSION_FLAG) + { + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_CONNECT, + (PVOID)TdiConnectHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + return(status); + } + + } + else + return(status); + } + } + + + } + else + { + // Datagram ports only need this event handler + if (PortNumber == NBT_DATAGRAM_UDP_PORT) + { + // Datagram Udp Handler + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_RECEIVE_DATAGRAM, + (PVOID)TdiRcvDatagramHandler, + (PVOID)pDeviceContext); + if (NT_SUCCESS(status)) + { + return(status); + } + } + else + { + // Name Service Udp handler + status = SetEventHandler( + *ppDeviceObject, + *ppFileObject, + TDI_EVENT_RECEIVE_DATAGRAM, + (PVOID)TdiRcvNameSrvHandler, + (PVOID)pDeviceContext); + + if (NT_SUCCESS(status)) + { + return(status); + } + } + } + + // + // ERROR Case + // + ObDereferenceObject(pFileObject); + ZwClose(FileHandle); + + return(status); + } + + } + else + { + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("Failed Open Address (Dereference Object) status = %X\n", + status)); + + ZwClose(FileHandle); + } + + } + + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenControl ( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine opens a control object with the transport. It is very similar + to opening an address object, above. + +Arguments: + + + +Return Value: + + Status of the operation. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR pName=L"Tcp"; + PFILE_FULL_EA_INFORMATION EaBuffer; + UNICODE_STRING DeviceName; + BOOLEAN Attached = FALSE; + + + CTEPagedCode(); + // copy device name into the unicode string + Status = CreateDeviceString(pName,&DeviceName); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + InitializeObjectAttributes ( + &ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer)); + + EaBuffer = NULL; + + Status = ZwCreateFile ( + (PHANDLE)&pDeviceContext->hControl, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + NULL, // block size (unused). + FILE_ATTRIBUTE_NORMAL, // file attributes. + 0, + FILE_CREATE, + 0, // create options. + (PVOID)EaBuffer, // EA buffer. + 0); // Ea length + + + CTEMemFree(DeviceName.Buffer); + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint( ("OpenControl CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status)); + + if ( NT_SUCCESS( Status )) + { + // if the ZwCreate passed set the status to the IoStatus + Status = IoStatusBlock.Status; + + if (!NT_SUCCESS(Status)) + { + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("Nbt:Failed to Open the control connection to the transport, status = %X\n", + Status)); + + } + else + { + // get a reference to the file object and save it since we can't + // dereference a file handle at DPC level so we do it now and keep + // the ptr around for later. + Status = ObReferenceObjectByHandle( + pDeviceContext->hControl, + 0L, + NULL, + KernelMode, + (PVOID *)&pDeviceContext->pControlFileObject, + NULL); + + if (!NT_SUCCESS(Status)) + { + ZwClose(pDeviceContext->hControl); + } + else + pDeviceContext->pControlDeviceObject = + IoGetRelatedDeviceObject(pDeviceContext->pControlFileObject); + } + + } + else + { + KdPrint(("Nbt:Failed to Open the control connection to the transport, status1 = %X\n", + Status)); + + // set control file object ptr to null so we know that we didnot open + // the control point. + // + pDeviceContext->pControlFileObject = NULL; + } + + return Status; + +} /* NbtTdiOpenConnection */ + + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine does not complete the Irp. It is used to signal to a + synchronous part of the NBT driver that it can proceed (i.e. + to allow some code that is waiting on a "KeWaitForSingleObject" to + proceeed. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the event associated with the Irp. + +Return Value: + + The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops + processing Irp stack locations at this point. + +--*/ +{ + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint( ("Completion event: %X, Irp: %X, DeviceObject: %X\n", + Context, + Irp, + DeviceObject)); + + KeSetEvent((PKEVENT )Context, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); +} + +//---------------------------------------------------------------------------- +NTSTATUS +SetEventHandler ( + IN PDEVICE_OBJECT DeviceObject, + IN PFILE_OBJECT FileObject, + IN ULONG EventType, + IN PVOID EventHandler, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine registers an event handler with a TDI transport provider. + +Arguments: + + IN PDEVICE_OBJECT DeviceObject - Supplies the device object of the transport provider. + IN PFILE_OBJECT FileObject - Supplies the address object's file object. + IN ULONG EventType, - Supplies the type of event. + IN PVOID EventHandler - Supplies the event handler. + IN PVOID Context - Supplies the context passed into the event handler when it runs + +Return Value: + + NTSTATUS - Final status of the set event operation + +--*/ + +{ + NTSTATUS Status; + PIRP Irp; + + CTEPagedCode(); + Irp = IoAllocateIrp(IoGetRelatedDeviceObject(FileObject)->StackSize, FALSE); + + if (Irp == NULL) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + TdiBuildSetEventHandler(Irp, DeviceObject, FileObject, + NULL, NULL, + EventType, EventHandler, Context); + + Status = SubmitTdiRequest(FileObject, Irp); + + IoFreeIrp(Irp); + + return Status; +} + +//---------------------------------------------------------------------------- +NTSTATUS +SubmitTdiRequest ( + IN PFILE_OBJECT FileObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine submits a request to TDI and waits for it to complete. + +Arguments: + + IN PFILE_OBJECT FileObject - Connection or Address handle for TDI request + IN PIRP Irp - TDI request to submit. + +Return Value: + + NTSTATUS - Final status of request. + +--*/ + +{ + KEVENT Event; + NTSTATUS Status; + + + CTEPagedCode(); + KeInitializeEvent (&Event, NotificationEvent, FALSE); + + // set the address of the routine to be executed when the IRP + // finishes. This routine signals the event and allows the code + // below to continue (i.e. KeWaitForSingleObject) + // + IoSetCompletionRoutine(Irp, + (PIO_COMPLETION_ROUTINE)CompletionRoutine, + (PVOID)&Event, + TRUE, TRUE, TRUE); + + CHECK_COMPLETION(Irp); + Status = IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp); + + // + // If it failed immediately, return now, otherwise wait. + // + + if (!NT_SUCCESS(Status)) + { + KdPrint(("Failed to Submit Tdi Request, status = %X\n",Status)); + return Status; + } + + if (Status == STATUS_PENDING) + { + + Status = KeWaitForSingleObject((PVOID)&Event, // Object to wait on. + Executive, // Reason for waiting + KernelMode, // Processor mode + FALSE, // Alertable + NULL); // Timeout + + if (!NT_SUCCESS(Status)) + { + KdPrint(("Failed on return from KeWaitForSingleObj in Set Event Handler, status = %X\n", + Status)); + return Status; + } + + Status = Irp->IoStatus.Status; + + IF_DBG(NBT_DEBUG_TDIADDR) + KdPrint(("Io Status from setting event = %X\n",Status)); + } + + return(Status); +} + + + diff --git a/private/ntos/nbt/nt/tdicnct.c b/private/ntos/nbt/nt/tdicnct.c new file mode 100644 index 000000000..3ff5610e4 --- /dev/null +++ b/private/ntos/nbt/nt/tdicnct.c @@ -0,0 +1,530 @@ +// +// +// NBTCONNCT.C +// +// This file contains code relating to opening connections with the transport +// provider. The Code is NT specific. + +#include "nbtprocs.h" + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGE, NbtTdiOpenConnection) +#pragma CTEMakePageable(PAGE, NbtTdiAssociateConnection) +#pragma CTEMakePageable(PAGE, TdiOpenandAssocConnection) +#pragma CTEMakePageable(PAGE, NbtTdiCloseConnection) +#pragma CTEMakePageable(PAGE, CreateDeviceString) +#pragma CTEMakePageable(PAGE, NbtTdiCloseAddress) +#endif +//******************* Pageable Routine Declarations **************** + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiOpenConnection ( + IN tLOWERCONNECTION *pLowerConn, + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine opens a connection with the transport provider. + +Arguments: + + pLowerConn - Pointer to where the handle to the Transport for this virtual + connection should be stored. + + pNbtConfig - the name of the adapter to connect to is in this structure + +Return Value: + + Status of the operation. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PWSTR pName=L"Tcp"; + PFILE_FULL_EA_INFORMATION EaBuffer; + UNICODE_STRING DeviceName; + PMDL pMdl; + PVOID pBuffer; + BOOLEAN Attached = FALSE; + + CTEPagedCode(); + // zero out the connection data structure + CTEZeroMemory(pLowerConn,sizeof(tLOWERCONNECTION)); + pLowerConn->State = NBT_IDLE; + pLowerConn->pDeviceContext = pDeviceContext; + pLowerConn->RefCount = 1; + pLowerConn->LockNumber = LOWERCON_LOCK; + pLowerConn->Verify = NBT_VERIFY_LOWERCONN; + + Status = CreateDeviceString(pName,&DeviceName); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + // + // Allocate an MDL for the Indication buffer since we may need to buffer + // up to 128 bytes + // + pBuffer = NbtAllocMem(NBT_INDICATE_BUFFER_SIZE,NBT_TAG('l')); + + if (!pBuffer) + { + CTEMemFree(DeviceName.Buffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pMdl = IoAllocateMdl(pBuffer,NBT_INDICATE_BUFFER_SIZE,FALSE,FALSE,NULL); + + if (pMdl) + { + + MmBuildMdlForNonPagedPool(pMdl); + + pLowerConn->pIndicateMdl = pMdl; + + + InitializeObjectAttributes ( + &ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + + IF_DBG(NBT_DEBUG_TDICNCT) + KdPrint(("tcp device to open = %ws\n",DeviceName.Buffer)); + + // Allocate memory for the address info to be passed to the transport + EaBuffer = (PFILE_FULL_EA_INFORMATION)NbtAllocMem ( + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_CONNECTION_CONTEXT_LENGTH + 1 + + sizeof(CONNECTION_CONTEXT),NBT_TAG('m')); + + if (EaBuffer) + { + + EaBuffer->NextEntryOffset = 0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; + EaBuffer->EaValueLength = sizeof (CONNECTION_CONTEXT); + + // TdiConnectionContext is a macro that = "ConnectionContext" - so move + // this text to EaName + RtlMoveMemory( EaBuffer->EaName, TdiConnectionContext, EaBuffer->EaNameLength + 1 ); + + // put the context value into the EaBuffer too - i.e. the value that the + // transport returns with each indication on this connection + RtlMoveMemory ( + (PVOID)&EaBuffer->EaName[EaBuffer->EaNameLength + 1], + (CONST PVOID)&pLowerConn, + sizeof (CONNECTION_CONTEXT)); + + { + + Status = ZwCreateFile ( + &pLowerConn->FileHandle, + GENERIC_READ | GENERIC_WRITE, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + NULL, // block size (unused). + FILE_ATTRIBUTE_NORMAL, // file attributes. + 0, + FILE_CREATE, + 0, // create options. + (PVOID)EaBuffer, // EA buffer. + sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_CONNECTION_CONTEXT_LENGTH + 1 + + sizeof(CONNECTION_CONTEXT) + ); + } + + IF_DBG(NBT_DEBUG_TDICNCT) + KdPrint( ("OpenConnection CreateFile Status:%X, IoStatus:%X\n", Status, IoStatusBlock.Status)); + + CTEMemFree((PVOID)EaBuffer); + + if ( NT_SUCCESS( Status )) + { + + // if the ZwCreate passed set the status to the IoStatus + // + Status = IoStatusBlock.Status; + + if (NT_SUCCESS(Status)) + { + // get a reference to the file object and save it since we can't + // dereference a file handle at DPC level so we do it now and keep + // the ptr around for later. + Status = ObReferenceObjectByHandle( + pLowerConn->FileHandle, + 0L, + NULL, + KernelMode, + (PVOID *)&pLowerConn->pFileObject, + NULL); + + if (NT_SUCCESS(Status)) + { + CTEMemFree(DeviceName.Buffer); + return(Status); + } + + ZwClose(pLowerConn->FileHandle); + + } + + } + + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + + IoFreeMdl(pMdl); + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + CTEMemFree(pBuffer); + CTEMemFree(DeviceName.Buffer); + + return Status; + +} /* NbtTdiOpenConnection */ + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiAssociateConnection( + IN PFILE_OBJECT pFileObject, + IN HANDLE Handle + ) +/*++ + +Routine Description: + + This routine associates an open connection with the address object. + +Arguments: + + + pFileObject - the connection file object + Handle - the address object to associate the connection with + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pIrp; + KEVENT Event; + BOOLEAN Attached = FALSE; + + CTEPagedCode(); + + KeInitializeEvent( + &Event, + SynchronizationEvent, + FALSE); + + pIrp = NTAllocateNbtIrp(IoGetRelatedDeviceObject(pFileObject)); + + if (!pIrp) + { + KdPrint(("NBT:Failed to build internal device Irp\n")); + return(STATUS_UNSUCCESSFUL); + } + + TdiBuildAssociateAddress ( + pIrp, + pFileObject->DeviceObject, + pFileObject, + CompletionRoutine, + &Event, + Handle); + + status = SubmitTdiRequest(pFileObject,pIrp); + + IoFreeIrp(pIrp); + + return status; + + +} +//---------------------------------------------------------------------------- +NTSTATUS +CreateDeviceString( + IN PWSTR AppendingString, + IN OUT PUNICODE_STRING pucDeviceName + ) +/*++ + +Routine Description: + + This routine creates a string name for the transport device such as + "\Device\Streams\Tcp" + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + ULONG Len; + PVOID pBuffer; + + CTEPagedCode(); + // copy device name into the unicode string - either Udp or Tcp + // + Len = (wcslen(NbtConfig.pTcpBindName) + wcslen(AppendingString) + 1) * sizeof(WCHAR); + + pBuffer = NbtAllocMem(Len,NBT_TAG('n')); + if (!pBuffer) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pucDeviceName->MaximumLength = (USHORT)Len; + pucDeviceName->Length = 0; + pucDeviceName->Buffer = pBuffer; + + // this puts \Device\Streams into the string + // + status = RtlAppendUnicodeToString(pucDeviceName,NbtConfig.pTcpBindName); + if (NT_SUCCESS(status)) + { + status = RtlAppendUnicodeToString (pucDeviceName,AppendingString); + } + else + CTEMemFree(pBuffer); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiOpenandAssocConnection( + IN tCONNECTELE *pConnEle, + IN tDEVICECONTEXT *pDeviceContext, + IN ULONG PortNumber + ) +/*++ + +Routine Description: + + This routine opens and associates an open connection. + + This routine is called with the Spin Lock held on the pConnele. It is + released in this routine. + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + NTSTATUS Locstatus; + PDEVICE_OBJECT pDeviceObject; + tLOWERCONNECTION *pLowerConn; + BOOLEAN Attached=FALSE; + + CTEPagedCode(); + + CTEAttachFsp(&Attached); + + // allocate memory for the lower connection block. + // + pConnEle->pLowerConnId = (PVOID)NbtAllocMem(sizeof(tLOWERCONNECTION),NBT_TAG('o')); + + if (!pConnEle->pLowerConnId) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // fill in the lower connection element to point to the upper one and + // vice versa + // + pLowerConn = pConnEle->pLowerConnId; + + status = NbtTdiOpenConnection(pLowerConn,pDeviceContext); + if (!NT_SUCCESS(status)) + { + CTEDetachFsp(Attached); + CTEMemFree((PVOID)pConnEle->pLowerConnId); + pConnEle->pLowerConnId = NULL; + return(status); + } + + pLowerConn->pUpperConnection = pConnEle; + pLowerConn->State = NBT_IDLE; + + // + // until the correct state proc is set (i.e.Outbound), reject any data + // (in other words, don't let this field stay NULL!) + // + SetStateProc( pLowerConn, RejectAnyData ) ; + + + if (NT_SUCCESS(status)) + { + + // Open an address object (aka port) + // + status = NbtTdiOpenAddress( + &pLowerConn->AddrFileHandle, + &pDeviceObject, // dummy argument, not used here + &pLowerConn->pAddrFileObject, + pDeviceContext, + (USHORT)PortNumber, // port + pDeviceContext->IpAddress, + TCP_FLAG); + + if (NT_SUCCESS(status)) + { + // now associate the two + status = NbtTdiAssociateConnection( + pLowerConn->pFileObject, + pLowerConn->AddrFileHandle); + + + if (NT_SUCCESS(status)) + { + CTEDetachFsp(Attached); + // + // put the lower connection on the Q of active lower connections for + // this device + // + ExInterlockedInsertTailList(&pDeviceContext->LowerConnection, + &pLowerConn->Linkage, + &pDeviceContext->SpinLock); + + return(status); + } + + ObDereferenceObject(pLowerConn->pAddrFileObject); + Locstatus = ZwClose(pLowerConn->AddrFileHandle); + + } + KdPrint(("Nbt:Open Xport Address Failed, status %X\n",status)); + + ObDereferenceObject(pLowerConn->pFileObject); + Locstatus = ZwClose(pLowerConn->FileHandle); + + } + + CTEDetachFsp(Attached); + + // Error Path... delete memory + // + pConnEle->pLowerConnId = NULL; + CTEMemFree((PVOID)pLowerConn); + + return(status); + +} + +//---------------------------------------------------------------------------- + +NTSTATUS +NbtTdiCloseConnection( + IN tLOWERCONNECTION * pLowerConn + ) +/*++ + +Routine Description: + + This routine closes a TDI connection + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + BOOLEAN Attached= FALSE; + + CTEPagedCode(); + ASSERT( pLowerConn != NULL ) ; + + CTEAttachFsp(&Attached); + + if (pLowerConn->FileHandle) { + status = ZwClose(pLowerConn->FileHandle); + pLowerConn->FileHandle = NULL; + } + +#if DBG + if (!NT_SUCCESS(status)) + KdPrint(("Nbt:Failed to close Connection FileHandle pLower %X, status %X\n",pLowerConn,status)); +#endif + + CTEDetachFsp(Attached); + + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NbtTdiCloseAddress( + IN tLOWERCONNECTION * pLowerConn + ) +/*++ + +Routine Description: + + This routine closes a TDI address + +Arguments: + + +Return Value: + + Status of the operation. + +--*/ +{ + NTSTATUS status; + BOOLEAN Attached= FALSE; + + CTEPagedCode(); + + ASSERT( pLowerConn != NULL ) ; + + CTEAttachFsp(&Attached); + + status = ZwClose(pLowerConn->AddrFileHandle); +#if DBG + if (!NT_SUCCESS(status)) + KdPrint(("Nbt:Failed to close Address FileHandle pLower %X,status %X\n",pLowerConn,status)); +#endif + + CTEDetachFsp(Attached); + + return(status); + +} diff --git a/private/ntos/nbt/nt/tdihndlr.c b/private/ntos/nbt/nt/tdihndlr.c new file mode 100644 index 000000000..bcd515d97 --- /dev/null +++ b/private/ntos/nbt/nt/tdihndlr.c @@ -0,0 +1,6286 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdihndlr.c + +Abstract: + + + This file contains the TDI handlers that are setup for Connects, + Receives, Disconnects, and Errors on various objects such as connections + and udp endpoints . + + This file represents the inbound TDI interface on the Bottom of NBT. Therefore + the code basically decodes the incoming information and passes it to + a non-Os specific routine to do what it can. Upon return from that + routine additional Os specific work may need to be done. + + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + +#include "nbtprocs.h" +#include "ctemacro.h" + +// this macro checks that the types field is always zero in the Session +// Pdu +// +#if DBG +#define CHECK_PDU( _Size,_Offset) \ + if (_Size > 1) \ + ASSERT(((PUCHAR)pTsdu)[_Offset] == 0) +#else +#define CHECK_PDU( _Size,_Offset ) +#endif + +#if DBG +UCHAR pLocBuff[256]; +UCHAR CurrLoc; + +ULONG R1; +ULONG R2; +ULONG R3; +ULONG R4; + +ULONG C1; +ULONG C2; +ULONG C3; +ULONG C4; + + +#define INCR_COUNT(_Count) _Count++ +#else +#define INCR_COUNT(_Count) +#endif + + +// +// This ntohl swaps just three bytes, since the 4th byte could be a session +// keep alive message type. +// +__inline long +myntohl(long x) +{ + return((((x) >> 24) & 0x000000FFL) | + (((x) >> 8) & 0x0000FF00L) | + (((x) << 8) & 0x00FF0000L)); +} + +NTSTATUS +LessThan4BytesRcvd( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + OUT PVOID *ppIrp + ); +NTSTATUS +ClientTookSomeOfTheData( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + IN ULONG BytesTaken, + IN ULONG PduSize + ); +NTSTATUS +MoreDataRcvdThanNeeded( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ); +NTSTATUS +NotEnoughDataYet( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN ULONG PduSize, + OUT PVOID *ppIrp + ); +NTSTATUS +ProcessIrp( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrp, + IN PVOID pBuffer, + IN PULONG BytesTaken, + IN ULONG BytesIndicted, + IN ULONG BytesAvailable + ); + +NTSTATUS +NtBuildIndicateForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ); + +NTSTATUS +AcceptCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +VOID +DpcNextOutOfRsrcKill( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +DpcGetRestOfIndication( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +ClientBufferOverFlow( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG BytesRcvd + ); +VOID +DpcHandleNewSessionPdu ( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); +VOID +HandleNewSessionPdu ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Offset, + IN ULONG ToGet + ); +NTSTATUS +NewSessionCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +BuildIrpForNewSessionInIndication ( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrpIn, + IN ULONG BytesAvailable, + IN ULONG RemainingPdu, + OUT PIRP *ppIrp + ); +VOID +TrackIndicatedBytes( + IN ULONG BytesIndicated, + IN ULONG BytesTaken, + IN tCONNECTELE *pConnEle + ); + +__inline +VOID +DerefLowerConnFast ( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN CTELockHandle OldIrq + ); + +NTSTATUS +CopyDataandIndicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ); + +VOID +SumMdlLengths ( + IN PMDL pMdl, + IN ULONG BytesAvailable, + IN tCONNECTELE *pConnectEle + ); + + + +NTSTATUS +RsrcKillCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +VOID +FillIrpCancelRoutine( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); + +NTSTATUS +NameSrvCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); +//---------------------------------------------------------------------------- +__inline +NTSTATUS +Normal( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + ASSERTMSG("Should not execute this procedure",0); + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +LessThan4BytesRcvd( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + NTSTATUS status; + + // for short indications less than 4 bytes we can't determine + // the pdu size so just get the header first then get the + // whole pdu next. + + status = NtBuildIrpForReceive(pLowerConn, + sizeof(tSESSIONHDR), + (PVOID *)ppIrp); + + pConnectEle = pLowerConn->pUpperConnection; + + pConnectEle->BytesInXport = BytesAvailable; + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return( STATUS_DATA_NOT_ACCEPTED); + } + // + // set the irp mdl length to size of session hdr so that + // we don't get more than one session pdu into the buffer + // + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + *BytesTaken = 0; + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to Ind Buff(<4 bytes), Avail = %X\n", + BytesAvailable)); + + PUSH_LOCATION(0); + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +ClientTookSomeOfTheData( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + IN ULONG BytesTaken, + IN ULONG PduSize + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + the client has not taken all of the data indicated to it. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + + // + // took some of the data, so keep track of the + // rest of the data left here by going to the PARTIALRCV + // state. + // + PUSH_LOCATION(0x5); + + pLowerConn->StateRcv = PARTIAL_RCV; + pLowerConn->CurrentStateProc = PartialRcv; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switch to Partial Rcv Indicated=%X, PduSize=%X\n", + BytesIndicated,PduSize-4)); + + // Note: PduSize must include the 4 byte session header for this to + // work correctly. + // + pConnectEle = pLowerConn->pUpperConnection; + // + // We always indicate the whole Pdu size to the client, so the amount + // indicated is that minus what was taken - typically the 4 byte + // session hdr + // + pConnectEle->ReceiveIndicated = PduSize - BytesTaken; + ASSERT(pConnectEle->ReceiveIndicated <= 0x20000); + + // amount left in the transport... + pConnectEle->BytesInXport = BytesAvailable - BytesTaken; + + // need to return this status since we took the 4 bytes + // session header at least, even if the client took none. + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +MoreDataRcvdThanNeeded( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + ULONG Length; + ULONG Remaining; + ULONG PduSize; + NTSTATUS status; + tSESSIONHDR UNALIGNED *pSessionHdr; + + + PUSH_LOCATION(0x6); + // + // there is too much data, so keep track of the + // fact that there is data left in the transport + // and get it with the indicate buffer + // + pLowerConn->StateRcv = INDICATE_BUFFER; + + + ASSERT(pLowerConn->BytesInIndicate == 0); +#if DBG + if (pLowerConn->BytesInIndicate) + { + KdPrint(("Nbt:Bytes in indicate should be ZERO, but are = %X\n", + pLowerConn->BytesInIndicate)); + } +#endif + pLowerConn->CurrentStateProc = IndicateBuffer; + pConnectEle = pLowerConn->pUpperConnection; + + pConnectEle->BytesInXport = BytesAvailable - *BytesTaken; + + // + // for short indications less than 4 bytes we can't determine + // the pdu size so just get the header first then get the + // whole pdu next. + // + Remaining = BytesIndicated - *BytesTaken; + if (Remaining < sizeof(tSESSIONHDR)) + { + status = NtBuildIrpForReceive(pLowerConn,sizeof(tSESSIONHDR),(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + // this is a serious error - we must + // kill of the connection and let the + // redirector restart it + KdPrint(("Nbt:Unable to get an Irp for RCv - Closing Connection!! %X\n",pLowerConn)); + CTESpinFreeAtDpc(pLowerConn); + + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_DATA_NOT_ACCEPTED); + } + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:< 4 Bytes,BytesTaken=%X,Avail=%X,Ind=%X,Remain=%X\n", + *BytesTaken,BytesAvailable,BytesIndicated, + Remaining)); + + // DEBUG + CTEZeroMemory(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl), + NBT_INDICATE_BUFFER_SIZE); + + PUSH_LOCATION(0x7); + status = STATUS_MORE_PROCESSING_REQUIRED; + } + else + { + // if we get to here there are enough bytes left to determine + // the next pdu size...so we can determine how much + // data to get for the indicate buffer + // + pSessionHdr = (tSESSIONHDR UNALIGNED *)((PUCHAR)pTsdu + *BytesTaken); + + PduSize = myntohl(pSessionHdr->UlongLength) + sizeof(tSESSIONHDR); + + + Length = (PduSize > NBT_INDICATE_BUFFER_SIZE) ? + NBT_INDICATE_BUFFER_SIZE : PduSize; + + // + // The NewSessionCompletion routine recalculates + // what is left in the transport when the + // irp completes + // + status = NtBuildIrpForReceive(pLowerConn,Length,(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + // this is a serious error - we must + // kill of the connection and let the + // redirector restart it + KdPrint(("Nbt:Unable to get an Irp for RCV(2) - Closing Connection!! %X\n",pLowerConn)); + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return(STATUS_DATA_NOT_ACCEPTED); + } + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switch to Ind Buff, InXport = %X, Pdusize=%X,ToGet=%X\n", + pConnectEle->BytesInXport,PduSize-4,Length)); + + } + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NotEnoughDataYet( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN ULONG PduSize, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles the case when data has arrived on a connection but + there isn't 128 bytes yet or a whole pdu. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tCONNECTELE *pConnectEle; + ULONG Length; + + PUSH_LOCATION(0x9); + // + // not enough data indicated, so use the indicate buffer + // + Length = (PduSize > NBT_INDICATE_BUFFER_SIZE) ? + NBT_INDICATE_BUFFER_SIZE : PduSize; + + status = NtBuildIrpForReceive(pLowerConn,Length,(PVOID *)ppIrp); + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + return(STATUS_DATA_NOT_ACCEPTED); + } + + *BytesTaken = 0; + + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + pConnectEle = pLowerConn->pUpperConnection; + pConnectEle->BytesInXport = BytesAvailable; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Not Enough data indicated in Tdihndlr, using indic. buffer Indicated = %X,Avail=%X,PduSize= %X\n", + BytesIndicated, BytesAvailable,PduSize-4)); + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +FillIrp( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + ASSERTMSG("Should not execute this procedure",0); + return(STATUS_SUCCESS); + // do nothing + +} +//---------------------------------------------------------------------------- +NTSTATUS +IndicateBuffer( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine handles reception of data while in the IndicateBuffer state. + In this state the indicate buffer is receiveing data until at least + 128 bytes have been receive, or a whole pdu has been received. + + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + NTSTATUS status; + ULONG PduSize; + ULONG ToCopy; + PVOID pIndicateBuffer; + ULONG Taken; + + // + // there is data in the indicate buffer and we got a new + // indication, so copy some or all of the indication to the + // indicate buffer + // + PVOID pDest; + ULONG RemainPdu; + ULONG SpaceLeft; + ULONG TotalBytes; + ULONG ToCopy1=0; + + INCR_COUNT(R3); + PUSH_LOCATION(0xe); + pConnectEle = pLowerConn->pUpperConnection; + ASSERT(pLowerConn->StateRcv == INDICATE_BUFFER); + // + // The indicate buffer always starts with a pdu + // + pIndicateBuffer = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + // the location to start copying the new data into is right + // after the existing data in the buffer + // + pDest = (PVOID)((PUCHAR)pIndicateBuffer + pLowerConn->BytesInIndicate); + + // + // the session header may not be all into the indicate + // buffer yet, so check that before getting the pdu length. + // + if (pLowerConn->BytesInIndicate < sizeof(tSESSIONHDR)) + { + PUSH_LOCATION(0xe); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Too Few in Indicate Buff, Adding InIndicate %X\n", + pLowerConn->BytesInIndicate)); + + ToCopy1 = sizeof(tSESSIONHDR) - pLowerConn->BytesInIndicate; + if (ToCopy1 > BytesIndicated) + { + ToCopy1 = BytesIndicated; + } + CTEMemCopy(pDest,pTsdu,ToCopy1); + + pDest = (PVOID)((PUCHAR)pDest + ToCopy1); + pTsdu = (PVOID)((PUCHAR)pTsdu + ToCopy1); + + pLowerConn->BytesInIndicate += (USHORT)ToCopy1; + + *BytesTaken = ToCopy1; + } + + // now check again, and pass down an irp to get more data if necessary + // + if (pLowerConn->BytesInIndicate < sizeof(tSESSIONHDR)) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:< 4 Bytes in IndicBuff, BytesinInd= %X, BytesIndicated=%x\n", + pLowerConn->BytesInIndicate,BytesIndicated)); + + PUSH_LOCATION(0xF); + + // + // the data left in the transport is what was Available + // minus what we just copied to the indicate buffer + // + pConnectEle->BytesInXport = BytesAvailable - ToCopy1; + + if (pConnectEle->BytesInXport) + { + PUSH_LOCATION(0x10); + // + // pass the indicate buffer down to get some more data + // to fill out to the end of the session hdr + // + NtBuildIndicateForReceive(pLowerConn, + sizeof(tSESSIONHDR)-pLowerConn->BytesInIndicate, + (PVOID *)ppIrp); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:INDIC_BUF...need more data for hdr Avail= %X, InXport = %X\n", + BytesAvailable,pConnectEle->BytesInXport,pLowerConn)); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // if we get to here there isn't 4 bytes in the indicate buffer and + // there is no more data in the Transport, so just wait for the next + // indication. + // + return(STATUS_SUCCESS); + } + + PduSize = myntohl(((tSESSIONHDR *)pIndicateBuffer)->UlongLength) + + sizeof(tSESSIONHDR); + + // copy up to 132 bytes or the whole pdu to the indicate buffer + // + RemainPdu = PduSize - pLowerConn->BytesInIndicate; + + SpaceLeft = NBT_INDICATE_BUFFER_SIZE - pLowerConn->BytesInIndicate; + + if (RemainPdu < SpaceLeft) + ToCopy = RemainPdu; + else + ToCopy = SpaceLeft; + + if (ToCopy > (BytesIndicated-ToCopy1)) + { + ToCopy = (BytesIndicated - ToCopy1); + } + + // + // Copy the indication or part of it to the indication + // buffer + // + CTEMemCopy(pDest,pTsdu,ToCopy); + + pLowerConn->BytesInIndicate += (USHORT)ToCopy; + + TotalBytes = pLowerConn->BytesInIndicate; + + // the amount of data taken is the amount copied to the + // indicate buffer + // + *BytesTaken = ToCopy + ToCopy1; + +#if DBG + { + tSESSIONHDR UNALIGNED *pSessionHdr; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pIndicateBuffer; + ASSERT((pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) || + (pSessionHdr->Type == NBT_SESSION_MESSAGE)); + } +#endif + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:INDIC_BUFF, TotalBytes= %X, InIndic=%X, Copied(0/1)= %X %X Avail %X\n", + TotalBytes,pLowerConn->BytesInIndicate,ToCopy,ToCopy1,BytesAvailable)); + + + // the data left in the transport is what was Available + // minus what we just copied to the indicate buffer + // + pConnectEle->BytesInXport = BytesAvailable - *BytesTaken; + + // now check if we have a whole pdu or 132 bytes, either way + // enough to indicate to the client. + // + ASSERT(TotalBytes <= NBT_INDICATE_BUFFER_SIZE); + + if ((TotalBytes == NBT_INDICATE_BUFFER_SIZE) || + (TotalBytes == PduSize)) + { + + status = CopyDataandIndicate( + ReceiveEventContext, + (PVOID)pLowerConn, + ReceiveFlags, + TotalBytes, + pConnectEle->BytesInXport + TotalBytes, + &Taken, + pIndicateBuffer, + (PIRP *)ppIrp); + + } + else + { + + // not enough data in the indicate buffer yet + // NOTE: *BytesTaken should be set correctly above... + // = ToCopy + ToCopy1; + + PUSH_LOCATION(0x11); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Not Enough data indicated(INDICBUFF state), Indicated = %X,PduSize= %X,InIndic=%X\n", + BytesIndicated, PduSize, pLowerConn->BytesInIndicate)); + + + status = STATUS_SUCCESS; + } + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +PartialRcv( + IN PVOID ReceiveEventContext, + IN tLOWERCONNECTION *pLowerConn, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + tCONNECTELE *pConnectEle; + // + // the data for the client may be in the indicate buffer and + // in this case the transport could indicate us with more data. Therefore + // track the number of bytes available in the transport which + // we will get when the client finally posts a buffer. + // This state could also happen on a zero length Rcv when the + // client does not accept the data, and later posts a rcv + // buffer for the zero length rcv. + // + INCR_COUNT(R4); + PUSH_LOCATION(0x13); + ASSERT(pLowerConn->StateRcv == PARTIAL_RCV); + pConnectEle = pLowerConn->pUpperConnection; + +// ASSERT(pConnectEle->BytesInXport == 0); +#if DBG + if (pConnectEle->BytesInXport != 0) + { + KdPrint(("Netbt!PartialRcv: pConnectEle->BytesInXport != 0 Avail %X, InIndicate=%X,InXport %X %X\n", + BytesAvailable,pLowerConn->BytesInIndicate, + pConnectEle->BytesInXport,pLowerConn)); + } +#endif // DBG + pConnectEle->BytesInXport = BytesAvailable; + + IF_DBG(NBT_DEBUG_NAMESRV) + KdPrint(("Nbt:Got Indicated while in PartialRcv state Avail %X, InIndicate=%X,InXport %X %X\n", + BytesAvailable,pLowerConn->BytesInIndicate, + pConnectEle->BytesInXport,pLowerConn)); + + *BytesTaken = 0; + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiReceiveHandler ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network. It calls + a non OS specific routine to decide what to do. That routine passes back + either a RcvElement (buffer) or a client rcv handler to call. + +Arguments: + + IN PVOID ReceiveEventContext - Context provided for this event when event set + IN PVOID ConnectionContext - Connection Context, (pLowerConnection) + IN USHORT ReceiveFlags - Flags describing the message + IN ULONG BytesIndicated - Number of bytes available at indication time + IN ULONG BytesAvailable - Number of bytes available to receive + OUT PULONG BytesTaken - Number of bytes consumed by redirector. + IN PVOID pTsdu - Data from remote machine. + OUT PIRP *ppIrp - I/O request packet filled in if received data + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ + +{ + register tLOWERCONNECTION *pLowerConn; + PIRP pIrp; + CTELockHandle OldIrq; + NTSTATUS status; + tCONNECTELE *pConnEle; + ULONG BTaken; + + *ppIrp = NULL; + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + + // NOTE: + // Access is synchronized through the spin lock on pLowerConn for all + // Session related stuff. This includes the case where the client + // posts another Rcv Buffer in NTReceive. - so there is no need to get the + // pConnEle Spin lock too. + // + + CTESpinLock(pLowerConn,OldIrq); +// pLowerConn->InRcvHandler = TRUE; + pLowerConn->RefCount++; + + // save this on the stack in case we need to dereference it below. + pConnEle = pLowerConn->pUpperConnection; + + // call the correct routine depending on the state of the connection + // Normal/FillIrp/PartialRcv/IndicateBuffer/Inbound/OutBound + // + if ((pLowerConn->State == NBT_SESSION_UP) && + (pLowerConn->StateRcv == FILL_IRP)) + { + PIO_STACK_LOCATION pIrpSp; + PMDL pNewMdl; + PFILE_OBJECT pFileObject; + ULONG RemainingPdu; + PVOID NewAddress; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + KIRQL OldIrq2; + ULONG RcvLength; + + + PUSH_LOCATION(0xa); + // we are still waiting for the rest of the session pdu so + // do not call the RcvHandlrNotOs, since we already have the buffer + // to put this data in. + // too much data may have arrived... i.e. part of the next session pdu.. + // so check and set the receive length accordingly + // + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + RcvLength = RemainingPdu; + // + // try high runner case first + // + if (BytesAvailable <= RemainingPdu) + { + PUSH_LOCATION(0xb); + // + // if the client buffer is too small to take all of the rest of the + // data, shorten the receive length and keep track of how many + // bytes are left in the transport. ReceiveIndicated should have + // been set when the irp was passed down originally. + // + if (BytesAvailable > pConnEle->FreeBytesInMdl) + { + + PUSH_LOCATION(0xb); + + RcvLength = pConnEle->FreeBytesInMdl; + pConnEle->BytesInXport = BytesAvailable - RcvLength; + } + } + else + { + // + // start of session pdu in the middle of the indication + // + PUSH_LOCATION(0xc); + // + // It is possible that the client buffer is too short, so check + // for that case. + // + if (RemainingPdu > pConnEle->FreeBytesInMdl) + { + RcvLength = pConnEle->FreeBytesInMdl; + PUSH_LOCATION(0xd); + } + /* Remember how much data is left in the transport + when this irp passes through the completionrcv routine + it will pass the indication buffer back to the transport + to get at least 4 bytes of header information so we + can determine the next session pdu's size before receiving + it. The trick is to avoid having more than one session + pdu in the buffer at once. + */ + pConnEle->BytesInXport = BytesAvailable - RcvLength; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:End of FILL_IRP, found new Pdu BytesInXport=%X\n", + pConnEle->BytesInXport)); + + + } + + pIrp = pConnEle->pIrpRcv; + + // if the transport has all of the data it says is available, then + // do the copy here ( if the client buffer is large enough - checked + // by !ReceiveIndicated) + // + if ((BytesAvailable == BytesIndicated) && + (RcvLength >= BytesIndicated) && + !pConnEle->ReceiveIndicated) + { + ULONG BytesCopied; + ULONG TotalBytes; + + PUSH_LOCATION(0x70); + + if (RcvLength > BytesIndicated) + RcvLength = BytesIndicated; + + status = TdiCopyBufferToMdl( + pTsdu, + 0, + RcvLength, + pConnEle->pNextMdl, + pConnEle->OffsetFromStart, + &BytesCopied); + + // + // if the irp is not yet full, or the free bytes have not + // been exhausted by this copy, then adjust some counts and return + // quickly, otherwise call the completion rcv routine as if the + // irp has completed normally from the transport - + // + TotalBytes = pConnEle->BytesRcvd + BytesCopied; + + if ((TotalBytes < pConnEle->TotalPcktLen) && + (BytesCopied < pConnEle->FreeBytesInMdl)) + { + PMDL pMdl; + + // + // take the short cut and do not call completion rcv since we + // are still waiting for more data + // + PUSH_LOCATION(0x81); + pConnEle->BytesRcvd += BytesCopied; + pConnEle->FreeBytesInMdl -= BytesCopied; + + // clean up the partial mdl. + // + pMdl = pConnEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + // set where the next rcvd data will start, by setting the pNextMdl and + // offset from start. + // + pMdl = pConnEle->pNextMdl; + if ((BytesCopied + pConnEle->OffsetFromStart) < MmGetMdlByteCount(pMdl)) + { + PUSH_LOCATION(0x82); + // + // All of this data will fit into the current Mdl, and + // the next data will start in the same Mdl (if there is more data) + // + pConnEle->OffsetFromStart += BytesCopied; + } + else + { + PUSH_LOCATION(0x83) + SumMdlLengths(pMdl, + pConnEle->OffsetFromStart + BytesCopied, + pConnEle); + } + *BytesTaken = BytesCopied; + status = STATUS_SUCCESS; + + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("I")); + goto ExitRoutine; + } + else + { + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("i")); + CTESpinFree(pLowerConn,OldIrq); + // + // the values are set to this so that when Completion Rcv is + // called it will increment the BytesRcvd by BytesCopied. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = BytesCopied; + + // + // now call the irp completion routine, shorting out the io + // subsystem - to process the irp + // + status = CompletionRcv(NULL,pIrp,(PVOID)pLowerConn); + // + // complete the irp back to the client if required + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + IoAcquireCancelSpinLock(&OldIrq2); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq2); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + } + // + // tell the transport we took all the data that we did take. + // Since CompletionRcv has unlocked the spin lock and decremented + // the refcount, return here. + // + *BytesTaken = BytesCopied; + return(STATUS_SUCCESS); + } + else + { + // + // Either BytesIndicated != BytesAvailable or the RcvBuffer + // is too short, so make up an Irp with a partial Mdl and pass it + // to the transport. + // + PUSH_LOCATION(0x71); + + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pConnEle->pNextMdl) + + pConnEle->OffsetFromStart); + + /* create a partial MDL so that the new data is copied after the existing data + in the MDL. Use the pNextMdl field stored in the pConnEle + that was set up during the last receive.( since at that time + we knew the BytesAvailable then). Without this we would have to + traverse the list of Mdls for each receive. + + 0 for length means map the rest of the buffer + */ + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pConnEle->pNextMdl,pNewMdl,NewAddress,0); + // + // hook the new partial mdl to the front of the MDL chain + // + pNewMdl->Next = pConnEle->pNextMdl->Next; + + pIrp->MdlAddress = pNewMdl; + ASSERT(pNewMdl); + + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + IoAcquireCancelSpinLock(&OldIrq2); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq2); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + /* this code is sped up somewhat by expanding the code here rather than calling + the TdiBuildReceive macro + + make the next stack location the current one. Normally IoCallDriver + would do this but we are not going through IoCallDriver here, since the + Irp is just passed back with RcvIndication. + */ + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + pIrpSp->CompletionRoutine = CompletionRcv; + + pParams->ReceiveLength = RcvLength; + + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pLowerConn; + + /* set flags so the completion routine is always invoked. + */ + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + + pFileObject = pLowerConn->pFileObject; + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + /* + pass the Irp back to the transport + */ + *ppIrp = (PVOID)pIrp; + *BytesTaken = 0; + + status = STATUS_MORE_PROCESSING_REQUIRED; + } + + + } + else + if ((pLowerConn->State == NBT_SESSION_UP) && + (pLowerConn->StateRcv == NORMAL)) + { + ULONG PduSize; + UCHAR Passit; + + INCR_COUNT(R1); + /* + check indication and if less than 1 pdu or 132 bytes then + copy to the indicate buffer and go to Indic_buffer state + The while loop allows us to indicate multiple Pdus to the + client in the event that several indications arrive in one + indication from the transport + NOTE: + It is possible to get an indication that occurs in the middle + of the pdu if the client took the first indication rather + than passing an irp back, and thence going to the FILL_IRP + state. So check if BytesRcvd is zero, meaning that we are + expecting a new PDU. + */ + ASSERT(pConnEle->BytesInXport == 0); + ASSERT(pLowerConn->StateRcv == NORMAL); + + if (pConnEle->BytesRcvd == 0) + { + if (BytesIndicated >= sizeof(tSESSIONHDR)) + { + PduSize = myntohl(((tSESSIONHDR UNALIGNED *)pTsdu)->UlongLength) + + sizeof(tSESSIONHDR); + Passit = FALSE; + + } + else + { + status = LessThan4BytesRcvd(pLowerConn, + BytesAvailable, + BytesTaken, + ppIrp); + goto ExitRoutine; + } + + } + else + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got rest of PDU in indication BytesInd %X, BytesAvail %X\n", + BytesIndicated, BytesAvailable)); + + /* This is the remaining pdu size + */ + PduSize = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + /* a flag to pass the if below, since we are passing the + remaining data of a pdu to the client and we do not have + to adhere to the 128 bytes restriction. + */ + PUSH_LOCATION(0x1); + if (pConnEle->JunkMsgFlag) + { + // + // in this case the client has indicated that it took the + // entire message on the previous indication, so don't + // indicate any more to it. + // + PUSH_LOCATION(0x1); + + if (BytesAvailable < PduSize) + { + BTaken = BytesAvailable; + } + else + { + BTaken = PduSize; + } + pConnEle->BytesRcvd += BTaken; + if (pConnEle->BytesRcvd == pConnEle->TotalPcktLen) + { + PUSH_LOCATION(0x1); + pConnEle->BytesRcvd = 0; // reset for the next session pdu + pConnEle->JunkMsgFlag = FALSE; + } + status = STATUS_SUCCESS; + goto SkipIndication; + } + Passit = TRUE; + + } + /* + be sure that there is at least 132 bytes or a whole pdu + Since a keep alive has a zero length byte, we check for + that because the 4 byte session hdr is added to the 0 length + giving 4, so a 4 byte Keep Alive pdu will pass this test. + */ + if ((BytesIndicated >= NBT_INDICATE_BUFFER_SIZE) || + (BytesIndicated >= PduSize) || Passit ) + { + + PUSH_LOCATION(0x2); + + /* + // Indicate to the client + */ + status = RcvHandlrNotOs( + ReceiveEventContext, + (PVOID)pLowerConn, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &BTaken, + pTsdu, + (PVOID)&pIrp + ); + + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ULONG RemainingPdu; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // check if we can copy to the client's irp directly - meaning + // that we have received the whole pdu in this indication and + // the client's buffer is large enough, and there is no more + // data in the transport. + // + + if ((RemainingPdu == (BytesIndicated - BTaken)) && + (BytesIndicated == BytesAvailable) && + (pClientParams->ReceiveLength >= RemainingPdu) && + pIrp->MdlAddress) + { + ULONG BytesCopied; + + PUSH_LOCATION(0x88); + + status = TdiCopyBufferToMdl( + (PVOID)((PUCHAR)pTsdu + BTaken), + 0, + RemainingPdu, + pIrp->MdlAddress, + 0, + &BytesCopied); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Copy to client Buffer RcvLen=%X,StateRcv=%X\n", + RemainingPdu,pLowerConn->StateRcv)); + + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // reset a few things since this pdu has been fully recv'd + // + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // + // tell the transport we took all the data that we did take. + // + *BytesTaken = BytesCopied + BTaken; + + // + // complete the irp back to the client if required + // + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("F")); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + pLowerConn->BytesRcvd += BytesCopied; + + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + return(STATUS_SUCCESS); + } + else + { + PUSH_LOCATION(0x3); + + status = ProcessIrp(pLowerConn, + pIrp, + pTsdu, + &BTaken, + BytesIndicated, + BytesAvailable); + + *BytesTaken = BTaken; + ASSERT(*BytesTaken <= (pConnEle->TotalPcktLen + sizeof(tSESSIONHDR)) ); + if (status == STATUS_RECEIVE_EXPEDITED) + { + // in this case the processirp routine has completed the + // irp, so just return since the completion routine will + // have adjusted the RefCount and InRcvHandler flag + // + *ppIrp = NULL; + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_SUCCESS); + } + else + if (status == STATUS_SUCCESS) + { + *ppIrp = NULL; + + } + else + { + *ppIrp = (PVOID)pIrp; + } + } + + + } + else + { + // for the skip indication case the client has told us it + // does not want to be indicated with any more of the data + // + SkipIndication: + // + // the client received some, all or none of the data + // For Keep Alives the PduSize is 4 and BytesTaken = 4 + // so this check and return status success + // + *BytesTaken = BTaken; + + pLowerConn->BytesRcvd += BTaken - sizeof(tSESSIONHDR); + + // + // if the connection has disonnected, then just return + // + if (!pLowerConn->pUpperConnection) + { + *BytesTaken = BytesAvailable; + status = STATUS_SUCCESS; + } + else + if (BTaken > BytesAvailable) + { + // + // in this case the client has taken all of the message + // which could be larger than the available because + // we set bytesavail to the message length. So set a flag + // that tells us to discard the rest of the message as + // it comes in. + // + pConnEle->JunkMsgFlag = TRUE; + pConnEle->BytesRcvd = BytesAvailable - sizeof(tSESSIONHDR); + *BytesTaken = BytesAvailable; + + } + else + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // this may be a zero length send -that the client has + // decided not to accept. If so then the state will be set + // to PartialRcv. In this case do NOT go down to the transport + // and get the rest of the data, but wait for the client + // to post a rcv buffer. + // + + // amount left in the transport... + pConnEle->BytesInXport = BytesAvailable - BTaken; + status = STATUS_SUCCESS; + } + else + if (BTaken == PduSize) + { + /* + Must have taken all of the pdu data, so check for + more data available - if so send down the indicate + buffer to get it. + */ + ASSERT(BTaken <= BytesIndicated); + if (BytesAvailable <= BTaken) + { + /* FAST PATH + */ + PUSH_LOCATION(0x8); + + status = STATUS_SUCCESS; + + } + else + { + /* + get remaining data with the indicate buffer + */ + status = MoreDataRcvdThanNeeded(pLowerConn, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + ppIrp); + } + } + else + { + // + // the client may have taken all the data in the + // indication!!, in which case return status success + // Note: that we check bytes available here not bytes + // indicated - since the client could take all indicated + // data but still leave data in the transport. + // + if (BTaken == BytesAvailable) + { + PUSH_LOCATION(0x4); + status = STATUS_SUCCESS; + + } + else + { + PUSH_LOCATION(0x87); + if (BTaken > PduSize) + { +#ifndef VXD +#if DBG + DbgBreakPoint(); +#endif +#endif + // + // the client took more than a PDU size worth, + // which is odd.... + // + PUSH_LOCATION(0x87); + ASSERT(BTaken <= PduSize); + + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + status = STATUS_SUCCESS; + + } + else + { + // + // otherwise the client did not take all of the data, + // which can mean that + // the client did not take all that it could, so + // go to the partial rcv state to keep track of it. + // + status = ClientTookSomeOfTheData(pLowerConn, + BytesIndicated, + BytesAvailable, + *BytesTaken, + PduSize); + } + } + } + + } + + } + else + { + status = NotEnoughDataYet(pLowerConn, + BytesIndicated, + BytesAvailable, + BytesTaken, + PduSize, + (PVOID *)ppIrp); + } + } + else + { + status = (*pLowerConn->CurrentStateProc)(ReceiveEventContext, + pLowerConn, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + BytesTaken, + pTsdu, + ppIrp); + } + + // + // in the IndicateBuffer state we have sent the indicate buffer + // down the the transport and expect it to come back in + // NewSessionCompletionRoutine. Therefore do not dereference the lower + // connection and do not change the InRcvHandler flag. + + // If an Irp + // is returned, then do not undo the reference - but rather + // wait for CompletionRcv to be called. + // +ExitRoutine: + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection + // + PUSH_LOCATION(0x50); + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + } + else + CTESpinFree(pLowerConn,OldIrq); + + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ProcessIrp( + IN tLOWERCONNECTION *pLowerConn, + IN PIRP pIrp, + IN PVOID pBuffer, + IN PULONG BytesTaken, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable + ) +/*++ + +Routine Description: + + This routine handles a Receive Irp that the client has returned on an + indication. The idea here is to check the Irp's MDL length to be + sure the pdu fits into the MDL, and also keep track of the situation where + more than one data is required to fill the pdu. + +Arguments: + + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnectEle; + PTDI_REQUEST_KERNEL_RECEIVE pClientParams; + ULONG RemainingPdu; + PMDL pMdl; + PFILE_OBJECT pFileObject; + ULONG ReceiveLength; + BOOLEAN QuickRoute; + BOOLEAN FromCopyData; + + pConnectEle = pLowerConn->pUpperConnection; + + status = STATUS_SUCCESS; + + // subtract session header and any bytes that the client took + // + BytesAvailable -= *BytesTaken; + + // + // put together an Irp stack location to process the receive and pass down + // to the transport. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + // + // check if this will be a multiple rcv session pdu. If it is then + // allocate a partial MDL to be used for mapping part of the first + // MDL in each chunk received + // + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + ReceiveLength = RemainingPdu; + PUSH_LOCATION(0x19); + pIrpSp = IoGetNextIrpStackLocation(pIrp); + + // this code should not be hit if called by CopyDataandIndicate + // which is in the indicate buffer state since it adjusts the bytesInXport + // which is also set by the code in TdiReceiveHndlr in the INDICATE_BUFFER + // state before calling CopyDataandIndicate. Also, CopyDataandIndicate + // does not want this routine to set the state to fillIrp when Bytes + // Available < RemainingPdu + // + FromCopyData = (pLowerConn->StateRcv == INDICATE_BUFFER); + if (!FromCopyData) + { + + QuickRoute = TRUE; + // we need this code within the check since this routine is also called by the + // HandleNewSessionPdu routine, which calls IoCallDriver, which + // increments the stack location itself. + // + ASSERT(pIrp->CurrentLocation > 1); + + if (BytesAvailable == RemainingPdu) + { + if (pClientParams->ReceiveLength >= BytesAvailable) + { + // *** FAST PATH CASE **** + goto ExitCode; + } + } + else + if (BytesAvailable < RemainingPdu ) // need more data from transport + { + PUSH_LOCATION(0x14); + // it is possible for the client to pass down an irp with no + // MDL in it, so we check for that here + // + if (pIrp->MdlAddress) + { + PUSH_LOCATION(0x14); + + // + // save the client's irp address since the session pdu will arrive + // in several chunks, and we need to continually pass the irp to the + // transport for each chunk. + // + //pConnectEle->pIrpRcv = pIrp; + // NOTE: the pIrp is NOT saved here because the irp is about + // to be passed back to the transport. Hence we do not want + // to accidently complete it in DisconnectHandlrNotOs + // if a disconnect comes in while the irp is in the transport. + // pIrpRcv is set to pIrp in Completion Rcv while we have + // the irp in our possession. + + // + // keep the initial Mdl(chain) since we need to + // to copy new data after the existing data, when the session pdu arrives + // as several chunks from TCP. Keeping the Mdl around allows us to + // reconstruct the original Mdl chain when we are all done. + // + pLowerConn->pMdl = pIrp->MdlAddress; + // + // this call maps the client's Mdl so that on each partial Mdl creation + // we don't go through a mapping and unmapping (when MmPrepareMdlForReuse) + // is called in the completion routine. + // + (PVOID)MmGetSystemAddressForMdl(pIrp->MdlAddress); + + pMdl = pIrp->MdlAddress; + + // the nextmdl is setup to allow us to create a partial Mdl starting + // from the next one. CompletionRcv will adjust this if it needs to. + // + pConnectEle->pNextMdl = pMdl; + + // need more data from the transport to fill this + // irp + // + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + } + + status = STATUS_MORE_PROCESSING_REQUIRED; + + // if the client buffer is big enough, increment to the next + // io stack location and jump to the code that sets up the + // irp, since we always want to pass it to the transport in this + // case because the transport will hold onto the irp till it is full + // if it can. (faster) + // + if (pClientParams->ReceiveLength >= RemainingPdu) + { + // *** FAST PATH CASE **** + IoSetNextIrpStackLocation(pIrp); + pConnectEle->FreeBytesInMdl = ReceiveLength; + pConnectEle->CurrentRcvLen = RemainingPdu; + goto ExitCode2; + } + + // + // if there is no mdl then we want to be able to go through the + // quick route below to return the null mdl right away, so + // don't set Quickroute false here. + // + + + } + else + if (BytesAvailable > RemainingPdu) + { + PUSH_LOCATION(0x15); + // + // there is too much data, so keep track of the + // fact that there is data left in the transport + // and get it when the irp completes through + // completion recv. + // + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + // this calculation may have to be adjusted below if the client's + // buffer is too short. NOTE: BytesTaken have already been subtracted + // from BytesAvailable (above). + // + pConnectEle->BytesInXport = BytesAvailable - RemainingPdu; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to Indicate Buff(Irp), Indic = %X, Pdusize=%X\n", + BytesIndicated,pConnectEle->TotalPcktLen)); + + + status = STATUS_DATA_NOT_ACCEPTED; + } + + // DEBUG* + //IoSetNextIrpStackLocation(pIrp); + } + else + { + QuickRoute = FALSE; + } + + // + // if the receive buffer is too short then flag it so when the client + // passes another buffer to NBT, nbt will pass it to the transport + // + //if (BytesAvailable > pClientParams->ReceiveLength ) + { + + // so just check for too short of a client buffer. + // + if (RemainingPdu > pClientParams->ReceiveLength) + { + PUSH_LOCATION(0x17); + + ReceiveLength = pClientParams->ReceiveLength; + // + // Adjust the number of bytes left in the transport up by the number of + // bytes not taken by the client. Be sure not to add in the number + // of bytes in the transport twice, since it could have been done + // above where the state is set to INDICATE_BUFFER + // + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // BytesInXport was already incremented to account for any + // amount over remainingPdu, so just add the amount that the + // client buffer is short of RemainingPdu + // + PUSH_LOCATION(0x18); + if (BytesAvailable > ReceiveLength ) + { + pConnectEle->BytesInXport += (RemainingPdu - ReceiveLength); + } + // the client has not taken all of the data , but has returned + // a buffer that is ReceiveLength long, therefore the amount + // that the client needs to take is just the total pdu - rcvlength. + // + pConnectEle->ReceiveIndicated = (RemainingPdu - + ReceiveLength); + } + else + { + // + // BytesInXport has not been incremented yet so add the entire + // amount that the client buffer is too short by. Check if + // the client's buffer will take all of the data. + // + if (BytesAvailable > ReceiveLength ) + { + pConnectEle->BytesInXport += (BytesAvailable - ReceiveLength); + } + // the client has not taken all of the data , but has returned + // a buffer that is ReceiveLength long, therefore the amount + // that the client needs to take is just what was indicated + // to the client - recvlength. + // + pConnectEle->ReceiveIndicated = (RemainingPdu - + ReceiveLength); + + } + + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Switching to PartialRcv for Irp. RecvInd. =%X, RemainPdu %X Avail %X\n", + pConnectEle->ReceiveIndicated,RemainingPdu,BytesAvailable)); + } + + } + +ExitCode: + + // keep track of data in MDL so we know when it is full and we need to + // return it to the user. CurrentRcvLen tells us how many bytes the current + // Irp can have max when the Mdl is full. + // + pConnectEle->FreeBytesInMdl = ReceiveLength; + pConnectEle->CurrentRcvLen = ReceiveLength; + if (ReceiveLength > RemainingPdu) + { + pConnectEle->CurrentRcvLen = RemainingPdu; + } + if (QuickRoute) + { + // + // check if we can copy the data to the client's MDL + // right here. If the indication is too short pass an Irp down + // to the transport. + // + BytesIndicated -= *BytesTaken; + + if ((ReceiveLength <= BytesIndicated)) + { + ULONG BytesCopied; + + PUSH_LOCATION(0x76); + + if (pIrp->MdlAddress) + { + + status = TdiCopyBufferToMdl( + (PVOID)((PUCHAR)pBuffer + *BytesTaken), + 0, + ReceiveLength, + pIrp->MdlAddress, + 0, + &BytesCopied); + + } + else + { + // + // No Mdl, so just return the irp to the client, and then + // return success to the caller so we tell the transport that + // we took only BytesTaken + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:No MDL, so complete Irp\n")); + + + PUSH_LOCATION(0x77); + BytesCopied = 0; + } + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Copy to client Buffer RcvLen=%X,StateRcv=%X\n", + ReceiveLength,pLowerConn->StateRcv)); + + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + // + // now call the irp completion routine, shorting out the io + // subsystem - to process the irp + // + CTESpinFreeAtDpc(pLowerConn); + status = CompletionRcv(NULL,pIrp,(PVOID)pLowerConn); + + // + // tell the transport we took all the data that we did take. + // + *BytesTaken += BytesCopied; + + IF_DBG(NBT_DEBUG_FASTPATH) + KdPrint(("f")); + // + // complete the irp back to the client if required + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + PUSH_LOCATION(0x76); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Completing Irp Quickly\n")); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + + // since we have called CompletionRcv, that routine has + // adjusted the refcount and InRcvHandlr flag, so return this + // status to cause the caller to return directly + CTESpinLockAtDpc(pLowerConn); + return(STATUS_RECEIVE_EXPEDITED); + + } + else + { + // + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + // + IoSetNextIrpStackLocation(pIrp); + } + + } +ExitCode2: + pIrpSp->CompletionRoutine = CompletionRcv; + pIrpSp->Context = (PVOID)pLowerConn; + + // set Control flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE; + + pFileObject = pLowerConn->pFileObject; + pIrpSp->FileObject = pFileObject; + pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject); + + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + pParams->ReceiveFlags = pClientParams->ReceiveFlags; + + // Set the correct receive length in the irp in case the client has + // passed one down that is larger than the message + // + pParams->ReceiveLength = ReceiveLength; + + // + // just check for a zero length send, where the client has + // passed down an Irp with a null mdl, or the pdu size is zero. We don't want to pass + // that to the transport because it will hold onto it till the next + // pdu comes in from the wire - we want to complete the irp when this routine + // returns. When this is called from CopyDataAndIndicate don't + // to this because copydataandindicate does all the checks. + // + if (!FromCopyData) + { + if ((RemainingPdu == 0) || !pIrp->MdlAddress) + { + // + // the call to IoCompleteRequest will call completionRcv which will + // decrement the RefCount. Similarly returning status success will + // cause the caller to decrement the ref count, so increment one + // more time here to account for this second decrement. + // + pLowerConn->RefCount++; + CTESpinFreeAtDpc(pLowerConn); + + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTESpinLockAtDpc(pLowerConn); + + status = STATUS_SUCCESS; + } + else + status = STATUS_MORE_PROCESSING_REQUIRED; + } + + return(status); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +ClientBufferOverFlow( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG BytesRcvd + ) +/*++ + +Routine Description: + + This routine completes the Irp by tracking the number of bytes received + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pLowerConn - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + + // *TODO* + +// ASSERT(0); + + switch (pLowerConn->StateRcv) + { + case PARTIAL_RCV: + + + case FILL_IRP: + + + case NORMAL: + + + + case INDICATE_BUFFER: + + + default: + ; + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRcv( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine completes the Irp by tracking the number of bytes received + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pLowerConn - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + register tCONNECTELE *pConnectEle; + NTSTATUS status; + ULONG BytesRcvd; + tLOWERCONNECTION *pLowerConn; + PKDPC pDpc; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + PMDL pMdl; + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + BOOLEAN AllowDereference=TRUE; + + // + // Do some checking to keep the Io system happy - propagate the pending + // bit up the irp stack frame.... if it was set by the driver below then + // it must be set by me + // + if (Irp->PendingReturned) + { + IoMarkIrpPending(Irp); + } + + // check the bytes recvd + pLowerConn = (tLOWERCONNECTION *)Context; + // + // if the link has disconnected, do not process the irp, just pass it + // up the chain. + // + CTESpinLock(pLowerConn,OldIrq); + if (!NT_SUCCESS(Irp->IoStatus.Status) || !pLowerConn->pUpperConnection) + { + PUSH_LOCATION(0x1); + if (pLowerConn->StateRcv == FILL_IRP) + { + PUSH_LOCATION(0x1); + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + } + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + // + // the rcv failed so kill the connection since + // we can't keep track of message boundaries any more. + // + CTESpinFree(pLowerConn,OldIrq); + OutOfRsrcKill(pLowerConn); + CTESpinLock(pLowerConn,OldIrq); + + status = STATUS_SUCCESS; + goto ExitCode; + } + + pConnectEle = pLowerConn->pUpperConnection; + + // keep track of how many bytes have been received + // + BytesRcvd = Irp->IoStatus.Information; + pConnectEle->BytesRcvd += BytesRcvd; + // + // subtract the number of bytes rcvd from the length of the client + // buffer + // so when more data arrives we can determine if we are going to + // overflow the client buffer. + // + pConnectEle->FreeBytesInMdl -= BytesRcvd; + + pIrpSp = IoGetCurrentIrpStackLocation(Irp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + + pLowerConn->BytesRcvd += BytesRcvd; + + CHECK_PTR(pConnectEle); + if (Irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW) + { + // + // the client's buffer was too short - probably because he said it + // was longer than it really was + // + PUSH_LOCATION(0x1a); + KdPrint(("Nbt:Client Buffer Too short on CompletionRcv\n")); + + if (pLowerConn->StateRcv == FILL_IRP) + { + PUSH_LOCATION(0x1a); + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + + } + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + status = ClientBufferOverFlow(pLowerConn,pConnectEle,Irp,BytesRcvd); + + // + // the client's buffer was too short so kill the connection since + // we can't keep track of message boundaries any more. + // + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + CTESpinFree(pLowerConn,OldIrq); + OutOfRsrcKill(pLowerConn); + CTESpinLock(pLowerConn,OldIrq); + + goto ExitCode; + } + else + if ((pConnectEle->FreeBytesInMdl == 0) || + (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen)) + { + INCR_COUNT(C1); + // + // this case handles when the Irp MDL is full or the whole pdu has been + // received. + // + + // + // reset the MDL fields back to where they were + // if this was a multi-rcv session pdu + // + // + if (pLowerConn->StateRcv == FILL_IRP) + { + + INCR_COUNT(C2); + PUSH_LOCATION(0x1b); + + Irp->MdlAddress = pLowerConn->pMdl; + ASSERT(Irp->MdlAddress); + + // + // allow the MDL to be used again for the next session PDU + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + pConnectEle->OffsetFromStart = 0; + + } + // + // The client may have passed down a too short irp which we are + // tracking with ReceiveIndicated, so set state to partialrcv if + // necessary. + // + if (pConnectEle->ReceiveIndicated == 0) + { + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + else + { + PUSH_LOCATION(0x26); + // + // there may still be data left in the transport + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Short Rcv, still data indicated to client\n")); + + pLowerConn->StateRcv = PARTIAL_RCV; + pLowerConn->CurrentStateProc = PartialRcv; + } + + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + // + // we have received all of the data + // so complete back to the client + // + status = STATUS_SUCCESS; + // + // the amount of data in this irp is the CurrentRcvLen which + // could be less than BytesRcvd when the client passes down + // short rcv buffers. + // + Irp->IoStatus.Information = pConnectEle->CurrentRcvLen; + + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + Irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + PUSH_LOCATION(0x27); + // + // this MDL must be too short to take the whole pdu, so set the + // status to buffer overflow. + // + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + + // + // Check if there is still more data in the transport or if the client + // has been indicated with more data and has subsequently posted a rcv + // which we must get now and pass to the transport. + // + if ((pConnectEle->BytesInXport) || (pLowerConn->StateRcv == PARTIAL_RCV)) + { + INCR_COUNT(C3); + // + // send down another + // irp to get the data and complete the client's current irp. + // + PUSH_LOCATION(0x1c); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:ComplRcv BytesInXport= %X, %X\n",pConnectEle->BytesInXport, + pLowerConn)); + + if (pLowerConn->StateRcv != PARTIAL_RCV) + { + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + pLowerConn->BytesInIndicate = 0; + } + + CTESpinFree(pLowerConn,OldIrq); + + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(Irp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + // Complete the current Irp + IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); + + CTESpinLock(pLowerConn,OldIrq); + + // rather than call HandleNewSessionPdu directly, we queue a + // Dpc since streams does not currently expect to get a recv + // posted while it is processing an indication response. The + // Dpc will run when streams is all done, and it should handle + // this posted receive ok. + + + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // + // check if the client has passed down another rcv buffer + // and if so, start a Dpc which will pass down the client's + // buffer. + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('p')); + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcGetRestOfIndication, + (PVOID)pLowerConn); + + KeInsertQueueDpc(pDpc,NULL,NULL); + // + // we don't want to dereference pLowerConn at the end + // since we will use it in the DPC routine. + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + + } + } + else + { + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('q')); + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + // + // just get the session hdr to start with so we know how large + // the pdu is, then get the rest of the pdu after that completes. + // + KeInsertQueueDpc(pDpc,NULL,(PVOID)sizeof(tSESSIONHDR)); + // + // we don't want to dereference pLowerConn at the end + // since we will use it in the DPC routine. + // + CTESpinFree(pLowerConn,OldIrq); + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + } + + status = STATUS_MORE_PROCESSING_REQUIRED; + goto ExitCode; + } + } + else + if (pConnectEle->BytesRcvd < pConnectEle->TotalPcktLen) + { + ULONG Bytes; + + INCR_COUNT(C4); + PUSH_LOCATION(0x1d); + // + // in this case we have not received all of the data from the transport + // for this session pdu, so tell the io subystem not to finish processing + // the irp yet if it is not a partial Rcv. + // + status = STATUS_MORE_PROCESSING_REQUIRED; + + // clean up the partial mdl. + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + // set where the next rcvd data will start, by setting the pNextMdl and + // offset from start. + // + pMdl = pConnectEle->pNextMdl; + ASSERT(pMdl); + + Bytes = BytesRcvd + pConnectEle->OffsetFromStart; + if (Bytes < MmGetMdlByteCount(pMdl)) + { + PUSH_LOCATION(0x74); + // + // All of this data will fit into the current Mdl, and + // the next data will start in the same Mdl (if there is more data) + // + pConnectEle->OffsetFromStart += BytesRcvd; + + IF_DBG(NBT_DEBUG_FILLIRP) + KdPrint(("~")); + } + else + { + // + // sum the Mdl lengths until we find enough space for the data + // to fit into. + // + IF_DBG(NBT_DEBUG_FILLIRP) + KdPrint(("^")); + PUSH_LOCATION(0x75); + + SumMdlLengths(pMdl,Bytes,pConnectEle); + + } + + // since we are holding on to the rcv Irp, set up a cancel routine + IoAcquireCancelSpinLock(&OldIrq2); + + // if the session was disconnected while the transport had the + // irp, then cancel the irp now... + // + if ((pConnectEle->state != NBT_SESSION_UP) || Irp->Cancel) + { + CHECK_PTR(pConnectEle); + pConnectEle->pIrpRcv = NULL; + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + + + IoReleaseCancelSpinLock(OldIrq2); + CTESpinFree(pLowerConn,OldIrq); + + // since the irp has been cancelled, don't touch it. + // return status success so the IO subsystem passes the irp + // back to the owner. + // + status = STATUS_SUCCESS; + +// Irp->IoStatus.Status = STATUS_CANCELLED; +// IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); + + // the irp is being cancelled in mid session pdu. We can't + // recover since we have given the client only part of a pdu, + // therefore disconnect the connection. + + OutOfRsrcKill(pLowerConn); + + CTESpinLock(pLowerConn,OldIrq); + + } + else + { + // setup the cancel routine + IoSetCancelRoutine(Irp,FillIrpCancelRoutine); + + // the pIrpRcv value is set to Zero when the irp is in the + // tranport, so we can't accidently complete it twice in + // disconnectHandlrNotOs when a disconnect occurs and the + // transport has the irp. So here we save the value again so FillIrp + // will work correctly. + // + pConnectEle->pIrpRcv = Irp; + // set the irp mdl back to its original so that a cancel will + // find the irp in the right state + // + Irp->MdlAddress = pLowerConn->pMdl; + + IoReleaseCancelSpinLock(OldIrq2); + + } + } + else + { + + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d,NewBytes =%d,%X\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen, + Irp->IoStatus.Information,pLowerConn)); + ASSERT(0); + // this status will return the irp to the user + // + status = STATUS_SUCCESS; + if (pLowerConn->StateRcv == FILL_IRP) + { + + PUSH_LOCATION(0x1f); + + Irp->MdlAddress = pLowerConn->pMdl; + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = 0; + + // + // allow the MDL to be used again for the next session PDU + // + pMdl = pConnectEle->pNewMdl; + MmPrepareMdlForReuse(pMdl); + + + } + pConnectEle->OffsetFromStart = 0; + pConnectEle->BytesRcvd = 0; + + pLowerConn->StateRcv = NORMAL; + + //WHAT ELSE TO DO HERE OTHER THAN KILL THE CONNECTION, SINCE WE ARE + // PROBABLY OFF WITH RESPECT TO THE SESSION HDR.... + // ....RESET THE CONNECTION ???? + + CTESpinFree(pLowerConn,OldIrq); + + OutOfRsrcKill(pLowerConn); + + CTESpinLock(pLowerConn,OldIrq); + + } + +ExitCode: + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection - this function is __inline!! + // + PUSH_LOCATION(0x52); + DerefLowerConnFast(pLowerConn,pConnectEle,OldIrq); + + return(status); + + UNREFERENCED_PARAMETER( DeviceObject ); +} +//---------------------------------------------------------------------------- + +__inline +NTSTATUS +RcvHandlrNotOs ( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PVOID *RcvBuffer + + ) +/*++ + +Routine Description: + + This routine is the receive event indication handler. + + It is called when an session packet arrives from the network, when the + session has already been established (NBT_SESSION_UP state). The routine + looks for a receive buffer first and failing that looks for a receive + indication handler to pass the message to. + +Arguments: + + pClientEle - ptr to the connecition record for this session + + +Return Value: + + NTSTATUS - Status of receive operation + +--*/ +{ + + NTSTATUS status; + PLIST_ENTRY pRcv; + PVOID pRcvElement; + tCLIENTELE *pClientEle; + tSESSIONHDR UNALIGNED *pSessionHdr; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnectEle; + CTELockHandle OldIrq; + PIRP pIrp; + ULONG ClientBytesTaken; + BOOLEAN DebugMore; + ULONG RemainingPdu; + +//******************************************************************** +//******************************************************************** +// +// NOTE: A copy of this procedure is in Tdihndlr.c - it is inlined for +// the NT case. Therefore, only change this procedure and then +// copy the procedure body to Tdihndlr.c +// +// +//******************************************************************** +//******************************************************************** + + // get the ptr to the lower connection, and from that get the ptr to the + // upper connection block + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pSessionHdr = (tSESSIONHDR UNALIGNED *)pTsdu; + + // + // Session ** UP ** processing + // + *BytesTaken = 0; + + pConnectEle = pLowerConn->pUpperConnection; + + ASSERT(pConnectEle->pClientEle); + + ASSERT(BytesIndicated >= sizeof(tSESSIONHDR)); + + // this routine can get called by the next part of a large pdu, so that + // we don't always started at the begining of a pdu. The Bytes Rcvd + // value is set to zero in CompletionRcv when a new pdu is expected + // + if (pConnectEle->BytesRcvd == 0) + { + + if (pSessionHdr->Type == NBT_SESSION_MESSAGE) + { + + // + // expecting the start of a new session Pkt, so get the length out + // of the pTsdu passed in + // + pConnectEle->TotalPcktLen = myntohl(pSessionHdr->UlongLength); + + // remove the Session header by adjusting the data pointer + pTsdu = (PVOID)((PUCHAR)pTsdu + sizeof(tSESSIONHDR)); + + // shorten the number of bytes since we have stripped off the + // session header + BytesIndicated -= sizeof(tSESSIONHDR); + BytesAvailable -= sizeof(tSESSIONHDR); + *BytesTaken = sizeof(tSESSIONHDR); + } + // + // Session Keep Alive + // + else + if (pSessionHdr->Type == NBT_SESSION_KEEP_ALIVE) + { + // session keep alives are simply discarded, since the act of sending + // a keep alive indicates the session is still alive, otherwise the + // transport would report an error. + + // tell the transport that we took the Pdu + *BytesTaken = sizeof(tSESSIONHDR); + return(STATUS_SUCCESS); + + } + else + { + IF_DBG(NBT_DEBUG_DISCONNECT) + KdPrint(("Nbt:Unexpected Session Pdu received: type = %X\n", + pSessionHdr->Type)); + + ASSERT(0); + *BytesTaken = BytesIndicated; + return(STATUS_SUCCESS); + + } + } + + // + // check if there are any receive buffers queued against this connection + // + if (!IsListEmpty(&pConnectEle->RcvHead)) + { + // get the first buffer off the receive list + pRcv = RemoveHeadList(&pConnectEle->RcvHead); +#ifndef VXD + pRcvElement = CONTAINING_RECORD(pRcv,IRP,Tail.Overlay.ListEntry); + + // the cancel routine was set when this irp was posted to Nbt, so + // clear it now, since the irp is being passed to the transport + // + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine((PIRP)pRcvElement,NULL); + IoReleaseCancelSpinLock(OldIrq); + +#else + pRcvElement = CONTAINING_RECORD(pRcv, RCV_CONTEXT, ListEntry ) ; +#endif + + // + // this buffer is actually an Irp, so pass it back to the transport + // as a return parameter + // + *RcvBuffer = pRcvElement; + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + // + // No receives on this connection. Is there a receive event handler for this + // address? + // + pClientEle = pConnectEle->pClientEle; + +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + if (pClientEle->evReceive) +#endif + { + + + // check that we have not received more data than we should for + // this session Pdu. i.e. part of the next session pdu. BytesRcvd may + // have a value other than zero if the pdu has arrived in two chunks + // and the client has taken the previous one in the indication rather + // than passing back an Irp. + // +#if DBG + DebugMore = FALSE; +#endif + RemainingPdu = pConnectEle->TotalPcktLen - pConnectEle->BytesRcvd; + if (BytesAvailable >= RemainingPdu) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:More Data Recvd than expecting! Avail= %X,TotalLen= %X,state=%x\n", + BytesAvailable,pConnectEle->TotalPcktLen,pLowerConn->StateRcv)); +#if DBG + DebugMore =TRUE; +#endif + // shorten the indication to the client so that they don't + // get more data than the end of the pdu + // + BytesAvailable = RemainingPdu; + if (BytesIndicated > BytesAvailable) + { + BytesIndicated = BytesAvailable; + } + // + // We always indicated at raised IRQL since we call freelockatdispatch + // below + // + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE | TDI_RECEIVE_AT_DISPATCH_LEVEL; + } + else + { + // the transport may have has this flag on. We need to + // turn it off if the entire message is not present, where entire + // message means within the bytesAvailable length. We deliberately + // use bytesavailable so that Rdr/Srv can know that the next + // indication will be a new message if they set bytestaken to + // bytesavailable. + // + ReceiveFlags &= ~TDI_RECEIVE_ENTIRE_MESSAGE; + ReceiveFlags |= TDI_RECEIVE_AT_DISPATCH_LEVEL; +#ifndef VXD + BytesAvailable = RemainingPdu; +#endif + } + + // + // NT-specific code locks pLowerConn before calling this routine, + // + CTESpinFreeAtDpc(pLowerConn); + + // call the Client Event Handler + ClientBytesTaken = 0; + status = (*pClientEle->evReceive)( + pClientEle->RcvEvContext, + pConnectEle->ConnectContext, + ReceiveFlags, + BytesIndicated, + BytesAvailable, + &ClientBytesTaken, + pTsdu, + &pIrp); + + CTESpinLockAtDpc(pLowerConn); +#if DBG + if (DebugMore) + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(( "Client TOOK %X bytes, pIrp = %X,status =%X\n", + ClientBytesTaken,pIrp,status)); + } +#endif + if (!pLowerConn->pUpperConnection) + { + // the connection was disconnected in the interim + // so do nothing. + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + } + else + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + ASSERT(pIrp); + // + // the client may pass back a receive in the pIrp. + // In this case pIrp is a valid receive request Irp + // and the status is MORE_PROCESSING + // + + // don't put these lines outside the if incase the client + // does not set ClientBytesTaken when it returns an error + // code... we don't want to use the value then + // + // count the bytes received so far. Most of the bytes + // will be received in the CompletionRcv handler in TdiHndlr.c + pConnectEle->BytesRcvd += ClientBytesTaken; + + // The client has taken some of the data at least... + *BytesTaken += ClientBytesTaken; + + *RcvBuffer = pIrp; + + // ** FAST PATH ** + return(status); + } + else + // + // no irp was returned... the client just took some of the bytes.. + // + if (status == STATUS_SUCCESS) + { + + // count the bytes received so far. + pConnectEle->BytesRcvd += ClientBytesTaken; + *BytesTaken += ClientBytesTaken; + + // + // look at how much data was taken and adjust some counts + // + if (pConnectEle->BytesRcvd == pConnectEle->TotalPcktLen) + { + // ** FAST PATH ** + CHECK_PTR(pConnectEle); + pConnectEle->BytesRcvd = 0; // reset for the next session pdu + return(status); + } + else + if (pConnectEle->BytesRcvd > pConnectEle->TotalPcktLen) + { + //IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Too Many Bytes Rcvd!! Rcvd# = %d, TotalLen = %d\n", + pConnectEle->BytesRcvd,pConnectEle->TotalPcktLen)); + + ASSERTMSG("Nbt:Client Took Too Much Data!!!\n",0); + + // + // try to recover by saying that the client took all of the + // data so at least the transport is not confused too + // + *BytesTaken = BytesIndicated; + + } + else + // the client did not take all of the data so + // keep track of the fact + { + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT:Client took Indication BytesRcvd=%X, TotalLen=%X BytesAvail %X ClientTaken %X\n", + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen, + BytesAvailable, + ClientBytesTaken)); + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement the + // ReceiveIndicated counter which is set in Tdihndlr.c + + } + } + else + if (status == STATUS_DATA_NOT_ACCEPTED) + { + // client has not taken ANY data... + // + // In this case the *BytesTaken is set to 4, the session hdr. + // since we really have taken that data to setup the PduSize + // in the pConnEle structure. + // + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("NBT: Status DATA NOT ACCEPTED returned from client Avail %X %X\n", + BytesAvailable,pConnectEle)); + + // the code in tdihndlr.c normally looks after incrementing + // the ReceiveIndicated count for data that is not taken by + // the client, but if it is a zero length send that code cannot + // detect it, so we put code here to handle that case + // + // It is possible for the client to do a disconnect after + // we release the spin lock on pLowerConn to call the Client's + // disconnect indication. If that occurs, do not overwrite + // the StateProc with PartialRcv + // + if ((pConnectEle->TotalPcktLen == 0) && + (pConnectEle->state == NBT_SESSION_UP)) + { + pLowerConn->StateRcv = PARTIAL_RCV; + SetStateProc( pLowerConn, PartialRcv ) ; + CHECK_PTR(pConnectEle); + pConnectEle->ReceiveIndicated = 0; // zero bytes waiting for client + } + else + { + // + // if any bytes were taken (i.e. the session hdr) then + // return status success. (otherwise the status is + // statusNotAccpeted). + // + if (*BytesTaken) + { + status = STATUS_SUCCESS; + } + } + + // + // the next time the client sends down a receive buffer + // the code will pass it to the transport and decrement this + // counter. + } + else + ASSERT(0); + + + return(status); + + } +#ifdef VXD + // + // there is always a receive event handler in the Nt case - it may + // be the default handler, but it is there, so no need for test. + // + else + { + // + // there is no client buffer to pass the data to, so keep + // track of the fact so when the next client buffer comes down + // we can get the data from the transport. + // + KdPrint(("NBT:Client did not have a Buffer posted, rcvs indicated =%X,BytesRcvd=%X, TotalLen=%X\n", + pConnectEle->ReceiveIndicated, + pConnectEle->BytesRcvd, + pConnectEle->TotalPcktLen)); + + // the routine calling this one increments ReceiveIndicated and sets the + // state to PartialRcv to keep track of the fact that there is data + // waiting in the transport + // + return(STATUS_DATA_NOT_ACCEPTED); + } +#endif +} + +//---------------------------------------------------------------------------- +__inline +VOID +DerefLowerConnFast( + IN tLOWERCONNECTION *pLowerConn, + IN tCONNECTELE *pConnEle, + IN CTELockHandle OldIrq + ) +/*++ + +Routine Description: + + This routine dereferences the lower connection and if someone has + tried to do that during the execution of the routine that called + this one, the pConnEle is dereferenced too. + +Arguments: + + +Return Value: + + +--*/ + +{ + // NOTE: we do not coordinate with the pConnEle using InRcvHandler any + // more- we just check if pUpperconnection is null or not. + // + //if (pLowerConn->InRcvHandler) + { + // pLowerConn->InRcvHandler = FALSE; + if (pLowerConn->RefCount > 1) + { + // This is the FAST PATH + pLowerConn->RefCount--; + CTESpinFree(pLowerConn,OldIrq); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + NbtDereferenceLowerConnection(pLowerConn); + + } + } +} +//---------------------------------------------------------------------------- +VOID +DpcGetRestOfIndication( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine is called when the client has been indicated with more + data than they will take and there is a rcv buffer on their RcvHead + list when completion rcv runs. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + tCONNECTELE *pConnEle; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + tLOWERCONNECTION *pLowerConn=(tLOWERCONNECTION *)Context; + PLIST_ENTRY pEntry; + + CTEMemFree((PVOID)pDpc); + + CTESpinLockAtDpc(&NbtConfig.JointLock); + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + if (!pLowerConn->pUpperConnection || pLowerConn->StateRcv != PARTIAL_RCV) + { + PUSH_LOCATION(0xA4); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + KdPrint(("Nbt:Unable to get an Irp - Closing Connection!!\n",0)); + status = OutOfRsrcKill(pLowerConn); + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + CTESpinLockAtDpc(pLowerConn); + + pConnEle = (tCONNECTELE *)pLowerConn->pUpperConnection; + + if (!IsListEmpty(&pConnEle->RcvHead)) + { + PUSH_LOCATION(0xA5); + pEntry = RemoveHeadList(&pConnEle->RcvHead); + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + + IoAcquireCancelSpinLock(&OldIrq); + IoSetCancelRoutine(pIrp,NULL); + IoReleaseCancelSpinLock(OldIrq); + + // + // call the same routine that the client would call to post + // a recv buffer, except now we are in the PARTIAL_RCV state + // and the buffer will be passed to the transport. + // + status = NTReceive(pLowerConn->pDeviceContext,pIrp); + + + } + else + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + PUSH_LOCATION(0xA6); + } + // + // Dereference pLowerConn + // + NbtDereferenceLowerConnection(pLowerConn); + +} + +//---------------------------------------------------------------------------- +VOID +DpcHandleNewSessionPdu ( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls HandleNewSessionPdu from a Dpc started in + NewSessionCompletionRoutine. + +Arguments: + + +Return Value: + + +--*/ + +{ + CTEMemFree((PVOID)pDpc); + + + HandleNewSessionPdu((tLOWERCONNECTION *)Context,(ULONG)SystemArgument1, + (ULONG)SystemArgument2); + +} + +//---------------------------------------------------------------------------- +VOID +HandleNewSessionPdu ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Offset, + IN ULONG ToGet + ) +/*++ + +Routine Description: + + This routine handles the case when a session pdu starts in the middle of + a data indication from the transport. It gets an Irp from the free list + and formulates a receive to pass to the transport to get that data. The + assumption is that the client has taken all data preceding the next session + pdu. If the client hasn't then this routine should not be called yet. + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + ULONG BytesTaken; + PIRP pIrp; + PFILE_OBJECT pFileObject; + PMDL pMdl; + ULONG BytesToGet; + tCONNECTELE *pConnEle; + + pIrp = NULL; + BytesTaken = 0; + + // we grab the joint lock because it is needed to separate the lower and + // upper connections, so with it we can check if they have been separated. + // + CTESpinLockAtDpc(&NbtConfig.JointLock); + pConnEle = pLowerConn->pUpperConnection; + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + if (!pLowerConn->pUpperConnection) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + KdPrint(("Nbt:Unable to get an Irp - Closing Connection!!\n",0)); + status = OutOfRsrcKill(pLowerConn); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + CTESpinLockAtDpc(pLowerConn); + // + // be sure the connection has not disconnected in the meantime... + // + if (pLowerConn->State != NBT_SESSION_UP) + { + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + // + // remove the reference from CompletionRcv + // + NbtDereferenceLowerConnection(pLowerConn); + return; + } + + pFileObject = pLowerConn->pFileObject; + + // use the indication buffer for the receive. + pMdl = pLowerConn->pIndicateMdl; + + // this flag is set below so we know if there is data in the indicate buffer + // or not. + if (Offset) + { + PVOID NewAddress; + PMDL pNewMdl; + + // there is still data in the indication buffer ,so only + // fill the empty space. This means adjusting the Mdl to + // to only map the last portion of the Indication Buffer + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pMdl) + + Offset); + + // create a partial MDL so that the new data is copied after the existing data + // in the MDL. + // + // 0 for length means map the rest of the buffer + // + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pMdl,pNewMdl,NewAddress,0); + + pMdl = pNewMdl; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Mapping IndicBuffer to partial Mdl Offset=%X, ToGet=%X %X\n", + Offset,ToGet, + pLowerConn)); + } + else + { + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + } + + // + // Only get the amount of data specified, which is either the 4 byte header + // or the rest of the pdu so that we never have + // more than one session pdu in the indicate buffer. + // + BytesToGet = ToGet; + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pFileObject), + pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pMdl, + (ULONG)TDI_RECEIVE_NORMAL, + BytesToGet); // only ask for the number of bytes left and no more + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(IoGetRelatedDeviceObject(pFileObject),pIrp); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NewSessionCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of the receive to get the remaining + data left in the transport when a session PDU starts in the middle of + an indication from the transport. This routine is run as the completion + of a recv Irp passed to the transport by NBT, to get the remainder of the + data in the transport. + + The routine then calls the normal receive handler, which can either + consume the data or pass back an Irp. If an Irp is passed back then + the data is copied into that irp in this routine. + +Arguments: + + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + ULONG BytesTaken; + tCONNECTELE *pConnEle; + PVOID pData; + KIRQL OldIrq; + PMDL pMdl; + ULONG BytesIndicated; + ULONG BytesAvailable; + PKDPC pDpc; + tLOWERCONNECTION *pLowerConn; + ULONG Length; + ULONG PduLen; + PIRP pRetIrp; + + // we grab the joint lock because it is needed to separate the lower and + // upper connections, so with it we can check if they have been separated. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pLowerConn = (tLOWERCONNECTION *)pContext; + pConnEle = pLowerConn->pUpperConnection; + + CTESpinLockAtDpc(pLowerConn); + + // a disconnect indication can come in any time and separate the lower and + // upper connections, so check for that + // + if (!pConnEle) + { + CTESpinFreeAtDpc(&NbtConfig.JointLock); + status = STATUS_UNSUCCESSFUL; + goto ExitRoutine; + } + + CTESpinFreeAtDpc(&NbtConfig.JointLock); + + + BytesTaken = 0; + + pMdl = pLowerConn->pIndicateMdl; + + pData = MmGetMdlVirtualAddress(pMdl); + + // + // The Indication buffer may have more data in it than what we think + // was left in the transport, because the transport may have received more + // data in the intervening time. Check for this case. + // + if (pIrp->IoStatus.Information > pConnEle->BytesInXport) + { + // no data left in transport + // + CHECK_PTR(pConnEle); + pConnEle->BytesInXport = 0; + } + else + { + // + // subtract what we just retrieved from the transport, from the count + // of data left in the transport + // + pConnEle->BytesInXport -= pIrp->IoStatus.Information; + } + // put the irp back on its free list + CHECK_PTR(pIrp); + pIrp->MdlAddress = NULL; + + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + + // + // there may be data still in the indication buffer, + // so add that amount to what we just received. + // + pLowerConn->BytesInIndicate += (USHORT)pIrp->IoStatus.Information; + BytesIndicated = pLowerConn->BytesInIndicate; + // + // we need to set the bytes available to be the data in the Xport + the + // bytes in the indicate buffer, so that + // ReceiveIndicated gets set to the correct value if the client does + // not take all of data + // + BytesAvailable = pConnEle->BytesInXport + BytesIndicated; + pRetIrp = NULL; + + // if the number of bytes is 4 then we just have the header and must go + // back to the transport for the rest of the pdu, or we have a keep + // alive pdu... + // + // + // This could be a session keep alive pdu so check the pdu type. Keep + // alives just go to the RcvHndlrNotOs routine and return, doing nothing. + // They have a length of zero, so the overall length is 4 and they could + // be confused for session pdus otherwise. + // + status = STATUS_SUCCESS; + if (BytesIndicated == sizeof(tSESSIONHDR)) + { + + PUSH_LOCATION(0x1e) + if (((tSESSIONHDR UNALIGNED *)pData)->Type == NBT_SESSION_MESSAGE) + { + // if there is still data in the transport we must send down an + // irp to get the data, however, if there is no data left in + // the transport, then the data will come up on its own, into + // the indicate_buffer case in the main Receivehandler. + // + if (pConnEle->BytesInXport) + { + PUSH_LOCATION(0x1e); + + // tell the DPC routine to get the data at an offset of 4 for length Length + + // + // this is the first indication to find out how large the pdu is, so + // get the length and go get the rest of the pdu. + // + Length = myntohl(((tSESSIONHDR UNALIGNED *)pData)->UlongLength); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got Pdu Hdr in sessioncmplionroutine, PduLen =%X\n",Length)); + + // it is possible to get a zero length pdu, in which case we + // do NOT need to go to the transport to get more data + // + if (Length) + { + PUSH_LOCATION(0x1e); + // + // now go get this amount of data and add it to the header + // + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('r')); + + ASSERT(pDpc); + + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + + + CTESpinFree(pLowerConn,OldIrq); + + // check that the pdu is not going to overflow the indicate buffer. + // + if (Length > NBT_INDICATE_BUFFER_SIZE - sizeof(tSESSIONHDR)) + { + Length = NBT_INDICATE_BUFFER_SIZE - sizeof(tSESSIONHDR); + } + + ASSERTMSG("Nbt:Getting ZERO bytes from Xport!!\n",Length); + + KeInsertQueueDpc(pDpc,(PVOID)sizeof(tSESSIONHDR),(PVOID)Length); + + // clean up the partial mdl since we are going to turn around and reuse + // it in HandleNewSessionPdu above.. + // + // THIS CALL SHOULD NOT BE NEEDED SINCE THE INDICATE BUFFER IS NON_PAGED + // POOL +// MmPrepareMdlForReuse(pConnEle->pNewMdl); + + // return this status to stop to tell the io subsystem to stop processing + // this irp when we return it. + // + return(STATUS_MORE_PROCESSING_REQUIRED); + } + } + + + } + } + + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:NewSessComplRcv BytesinXport= %X,InIndicate=%X Indic. %X,Avail=%X %X\n", + pConnEle->BytesInXport,pLowerConn->BytesInIndicate,BytesIndicated, + BytesAvailable,pConnEle->pLowerConnId)); + + + if (!NT_SUCCESS(pIrp->IoStatus.Status)) + { + ASSERTMSG("Nbt:Not Expecting a Bad Status Code\n",0); + goto ExitRoutine; + } + + // + // check if we have a whole pdu in the indicate buffer or not. IF not + // then just return and wait for more data to hit the TdiReceiveHandler + // code. This check passes KeepAlives correctly since they have a pdu + // length of 0, and adding the header gives 4, their overall length. + // + PduLen = myntohl(((tSESSIONHDR UNALIGNED *)pData)->UlongLength); + if ((BytesIndicated < PduLen + sizeof(tSESSIONHDR)) && + (BytesIndicated != NBT_INDICATE_BUFFER_SIZE)) + + { + PUSH_LOCATION(0x1f); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Returning in NewSessionCompletion BytesIndicated = %X\n", + BytesIndicated)); + } + else + { + PUSH_LOCATION(0x20); + + status = CopyDataandIndicate( + NULL, + (PVOID)pLowerConn, + 0, // rcv flags + BytesIndicated, + BytesAvailable, + &BytesTaken, + pData, + (PVOID)&pRetIrp); + +// status = STATUS_MORE_PROCESSING_REQUIRED; + + } + +ExitRoutine: + // + // check if an irp is passed back, so we don't Deref in that case. + // + if (status != STATUS_MORE_PROCESSING_REQUIRED) + { + // + // quickly check if we can just decrement the ref count without calling + // nbtDereferenceConnection + // + PUSH_LOCATION(0x51); + DerefLowerConnFast(pLowerConn,pConnEle,OldIrq); + } + else + { + CTESpinFree(pLowerConn,OldIrq); + } + + + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NtBuildIndicateForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine sets up the indicate buffer to get data from the transport + when the indicate buffer already has some data in it. A partial MDL is + built and the attached to the irp. + before we indicate. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PIRP pIrp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + tCONNECTELE *pConnEle; + PMDL pNewMdl; + PVOID NewAddress; + + // + // get an Irp from the list + // + + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Unable to get Irp, Kill connection\n")); + + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pConnEle= pLowerConn->pUpperConnection; + + NewAddress = (PVOID)((PCHAR)MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl) + + pLowerConn->BytesInIndicate); + + // create a partial MDL so that the new data is copied after the existing data + // in the MDL. + // + // 0 for length means map the rest of the buffer + // + pNewMdl = pConnEle->pNewMdl; + + IoBuildPartialMdl(pLowerConn->pIndicateMdl,pNewMdl,NewAddress,0); + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pLowerConn->pFileObject), + pLowerConn->pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pNewMdl, + (ULONG)TDI_RECEIVE_NORMAL, + Length); + + // + // we need to set the next Irp stack location because this irp is returned + // as a return parameter rather than being passed through IoCallDriver + // which increments the stack location itself + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + *ppIrp = (PVOID)pIrp; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +NtBuildIrpForReceive ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG Length, + OUT PVOID *ppIrp + ) +/*++ + +Routine Description: + + This routine gets an Irp to be used to receive data and hooks the indication + Mdl to it, so we can accumulate at least 128 bytes of data for the client + before we indicate. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PIRP pIrp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + PIO_STACK_LOCATION pIrpSp; + + // + // get an Irp from the list + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("NBT:Unable to get Irp, Kill connection\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + + TdiBuildReceive( + pIrp, + IoGetRelatedDeviceObject(pLowerConn->pFileObject), + pLowerConn->pFileObject, + NewSessionCompletionRoutine, + (PVOID)pLowerConn, + pLowerConn->pIndicateMdl, + (ULONG)TDI_RECEIVE_NORMAL, + Length); + + // + // we need to set the next Irp stack location because this irp is returned + // as a return parameter rather than being passed through IoCallDriver + // which increments the stack location itself + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + *ppIrp = (PVOID)pIrp; + + return(STATUS_SUCCESS); +} + +#pragma inline_depth(0) +//---------------------------------------------------------------------------- +NTSTATUS +CopyDataandIndicate( + IN PVOID ReceiveEventContext, + IN PVOID ConnectionContext, + IN USHORT ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT PULONG BytesTaken, + IN PVOID pTsdu, + OUT PIRP *ppIrp + ) +/*++ + +Routine Description: + + + This routine combines data indicated with the indicate buffer to + indicate the total to the client. Any bytes Indicated are those bytes + in the indicate buffer. Bytes available adds in any bytes in the transport. + + The idea here is to copy as much as possible from the indicate buffer and + then pass back an irp if there is still more data in the transport. If + no data left in the transport, this routine completes the client irp and + returns STATUS_SUCCESS. + +Arguments: + + +Return Value: + + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tLOWERCONNECTION *pLowerConn; + tCONNECTELE *pConnEle; + ULONG BytesCopied; + ULONG Indicated; + ULONG Available; + ULONG Taken; + ULONG AmountAlreadyInIndicateBuffer; + PVOID pBuffer; + PIRP pIrp; + BOOLEAN bReIndicate=FALSE; + ULONG RemainingPdu; + ULONG ToCopy; + PKDPC pDpc; + ULONG SaveInXport; + ULONG PduSize; + + pLowerConn = (tLOWERCONNECTION *)ConnectionContext; + pConnEle = pLowerConn->pUpperConnection; + + AmountAlreadyInIndicateBuffer = pLowerConn->BytesInIndicate; + + // + // set the parameters for the call to the TdiReceiveHandler routine + // + + Indicated = BytesIndicated; + Available = BytesAvailable; + Taken = 0; + + + ASSERT(pLowerConn->StateRcv == INDICATE_BUFFER); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Amount In Indicate = %X\n",AmountAlreadyInIndicateBuffer)); + + // now that we have 128 bytes (plus the session hdr = 132 total) we + // can indicate to the client + + pBuffer = MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:FromCopyData, BytesAvail= %X,BytesInd= %X,BytesRcvd= %X,Amount=%X, %X,state=%X,RcvEC=%X\n", + Available,Indicated,pConnEle->BytesRcvd, + AmountAlreadyInIndicateBuffer,pLowerConn,pLowerConn->StateRcv, + ReceiveEventContext)); + + pIrp = NULL; + + // + // Reset this count so that the routine processes the Session header correctly + // + CHECK_PTR(pConnEle); + pConnEle->BytesRcvd = 0; + PUSH_LOCATION(0x21); + status = RcvHandlrNotOs( + NULL, + ConnectionContext, + ReceiveFlags, + Indicated, + Available, + &Taken, + pBuffer, + (PVOID)&pIrp + ); + + // + // if the connection has disonnected, then just return + // + if (!pLowerConn->pUpperConnection) + { + *BytesTaken = BytesAvailable; + return(STATUS_SUCCESS); + } + + // do not use pConnEle->TotalPcktLen here becauase it won't be set for + // keep alives - must use actual buffer to get length. + PduSize = myntohl(((tSESSIONHDR UNALIGNED *)pBuffer)->UlongLength) + sizeof(tSESSIONHDR); + + RemainingPdu = pConnEle->TotalPcktLen - pConnEle->BytesRcvd; + + if (Taken <= pLowerConn->BytesInIndicate) + { + pLowerConn->BytesInIndicate -= (USHORT)Taken; + } + else + { + pLowerConn->BytesInIndicate = 0; + } + + if (pIrp) + { + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_RECEIVE pParams; + ULONG ClientRcvLen; + + PUSH_LOCATION(0x22); + // + // BytesInXport will be recalculated by ProcessIrp based on BytesAvailable + // and the ClientRcvLength, so set it to 0 here. + // + SaveInXport = pConnEle->BytesInXport; + CHECK_PTR(pConnEle); + pConnEle->BytesInXport = 0; + status = ProcessIrp(pLowerConn, + pIrp, + pBuffer, + &Taken, + Indicated, + Available); + + // + // copy the data in the indicate buffer that was not taken by the client + // into the MDL and then update the bytes taken and pass the irp on downwar + // to the transport + // + ToCopy = Indicated - Taken; + + // the Next stack location has the correct info in it because we + // called TdiRecieveHandler with a null ReceiveEventContext, + // so that routine does not increment the stack location + // + pIrpSp = IoGetNextIrpStackLocation(pIrp); + pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters; + ClientRcvLen = pParams->ReceiveLength; + + // did the client's Pdu fit entirely into the indication buffer? + // + if (ClientRcvLen <= ToCopy) + { + PUSH_LOCATION(0x23); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Took some(or all) RemainingPdu= %X, ClientRcvLen= %X,InXport=%X %X\n", + RemainingPdu,ClientRcvLen,pConnEle->BytesInXport,pLowerConn)); + + // if ProcessIrp has recalculated the bytes in the Xport + // then set it back to where it should be, Since ProcessIrp will + // put all not taken bytes as bytes in the transport - but some + // of the bytes are still in the indicate buffer. + // + pConnEle->BytesInXport = SaveInXport; + + // it could be a zero length send where the client returns a null + // mdl, or the client returns an mdl and the RcvLen is really zero. + // + if (pIrp->MdlAddress && ClientRcvLen) + { + TdiCopyBufferToMdl(pBuffer, // indicate buffer + Taken, // src offset + ClientRcvLen, + pIrp->MdlAddress, + 0, // dest offset + &BytesCopied); + } + else + BytesCopied = 0; + + // + // check for data still in the transport - subtract data copied to + // Irp, since Taken was already subtracted. + // + pLowerConn->BytesInIndicate -= (USHORT)BytesCopied; + + *BytesTaken = Taken + BytesCopied; + ASSERT(BytesCopied == ClientRcvLen); + + // the client has received all of the data, so complete his irp + // + pIrp->IoStatus.Information = BytesCopied; + pIrp->IoStatus.Status = STATUS_SUCCESS; + + // since we are completing it and TdiRcvHandler did not set the next + // one. + // + ASSERT(pIrp->CurrentLocation > 1); + + // since we are completing the irp here, no need to call + // this, because it will complete through completionrcv. + IoSetNextIrpStackLocation(pIrp); + + // there should not be any data in the indicate buffer since it + // only holds either 132 bytes or a whole pdu unless the client + // receive length is too short... + // + if (pLowerConn->BytesInIndicate) + { + PUSH_LOCATION(0x23); + // when the irp goes through completionRcv it should set the + // state to PartialRcv and the next posted buffer from + // the client should pickup this data. + CopyToStartofIndicate(pLowerConn,(Taken+BytesCopied)); + } + else + { + // + // this will complete through CompletionRcv and for that + // reason it will get any more data left in the transport. The + // Completion routine will set the correct state for the rcv when + // it processes this Irp ( to INDICATED, if needed). ProcessIrp + // may have set ReceiveIndicated, so that CompletionRcv will + // set the state to PARTIAL_RCV when it runs. + // + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + } + + CTESpinFreeAtDpc(pLowerConn); + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + CTESpinLockAtDpc(pLowerConn); + // + // this was undone by CompletionRcv, so redo them, since the + // caller will undo them again. + // + pLowerConn->RefCount++; + + return(STATUS_SUCCESS); + } + else + { + + PUSH_LOCATION(0x24); + // + // there is still data that we need to get to fill the PDU. There + // may be more data left in the transport or not after the irp is + // filled. + // In either case the Irps' Mdl must be adjusted to account for + // filling part of it. + // + TdiCopyBufferToMdl(pBuffer, // IndicateBuffer + Taken, // src offset + ToCopy, + pIrp->MdlAddress, + 0, // dest offset + &BytesCopied); + + // + // save the Mdl so we can reconstruct things later + // + pLowerConn->pMdl = pIrp->MdlAddress; + pConnEle->pNextMdl = pIrp->MdlAddress; + ASSERT(pIrp->MdlAddress); + // + // The irp is being passed back to the transport, so we NULL + // our ptr to it so we don't try to cancel it on a disconnect + // + CHECK_PTR(pConnEle); + pConnEle->pIrpRcv = NULL; + + // Adjust the number of bytes in the Mdl chain so far since the + // completion routine will only count the bytes filled in by the + // transport + // + pConnEle->BytesRcvd += BytesCopied; + + *BytesTaken = BytesIndicated; + + // + // clear the number of bytes in the indicate buffer since the client + // has taken more than the data left in the Indicate buffer + // + CHECK_PTR(pLowerConn); + pLowerConn->BytesInIndicate = 0; + + // decrement the client rcv len by the amount already put into the + // client Mdl + // + ClientRcvLen -= BytesCopied; + // + // if ProcessIrp did recalculate the bytes in the transport + // then set back to what it was. Process irp will do this + // recalculation if the clientrcv buffer is too short only. + // + pConnEle->BytesInXport = SaveInXport; + + // + // adjust the number of bytes downward due to the client rcv + // buffer + // + if (ClientRcvLen < SaveInXport) + { + PUSH_LOCATION(0x24); + pConnEle->BytesInXport -= ClientRcvLen; + } + else + { + pConnEle->BytesInXport = 0; + } + + // ProcessIrp will set bytesinXport and ReceiveIndicated - since + // the indicate buffer is empty that calculation of BytesInXport + // will be correct. + // + + // We MUST set the state to FILL_IRP so that completion Rcv + // undoes the partial MDL stuff - i.e. it puts the original + // MdlAddress in the Irp, rather than the partial Mdl address. + // CompletionRcv will set the state to partial Rcv if ReceiveIndicated + // is not zero. + // + pLowerConn->StateRcv = FILL_IRP; + pLowerConn->CurrentStateProc = FillIrp; + + // the client is going to take more data from the transport with + // this Irp. Set the new Rcv Length that accounts for the data just + // copied to the Irp. + // + pParams->ReceiveLength = ClientRcvLen; + + // keep track of data in MDL so we know when it is full and we need to + // return it to the user - ProcessIrp set it to ClientRcvLen, so + // shorten it here. + // + pConnEle->FreeBytesInMdl -= BytesCopied; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:ClientRcvLen = %X, LeftinXport= %X RemainingPdu= %X %X\n",ClientRcvLen, + pConnEle->BytesInXport,RemainingPdu,pLowerConn)); + + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + MakePartialMdl(pConnEle,pIrp,BytesCopied); + + *ppIrp = pIrp; + + // increments the stack location, since TdiReceiveHandler did not. + // + if (ReceiveEventContext) + { + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + else + { + // pass the Irp to the transport since we were called from + // NewSessionCompletionRoutine + // + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Calling IoCallDriver\n")); + ASSERT(pIrp->CurrentLocation > 1); + + CTESpinFreeAtDpc(pLowerConn); + CHECK_COMPLETION(pIrp); + IoCallDriver(IoGetRelatedDeviceObject(pLowerConn->pFileObject),pIrp); + CTESpinLockAtDpc(pLowerConn); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + + } + } + else + { + PUSH_LOCATION(0x54); + // + // no Irp passed back, the client just took some or all of the data + // + *BytesTaken = Taken; + pLowerConn->BytesRcvd += Taken - sizeof(tSESSIONHDR); + + ASSERT(*BytesTaken < 0x7FFFFFFF ); + + // + // if more than the indicate buffer is taken, then the client + // is probably trying to say it doesn't want any more of the + // message. + // + if (Taken > BytesIndicated) + { + // + // in this case the client has taken more than the indicated. + // We set bytesavailable to the message length in RcvHndlrNotOs, + // so the client has probably said BytesTaken=BytesAvailable. + // So kill the connection + // because we have no way of handling this case here, since + // part of the message may still be in the transport, and we + // might have to send the indicate buffer down there multiple + // times to get all of it...a mess! The Rdr only sets bytestaken = + // bytesAvailable under select error conditions anyway. + // + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + + *BytesTaken = BytesAvailable; + + } + else + if (pLowerConn->StateRcv == PARTIAL_RCV) + { + // this may be a zero length send -that the client has + // decided not to accept. If so then the state will be set + // to PartialRcv. In this case do NOT go down to the transport + // and get the rest of the data, but wait for the client + // to post a rcv buffer. + // + PUSH_LOCATION(0x54); + return(STATUS_SUCCESS); + } + else + if (Taken == PduSize) + { + // + // Must have taken all of the pdu data, so check for + // more data available - if so send down the indicate + // buffer to get it. + // + if (pConnEle->BytesInXport) + { + PUSH_LOCATION(0x28); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:CopyData BytesInXport= %X, %X\n",pConnEle->BytesInXport, + pLowerConn)); + + // + // there is still data in the transport so Q a Dpc to use + // the indicate buffer to get the data + // + pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('s')); + + if (pDpc) + { + KeInitializeDpc(pDpc, + DpcHandleNewSessionPdu, + (PVOID)pLowerConn); + + pLowerConn->StateRcv = INDICATE_BUFFER; + pLowerConn->CurrentStateProc = IndicateBuffer; + + // get just the header first to see how large the pdu is + // + pLowerConn->RefCount++; + KeInsertQueueDpc(pDpc,NULL,(PVOID)sizeof(tSESSIONHDR)); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + OutOfRsrcKill(pLowerConn); + CTESpinLockAtDpc(pLowerConn); + } + + } + else + { + PUSH_LOCATION(0x29); + // + // clear the flag saying that we are using the indicate buffer + // + pLowerConn->StateRcv = NORMAL; + pLowerConn->CurrentStateProc = Normal; + + } + + PUSH_LOCATION(0x2a); + return(STATUS_SUCCESS); + + } + else + { + // + // the client may have taken all the data in the + // indication!!, in which case return status success + // Note: that we check bytes available here not bytes + // indicated - since the client could take all indicated + // data but still leave data in the transport. If the client + // got told there was more available but only took the indicated, + // the we need to do the else and track ReceiveIndicated, but if + // Indicated == Available, then we take the if and wait for + // another indication from the transport. + // + if (Taken == BytesAvailable) + { + PUSH_LOCATION(0x4); + status = STATUS_SUCCESS; + + } + else + { + + // did not take all of the data in the Indication + // + + PUSH_LOCATION(0x2b); + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Took Part of indication... BytesRemaining= %X, LeftInXport= %X, %X\n", + pLowerConn->BytesInIndicate,pConnEle->BytesInXport,pLowerConn)); + + // + // The amount of data Indicated to the client should not exceed + // the Pdu size, so check that, since this routine could get + // called with bytesAvailable > than the Pdu size. + // + // That is checked above where we check if Taken > BytesIndicated. + + SaveInXport = pConnEle->BytesInXport; + ASSERT(Taken <= PduSize); + status = ClientTookSomeOfTheData(pLowerConn, + Indicated, + Available, + Taken, + PduSize); + + // + // Since the data may be divided between some in the transport + // and some in the indicate buffer do not let ClientTookSomeOf... + // recalculate the amount in the transport, since it assumes all + // untaken data is in the transport. Since the client did not + // take of the indication, the Bytes in Xport have not changed. + // + pConnEle->BytesInXport = SaveInXport; + // + // need to move the data forward in the indicate buffer so that + // it begins at the start of the buffer + // + if (Taken) + { + CopyToStartofIndicate(pLowerConn,Taken); + } + + } + } + + } + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiConnectHandler ( + IN PVOID pConnectEventContext, + IN int RemoteAddressLength, + IN PVOID pRemoteAddress, + IN int UserDataLength, + IN PVOID UNALIGNED pUserData, + IN int OptionsLength, + IN PVOID pOptions, + OUT CONNECTION_CONTEXT *pConnectionContext, + OUT PIRP *ppAcceptIrp + ) +/*++ + +Routine Description: + + This routine is connect event handler. It is invoked when a request for + a connection has been received by the provider. NBT accepts the connection + on one of its connections in its LowerConnFree list + + Initially a TCP connection is setup with this port. Then a Session Request + packet is sent across the connection to indicate the name of the destination + process. This packet is received in the RcvHandler. + +Arguments: + + pConnectEventContext - the context passed to the transport when this event was setup + RemoteAddressLength - the length of the source address (4 bytes for IP) + pRemoteAddress - a ptr to the source address + UserDataLength - the number of bytes of user data - includes the session Request hdr + pUserData - ptr the the user data passed in + OptionsLength - number of options to pass in + pOptions - ptr to the options + +Return Value: + + pConnectionContext - connection context returned to the transport(connection to use) + + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT pFileObject; + PIRP pRequestIrp; + CONNECTION_CONTEXT pConnectionId; + tDEVICECONTEXT *pDeviceContext; + + *pConnectionContext = NULL; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)pConnectEventContext; + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("pDeviceContxt = %X ConnectEv = %X",pDeviceContext,pConnectEventContext)); + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + + // call the non-OS specific routine to find a free connection. + + status = ConnectHndlrNotOs( + pConnectEventContext, + RemoteAddressLength, + pRemoteAddress, + UserDataLength, + pUserData, + &pConnectionId); + + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("NO FREE CONNECTIONS in connect handler\n")); + + // put the Irp back on its free list + // + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_DATA_NOT_ACCEPTED); + } + + pFileObject = ((tLOWERCONNECTION *)pConnectionId)->pFileObject; + + TdiBuildAccept( + pRequestIrp, + IoGetRelatedDeviceObject(pFileObject), + pFileObject, + AcceptCompletionRoutine, + (PVOID)pConnectionId, + NULL, + NULL); + + // we need to null the MDL address because the transport KEEPS trying to + // release buffers!! which do not exist!!! + // + CHECK_PTR(pRequestIrp); + pRequestIrp->MdlAddress = NULL; + + + // return the connection id to accept the connect indication on. + *pConnectionContext = (CONNECTION_CONTEXT)pConnectionId; + *ppAcceptIrp = pRequestIrp; + // + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with Connect Indication. + // + ASSERT(pRequestIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pRequestIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); +} + +//---------------------------------------------------------------------------- +NTSTATUS +AcceptCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of an Accept to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + tLOWERCONNECTION *pLowerConn; + CTELockHandle OldIrq; + tDEVICECONTEXT *pDeviceContext; + + pLowerConn = (tLOWERCONNECTION *)pContext; + pDeviceContext = pLowerConn->pDeviceContext; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + CTESpinLockAtDpc(pLowerConn); + // + // if the connection disconnects before the connect accept irp (this irp) + // completes do not put back on the free list here but let nbtdisconnect + // handle it. + // (i.e if the state is no longer INBOUND, then don't touch the connection + // + if ((!NT_SUCCESS(pIrp->IoStatus.Status)) && + (pLowerConn->State == NBT_SESSION_INBOUND)) + { + + // + // the accept failed, so close the connection and create + // a new one to be sure all activity is run down on the connection. + // + + pLowerConn->State = NBT_IDLE; + + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + KdPrint(("AcceptCompletionRoutine: error: %lx\n", pIrp->IoStatus.Status)); + + CTEQueueForNonDispProcessing( + NULL, + pLowerConn, + NULL, + CleanupAfterDisconnect, + pDeviceContext); + + PUSH_LOCATION(0x93); + } + else + { + CTESpinFreeAtDpc(pLowerConn); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + // put the Irp back on its free list + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is not initiating thread - we are the initiator + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiDisconnectHandler ( + IN PVOID EventContext, + IN PVOID ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID pDisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID pDisconnectInformation, + IN ULONG DisconnectIndicators + ) +/*++ + +Routine Description: + + This routine is called when a session is disconnected from a remote + machine. + +Arguments: + + IN PVOID EventContext, + IN PCONNECTION_CONTEXT ConnectionContext, + IN ULONG DisconnectDataLength, + IN PVOID DisconnectData, + IN ULONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectIndicators + +Return Value: + + NTSTATUS - Status of event indicator + +--*/ + +{ + + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext; + + // convert the context value into the device context record ptr + pDeviceContext = (tDEVICECONTEXT *)EventContext; + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("pDeviceContxt = %X ConnectEv = %X\n",pDeviceContext,ConnectionContext)); + ASSERTMSG("Bad Device context passed to the Connection Event Handler", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT); + + // call the non-OS specific routine to find a free connection. + + status = DisconnectHndlrNotOs( + EventContext, + ConnectionContext, + DisconnectDataLength, + pDisconnectData, + DisconnectInformationLength, + pDisconnectInformation, + DisconnectIndicators); + + if (!NT_SUCCESS(status)) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("NO FREE CONNECTIONS in connect handler\n")); + return(STATUS_DATA_NOT_ACCEPTED); + } + + + return status; + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiRcvDatagramHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT PIRP *pIoRequestPacket + ) +/*++ + +Routine Description: + + This routine is the receive datagram event indication handler. + + It is called when an Datagram arrives from the network, it will look for a + the address with an appropriate read datagram outstanding or a Datagrm + Event handler setup. + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tDGRAMHDR UNALIGNED *pDgram = (tDGRAMHDR UNALIGNED *)pTsdu; + PIRP pIrp = NULL; + ULONG lBytesTaken; + tCLIENTLIST *pClientList; + + + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT receive datagram handler pDeviceContext: %X\n", + pDeviceContext )); + + *pIoRequestPacket = NULL; + + ASSERTMSG("NBT:Invalid Device Context passed to DgramRcv Handler!!\n", + pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); + + // call a non-OS specific routine to decide what to do with the datagrams + pIrp = NULL; + pClientList = NULL; + status = DgramHndlrNotOs( + pDgramEventContext, + SourceAddressLength, + pSourceAddress, + OptionsLength, + pOptions, + ReceiveDatagramFlags, + BytesIndicated, + BytesAvailable, + &lBytesTaken, + pTsdu, + (PVOID *)&pIrp, + &pClientList); + + + if ( !NT_SUCCESS(status) ) + { + // fail the request back to the transport provider since we + // could not find a receive buffer or receive handler or the + // data was taken in the indication handler. + // + return(STATUS_DATA_NOT_ACCEPTED); + + } + else + { + // a rcv buffer was returned, so use it for the receive.(an Irp) + PTDI_REQUEST_KERNEL_RECEIVEDG pParams; + PIO_STACK_LOCATION pIrpSp; + ULONG lRcvLength; + ULONG lRcvFlags; + + // When the client list is returned, we need to make up an irp to + // send down to the transport, which we will use in the completion + // routine to copy the data to all clients, ONLY if we are not + // using a client buffer, so check that flag first. + // + if (pClientList && !pClientList->fUsingClientBuffer) + { + PMDL pMdl; + PVOID pBuffer; + + // + // get an irp to do the receive with and attach + // a buffer to it. + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + return(STATUS_DATA_NOT_ACCEPTED); + + // + // make an Mdl for a buffer to get all of the data from + // the transprot + // + pBuffer = NbtAllocMem(BytesAvailable,NBT_TAG('t')); + + pMdl = NULL; + if (pBuffer) + { + // Allocate a MDL and set the header sizes correctly + pMdl = IoAllocateMdl( + pBuffer, + BytesAvailable, + FALSE, + FALSE, + NULL); + + } + if (!pMdl) + { + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + if (pBuffer) + CTEMemFree(pBuffer); + + //ASSERT(pMdl); + KdPrint(("Nbt:Unable to get Mdl, Kill Connection\n")); + return(STATUS_DATA_NOT_ACCEPTED); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + pIrp->MdlAddress = pMdl; + ASSERT(pMdl); + + lRcvFlags = 0; + + lRcvLength = BytesAvailable; + } + else + { + ASSERT(pIrp); + // *TODO* may have to keep track of the case where the + // client returns a buffer that is not large enough for all of the + // data indicated. So the next posting of a buffer gets passed + // directly to the transport. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + lRcvFlags = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters)->ReceiveFlags; + + lRcvLength = ((PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters)->ReceiveLength; + + if (lRcvLength < BytesIndicated - lBytesTaken) + { + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(("Nbt:Clients Buffer is too short on Rcv Dgram size= %X, needed = %X\n", + lRcvLength, BytesIndicated-lBytesTaken)); + } + } + + + // this code is sped up somewhat by expanding the code here rather than calling + // the TdiBuildReceive macro + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->CompletionRoutine = CompletionRcvDgram; + + // pass the ClientList to the completion routine so it can + // copy the datagram to several clients that may be listening on the + // same name + // + pIrpSp->Context = (PVOID)pClientList; + CHECK_PTR(pIrpSp); + pIrpSp->Flags = 0; + + // set flags so the completion routine is always invoked. + pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + pIrpSp->MinorFunction = TDI_RECEIVE_DATAGRAM; + pIrpSp->DeviceObject = pDeviceContext->pDgramDeviceObject; + pIrpSp->FileObject = pDeviceContext->pDgramFileObject; + + pParams = (PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters; + pParams->ReceiveFlags = lRcvFlags; + pParams->ReceiveLength = lRcvLength; + + // pass back the irp to the transport provider and increment the stack + // location so it can write to the irp if it needs to. + *pIoRequestPacket = pIrp; + *pBytesTaken = lBytesTaken; + + return(STATUS_MORE_PROCESSING_REQUIRED); + + } + + // + // Transport will complete the processing of the request, we don't + // want the datagram. + // + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT receive datagram handler ignored receive, pDeviceContext: %X\n", + pDeviceContext )); + + return STATUS_DATA_NOT_ACCEPTED; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiRcvNameSrvHandler( + IN PVOID pDgramEventContext, + IN int SourceAddressLength, + IN PVOID pSourceAddress, + IN int OptionsLength, + IN PVOID pOptions, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *pBytesTaken, + IN PVOID pTsdu, + OUT PIRP *pIoRequestPacket + ) +/*++ + +Routine Description: + + This routine is the Name Service datagram event indication handler. + It gets all datagrams destined for UDP port 137 + + +Arguments: + + pDgramEventContext - Context provided for this event - pab + SourceAddressLength, - length of the src address + pSourceAddress, - src address + OptionsLength, - options length for the receive + pOptions, - options + BytesIndicated, - number of bytes this indication + BytesAvailable, - number of bytes in complete Tsdu + pTsdu - pointer to the datagram + + +Return Value: + + *pBytesTaken - number of bytes used + *IoRequestPacket - Receive IRP if MORE_PROCESSING_REQUIRED. + NTSTATUS - Status of receive operation + +--*/ + +{ + NTSTATUS status; + tDEVICECONTEXT *pDeviceContext = (tDEVICECONTEXT *)pDgramEventContext; + tNAMEHDR UNALIGNED *pNameSrv = (tNAMEHDR UNALIGNED *)pTsdu; + USHORT OpCode; + + + IF_DBG(NBT_DEBUG_TDIHNDLR) + KdPrint(( "NBT: NAMEHDR datagram handler pDeviceContext: %X\n", + pDeviceContext )); + + *pIoRequestPacket = NULL; + // + // check if the whole datagram has arrived yet + // + if (BytesIndicated != BytesAvailable) + { + PIRP pIrp; + PVOID pBuffer; + PMDL pMdl; + ULONG Length; + + // + // get an irp to do the receive with and attach + // a buffer to it. + // + status = GetIrp(&pIrp); + + if (!NT_SUCCESS(status)) + return(STATUS_DATA_NOT_ACCEPTED); + + // + // make an Mdl for a buffer to get all of the data from + // the transprot + // + Length = BytesAvailable + SourceAddressLength + sizeof(ULONG); + Length = ((Length + 3)/sizeof(ULONG)) * sizeof(ULONG); + pBuffer = NbtAllocMem(Length,NBT_TAG('u')); + if (pBuffer) + { + PVOID pSrcAddr; + + // + // save the source address and length in the buffer for later + // indication back to this routine. + // + *(ULONG UNALIGNED *)((PUCHAR)pBuffer + BytesAvailable) = SourceAddressLength; + pSrcAddr = (PVOID)((PUCHAR)pBuffer + BytesAvailable + sizeof(ULONG)); + + CTEMemCopy(pSrcAddr, + pSourceAddress, + SourceAddressLength); + + // Allocate a MDL and set the header sizes correctly + pMdl = IoAllocateMdl( + pBuffer, + BytesAvailable, + FALSE, + FALSE, + NULL); + + if (pMdl) + { + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + pIrp->MdlAddress = pMdl; + ASSERT(pMdl); + + TdiBuildReceive( + pIrp, + &pDeviceContext->DeviceObject, + pDeviceContext->pNameServerFileObject, + NameSrvCompletionRoutine, + (PVOID)BytesAvailable, + pMdl, + (ULONG)TDI_RECEIVE_NORMAL, + BytesAvailable); + + *pBytesTaken = 0; + + *pIoRequestPacket = pIrp; + + // make the next stack location the current one. Normally IoCallDriver + // would do this but we are not going through IoCallDriver here, since the + // Irp is just passed back with RcvIndication. + // + ASSERT(pIrp->CurrentLocation > 1); + IoSetNextIrpStackLocation(pIrp); + + return(STATUS_MORE_PROCESSING_REQUIRED); + } + + + } + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + return(STATUS_DATA_NOT_ACCEPTED); + + } + + if (pWinsInfo) + { + USHORT TransactionId; + ULONG SrcAddress; + + SrcAddress = ntohl(((PTDI_ADDRESS_IP)&((PTRANSPORT_ADDRESS)pSourceAddress)->Address[0].Address[0])->in_addr); + // + // Pass To Wins if: + // + // 1) It is a response pdu with the transaction id in the WINS range + // that is not a WACK... OR + // 2) It is a request that is NOT broadcast....and... + // 2) It is a name query(excluding node status requests), + // Allowing queries from other netbt clients + // allowing queries from anyone not on this machine OR + // 3) It is a name release request. OR + // 4) It is a name refresh OR + // 5) It is a name registration + // + OpCode = pNameSrv->OpCodeFlags; + TransactionId = ntohs(pNameSrv->TransactId); + + if (((OpCode & OP_RESPONSE) && + (TransactionId <= WINS_MAXIMUM_TRANSACTION_ID) && + (OpCode != OP_WACK)) + || + (!(OpCode & (OP_RESPONSE | FL_BROADCAST)) && + ( + (((OpCode & NM_FLAGS_MASK) == OP_QUERY) && + (OpCode & FL_RECURDESIRE) && // not node status request + ((TransactionId > WINS_MAXIMUM_TRANSACTION_ID) || + ( !SrcIsUs(SrcAddress) ))) +// (SrcAddress != pDeviceContext->lNameServerAddress ))) + || + (OpCode & (OP_RELEASE | OP_REFRESH)) + || + (OpCode & OP_REGISTRATION) )) ) + { + status = PassNamePduToWins( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated); + +// NbtConfig.DgramBytesRcvd += BytesIndicated; + + // + // if WINS took the data then tell the transport to dump the data + // since we have buffered it already. Otherwise, let nbt take + // a look at the data + // + if (NT_SUCCESS(status)) + { + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + + { + + // DO a quick check of the name to see if it is in the local name table + // and reject it otherwise - for name queries only, if not the proxy + // + if ((BytesIndicated >= NBT_MINIMUM_QUERY) && !(NodeType & PROXY)) + { + ULONG UNALIGNED *pHdr; + ULONG i,lValue; + UCHAR pNameStore[NETBIOS_NAME_SIZE]; + UCHAR *pName; + tNAMEADDR *pNameAddr; + CTELockHandle OldIrq; + + // it must be a name query request, not a response, and not a + // node status request, to enter this special check + // + OpCode = pNameSrv->OpCodeFlags; + if (((OpCode & NM_FLAGS_MASK) == OP_QUERY) && + (!(OpCode & OP_RESPONSE)) && + (OpCode & FL_RECURDESIRE)) // not node status request + { + pHdr = (ULONG UNALIGNED *)pNameSrv->NameRR.NetBiosName; + pName = pNameStore; + + // the Half Ascii portion of the netbios name is always 32 bytes long + for (i=0; i < NETBIOS_NAME_SIZE*2 ;i +=4 ) + { + lValue = *pHdr - 0x41414141; // four A's + pHdr++; + lValue = ((lValue & 0x0F000000) >> 16) + + ((lValue & 0x0F0000) >> 4) + + ((lValue & 0x0F00) >> 8) + + ((lValue & 0x0F) << 4); + *(PUSHORT)pName = (USHORT)lValue; + ((PUSHORT)pName)++; + + } + CTESpinLock(&NbtConfig.JointLock,OldIrq); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pNameStore, + NULL, + &pNameAddr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (!NT_SUCCESS(status)) + { + *pBytesTaken = BytesIndicated; + return(STATUS_DATA_NOT_ACCEPTED); + } + } + } + + + // call a non-OS specific routine to decide what to do with the datagrams + status = NameSrvHndlrNotOs( + pDeviceContext, + pSourceAddress, + pNameSrv, + BytesIndicated, + (BOOLEAN)((ReceiveDatagramFlags & TDI_RECEIVE_BROADCAST) != 0)); + +// NbtConfig.DgramBytesRcvd += BytesIndicated + + } + + return status; + + // to keep the compiler from generating warnings... + UNREFERENCED_PARAMETER( SourceAddressLength ); + UNREFERENCED_PARAMETER( BytesIndicated ); + UNREFERENCED_PARAMETER( BytesAvailable ); + UNREFERENCED_PARAMETER( pBytesTaken ); + UNREFERENCED_PARAMETER( pTsdu ); + UNREFERENCED_PARAMETER( OptionsLength ); + UNREFERENCED_PARAMETER( pOptions ); + +} +//---------------------------------------------------------------------------- +NTSTATUS +NameSrvCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine handles the case when a name service datagram is too + short and and Irp has to be passed back to the transport to get the + rest of the datagram. The irp completes through here when full. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PIRP pIoRequestPacket; + ULONG BytesTaken; + ULONG Offset = (ULONG)Context; + PVOID pBuffer; + ULONG SrcAddressLength; + PVOID pSrcAddress; + + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(("NameSrvCompletionRoutine pRcvBuffer: %X, Status: %X Length %X\n", + Context, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information )); + + pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + SrcAddressLength = *(ULONG UNALIGNED *)((PUCHAR)pBuffer + Offset); + pSrcAddress = (PVOID)((PUCHAR)pBuffer + Offset + sizeof(ULONG)); + // + // just call the regular indication routine as if UDP had done it. + // + TdiRcvNameSrvHandler( + DeviceObject, + SrcAddressLength, + pSrcAddress, + 0, + NULL, + TDI_RECEIVE_NORMAL, + pIrp->IoStatus.Information, + pIrp->IoStatus.Information, + &BytesTaken, + pBuffer, + &pIoRequestPacket ); + + CTEMemFree(MmGetSystemAddressForMdl(pIrp->MdlAddress)); + + IoFreeMdl(pIrp->MdlAddress); + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +CompletionRcvDgram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine completes the Irp by removing the Rcv Element off the queue + and putting it back on the free list. + +Arguments: + + DeviceObject - unused. + + Irp - Supplies Irp that the transport has finished processing. + + Context - Supplies the pConnectEle - the connection data structure + +Return Value: + + The final status from the operation (success or an exception). + +--*/ +{ + NTSTATUS status; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PVOID pRemoteAddress; + ULONG RemoteAddressLength; + ULONG BytesCopied; + PVOID pTsdu; + ULONG ReceiveFlags; + tCLIENTLIST *pClientList; + CTELockHandle OldIrq; + CTELockHandle OldIrq2; + ULONG ClientBytesTaken; + ULONG DataLength; + tADDRESSELE *pAddress; + tRCVELE *pRcvEle; + PLIST_ENTRY pRcvEntry; + tDEVICECONTEXT *pDeviceContext; + CTEULONGLONG AdapterNumber; + + + + IF_DBG (NBT_DEBUG_TDIHNDLR) + KdPrint(("CompletionRcvDgram pRcvBuffer: %X, Status: %X Length %X\n", + Context, + Irp->IoStatus.Status, + Irp->IoStatus.Information )); + + + // there may be several clients that want to see this datagram so check + // the client list to see... + // + if (Context) + { + tCLIENTELE *pClientPrev = NULL; + + DataLength = Irp->IoStatus.Information; + + pTsdu = MmGetSystemAddressForMdl(Irp->MdlAddress); + + pClientList = (tCLIENTLIST *)Context; + + // for the multihomed host, we only want to distribute the inbound + // datagram to clients on this same adapter, to avoid giving the + // datagram to the same client several times, once for each adapter + // it is bound to. + // + pDeviceContext = pClientList->pClientEle->pDeviceContext; + AdapterNumber = pDeviceContext->AdapterNumber; + + pAddress = pClientList->pAddress; + pRemoteAddress = pClientList->pRemoteAddress; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pHead = &pClientList->pAddress->ClientHead; + pEntry = pHead->Flink; + RemoteAddressLength = pClientList->RemoteAddressLength; + ReceiveFlags = pClientList->ReceiveDatagramFlags; + +#ifdef PROXY_NODE + if (!pClientList->fProxy) + { +#endif + if (!pClientList->fUsingClientBuffer) + { + + while (pEntry != pHead) + { + PTDI_IND_RECEIVE_DATAGRAM EvRcvDgram; + PVOID RcvDgramEvContext; + tCLIENTELE *pClientEle; + PIRP pRcvIrp; + + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // for multihomed hosts only distribute the datagram to + // clients hooked to this device context to avoid duplicate + // indications + // + if (pClientEle->pDeviceContext->AdapterNumber == AdapterNumber) + { + InterlockedIncrement(&pClientEle->RefCount); + + EvRcvDgram = pClientEle->evRcvDgram; + RcvDgramEvContext = pClientEle->RcvDgramEvContext; + + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + // dereference the previous client in the list + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + + pRcvIrp = NULL; + + ClientBytesTaken = 0; + + status = (*EvRcvDgram)(RcvDgramEvContext, + RemoteAddressLength, + pRemoteAddress, + 0, + NULL, + #ifndef VXD + ReceiveFlags, + #endif + DataLength, + DataLength, + &ClientBytesTaken, + pTsdu, + &pRcvIrp); + + if (!pRcvIrp) + { + // if no buffer is returned, then the client is done + // with the data so go to the next client ...since it may + // be possible to process all clients in this loop without + // ever sending an irp down to the transport + // free the remote address mem block + + status = STATUS_DATA_NOT_ACCEPTED; + } + else + { + + // the client has passed back an irp so + // copy the data to the client's Irp + // + TdiCopyBufferToMdl(pTsdu, + ClientBytesTaken, + DataLength - ClientBytesTaken, + pRcvIrp->MdlAddress, + 0, + &BytesCopied); + + // length is copied length (since the MDL may be + // too short to take it all) + // + if (BytesCopied < (DataLength-ClientBytesTaken)) + { + pRcvIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + else + { + pRcvIrp->IoStatus.Status = STATUS_SUCCESS; + } + + pRcvIrp->IoStatus.Information = BytesCopied; + + IoCompleteRequest(pRcvIrp,IO_NETWORK_INCREMENT); + } + + CTESpinLock(&NbtConfig.JointLock, OldIrq); + + + pClientPrev = pClientEle; + } + // this code is protected from a client removing itself + // from the list of clients attached to an address by + // referencing the client prior to releasing the spin lock + // on the address. The client element does not get + // removed from the address list until its ref count goes + // to zero. We must hold the joint spin lock to prevent the + // next client from deleting itself from the list before we + // can increment its reference count. + // + pEntry = pEntry->Flink; + + + } // of while(pEntry != pHead) + + + } + else + { + // *** Client Has posted a receive Buffer, rather than using + // *** receive handler - VXD case! + // *** + while (pEntry != pHead) + { + tCLIENTELE *pClientEle; + PIRP pRcvIrp; + + pClientEle = CONTAINING_RECORD(pEntry,tCLIENTELE,Linkage); + + // for multihomed hosts only distribute the datagram to + // clients hooked to this device context to avoid duplicate + // indications + // + if (pClientEle->pDeviceContext->AdapterNumber == AdapterNumber) + { + InterlockedIncrement(&pClientEle->RefCount); + + // check for datagrams posted to this name + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // undo the InterlockedIncrement on the refcount + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + pClientPrev = NULL; + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pClientEle == pClientList->pClientEle) + { + // this is the client whose buffer we are using - it is + // passed up to the client after all other clients + // have been processed. + // + InterlockedDecrement(&pClientEle->RefCount); + pEntry = pEntry->Flink; + continue; + } + else + if (!IsListEmpty(&pClientEle->RcvDgramHead)) + { + + pRcvEntry = RemoveHeadList(&pClientEle->RcvDgramHead); + pRcvEle = CONTAINING_RECORD(pRcvEntry,tRCVELE,Linkage); + pRcvIrp = pRcvEle->pIrp; + + // + // copy the data to the client's Irp + // + TdiCopyBufferToMdl(pTsdu, + 0, + DataLength, + pRcvIrp->MdlAddress, + 0, + &BytesCopied); + + // length is copied length (since the MDL may be too short to take it all) + if (BytesCopied < DataLength) + { + pRcvIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + } + else + { + pRcvIrp->IoStatus.Status = STATUS_SUCCESS; + } + + pRcvIrp->IoStatus.Information = BytesCopied; + + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + IoCompleteRequest(pRcvIrp,IO_NETWORK_INCREMENT); + + + // free the receive block + CTEMemFree((PVOID)pRcvEle); + + CTESpinLock(&NbtConfig.JointLock, OldIrq); + } + + pEntry = pEntry->Flink; + + pClientPrev = pClientEle; + } + + } // of while(pEntry != pHead) + + // free the remote address structure and the client list + // allocated in DgramHndlrNotOs + // + CTESpinFree(&NbtConfig.JointLock, OldIrq); + + // undo the InterlockedIncrement on the refcount + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + CTEMemFree(pClientList->pRemoteAddress); + + // + // The address was referenced in DgramRcvNotOs to be sure it did not + // disappear until this dgram rcv was done, which is now. + // + NbtDereferenceAddress(pClientList->pAddress); + + CTEMemFree(Context); + + + // returning success allows the IO subsystem to complete the + // irp that we used to get the data - i.e. one client's + // buffer + // + return(STATUS_SUCCESS); + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // dereference the previous client in the list from the RcvHANDLER + // case a page or so above... + // + if (pClientPrev) + { + NbtDereferenceClient(pClientPrev); + } + + // + // The address was referenced in DgramRcvNotOs to be sure it did not + // disappear until this dgram rcv was done, which is now. + // + NbtDereferenceAddress(pClientList->pAddress); + + +#ifdef PROXY_NODE + } + else + { + // + // Call the ProxyDoDgramDist after freeing the jointlock. + // + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = ProxyDoDgramDist( + pTsdu, + DataLength, + (tNAMEADDR *)pClientList->pAddress, //NameAddr + pClientList->pRemoteAddress //device context + ); + + } +#endif + + + // + // Free the buffers allocated + // + CTEMemFree(pTsdu); + if (!pClientList->fProxy) + { + CTEMemFree(pClientList->pRemoteAddress); + } + CTEMemFree(Context); + + IF_DBG(NBT_DEBUG_RCV) + KdPrint(("****Freeing Mdl: Irp = %X Mdl = %X\n",Irp,Irp->MdlAddress)); + IoFreeMdl(Irp->MdlAddress); + + // put our Irp back on its free list + // + REMOVE_FROM_LIST(&Irp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &Irp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + return(STATUS_MORE_PROCESSING_REQUIRED); + + } + + // for the single receive case this passes the rcv up to the client + // + return(STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiErrorHandler ( + IN PVOID Context, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called on any error indications passed back from the + transport. It implements LAN_STATUS_ALERT. + +Arguments: + + Context - Supplies the pfcb for the address. + + Status - Supplies the error. + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + + // debug *JS + KdPrint(("Nbt: Error Event HAndler hit unexpectedly\n")); + return(STATUS_DATA_NOT_ACCEPTED); + + return STATUS_SUCCESS; +} + + +//---------------------------------------------------------------------------- +VOID +SumMdlLengths ( + IN PMDL pMdl, + IN ULONG BytesCopied, + IN tCONNECTELE *pConnectEle + ) + +/*++ + +Routine Description: + + This routine is called to sum the lengths of MDLs in a chain. + +Arguments: + + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + ULONG TotalLength; + + TotalLength = 0; + + do + { + if ((TotalLength + MmGetMdlByteCount(pMdl)) > BytesCopied) + { + pConnectEle->OffsetFromStart = BytesCopied - TotalLength; + pConnectEle->pNextMdl = pMdl; + break; + } + else + { + TotalLength += MmGetMdlByteCount(pMdl); + } + } + while (pMdl=(PMDL)pMdl->Next); + + return; +} + +//---------------------------------------------------------------------------- +NTSTATUS +AllocateMdl ( + IN tCONNECTELE *pConnEle + ) + +/*++ + +Routine Description: + + This routine is called to allocate a Mdl for the Connection. + +Arguments: + + pConnEle - ptr to the connection element + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + PMDL pNewMdl; + PVOID NewAddress; + + // + // Allocate an extra MDL for this connection if there isn't one + // already allocated. + // + if (pConnEle->pNewMdl == NULL ) + { + // The length of the + // Mdl is set to 64K(MAXUSHORT) so that there are enough pfns in the + // Mdl to map a large buffer. + // + // use the pConnEle as the Virtual address, since it doesn't matter + // because it will be overwritten when the partial Mdl is created. + // + NewAddress = pConnEle; + pNewMdl = IoAllocateMdl( + NewAddress, + MAXUSHORT, + FALSE, + FALSE,NULL); + if (!pNewMdl) + { + ASSERTMSG("Nbt:Unable to allocate a MDL!!\n",0); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + pConnEle->pNewMdl = pNewMdl; + } + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- +VOID +MakePartialMdl ( + IN tCONNECTELE *pConnEle, + IN PIRP pIrp, + IN ULONG ToCopy + ) + +/*++ + +Routine Description: + + This routine is called to build a partial Mdl that accounts for ToCopy + bytes of data being copied to the start of the Client's Mdl. + +Arguments: + + pConnEle - ptr to the connection element + +Return Value: + + NTSTATUS - Status of event indication + +--*/ + +{ + PMDL pNewMdl; + PVOID NewAddress; + + // Build a partial Mdl to represent the client's Mdl chain since + // we have copied data to it, and the transport must copy + // more data to it after that data. + // + SumMdlLengths(pIrp->MdlAddress,ToCopy,pConnEle); + + // this routine has set the Mdl that the next data starts at and + // the offset from the start of that Mdl, so create a partial Mdl + // to map that buffer and tack it on the mdl chain instead of the + // original + // + pNewMdl = pConnEle->pNewMdl; + NewAddress = (PVOID)((PUCHAR)MmGetMdlVirtualAddress(pConnEle->pNextMdl) + + pConnEle->OffsetFromStart); + IoBuildPartialMdl(pConnEle->pNextMdl,pNewMdl,NewAddress,0); + + // hook the new partial mdl to the front of the MDL chain + // + pNewMdl->Next = pConnEle->pNextMdl->Next; + + pIrp->MdlAddress = pNewMdl; + ASSERT(pNewMdl); +} +//---------------------------------------------------------------------------- +VOID +CopyToStartofIndicate ( + IN tLOWERCONNECTION *pLowerConn, + IN ULONG DataTaken + ) + +/*++ + +Routine Description: + + This routine is called to copy data remaining in the indicate buffer to + the head of the indicate buffer. + +Arguments: + + pLowerConn - ptr to the lower connection element + +Return Value: + + none + +--*/ + +{ + PVOID pSrc; + ULONG DataLeft; + PVOID pMdl; + + + DataLeft = pLowerConn->BytesInIndicate; + + pMdl = (PVOID)MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl); + + pSrc = (PVOID)( (PUCHAR)pMdl + DataTaken); + + CTEMemCopy(pMdl,pSrc,DataLeft); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTProcessAcceptIrp( + IN PIRP pIrp, + OUT tCONNECTELE **ppConnEle) + +/*++ +Routine Description: + + This Routine handles a Client's AcceptIrp, extracting the pConnEle and + returning it. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTDI_REQUEST_KERNEL_ACCEPT pRequest; + tCONNECTELE *pConnEle; + + + // pull the junk out of the Irp and call the non-OS specific routine. + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + // the Parameters value points to a Request structure... + pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters; + + // the pConnEle ptr was stored in the FsContext value when the connection + // was initially created. + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + *ppConnEle = pConnEle; + + return(STATUS_SUCCESS); +} + +//---------------------------------------------------------------------------- + +NTSTATUS +OutOfRsrcKill( + OUT tLOWERCONNECTION *pLowerConn) + +/*++ +Routine Description: + + This Routine handles killing a connection when an out of resource condition + occurs. It uses a special Irp that it has saved away, and a linked list + if that irp is currently in use. + +Arguments: + + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + NTSTATUS status; + CTELockHandle OldIrq; + CTELockHandle OldIrq1; + PIRP pIrp; + PFILE_OBJECT pFileObject; + PDEVICE_OBJECT pDeviceObject; + tDEVICECONTEXT *pDeviceContext = pLowerConn->pDeviceContext; + + + CTESpinLock(pDeviceContext,OldIrq); + CTESpinLock(&NbtConfig,OldIrq1); + + InterlockedIncrement(&pLowerConn->RefCount); + if (NbtConfig.OutOfRsrc.pIrp) + { + // get an Irp to send the message in + pIrp = NbtConfig.OutOfRsrc.pIrp; + NbtConfig.OutOfRsrc.pIrp = NULL; + + pFileObject = pLowerConn->pFileObject; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + CTESpinFree(&NbtConfig,OldIrq1); + CTESpinFree(pDeviceContext,OldIrq); + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildDisconnect( + pIrp, + pDeviceObject, + pFileObject, + RsrcKillCompletion, + pLowerConn, //context value passed to completion routine + NULL, // Timeout... + TDI_DISCONNECT_ABORT, + NULL, // send connection info + NULL); // return connection info + + CHECK_PTR(pIrp); + pIrp->MdlAddress = NULL; + + CHECK_COMPLETION(pIrp); + status = IoCallDriver(pDeviceObject,pIrp); + + IF_DBG(NBT_DEBUG_REF) + KdPrint(("Nbt:Out of Resource, Kill connection, %X\n",pLowerConn)); + + return(status); + + } + else + { + // + // The lower conn could get removed here, then get dequed from the ConnectionHead and come here + // (via DpcNextOutOfRsrcKill), and fail to get an Irp; we re-enque it into the OutOfRsrc list, + // but should not try to deque it here. + // + if (!pLowerConn->OutOfRsrcFlag) { + + RemoveEntryList(&pLowerConn->Linkage); + + // + // The lower conn gets removed from the inactive list here and again when CleanupAfterDisconnect + // calls NbtDeleteLowerConn. In order to prevent the second deque, we set a flag here and test + // for it in NbtDeleteLowerConn. + // + pLowerConn->OutOfRsrcFlag = TRUE; + } + + pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (struct _LIST_ENTRY * volatile)0x00006041; + InsertTailList(&NbtConfig.OutOfRsrc.ConnectionHead,&pLowerConn->Linkage); + + CTESpinFree(&NbtConfig,OldIrq1); + CTESpinFree(pDeviceContext,OldIrq); + } + + return(STATUS_SUCCESS); +} +//---------------------------------------------------------------------------- +NTSTATUS +RsrcKillCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a disconnect to the transport. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + NTSTATUS status; + KIRQL OldIrq; + PLIST_ENTRY pEntry; + tLOWERCONNECTION *pLowerConn; + PKDPC pDpc; + + + + pLowerConn = (tLOWERCONNECTION *)pContext; + + // this call will indicate the disconnect to the client and clean up + // abit. + // + status = DisconnectHndlrNotOs ( + NULL, + (PVOID)pLowerConn, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT); + + NbtDereferenceLowerConnection(pLowerConn); + + CTESpinLock(&NbtConfig,OldIrq); + NbtConfig.OutOfRsrc.pIrp = pIrp; + + if (!IsListEmpty(&NbtConfig.OutOfRsrc.ConnectionHead)) + { + if (NbtConfig.OutOfRsrc.pDpc) + { + pDpc = NbtConfig.OutOfRsrc.pDpc; + NbtConfig.OutOfRsrc.pDpc = NULL; + + pEntry = RemoveHeadList(&NbtConfig.OutOfRsrc.ConnectionHead); + pLowerConn = CONTAINING_RECORD(pEntry,tLOWERCONNECTION,Linkage); + + pLowerConn->Linkage.Flink = pLowerConn->Linkage.Blink = (struct _LIST_ENTRY * volatile)0x00006109; + KeInitializeDpc(pDpc, + DpcNextOutOfRsrcKill, + (PVOID)pLowerConn); + + KeInsertQueueDpc(pDpc,NULL,NULL); + + CTESpinFree(&NbtConfig,OldIrq); + + } + else + CTESpinFree(&NbtConfig,OldIrq); + } + else + { + CTESpinFree(&NbtConfig,OldIrq); + } + + + + // + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is no initiating thread - we are the initiator + // + return(STATUS_MORE_PROCESSING_REQUIRED); +} + + +//---------------------------------------------------------------------------- +VOID +DpcNextOutOfRsrcKill( + IN PKDPC pDpc, + IN PVOID Context, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This routine simply calls OutOfRsrcKill from a Dpc started in + RsrcKillCompletion. + +Arguments: + + +Return Value: +--*/ +{ + + KIRQL OldIrq; + tLOWERCONNECTION *pLowerConn; + + + pLowerConn = (tLOWERCONNECTION *)Context; + + CTESpinLock(&NbtConfig,OldIrq); + NbtConfig.OutOfRsrc.pDpc = pDpc; + CTESpinFree(&NbtConfig,OldIrq); + + OutOfRsrcKill(pLowerConn); + + // + // to remove the extra reference put on pLowerConn when OutOfRsrc called + // + NbtDereferenceLowerConnection(pLowerConn); + +} + + +//---------------------------------------------------------------------------- +VOID +FillIrpCancelRoutine( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a Receive Irp that has been saved + during the FILL_IRP state. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + tCONNECTELE *pConnEle; + KIRQL OldIrq; + KIRQL OldIrq1; + KIRQL OldIrq2; + PIO_STACK_LOCATION pIrpSp; + tLOWERCONNECTION *pLowerConn; + BOOLEAN CompleteIt = FALSE; + + IF_DBG(NBT_DEBUG_INDICATEBUFF) + KdPrint(("Nbt:Got a Receive Cancel Irp !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + // now look for an Irp to cancel + // + CTESpinLock(&NbtConfig.JointLock,OldIrq1); + CTESpinLock(pConnEle,OldIrq); + + + pLowerConn = pConnEle->pLowerConnId; + if (pLowerConn) + { + CTESpinLock(pLowerConn,OldIrq2); + pLowerConn->StateRcv = INDICATE_BUFFER; + SetStateProc(pLowerConn,RejectAnyData); + + } + + + CHECK_PTR(pConnEle); + + pConnEle->pIrpRcv = NULL; + + + if (pLowerConn) + { + CTESpinFree(pLowerConn,OldIrq2); + } + + CTESpinFree(pConnEle,OldIrq); + CTESpinFree(&NbtConfig.JointLock,OldIrq1); + + // complete the irp + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + if (pLowerConn) + { + // + // Cancelling a Rcv Irp in the fill irp state will cause netbt + // to lose track of where it is in the message so it must kill + // the connection. + // + OutOfRsrcKill(pLowerConn); + } + return; + +} + + diff --git a/private/ntos/nbt/nt/tdiout.c b/private/ntos/nbt/nt/tdiout.c new file mode 100644 index 000000000..da98a8850 --- /dev/null +++ b/private/ntos/nbt/nt/tdiout.c @@ -0,0 +1,727 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + Tdiout.c + +Abstract: + + + This file represents the TDI interface on the bottom edge of NBT. + The procedures herein conform to the TDI I/F spec. and then convert + the information to NT specific Irps etc. This implementation can be + changed out to run on another OS. + +Author: + + Jim Stewart (Jimst) 10-2-92 + +Revision History: + +--*/ + + +#include "nbtprocs.h" // procedure headings + +// function prototypes for completion routines used in this file +NTSTATUS +DgramSendComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +TcpConnectComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +TcpDisconnectComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); +NTSTATUS +SendSessionCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ); + +// DEBUG +VOID +CheckIrpList( + ); + +//---------------------------------------------------------------------------- +NTSTATUS +TdiSendDatagram( + IN PTDI_REQUEST pRequestInfo, + IN PTDI_CONNECTION_INFORMATION pSendDgramInfo, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pSendBuffer, + IN ULONG SendFlags + ) +/*++ + +Routine Description: + + This routine sends a datagram to the transport + +Arguments: + + pSendBuffer - this is really an Mdl in NT land. It must be tacked on + the end of the Mdl created for the Nbt datagram header. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PMDL pMdl; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + PVOID pCompletionRoutine; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiSendDatagram")); + + // call the completion routine with this status (if there is one) + if (pRequestInfo->RequestNotifyObject) + { + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + + // so the Irp is not completed twice. + return(STATUS_PENDING); + } + + pRequestIrp->CancelRoutine = NULL; + + // set up the completion routine passed in from Udp Send using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields our self! + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + // Allocate a MDL and set the head sizes correctly + pMdl = IoAllocateMdl( + pSendBuffer->pDgramHdr, + pSendBuffer->HdrLength, + FALSE, + FALSE, + NULL); + + if (!pMdl) + { + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + if (pRequestInfo->RequestNotifyObject) + { + // call the completion routine will this status + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + ASSERT(pMdl); + return(STATUS_PENDING); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + + + pCompletionRoutine = DgramSendComplete; + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildSendDatagram( + pRequestIrp, + pDeviceObject, + pFileObject, + pCompletionRoutine, + NULL, //context value passed to completion routine + pMdl, + SendLength, + pSendDgramInfo); + + // tack the client's send buffer (MDL) onto the end of the datagram header + // Mdl, and then pass the irp on downward to the transport + if (pSendBuffer->pBuffer) + pMdl->Next = (PMDL)pSendBuffer->pBuffer; + + CHECK_COMPLETION(pRequestIrp); + status = IoCallDriver(pDeviceObject,pRequestIrp); + + // Fill in the SentSize + *pSentSize = SendLength; + + // The transport always completes the IRP, so as long as the irp made it + // to the transport it got completed. The return code from the transport + // does not indicate if the irp was completed or not. The real status + // of the operation is in the Irp Iostatus return code. + // What we need to do is make sure NBT does not complete the irp AND the + // transport complete the Irp. Therefore this routine returns + // status pending if the Irp was passed to the transport, regardless of + // the return code from the transport. This return code signals the caller + // that the irp will be completed via the completion routine and the + // actual status of the send can be found in the Irpss IoStatus.Status + // variable. + // + // If the Caller of this routine gets a bad return code, they can assume + // that this routine failed to give the Irp to the transport and it + // is safe for them to complete the Irp themselves. + // + // If the Completion routine is set to null, then there is no danger + // of the irp completing twice and this routine will return the transport + // return code in that case. + + if (pRequestInfo->RequestNotifyObject) + return(STATUS_PENDING); + else + return(status); +} + +//---------------------------------------------------------------------------- +NTSTATUS +DgramSendComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a datagram send to the transport. + It must call the client completion routine and free the Irp and Mdl. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + KIRQL OldIrq; + + // check for a completion routine of the clients to call... + if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine) + { + (*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine)) + ((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information); // sent length + + } + + // deallocate the MDL.. this is done by the IO subsystem in IoCompleteRequest + IoFreeMdl(pIrp->MdlAddress); + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is no initiating thread - we are the initiator + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + + + +//---------------------------------------------------------------------------- +PIRP +NTAllocateNbtIrp( + IN PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + This routine allocates an irp by calling the IO system, and then it + undoes the queuing of the irp to the current thread, since these are + NBTs own irps, and should not be attached to a thread. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + PIRP pIrp; + + + + // call the IO subsystem to allocate the irp + + pIrp = IoAllocateIrp(DeviceObject->StackSize,FALSE); + + if (!pIrp) + { + return(NULL); + } + // + // Simply return a pointer to the packet. + // + + return pIrp; + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TdiConnect( + IN PTDI_REQUEST pRequestInfo, + IN ULONG lTimeout, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + IN PIRP pClientIrp + ) +/*++ + +Routine Description: + + This routine sends a connect request to the tranport provider, to setup + a connection to the other side... + +Arguments: + + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiConnect")); + // call the completion routine with this status + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + return(STATUS_PENDING); + } + pRequestIrp->CancelRoutine = NULL; + + // set up the completion routine passed in from Tcp SessionStart using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields ourselves + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + TdiBuildConnect( + pClientIrp, + pDeviceObject, + pFileObject, + TcpConnectComplete, + (PVOID)pRequestIrp, //context value passed to completion routine + lTimeout, // use timeout on connect + pSendInfo, + NULL); + + pRequestIrp->MdlAddress = NULL; + + CHECK_COMPLETION(pClientIrp); + status = IoCallDriver(pDeviceObject,pClientIrp); + + // the transport always completes the IRP, so we always return status pending + return(STATUS_PENDING); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +TdiDisconnect( + IN PTDI_REQUEST pRequestInfo, + IN PVOID lTimeout, + IN ULONG Flags, + IN PTDI_CONNECTION_INFORMATION pSendInfo, + IN PCTE_IRP pClientIrp, + IN BOOLEAN Wait + ) +/*++ + +Routine Description: + + This routine sends a connect request to the tranport provider, to setup + a connection to the other side... + +Arguments: + + pClientIrp - this is the irp that the client used when it issued an + NbtDisconnect. We pass this irp to the transport so that + the client can do a Ctrl C and cancel the irp if the + disconnect takes too long. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + status = GetIrp(&pRequestIrp); + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiDisConnect")); + // call the completion routine will this status + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + return(STATUS_PENDING); + } + if (!pClientIrp) + { + // if no client irp was passed in, then just use our Irp + pClientIrp = pRequestIrp; + } + pRequestIrp->CancelRoutine = NULL; + + // set up the completion routine passed in from Tcp SessionStart using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields ourselves + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + // store some context stuff in the Irp stack so we can call the completion + // routine set by the Udpsend code... + // Note that pRequestIrp is passed to the completion routine as a context + // value so we will know the routine to call for the client's completion. + TdiBuildDisconnect( + pClientIrp, + pDeviceObject, + pFileObject, + TcpConnectComplete, + (PVOID)pRequestIrp, //context value passed to completion routine + lTimeout, + Flags, + NULL, // send connection info + NULL); // return connection info + + pRequestIrp->MdlAddress = NULL; + + // if Wait is set, then this means do a synchronous disconnect and block + // until the irp is returned. + // + if (Wait) + { + status = SubmitTdiRequest(pFileObject,pClientIrp); + // + // return the irp to its pool + // + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + return(status); + } + else + { + CHECK_COMPLETION(pClientIrp); + status = IoCallDriver(pDeviceObject,pClientIrp); + // the transport always completes the IRP, so we always return status pending + return(STATUS_PENDING); + } + + +} + +//---------------------------------------------------------------------------- +NTSTATUS +TcpConnectComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a TCP session setup. The TCP + connection is either setup or not depending on the status returned here. + It must called the clients completion routine (in udpsend.c). Which should + look after sending the NetBios sesion startup pdu across the TCP connection. + + The pContext value is actually one of NBTs irps that is JUST used to store + the calling routines completion routine. The real Irp used is the original + client's irp. This is done so that IoCancelIrp will cancel the connect + properly. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + KIRQL OldIrq; + PIRP pMyIrp; + + pMyIrp = (PIRP)pContext; + + // check for a completion routine of the clients to call... + if (pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine) + { + (*((NBT_COMPLETION)pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine)) + ((PVOID)pMyIrp->Overlay.AsynchronousParameters.UserApcContext, + pIrp->IoStatus.Status, + 0L); + + } + + REMOVE_FROM_LIST(&pMyIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pMyIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is not initiating thread - we are the initiator + return(STATUS_MORE_PROCESSING_REQUIRED); + +} +//---------------------------------------------------------------------------- +NTSTATUS +TdiSend( + IN PTDI_REQUEST pRequestInfo, + IN USHORT sFlags, + IN ULONG SendLength, + OUT PULONG pSentSize, + IN tBUFFER *pSendBuffer, + IN ULONG Flags + ) +/*++ + +Routine Description: + + This routine sends a packet to the transport on a TCP connection + +Arguments: + + pSendBuffer - this is really an Mdl in NT land. It must be tacked on + the end of the Mdl created for the Nbt datagram header. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PIRP pRequestIrp; + PMDL pMdl; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // get an Irp to send the message in + pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle; + pDeviceObject = IoGetRelatedDeviceObject(pFileObject); + + // get an Irp from the list + status = GetIrp(&pRequestIrp); + + if (!NT_SUCCESS(status)) + { + KdPrint(("Nbt:Failed to get an Irp in TdiSend")); + // call the completion routine with this status + if (pRequestInfo->RequestNotifyObject) + { + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + + return(STATUS_INSUFFICIENT_RESOURCES); + } + pRequestIrp->CancelRoutine = NULL; + + + // set up the completion routine passed in from Udp Send using the APC + // fields in the Irp that would normally be used to complete the request + // back to the client - although we are really the client here so we can + // use these fields our self! + pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine = + (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject; + pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = + (PVOID)pRequestInfo->RequestContext; + + + // get the MDL that is currently linked to the IRP (i.e. created at the + // same time that we created the IRP list. Set the sizes correctly in + // the MDL header. + pMdl = IoAllocateMdl( + pSendBuffer->pDgramHdr, + pSendBuffer->HdrLength, + FALSE, + FALSE, + NULL); + + if (!pMdl) + { + REMOVE_FROM_LIST(&pRequestIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pRequestIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + + // call the completion routine will this status + if (pRequestInfo->RequestNotifyObject) + { + (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject)) + ((PVOID)pRequestInfo->RequestContext, + STATUS_INSUFFICIENT_RESOURCES, + 0L); + } + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Map the pages in memory... + MmBuildMdlForNonPagedPool(pMdl); + + TdiBuildSend( + pRequestIrp, + pDeviceObject, + pFileObject, + SendSessionCompletionRoutine, + NULL, //context value passed to completion routine + pMdl, + sFlags, + SendLength); // include session hdr length (ULONG) + // + // tack the Client's buffer on the end, if there is one + // + if (pSendBuffer->Length) + { + pMdl->Next = pSendBuffer->pBuffer; + } + + CHECK_COMPLETION(pRequestIrp); + status = IoCallDriver(pDeviceObject,pRequestIrp); + + *pSentSize = SendLength; // the size we attempted to send + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +SendSessionCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP pIrp, + IN PVOID pContext + ) +/*++ + +Routine Description: + + This routine handles the completion of a send to the transport. + It must call any client supplied completion routine and free the Irp + and Mdl back to its pool. + +Arguments: + + +Return Value: + + NTSTATUS - success or not + +--*/ +{ + KIRQL OldIrq; + + // + // check for a completion routine of the clients to call... + // + if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine) + { + (*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine)) + ((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext, + pIrp->IoStatus.Status, + pIrp->IoStatus.Information); // sent length + + } + + + + IoFreeMdl(pIrp->MdlAddress); + + REMOVE_FROM_LIST(&pIrp->ThreadListEntry); + ExInterlockedInsertTailList(&NbtConfig.IrpFreeList, + &pIrp->Tail.Overlay.ListEntry, + &NbtConfig.SpinLock); + // + // return this status to stop the IO subsystem from further processing the + // IRP - i.e. trying to complete it back to the initiating thread! -since + // there is no initiating thread - we are the initiator + // + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + + + diff --git a/private/ntos/nbt/nt/up/makefile b/private/ntos/nbt/nt/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nbt/nt/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nbt/nt/up/netbt.prf b/private/ntos/nbt/nt/up/netbt.prf new file mode 100644 index 000000000..84914dcb9 --- /dev/null +++ b/private/ntos/nbt/nt/up/netbt.prf @@ -0,0 +1,170 @@ +SendCompletion@12 +NbtDispatchInternalCtrl@8 +NTSend@8 +TdiReceiveHandler@32 +CompletionRcv@12 +ProcessIrp@24 +CTECountedAllocMem@8 +DgramHndlrNotOs@48 +ConvertToAscii@20 +UdpSendDatagram@28 +NbtDereferenceClient@4 +NTIoComplete@12 +MakeRemoteAddressStructure@16 +FindName@16 +GetIrp@4 +TdiSendDatagram@24 +DgramSendComplete@12 +FindInHashTable@16 +SendDgramCompletion@12 +CTECountedFreeMem@8 +DgramSendCleanupTracker@12 +NbtDereferenceName@4 +TdiRcvDatagramHandler@44 +GetNetBiosNameFromTransportAddress@12 +FindNameOrQuery@28 +NbtAllocTracker@0 +NbtDereferenceAddress@4 +NTSendDatagram@8 +NbtSendDatagram@28 +SendDgram@8 +ConvertToHalfAscii@16 +BuildSendDgramHdr@32 +RemoteHashTimeout@12 +TimerExpiry@16 +CTEStartTimer@16 +NTQueryInformation@8 +NTOpenConnection@8 +CreateDeviceString@8 +FreeTracker@8 +GetTracker@4 +SubmitTdiRequest@8 +TdiRcvNameSrvHandler@44 +GetEntry@12 +UdpSendNSBcast@36 +NDgramSendCompleted@12 +CreatePdu@32 +FindInEA@8 +MSnodeCompletion@12 +NbtOpenAndAssocConnection@8 +SrcIsUs@4 +AddToPendingList@8 +QueryNameOnNet@44 +NbtTdiAssociateConnection@8 +CompleteClientReq@12 +ClearCancelRoutine@4 +StartTimer@32 +StartLmHostTimer@8 +LmHostQueueRequest@16 +GetNameToFind@4 +RemoveNameAndCompleteReq@8 +RemoveName@4 +NameSrvHndlrNotOs@16 +ReturnIrp@8 +CheckRegistrationFromNet@16 +DereferenceTracker@4 +CTEInitTimer@4 +TimeoutQEntries@12 +LmHostTimeout@12 +GetContext@4 +MakePending@4 +FindNameRemoteThenLocal@8 +NbtOpenConnection@12 +NbtTdiOpenConnection@8 +NbtDispatchCreate@8 +SendDgramContinue@8 +CompletionRoutine@12 +AllocateMdl@4 +NTAssocAddress@8 +NbtAssociateAddress@12 +NTAllocateNbtIrp@4 +NbtGetMdl@8 +NbtListen@20 +NbtAllocateClientBlock@4 +NbtSetEventHandler@16 +InterlockedCallCompletion@8 +NTSetEventHandler@8 +FindInDomainList@8 +NbtRegisterName@32 +NTCheckSetCancelRoutine@12 +InitTimerQ@4 +CountLocalNames@4 +NbtInitConnQ@16 +AddToHashTable@28 +NbtInitTrackerQ@8 +NbtAppendString@12 +NbtQueryAdapterStatus@12 +QueryProviderCompletion@12 +NbtAddPermanentName@4 +ReadParameters2@8 +IncrementNameStats@8 +MSnodeRegCompletion@12 +NbtRegisterCompletion@8 +NbtOpenAddress@24 +InitQ@12 +NTSetFileObjectContexts@12 +NbtCreateDeviceObject@24 +CreateHashTable@12 +InitNotOs@0 +InitTimersNotOs@0 +TcpSendSessionResponse@12 +TcpSendSession@12 +Inbound@32 +CompleteSessionSetup@20 +SessionRespDone@12 +UdpSendResponse@32 +QueryRespDone@12 +SrcIsNameServer@8 +QueryFromNet@20 +NbtDispatchCleanup@8 +ConvertDottedDecimalToUlong@8 +NTGetLmHostPath@4 +NbtDispatchClose@8 +NbtDispatchDevCtrl@8 +NTOpenControl@8 +NbtCreateAddressObjects@12 +NTSetSharedAccess@12 +FindSessionEndPoint@24 +DispatchIoctls@12 +ReadParameters@8 +NTListen@8 +ConvertToUlong@8 +NbtInitMdlQ@8 +GetExtendedAttributes@4 +ReadNameServerAddresses@16 +GetServerAddress@12 +NbtReadRegistry@24 +GetIPFromRegistry@16 +NbtReadLinkageInformation@16 +NTReadIniString@12 +ReadElement@12 +NbtReadSingleParameter@16 +NbtOpenRegistry@12 +NbtFindLastSlash@12 +ReadStringRelative@16 +OpenAndReadElement@12 +SetEventHandler@20 +NbtTdiOpenAddress@28 +NbtTdiOpenControl@4 +SetWinsDownFlag@4 +LmGetFullPath@8 +TdiConnectHandler@36 +NTOpenAddr@8 +AcceptCompletionRoutine@12 +NTCheckSharedAccess@12 +LmOpenFile@4 +LmpBreakRecursion@8 +SendSessionCompletionRoutine@12 +ScanLmHostFile@4 +TdiSend@24 +NTReceive@8 +ClientTookSomeOfTheData@20 +LmGetIpAddr@16 +ConnectHndlrNotOs@24 +NTProcessAcceptIrp@8 +InitRemoteHashTable@12 +DriverEntry@8 +ReadLmHostFile@8 +ReadScope@8 +ClearConnStructures@8 +NbtQueryConnectionList@12 diff --git a/private/ntos/nbt/nt/up/sources b/private/ntos/nbt/nt/up/sources new file mode 100644 index 000000000..944b046e1 --- /dev/null +++ b/private/ntos/nbt/nt/up/sources @@ -0,0 +1,30 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj +TARGETLIBS=..\..\nbt\up\obj\*\nbt.lib + +!include ..\sources.inc diff --git a/private/ntos/nbt/nt/winsif.c b/private/ntos/nbt/nt/winsif.c new file mode 100644 index 000000000..d17ab2d7e --- /dev/null +++ b/private/ntos/nbt/nt/winsif.c @@ -0,0 +1,1390 @@ +/*++ + +Copyright (c) 1989-1994 Microsoft Corporation + +Module Name: + + Winsif.c + +Abstract: + + This module implements all the code surrounding the WINS interface to + netbt that allows WINS to share the same 137 socket as netbt. + +Author: + + Jim Stewart (Jimst) 1-30-94 + +Revision History: + +--*/ + + +#include "nbtprocs.h" +#include <nbtioctl.h> + +VOID +WinsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); +VOID +WinsSendIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ); +VOID +WinsDgramCompletion( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS status, + IN ULONG Length + ); + +NTSTATUS +CheckIfLocalNameActive( + IN tREM_ADDRESS *pSendAddr + ); + +PVOID +WinsAllocMem( + IN ULONG Size, + IN BOOLEAN Rcv + ); + +VOID +WinsFreeMem( + IN PVOID pBuffer, + IN ULONG Size, + IN BOOLEAN Rcv + ); + +VOID +InitiateRefresh ( + IN tDEVICECONTEXT *pDeviceContext + ); + +BOOLEAN RefreshedYet; + +// +// take this define from Winsock.h since including winsock.h causes +// redefinition problems with various types. +// +#define AF_UNIX 1 +#define AF_INET 2 + +//******************* Pageable Routine Declarations **************** +#ifdef ALLOC_PRAGMA +#pragma CTEMakePageable(PAGENBT, NTCloseWinsAddr) +#pragma CTEMakePageable(PAGENBT, InitiateRefresh) +#pragma CTEMakePageable(PAGENBT, RcvIrpFromWins) +#pragma CTEMakePageable(PAGENBT, PassNamePduToWins) +#pragma CTEMakePageable(PAGENBT, WinsIrpCancel) +#pragma CTEMakePageable(PAGENBT, WinsSendIrpCancel) +#pragma CTEMakePageable(PAGENBT, WinsSendDatagram) +#pragma CTEMakePageable(PAGENBT, CheckIfLocalNameActive) +#pragma CTEMakePageable(PAGENBT, WinsDgramCompletion) +#pragma CTEMakePageable(PAGENBT, WinsFreeMem) +#pragma CTEMakePageable(PAGENBT, WinsAllocMem) +#endif +//******************* Pageable Routine Declarations **************** + +tWINS_INFO *pWinsInfo; +HANDLE NbtDiscardableCodeHandle={0}; + +#define COUNT_MAX 10 + +//---------------------------------------------------------------------------- +NTSTATUS +NTOpenWinsAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles opening the Wins Object that is used by + by WINS to send and receive name service datagrams on port 137. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tWINS_INFO *pWins; + CTELockHandle OldIrq; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->FileObject->FsContext2 =(PVOID)NBT_WINS_TYPE; + + + // + // if the WINs endpoint structure is not allocated, then allocate it + // and initialize it. + // + status = STATUS_UNSUCCESSFUL; + if (!pWinsInfo) + { + + pWins = NbtAllocMem(sizeof(tWINS_INFO),NBT_TAG('v')); + if (pWins) + { + + // Page in the Wins Code, if it hasn't already been paged in. + // + if (!NbtDiscardableCodeHandle) + { + NbtDiscardableCodeHandle = MmLockPagableCodeSection( NTCloseWinsAddr ); + } + + // it could fail to lock the pages so check for that + // + if (NbtDiscardableCodeHandle) + { + CTEZeroMemory(pWins,sizeof(tWINS_INFO)); + InitializeListHead(&pWins->RcvList); + InitializeListHead(&pWins->SendList); + + pWins->RcvMemoryMax = NbtConfig.MaxDgramBuffering; + pWins->SendMemoryMax = NbtConfig.MaxDgramBuffering; + + status = STATUS_SUCCESS; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + pWinsInfo = pWins; + pWinsInfo->pDeviceContext = pDeviceContext; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + pIrpSp->FileObject->FsContext = (PVOID)pWinsInfo; + } + else + { + status = STATUS_UNSUCCESSFUL; + CTEMemFree(pWins); + } + + RefreshedYet = FALSE; + } + + + } + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Open Wins Address Rcvd, status= %X\n",status)); + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +NTCloseWinsAddr( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp) + +/*++ +Routine Description: + + This Routine handles closing the Wins Object that is used by + by WINS to send and receive name service datagrams on port 137. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tWINS_INFO *pWins; + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->FileObject->FsContext2 = (PVOID)NBT_CONTROL_TYPE; + + // + // if the WINs endpoint structure is allocated, then deallocate it + // + pWins = pIrpSp->FileObject->FsContext; + status = STATUS_INVALID_HANDLE; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pWinsInfo && (pWins == pWinsInfo)) + { + + status = STATUS_SUCCESS; + + // + // prevent any more dgram getting queued up + // + pWinsInfo = NULL; + + // + // free any rcv buffers that may be queued up + // + pHead = &pWins->RcvList; + while (!IsListEmpty(pHead)) + { + PLIST_ENTRY pRcvEntry; + tWINSRCV_BUFFER *pRcv; + + KdPrint(("***Nbt:Freeing Rcv buffered for Wins\n")); + + pRcvEntry = RemoveHeadList(pHead); + + pRcv = CONTAINING_RECORD(pRcvEntry,tWINSRCV_BUFFER,Linkage); + + WinsFreeMem(pRcv,pRcv->DgramLength,TRUE); + + } + + // + // return any Send buffers that may be queued up + // + pHead = &pWins->SendList; + while (!IsListEmpty(pHead)) + { + PLIST_ENTRY pRcvEntry; + PIRP pIrp; + + KdPrint(("***Nbt:Freeing Send Wins Address!\n")); + + pRcvEntry = RemoveHeadList(pHead); + + pIrp = CONTAINING_RECORD(pRcvEntry,IRP,Tail.Overlay.ListEntry); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + CTEIoComplete(pIrp,STATUS_CANCELLED,0); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + } + + CTEMemFree(pWins); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // + // Free The Wins Code section - DONOT do this to avoid any potential + // time windows calling these routines. + // + // MmUnlockPagableImageSection( NbtDiscardableCodeHandle ); + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Close Wins Address Rcvd\n")); + return(status); + +} +//---------------------------------------------------------------------------- +VOID +InitiateRefresh ( + IN tDEVICECONTEXT *pDeviceContext + ) +/*++ + +Routine Description: + + This routine tries to refresh all names with WINS on THIS node. + +Arguments: + + pDeviceContext - not used + pIrp - Wins Rcv Irp + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + CTELockHandle OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + ULONG Count; + ULONG NumberNames; + + + // + // be sure all net cards have this card as the primary wins + // server since Wins has to answer name queries for this + // node. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (!(NodeType & BNODE)) + { + LONG i; + Count = 0; + + NumberNames = 0; + + for (i=0 ;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ ) + { + + pHead = &NbtConfig.pLocalHashTbl->Bucket[i]; + pEntry = pHead; + while ((pEntry = pEntry->Flink) != pHead) + { + NumberNames++; + } + } + + while (Count < COUNT_MAX) + { + if (!NbtConfig.DoingRefreshNow) + { + + // + // set this to one so that refresh begin skips trying to + // switch to the backup. + // + NbtConfig.sTimeoutCount = 1; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + ReRegisterLocalNames(); + + break; + } + else + { + LARGE_INTEGER Timout; + NTSTATUS Locstatus; + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Waiting for Refresh to finish, so names can be reregistered\n")); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + // + // set a timeout that should be long enough to wait + // for all names to fail registration with a down + // wins server. + // + // 2 sec*3 retries * 8 names / 5 = 9 seconds a shot. + // for a total of 90 seconds max. + // + Timout.QuadPart = Int32x32To64( + MILLISEC_TO_100NS/(COUNT_MAX/2), + (NbtConfig.uRetryTimeout*NbtConfig.uNumRetries) + *NumberNames); + + Timout.QuadPart = -(Timout.QuadPart); + + // + // wait for a few seconds and try again. + // + Locstatus = KeDelayExecutionThread( + KernelMode, + FALSE, // Alertable + &Timout); // Timeout + + + + Count++; + if (Count < COUNT_MAX) + { + CTESpinLock(&NbtConfig.JointLock,OldIrq); + } + } + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } +} + +//---------------------------------------------------------------------------- +NTSTATUS +RcvIrpFromWins ( + IN tDEVICECONTEXT *pDeviceContext, + IN PCTE_IRP pIrp + ) +/*++ + +Routine Description: + + This function takes the rcv irp posted by WINS and decides if there are + any datagram queued waiting to go up to WINS. If so then the datagram + is copied to the WINS buffer and passed back up. Otherwise the irp is + held by Netbt until a datagram does come in. + +Arguments: + + pDeviceContext - not used + pIrp - Wins Rcv Irp + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS status; + NTSTATUS Locstatus; + tREM_ADDRESS *pWinsBuffer; + tWINSRCV_BUFFER *pBuffer; + PLIST_ENTRY pEntry; + CTELockHandle OldIrq; + tWINS_INFO *pWins; + PIO_STACK_LOCATION pIrpSp; + + status = STATUS_INVALID_HANDLE; + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pWins = pIrpSp->FileObject->FsContext; + + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if ((!RefreshedYet) && (pWins == pWinsInfo)) + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + InitiateRefresh(pDeviceContext); + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + RefreshedYet = TRUE; + } + + if (pWins == pWinsInfo) + { + + if (!IsListEmpty(&pWinsInfo->RcvList)) + { + PMDL pMdl; + ULONG CopyLength; + ULONG DgramLength; + ULONG BufferLength; + + // + // There is at least one datagram waiting to be received + // + pEntry = RemoveHeadList(&pWinsInfo->RcvList); + + pBuffer = CONTAINING_RECORD(pEntry,tWINSRCV_BUFFER,Linkage); + + // + // Copy the datagram and the source address to WINS buffer and + // return to WINS + // + pMdl = pIrp->MdlAddress; + pWinsBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + + BufferLength = MmGetMdlByteCount(pMdl); + DgramLength = pBuffer->DgramLength; + + CopyLength = (DgramLength <= BufferLength) ? DgramLength : BufferLength; + CTEMemCopy((PVOID)pWinsBuffer, + (PVOID)&pBuffer->Address.Family, + CopyLength); + + // + // subtract from the total amount buffered for WINS since we are + // passing a datagram up to WINS now. + // + pWinsInfo->RcvMemoryAllocated -= pBuffer->DgramLength; + CTEMemFree(pBuffer); + + ASSERT(pWinsBuffer->Port); + ASSERT(pWinsBuffer->IpAddress); + // + // pass the irp up to WINS + // + if (CopyLength < DgramLength) + { + Locstatus = STATUS_BUFFER_OVERFLOW; + } + else + { + Locstatus = STATUS_SUCCESS; + } + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Returning Wins rcv Irp immediately with queued dgram, status=%X,pIrp=%X\n" + ,status,pIrp)); + + pIrp->IoStatus.Information = CopyLength; + pIrp->IoStatus.Status = Locstatus; + + + IoCompleteRequest(pIrp,IO_NO_INCREMENT); + + return(STATUS_SUCCESS); + + } + else + { + + status = NTCheckSetCancelRoutine(pIrp,WinsIrpCancel,pDeviceContext); + + if (!NT_SUCCESS(status)) + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(pIrp,status,0); + } + else + { + pWinsInfo->RcvIrp = pIrp; + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Holding onto Wins Rcv Irp, pIrp =%Xstatus=%X\n", + status,pIrp)); + + status = STATUS_PENDING; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + + } + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INVALID_HANDLE; + NTIoComplete(pIrp,status,0); + } + + return(status); + +} + +//---------------------------------------------------------------------------- +NTSTATUS +PassNamePduToWins ( + IN tDEVICECONTEXT *pDeviceContext, + IN PVOID pSrcAddress, + IN tNAMEHDR UNALIGNED *pNameSrv, + IN ULONG uNumBytes + ) +/*++ + +Routine Description: + + This function is used to allow NBT to pass name query service Pdu's to + WINS. Wins posts a Rcv irp to Netbt. If the Irp is here then simply + copy the data to the irp and return it, otherwise buffer the data up + to a maximum # of bytes. Beyond that limit the datagrams are discarded. + + If Retstatus is not success then the pdu will also be processed by + nbt. This allows nbt to process packets when wins pauses and + its list of queued buffers is exceeded. + +Arguments: + + pDeviceContext - card that the request can in on + pSrcAddress - source address + pNameSrv - ptr to the datagram + uNumBytes - length of datagram + +Return Value: + + STATUS_PENDING if the buffer is to be held on to , the normal case. + +Notes: + + +--*/ + +{ + NTSTATUS Retstatus; + NTSTATUS status; + tREM_ADDRESS *pWinsBuffer; + PCTE_IRP pIrp; + CTELockHandle OldIrq; + PTRANSPORT_ADDRESS pSourceAddress; + ULONG SrcAddress; + SHORT SrcPort; + + + // + // Get the source port and ip address, since WINS needs this information. + // + pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress; + SrcAddress = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr; + SrcPort = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port; + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + Retstatus = STATUS_SUCCESS; + if (pWinsInfo) + { + if (!pWinsInfo->RcvIrp) + { + // + // Queue the name query pdu if we have not exeeded our current queue + // length + // + if (pWinsInfo->RcvMemoryAllocated < pWinsInfo->RcvMemoryMax) + { + tWINSRCV_BUFFER *pBuffer; + + pBuffer = NbtAllocMem(uNumBytes + sizeof(tWINSRCV_BUFFER)+8,NBT_TAG('v')); + if (pBuffer) + { + // + // check if it is a name reg from this node + // + if (pNameSrv->AnCount == WINS_SIGNATURE) + { + pNameSrv->AnCount = 0; + pBuffer->Address.Family = AF_UNIX; + } + else + { + pBuffer->Address.Family = AF_INET; + } + + CTEMemCopy((PUCHAR)((PUCHAR)pBuffer + sizeof(tWINSRCV_BUFFER)), + (PVOID)pNameSrv,uNumBytes); + + pBuffer->Address.Port = SrcPort; + pBuffer->Address.IpAddress = SrcAddress; + pBuffer->Address.LengthOfBuffer = uNumBytes; + + ASSERT(pBuffer->Address.Port); + ASSERT(pBuffer->Address.IpAddress); + + // total amount allocated + pBuffer->DgramLength = uNumBytes + sizeof(tREM_ADDRESS); + + + // + // Keep track of the total amount buffered so that we don't + // eat up all non-paged pool buffering for WINS + // + pWinsInfo->RcvMemoryAllocated += pBuffer->DgramLength; + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Buffering Wins Rcv - no Irp, status=%X\n")); + InsertTailList(&pWinsInfo->RcvList,&pBuffer->Linkage); + + } + } + else + { + // this ret status will allow netbt to process the packet. + // + Retstatus = STATUS_INSUFFICIENT_RESOURCES; + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + else + { + PMDL pMdl; + ULONG CopyLength; + ULONG DgramLength; + ULONG BufferLength; + + // + // The recv irp is here so copy the data to its buffer and + // pass it up to WINS + // + pIrp = pWinsInfo->RcvIrp; + pWinsInfo->RcvIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Copy the datagram and the source address to WINS buffer and + // return to WINS + // + pMdl = pIrp->MdlAddress; + pWinsBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); + + BufferLength = MmGetMdlByteCount(pMdl); + DgramLength = uNumBytes; + + CopyLength = (DgramLength+sizeof(tREM_ADDRESS)) <= BufferLength ? DgramLength : BufferLength; + + // + // check if it is a name reg from this node + // + if (pNameSrv->AnCount == WINS_SIGNATURE) + { + pNameSrv->AnCount = 0; + pWinsBuffer->Family = AF_UNIX; + } + else + { + pWinsBuffer->Family = AF_INET; + } + CTEMemCopy((PVOID)((PUCHAR)pWinsBuffer + sizeof(tREM_ADDRESS)), + (PVOID)pNameSrv, + CopyLength); + + pWinsBuffer->Port = SrcPort; + pWinsBuffer->IpAddress = SrcAddress; + pWinsBuffer->LengthOfBuffer = uNumBytes; + + ASSERT(pWinsBuffer->Port); + ASSERT(pWinsBuffer->IpAddress); + + // + // pass the irp up to WINS + // + if (CopyLength < DgramLength) + { + status = STATUS_BUFFER_OVERFLOW; + } + else + { + status = STATUS_SUCCESS; + } + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Returning Wins Rcv Irp - data from net, Length=%X,pIrp=%X\n" + ,uNumBytes,pIrp)); + + NTIoComplete(pIrp,status,CopyLength); + + } + } + else + { + // + // this ret status will allow netbt to process the packet. + // + Retstatus = STATUS_INSUFFICIENT_RESOURCES; + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + return(Retstatus); + +} + +//---------------------------------------------------------------------------- +VOID +WinsIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a WinsRcv Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + KIRQL OldIrq; + PIO_STACK_LOCATION pIrpSp; + tWINS_INFO *pWins; + + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Got a Wins Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + // + // Be sure that PassNamePduToWins has not taken the RcvIrp for a + // Rcv just now. + // + if ((pWins == pWinsInfo) && (pWinsInfo->RcvIrp == pIrp)) + { + + pWinsInfo->RcvIrp = NULL; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + + +} +//---------------------------------------------------------------------------- +VOID +WinsSendIrpCancel( + IN PDEVICE_OBJECT DeviceContext, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This routine handles the cancelling a WinsRcv Irp. It must release the + cancel spin lock before returning re: IoCancelIrp(). + +Arguments: + + +Return Value: + + The final status from the operation. + +--*/ +{ + KIRQL OldIrq; + PLIST_ENTRY pHead; + PLIST_ENTRY pEntry; + PIO_STACK_LOCATION pIrpSp; + tWINS_INFO *pWins; + BOOLEAN Found; + PIRP pIrpList; + + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Got a Wins Send Irp Cancel !!! *****************\n")); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext; + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + CTESpinLock(&NbtConfig.JointLock,OldIrq); + + if (pWins == pWinsInfo) + { + // + // find the matching irp on the list and remove it + // + pHead = &pWinsInfo->SendList; + pEntry = pHead; + Found = FALSE; + + while ((pEntry = pEntry->Flink) != pHead) + { + pIrpList = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + if (pIrp == pIrpList) + { + RemoveEntryList(pEntry); + Found = TRUE; + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + if (Found) + { + pIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + } + + +} +//---------------------------------------------------------------------------- +NTSTATUS +WinsSendDatagram( + IN tDEVICECONTEXT *pDeviceContext, + IN PIRP pIrp, + IN BOOLEAN MustSend) + +/*++ +Routine Description: + + This Routine handles sending a datagram down to the transport. MustSend + it set true by the Send Completion routine when it attempts to send + one of the queued datagrams, in case we still don't pass the memory + allocated check and refuse to do the send - sends will just stop then without + this boolean. + +Arguments: + + pIrp - a ptr to an IRP + +Return Value: + + NTSTATUS - status of the request + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS status; + tWINS_INFO *pWins; + tREM_ADDRESS *pSendAddr; + PVOID pDgram; + ULONG DgramLength; + tDGRAM_SEND_TRACKING *pTracker; + CTELockHandle OldIrq; + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext; + + + status = STATUS_UNSUCCESSFUL; + + // + // check if it is a name that is registered on this machine + // + pSendAddr = (tREM_ADDRESS *)MmGetSystemAddressForMdl(pIrp->MdlAddress); + if (pSendAddr->Family == AF_UNIX) + { + status = CheckIfLocalNameActive(pSendAddr); + } + + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pWins == pWinsInfo) + { + + if ((pWins->SendMemoryAllocated < pWins->SendMemoryMax) || MustSend) + { + + if (pSendAddr->IpAddress != 0) + { + + DgramLength = pSendAddr->LengthOfBuffer; + pDgram = WinsAllocMem(DgramLength,FALSE); + + + if (pDgram) + { + CTEMemCopy(pDgram, + (PVOID)((PUCHAR)pSendAddr+sizeof(tREM_ADDRESS)), + DgramLength + ); + + // + // get a buffer for tracking Dgram Sends + // + pTracker = NbtAllocTracker(); + if (pTracker) + { + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + pTracker->SendBuffer.pBuffer = NULL; + pTracker->SendBuffer.Length = 0; + pTracker->SendBuffer.pDgramHdr = pDgram; + pTracker->SendBuffer.HdrLength = DgramLength; + pTracker->pClientEle = NULL; + pTracker->pDestName = NULL; + pTracker->AllocatedLength = DgramLength; + + + // send the Datagram... + status = UdpSendDatagram( + pTracker, + ntohl(pSendAddr->IpAddress), + pDeviceContext->pNameServerFileObject, + WinsDgramCompletion, + pTracker, // context for completion + (USHORT)ntohs(pSendAddr->Port), + NBT_NAME_SERVICE); + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Doing Wins Send, status=%X\n",status)); + + // sending the datagram could return status pending, + // but since we have buffered the dgram, return status + // success to the client + // + status = STATUS_SUCCESS; + // + // Fill in the sent size + // + pIrp->IoStatus.Information = DgramLength; + + } + else + { + WinsFreeMem((PVOID)pDgram,DgramLength,FALSE); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INVALID_PARAMETER; + } + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp,IO_NO_INCREMENT); + } + else + { + + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Holding onto Buffering Wins Send, status=%X\n")); + + + // + // Hold onto the datagram till memory frees up + // + InsertTailList(&pWins->SendList,&pIrp->Tail.Overlay.ListEntry); + + status = NTCheckSetCancelRoutine(pIrp,WinsSendIrpCancel,pDeviceContext); + if (!NT_SUCCESS(status)) + { + RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + NTIoComplete(pIrp,status,0); + + } + else + { + status = STATUS_PENDING; + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + } + + + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + status = STATUS_INVALID_HANDLE; + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp,IO_NO_INCREMENT); + } + + return(status); + +} + + +//---------------------------------------------------------------------------- +NTSTATUS +CheckIfLocalNameActive( + IN tREM_ADDRESS *pSendAddr + ) + +/*++ +Routine Description + + This routine checks if this is a name query response and if the + name is still active on the local node. + +Arguments: + + pMdl = ptr to WINS Mdl + +Return Values: + + VOID + +--*/ + +{ + NTSTATUS status; + tNAMEHDR UNALIGNED *pNameHdr; + tNAMEADDR *pResp; + UCHAR pName[NETBIOS_NAME_SIZE]; + PUCHAR pScope; + ULONG lNameSize; + CTELockHandle OldIrq; + + pNameHdr = (tNAMEHDR UNALIGNED *)((PUCHAR)pSendAddr + sizeof(tREM_ADDRESS)); + // + // Be sure it is a name query PDU that we are checking + // + if (((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_QUERY) || + ((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_RELEASE)) + { + status = ConvertToAscii( + (PCHAR)&pNameHdr->NameRR.NameLength, + pSendAddr->LengthOfBuffer, + pName, + &pScope, + &lNameSize); + + if (NT_SUCCESS(status)) + { + + // + // see if the name is still active in the local hash table + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + status = FindInHashTable(NbtConfig.pLocalHashTbl, + pName, + pScope, + &pResp); + + + if ((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_QUERY) + { + if (NT_SUCCESS(status)) + { + // + // if not resolved then set to negative name query resp. + // + if (!(pResp->NameTypeState & STATE_RESOLVED)) + { + pNameHdr->OpCodeFlags |= htons(NAME_ERROR); + } + } + else + { + pNameHdr->OpCodeFlags |= htons(NAME_ERROR); + } + } + else + { + // + // check if it is a release response - if so we must have + // received a name release request, so mark the name in + // conflict and return a positive release response. + // + if (pNameHdr->OpCodeFlags & OP_RESPONSE) + { + if (NT_SUCCESS(status) && + (pResp->NameTypeState & STATE_RESOLVED)) + { + NbtLogEvent(EVENT_NBT_NAME_RELEASE,pSendAddr->IpAddress); + + pResp->NameTypeState &= ~NAME_STATE_MASK; + pResp->NameTypeState |= STATE_CONFLICT; + + // + // change to successful response + // + pNameHdr->OpCodeFlags &= 0xF0FF; + + } + } + } + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + // + // the name is not in the local table so fail the datagram send attempt + // + return(STATUS_SUCCESS); + +} + +//---------------------------------------------------------------------------- +VOID +WinsDgramCompletion( + IN tDGRAM_SEND_TRACKING *pTracker, + IN NTSTATUS status, + IN ULONG Length + ) + +/*++ +Routine Description + + This routine cleans up after a data gram send. + +Arguments: + + pTracker + status + Length + +Return Values: + + VOID + +--*/ + +{ + CTELockHandle OldIrq; + LIST_ENTRY *pEntry; + PIRP pIrp; + BOOLEAN MustSend; + + // + // free the buffer used for sending the data and the tracker - note + // that the datagram header and the send buffer are allocated as one + // chunk. + // + CTESpinLock(&NbtConfig.JointLock,OldIrq); + if (pWinsInfo) + { + WinsFreeMem((PVOID)pTracker->SendBuffer.pDgramHdr, + pTracker->AllocatedLength, + FALSE); + + if (!IsListEmpty(&pWinsInfo->SendList)) + { + IF_DBG(NBT_DEBUG_WINS) + KdPrint(("Nbt:Sending another Wins Dgram that is Queued to go\n")); + + pEntry = RemoveHeadList(&pWinsInfo->SendList); + pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry); + + CTESpinFree(&NbtConfig.JointLock,OldIrq); + + // + // Send this next datagram + // + status = WinsSendDatagram(pTracker->pDeviceContext, + pIrp, + MustSend = TRUE); + + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); + + } + else + { + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + } + else + { + // + // just free the memory since WINS has closed its address handle. + // + CTEMemFree((PVOID)pTracker->SendBuffer.pDgramHdr); + CTESpinFree(&NbtConfig.JointLock,OldIrq); + } + + CTEFreeMem(pTracker); + + +} + +//---------------------------------------------------------------------------- +PVOID +WinsAllocMem( + IN ULONG Size, + IN BOOLEAN Rcv + ) + +/*++ +Routine Description: + + This Routine handles allocating memory and keeping track of how + much has been allocated. + +Arguments: + + Size - number of bytes to allocate + Rcv - boolean that indicates if it is rcv or send buffering + +Return Value: + + ptr to the memory allocated + +--*/ + +{ + if (Rcv) + { + if (pWinsInfo->RcvMemoryAllocated > pWinsInfo->RcvMemoryMax) + { + return NULL; + } + else + { + pWinsInfo->RcvMemoryAllocated += Size; + return (NbtAllocMem(Size,NBT_TAG('v'))); + } + } + else + { + if (pWinsInfo->SendMemoryAllocated > pWinsInfo->SendMemoryMax) + { + return(NULL); + } + else + { + pWinsInfo->SendMemoryAllocated += Size; + return(NbtAllocMem(Size,NBT_TAG('v'))); + } + } +} +//---------------------------------------------------------------------------- +VOID +WinsFreeMem( + IN PVOID pBuffer, + IN ULONG Size, + IN BOOLEAN Rcv + ) + +/*++ +Routine Description: + + This Routine handles freeing memory and keeping track of how + much has been allocated. + +Arguments: + + pBuffer - buffer to free + Size - number of bytes to allocate + Rcv - boolean that indicates if it is rcv or send buffering + +Return Value: + + none + +--*/ + +{ + if (pWinsInfo) + { + if (Rcv) + { + pWinsInfo->RcvMemoryAllocated -= Size; + } + else + { + pWinsInfo->SendMemoryAllocated -= Size; + } + } + + CTEMemFree(pBuffer); +} |