/*++ Copyright (c) 1991 Microsoft Corporation Module Name: nb.c Abstract: This module contains code which defines the NetBIOS driver's device object. Author: Colin Watson (ColinW) 13-Mar-1991 Environment: Kernel mode Revision History: --*/ #include "nb.h" //#include //#include typedef ADAPTER_STATUS UNALIGNED *PUADAPTER_STATUS; typedef NAME_BUFFER UNALIGNED *PUNAME_BUFFER; typedef SESSION_HEADER UNALIGNED *PUSESSION_HEADER; typedef SESSION_BUFFER UNALIGNED *PUSESSION_BUFFER; #if DBG ULONG NbDebug = 0; #endif #if PAGED_DBG ULONG ThisCodeCantBePaged; #endif PEPROCESS NbFspProcess = NULL; NTSTATUS NbAstat( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ); VOID CopyAddresses( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ); NTSTATUS NbFindName( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ); NTSTATUS NbSstat( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ); VOID CopySessionStatus( IN PDNCB pdncb, IN PCB pcb, IN PUSESSION_HEADER pSessionHeader, IN PUSESSION_BUFFER* ppSessionBuffer, IN PULONG pLengthRemaining ); NTSTATUS NbEnum( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ); NTSTATUS NbReset( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS NbAction( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS NbCancel( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); VOID CancelRoutine( IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PIRP Irp ); NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, DriverEntry) #pragma alloc_text(PAGE, NbDispatch) #pragma alloc_text(PAGE, NbDeviceControl) #pragma alloc_text(PAGE, NbOpen) #pragma alloc_text(PAGE, NbClose) #pragma alloc_text(PAGE, NbAstat) #pragma alloc_text(PAGE, NbEnum) #pragma alloc_text(PAGE, NbReset) #pragma alloc_text(PAGE, NbFindName) #endif NTSTATUS NbCompletionEvent( 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 Netbios driver that it can proceed. 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_NBDBG (NB_DEBUG_COMPLETE) { NbPrint( ("NbCompletion event: %lx, Irp: %lx, DeviceObject: %lx\n", Context, Irp, DeviceObject)); } KeSetEvent((PKEVENT )Context, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; UNREFERENCED_PARAMETER( DeviceObject ); UNREFERENCED_PARAMETER( Irp ); } NTSTATUS NbCompletionPDNCB( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: This routine completes the Irp by setting the length and status bytes in the NCB supplied in context. Send requests have additional processing to remove the send request from the connection block send list. Arguments: DeviceObject - unused. Irp - Supplies Irp that the transport has finished processing. Context - Supplies the NCB associated with the Irp. Return Value: The final status from the operation (success or an exception). --*/ { PDNCB pdncb = (PDNCB) Context; NTSTATUS Status = STATUS_SUCCESS; IF_NBDBG (NB_DEBUG_COMPLETE) { NbPrint(("NbCompletionPDNCB pdncb: %lx, Status: %X, Length %lx\n", Context, Irp->IoStatus.Status, Irp->IoStatus.Information )); } // Tell application how many bytes were transferred pdncb->ncb_length = (unsigned short)Irp->IoStatus.Information; if ( NT_SUCCESS(Irp->IoStatus.Status) ) { NCB_COMPLETE( pdncb, NRC_GOODRET ); } else { if (((pdncb->ncb_command & ~ASYNCH) == NCBRECV ) || ((pdncb->ncb_command & ~ASYNCH) == NCBRECVANY )) { if ( Irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW ) { PIRP LocalIrp = NULL; KIRQL OldIrql; // Used when SpinLock held. PPCB ppcb; PDEVICE_OBJECT LocalDeviceObject; LOCK_SPINLOCK( pdncb->pfcb, OldIrql ); // // The transport will not indicate again so we must put // another receive down if we can. // If an Irp cannot be built then BuildReceiveIrp will // set ReceiveIndicated. // ppcb = FindCb( pdncb->pfcb, pdncb, FALSE ); if ( ppcb != NULL ) { LocalDeviceObject = (*ppcb)->DeviceObject; LocalIrp = BuildReceiveIrp( *ppcb ); } UNLOCK_SPINLOCK( pdncb->pfcb, OldIrql ); if ( LocalIrp != NULL ) { IoCallDriver (LocalDeviceObject, LocalIrp); } } } NCB_COMPLETE( pdncb, NbMakeNbError( Irp->IoStatus.Status ) ); } // // Tell IopCompleteRequest how much to copy back when the request // completes. // Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); // // Remove the Send request from the send queue. We have to scan // the queue because they may be completed out of order if a send // is rejected because of resource limitations. // if (((pdncb->ncb_command & ~ASYNCH) == NCBSEND ) || ((pdncb->ncb_command & ~ASYNCH) == NCBCHAINSEND ) || ((pdncb->ncb_command & ~ASYNCH) == NCBSENDNA ) || ((pdncb->ncb_command & ~ASYNCH) == NCBCHAINSENDNA )) { PLIST_ENTRY SendEntry; PPCB ppcb; KIRQL OldIrql; // Used when SpinLock held. LOCK_SPINLOCK( pdncb->pfcb, OldIrql ); ppcb = FindCb( pdncb->pfcb, pdncb, FALSE ); // // If the connection block still exists remove the send. If the connection // has gone then we no longer need to worry about maintaining the list. // if ( ppcb != NULL ) { #if DBG BOOLEAN Found = FALSE; #endif PCB pcb = *ppcb; for (SendEntry = pcb->SendList.Flink ; SendEntry != &pcb->SendList ; SendEntry = SendEntry->Flink) { PDNCB pSend = CONTAINING_RECORD( SendEntry, DNCB, ncb_next); if ( pSend == pdncb ) { #if DBG Found = TRUE; #endif RemoveEntryList( &pdncb->ncb_next ); break; } } ASSERT( Found == TRUE); // // If the session is being hung up then we may wish to cleanup the connection // as well. STATUS_HANGUP_REQUIRED will cause the dll to manufacture // another hangup. The manufactured hangup will complete along with // pcb->pdncbHangup. This method is used to ensure that when a // hangup is delayed by an outstanding send and the send finally // completes, that the user hangup completes after all operations // on the connection. // if (( IsListEmpty( &pcb->SendList) ) && ( pcb->pdncbHangup != NULL )) { IF_NBDBG (NB_DEBUG_COMPLETE) { NbPrint( ("NbCompletionPDNCB Hangup session: %lx\n", ppcb )); } Status = STATUS_HANGUP_REQUIRED; } } UNLOCK_SPINLOCK( pdncb->pfcb, OldIrql ); } // // Must return a non-error status otherwise the IO system will not copy // back the NCB into the users buffer. // Irp->IoStatus.Status = Status; return Status; UNREFERENCED_PARAMETER( DeviceObject ); } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine performs initialization of the NetBIOS driver. Arguments: DriverObject - Pointer to driver object created by the system. RegistryPath - The name of the Netbios node in the registry. Return Value: The function value is the final status from the initialization operation. --*/ { PDEVICE_CONTEXT DeviceContext; NTSTATUS status; UNICODE_STRING UnicodeString; //STRING AnsiNameString; PAGED_CODE(); // #ifdef MEMPRINT MemPrintInitialize (); #endif // // Create the device object for NETBEUI. For now, we simply create // \Device\Netbios using a unicode string. // NbFspProcess = PsGetCurrentProcess(); RtlInitUnicodeString( &UnicodeString, NB_DEVICE_NAME); status = NbCreateDeviceContext (DriverObject, &UnicodeString, &DeviceContext, RegistryPath); if (!NT_SUCCESS (status)) { NbPrint( ("NbInitialize: Netbios failed to initialize\n")); return status; } DeviceContext->Initialized = TRUE; IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbInitialize: Netbios initialized.\n")); } return (status); } NTSTATUS NbDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the main dispatch routine for the NB device driver. It accepts an I/O Request Packet, performs the request, and then returns with the appropriate status. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PDEVICE_CONTEXT DeviceContext; PAGED_CODE(); // // Check to see if NB has been initialized; if not, don't allow any use. // DeviceContext = (PDEVICE_CONTEXT)DeviceObject; if (!DeviceContext->Initialized) { return STATUS_UNSUCCESSFUL; } // // Get a pointer to the current stack location in the IRP. This is where // the function codes and parameters are stored. // IrpSp = IoGetCurrentIrpStackLocation (Irp); // // Case on the function that is being performed by the requestor. If the // operation is a valid one for this device, then make it look like it was // successfully completed, where possible. // switch (IrpSp->MajorFunction) { // // The Create function opens a handle that can be used with fsctl's // to build all interesting operations. // case IRP_MJ_CREATE: IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: IRP_MJ_CREATE.\n")); } Status = NbOpen ( DeviceContext, IrpSp ); Irp->IoStatus.Information = FILE_OPENED; break; // // The Close function closes a transport , terminates // all outstanding transport activity on the transport, and unbinds // the from its transport address, if any. If this // is the last transport endpoint bound to the address, then // the address is removed by the provider. // case IRP_MJ_CLOSE: IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: IRP_MJ_CLOSE.\n")); } Status = NbClose( IrpSp); break; // // The DeviceControl function is the main path to the transport // driver interface. Every TDI request is assigned a minor // function code that is processed by this function. // case IRP_MJ_DEVICE_CONTROL: IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: IRP_MJ_DEVICE_CONTROL, Irp: %lx.\n", Irp )); } Status = NbDeviceControl (DeviceObject, Irp, IrpSp); if (Status != STATUS_PENDING) { // // Tell IopCompleteRequest how much to copy back when the // request completes. We need to do this for cases where // NbCompletionPDNCB is not used. // Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); } #if DBG if ( (Status != STATUS_SUCCESS) && (Status != STATUS_PENDING ) && (Status != STATUS_HANGUP_REQUIRED )) { IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: Invalid status: %X.\n", Status )); ASSERT( FALSE ); } } #endif break; // // Handle the two stage IRP for a file close operation. When the first // stage hits, ignore it. We will do all the work on the close Irp. // case IRP_MJ_CLEANUP: IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: IRP_MJ_CLEANUP.\n")); } Status = STATUS_SUCCESS; break; default: IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: OTHER (DEFAULT).\n")); } Status = STATUS_INVALID_DEVICE_REQUEST; } /* major function switch */ if (Status == STATUS_PENDING) { IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: request PENDING from handler.\n")); } } else { IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDispatch: request COMPLETED by handler.\n")); } NbCompleteRequest( Irp, Status); } return Status; } /* NbDispatch */ NTSTATUS NbDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine dispatches NetBios request types to different handlers based on the minor IOCTL function code in the IRP's current stack location. In addition to cracking the minor function code, this routine also reaches into the IRP and passes the packetized parameters stored there as parameters to the various request handlers so that they are not IRP-dependent. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; PNCB pUsersNcb; PDNCB pdncb; PUCHAR Buffer2; ULONG Buffer2Length; ULONG RequestLength; PAGED_CODE(); IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDeviceControl: Entered...\n")); } if (IrpSp->Parameters.DeviceIoControl.IoControlCode != IOCTL_NB_NCB) { IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDeviceControl: invalid request type.\n")); } return STATUS_INVALID_DEVICE_REQUEST; } // // Caller provided 2 buffers. The first is the NCB. // The second is an optional buffer for send or receive data. // Since the Netbios driver only operates in the context of the // calling application, these buffers are directly accessable. // however they can be deleted by the user so try-except clauses are // required. // pUsersNcb = (PNCB)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; Buffer2 = Irp->UserBuffer; Buffer2Length = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; if ( RequestLength != sizeof( NCB ) ) { return STATUS_INVALID_PARAMETER; } // // Create a copy of the NCB and convince the IO system to // copy it back (and deallocate it) when the IRP completes. // Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag( NonPagedPool, sizeof( DNCB ), 'nSBN' ); if (Irp->AssociatedIrp.SystemBuffer == NULL) { // // Since we cannot allocate the drivers copy of the NCB, we // must turn around and use the original Ncb to return the error. // pUsersNcb->ncb_retcode = NRC_NORES; Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); return STATUS_SUCCESS; } try { // In the driver we should now use our copy of the NCB pdncb = Irp->AssociatedIrp.SystemBuffer; RtlMoveMemory( pdncb, pUsersNcb, FIELD_OFFSET( DNCB, ncb_cmd_cplt )+1 ); Irp->Flags |= (ULONG) (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION ); // // Save the users virtual address for the NCB just in case the // virtual address is supplied in an NCBCANCEL. This is the same // as Irp->UserBuffer. // pdncb->users_ncb = pUsersNcb; // // Tell the IO system where to copy the ncb back to during // IoCompleteRequest. // Irp->UserBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDeviceControl: Exception1 %X.\n", Status)); } NCB_COMPLETE( pdncb, NbMakeNbError(Status) ); return Status; } if ( Buffer2Length ) { // Mdl will be freed by IopCompleteRequest. Irp->MdlAddress = IoAllocateMdl( Buffer2, Buffer2Length, FALSE, FALSE, Irp ); ASSERT( Irp->MdlAddress != NULL ); try { MmProbeAndLockPages( Irp->MdlAddress, Irp->RequestorMode, (LOCK_OPERATION) IoModifyAccess); } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDeviceControl: Exception2 %X.\n", Status)); } if ( Irp->MdlAddress != NULL ) { IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; } NCB_COMPLETE( pdncb, NbMakeNbError(Status) ); return STATUS_SUCCESS; } } else { ASSERT( Irp->MdlAddress == NULL ); } IF_NBDBG (NB_DEBUG_DISPATCH) { NbPrint( ("NbDeviceControl: IoContolCode: %lx, Fcb: %lx," " ncb_command %lx, Buffer2Length: %lx\n", IrpSp->Parameters.DeviceIoControl.IoControlCode, IrpSp->FileObject->FsContext2, pdncb->ncb_command, Buffer2Length)); } switch ( pdncb->ncb_command & ~ASYNCH ) { case NCBCALL: case NCALLNIU: Status = NbCall( pdncb, Irp, IrpSp ); break; case NCBCANCEL: Status = NbCancel( pdncb, Irp, IrpSp ); break; case NCBLISTEN: Status = NbListen( pdncb, Irp, IrpSp ); break; case NCBHANGUP: Status = NbHangup( pdncb, Irp, IrpSp ); break; case NCBASTAT: Status = NbAstat( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBFINDNAME: Status = NbFindName( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBSSTAT: Status = NbSstat( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBENUM: NbEnum( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBRECV: Status = NbReceive( pdncb, Irp, IrpSp, Buffer2Length, FALSE, 0 ); break; case NCBRECVANY: Status = NbReceiveAny( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBDGRECV: case NCBDGRECVBC: Status = NbReceiveDatagram( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBSEND: case NCBSENDNA: case NCBCHAINSEND: case NCBCHAINSENDNA: Status = NbSend( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBDGSEND: case NCBDGSENDBC: Status = NbSendDatagram( pdncb, Irp, IrpSp, Buffer2Length ); break; case NCBADDNAME: case NCBADDGRNAME: case NCBQUICKADDNAME: case NCBQUICKADDGRNAME: NbAddName( pdncb, IrpSp ); break; case NCBDELNAME: NbDeleteName( pdncb, IrpSp ); break; case NCBLANSTALERT: Status = NbLanStatusAlert( pdncb, Irp, IrpSp ); break; case NCBRESET: Status = NbReset( pdncb, Irp, IrpSp ); break; case NCBACTION: Status = NbAction( pdncb, Irp, IrpSp); break; // The following are No-operations that return success for compatibility case NCBUNLINK: case NCBTRACE: NCB_COMPLETE( pdncb, NRC_GOODRET ); break; default: NCB_COMPLETE( pdncb, NRC_ILLCMD ); break; } return Status; UNREFERENCED_PARAMETER( DeviceObject ); } /* NbDeviceControl */ NTSTATUS NbOpen( IN PDEVICE_CONTEXT DeviceContext, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Arguments: DeviceContext - Includes the name of the netbios node in the registry. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { PAGED_CODE(); return NewFcb( DeviceContext, IrpSp ); } /* NbOpen */ NTSTATUS NbClose( IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine is called to close an existing handle. This involves running down all of the current and pending activity associated with the handle, and dereferencing structures as appropriate. Arguments: Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { PFCB pfcb = IrpSp->FileObject->FsContext2; PAGED_CODE(); if (pfcb!=NULL) { CleanupFcb( IrpSp, pfcb ); } return STATUS_SUCCESS; } /* NbClose */ NTSTATUS NbAstat( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ) /*++ Routine Description: This routine is called to return the adapter status. It queries the transport for the main adapter status data such as number of FRMR frames received and then uses CopyAddresses to fill in the status for the names that THIS application has added. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Buffer2Length - User provided buffer length for data. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; TDI_CONNECTION_INFORMATION RequestInformation; TA_NETBIOS_ADDRESS ConnectBlock; PTDI_ADDRESS_NETBIOS temp; PFCB pfcb = IrpSp->FileObject->FsContext2; PAGED_CODE(); if ( Buffer2Length >= sizeof(ADAPTER_STATUS) ) { KEVENT Event1; NTSTATUS Status; HANDLE TdiHandle; PFILE_OBJECT TdiObject; PDEVICE_OBJECT DeviceObject; if ( pdncb->ncb_lana_num > pfcb->MaxLana ) { NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } if (( pfcb == NULL ) || (pfcb->ppLana[pdncb->ncb_lana_num] == NULL ) || (pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED)) { NCB_COMPLETE( pdncb, NRC_ENVNOTDEF ); // need a reset return STATUS_SUCCESS; } // NULL returns a handle for doing control functions Status = NbOpenAddress ( &TdiHandle, (PVOID*)&TdiObject, pfcb, pdncb->ncb_lana_num, NULL); if (!NT_SUCCESS(Status)) { IF_NBDBG (NB_DEBUG_ASTAT) { NbPrint(( "\n FAILED on open of Tdi: %X ******\n", Status )); } NCB_COMPLETE( pdncb, NRC_SYSTEM ); return STATUS_SUCCESS; } KeInitializeEvent ( &Event1, SynchronizationEvent, FALSE); DeviceObject = IoGetRelatedDeviceObject( TdiObject ); TdiBuildQueryInformation( Irp, DeviceObject, TdiObject, NbCompletionEvent, &Event1, TDI_QUERY_ADAPTER_STATUS, Irp->MdlAddress); if ( pdncb->ncb_callname[0] != '*') { // // Remote Astat. The variables used to specify the remote adapter name // are kept the same as those in connect.c to aid maintenance. // PIO_STACK_LOCATION NewIrpSp = IoGetNextIrpStackLocation (Irp); ConnectBlock.TAAddressCount = 1; ConnectBlock.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; ConnectBlock.Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock.Address[0].Address; temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; RtlMoveMemory( temp->NetbiosName, pdncb->ncb_callname, NCBNAMSZ ); RequestInformation.RemoteAddress = &ConnectBlock; RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + sizeof (TDI_ADDRESS_NETBIOS); ((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&NewIrpSp->Parameters) ->RequestConnectionInformation = &RequestInformation; } else { // // Avoid situation where adapter has more names added than the process and // then extra names get added to the end of the buffer. // // // Map the users buffer now so that the whole buffer is mapped (not // just sizeof ADAPTER_STATUS). // if (Irp->MdlAddress) { MmGetSystemAddressForMdl (Irp->MdlAddress); } else { ASSERT(FALSE); } Irp->MdlAddress->ByteCount = sizeof(ADAPTER_STATUS); } IoCallDriver (DeviceObject, Irp); Status = KeWaitForSingleObject (&Event1, Executive, KernelMode, TRUE, NULL); // // Restore length now that the transport has filled in no more than // is required of it. // if (Irp->MdlAddress) { Irp->MdlAddress->ByteCount = Buffer2Length; } NbAddressClose( TdiHandle, TdiObject ); if (!NT_SUCCESS(Status)) { NCB_COMPLETE( pdncb, NRC_SYSTEM ); return Status; } Status = Irp->IoStatus.Status; if (( Status == STATUS_BUFFER_OVERFLOW ) && ( pdncb->ncb_callname[0] == '*')) { // // This is a local ASTAT. Don't worry if there was not enough room in the // users buffer for all the addresses that the transport knows about. There // only needs to be space for the names the user has added and we will check // that later. // Status = STATUS_SUCCESS; } if (!NT_SUCCESS(Status)) { pdncb->ncb_length = (WORD)Irp->IoStatus.Information; NCB_COMPLETE( pdncb, NbMakeNbError(Status) ); } else { if ( pdncb->ncb_callname[0] == '*') { // // Append the addresses and Netbios maintained counts. // CopyAddresses( pdncb, Irp, IrpSp, Buffer2Length); // CopyAddresses completes the NCB appropriately. } else { pdncb->ncb_length = (WORD)Irp->IoStatus.Information; NCB_COMPLETE( pdncb, NRC_GOODRET ); } } } else { NCB_COMPLETE( pdncb, NRC_BUFLEN ); } #if DBG IF_NBDBG (NB_DEBUG_ASTAT) { NbFormattedDump(MmGetSystemAddressForMdl (Irp->MdlAddress), pdncb->ncb_length ); } #endif // // Because the completion routine returned STATUS_MORE_PROCESSING_REQUIRED // NbAstat must return a status other than STATUS_PENDING so that the // users Irp gets completed. // ASSERT( Status != STATUS_PENDING ); return Status; UNREFERENCED_PARAMETER( IrpSp ); } VOID CopyAddresses( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ) /*++ Routine Description: This routine is called to finish the adapter status. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Buffer2Length - User provided buffer length for data. Return Value: none. --*/ { ULONG LengthRemaining = Buffer2Length - sizeof(ADAPTER_STATUS); PUADAPTER_STATUS pAdapter; PUNAME_BUFFER pNameArray; int NextEntry = 0; // Used to walk pNameArray PFCB pfcb = IrpSp->FileObject->FsContext2; PLANA_INFO plana; int index; // Used to access AddressBlocks KIRQL OldIrql; // Used when SpinLock held. LOCK( pfcb, OldIrql ); plana = pfcb->ppLana[pdncb->ncb_lana_num]; if ((plana == NULL ) || (plana->Status != NB_INITIALIZED)) { NCB_COMPLETE( pdncb, NRC_ENVNOTDEF ); // need a reset UNLOCK( pfcb, OldIrql ); return; } // // Map the users buffer so we can poke around inside // if (Irp->MdlAddress) { pAdapter = MmGetSystemAddressForMdl (Irp->MdlAddress); } else { ASSERT(FALSE); } pNameArray = (PUNAME_BUFFER)((PUCHAR)pAdapter + sizeof(ADAPTER_STATUS)); pAdapter->rev_major = 0x03; pAdapter->rev_minor = 0x00; pAdapter->free_ncbs = 255; pAdapter->max_cfg_ncbs = 255; pAdapter->max_ncbs = 255; pAdapter->pending_sess = 0; for ( index = 0; index <= MAXIMUM_CONNECTION; index++ ) { if ( plana->ConnectionBlocks[index] != NULL) { pAdapter->pending_sess++; } } pAdapter->max_cfg_sess = (WORD)plana->MaximumConnection; pAdapter->max_sess = (WORD)plana->MaximumConnection; pAdapter->name_count = 0; // Don't include the reserved address so start at index=2. for ( index = 2; index < MAXIMUM_ADDRESS; index++ ) { if ( plana->AddressBlocks[index] != NULL ) { if ( LengthRemaining >= sizeof(NAME_BUFFER) ) { RtlCopyMemory( (PUCHAR)&pNameArray[NextEntry], &plana->AddressBlocks[index]->Name, sizeof(NAME)); pNameArray[NextEntry].name_num = plana->AddressBlocks[index]->NameNumber; pNameArray[NextEntry].name_flags = plana->AddressBlocks[index]->Status; LengthRemaining -= sizeof(NAME_BUFFER); NextEntry++; pAdapter->name_count++; } else { NCB_COMPLETE( pdncb, NRC_INCOMP ); goto exit; } } } NCB_COMPLETE( pdncb, NRC_GOODRET ); exit: pdncb->ncb_length = (unsigned short)( sizeof(ADAPTER_STATUS) + ( sizeof(NAME_BUFFER) * NextEntry)); UNLOCK( pfcb, OldIrql ); } NTSTATUS NbFindName( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ) /*++ Routine Description: This routine is called to return the result of a name query. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Buffer2Length - User provided buffer length for data. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; TDI_CONNECTION_INFORMATION RequestInformation; TA_NETBIOS_ADDRESS ConnectBlock; PTDI_ADDRESS_NETBIOS temp; PFCB pfcb = IrpSp->FileObject->FsContext2; KEVENT Event1; HANDLE TdiHandle; PFILE_OBJECT TdiObject; PDEVICE_OBJECT DeviceObject; PIO_STACK_LOCATION NewIrpSp = IoGetNextIrpStackLocation (Irp); PAGED_CODE(); if ( Buffer2Length < (sizeof(FIND_NAME_HEADER) + sizeof(FIND_NAME_BUFFER)) ) { NCB_COMPLETE( pdncb, NRC_BUFLEN ); return STATUS_SUCCESS; } LOCK_RESOURCE( pfcb ); if (( pdncb->ncb_lana_num > pfcb->MaxLana ) || ( pfcb == NULL ) || (pfcb->ppLana[pdncb->ncb_lana_num] == NULL ) || (pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED)) { UNLOCK_RESOURCE( pfcb ); NCB_COMPLETE( pdncb, NRC_ENVNOTDEF ); // need a reset return STATUS_SUCCESS; } UNLOCK_RESOURCE( pfcb ); // NULL returns a handle for doing control functions Status = NbOpenAddress ( &TdiHandle, (PVOID*)&TdiObject, pfcb, pdncb->ncb_lana_num, NULL); if (!NT_SUCCESS(Status)) { IF_NBDBG (NB_DEBUG_ASTAT) { NbPrint(( "\n FAILED on open of Tdi: %X ******\n", Status )); } NCB_COMPLETE( pdncb, NRC_SYSTEM ); return STATUS_SUCCESS; } KeInitializeEvent ( &Event1, SynchronizationEvent, FALSE); DeviceObject = IoGetRelatedDeviceObject( TdiObject ); TdiBuildQueryInformation( Irp, DeviceObject, TdiObject, NbCompletionEvent, &Event1, TDI_QUERY_FIND_NAME, Irp->MdlAddress); // // The variables used to specify the remote adapter name // are kept the same as those in connect.c to aid maintenance. // ConnectBlock.TAAddressCount = 1; ConnectBlock.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; ConnectBlock.Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock.Address[0].Address; temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; RtlMoveMemory( temp->NetbiosName, pdncb->ncb_callname, NCBNAMSZ ); RequestInformation.RemoteAddress = &ConnectBlock; RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + sizeof (TDI_ADDRESS_NETBIOS); ((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&NewIrpSp->Parameters) ->RequestConnectionInformation = &RequestInformation; IoCallDriver (DeviceObject, Irp); Status = KeWaitForSingleObject (&Event1, Executive, KernelMode, TRUE, NULL); NbAddressClose( TdiHandle, TdiObject ); if (NT_SUCCESS(Status)) { Status = Irp->IoStatus.Status; } if (!NT_SUCCESS(Status)) { NCB_COMPLETE( pdncb, NbMakeNbError(Status) ); Status = STATUS_SUCCESS; } else { pdncb->ncb_length = (WORD)Irp->IoStatus.Information; NCB_COMPLETE( pdncb, NRC_GOODRET ); } // // Because the completion routine returned STATUS_MORE_PROCESSING_REQUIRED // NbFindName must return a status other than STATUS_PENDING so that the // users Irp gets completed. // ASSERT( Status != STATUS_PENDING ); return Status; } NTSTATUS NbSstat( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ) /*++ Routine Description: This routine is called to return session status. It uses only structures internal to this driver. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Buffer2Length - User provided buffer length for data. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; if ( Buffer2Length >= sizeof(SESSION_HEADER) ) { PFCB pfcb = IrpSp->FileObject->FsContext2; PLANA_INFO plana; int index; PUSESSION_HEADER pSessionHeader; PUSESSION_BUFFER pSessionBuffer; ULONG LengthRemaining; PAB pab; KIRQL OldIrql; // Used when SpinLock held. // // Prevent indications from the transport, post routines being called // and another thread making a request while manipulating the netbios // data structures. // LOCK( pfcb, OldIrql ); if (pdncb->ncb_lana_num > pfcb->MaxLana ) { UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } if (( pfcb == NULL ) || ( pfcb->ppLana[pdncb->ncb_lana_num] == (LANA_INFO *) NULL ) || ( pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED) ) { UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } plana = pfcb->ppLana[pdncb->ncb_lana_num]; if ( pdncb->ncb_name[0] != '*') { PPAB ppab = FindAb(pfcb, pdncb, FALSE); if ( ppab == NULL) { UNLOCK( pfcb, OldIrql ); pdncb->ncb_retcode = NRC_PENDING; NCB_COMPLETE( pdncb, NRC_NOWILD ); return STATUS_SUCCESS; } pab = *ppab; } // // Map the users buffer so we can poke around inside // if (Irp->MdlAddress) { pSessionHeader = MmGetSystemAddressForMdl (Irp->MdlAddress); } else { ASSERT(FALSE); } pSessionHeader->sess_name = 0; pSessionHeader->num_sess = 0; pSessionHeader->rcv_dg_outstanding = 0; pSessionHeader->rcv_any_outstanding = 0; if ( pdncb->ncb_name[0] == '*') { for ( index = 0; index <= MAXIMUM_ADDRESS; index++ ) { if ( plana->AddressBlocks[index] != NULL ) { PLIST_ENTRY Entry; pab = plana->AddressBlocks[index]; for (Entry = pab->ReceiveDatagramList.Flink ; Entry != &pab->ReceiveDatagramList ; Entry = Entry->Flink) { pSessionHeader->rcv_dg_outstanding++ ; } for (Entry = pab->ReceiveBroadcastDatagramList.Flink ; Entry != &pab->ReceiveBroadcastDatagramList ; Entry = Entry->Flink) { pSessionHeader->rcv_dg_outstanding++ ; } for (Entry = pab->ReceiveAnyList.Flink ; Entry != &pab->ReceiveAnyList ; Entry = Entry->Flink) { pSessionHeader->rcv_any_outstanding++; } } } pSessionHeader->sess_name = MAXIMUM_ADDRESS; } else { PLIST_ENTRY Entry; PAB pab255; // Add entries for this name alone. for (Entry = pab->ReceiveDatagramList.Flink ; Entry != &pab->ReceiveDatagramList ; Entry = Entry->Flink) { pSessionHeader->rcv_dg_outstanding++ ; } pab255 = plana->AddressBlocks[MAXIMUM_ADDRESS]; for (Entry = pab255->ReceiveBroadcastDatagramList.Flink ; Entry != &pab255->ReceiveBroadcastDatagramList ; Entry = Entry->Flink) { PDNCB pdncbEntry = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( pdncbEntry->ncb_num == pab->NameNumber ) { pSessionHeader->rcv_dg_outstanding++ ; } } for (Entry = pab->ReceiveAnyList.Flink ; Entry != &pab->ReceiveAnyList ; Entry = Entry->Flink) { pSessionHeader->rcv_any_outstanding++; } pSessionHeader->sess_name = pab->NameNumber; } LengthRemaining = Buffer2Length - sizeof(SESSION_HEADER); pSessionBuffer = (PUSESSION_BUFFER)( pSessionHeader+1 ); for ( index=1 ; index <= MAXIMUM_CONNECTION; index++ ) { CopySessionStatus( pdncb, plana->ConnectionBlocks[index], pSessionHeader, &pSessionBuffer, &LengthRemaining); } /* Undocumented Netbios 3.0 feature, returned length == requested length and not the length of data returned. The following expression gives the number of bytes actually used. pdncb->ncb_length = (USHORT) (sizeof(SESSION_HEADER)+ (sizeof(SESSION_BUFFER) * pSessionHeader->num_sess)); */ UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_GOODRET ); } else { NCB_COMPLETE( pdncb, NRC_BUFLEN ); } return STATUS_SUCCESS; UNREFERENCED_PARAMETER( IrpSp ); } VOID CopySessionStatus( IN PDNCB pdncb, IN PCB pcb, IN PUSESSION_HEADER pSessionHeader, IN PUSESSION_BUFFER* ppSessionBuffer, IN PULONG pLengthRemaining ) /*++ Routine Description: This routine is called to determine if a session should be added to the callers buffer and if so it fills in the data. If there is an error it records the problem in the callers NCB. Arguments: pdncb - Pointer to the NCB. pcb - Connection Block for a particular session pSessionHeader - Start of the callers buffer ppSessionBuffer - Next position to fill in inside the users buffer. pLengthRemaining - size in bytes remaining to be filled. Return Value: none. --*/ { PAB pab; PLIST_ENTRY Entry; if ( pcb == NULL ) { return; } pab = *(pcb->ppab); if (( pdncb->ncb_name[0] == '*') || (RtlEqualMemory( &pab->Name, pdncb->ncb_name, NCBNAMSZ))) { pSessionHeader->num_sess++; if ( *pLengthRemaining < sizeof(SESSION_BUFFER) ) { NCB_COMPLETE( pdncb, NRC_INCOMP ); return; } (*ppSessionBuffer)->lsn = pcb->SessionNumber; (*ppSessionBuffer)->state = pcb->Status; RtlMoveMemory((*ppSessionBuffer)->local_name, &pab->Name, NCBNAMSZ); RtlMoveMemory((*ppSessionBuffer)->remote_name, &pcb->RemoteName, NCBNAMSZ); (*ppSessionBuffer)->sends_outstanding = 0; (*ppSessionBuffer)->rcvs_outstanding = 0; for (Entry = pcb->SendList.Flink ; Entry != &pcb->SendList ; Entry = Entry->Flink) { (*ppSessionBuffer)->sends_outstanding++; } for (Entry = pcb->ReceiveList.Flink ; Entry != &pcb->ReceiveList ; Entry = Entry->Flink) { (*ppSessionBuffer)->rcvs_outstanding++; } *ppSessionBuffer +=1; *pLengthRemaining -= sizeof(SESSION_BUFFER); } } NTSTATUS NbEnum( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN ULONG Buffer2Length ) /*++ Routine Description: This routine is called to discover the available lana numbers. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Buffer2Length - Length of user provided buffer for data. Return Value: The function value is the status of the operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; PUCHAR Buffer2; PFCB pfcb = IrpSp->FileObject->FsContext2; PAGED_CODE(); // // Map the users buffer so we can poke around inside // if (Irp->MdlAddress) { Buffer2 = MmGetSystemAddressForMdl (Irp->MdlAddress); } else { // // Either a zero byte read/write or the request only has an NCB. // Buffer2 = NULL; Buffer2Length = 0; } // Copy over as much information as the user allows. if ( (ULONG)pfcb->LanaEnum.length + 1 > Buffer2Length ) { if ( Buffer2Length > 0 ) { RtlMoveMemory( Buffer2, &pfcb->LanaEnum, Buffer2Length); } NCB_COMPLETE( pdncb, NRC_BUFLEN ); } else { RtlMoveMemory( Buffer2, &pfcb->LanaEnum, (ULONG)pfcb->LanaEnum.length + 1 ); NCB_COMPLETE( pdncb, NRC_GOODRET ); } return Status; } NTSTATUS NbReset( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine is called to reset an adapter. Until an adapter is reset, no access to the lan is allowed. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { PFCB pfcb = IrpSp->FileObject->FsContext2; PAGED_CODE(); IF_NBDBG (NB_DEBUG_CALL) { NbPrint(( "\n****** Start of NbReset ****** pdncb %lx\n", pdncb )); } if ( pdncb->ncb_lana_num > pfcb->MaxLana) { NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } // MaxLana is really the last assigned lana number hence > not >= if ( pdncb->ncb_lana_num > pfcb->MaxLana) { NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } // // Wait till all addnames are completed and prevent any new // ones while we reset the lana. Note We lock out addnames for all // lanas. This is ok since addnames are pretty rare as are resets. // ExAcquireResourceExclusive( &pfcb->AddResource, TRUE); IF_NBDBG (NB_DEBUG_CALL) { NbPrint(( "\nNbReset have resource exclusive\n" )); } if ( pfcb->ppLana[pdncb->ncb_lana_num] != NULL ) { CleanupLana( pfcb, pdncb->ncb_lana_num, TRUE); } if ( pdncb->ncb_lsn == 0 ) { // Allocate resources OpenLana( pdncb, Irp, IrpSp ); } else { NCB_COMPLETE( pdncb, NRC_GOODRET ); } // Allow more addnames ExReleaseResource( &pfcb->AddResource ); return STATUS_SUCCESS; } NTSTATUS NbAction( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine is called to access a transport specific extension. Netbios does not know anything about what the extension does. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { PFCB pfcb = IrpSp->FileObject->FsContext2; PCB pcb; PDEVICE_OBJECT DeviceObject; KIRQL OldIrql; // Used when SpinLock held. IF_NBDBG (NB_DEBUG_CALL) { NbPrint(( "\n****** Start of NbAction ****** pdncb %lx\n", pdncb )); } if ( pdncb->ncb_lana_num > pfcb->MaxLana) { NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } // // The operation can only be performed on one handle so if the NCB specifies both // a connection and an address then reject the request. // if (( pdncb->ncb_lsn != 0) && ( pdncb->ncb_num != 0)) { NCB_COMPLETE( pdncb, NRC_ILLCMD ); // No really good errorcode for this return STATUS_SUCCESS; } if ( pdncb->ncb_length < sizeof(ACTION_HEADER) ) { NCB_COMPLETE( pdncb, NRC_BUFLEN ); return STATUS_SUCCESS; } if ( (ULONG)pdncb->ncb_buffer & 3 ) { NCB_COMPLETE( pdncb, NRC_BADDR ); // Buffer not word aligned return STATUS_SUCCESS; } LOCK( pfcb, OldIrql ); pdncb->irp = Irp; pdncb->pfcb = pfcb; if ( pdncb->ncb_lsn != 0) { // Use handle associated with this connection PPCB ppcb; ppcb = FindCb( pfcb, pdncb, FALSE); if ( ppcb == NULL ) { // FindCb has put the error in the NCB UNLOCK( pfcb, OldIrql ); if ( pdncb->ncb_retcode == NRC_SCLOSED ) { // Tell dll to hangup the connection. return STATUS_HANGUP_REQUIRED; } else { return STATUS_SUCCESS; } } pcb = *ppcb; if ( (pcb->DeviceObject == NULL) || (pcb->ConnectionObject == NULL)) { UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_SCLOSED ); return STATUS_SUCCESS; } TdiBuildAction (Irp, pcb->DeviceObject, pcb->ConnectionObject, NbCompletionPDNCB, pdncb, Irp->MdlAddress); DeviceObject = pcb->DeviceObject; UNLOCK( pfcb, OldIrql ); IoMarkIrpPending( Irp ); IoCallDriver (DeviceObject, Irp); IF_NBDBG (NB_DEBUG_ACTION) { NbPrint(( "NB ACTION submit connection: %X\n", Irp->IoStatus.Status )); } // // Transport will complete the request. Return pending so that // netbios does not complete as well. // return STATUS_PENDING; } else if ( pdncb->ncb_num != 0) { // Use handle associated with this name PPAB ppab; PAB pab; ppab = FindAbUsingNum( pfcb, pdncb, pdncb->ncb_num ); if ( ppab == NULL ) { UNLOCK( pfcb, OldIrql ); return STATUS_SUCCESS; } pab = *ppab; TdiBuildAction (Irp, pab->DeviceObject, pab->AddressObject, NbCompletionPDNCB, pdncb, Irp->MdlAddress); DeviceObject = pab->DeviceObject; UNLOCK( pfcb, OldIrql ); IoMarkIrpPending( Irp ); IoCallDriver (DeviceObject, Irp); IF_NBDBG (NB_DEBUG_ACTION) { NbPrint(( "NB ACTION submit address: %X\n", Irp->IoStatus.Status )); } // // Transport will complete the request. Return pending so that // netbios does not complete as well. // return STATUS_PENDING; } else { // Use the control channel PLANA_INFO plana; if (( pdncb->ncb_lana_num > pfcb->MaxLana ) || ( pfcb == NULL ) || ( pfcb->ppLana[pdncb->ncb_lana_num] == NULL) || ( pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED) ) { UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } plana = pfcb->ppLana[pdncb->ncb_lana_num]; TdiBuildAction (Irp, plana->ControlDeviceObject, plana->ControlFileObject, NbCompletionPDNCB, pdncb, Irp->MdlAddress); DeviceObject = plana->ControlDeviceObject; UNLOCK( pfcb, OldIrql ); IoMarkIrpPending( Irp ); IoCallDriver (DeviceObject, Irp); IF_NBDBG (NB_DEBUG_ACTION) { NbPrint(( "NB ACTION submit control: %X\n", Irp->IoStatus.Status )); } // // Transport will complete the request. Return pending so that // netbios does not complete as well. // return STATUS_PENDING; UNLOCK( pfcb, OldIrql ); return STATUS_SUCCESS; } } NTSTATUS NbCancel( IN PDNCB pdncb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine is called to cancel the ncb pointed to by NCB_BUFFER. Arguments: pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. IrpSp - Pointer to current IRP stack frame. Return Value: The function value is the status of the operation. --*/ { PFCB pfcb = IrpSp->FileObject->FsContext2; PDNCB target; // Mapped in location of the USERS NCB. Not the drivers copy of the DNCB! BOOL SpinLockHeld; KIRQL OldIrql; // Used when SpinLock held. IF_NBDBG (NB_DEBUG_CALL) { NbPrint(( "\n****** Start of NbCancel ****** pdncb %lx\n", pdncb )); } if ( pdncb->ncb_lana_num > pfcb->MaxLana) { NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } LOCK( pfcb, OldIrql ); SpinLockHeld = TRUE; if (( pfcb->ppLana[pdncb->ncb_lana_num] == NULL ) || ( pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED) ) { UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_BRIDGE ); return STATUS_SUCCESS; } // // Map the users buffer so we can poke around inside // if (Irp->MdlAddress) { target = MmGetSystemAddressForMdl (Irp->MdlAddress); } else { ASSERT(FALSE); UNLOCK( pfcb, OldIrql ); NCB_COMPLETE( pdncb, NRC_CANOCCR ); return STATUS_SUCCESS; } IF_NBDBG (NB_DEBUG_CALL) { NbDisplayNcb( target ); } try { if ( target->ncb_lana_num == pdncb->ncb_lana_num ) { switch ( target->ncb_command & ~ASYNCH ) { case NCBCALL: case NCALLNIU: case NCBLISTEN: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { // // Search for the correct ppcb. We cannot use FindCb // because the I/O system will not copy back the ncb_lsn // field into target until the I/O request completes. // PPCB ppcb = FindCallCb( pfcb, (PNCB)pdncb->ncb_buffer); if (( ppcb == NULL ) || ((*ppcb)->pdncbCall->ncb_cmd_cplt != NRC_PENDING ) || (( (*ppcb)->Status != CALL_PENDING ) && ( (*ppcb)->Status != LISTEN_OUTSTANDING ))) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { NCB_COMPLETE( (*ppcb)->pdncbCall, NRC_CMDCAN ); SpinLockHeld = FALSE; (*ppcb)->DisconnectReported = TRUE; UNLOCK_SPINLOCK( pfcb, OldIrql ); CleanupCb( ppcb, NULL ); NCB_COMPLETE( pdncb, NRC_GOODRET ); } } break; case NCBHANGUP: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { PPCB ppcb = FindCb( pfcb, target, FALSE ); if (( ppcb != NULL ) && ((*ppcb)->Status == HANGUP_PENDING )) { PDNCB pdncbHangup; // Restore the session status and remove the hangup. (*ppcb)->Status = SESSION_ESTABLISHED; pdncbHangup = (*ppcb)->pdncbHangup; (*ppcb)->pdncbHangup = NULL; if ( pdncbHangup != NULL ) { NCB_COMPLETE( pdncbHangup, NRC_CMDCAN ); pdncbHangup->irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); NbCompleteRequest( pdncbHangup->irp ,STATUS_SUCCESS); } NCB_COMPLETE( pdncb, NRC_GOODRET ); } else { // Doesn't look like this is a real hangup so refuse. NCB_COMPLETE( pdncb, NRC_CANCEL ); } } break; case NCBASTAT: NCB_COMPLETE( pdncb, NRC_CANOCCR ); break; case NCBLANSTALERT: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { CancelLanAlert( pfcb, pdncb ); } break; case NCBRECVANY: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { PPAB ppab; PLIST_ENTRY Entry; ppab = FindAbUsingNum( pfcb, target, target->ncb_num ); if ( ppab == NULL ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); break; } for (Entry = (*ppab)->ReceiveAnyList.Flink ; Entry != &(*ppab)->ReceiveAnyList; Entry = Entry->Flink) { PDNCB pReceive = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( pReceive->users_ncb == (PNCB)pdncb->ncb_buffer ) { PIRP Irp; RemoveEntryList( &pReceive->ncb_next ); SpinLockHeld = FALSE; UNLOCK_SPINLOCK( pfcb, OldIrql ); Irp = pReceive->irp; IoAcquireCancelSpinLock(&Irp->CancelIrql); // // Remove the cancel request for this IRP. If its cancelled then its // ok to just process it because we will be returning it to the caller. // Irp->Cancel = FALSE; IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(Irp->CancelIrql); NCB_COMPLETE( pReceive, NRC_CMDCAN ); Irp->IoStatus.Status = STATUS_SUCCESS, Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); NbCompleteRequest( Irp, STATUS_SUCCESS ); // The receive is cancelled, complete the cancel NCB_COMPLETE( pdncb, NRC_GOODRET ); break; } } // Command not in receive list! NCB_COMPLETE( pdncb, NRC_CANOCCR ); } break; case NCBDGRECV: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { PPAB ppab; PLIST_ENTRY Entry; ppab = FindAbUsingNum( pfcb, target, target->ncb_num ); if ( ppab == NULL ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); break; } for (Entry = (*ppab)->ReceiveDatagramList.Flink ; Entry != &(*ppab)->ReceiveDatagramList; Entry = Entry->Flink) { PDNCB pReceive = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( pReceive->users_ncb == (PNCB)pdncb->ncb_buffer ) { PIRP Irp; RemoveEntryList( &pReceive->ncb_next ); SpinLockHeld = FALSE; UNLOCK_SPINLOCK( pfcb, OldIrql ); Irp = pReceive->irp; IoAcquireCancelSpinLock(&Irp->CancelIrql); // // Remove the cancel request for this IRP. If its cancelled then its // ok to just process it because we will be returning it to the caller. // Irp->Cancel = FALSE; IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(Irp->CancelIrql); NCB_COMPLETE( pReceive, NRC_CMDCAN ); Irp->IoStatus.Status = STATUS_SUCCESS, Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); NbCompleteRequest( Irp, STATUS_SUCCESS ); // The receive is cancelled, complete the cancel NCB_COMPLETE( pdncb, NRC_GOODRET ); break; } } // Command not in receive list! NCB_COMPLETE( pdncb, NRC_CANOCCR ); } break; case NCBDGRECVBC: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { PPAB ppab; PLIST_ENTRY Entry; ppab = FindAbUsingNum( pfcb, target, MAXIMUM_ADDRESS ); if ( ppab == NULL ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); break; } for (Entry = (*ppab)->ReceiveBroadcastDatagramList.Flink ; Entry != &(*ppab)->ReceiveBroadcastDatagramList; Entry = Entry->Flink) { PDNCB pReceive = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( pReceive->users_ncb == (PNCB)pdncb->ncb_buffer ) { PIRP Irp; RemoveEntryList( &pReceive->ncb_next ); SpinLockHeld = FALSE; UNLOCK_SPINLOCK( pfcb, OldIrql ); Irp = pReceive->irp; IoAcquireCancelSpinLock(&Irp->CancelIrql); // // Remove the cancel request for this IRP. If its cancelled then its // ok to just process it because we will be returning it to the caller. // Irp->Cancel = FALSE; IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(Irp->CancelIrql); NCB_COMPLETE( pReceive, NRC_CMDCAN ); Irp->IoStatus.Status = STATUS_SUCCESS, Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); NbCompleteRequest( Irp, STATUS_SUCCESS ); // The receive is cancelled, complete the cancel NCB_COMPLETE( pdncb, NRC_GOODRET ); break; } } // Command not in receive list! NCB_COMPLETE( pdncb, NRC_CANOCCR ); } break; // Session cancels close the connection. case NCBRECV: case NCBSEND: case NCBSENDNA: case NCBCHAINSEND: case NCBCHAINSENDNA: if ( target->ncb_cmd_cplt != NRC_PENDING ) { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { PPCB ppcb; ppcb = FindCb( pfcb, target, FALSE); if ( ppcb == NULL ) { // No such connection NCB_COMPLETE( pdncb, NRC_CANOCCR ); } else { PDNCB pTarget = NULL; PLIST_ENTRY Entry; if ((target->ncb_command & ~ASYNCH) == NCBRECV ) { for (Entry = (*ppcb)->ReceiveList.Flink ; Entry != &(*ppcb)->ReceiveList; Entry = Entry->Flink) { pTarget = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( pTarget->users_ncb == (PNCB)pdncb->ncb_buffer ) { break; } pTarget = NULL; } } else { for (Entry = (*ppcb)->SendList.Flink ; Entry != &(*ppcb)->SendList; Entry = Entry->Flink) { pTarget = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( pTarget->users_ncb == (PNCB)pdncb->ncb_buffer ) { break; } pTarget = NULL; } } if ( pTarget != NULL ) { // pTarget points to the real Netbios drivers DNCB. NCB_COMPLETE( pTarget, NRC_CMDCAN ); SpinLockHeld = FALSE; (*ppcb)->DisconnectReported = TRUE; UNLOCK_SPINLOCK( pfcb, OldIrql ); CleanupCb( ppcb, NULL ); NCB_COMPLETE( pdncb, NRC_GOODRET ); } else { NCB_COMPLETE( pdncb, NRC_CANOCCR ); } } } break; default: NCB_COMPLETE( pdncb, NRC_CANCEL ); // Invalid command to cancel break; } } else { NCB_COMPLETE( pdncb, NRC_BRIDGE ); } } except(EXCEPTION_EXECUTE_HANDLER) { if ( SpinLockHeld == TRUE ) { UNLOCK( pfcb, OldIrql ); } else { UNLOCK_RESOURCE( pfcb ); } NCB_COMPLETE( pdncb, NRC_INVADDRESS ); return STATUS_SUCCESS; } if ( SpinLockHeld == TRUE ) { UNLOCK( pfcb, OldIrql ); } else { UNLOCK_RESOURCE( pfcb ); } NCB_COMPLETE( pdncb, NRC_GOODRET ); return STATUS_SUCCESS; UNREFERENCED_PARAMETER( Irp ); } VOID QueueRequest( IN PLIST_ENTRY List, IN PDNCB pdncb, IN PIRP Irp, IN PFCB pfcb, IN KIRQL OldIrql, IN BOOLEAN Head) /*++ Routine Description: This routine is called to add a dncb to List. Note: QueueRequest UNLOCKS the fcb. This means the resource and spinlock are owned when this routine is called. Arguments: List - List of pdncb's. pdncb - Pointer to the NCB. Irp - Pointer to the request packet representing the I/O request. pfcb & OldIrql - Used to free locks Head - TRUE if pdncb should be inserted at head of list Return Value: None. --*/ { pdncb->irp = Irp; pdncb->pfcb = pfcb; IoMarkIrpPending( Irp ); IoAcquireCancelSpinLock(&Irp->CancelIrql); if ( Head == FALSE ) { InsertTailList(List, &pdncb->ncb_next); } else { InsertHeadList(List, &pdncb->ncb_next); } if (Irp->Cancel) { // // CancelRoutine will lock the resource & spinlock and try to find the // request from scratch. It may fail to find the request if it has // been picked up by an indication from the transport. // UNLOCK( pfcb, OldIrql ); CancelRoutine (NULL, Irp); } else { IoSetCancelRoutine(Irp, CancelRoutine); IoReleaseCancelSpinLock (Irp->CancelIrql); UNLOCK( pfcb, OldIrql ); } } PDNCB DequeueRequest( IN PLIST_ENTRY List ) /*++ Routine Description: This routine is called to remove a dncb from List. Assume fcb spinlock held. Arguments: List - List of pdncb's. Return Value: PDNCB or NULL. --*/ { PIRP Irp; PDNCB pdncb; PLIST_ENTRY ReceiveEntry; if (IsListEmpty(List)) { // // There are no waiting request announcement FsControls, so // return success. // return NULL; } ReceiveEntry = RemoveHeadList( List); pdncb = CONTAINING_RECORD( ReceiveEntry, DNCB, ncb_next); Irp = pdncb->irp; IoAcquireCancelSpinLock(&Irp->CancelIrql); // // Remove the cancel request for this IRP. If its cancelled then its // ok to just process it because we will be returning it to the caller. // Irp->Cancel = FALSE; IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(Irp->CancelIrql); return pdncb; } VOID CancelRoutine( IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PIRP Irp ) /*++ Routine Description: This routine is called when the IO system wants to cancel a queued request. The netbios driver queues LanAlerts, Receives and Receive Datagrams Arguments: IN PDEVICE_OBJECT DeviceObject - Ignored. IN PIRP Irp - Irp to cancel. Return Value: None --*/ { PFCB pfcb; PDNCB pdncb; DNCB LocalCopy; PLIST_ENTRY List = NULL; PPAB ppab; PPCB ppcb; PFILE_OBJECT FileObject; KIRQL OldIrql; // // Clear the cancel routine from the IRP - It can't be cancelled anymore. // IoSetCancelRoutine(Irp, NULL); // // Remove all the info from the pdncb that we will need to find the // request. Once we release the cancel spinlock this request could be // completed by another action so it is possible that we will not find // the request to cancel. // pdncb = Irp->AssociatedIrp.SystemBuffer; RtlMoveMemory( &LocalCopy, pdncb, sizeof( DNCB ) ); IF_NBDBG (NB_DEBUG_IOCANCEL) { NbPrint(( "IoCancel Irp %lx\n", Irp )); NbDisplayNcb(&LocalCopy); } #if DBG pdncb = (PDNCB)0xDEADBEEF; #endif pfcb = LocalCopy.pfcb; // // Reference the FileObject associated with this Irp. This will stop // the callers handle to \device\netbios from closing and therefore // the fcb will not get deleted while we try to lock the fcb. // FileObject = (IoGetCurrentIrpStackLocation (Irp))->FileObject; ObReferenceObject(FileObject); IoReleaseCancelSpinLock( Irp->CancelIrql ); LOCK( pfcb, OldIrql ); // // We now have exclusive access to all CB's and AB's with their associated // lists. // switch ( LocalCopy.ncb_command & ~ASYNCH ) { case NCBRECV: ppcb = FindCb( pfcb, &LocalCopy, TRUE); if ( ppcb != NULL ) { List = &(*ppcb)->ReceiveList; } break; case NCBRECVANY: ppab = FindAbUsingNum( pfcb, &LocalCopy, LocalCopy.ncb_num ); if ( ppab != NULL ) { List = &(*ppab)->ReceiveAnyList; } break; case NCBDGRECVBC: ppab = FindAbUsingNum( pfcb, &LocalCopy, MAXIMUM_ADDRESS ); if ( ppab != NULL ) { List = &(*ppab)->ReceiveBroadcastDatagramList; } break; case NCBDGRECV: ppab = FindAbUsingNum( pfcb, &LocalCopy, LocalCopy.ncb_num ); if ( ppab != NULL ) { List = &(*ppab)->ReceiveDatagramList; } break; case NCBLANSTALERT: List = &(pfcb->ppLana[LocalCopy.ncb_lana_num]->LanAlertList); break; } if ( List != NULL ) { // We have a list to scan for canceled pdncb's PLIST_ENTRY Entry; RestartScan: for (Entry = List->Flink ; Entry != List ; Entry = Entry->Flink) { PDNCB p = CONTAINING_RECORD( Entry, DNCB, ncb_next); if ( p->irp->Cancel ) { RemoveEntryList( &p->ncb_next ); NCB_COMPLETE( p, NRC_CMDCAN ); p->irp->IoStatus.Status = STATUS_SUCCESS; p->irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); IoCompleteRequest( p->irp, IO_NETWORK_INCREMENT); goto RestartScan; } } } UNLOCK( pfcb, OldIrql ); ObDereferenceObject(FileObject); }