summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/acd/api.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/tdi/acd/api.c')
-rw-r--r--private/ntos/tdi/acd/api.c1556
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