windows-nt/Source/XPSP1/NT/net/sockets/winsock2/wsp/afdsys/eventsel.c

560 lines
13 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
eventsel.c
Abstract:
This module contains routines for supporting the WinSock 2.0
WSAEventSelect() and WSAEnumNetworkEvents() APIs.
Author:
Keith Moore (keithmo) 02-Aug-1995
Revision History:
--*/
#include "afdp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdEventSelect )
#pragma alloc_text( PAGEAFD, AfdEnumNetworkEvents )
#endif
NTSTATUS
AfdEventSelect (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PUINT_PTR Information
)
/*++
Routine Description:
Associates an event object with the socket such that the event object
will be signalled when any of the specified network events becomes
active.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the APC was successfully queued.
--*/
{
NTSTATUS status;
PAFD_ENDPOINT endpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PKEVENT eventObject;
ULONG eventMask;
AFD_EVENT_SELECT_INFO eventInfo;
ULONG previousRecord = 0;
BOOLEAN countsUpdated = FALSE;
*Information = 0;
status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_EVENT_SELECT_INFO32 eventInfo32 = InputBuffer;
if( InputBufferLength < sizeof(*eventInfo32)) {
return STATUS_INVALID_PARAMETER;
}
//
// Validate the input structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForRead (InputBuffer,
sizeof (*eventInfo32),
PROBE_ALIGNMENT32(AFD_EVENT_SELECT_INFO32));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
eventInfo.Event = eventInfo32->Event;
eventInfo.PollEvents = eventInfo32->PollEvents;
}
else
#endif
{
if(InputBufferLength < sizeof(eventInfo)) {
return STATUS_INVALID_PARAMETER;
}
if (RequestorMode != KernelMode ) {
ProbeForRead (InputBuffer,
sizeof (eventInfo),
PROBE_ALIGNMENT(AFD_EVENT_SELECT_INFO));
}
//
// Make local copies of the embeded pointer and parameters
// that we will be using more than once in case malicios
// application attempts to change them while we are
// validating
//
eventInfo = *((PAFD_EVENT_SELECT_INFO)InputBuffer);
}
}
except (AFD_EXCEPTION_FILTER (&status)) {
return status;
}
if ( eventInfo.Event == NULL &&
eventInfo.PollEvents != 0 ) {
return STATUS_INVALID_PARAMETER;
}
//
// Grab the endpoint from the socket handle.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Reference the target event object.
//
eventObject = NULL;
if( eventInfo.Event != NULL ) {
status = AfdReferenceEventObjectByHandle(
eventInfo.Event,
RequestorMode,
(PVOID *)&eventObject
);
if( !NT_SUCCESS(status) ) {
return status;
}
ASSERT( eventObject != NULL );
if (IS_SAN_ENDPOINT (endpoint)) {
//
// Inform the switch that select is active on this endpoint.
//
status = AfdSanPollBegin (endpoint, eventInfo.PollEvents);
if (!NT_SUCCESS (status)) {
ObDereferenceObject (eventObject);
return status;
}
countsUpdated = TRUE;
}
}
//
// Acquire the spinlock protecting the endpoint.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// If this endpoint has an active EventSelect, dereference the
// associated event object.
//
if( endpoint->EventObject != NULL ) {
ObDereferenceObject( endpoint->EventObject );
if (IS_SAN_ENDPOINT (endpoint)) {
previousRecord = endpoint->EventsEnabled;
}
}
//
// Fill in the info.
//
endpoint->EventObject = eventObject;
endpoint->EventsEnabled = eventInfo.PollEvents;
if (countsUpdated) {
endpoint->EventsEnabled |= AFD_POLL_SANCOUNTS_UPDATED;
//
// AfdSanPollBegin puts latest events into
// Endpoint->Common.SanEndp.SelectEventsActive. This is fine for
// select/AsyncSelect, but not for EventSelect. So, if being called
// for the first time, then read current active events from there.
//
if (!(previousRecord & AFD_POLL_SANCOUNTS_UPDATED)) {
endpoint->EventsActive = endpoint->Common.SanEndp.SelectEventsActive;
}
}
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdEventSelect: Endpoint-%p, eventOb-%p, enabled-%lx, active-%lx\n",
endpoint,
eventObject,
endpoint->EventsEnabled,
endpoint->EventsActive
));
}
//
// While we've got the spinlock held, determine if any conditions
// are met, and if so, signal the event object.
//
eventMask = endpoint->EventsActive & endpoint->EventsEnabled;
if( eventMask != 0 && eventObject != NULL ) {
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdEventSelect: Setting event %p\n",
eventObject
));
}
KeSetEvent(
eventObject,
AfdPriorityBoost,
FALSE
);
}
//
// Release the spin lock and return.
//
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
if (previousRecord & AFD_POLL_SANCOUNTS_UPDATED) {
AfdSanPollEnd (endpoint, previousRecord);
}
return STATUS_SUCCESS;
} // AfdEventSelect
NTSTATUS
AfdEnumNetworkEvents (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PUINT_PTR Information
)
/*++
Routine Description:
Retrieves event select information from the socket.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the APC was successfully queued.
--*/
{
NTSTATUS status;
PAFD_ENDPOINT endpoint;
AFD_ENUM_NETWORK_EVENTS_INFO eventInfo;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PKEVENT eventObject;
ULONG pollEvents;
status = STATUS_SUCCESS;
*Information = 0;
//
// Validate the parameters.
//
if(OutputBufferLength < sizeof(eventInfo) ) {
return STATUS_INVALID_PARAMETER;
}
//
// Reference the target event object.
//
eventObject = NULL;
if( InputBuffer != NULL ) {
status = AfdReferenceEventObjectByHandle(
InputBuffer,
RequestorMode,
(PVOID *)&eventObject
);
if( !NT_SUCCESS(status) ) {
return status;
}
ASSERT( eventObject != NULL );
}
//
// Grab the endpoint from the socket handle.
//
endpoint = FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
//
// Acquire the spinlock protecting the endpoint.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdEnumNetworkEvents: endp-%p, eventobj-%p, enabled-%lx, active-%lx\n",
endpoint,
eventObject,
endpoint->EventsEnabled,
endpoint->EventsActive
));
}
//
// Copy the data to the user's structure.
//
pollEvents = endpoint->EventsActive & endpoint->EventsEnabled;
eventInfo.PollEvents = pollEvents;
RtlCopyMemory(
eventInfo.EventStatus,
endpoint->EventStatus,
sizeof(eventInfo.EventStatus)
);
//
// If there was an event object handle passed in with this
// request, reset and dereference it.
//
if( eventObject != NULL ) {
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdEnumNetworkEvents: Resetting event %p\n",
eventObject
));
}
KeResetEvent( eventObject );
ObDereferenceObject( eventObject );
}
//
// Reset internal event record for all the events that
// we could potentially report to the application
//
endpoint->EventsActive &= ~(endpoint->EventsEnabled);
//
// Release the spin lock and return.
//
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
try {
//
// Validate the output structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ProbeForWrite (OutputBuffer,
sizeof (eventInfo),
PROBE_ALIGNMENT (AFD_ENUM_NETWORK_EVENTS_INFO));
}
//
// Copy parameters back to application's memory
//
*((PAFD_ENUM_NETWORK_EVENTS_INFO)OutputBuffer) = eventInfo;
} except( AFD_EXCEPTION_FILTER(&status) ) {
return status;
}
//
// Before returning, tell the I/O subsystem how may bytes to copy
// to the user's output buffer.
//
*Information = sizeof(eventInfo);
return STATUS_SUCCESS;
} // AfdEnumNetworkEvents
VOID
AfdIndicateEventSelectEvent (
IN PAFD_ENDPOINT Endpoint,
IN ULONG PollEventMask,
IN NTSTATUS Status
)
{
ULONG oldEventsActive;
ULONG eventBit;
//
// Sanity check.
//
ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) );
ASSERT (PollEventMask!=0);
ASSERT (((~((1<<AFD_NUM_POLL_EVENTS)-1)) & PollEventMask)==0);
ASSERT( KeGetCurrentIrql() >= DISPATCH_LEVEL );
//
// Note that AFD_POLL_ABORT implies AFD_POLL_SEND.
//
if( PollEventMask & AFD_POLL_ABORT ) {
PollEventMask |= AFD_POLL_SEND;
}
//
// Special handling of send event. Don't record if not enabled
// and disable further indication upon recording (it is only enabled
// after we fail non-blocking send
//
//
// Update the counts for the polls that were issued before
// the endpoint was converted to SAN.
//
if ( IS_SAN_ENDPOINT (Endpoint) &&
!(Endpoint->EventsEnabled & AFD_POLL_SANCOUNTS_UPDATED) &&
Endpoint->Common.SanEndp.LocalContext!=NULL) {
AfdSanPollUpdate (Endpoint, Endpoint->EventsEnabled);
Endpoint->EventsEnabled |= AFD_POLL_SANCOUNTS_UPDATED;
}
if (PollEventMask&AFD_POLL_SEND) {
if (Endpoint->EnableSendEvent) {
Endpoint ->EnableSendEvent = FALSE;
}
else {
PollEventMask &= (~AFD_POLL_SEND);
if (PollEventMask==0) {
return;
}
}
}
//
// Calculate the actual event bit.
//
oldEventsActive = Endpoint->EventsActive;
Endpoint->EventsActive |= PollEventMask;
for (eventBit=0; eventBit<AFD_NUM_POLL_EVENTS; eventBit++) {
if ((1<<eventBit) & PollEventMask) {
Endpoint->EventStatus[eventBit] = Status;
}
}
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdIndicateEventSelectEvent: endp-%p, eventobj-%p, enabled-%lx, active-%lx, indicated-%lx\n",
Endpoint,
Endpoint->EventObject,
Endpoint->EventsEnabled,
Endpoint->EventsActive,
PollEventMask
));
}
//
// Only signal the endpoint's event object if the current event
// is enabled, AND the current event was not already active, AND
// there is an event object associated with this endpoint.
//
PollEventMask &= Endpoint->EventsEnabled & ~oldEventsActive;
if( PollEventMask != 0 && Endpoint->EventObject != NULL ) {
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdIndicateEventSelectEvent: Setting event %p\n",
Endpoint->EventObject
));
}
KeSetEvent(
Endpoint->EventObject,
AfdPriorityBoost,
FALSE
);
}
} // AfdIndicateEventSelectEvent