From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/tdi/acd/acddefs.h | 100 +++ private/ntos/tdi/acd/api.c | 1556 ++++++++++++++++++++++++++++++++++++++++ private/ntos/tdi/acd/debug.h | 74 ++ private/ntos/tdi/acd/makefile | 6 + private/ntos/tdi/acd/mem.c | 289 ++++++++ private/ntos/tdi/acd/mem.h | 41 ++ private/ntos/tdi/acd/ntdisp.c | 411 +++++++++++ private/ntos/tdi/acd/ntinit.c | 223 ++++++ private/ntos/tdi/acd/rasacd.rc | 12 + private/ntos/tdi/acd/request.c | 303 ++++++++ private/ntos/tdi/acd/request.h | 31 + private/ntos/tdi/acd/sources | 46 ++ private/ntos/tdi/acd/table.c | 286 ++++++++ private/ntos/tdi/acd/table.h | 77 ++ private/ntos/tdi/acd/timer.c | 145 ++++ 15 files changed, 3600 insertions(+) create mode 100644 private/ntos/tdi/acd/acddefs.h create mode 100644 private/ntos/tdi/acd/api.c create mode 100644 private/ntos/tdi/acd/debug.h create mode 100644 private/ntos/tdi/acd/makefile create mode 100644 private/ntos/tdi/acd/mem.c create mode 100644 private/ntos/tdi/acd/mem.h create mode 100644 private/ntos/tdi/acd/ntdisp.c create mode 100644 private/ntos/tdi/acd/ntinit.c create mode 100644 private/ntos/tdi/acd/rasacd.rc create mode 100644 private/ntos/tdi/acd/request.c create mode 100644 private/ntos/tdi/acd/request.h create mode 100644 private/ntos/tdi/acd/sources create mode 100644 private/ntos/tdi/acd/table.c create mode 100644 private/ntos/tdi/acd/table.h create mode 100644 private/ntos/tdi/acd/timer.c (limited to 'private/ntos/tdi/acd') diff --git a/private/ntos/tdi/acd/acddefs.h b/private/ntos/tdi/acd/acddefs.h new file mode 100644 index 000000000..0563e9926 --- /dev/null +++ b/private/ntos/tdi/acd/acddefs.h @@ -0,0 +1,100 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + acddefs.h + +Abstract: + + Shared internal structure defintions for the Implicit + Connection Driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 23-Jun-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#ifndef _ACDDEFS_ +#define _ACDDEFS_ + +// +// Min macro +// +#define MIN(a, b) (a) < (b) ? (a) : (b) + +// +// List entry structure for the AcdCompletionQueue. +// +typedef struct _ACD_COMPLETION { + LIST_ENTRY ListEntry; + ULONG ulDriverId; // transport driver id + BOOLEAN fCanceled; // TRUE if request was canceled + BOOLEAN fCompleted; // TRUE if request was completed + ACD_NOTIFICATION notif; + ACD_CONNECT_CALLBACK pProc; // callback proc + USHORT nArgs; // argument count + PVOID pArgs[1]; // variable length arguments +} ACD_COMPLETION, *PACD_COMPLETION; + +// +// A connection block. +// +// For each pending connection, there is a +// connection block that describes the current +// state. +// +typedef struct _ACD_CONNECTION { + LIST_ENTRY ListEntry; // connection list + BOOLEAN fNotif; // TRUE if service has been notified + BOOLEAN fProgressPing; // TRUE if service has pinged + BOOLEAN fCompleting; // TRUE if in AcdSignalCompletionCommon + ULONG ulTimerCalls; // # of total pings + ULONG ulMissedPings; // # of missed pings + LIST_ENTRY CompletionList; // completion list +} ACD_CONNECTION, *PACD_CONNECTION; + +// +// Generic hash table entry. +// +typedef struct _HASH_ENTRY { + LIST_ENTRY ListEntry; + ACD_ADDR szKey; + ULONG ulData; +} HASH_ENTRY, *PHASH_ENTRY; + +extern KSPIN_LOCK AcdSpinLockG; + +extern KEVENT AcdRequestThreadEventG; + +extern LIST_ENTRY AcdNotificationQueueG; +extern LIST_ENTRY AcdCompletionQueueG; +extern LIST_ENTRY AcdConnectionQueueG; +extern LIST_ENTRY AcdDriverListG; + +extern BOOLEAN fConnectionInProgressG; +extern BOOLEAN fProgressPingG; +extern ULONG nTimerCallsG; +extern ULONG nMissedPingsG; + +extern PDEVICE_OBJECT pAcdDeviceObjectG; + +extern ACD_ADDR szComputerName; + +// +// Miscellaneous routines. +// +VOID +AcdPrintAddress( + IN PACD_ADDR pAddr + ); + +#endif // _ACDDEFS_ 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 +#include +#include +#include +#include +#include +#include + +#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 diff --git a/private/ntos/tdi/acd/debug.h b/private/ntos/tdi/acd/debug.h new file mode 100644 index 000000000..421c43de6 --- /dev/null +++ b/private/ntos/tdi/acd/debug.h @@ -0,0 +1,74 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + debug.h + +Abstract: + + Debugging defintions for the Automatic + Connection Driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 3-Aug-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#ifndef _ACDDBG_ +#define _ACDDBG_ + +// +// Debug tracing flags. +// +// To enable debug tracing for a module, set the +// appropriate bit in ntinit\AcdDebugG. +// +#if DBG + +#define ACD_DEBUG_IOCTL 0x00000001 // ntdisp.c/AcdDispatchDeviceControl() +#define ACD_DEBUG_OPENCOUNT 0x00000002 // ntdisp.c/Acd{Open,Close}() +#define ACD_DEBUG_TIMER 0x00000004 // timer.c +#define ACD_DEBUG_CONNECTION 0x00000008 // api.c/AcdStartConnection() +#define ACD_DEBUG_WORKER 0x00000010 // api.c/AcdNotificationRequestThread() +#define ACD_DEBUG_RESET 0x00000020 // api.c/AcdReset() +#define ACD_DEBUG_MEMORY 0x80000000 // memory alloc/free + +#define IF_ACDDBG(flag) if (AcdDebugG & flag) +#define AcdPrint(many_args) DbgPrint many_args + +#define ALLOCATE_MEMORY(fObject, pObject) \ + pObject = AllocateObjectMemory(fObject); \ + IF_ACDDBG(ACD_DEBUG_MEMORY) \ + AcdPrint(("ALLOCATE_MEMORY: %s(%d): fObject=%d, pObject=0x%x\n", __FILE__, __LINE__, fObject, pObject)) + +#define FREE_MEMORY(pObject) \ + IF_ACDDBG(ACD_DEBUG_MEMORY) \ + AcdPrint(("FREE_MEMORY: %s(%d): pObject=0x%x\n", __FILE__, __LINE__, pObject)); \ + FreeObjectMemory(pObject) + +extern ULONG AcdDebugG; + +#else + +#define IF_ACDDBG(flag) if (0) +#define AcdPrint(many_args) + +#define ALLOCATE_MEMORY(fObject, pObject) \ + pObject = AllocateObjectMemory(fObject); + +#define FREE_MEMORY(pObject) \ + FreeObjectMemory(pObject) + +#endif + + +#endif // _ACDDBG_ diff --git a/private/ntos/tdi/acd/makefile b/private/ntos/tdi/acd/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/acd/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/acd/mem.c b/private/ntos/tdi/acd/mem.c new file mode 100644 index 000000000..e3c9166c1 --- /dev/null +++ b/private/ntos/tdi/acd/mem.c @@ -0,0 +1,289 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + table.c + +ABSTRACT + Generic hash table manipulation routines. + +AUTHOR + Anthony Discolo (adiscolo) 28-Jul-1995 + +REVISION HISTORY + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + +// +// The maximum number of allocated +// objects we allocate from outside +// our zones. +// +#define MAX_ALLOCATED_OBJECTS 100 + +// +// Rounding up macro. +// +#define ROUNDUP(n, b) (((n) + ((b) - 1)) & ~((b) - 1)) + +// +// Map an object type to a zone. +// +#define OBJECT_INFO(fObject) \ + (fObject < ACD_OBJECT_MAX) ? &AcdObjectInfoG[fObject] : &AcdObjectInfoG[ACD_OBJECT_MAX] + +// +// The spin lock for this module. +// +KSPIN_LOCK AcdMemSpinLockG; + +// +// Zone-based object information. One zone +// per object type. +// +typedef struct _OBJECT_INFORMATION { + ZONE_HEADER zone; + ULONG ulSize; // object size + ULONG ulTag; // ExAllocateFromPoolWithTag() tag + ULONG ulCurrent; // # currently allocated in zone + ULONG ulTotal; // total # zone allocations +} OBJECT_INFORMATION, *POBJECT_INFORMATION; + +OBJECT_INFORMATION AcdObjectInfoG[ACD_OBJECT_MAX + 1]; + +// +// Pool-based object allocation. This is for +// objects that don't fit into any of the zones, +// or when the zone is full. +// +typedef struct _POOL_INFORMATION { + ULONG cbMin; // minimum size + ULONG cbMax; // maximum size + ULONG ulCurrent; // # current allocations + ULONG ulTotal; // total allocations + ULONG ulFailures; // total failures +} POOL_INFORMATION, *PPOOL_INFORMATION; + +POOL_INFORMATION AcdPoolInfoG; + + + +VOID +InitializeObjectAllocator() +{ + NTSTATUS status; + PVOID pMem; + ULONG ulSize; + + KeInitializeSpinLock(&AcdMemSpinLockG); + // + // Initialize zone 0 (ACD_OBJECT_CONNECTION). + // + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulTag = 'NdcA'; + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize = + ROUNDUP(sizeof (ACD_CONNECTION), 8); + ulSize = PAGE_SIZE; + pMem = ExAllocatePoolWithTag( + NonPagedPool, + ulSize, + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulTag); + ASSERT(pMem != NULL); + status = ExInitializeZone( + &AcdObjectInfoG[ACD_OBJECT_CONNECTION].zone, + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize, + pMem, + ulSize); + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "InitializeObjectAllocator: zone 0 created: blksiz=%d, size=%d (status=%d)\n", + AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize, + ulSize, + status)); + } + // + // Initialize zone 1 (ACD_OBJECT_COMPLETION). + // + AcdObjectInfoG[ACD_OBJECT_MAX].ulTag = 'MdcA'; + // + // Allow for up to 6 parameters to a completion + // request (6 used by tcpip.sys). + // + AcdObjectInfoG[ACD_OBJECT_MAX].ulSize = + ROUNDUP(sizeof (ACD_COMPLETION) + (6 * sizeof (PVOID)), 8); + ulSize = ROUNDUP(6 * AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, PAGE_SIZE), + pMem = ExAllocatePoolWithTag( + NonPagedPool, + ulSize, + AcdObjectInfoG[ACD_OBJECT_MAX].ulTag); + ASSERT(pMem != NULL); + status = ExInitializeZone( + &AcdObjectInfoG[ACD_OBJECT_MAX].zone, + AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, + pMem, + ulSize); + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "InitializeObjectAllocator: zone 1 created: blksiz=%d size=%d (status=%d)\n", + AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, + ulSize, + status)); + } + // + // Initialize the pool info. + // + AcdPoolInfoG.cbMin = 0xffffffff; + AcdPoolInfoG.cbMax = 0; + AcdPoolInfoG.ulCurrent = 0; + AcdPoolInfoG.ulTotal = 0; + AcdPoolInfoG.ulFailures = 0; +} // InitializeObjectAllocator + + + +PVOID +AllocateObjectMemory( + IN ULONG fObject + ) +{ + KIRQL irql; + POBJECT_INFORMATION pObjectInfo = OBJECT_INFO(fObject); + PVOID pObject; + ULONG cbBytes = 0, ulTag; + static ULONG nAllocations = 0; + + KeAcquireSpinLock(&AcdMemSpinLockG, &irql); + // + // If the zone is full, or the object + // size is greater than the zone object size, + // then use the pool allocator. + // + if (fObject > pObjectInfo->zone.BlockSize) { + cbBytes = fObject; + ulTag = 'PdcA'; + } + else if (ExIsFullZone(&pObjectInfo->zone)) { + cbBytes = pObjectInfo->ulSize; + ulTag = pObjectInfo->ulTag; + } + if (cbBytes) { + // + // Limit memory usage under stress. + // If we have more than 100 outstanding + // requests, then we start dropping + // them. + // + if (AcdPoolInfoG.ulCurrent < MAX_ALLOCATED_OBJECTS) + pObject = ExAllocatePoolWithTag(NonPagedPool, cbBytes, ulTag); + else { + pObject = NULL; + AcdPoolInfoG.ulFailures++; + goto done; + } + if (cbBytes < AcdPoolInfoG.cbMin) + AcdPoolInfoG.cbMin = cbBytes; + if (cbBytes > AcdPoolInfoG.cbMax) + AcdPoolInfoG.cbMax = cbBytes; + AcdPoolInfoG.ulCurrent++; + AcdPoolInfoG.ulTotal++; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "AllocateObjectMemory: allocated type %d from pool: pObject=0x%x\n", + fObject, + pObject)); + } + } + else { + pObject = ExAllocateFromZone(&pObjectInfo->zone); + pObjectInfo->ulCurrent++; + pObjectInfo->ulTotal++; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "AllocateObjectMemory: allocated type %d from zone: pObject=0x%x\n", + fObject, + pObject)); + } + } +#if DBG + IF_ACDDBG(ACD_DEBUG_MEMORY) { + INT i; + + if (!(++nAllocations % 10)) { + for (i = 0; i <= ACD_OBJECT_MAX; i++) { + AcdPrint(( + "Zone %d: ulCurrent=%d, ulTotal=%d\n", + i, + AcdObjectInfoG[i].ulCurrent, + AcdObjectInfoG[i].ulTotal)); + } + AcdPrint(( + "Pool: ulCurrent=%d, ulTotal=%d\n", + AcdPoolInfoG.ulCurrent, + AcdPoolInfoG.ulTotal)); + } + } +#endif +done: + KeReleaseSpinLock(&AcdMemSpinLockG, irql); + + return pObject; +} // AllocateObjectMemory + + + +VOID +FreeObjectMemory( + IN PVOID pObject + ) +{ + KIRQL irql; + INT i; + POBJECT_INFORMATION pObjectInfo; + + KeAcquireSpinLock(&AcdMemSpinLockG, &irql); + for (i = 0; i <= ACD_OBJECT_MAX; i++) { + pObjectInfo = &AcdObjectInfoG[i]; + + if (ExIsObjectInFirstZoneSegment(&pObjectInfo->zone, pObject)) { + ExFreeToZone(&pObjectInfo->zone, pObject); + pObjectInfo->ulCurrent--; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "FreeObjectMemory: freed type %d into zone: pObject=0x%x\n", + i, + pObject)); + } + goto done; + } + } + ExFreePool(pObject); + AcdPoolInfoG.ulCurrent--; + IF_ACDDBG(ACD_DEBUG_MEMORY) { + AcdPrint(( + "FreeObjectMemory: freed into pool: pObject=0x%x\n", + pObject)); + } +done: + KeReleaseSpinLock(&AcdMemSpinLockG, irql); +} // FreeObjectMemory + + + +VOID +FreeObjectAllocator() +{ + // Apparently, we can't do this? +} // FreeObjectAllocator diff --git a/private/ntos/tdi/acd/mem.h b/private/ntos/tdi/acd/mem.h new file mode 100644 index 000000000..3811029e7 --- /dev/null +++ b/private/ntos/tdi/acd/mem.h @@ -0,0 +1,41 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + mem.h + +ABSTRACT + Header file for memory allocation routines. + +AUTHOR + Anthony Discolo (adiscolo) 18-Aug-1995 + +REVISION HISTORY + +--*/ + +// +// Pre-defined object types. +// Any other value represents a +// byte count. +// +#define ACD_OBJECT_CONNECTION 0 +#define ACD_OBJECT_MAX 1 + +VOID +InitializeObjectAllocator(); + +PVOID +AllocateObjectMemory( + IN ULONG fObject + ); + +VOID +FreeObjectMemory( + IN PVOID pObject + ); + +VOID +FreeObjectAllocator(); + diff --git a/private/ntos/tdi/acd/ntdisp.c b/private/ntos/tdi/acd/ntdisp.c new file mode 100644 index 000000000..5388656d2 --- /dev/null +++ b/private/ntos/tdi/acd/ntdisp.c @@ -0,0 +1,411 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ntdisp.c + +Abstract: + + NT specific routines for dispatching and handling automatic + connection notification IRPs. + + The basic architecture involves a user address space, + a network transport, and this driver. + + The user address space is responsible for creating a + new network connection given a notification from this + driver (IOCTL_ACD_NOTIFICATION). When it gets a + notification, it is also responsible for pinging the + this driver (IOCTL_ACD_KEEPALIVE) so it can be guaranteed + the connection is progressing. Once the connection is + created, it informs this driver of the success or + failure of the connection attempt (IOCTL_ACD_CONNECTION). + + Network transports are responsible for informing this + driver of network unreachable errors via TdiConnect() + or TdiSendDatagram(). When this happens, the transport + is responsible for dequeueing the send request from any + of its internal queues and enqueueing the request in + this driver (AcdWaitForCompletion()), supplying a callback + to be called when the connection has been completed. + +Author: + + Anthony Discolo (adiscolo) 18-Apr-1995 + +Revision History: + +--*/ +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "debug.h" + +// +// Driver reference count +// +ULONG ulAcdOpenCountG; + +// +// Imported routines +// +NTSTATUS +AcdEnable( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +VOID +AcdCancelNotifications(); + +NTSTATUS +AcdWaitForNotification( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdConnectionInProgress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdSignalCompletion( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdConnectAddress( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +VOID +AcdReset(); + +NTSTATUS +AcdGetAddressAttributes( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdSetAddressAttributes( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +// +// Internal function prototypes +// +NTSTATUS +AcdCreate( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdDispatchDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdDispatchInternalDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdCleanup( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdClose( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdBind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +NTSTATUS +AcdUnbind( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ); + +// +// All of this code is pageable. +// +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, AcdCreate) +#pragma alloc_text(PAGE, AcdDispatchDeviceControl) +#pragma alloc_text(PAGE, AcdDispatchInternalDeviceControl) +#pragma alloc_text(PAGE, AcdCleanup) +#pragma alloc_text(PAGE, AcdClose) +#endif // ALLOC_PRAGMA + + + +NTSTATUS +AcdCreate( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + ulAcdOpenCountG++; + IF_ACDDBG(ACD_DEBUG_OPENCOUNT) { + AcdPrint(("AcdCreate: ulAcdOpenCountG=%d\n", ulAcdOpenCountG)); + } + return STATUS_SUCCESS; +} // AcdCreate + + + +NTSTATUS +AcdDispatchDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + NTSTATUS status; + + + PAGED_CODE(); + // + // Set this in advance. Any IOCTL dispatch routine that cares about it + // will modify it itself. + // + pIrp->IoStatus.Information = 0; + + switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_ACD_RESET: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_RESET\n")); + } + AcdReset(); + status = STATUS_SUCCESS; + break; + case IOCTL_ACD_ENABLE: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_ENABLE\n")); + } + // + // Enable/disable requests to/from the driver. + // + status = AcdEnable(pIrp, pIrpSp); + break; + case IOCTL_ACD_NOTIFICATION: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_NOTIFICATION\n")); + } + // + // This irp will be completed upon the + // next connection attempt to + // allow a user-space process to attempt + // to make a connection. + // + status = AcdWaitForNotification(pIrp, pIrpSp); + break; + case IOCTL_ACD_KEEPALIVE: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_KEEPALIVE\n")); + } + // + // Inform the driver that the connection + // is in the process of being created. + // + status = AcdConnectionInProgress(pIrp, pIrpSp); + break; + case IOCTL_ACD_COMPLETION: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_COMPLETION\n")); + } + // + // Complete all pending irps that initially + // encountered a network unreachable error, + // and have been waiting for a connection to be + // made. + // + status = AcdSignalCompletion(pIrp, pIrpSp); + break; + case IOCTL_ACD_CONNECT_ADDRESS: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_CONNECT_ADDRESS\n")); + } + // + // This allows a user space application to + // generate the same automatic connection + // mechanism as a transport protocol. + // + status = AcdConnectAddress(pIrp, pIrpSp); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != STATUS_PENDING) { + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return status; +} // AcdDispatchDeviceControl + + + +NTSTATUS +AcdDispatchInternalDeviceControl( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + NTSTATUS status; + + PAGED_CODE(); + // + // Set this in advance. Any IOCTL dispatch routine that cares about it + // will modify it itself. + // + pIrp->IoStatus.Information = 0; + + switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_INTERNAL_ACD_BIND: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchInternalDeviceControl: IOCTL_INTERNAL_ACD_BIND\n")); + } + // + // Transfer entrypoints to client. + // + status = AcdBind(pIrp, pIrpSp); + break; + case IOCTL_INTERNAL_ACD_UNBIND: + IF_ACDDBG(ACD_DEBUG_IOCTL) { + AcdPrint(("AcdDispatchInternalDeviceControl: IOCTL_INTERNAL_ACD_UNBIND\n")); + } + // + // Remove any pending requests from + // this driver. + // + status = AcdUnbind(pIrp, pIrpSp); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != STATUS_PENDING) { + pIrp->IoStatus.Status = status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return status; +} // AcdDispatchInternalDeviceControl + + + +NTSTATUS +AcdCleanup( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + return STATUS_SUCCESS; +} // AcdCleanup + + + +NTSTATUS +AcdClose( + IN PIRP pIrp, + IN PIO_STACK_LOCATION pIrpSp + ) +{ + ulAcdOpenCountG--; + IF_ACDDBG(ACD_DEBUG_OPENCOUNT) { + AcdPrint(("AcdClose: ulAcdOpenCountG=%d\n", ulAcdOpenCountG)); + } + if (!ulAcdOpenCountG) + AcdReset(); + return STATUS_SUCCESS; +} // AcdClose + + + +NTSTATUS +AcdDispatch( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) + +/*++ + +DESCRIPTION + This is the dispatch routine for the network connection + notification driver. + +ARGUMENTS + pDeviceObject: a pointer to device object for target device + + pIrp: a pointer to I/O request packet + +Return Value: + NTSTATUS + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION pIrpSp; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + switch (pIrpSp->MajorFunction) { + case IRP_MJ_CREATE: + status = AcdCreate(pIrp, pIrpSp); + break; + case IRP_MJ_DEVICE_CONTROL: + return AcdDispatchDeviceControl(pIrp, pIrpSp); + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return AcdDispatchInternalDeviceControl(pIrp, pIrpSp); + case IRP_MJ_CLEANUP: + status = AcdCleanup(pIrp, pIrpSp); + break; + case IRP_MJ_CLOSE: + status = AcdClose(pIrp, pIrpSp); + break; + default: + DbgPrint("AcdDispatch: Invalid major function %lx\n", + pIrpSp->MajorFunction); + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != STATUS_PENDING) { + pIrp->IoStatus.Status = status; + pIrp->IoStatus.Information = 0; + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return status; +} // AcdDispatch + diff --git a/private/ntos/tdi/acd/ntinit.c b/private/ntos/tdi/acd/ntinit.c new file mode 100644 index 000000000..24ad73ef0 --- /dev/null +++ b/private/ntos/tdi/acd/ntinit.c @@ -0,0 +1,223 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntinit.c + +Abstract: + + NT specific routines for loading and configuring the + automatic connection notification driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 18-Apr-1995 + +Revision History: + +--*/ +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + + +// +// Global variables +// +#if DBG +ULONG AcdDebugG = 0x0; // see debug.h for flags +#endif + +PDRIVER_OBJECT pAcdDriverObjectG; +PDEVICE_OBJECT pAcdDeviceObjectG; + +HANDLE hSignalNotificationThreadG; + +// +// Imported routines +// +VOID +AcdNotificationRequestThread( + PVOID context + ); + +// +// External function prototypes +// +NTSTATUS +AcdDispatch( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +VOID +AcdConnectionTimer( + IN PDEVICE_OBJECT pDeviceObject, + IN PVOID pContext + ); + +// +// Internal function prototypes +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT pDriverObject, + IN PUNICODE_STRING pRegistryPath + ); + +BOOLEAN +GetComputerName( + IN PUCHAR szName, + IN USHORT cbName + ); + +VOID +AcdUnload( + IN PDRIVER_OBJECT pDriverObject + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, AcdUnload) +#endif // ALLOC_PRAGMA + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT pDriverObject, + IN PUNICODE_STRING pRegistryPath + ) + +/*++ + +DESCRIPTION + Initialization routine for the network connection notification driver. + It creates the device object and initializes the driver. + +ARGUMENTS + pDriverObject: a pointer to the driver object created by the system. + + pRegistryPath - the name of the configuration node in the registry. + +RETURN VALUE + The final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING deviceName; + ULONG i; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatusBlock; + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT pFileObject; + + // + // Initialize the spin lock. + // + KeInitializeSpinLock(&AcdSpinLockG); + // + // Initialize the notification and completion + // connection queues. + // + InitializeListHead(&AcdNotificationQueueG); + InitializeListHead(&AcdCompletionQueueG); + InitializeListHead(&AcdConnectionQueueG); + InitializeListHead(&AcdDriverListG); + // + // Initialize our zone allocator. + // + InitializeObjectAllocator(); + // + // Create the device object. + // + pAcdDriverObjectG = pDriverObject; + RtlInitUnicodeString(&deviceName, ACD_DEVICE_NAME); + status = IoCreateDevice( + pDriverObject, + 0, + &deviceName, + FILE_DEVICE_ACD, + 0, + FALSE, + &pAcdDeviceObjectG); + + if (!NT_SUCCESS(status)) { + DbgPrint( + "AcdDriverEntry: IoCreateDevice failed (status=0x%x)\n", + status); + return status; + } + // + // Initialize the driver object. + // + //pDriverObject->DriverUnload = AcdUnload; + pDriverObject->DriverUnload = NULL; + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + pDriverObject->MajorFunction[i] = AcdDispatch; + pDriverObject->FastIoDispatch = NULL; + // + // Initialize the connection timer. This is + // used to make sure pending requests aren't + // blocked forever because the user-space + // process died trying to make a connection. + // + IoInitializeTimer(pAcdDeviceObjectG, AcdConnectionTimer, NULL); + // + // Create the worker thread. We need + // a thread because these operations can occur at + // DPC irql. + // + KeInitializeEvent( + &AcdRequestThreadEventG, + NotificationEvent, + FALSE); + status = PsCreateSystemThread( + &hSignalNotificationThreadG, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + AcdNotificationRequestThread, + NULL); + if (!NT_SUCCESS(status)) { + DbgPrint( + "AcdDriverEntry: PsCreateSystemThread failed (status=0x%x)\n", + status); + return status; + } + + return STATUS_SUCCESS; +} // DriverEntry + + + +VOID +AcdUnload( + IN PDRIVER_OBJECT pDriverObject + ) +{ + NTSTATUS status; + + // + // BUGBUG: Make sure to unlink all driver + // blocks before unloading! + // + IoDeleteDevice(pAcdDeviceObjectG); + // + // Free zone allocator. + // + FreeObjectAllocator(); +} // AcdUnload diff --git a/private/ntos/tdi/acd/rasacd.rc b/private/ntos/tdi/acd/rasacd.rc new file mode 100644 index 000000000..d8c0d68bf --- /dev/null +++ b/private/ntos/tdi/acd/rasacd.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "RAS Automatic Connection Driver" +#define VER_INTERNALNAME_STR "rasacd.sys" +#define VER_ORIGINALFILENAME_STR "rasacd.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/acd/request.c b/private/ntos/tdi/acd/request.c new file mode 100644 index 000000000..7976e733d --- /dev/null +++ b/private/ntos/tdi/acd/request.c @@ -0,0 +1,303 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + Worker thread for the automatic connection driver. + +Author: + + Anthony Discolo (adiscolo) 17-Apr-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + +// +// External declarations +// +VOID AcdPrintAddress( + IN PACD_ADDR pAddr + ); + + + +VOID +ProcessCompletion( + IN PACD_COMPLETION pCompletion, + IN KIRQL irqlCancel, + IN KIRQL irqlLock + ) +{ + PLIST_ENTRY pHead; + KIRQL irql; + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + PACD_NOTIFICATION pNotification; + + ASSERT(!IsListEmpty(&AcdNotificationQueueG)); + // + // Complete the next irp in the + // AcdNotificationQueueG queue. These + // represent the ioctl completions the + // system service has posted. Completing + // this request will start the system service + // to create a new RAS connection. + // Logically, there is always just one. + // + pHead = RemoveHeadList(&AcdNotificationQueueG); + pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry); + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + // + // Disable the irp's cancel routine. + // + IoSetCancelRoutine(pIrp, NULL); + // + // Copy the success flag and the address into the + // system buffer. This will get copied into the + // user's buffer on return. + // + pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer; + RtlCopyMemory( + pNotification, + &pCompletion->notif, + sizeof (ACD_NOTIFICATION)); + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: ")); + AcdPrintAddress(&pCompletion->notif.addr); + AcdPrint((", ulFlags=0x%x\n", pCompletion->notif.ulFlags)); + } + // + // We can release both the cancel lock + // and our lock now. + // + KeReleaseSpinLock(&AcdSpinLockG, irqlLock); + IoReleaseCancelSpinLock(irqlCancel); + // + // Set the status code and the number + // of bytes to be copied back to the user + // buffer. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = sizeof (ACD_NOTIFICATION); + // + // Complete the irp. + // + IoCompleteRequest(pIrp, IO_NO_INCREMENT); +} // ProcessCompletion + + + +VOID +AcdNotificationRequestThread( + PVOID context + ) + +/*++ + +DESCRIPTION + This thread handles the notification that an automatic + connection may need to be initiated. This needs to + happen in a separate thread, because the notification + may occur at DPC irql. + +ARGUMENTS + None. + +RETURN VALUE + None. + +--*/ + +{ + KIRQL irql, irql2; + PLIST_ENTRY pEntry, pEntry2; + PACD_CONNECTION pConnection; + PACD_COMPLETION pCompletion; + BOOLEAN bStartTimer, bStopTimer; + + UNREFERENCED_PARAMETER(context); + + IoStartTimer(pAcdDeviceObjectG); + + for (;;) { + bStartTimer = bStopTimer = FALSE; + // + // Acquire our lock. + // + IoAcquireCancelSpinLock(&irql); + KeAcquireSpinLock(&AcdSpinLockG, &irql2); + // + // If there are no irps to complete, + // then go back to sleep. + // + if (IsListEmpty(&AcdNotificationQueueG)) { + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: no ioctl to complete\n")); + } + KeReleaseSpinLock(&AcdSpinLockG, irql2); + IoReleaseCancelSpinLock(irql); + goto again; + } + // + // Search for connections that haven't + // been processed yet. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + // + // Don't issue a request to the service + // for more than one simultaneous connection. + // + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(( + "AcdNotificationRequestThread: pConnection=0x%x, fNotif=%d, fCompleting=%d\n", + pConnection, + pConnection->fNotif, + pConnection->fCompleting)); + } + if (pConnection->fNotif) + break; + // + // Skip all connections that are in + // the process of being completed. + // + if (pConnection->fCompleting) + continue; + // + // Make sure there is at least one + // request in this connection that + // hasn't been canceled. + // + for (pEntry2 = pConnection->CompletionList.Flink; + pEntry2 != &pConnection->CompletionList; + pEntry2 = pEntry2->Flink) + { + pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry); + + if (!pCompletion->fCanceled) { + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(( + "AcdNotificationRequestThread: starting pConnection=0x%x, pCompletion=0x%x\n", + pConnection, + pCompletion)); + } + pConnection->fNotif = TRUE; + // + // This call releases both the cancel lock + // and our lock. + // + ProcessCompletion(pCompletion, irql, irql2); + // + // Start the connection timer. + // + bStartTimer = TRUE; + // + // We can only process one completion + // at a time. + // + goto again; + } + } + } + // + // Complete other requests. + // + if (!IsListEmpty(&AcdCompletionQueueG)) { + pEntry = RemoveHeadList(&AcdCompletionQueueG); + pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry); + + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(( + "AcdNotificationRequestThread: starting pCompletion=0x%x\n", + pCompletion)); + } + // + // This call releases both the cancel lock + // and our lock. + // + ProcessCompletion(pCompletion, irql, irql2); + // + // We are done with the completion, + // so we can free the memory now. + // + FREE_MEMORY(pCompletion); + // + // We can only process one completion + // at a time. + // + goto again; + + } + // + // If there are no connections pending, + // then stop the connection timer. + // + if (IsListEmpty(&AcdConnectionQueueG)) + bStopTimer = TRUE; + // + // Release our lock. + // + KeReleaseSpinLock(&AcdSpinLockG, irql2); + IoReleaseCancelSpinLock(irql); +again: + // + // Start or stop the timer, depending + // on what we found while we had the + // spinlock. We can't hold our spin + // lock when we call the Io*Timer + // routines. + // +#ifdef notdef + if (bStopTimer) + IoStopTimer(pAcdDeviceObjectG); + else if (bStartTimer) + IoStartTimer(pAcdDeviceObjectG); +#endif + // + // Wait for something to do. This event + // will be signaled by AcdSignalNotification(). + // + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: waiting on AcdPendingCompletionEventG\n")); + } + KeWaitForSingleObject( + &AcdRequestThreadEventG, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(&AcdRequestThreadEventG); + IF_ACDDBG(ACD_DEBUG_WORKER) { + AcdPrint(("AcdNotificationRequestThread: AcdPendingCompletionEventG signalled\n")); + } + } +} // AcdNotificationRequestThread + + diff --git a/private/ntos/tdi/acd/request.h b/private/ntos/tdi/acd/request.h new file mode 100644 index 000000000..4d2c69824 --- /dev/null +++ b/private/ntos/tdi/acd/request.h @@ -0,0 +1,31 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + request.h + +ABSTRACT + Header file for completion queue routines. + +AUTHOR + Anthony Discolo (adiscolo) 18-Aug-1995 + +REVISION HISTORY + +--*/ + +PACD_COMPLETION GetNextRequest(); + +BOOLEAN +EqualAddress( + PACD_ADDR pszAddr1, + PACD_ADDR pszAddr2 + ); + +PACD_COMPLETION GetNextRequestAddress( + IN PACD_ADDR pszAddr + ); + +PACD_COMPLETION GetCurrentRequest(); + diff --git a/private/ntos/tdi/acd/sources b/private/ntos/tdi/acd/sources new file mode 100644 index 000000000..b787b8419 --- /dev/null +++ b/private/ntos/tdi/acd/sources @@ -0,0 +1,46 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP= +MINORCOMP= + +TARGETNAME=rasacd +TARGETPATH=obj +TARGETTYPE=DRIVER + +MSC_WARNING_LEVEL=/W3 /WX + +INCLUDES=..\inc;..\..\inc;..\..\..\inc;..\..\..\..\inc + +C_DEFINES=-DNT -DNETSCAPE_HACK + +SOURCES= \ + ntinit.c \ + ntdisp.c \ + api.c \ + request.c \ + timer.c \ + mem.c \ + rasacd.rc + diff --git a/private/ntos/tdi/acd/table.c b/private/ntos/tdi/acd/table.c new file mode 100644 index 000000000..0846b97e6 --- /dev/null +++ b/private/ntos/tdi/acd/table.c @@ -0,0 +1,286 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + table.c + +ABSTRACT + Generic hash table manipulation routines. + +AUTHOR + Anthony Discolo (adiscolo) 28-Jul-1995 + +REVISION HISTORY + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "table.h" +#include "acddefs.h" +#include "mem.h" +#include "debug.h" + + + +PHASH_TABLE +NewTable() +{ + PHASH_TABLE pTable; + INT i; + + ALLOCATE_MEMORY(sizeof (HASH_TABLE), pTable); + if (pTable == NULL) { + DbgPrint("AcdNewTable: ExAllocatePool failed\n"); + return NULL; + } + KeInitializeSpinLock(&pTable->SpinLock); + for (i = 0; i < NBUCKETS; i++) + InitializeListHead(&pTable->ListEntry[i]); + + return pTable; +} // NewTable + + + +VOID +FreeHashTableEntry( + PHASH_ENTRY pHashEntry + ) +{ + FREE_MEMORY(pHashEntry); +} // FreeHashTableEntry + + + +VOID +ClearTable( + PHASH_TABLE pTable + ) +{ + KIRQL irql; + INT i; + PLIST_ENTRY pHead; + PHASH_ENTRY pHashEntry; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + for (i = 0; i < NBUCKETS; i++) { + while (!IsListEmpty(&pTable->ListEntry[i])) { + pHead = RemoveHeadList(&pTable->ListEntry[i]); + pHashEntry = CONTAINING_RECORD(pHead, HASH_ENTRY, ListEntry); + + FreeHashTableEntry(pHashEntry); + } + } + KeReleaseSpinLock(&pTable->SpinLock, irql); +} // ClearTable + + + +VOID +FreeTable( + PHASH_TABLE pTable + ) +{ + ClearTable(pTable); + FREE_MEMORY(pTable); +} // FreeTable + + + +VOID +EnumTable( + IN PHASH_TABLE pTable, + IN PHASH_TABLE_ENUM_PROC pProc, + IN PVOID pArg + ) +{ + INT i; + PLIST_ENTRY pEntry; + PHASH_ENTRY pHashEntry; + KIRQL irql; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + for (i = 0; i < NBUCKETS; i++) { + for (pEntry = pTable->ListEntry[i].Flink; + pEntry != &pTable->ListEntry[i]; + pEntry = pEntry->Flink) + { + pHashEntry = CONTAINING_RECORD(pEntry, HASH_ENTRY, ListEntry); + + // + // If the enumerator procedure + // returns FALSE, terminate the + // enumeration. + // + if (!pProc(pArg, &pHashEntry->szKey, pHashEntry->ulData)) + goto done; + } + } +done: + KeReleaseSpinLock(&pTable->SpinLock, irql); +} // EnumTable + + + +INT +HashString( + IN PACD_ADDR pszKey + ) +{ + ULONG ulHashValue = 0; + CHAR ch; + PCSZ p = (PCSZ)pszKey; + + while (*p != L'\0') { + ch = tolower(*p); + ulHashValue += (INT)(ch) * (INT)(ch); + p++; + } + + return (INT)(ulHashValue % NBUCKETS); +} // HashString + + + +BOOLEAN +IsEqualKey( + PACD_ADDR pszKey1, + PACD_ADDR pszKey2 + ) +{ + BOOLEAN fFound; + + fFound = (BOOLEAN)RtlEqualMemory(pszKey1, pszKey2, sizeof (ACD_ADDR)); + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdIsEqualKey(%s, %s) returns %d\n", pszKey1, pszKey2, fFound)); + } + return fFound; +} // IsEqualKey + + + +PHASH_ENTRY +GetTableEntryNL( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey + ) +{ + INT nBucket = HashString(pszKey); + PLIST_ENTRY pEntry; + PHASH_ENTRY pHashEntry; + + for (pEntry = pTable->ListEntry[nBucket].Flink; + pEntry != &pTable->ListEntry[nBucket]; + pEntry = pEntry->Flink) + { + pHashEntry = CONTAINING_RECORD(pEntry, HASH_ENTRY, ListEntry); + + if (IsEqualKey(&pHashEntry->szKey, pszKey)) { + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdGetTableEntryNL(0x%x, %s) returns 0x%x\n", pTable, pszKey, pHashEntry)); + } + return pHashEntry; + } + } + + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdGetTableEntryNL(0x%x, %s) returns NULL\n", pTable, pszKey)); + } + return NULL; +} // GetTableEntryNL + + + +BOOLEAN +GetTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + OUT PULONG pulData + ) +{ + KIRQL irql; + PHASH_ENTRY pHashEntry; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + pHashEntry = GetTableEntryNL(pTable, pszKey); + KeReleaseSpinLock(&pTable->SpinLock, irql); + + if (pHashEntry != NULL) { + if (pulData != NULL) + *pulData = pHashEntry->ulData; + return TRUE; + } + + return FALSE; +} // GetTableEntry + + + +BOOLEAN +PutTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + IN ULONG ulData + ) +{ + KIRQL irql; + BOOLEAN fSuccess = FALSE; + INT nBucket = HashString(pszKey); + PHASH_ENTRY pHashEntry; + + IF_ACDDBG(ACD_DEBUG_TABLE) { + AcdPrint(("AcdPutTableEntry(0x%x, %s)\n", pTable, pszKey)); + } + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + + pHashEntry = GetTableEntryNL(pTable, pszKey); + if (pHashEntry == NULL) { + ALLOCATE_MEMORY(ACD_OBJECT_HASHENTRY, pHashEntry); + if (pHashEntry == NULL) { + DbgPrint("PutTableEntry: ExAllocatePool failed\n"); + goto done; + } + RtlCopyMemory(pHashEntry->szKey, pszKey, sizeof (ACD_ADDR)); + InsertHeadList( + &pTable->ListEntry[nBucket], + &pHashEntry->ListEntry); + } + pHashEntry->ulData = ulData; + fSuccess = TRUE; + +done: + KeReleaseSpinLock(&pTable->SpinLock, irql); + return fSuccess; +} // PutTableEntry + + + +BOOLEAN +DeleteTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey + ) +{ + KIRQL irql; + PHASH_ENTRY pHashEntry; + + KeAcquireSpinLock(&pTable->SpinLock, &irql); + pHashEntry = GetTableEntryNL(pTable, pszKey); + if (pHashEntry != NULL) { + RemoveEntryList(&pHashEntry->ListEntry); + FreeHashTableEntry(pHashEntry); + } + KeReleaseSpinLock(&pTable->SpinLock, irql); + + return (pHashEntry != NULL); +} // DeleteTableEntry diff --git a/private/ntos/tdi/acd/table.h b/private/ntos/tdi/acd/table.h new file mode 100644 index 000000000..17a5ef1a0 --- /dev/null +++ b/private/ntos/tdi/acd/table.h @@ -0,0 +1,77 @@ +/*++ + +Copyright(c) 1995 Microsoft Corporation + +MODULE NAME + table.h + +ABSTRACT + Header file for generic hash table routines. + +AUTHOR + Anthony Discolo (adiscolo) 28-Jul-1995 + +REVISION HISTORY + +--*/ + +// +// Number of hash table buckets. +// +#define NBUCKETS 13 + +// +// Generic hash table structure. +// +typedef struct _HASH_TABLE { + LIST_ENTRY ListEntry[NBUCKETS]; + KSPIN_LOCK SpinLock; +} HASH_TABLE, *PHASH_TABLE; + +// +// Hash table enumerator procedure. +// Returns TRUE to continue enumeration, +// FALSE to terminate enumeration. +// +typedef BOOLEAN (*PHASH_TABLE_ENUM_PROC)(PVOID, PACD_ADDR, ULONG); + + +PHASH_TABLE +NewTable(); + +VOID +ClearTable( + IN PHASH_TABLE pTable + ); + +VOID +FreeTable( + IN PHASH_TABLE pTable + ); + +VOID +EnumTable( + IN PHASH_TABLE pTable, + IN PHASH_TABLE_ENUM_PROC pProc, + IN PVOID pArg + ); + +BOOLEAN +GetTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + OUT PULONG pulData + ); + +BOOLEAN +PutTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey, + IN ULONG ulData + ); + +BOOLEAN +DeleteTableEntry( + IN PHASH_TABLE pTable, + IN PACD_ADDR pszKey + ); diff --git a/private/ntos/tdi/acd/timer.c b/private/ntos/tdi/acd/timer.c new file mode 100644 index 000000000..cf4f3ab63 --- /dev/null +++ b/private/ntos/tdi/acd/timer.c @@ -0,0 +1,145 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + Timer thread to monitor connection progress in the + automatic connection driver (acd.sys). + +Author: + + Anthony Discolo (adiscolo) 25-Apr-1995 + +Environment: + + Kernel Mode + +Revision History: + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "acdapi.h" +#include "table.h" +#include "acddefs.h" +#include "debug.h" + +// +// Imported routines. +// +VOID +AcdSignalCompletionCommon( + IN PACD_CONNECTION pConnection, + IN BOOLEAN fSuccess + ); + +// +// Keep track how long the user-space +// process has been attempting a connection. +// +#define ACD_MAX_TIMER_CALLS 3*60 // 3 minutes + +// +// We give the user-space process +// some slack on missed pings. +// +#define ACD_MAX_MISSED_PINGS 40 // 20 seconds + + + +VOID +AcdConnectionTimer( + IN PDEVICE_OBJECT pDeviceObject, + IN PVOID pContext + ) +{ + PLIST_ENTRY pEntry; + PACD_CONNECTION pConnection; + BOOLEAN bCancel = FALSE; + + // + // Acquire the spin lock. + // We're guaranteed to be at DPC + // since this is a timer routine. + // + KeAcquireSpinLockAtDpcLevel(&AcdSpinLockG); + // + // If the user-space process responsible + // for creating the connection hasn't + // pinged us in a while, or if it hasn't + // created a connection in 3 minutes, + // cancel all the pending requests. + // + for (pEntry = AcdConnectionQueueG.Flink; + pEntry != &AcdConnectionQueueG; + pEntry = pEntry->Flink) + { + pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry); + + IF_ACDDBG(ACD_DEBUG_TIMER) { + PACD_COMPLETION pCompletion; + + AcdPrint(( + "AcdConnectionTimer: pConnection=0x%x, fNotif=%d, szAddr=", + pConnection, + pConnection->fNotif)); + pCompletion = CONTAINING_RECORD(pConnection->CompletionList.Flink, ACD_COMPLETION, ListEntry); + AcdPrintAddress(&pCompletion->notif.addr); + AcdPrint((", nTimerCalls=%d, nMissedPings=%d\n", + pConnection->ulTimerCalls, + pConnection->ulMissedPings)); + } + // + // If we haven't reported the connection to + // user space yet, or it is in the process of + // being completed, then don't time it out. + // + if (!pConnection->fNotif || pConnection->fCompleting) + continue; + + pConnection->ulTimerCalls++; + if (pConnection->fProgressPing) + pConnection->ulMissedPings = 0; + else + pConnection->ulMissedPings++; + if (pConnection->ulTimerCalls >= ACD_MAX_TIMER_CALLS || + pConnection->ulMissedPings >= ACD_MAX_MISSED_PINGS) + { + IF_ACDDBG(ACD_DEBUG_TIMER) { + AcdPrint(( + "AcdConnectionTimer: canceling pConnection=0x%x\n", + pConnection)); + } + // + // Set the completion-in-progress flag so + // this request cannot be completed after + // we release the spin lock. + // + pConnection->fCompleting = TRUE; + bCancel = TRUE; + break; + } + } + // + // Release the spin lock. + // + KeReleaseSpinLockFromDpcLevel(&AcdSpinLockG); + // + // We now process all the canceled requests. + // + if (bCancel) + AcdSignalCompletionCommon(pConnection, FALSE); +} // AcdConnectionTimer + -- cgit v1.2.3