1918 lines
44 KiB
C
1918 lines
44 KiB
C
/*++
|
||
|
||
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 <ntddk.h>
|
||
//#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"
|
||
|
||
PACD_DISABLED_ADDRESSES pDisabledAddressesG;
|
||
|
||
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Count of outstanding irps - we need to maintain this
|
||
// to limit the number of outstanding requests to acd
|
||
// ow there is a potential of running out of non-paged
|
||
// pool memory.
|
||
//
|
||
LONG lOutstandingRequestsG = 0;
|
||
|
||
// ULONG count = 0;
|
||
|
||
#define MAX_ACD_REQUESTS 100
|
||
|
||
//
|
||
// BOOLEAN that enables autoconnect notifications
|
||
// from redir/CSC.
|
||
//
|
||
extern BOOLEAN fAcdEnableRedirNotifs;
|
||
|
||
//
|
||
// 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);
|
||
|
||
if(pDisabledAddressesG->ulNumAddresses > 1)
|
||
{
|
||
PLIST_ENTRY pEntry;
|
||
PACD_DISABLED_ADDRESS pDisabledAddress;
|
||
|
||
while(pDisabledAddressesG->ulNumAddresses > 1)
|
||
{
|
||
pEntry = pDisabledAddressesG->ListEntry.Flink;
|
||
|
||
RemoveEntryList(
|
||
pDisabledAddressesG->ListEntry.Flink);
|
||
|
||
pDisabledAddress =
|
||
CONTAINING_RECORD(pEntry, ACD_DISABLED_ADDRESS, ListEntry);
|
||
|
||
FREE_MEMORY(pDisabledAddress);
|
||
|
||
pDisabledAddressesG->ulNumAddresses -= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
//
|
||
// Remove it from our list.
|
||
//
|
||
KeAcquireSpinLock(&AcdSpinLockG, &irql);
|
||
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
//
|
||
// Release the spinlock Io Subsystem acquired.
|
||
//
|
||
IoReleaseCancelSpinLock(pIrp->CancelIrql);
|
||
//
|
||
// 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 - note that this
|
||
// should only be called from rasuato service which
|
||
// is a 64 bit process on win64. This should never
|
||
// be called from a 32 bit process so no thunking is
|
||
// done.
|
||
//
|
||
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_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;
|
||
|
||
if(lOutstandingRequestsG >= MAX_ACD_REQUESTS)
|
||
{
|
||
/*
|
||
if(0 == (count % 5))
|
||
{
|
||
count += 1;
|
||
}
|
||
*/
|
||
return FALSE;
|
||
}
|
||
|
||
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.Pid = PsGetCurrentProcessId();
|
||
|
||
// DbgPrint("ACD: request by Process %lx\n",
|
||
// pCompletion->notif.Pid);
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
lOutstandingRequestsG++;
|
||
|
||
//
|
||
// 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
|
||
|
||
BOOLEAN
|
||
FDisabledAddress(
|
||
IN ACD_ADDR *pAddr
|
||
)
|
||
{
|
||
BOOLEAN bRet = FALSE;
|
||
KIRQL irql;
|
||
PACD_DISABLED_ADDRESS pDisabledAddress;
|
||
PLIST_ENTRY pEntry;
|
||
|
||
KeAcquireSpinLock(&AcdSpinLockG, &irql);
|
||
|
||
if(!fAcdEnabledG)
|
||
{
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
return FALSE;
|
||
}
|
||
|
||
for (pEntry = pDisabledAddressesG->ListEntry.Flink;
|
||
pEntry != &pDisabledAddressesG->ListEntry;
|
||
pEntry = pEntry->Flink)
|
||
{
|
||
pDisabledAddress =
|
||
CONTAINING_RECORD(pEntry, ACD_DISABLED_ADDRESS, ListEntry);
|
||
|
||
if(pDisabledAddress->EnableAddress.fDisable &&
|
||
RtlEqualMemory(
|
||
pDisabledAddress->EnableAddress.addr.szInet,
|
||
pAddr->szInet,
|
||
ACD_ADDR_INET_LEN))
|
||
{
|
||
bRet = TRUE;
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
|
||
//DbgPrint("FDisabledAddress: Address %s. Disabled=%d\n",
|
||
// pAddr->szInet, bRet);
|
||
|
||
return bRet;
|
||
}
|
||
|
||
|
||
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];
|
||
ACD_ADDR *pAddr;
|
||
ACD_ADAPTER *pAdapter;
|
||
ULONG ulFlags;
|
||
|
||
//
|
||
// Verify the input buffer is sufficient to hold
|
||
// an ACD_NOTIFICATION (_32) structure.
|
||
//
|
||
#if defined (_WIN64)
|
||
ACD_NOTIFICATION_32 *pNotification32;
|
||
|
||
if(IoIs32bitProcess(pIrp))
|
||
{
|
||
if(pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(ACD_NOTIFICATION_32))
|
||
{
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof (ACD_NOTIFICATION))
|
||
{
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Doing the whole 32 bit stuff for correctness. The code will
|
||
// work even if left alone i.e casting the systembuffer to
|
||
// ACD_NOTIFICATION * [raos].
|
||
//
|
||
#if defined (_WIN64)
|
||
if(IoIs32bitProcess(pIrp))
|
||
{
|
||
pNotification32 = (PACD_NOTIFICATION_32)
|
||
pIrp->AssociatedIrp.SystemBuffer;
|
||
|
||
pAddr = &pNotification32->addr;
|
||
pAdapter = &pNotification32->adapter;
|
||
ulFlags = pNotification32->ulFlags;
|
||
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer;
|
||
pAddr = &pNotification->addr;
|
||
pAdapter = &pNotification->adapter;
|
||
ulFlags = pNotification->ulFlags;
|
||
}
|
||
|
||
if(FDisabledAddress(pAddr))
|
||
{
|
||
//DbgPrint("AcdConnectAddress: returning because address is disabled\n");
|
||
return status;
|
||
}
|
||
|
||
pArgs[0] = pIrp;
|
||
pArgs[1] = pIrpSp;
|
||
//
|
||
// Start the connection.
|
||
//
|
||
IF_ACDDBG(ACD_DEBUG_CONNECTION) {
|
||
AcdPrint(("AcdConnectAddress: "));
|
||
AcdPrintAddress(pAddr);
|
||
AcdPrint((", ulFlags=0x%x\n", ulFlags));
|
||
}
|
||
if (ulFlags & ACD_NOTIFICATION_SUCCESS) {
|
||
AcdNewConnection(
|
||
pAddr,
|
||
pAdapter);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
IoAcquireCancelSpinLock(&irql);
|
||
if (AcdStartConnection(
|
||
0,
|
||
pAddr,
|
||
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
|
||
|
||
NTSTATUS
|
||
AcdQueryState(
|
||
IN PIRP pIrp,
|
||
IN PIO_STACK_LOCATION pIrpSp
|
||
)
|
||
{
|
||
KIRQL irql;
|
||
|
||
if(pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(BOOLEAN))
|
||
{
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
KeAcquireSpinLock(&AcdSpinLockG, &irql);
|
||
|
||
if(fAcdEnableRedirNotifs)
|
||
{
|
||
*(BOOLEAN *)pIrp->AssociatedIrp.SystemBuffer = fAcdEnabledG;
|
||
}
|
||
else
|
||
{
|
||
*(BOOLEAN *)pIrp->AssociatedIrp.SystemBuffer = FALSE;
|
||
}
|
||
|
||
pIrp->IoStatus.Information = sizeof(BOOLEAN);
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
|
||
// KdPrint(("AcdQueryState: returned %d\n",
|
||
// *(BOOLEAN *)pIrp->AssociatedIrp.SystemBuffer));
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
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);
|
||
|
||
lOutstandingRequestsG--;
|
||
|
||
}
|
||
FREE_CONNECTION(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
|
||
|
||
NTSTATUS
|
||
AcdpEnableAddress(PACD_ENABLE_ADDRESS pEnableAddress)
|
||
{
|
||
PLIST_ENTRY pEntry = NULL;
|
||
PACD_DISABLED_ADDRESS pDisabledAddress = NULL;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
KIRQL irql;
|
||
|
||
KeAcquireSpinLock(&AcdSpinLockG, &irql);
|
||
|
||
ASSERT(pDisabledAddressesG->ulNumAddresses >= 1);
|
||
|
||
if(pDisabledAddressesG->ulNumAddresses == 1)
|
||
{
|
||
pDisabledAddress =
|
||
CONTAINING_RECORD(pDisabledAddressesG->ListEntry.Flink,
|
||
ACD_DISABLED_ADDRESS, ListEntry);
|
||
|
||
RtlZeroMemory(&pDisabledAddress->EnableAddress,
|
||
sizeof(ACD_ENABLE_ADDRESS));
|
||
|
||
//DbgPrint("AcdEnableAddress: reenabling\n");
|
||
}
|
||
else if(pDisabledAddressesG->ulNumAddresses > 1)
|
||
{
|
||
for (pEntry = pDisabledAddressesG->ListEntry.Flink;
|
||
pEntry != &pDisabledAddressesG->ListEntry;
|
||
pEntry = pEntry->Flink)
|
||
{
|
||
pDisabledAddress =
|
||
CONTAINING_RECORD(pEntry, ACD_DISABLED_ADDRESS, ListEntry);
|
||
|
||
if(RtlEqualMemory(
|
||
pDisabledAddress->EnableAddress.addr.szInet,
|
||
pEnableAddress->addr.szInet,
|
||
ACD_ADDR_INET_LEN))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(pEntry != &pDisabledAddressesG->ListEntry)
|
||
{
|
||
//DbgPrint("AcdEnableAddress: removing %s (%p) from disabled list\n",
|
||
// pDisabledAddress->EnableAddress.addr.szInet,
|
||
// pDisabledAddress);
|
||
|
||
RemoveEntryList(pEntry);
|
||
pDisabledAddressesG->ulNumAddresses -= 1;
|
||
}
|
||
else
|
||
{
|
||
pEntry = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
|
||
if(pEntry != NULL)
|
||
{
|
||
FREE_MEMORY(pDisabledAddress);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
AcdpDisableAddress(PACD_ENABLE_ADDRESS pEnableAddress)
|
||
{
|
||
PACD_DISABLED_ADDRESS pDisabledAddress;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
KIRQL irql;
|
||
|
||
KeAcquireSpinLock(&AcdSpinLockG, &irql);
|
||
|
||
ASSERT(pDisabledAddressesG->ulNumAddresses >= 1);
|
||
pDisabledAddress =
|
||
CONTAINING_RECORD(pDisabledAddressesG->ListEntry.Flink,
|
||
ACD_DISABLED_ADDRESS, ListEntry);
|
||
|
||
if(!pDisabledAddress->EnableAddress.fDisable)
|
||
{
|
||
RtlCopyMemory(&pDisabledAddress->EnableAddress,
|
||
pEnableAddress,
|
||
sizeof(ACD_ENABLE_ADDRESS));
|
||
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
|
||
}
|
||
else if(pDisabledAddressesG->ulNumAddresses <
|
||
pDisabledAddressesG->ulMaxAddresses)
|
||
{
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
|
||
ALLOCATE_MEMORY(sizeof(ACD_DISABLED_ADDRESS), pDisabledAddress);
|
||
if(pDisabledAddress != NULL)
|
||
{
|
||
RtlCopyMemory(&pDisabledAddress->EnableAddress,
|
||
pEnableAddress,
|
||
sizeof(ACD_ENABLE_ADDRESS));
|
||
|
||
//DbgPrint("AcdEnableAddress: Adding %p to list \n",
|
||
// pDisabledAddress) ;
|
||
|
||
KeAcquireSpinLock(&AcdSpinLockG, &irql);
|
||
InsertTailList(&pDisabledAddressesG->ListEntry,
|
||
&pDisabledAddress->ListEntry);
|
||
pDisabledAddressesG->ulNumAddresses += 1;
|
||
KeReleaseSpinLock(&AcdSpinLockG, irql);
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
//DbgPrint("AcdDisableAddress: Disabling %s, status=0x%x\n",
|
||
// pEnableAddress->addr.szInet, status);
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
AcdEnableAddress(
|
||
IN PIRP pIrp,
|
||
IN PIO_STACK_LOCATION pIrpSp
|
||
)
|
||
{
|
||
PACD_ENABLE_ADDRESS pEnableAddress;
|
||
KIRQL irql;
|
||
PACD_DISABLED_ADDRESS pDisabledAddress = NULL;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
if(pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(ACD_ENABLE_ADDRESS))
|
||
{
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
if(!fAcdEnabledG)
|
||
{
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
pEnableAddress = (PACD_ENABLE_ADDRESS)pIrp->AssociatedIrp.SystemBuffer;
|
||
|
||
if(pEnableAddress->fDisable)
|
||
{
|
||
Status = AcdpDisableAddress(pEnableAddress);
|
||
}
|
||
else
|
||
{
|
||
Status = AcdpEnableAddress(pEnableAddress);
|
||
}
|
||
|
||
//DbgPrint("AcdEnableAddress. status=0x%x\n", Status);
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
|
||
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);
|
||
|
||
lOutstandingRequestsG--;
|
||
|
||
}
|
||
} // 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
|