/*++ 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 "precomp.h" // procedure headings #ifdef RASAUTODIAL #ifndef VXD #include #include #endif //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #pragma CTEMakePageable(INIT, NbtAcdBind) #pragma CTEMakePageable(PAGE, NbtAcdUnbind) #endif //******************* Pageable Routine Declarations **************** // // 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 PIRP pIrp ); NTSTATUS NbtpConnectCompletionRoutine( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID pCompletionContext ); 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]; PIRP pIrp = pArgs[3]; TDI_REQUEST request; KIRQL irql; CTELockHandle OldIrq; tDEVICECONTEXT *pDeviceContext = pConnEle->pDeviceContext; IF_DBG(NBT_DEBUG_NAME) KdPrint(("Nbt.NbtRetryPreConnect: fSuccess=%d, pIrp=0x%x, pIrp->Cancel=%d, pConnEle=0x%x\n", fSuccess, pIrp, pIrp->Cancel, pConnEle)); request.Handle.ConnectionContext = pConnEle; status = NbtCancelCancelRoutine (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, 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); } NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_AUTODIAL, FALSE); } } // 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 *pAddr; BOOLEAN fCancelled; CTELockHandle OldIrq; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pConnEle = (tCONNECTELE *) pIrpSp->FileObject->FsContext; if ((!pConnEle) || (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN)) || (!(pAddr = (ACD_ADDR *) NbtAllocMem(sizeof(ACD_ADDR),NBT_TAG('A'))))) { IoReleaseCancelSpinLock(pIrp->CancelIrql); ASSERTMSG ("Nbt.NbtCancelPreConnect: ERROR - Invalid Connection Handle\n", 0); return FALSE; } IF_DBG(NBT_DEBUG_NAME) KdPrint(("NbtCancelPreConnect: pIrp=0x%x, pConnEle=0x%x\n", pIrp, pConnEle)); // // Get the address of the connection. // pAddr->fType = ACD_ADDR_NB; RtlCopyMemory(&pAddr->cNetbios, pConnEle->RemoteName, 16); // // Cancel the autodial request. // fCancelled = (*AcdDriverG.lpfnCancelConnection) (ulDriverIdG, pAddr, NbtCancelAutoDialRequest, pIrp); if (fCancelled) { IoSetCancelRoutine(pIrp, NULL); } IoReleaseCancelSpinLock(pIrp->CancelIrql); CTEMemFree(pAddr); // // If the request could not be found // in the driver, then it has already // been completed, so we simply return. // if (!fCancelled) { return FALSE; } KeRaiseIrql(DISPATCH_LEVEL, &irql); pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; NBT_DEREFERENCE_DEVICE (pConnEle->pDeviceContext, REF_DEV_AUTODIAL, FALSE); // // 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. // // 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. // CTESpinLock(pConnEle,OldIrq); pConnEle->pIrp = NULL; CTESpinFree(pConnEle,OldIrq); IoCompleteRequest(pIrp, IO_NO_INCREMENT); KeLowerIrql(irql); return TRUE; } // NbtCancelPreConnect BOOLEAN NbtCancelPostConnect( IN PIRP pIrp ) { PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); tCONNECTELE *pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext; ACD_ADDR *pAddr; BOOLEAN fCancelled; if ((!pConnEle) || (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN)) || (!(pAddr = (ACD_ADDR *) NbtAllocMem(sizeof(ACD_ADDR),NBT_TAG('A'))))) { ASSERTMSG ("Nbt.NbtCancelPostConnect: ERROR - Invalid Connection Handle\n", 0); return FALSE; } IF_DBG(NBT_DEBUG_NAME) KdPrint(("Nbt.NbtCancelPostConnect: pIrp=0x%x, pConnEle=0x%x\n", pIrp, pConnEle)); // // Get the address of the connection. // pAddr->fType = ACD_ADDR_NB; RtlCopyMemory(&pAddr->cNetbios, pConnEle->RemoteName, 15); // // Cancel the autodial request. // fCancelled = (*AcdDriverG.lpfnCancelConnection) (ulDriverIdG, pAddr, NbtCancelAutoDialRequest, pIrp); if (fCancelled) { NBT_DEREFERENCE_DEVICE (pConnEle->pDeviceContext, REF_DEV_AUTODIAL, FALSE); } CTEMemFree(pAddr); return (fCancelled); } // NbtCancelPostConnect BOOLEAN NbtAttemptAutoDial( IN tCONNECTELE *pConnEle, IN PVOID pTimeout, IN PTDI_CONNECTION_INFORMATION pCallInfo, 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 *pAddr = NULL; KIRQL irql; PVOID pArgs[4]; PCHAR pName; ULONG ulcbName; LONG lNameType; TDI_ADDRESS_NETBT_INTERNAL TdiAddr; PIO_STACK_LOCATION pIrpSp; ASSERT(pCallInfo); // // If this connection has already been through the // automatic connection process, don't do it again. // if ((pConnEle->fAutoConnected)) { return FALSE; } pIrpSp = IoGetCurrentIrpStackLocation(pIrp); if (pIrpSp->CompletionRoutine != NbtpConnectCompletionRoutine) { status = GetNetBiosNameFromTransportAddress((PTRANSPORT_ADDRESS) pCallInfo->RemoteAddress, pCallInfo->RemoteAddressLength, &TdiAddr); } else { ASSERT(((PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress)->Address[0].AddressType == TDI_ADDRESS_TYPE_UNSPEC); CTEMemCopy(&TdiAddr, (PTDI_ADDRESS_NETBT_INTERNAL)((PTRANSPORT_ADDRESS)pCallInfo->RemoteAddress)->Address[0].Address, sizeof(TdiAddr)); status = STATUS_SUCCESS; } if (status != STATUS_SUCCESS || (!NBT_REFERENCE_DEVICE (pConnEle->pDeviceContext, REF_DEV_AUTODIAL, FALSE)) || (!(pAddr = (ACD_ADDR *) NbtAllocMem(sizeof(ACD_ADDR),NBT_TAG('A'))))) { if (pAddr) { CTEMemFree(pAddr); } return FALSE; } pName = TdiAddr.OEMRemoteName.Buffer; ulcbName = TdiAddr.OEMRemoteName.Length; lNameType = TdiAddr.NameType; // // Save the address for pre-connect attempts, // because if we have to cancel this irp, // it is not saved anywhere else. // CTESpinLock(pConnEle, irql); pConnEle->fAutoConnecting = TRUE; CTEMemCopy(pConnEle->RemoteName, pName, NETBIOS_NAME_SIZE); CTESpinFree(pConnEle, irql); pAddr->fType = ACD_ADDR_NB; RtlCopyMemory(&pAddr->cNetbios, pName, NETBIOS_NAME_SIZE); IF_DBG(NBT_DEBUG_NAME) KdPrint(("Nbt.NbtAttemptAutodial: szAddr=%-15.15s\n", pName)); // // Enqueue this request on the network // connection pending queue. // pArgs[0] = pConnEle; pArgs[1] = pTimeout; pArgs[2] = pCallInfo; pArgs[3] = pIrp; fSuccess = (*AcdDriverG.lpfnStartConnection) (ulDriverIdG, pAddr, ulFlags, pProc, 4, pArgs); // // If fSuccess is TRUE, then it means that the NetBT proc has // already been called to setup the connection, and hence the // data in pConnEle may not be valid now // // In the case it is FALSE, then pProc has not been called, and // we should set the fAutoConnecting flag to FALSE also // if (!fSuccess) { NBT_DEREFERENCE_DEVICE (pConnEle->pDeviceContext, REF_DEV_AUTODIAL, FALSE); CTESpinLock(pConnEle, irql); pConnEle->fAutoConnecting = FALSE; CTESpinFree(pConnEle, irql); } CTEMemFree(pAddr); return fSuccess; } // NbtAttemptAutoDial VOID NbtNoteNewConnection( IN tNAMEADDR *pNameAddr, IN ULONG IpAddress ) /*++ Routine Description: Inform the automatic connection driver of a successful new connection. Arguments: pNameAddr - a pointer to the remote name IpAddress - Source IP address of the connection Return Value: None. --*/ { ACD_ADDR *pAddr = NULL; ACD_ADAPTER *pAdapter = NULL; // // Notify the AcdDriver only if we have a valid Source address // // We can end up blowing the stack if we pre-allocate ACD_ADDR // and ACD_ADAPTER on the stack -- so allocate them dynamically! // if ((IpAddress) && (pAddr = (ACD_ADDR *) NbtAllocMem(sizeof(ACD_ADDR),NBT_TAG('A'))) && (pAdapter = (ACD_ADAPTER *) NbtAllocMem(sizeof(ACD_ADAPTER),NBT_TAG('A')))) { pAddr->fType = ACD_ADDR_NB; RtlCopyMemory(&pAddr->cNetbios, pNameAddr->Name, 15); pAdapter->fType = ACD_ADAPTER_IP; pAdapter->ulIpaddr = htonl(IpAddress); // Get the source IP address of the connection. (*AcdDriverG.lpfnNewConnection) (pAddr, pAdapter); } if (pAddr) { CTEMemFree(pAddr); } if (pAdapter) { CTEMemFree(pAdapter); } } // 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; } fAcdLoadedG = FALSE; // // 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