diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/acd/api.c | 1556 |
1 files changed, 1556 insertions, 0 deletions
diff --git a/private/ntos/tdi/acd/api.c b/private/ntos/tdi/acd/api.c new file mode 100644 index 000000000..4ad13b54f --- /dev/null +++ b/private/ntos/tdi/acd/api.c @@ -0,0 +1,1556 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + api.c + +Abstract: + + Exported routines to transports for automatic connection + management. + +Author: + + Anthony Discolo (adiscolo) 17-Apr-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#include <ntifs.h> +#include <cxport.h> +#include <tdi.h> +#include <tdikrnl.h> +#include <tdistat.h> +#include <tdiinfo.h> +#include <acd.h> + +#include "acdapi.h" +#include "acddefs.h" +#include "request.h" +#include "mem.h" +#include "debug.h" + +// +// Driver enabled mode. The automatic +// connection system service sets +// this depending on whether a user +// has logged in, and whether there's +// general network connectivity. +// +BOOLEAN fAcdEnabledG; + +// +// Spin lock for this module. +// +KSPIN_LOCK AcdSpinLockG; + +// +// Event signaled when the AcdNotificationRequestThread +// thread has a notification to process. +// +KEVENT AcdRequestThreadEventG; + +// +// This is a list of one irp representing +// a user-space process waiting to create a +// new network connection given an address. +// +LIST_ENTRY AcdNotificationQueueG; + +// +// This is a list of ACD_CONNECTION blocks representing +// requests from transports about unsuccessful connection +// attempts. There may be multiple ACD_COMPLETION block +// linked onto the same ACD_CONNECTION, grouped by +// address. +// +LIST_ENTRY AcdConnectionQueueG; + +// +// This is a list of ACD_COMPLETION blocks representing +// other requests from transports. +// +LIST_ENTRY AcdCompletionQueueG; + +// +// The list of drivers that have binded +// with us. +// +LIST_ENTRY AcdDriverListG; + +// +// Statistics +// +typedef struct _ACD_STATS { + ULONG ulConnects; // connection attempts + ULONG ulCancels; // connection cancels +} ACD_STATS; +ACD_STATS AcdStatsG[ACD_ADDR_MAX]; + +// +// Forward declarations +// +VOID +AcdPrintAddress( + IN PACD_ADDR pAddr + ); + +VOID +ClearRequests( + IN KIRQL irql + ); + +// +// External variables +// +extern ULONG ulAcdOpenCountG; + + + +VOID +SetDriverMode( + IN BOOLEAN fEnable + ) + +/*++ + +DESCRIPTION + Set the global driver mode value, and inform + all bound transports of the change. + + Note: this call assumes AcdSpinLockG is already + acquired. + +ARGUMENTS + fEnable: the new driver mode value + +RETURN VALUE + None. + +--*/ + +{ + KIRQL dirql; + PLIST_ENTRY pEntry; + PACD_DRIVER pDriver; + + // + // Set the new global driver mode value. + // + fAcdEnabledG = fEnable; + // + // Inform all the drivers that have binded + // with us of the new enable mode. + // + for (pEntry = AcdDriverListG.Flink; + pEntry != &AcdDriverListG; + pEntry = pEntry->Flink) + { + pDriver = CONTAINING_RECORD(pEntry, ACD_DRIVER, ListEntry); + + KeAcquireSpinLock(&pDriver->SpinLock, &dirql); + pDriver->fEnabled = fEnable; + KeReleaseSpinLock(&pDriver->SpinLock, dirql); + } +} // SetDriverMode + + + +NTSTATUS +AcdEnable( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Set the enable mode for the driver. This determines + which notifications it will pass up to the automatic + connection system service. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold + an ACD_ENABLE_MODE value. + + STATUS_SUCCESS: if the enabled bit was set successfully. + +--*/ + +{ + KIRQL irql; + BOOLEAN fEnable; + + // + // Verify the input buffer is sufficient to hold + // a BOOLEAN structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (BOOLEAN)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + KeAcquireSpinLock(&AcdSpinLockG, &irql); + fEnable = *(BOOLEAN *)pIrp->AssociatedIrp.SystemBuffer; + SetDriverMode(fEnable); + // + // Clear all pending requests if + // we are disabling the driver. + // + if (!fEnable) + ClearRequests(irql); + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return STATUS_SUCCESS; +} // AcdEnable + + + +VOID +CancelNotification( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) + +/*++ + +DESCRIPTION + Generic cancel routine for irps on the AcdNotificationQueueG. + +ARGUMENTS + pDeviceObject: unused + + pIrp: pointer to the irp to be cancelled. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql; + + UNREFERENCED_PARAMETER(pDeviceObject); + // + // Mark this irp as cancelled. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + // + // Release the spin lock the I/O system acquired. + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + // + // Remove it from our list. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // Complete the request. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // CancelNotification + + + +VOID +AcdCancelNotifications() + +/*++ + +DESCRIPTION + Cancel all irps on the AcdNotification queue. Although + technically more than one user address space can be waiting + for these notifications, we allow only one at this time. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql; + PLIST_ENTRY pHead; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + + // + // Complete all the irps in the list. + // + while ((pHead = ExInterlockedRemoveHeadList( + &AcdNotificationQueueG, + &AcdSpinLockG)) != NULL) + { + pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry); + // + // Mark this irp as cancelled. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + // + // Complete the irp. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } +} // AcdCancelNotifications + + + +NTSTATUS +AcdWaitForNotification( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Enqueue an connection notification irp. This is done + done by the automatic connection system service. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold + an ACD_NOTIFICATION structure. + + STATUS_PENDING: if the ioctl was successfully enqueued + + STATUS_SUCCESS: if there is a notification already available + +--*/ + +{ + KIRQL irql, irql2; + PLIST_ENTRY pHead; + PACD_COMPLETION pCompletion; + PACD_NOTIFICATION pNotification; + PEPROCESS pProcess; + + // + // Verify the output buffer is sufficient to hold + // an ACD_NOTIFICATION structure. + // + if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof (ACD_NOTIFICATION)) + { + return STATUS_BUFFER_TOO_SMALL; + } + IoAcquireCancelSpinLock(&irql); + KeAcquireSpinLock(&AcdSpinLockG, &irql2); + // + // There is no notification available. + // Mark the irp as pending and wait for one. + // + pIrp->IoStatus.Status = STATUS_PENDING; + IoMarkIrpPending(pIrp); + // + // Set the irp's cancel routine. + // + IoSetCancelRoutine(pIrp, CancelNotification); + // + // Append the irp at the end of the + // connection notification list. + // + InsertTailList(&AcdNotificationQueueG, &pIrp->Tail.Overlay.ListEntry); + // + // Signal the request thread there is + // work to do. + // + KeSetEvent(&AcdRequestThreadEventG, 0, FALSE); + + KeReleaseSpinLock(&AcdSpinLockG, irql2); + IoReleaseCancelSpinLock(irql); + + return STATUS_PENDING; +} // AcdWaitForNotification + + + +BOOLEAN +EqualAddress( + IN PACD_ADDR p1, + IN PACD_ADDR p2 + ) +{ + ULONG i; + + if (p1->fType != p2->fType) + return FALSE; + + switch (p1->fType) { + case ACD_ADDR_IP: + return (p1->ulIpaddr == p2->ulIpaddr); + case ACD_ADDR_IPX: + return (BOOLEAN)RtlEqualMemory( + &p1->cNode, + &p2->cNode, + ACD_ADDR_IPX_LEN); + case ACD_ADDR_NB: + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "EqualAddress: NB: (%15s,%15s) result=%d\n", + p1->cNetbios, + p2->cNetbios, + RtlEqualMemory(&p1->cNetbios, &p2->cNetbios, ACD_ADDR_NB_LEN - 1))); + } + return (BOOLEAN)RtlEqualMemory( + &p1->cNetbios, + &p2->cNetbios, + ACD_ADDR_NB_LEN - 1); + case ACD_ADDR_INET: + for (i = 0; i < ACD_ADDR_INET_LEN; i++) { + if (p1->szInet[i] != p2->szInet[i]) + return FALSE; + if (p1->szInet[i] == '\0' || p2->szInet[i] == '\0') + break; + } + return TRUE; + default: + ASSERT(FALSE); + break; + } + + return FALSE; +} // EqualAddress + + + +PACD_CONNECTION +FindConnection( + IN PACD_ADDR pAddr + ) + +/*++ + +DESCRIPTION + Search for a connection block with the specified + address. + +ARGUMENTS + pAddr: a pointer to the target ACD_ADDR + +RETURN VALUE + A PACD_CONNECTION with the specified address, if found; + otherwise NULL. + +--*/ + +{ + PLIST_ENTRY pEntry; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + pCompletion = CONTAINING_RECORD(pConnection->CompletionList.Flink, ACD_COMPLETION, ListEntry); + + if (EqualAddress(pAddr, &pCompletion->notif.addr)) + return pConnection; + } + + return NULL; +} // FindConnection + + + +NTSTATUS +AcdConnectionInProgress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Refresh the progress indicator for the connection + attempt. If the progress indicator is not updated + by the user + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_INVALID_CONNECTION: if there is no connection + attempt in progress. + + STATUS_SUCCESS + +--*/ + +{ + KIRQL irql; + PACD_STATUS pStatus; + PACD_CONNECTION pConnection; + + // + // Verify the input buffer is sufficient to hold + // a BOOLEAN structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (ACD_STATUS)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Get the success code from the + // connection attempt and pass it + // to the completion routine. + // + pStatus = (PACD_STATUS)pIrp->AssociatedIrp.SystemBuffer; + KeAcquireSpinLock(&AcdSpinLockG, &irql); + pConnection = FindConnection(&pStatus->addr); + if (pConnection != NULL) + pConnection->fProgressPing = TRUE; + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return (pConnection != NULL) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} // AcdConnectionInProgress + + + +BOOLEAN +AddCompletionToConnection( + IN PACD_COMPLETION pCompletion + ) +{ + PACD_CONNECTION pConnection; + + pConnection = FindConnection(&pCompletion->notif.addr); + // + // If the connection already exists, then add + // the completion request to its list. + // + if (pConnection != NULL) { + InsertTailList(&pConnection->CompletionList, &pCompletion->ListEntry); + return TRUE; + } + // + // This is a connection to a new address. + // Create the connection block, enqueue it, + // and start the connection timer. + // + ALLOCATE_MEMORY(ACD_OBJECT_CONNECTION, pConnection); + if (pConnection == NULL) { + DbgPrint("AddCompletionToConnection: ExAllocatePool failed\n"); + return FALSE; + } + pConnection->fNotif = FALSE; + pConnection->fProgressPing = FALSE; + pConnection->fCompleting = FALSE; + pConnection->ulTimerCalls = 0; + pConnection->ulMissedPings = 0; + InitializeListHead(&pConnection->CompletionList); + InsertHeadList(&pConnection->CompletionList, &pCompletion->ListEntry); + InsertTailList(&AcdConnectionQueueG, &pConnection->ListEntry); + return TRUE; +} // AddCompletionToConnection + + + +BOOLEAN +AddCompletionBlock( + IN ULONG ulDriverId, + IN PACD_ADDR pAddr, + IN ULONG ulFlags, + IN PACD_ADAPTER pAdapter, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) + +/*++ + +DESCRIPTION + Create a block that represents an outstanding + transport request waiting for an automatic + connection. Link this block into the global + list of outstanding transport requests. + +ARGUMENTS + ulDriverId: a unique value for the transport driver + + pAddr: the network address of the connection + + ulFlags: connection flags + + pAdapter: pointer to adapter identifier + + pProc: a completion callback procedure + + nArgs: the number of parameters passed in pArgs + + pArgs: the parameters to pProc + +RETURN VALUE + TRUE if successful, FALSE otherwise + +--*/ + +{ + PACD_COMPLETION pCompletion; + ULONG i; + + ALLOCATE_MEMORY( + sizeof (ACD_COMPLETION) + ((nArgs - 1) * sizeof (PVOID)), + pCompletion); + if (pCompletion == NULL) { + DbgPrint("AcdAddCompletionBlock: ExAllocatePool failed\n"); + return FALSE; + } + // + // Copy the arguments into the information block. + // + pCompletion->ulDriverId = ulDriverId; + pCompletion->fCanceled = FALSE; + pCompletion->fCompleted = FALSE; + RtlCopyMemory(&pCompletion->notif.addr, pAddr, sizeof (ACD_ADDR)); + pCompletion->notif.ulFlags = ulFlags; + if (pAdapter != NULL) { + RtlCopyMemory( + &pCompletion->notif.adapter, + pAdapter, + sizeof (ACD_ADAPTER)); + } + else + RtlZeroMemory(&pCompletion->notif.adapter, sizeof (ACD_ADAPTER)); + pCompletion->pProc = pProc; + pCompletion->nArgs = nArgs; + for (i = 0; i < nArgs; i++) + pCompletion->pArgs[i] = pArgs[i]; + // + // If this is a unsuccessful connection request, + // then insert it onto the connection queue for + // that address; Otherwise, insert it into the list + // for all other requests. + // + if (ulFlags & ACD_NOTIFICATION_SUCCESS) { + InsertTailList(&AcdCompletionQueueG, &pCompletion->ListEntry); + } + else { + if (!AddCompletionToConnection(pCompletion)) { + FREE_MEMORY(pCompletion); + return FALSE; + } + } + // + // Inform the request thread + // there is new work to do. + // + KeSetEvent(&AcdRequestThreadEventG, 0, FALSE); + + return TRUE; +} // AddCompletionBlock + + + +VOID +AcdNewConnection( + IN PACD_ADDR pAddr, + IN PACD_ADAPTER pAdapter + ) +{ + KIRQL irql; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdNewConnection: ")); + AcdPrintAddress(pAddr); + AcdPrint(("\n")); + } + // + // If the driver is disabled, then fail + // all requests. + // + if (!fAcdEnabledG) { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdNewConnection: driver disabled\n")); + } + return; + } + // + // Acquire our spin lock. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Allocate a new completion block. + // + AddCompletionBlock( + 0, + pAddr, + ACD_NOTIFICATION_SUCCESS, + pAdapter, + NULL, + 0, + NULL); + // + // Release the spin lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); +} // AcdNewConnection + + + +BOOLEAN +AcdStartConnection( + IN ULONG ulDriverId, + IN PACD_ADDR pAddr, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) + +/*++ + +DESCRIPTION + Create a new connection completion block, and enqueue + it on the list of network requests to be completed when + a new network connection has been created. + +ARGUMENTS + ulDriverId: a unique value for the transport driver + + pAddr: the address of the connection attempt + + ulFlags: connection flags + + pProc: the transport callback to be called when a new + connection has been created. + + nArgs: the number of arguments to pProc. + + pArgs: a pointer to an array of pProc's parameters + +RETURN VALUE + TRUE if successful, FALSE otherwise. + +--*/ + +{ + BOOLEAN fSuccess = FALSE, fFound; + KIRQL irql; + ULONG ulAttributes = 0; + PACD_COMPLETION pCompletion; + PCHAR psz, pszOrg; + ACD_ADDR szOrgAddr; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdStartConnection: ")); + AcdPrintAddress(pAddr); + AcdPrint((", ulFlags=0x%x\n", ulFlags)); + } + // + // If the driver is disabled, then fail + // all requests. + // + if (!fAcdEnabledG) { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdStartConnection: driver disabled\n")); + } + return FALSE; + } + // + // Validate the address type. + // + if ((ULONG)pAddr->fType >= ACD_ADDR_MAX) { + AcdPrint(("AcdStartConnection: bad address type (%d)\n", pAddr->fType)); + return FALSE; + } + // + // Acquire our spin lock. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Update statistics. + // + AcdStatsG[pAddr->fType].ulConnects++; + // + // Allocate a new completion block. + // + fSuccess = AddCompletionBlock( + ulDriverId, + pAddr, + ulFlags, + NULL, + pProc, + nArgs, + pArgs); + // + // Release the spin lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return fSuccess; +} // AcdStartConnection + + + +BOOLEAN +AcdCancelConnection( + IN ULONG ulDriverId, + IN PACD_ADDR pAddr, + IN ACD_CANCEL_CALLBACK pProc, + IN PVOID pArg + ) + +/*++ + +DESCRIPTION + Remove a previously enqueued connection information + block from the list. + +ARGUMENTS + ulDriverId: a unique value for the transport driver + + pAddr: the address of the connection attempt + + pProc: the enumerator procecdure + + pArg: the enumerator procedure argument + +RETURN VALUE + None. + +--*/ + +{ + BOOLEAN fCanceled = FALSE; + KIRQL irql; + PLIST_ENTRY pEntry; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdCancelConnection: ulDriverId=0x%x, ")); + AcdPrintAddress(pAddr); + AcdPrint(("\n")); + } + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Enumerate the list looking for + // the information block with the + // supplied parameters. + // + pConnection = FindConnection(pAddr); + if (pConnection != NULL) { + for (pEntry = pConnection->CompletionList.Flink; + pEntry != &pConnection->CompletionList; + pEntry = pEntry->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + // + // If we have a match, remove it from + // the list and free the information block. + // + if (pCompletion->ulDriverId == ulDriverId && + !pCompletion->fCanceled && + !pCompletion->fCompleted) + { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "AcdCancelConnection: pCompletion=0x%x\n", + pCompletion)); + } + if ((*pProc)( + pArg, + pCompletion->notif.ulFlags, + pCompletion->pProc, + pCompletion->nArgs, + pCompletion->pArgs)) + { + pCompletion->fCanceled = TRUE; + fCanceled = TRUE; + // + // Update statistics. + // + AcdStatsG[pAddr->fType].ulCancels++; + break; + } + } + } + } + KeReleaseSpinLock(&AcdSpinLockG, irql); + + return fCanceled; +} // AcdCancelConnection + + + +VOID +ConnectAddressComplete( + BOOLEAN fSuccess, + PVOID *pArgs + ) +{ + PIRP pIrp = pArgs[0]; + PIO_STACK_LOCATION pIrpSp = pArgs[1]; + KIRQL irql; + + // + // Complete the request. + // + pIrp->IoStatus.Status = fSuccess ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; + pIrp->IoStatus.Information = 0; + IoAcquireCancelSpinLock(&irql); + IoSetCancelRoutine(pIrp, NULL); + IoReleaseCancelSpinLock(irql); + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // ConnectAddressComplete + + + +BOOLEAN +CancelConnectAddressCallback( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ + return (nArgs == 2 && pArgs[0] == pArg); +} // CancelConnectAddressCallback + + + +VOID +CancelConnectAddress( + PDEVICE_OBJECT pDevice, + PIRP pIrp + ) +{ + KIRQL irql; + PACD_NOTIFICATION pNotification; + + ASSERT(pIrp->Cancel); + // + // Remove our outstanding request. + // + pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; + // + // If we can't find the request on the connection + // list, then it has already been completed. + // + if (!AcdCancelConnection( + 0, + &pNotification->addr, + CancelConnectAddressCallback, + pIrp)) + { + IoReleaseCancelSpinLock(pIrp->CancelIrql); + return; + } + // + // Mark this irp as cancelled. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + IoSetCancelRoutine(pIrp, NULL); + // + // Release the spin lock the I/O system acquired. + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + // + // Complete the I/O request. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // CancelConnectAddress + + + +NTSTATUS +AcdConnectAddress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Manufacture a call to ourselves to simulate a transport + requesting an automatic connection. This allows a user + address space to initiate an automatic connection. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold + an ACD_NOTIFICATION structure. + + STATUS_UNSUCCESSFUL: an error occurred initiating the + automatic connection. + + STATUS_PENDING: success + +--*/ + +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + KIRQL irql; + PACD_NOTIFICATION pNotification; + PVOID pArgs[2]; + + // + // Verify the input buffer is sufficient to hold + // an ACD_NOTIFICATION structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (ACD_NOTIFICATION)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; + pArgs[0] = pIrp; + pArgs[1] = pIrpSp; + // + // Start the connection. + // + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdConnectAddress: ")); + AcdPrintAddress(&pNotification->addr); + AcdPrint((", ulFlags=0x%x\n", pNotification->ulFlags)); + } + if (pNotification->ulFlags & ACD_NOTIFICATION_SUCCESS) { + AcdNewConnection( + &pNotification->addr, + &pNotification->adapter); + status = STATUS_SUCCESS; + } + else { + IoAcquireCancelSpinLock(&irql); + if (AcdStartConnection( + 0, + &pNotification->addr, + pNotification->ulFlags, + ConnectAddressComplete, + 2, + pArgs)) + { + // + // We enqueued the request successfully. + // Mark the irp as pending. + // + IoSetCancelRoutine(pIrp, CancelConnectAddress); + IoMarkIrpPending(pIrp); + status = STATUS_PENDING; + } + IoReleaseCancelSpinLock(irql); + } + + return status; +} // AcdConnectAddress + + + +VOID +AcdSignalCompletionCommon( + IN PACD_CONNECTION pConnection, + IN BOOLEAN fSuccess + ) +{ + KIRQL irql; + PLIST_ENTRY pEntry; + PACD_COMPLETION pCompletion; + BOOLEAN fFound; + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "AcdSignalCompletionCommon: pConnection=0x%x, fCompleting=%d\n", + pConnection, + pConnection->fCompleting)); + } +again: + fFound = FALSE; + // + // Acquire our lock and look for + // the next uncompleted request. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + for (pEntry = pConnection->CompletionList.Flink; + pEntry != &pConnection->CompletionList; + pEntry = pEntry->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(( + "AcdSignalCompletionCommon: pCompletion=0x%x, fCanceled=%d, fCompleted=%d\n", + pCompletion, + pCompletion->fCanceled, + pCompletion->fCompleted)); + } + // + // Only complete this request if it + // hasn't already been completed + // or canceled. + // + if (!pCompletion->fCanceled && !pCompletion->fCompleted) { + pCompletion->fCompleted = TRUE; + fFound = TRUE; + break; + } + } + // + // If there are no more requests to + // complete then remove this connection + // from the connection list and free its + // memory. + // + if (!fFound) { + RemoveEntryList(&pConnection->ListEntry); + while (!IsListEmpty(&pConnection->CompletionList)) { + pEntry = RemoveHeadList(&pConnection->CompletionList); + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + + FREE_MEMORY(pCompletion); + } + FREE_MEMORY(pConnection); + // + // Signal the request thread that + // the connection list has changed. + // + KeSetEvent(&AcdRequestThreadEventG, 0, FALSE); + } + // + // Release our lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // If we found a request, then + // call its completion proc. + // + if (fFound) { + IF_ACDDBG(ACD_DEBUG_CONNECTION) { + AcdPrint(("AcdSignalCompletionCommon: pCompletion=0x%x, ", pCompletion)); + AcdPrintAddress(&pCompletion->notif.addr); + AcdPrint(("\n")); + } + (*pCompletion->pProc)(fSuccess, pCompletion->pArgs); + // + // Look for another request. + // + goto again; + } +} // AcdSignalCompletionCommon + + + +NTSTATUS +AcdSignalCompletion( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + For each thread waiting on the AcdCompletionQueueG, + call the transport-dependent callback to retry the + connection attempt and complete the irp. + +ARGUMENTS + pIrp: unused + + pIrpSp: unused + +RETURN VALUE + STATUS_SUCCESS + +--*/ + +{ + KIRQL irql; + PACD_STATUS pStatus; + PACD_CONNECTION pConnection; + BOOLEAN fFound = FALSE; + + // + // Verify the input buffer is sufficient to hold + // a BOOLEAN structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (ACD_STATUS)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Get the success code from the + // connection attempt and pass it + // to the completion routine. + // + pStatus = (PACD_STATUS)pIrp->AssociatedIrp.SystemBuffer; + KeAcquireSpinLock(&AcdSpinLockG, &irql); + pConnection = FindConnection(&pStatus->addr); + if (pConnection != NULL && !pConnection->fCompleting) { + // + // Set the completion-in-progress flag so + // this request cannot be timed-out after + // we release the spin lock. + // + pConnection->fCompleting = TRUE; + fFound = TRUE; + } + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // If we didn't find the connection block, + // or the completion was already in progress, + // then return an error. + // + if (!fFound) + return STATUS_UNSUCCESSFUL; + + AcdSignalCompletionCommon(pConnection, pStatus->fSuccess); + return STATUS_SUCCESS; +} // AcdSignalCompletion + + + +VOID +ClearRequests( + IN KIRQL irql + ) + +/*++ + +DESCRIPTION + Complete all pending requests with failure status. + This call assumes the AcdSpinLockG is already held, + and it returns with it held. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + PLIST_ENTRY pHead, pEntry; + PACD_COMPLETION pCompletion; + PACD_CONNECTION pConnection; + +again: + // + // Complete all pending connections with + // an error. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + if (!pConnection->fCompleting) { + pConnection->fCompleting = TRUE; + // + // We need to release our lock to + // complete the request. + // + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // Complete the request. + // + AcdSignalCompletionCommon(pConnection, FALSE); + // + // Check for more uncompleted requests. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + goto again; + } + } + // + // Clear out all other pending requests. + // + while (!IsListEmpty(&AcdCompletionQueueG)) { + pHead = RemoveHeadList(&AcdCompletionQueueG); + pCompletion = CONTAINING_RECORD(pHead, ACD_COMPLETION, ListEntry); + + FREE_MEMORY(pCompletion); + } +} // ClearRequests + + + +VOID +AcdReset() + +/*++ + +DESCRIPTION + Complete all pending requests with failure status. + This is called when the reference count on the driver + object goes to zero, and prevents stale requests from + being presented to the system service if it is restarted + when there are pending completion requests. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql; + PLIST_ENTRY pHead, pEntry; + PACD_COMPLETION pCompletion; + PACD_CONNECTION pConnection; + + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Reset the notification mode to disabled. + // + SetDriverMode(FALSE); + // + // Complete all pending connections with + // an error. + // + ClearRequests(irql); + KeReleaseSpinLock(&AcdSpinLockG, irql); +} // AcdReset + + + +NTSTATUS +AcdBind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Return the list of entry points to a client + transport driver. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL if the supplied SystemBuffer is too + small. STATUS_SUCCESS otherwise. + +--*/ + +{ + NTSTATUS status; + PACD_DRIVER *ppDriver, pDriver; + KIRQL irql, dirql; + + // + // Verify the input buffer a pointer to + // the driver's ACD_DRIVER structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (PACD_DRIVER)) + { + return STATUS_BUFFER_TOO_SMALL; + } + ppDriver = (PACD_DRIVER *)pIrp->AssociatedIrp.SystemBuffer; + pDriver = *ppDriver; +#if DBG + // + // Selectively bind with some transports. + // + switch (pDriver->ulDriverId) { + case 'Nbf ': + break; + case 'Tcp ': +#ifdef notdef + DbgPrint("AcdBind: ignoring Tcp\n"); + pDriver->fEnabled = FALSE; + pIrp->IoStatus.Information = 0; + return STATUS_SUCCESS; +#endif + break; + case 'Nbi ': +#ifdef notdef + DbgPrint("AcdBind: ignoring Nbi\n"); + pDriver->fEnabled = FALSE; + pIrp->IoStatus.Information = 0; + return STATUS_SUCCESS; +#endif + break; + } +#endif + // + // Fill in the entry point structure. + // + pDriver->lpfnNewConnection = AcdNewConnection; + pDriver->lpfnStartConnection = AcdStartConnection; + pDriver->lpfnCancelConnection = AcdCancelConnection; + // + // Insert this block into our driver list. + // + KeAcquireSpinLock(&AcdSpinLockG, &irql); + KeAcquireSpinLock(&pDriver->SpinLock, &dirql); + pDriver->fEnabled = fAcdEnabledG; + KeReleaseSpinLock(&pDriver->SpinLock, dirql); + InsertTailList(&AcdDriverListG, &pDriver->ListEntry); + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // No data should be copied back. + // + pIrp->IoStatus.Information = 0; + + return STATUS_SUCCESS; +} // AcdBind + + + +NTSTATUS +AcdUnbind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) + +/*++ + +DESCRIPTION + Unbind a client transport driver. + +ARGUMENTS + pIrp: a pointer to the irp to be enqueued. + + pIrpSp: a pointer to the current irp stack. + +RETURN VALUE + STATUS_BUFFER_TOO_SMALL if the supplied SystemBuffer is too + small. STATUS_SUCCESS otherwise. + +--*/ + +{ + KIRQL irql, dirql; + PLIST_ENTRY pEntry, pEntry2; + PACD_DRIVER *ppDriver, pDriver; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + + // + // Verify the input buffer a pointer to + // the driver's ACD_DRIVER structure. + // + if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof (PACD_DRIVER)) + { + return STATUS_BUFFER_TOO_SMALL; + } + ppDriver = (PACD_DRIVER *)pIrp->AssociatedIrp.SystemBuffer; + pDriver = *ppDriver; + + KeAcquireSpinLock(&AcdSpinLockG, &irql); + // + // Enumerate the list looking for + // any connection request initiated by the + // specified driver. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + for (pEntry2 = pConnection->CompletionList.Flink; + pEntry2 != &pConnection->CompletionList; + pEntry2 = pEntry2->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry); + + // + // If we have a match, cancel it. + // + if (pCompletion->ulDriverId == pDriver->ulDriverId) + pCompletion->fCanceled = TRUE; + } + } + // + // Set this driver's enable mode to ACD_ENABLE_NONE. + // + KeAcquireSpinLock(&pDriver->SpinLock, &dirql); + pDriver->fEnabled = FALSE; + KeReleaseSpinLock(&pDriver->SpinLock, dirql); + // + // Remove this driver from the list. + // + RemoveEntryList(&pDriver->ListEntry); + KeReleaseSpinLock(&AcdSpinLockG, irql); + // + // No data should be copied back. + // + pIrp->IoStatus.Information = 0; + + return STATUS_SUCCESS; +} // AcdUnbind + + +VOID +AcdPrintAddress( + IN PACD_ADDR pAddr + ) +{ +#if DBG + PUCHAR puc; + + switch (pAddr->fType) { + case ACD_ADDR_IP: + puc = (PUCHAR)&pAddr->ulIpaddr; + AcdPrint(("IP: %d.%d.%d.%d", puc[0], puc[1], puc[2], puc[3])); + break; + case ACD_ADDR_IPX: + AcdPrint(( + "IPX: %02x:%02x:%02x:%02x:%02x:%02x", + pAddr->cNode[0], + pAddr->cNode[1], + pAddr->cNode[2], + pAddr->cNode[3], + pAddr->cNode[4], + pAddr->cNode[5])); + break; + case ACD_ADDR_NB: + AcdPrint(("NB: %15.15s", pAddr->cNetbios)); + break; + case ACD_ADDR_INET: + AcdPrint(("INET: %s", pAddr->szInet)); + break; + default: + AcdPrint(("UNKNOWN: ????")); + break; + } +#endif +} // AcdPrintAddress |