You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9315 lines
250 KiB
9315 lines
250 KiB
/*++
|
|
|
|
Copyright (c) 1998-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ultdi.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the TDI component.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 15-Jun-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
//
|
|
// Global lists of all active and all waiting-to-be-deleted endpoints.
|
|
//
|
|
|
|
LIST_ENTRY g_TdiEndpointListHead;
|
|
LIST_ENTRY g_TdiDeletedEndpointListHead; // for debugging
|
|
ULONG g_TdiEndpointCount; // #elements in active endpoint list
|
|
|
|
//
|
|
// Global lists of all connections, active, idle, or retiring
|
|
//
|
|
|
|
LIST_ENTRY g_TdiConnectionListHead;
|
|
ULONG g_TdiConnectionCount; // #elements in connection list
|
|
|
|
//
|
|
// Global list of all addresses to use when creating a listening
|
|
// endpoint object
|
|
//
|
|
|
|
ULONG g_TdiListenAddrCount = 0;
|
|
PUL_TRANSPORT_ADDRESS g_pTdiListenAddresses = NULL;
|
|
|
|
//
|
|
// Spinlock protecting the above lists.
|
|
//
|
|
|
|
UL_SPIN_LOCK g_TdiSpinLock;
|
|
|
|
//
|
|
// Global initialization flag.
|
|
//
|
|
|
|
BOOLEAN g_TdiInitialized = FALSE;
|
|
|
|
//
|
|
// Used to wait for endpoints and connections to close on shutdown
|
|
//
|
|
|
|
BOOLEAN g_TdiWaitingForEndpointDrain;
|
|
KEVENT g_TdiEndpointDrainEvent;
|
|
KEVENT g_TdiConnectionDrainEvent;
|
|
|
|
//
|
|
// TDI Send routine if Fast Send is possible.
|
|
//
|
|
|
|
PUL_TCPSEND_DISPATCH g_TcpFastSendIPv4 = NULL;
|
|
PUL_TCPSEND_DISPATCH g_TcpFastSendIPv6 = NULL;
|
|
|
|
//
|
|
// Connection statistics.
|
|
//
|
|
|
|
UL_CONNECTION_STATS g_UlConnectionStats;
|
|
|
|
//
|
|
// The idle list trim timer.
|
|
//
|
|
|
|
UL_TRIM_TIMER g_UlTrimTimer;
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( INIT, UlInitializeTdi )
|
|
#pragma alloc_text( PAGE, UlCloseListeningEndpoint )
|
|
#pragma alloc_text( PAGE, UlpEndpointCleanupWorker )
|
|
#pragma alloc_text( PAGE, UlpDestroyConnectionWorker )
|
|
#pragma alloc_text( PAGE, UlpAssociateConnection )
|
|
#pragma alloc_text( PAGE, UlpDisassociateConnection )
|
|
#pragma alloc_text( PAGE, UlpInitializeAddrIdleList )
|
|
#pragma alloc_text( PAGE, UlpCleanupAddrIdleList )
|
|
#pragma alloc_text( PAGE, UlpReplenishAddrIdleList )
|
|
#pragma alloc_text( PAGE, UlpOptimizeForInterruptModeration )
|
|
#pragma alloc_text( PAGE, UlpSetNagling )
|
|
#pragma alloc_text( PAGE, UlpPopulateIdleList )
|
|
#pragma alloc_text( PAGE, UlpIdleListTrimTimerWorker )
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
#if 0
|
|
NOT PAGEABLE -- UlWaitForEndpointDrain
|
|
NOT PAGEABLE -- UlCreateListeningEndpoint
|
|
NOT PAGEABLE -- UlCloseConnection
|
|
NOT PAGEABLE -- UlReceiveData
|
|
NOT PAGEABLE -- UlSendData
|
|
NOT PAGEABLE -- UlAddSiteToEndpointList
|
|
NOT PAGEABLE -- UlRemoveSiteFromEndpointList
|
|
NOT PAGEABLE -- UlpReplenishAddrIdleListWorker
|
|
NOT PAGEABLE -- UlpDestroyEndpoint
|
|
NOT PAGEABLE -- UlpDestroyConnection
|
|
NOT PAGEABLE -- UlpDequeueIdleConnectionToDrain
|
|
NOT PAGEABLE -- UlpDequeueIdleConnection
|
|
NOT PAGEABLE -- UlpEnqueueActiveConnection
|
|
NOT PAGEABLE -- UlpConnectHandler
|
|
NOT PAGEABLE -- UlpDisconnectHandler
|
|
NOT PAGEABLE -- UlpDoDisconnectNotification
|
|
NOT PAGEABLE -- UlpCloseRawConnection
|
|
NOT PAGEABLE -- UlpQueryTcpFastSend
|
|
NOT PAGEABLE -- UlpSendRawData
|
|
NOT PAGEABLE -- UlpReceiveRawData
|
|
NOT PAGEABLE -- UlpReceiveHandler
|
|
NOT PAGEABLE -- UlpDummyReceiveHandler
|
|
NOT PAGEABLE -- UlpReceiveExpeditedHandler
|
|
NOT PAGEABLE -- UlpRestartAccept
|
|
NOT PAGEABLE -- UlpRestartSendData
|
|
NOT PAGEABLE -- UlpReferenceEndpoint
|
|
NOT PAGEABLE -- UlpDereferenceEndpoint
|
|
NOT PAGEABLE -- UlReferenceConnection
|
|
NOT PAGEABLE -- UlDereferenceConnection
|
|
NOT PAGEABLE -- UlpCleanupConnectionId
|
|
NOT PAGEABLE -- UlpCleanupEarlyConnection
|
|
NOT PAGEABLE -- UlpConnectionCleanupWorker
|
|
NOT PAGEABLE -- UlpCreateConnection
|
|
NOT PAGEABLE -- UlpInitializeConnection
|
|
NOT PAGEABLE -- UlpBeginDisconnect
|
|
NOT PAGEABLE -- UlpRestartDisconnect
|
|
NOT PAGEABLE -- UlpBeginAbort
|
|
NOT PAGEABLE -- UlpRestartAbort
|
|
NOT PAGEABLE -- UlpRemoveFinalReference
|
|
NOT PAGEABLE -- UlpRestartReceive
|
|
NOT PAGEABLE -- UlpRestartClientReceive
|
|
NOT PAGEABLE -- UlpDisconnectAllActiveConnections
|
|
NOT PAGEABLE -- UlpUnbindConnectionFromEndpoint
|
|
NOT PAGEABLE -- UlpSynchronousIoComplete
|
|
NOT PAGEABLE -- UlpFindEndpointForPort
|
|
NOT PAGEABLE -- UlpRestartQueryAddress
|
|
NOT PAGEABLE -- UlpIdleListTrimTimerDpcRoutine
|
|
NOT PAGEABLE -- UlpSetIdleListTrimTimer
|
|
NOT PAGEABLE -- UlCheckListeningEndpointState
|
|
NOT PAGEABLE -- UlpIsUrlRouteableInListenScope
|
|
#endif
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Performs global initialization of this module.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlInitializeTdi(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( !g_TdiInitialized );
|
|
|
|
//
|
|
// Initialize global data.
|
|
//
|
|
|
|
InitializeListHead( &g_TdiEndpointListHead );
|
|
InitializeListHead( &g_TdiDeletedEndpointListHead );
|
|
InitializeListHead( &g_TdiConnectionListHead );
|
|
UlInitializeSpinLock( &g_TdiSpinLock, "g_TdiSpinLock" );
|
|
|
|
g_TdiEndpointCount = 0;
|
|
g_TdiConnectionCount = 0;
|
|
|
|
RtlZeroMemory(&g_UlConnectionStats, sizeof(g_UlConnectionStats));
|
|
|
|
KeInitializeEvent(
|
|
&g_TdiEndpointDrainEvent,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
KeInitializeEvent(
|
|
&g_TdiConnectionDrainEvent,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if (g_UlIdleConnectionsHighMark == DEFAULT_IDLE_CONNECTIONS_HIGH_MARK)
|
|
{
|
|
//
|
|
// Let's start with one connection per 2 MB and enforce a 64-512 range.
|
|
//
|
|
|
|
g_UlIdleConnectionsHighMark = (USHORT) g_UlTotalPhysicalMemMB / 2;
|
|
g_UlIdleConnectionsHighMark = MAX(64, g_UlIdleConnectionsHighMark);
|
|
g_UlIdleConnectionsHighMark = MIN(512, g_UlIdleConnectionsHighMark);
|
|
}
|
|
|
|
if (g_UlIdleConnectionsLowMark == DEFAULT_IDLE_CONNECTIONS_LOW_MARK)
|
|
{
|
|
//
|
|
// To reduce the NPP usage on inactive endpoints. Pick the low mark
|
|
// as small as possible, however not too small to surface a connection
|
|
// drop problem. If we pick 1/4 of high, this would give us a range
|
|
// of (16 .. 128).
|
|
//
|
|
|
|
g_UlIdleConnectionsLowMark = g_UlIdleConnectionsHighMark / 8;
|
|
}
|
|
|
|
UlTrace(TDI_STATS, (
|
|
"UlInitializeTdi ...\n"
|
|
"\tg_UlTotalPhysicalMemMB : %d\n"
|
|
"\tg_UlIdleConnectionsLowMark : %d\n"
|
|
"\tg_UlIdleConnectionsHighMark: %d\n",
|
|
g_UlTotalPhysicalMemMB,
|
|
g_UlIdleConnectionsLowMark,
|
|
g_UlIdleConnectionsHighMark
|
|
));
|
|
|
|
if (g_UlMaxEndpoints == DEFAULT_MAX_ENDPOINTS)
|
|
{
|
|
//
|
|
// Compute a default based on physical memory. This starts at 16
|
|
// for a 64MB machine and it is capped at 64 for a 256MB+ machine.
|
|
//
|
|
|
|
g_UlMaxEndpoints = (USHORT) g_UlTotalPhysicalMemMB / 4;
|
|
g_UlMaxEndpoints = MIN(64, g_UlMaxEndpoints);
|
|
}
|
|
|
|
//
|
|
// Init the idle list trim timer.
|
|
//
|
|
|
|
g_UlTrimTimer.Initialized = TRUE;
|
|
g_UlTrimTimer.Started = FALSE;
|
|
|
|
UlInitializeSpinLock(&g_UlTrimTimer.SpinLock,"IdleListTrimTimerSpinLock");
|
|
|
|
KeInitializeDpc(&g_UlTrimTimer.DpcObject,
|
|
&UlpIdleListTrimTimerDpcRoutine,
|
|
NULL
|
|
);
|
|
|
|
KeInitializeTimer(&g_UlTrimTimer.Timer);
|
|
|
|
UlInitializeWorkItem(&g_UlTrimTimer.WorkItem);
|
|
g_UlTrimTimer.WorkItemScheduled = FALSE;
|
|
InitializeListHead(&g_UlTrimTimer.ZombieConnectionListHead);
|
|
|
|
//
|
|
// Init list of addresses to listen on if it hasn't been done already
|
|
//
|
|
|
|
if ( !g_pTdiListenAddresses )
|
|
{
|
|
PUL_TRANSPORT_ADDRESS pTa, pTaCurrent;
|
|
USHORT ip6addr_any[8] = { 0 };
|
|
|
|
//
|
|
// Allocate for two addresses, INADDR_ANY and in6addr_any.
|
|
//
|
|
// CODEWORK: check if IPv6 is enabled to see if we should add IPv6
|
|
// address.
|
|
|
|
pTa = UL_ALLOCATE_ARRAY(
|
|
NonPagedPool,
|
|
UL_TRANSPORT_ADDRESS,
|
|
2,
|
|
UL_TRANSPORT_ADDRESS_POOL_TAG
|
|
);
|
|
|
|
if (pTa == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
g_TdiListenAddrCount = 2;
|
|
g_pTdiListenAddresses = pTa;
|
|
|
|
RtlZeroMemory( pTa, g_TdiListenAddrCount * sizeof(UL_TRANSPORT_ADDRESS) );
|
|
pTaCurrent = &pTa[0];
|
|
|
|
UlInitializeIpTransportAddress( &(pTa[0].TaIp), 0L, 0 );
|
|
UlInitializeIp6TransportAddress( &(pTa[1].TaIp6), ip6addr_any, 0, 0 );
|
|
}
|
|
|
|
// NOTE: we always want to be dual stack (IPv4/IPv6).
|
|
|
|
status = UlpQueryTcpFastSend(DD_TCP_DEVICE_NAME, &g_TcpFastSendIPv4);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
NTSTATUS status6;
|
|
|
|
status6 = UlpQueryTcpFastSend(DD_TCPV6_DEVICE_NAME, &g_TcpFastSendIPv6);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
g_TdiInitialized = TRUE;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlInitializeTdi
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Performs global termination of this module.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlTerminateTdi(
|
|
VOID
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
if (g_TdiInitialized)
|
|
{
|
|
UlTrace(TDI,
|
|
("UlTerminateTdi: connections refused:\n"
|
|
"\tTotalConnections=%lu\n"
|
|
"\tGlobalLimit=%lu\n"
|
|
"\tEndpointDying=%lu\n"
|
|
"\tNoIdleConn=%lu\n",
|
|
g_UlConnectionStats.TotalConnections,
|
|
g_UlConnectionStats.GlobalLimit,
|
|
g_UlConnectionStats.EndpointDying,
|
|
g_UlConnectionStats.NoIdleConn
|
|
));
|
|
|
|
ASSERT( IsListEmpty( &g_TdiEndpointListHead )) ;
|
|
ASSERT( IsListEmpty( &g_TdiDeletedEndpointListHead )) ;
|
|
ASSERT( IsListEmpty( &g_TdiConnectionListHead )) ;
|
|
ASSERT( g_TdiEndpointCount == 0 );
|
|
ASSERT( g_TdiConnectionCount == 0 );
|
|
|
|
UlAcquireSpinLock(&g_UlTrimTimer.SpinLock, &OldIrql);
|
|
|
|
g_UlTrimTimer.Initialized = FALSE;
|
|
|
|
KeCancelTimer(&g_UlTrimTimer.Timer);
|
|
|
|
UlReleaseSpinLock(&g_UlTrimTimer.SpinLock, OldIrql);
|
|
|
|
g_TdiInitialized = FALSE;
|
|
}
|
|
|
|
} // UlTerminateTdi
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
One minute idle timer for trimming the idle list of each endpoint.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
--***************************************************************************/
|
|
|
|
VOID
|
|
UlpSetIdleListTrimTimer(
|
|
VOID
|
|
)
|
|
{
|
|
LONGLONG BufferPeriodTime100Ns;
|
|
LONG BufferPeriodTimeMs;
|
|
LARGE_INTEGER BufferPeriodTime;
|
|
|
|
//
|
|
// Remaining time to next tick. Default value is in seconds.
|
|
//
|
|
|
|
BufferPeriodTimeMs = g_UlIdleListTrimmerPeriod * 1000;
|
|
BufferPeriodTime100Ns = (LONGLONG) BufferPeriodTimeMs * 10 * 1000;
|
|
|
|
//
|
|
// Negative time for relative value.
|
|
//
|
|
|
|
BufferPeriodTime.QuadPart = -BufferPeriodTime100Ns;
|
|
|
|
KeSetTimerEx(
|
|
&g_UlTrimTimer.Timer,
|
|
BufferPeriodTime, // Must be in nanosec
|
|
BufferPeriodTimeMs, // Must be in millisec
|
|
&g_UlTrimTimer.DpcObject
|
|
);
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function blocks until the endpoint list is empty. It also prevents
|
|
new endpoints from being created.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlWaitForEndpointDrain(
|
|
VOID
|
|
)
|
|
{
|
|
KIRQL oldIrql;
|
|
BOOLEAN WaitConnection = FALSE;
|
|
BOOLEAN WaitEndpoint = FALSE;
|
|
ULONG WaitCount = 0;
|
|
|
|
if (g_TdiInitialized)
|
|
{
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
|
|
|
|
if (!g_TdiWaitingForEndpointDrain)
|
|
{
|
|
g_TdiWaitingForEndpointDrain = TRUE;
|
|
}
|
|
|
|
if (g_TdiEndpointCount > 0)
|
|
{
|
|
WaitEndpoint = TRUE;
|
|
}
|
|
|
|
if (g_TdiConnectionCount > 0)
|
|
{
|
|
WaitConnection = TRUE;
|
|
}
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
|
|
if (WaitConnection || WaitEndpoint)
|
|
{
|
|
PVOID Events[2];
|
|
|
|
if (WaitEndpoint && WaitConnection)
|
|
{
|
|
Events[0] = &g_TdiEndpointDrainEvent;
|
|
Events[1] = &g_TdiConnectionDrainEvent;
|
|
WaitCount = 2;
|
|
}
|
|
else
|
|
{
|
|
if (WaitEndpoint)
|
|
{
|
|
Events[0] = &g_TdiEndpointDrainEvent;
|
|
}
|
|
else
|
|
{
|
|
Events[0] = &g_TdiConnectionDrainEvent;
|
|
}
|
|
|
|
Events[1] = NULL;
|
|
WaitCount = 1;
|
|
}
|
|
|
|
KeWaitForMultipleObjects(
|
|
WaitCount,
|
|
Events,
|
|
WaitAll,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
} // UlWaitForEndpointDrain
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new listening endpoint bound to the specified port on
|
|
all available TDI addresses (see: g_TdiListenAddresses and
|
|
g_TdiListenAddressCount).
|
|
|
|
Arguments:
|
|
|
|
Port - TCP Port for this endpoint.
|
|
|
|
InitialBacklog - Supplies the initial number of idle connections
|
|
to add to the endpoint.
|
|
|
|
pConnectionRequestHandler - Supplies a pointer to an indication
|
|
handler to invoke when incoming connections arrive.
|
|
|
|
pConnectionCompleteHandler - Supplies a pointer to an indication
|
|
handler to invoke when either a) the incoming connection is
|
|
fully accepted, or b) the incoming connection could not be
|
|
accepted due to a fatal error.
|
|
|
|
pConnectionDisconnectHandler - Supplies a pointer to an indication
|
|
handler to invoke when connections are disconnected by the
|
|
remote (client) side.
|
|
|
|
pConnectionDestroyedHandler - Supplies a pointer to an indication
|
|
handle to invoke after a connection has been fully destroyed.
|
|
This is typically the TDI client's opportunity to cleanup
|
|
any allocated resources.
|
|
|
|
pDataReceiveHandler - Supplies a pointer to an indication handler to
|
|
invoke when incoming data arrives.
|
|
|
|
pListeningContext - Supplies an uninterpreted context value to
|
|
associate with the new listening endpoint.
|
|
|
|
ppListeningEndpoint - Receives a pointer to the new listening
|
|
endpoint if successful.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlCreateListeningEndpoint(
|
|
IN PHTTP_PARSED_URL pParsedUrl,
|
|
IN PUL_CONNECTION_REQUEST pConnectionRequestHandler,
|
|
IN PUL_CONNECTION_COMPLETE pConnectionCompleteHandler,
|
|
IN PUL_CONNECTION_DISCONNECT pConnectionDisconnectHandler,
|
|
IN PUL_CONNECTION_DISCONNECT_COMPLETE pConnectionDisconnectCompleteHandler,
|
|
IN PUL_CONNECTION_DESTROYED pConnectionDestroyedHandler,
|
|
IN PUL_DATA_RECEIVE pDataReceiveHandler,
|
|
OUT PUL_ENDPOINT *ppListeningEndpoint
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_ENDPOINT pEndpoint;
|
|
ULONG i;
|
|
ULONG AddrIdleListSize;
|
|
ULONG FailedAddrIdleList;
|
|
KIRQL OldIrql;
|
|
WCHAR IpAddressString[MAX_IP_ADDR_AND_PORT_STRING_LEN + 1];
|
|
USHORT BytesWritten;
|
|
USHORT Port;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( pParsedUrl );
|
|
|
|
Port = SWAP_SHORT(pParsedUrl->PortNumber);
|
|
|
|
ASSERT( Port > 0 );
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on a fatal exit.
|
|
//
|
|
|
|
pEndpoint = NULL;
|
|
|
|
if (!g_pTdiListenAddresses || (0 == g_TdiListenAddrCount))
|
|
{
|
|
// Fail. We have failed to initialize properly.
|
|
// REVIEW: is there a better return code in this case?
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Allocate enough pool for the endpoint structure and the
|
|
// array of UL_ADDR_IDLE_LISTs.
|
|
//
|
|
|
|
AddrIdleListSize = g_TdiListenAddrCount * sizeof(UL_ADDR_IDLE_LIST);
|
|
pEndpoint = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
NonPagedPool,
|
|
UL_ENDPOINT,
|
|
AddrIdleListSize,
|
|
UL_ENDPOINT_POOL_TAG
|
|
);
|
|
|
|
if (pEndpoint == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Initialize the easy parts.
|
|
//
|
|
|
|
pEndpoint->Signature = UL_ENDPOINT_SIGNATURE;
|
|
pEndpoint->ReferenceCount = 0;
|
|
pEndpoint->UsageCount = 1;
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pEndpointUsageTraceLog,
|
|
REF_ACTION_REFERENCE_ENDPOINT_USAGE,
|
|
pEndpoint->UsageCount,
|
|
pEndpoint,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
UlInitializeWorkItem(&pEndpoint->WorkItem);
|
|
pEndpoint->WorkItemScheduled = FALSE;
|
|
|
|
REFERENCE_ENDPOINT(pEndpoint, REF_ACTION_INIT);
|
|
REFERENCE_ENDPOINT(pEndpoint, REF_ACTION_ENDPOINT_USAGE_REFERENCE);
|
|
REFERENCE_ENDPOINT(pEndpoint, REF_ACTION_ENDPOINT_EVENT_REFERENCE);
|
|
|
|
pEndpoint->pConnectionRequestHandler = pConnectionRequestHandler;
|
|
pEndpoint->pConnectionCompleteHandler = pConnectionCompleteHandler;
|
|
pEndpoint->pConnectionDisconnectHandler = pConnectionDisconnectHandler;
|
|
pEndpoint->pConnectionDisconnectCompleteHandler = pConnectionDisconnectCompleteHandler;
|
|
pEndpoint->pConnectionDestroyedHandler = pConnectionDestroyedHandler;
|
|
pEndpoint->pDataReceiveHandler = pDataReceiveHandler;
|
|
pEndpoint->pListeningContext = (PVOID)pEndpoint;
|
|
|
|
pEndpoint->LocalPort = Port;
|
|
pEndpoint->Secure = pParsedUrl->Secure;
|
|
pEndpoint->Counted = FALSE;
|
|
pEndpoint->Deleted = FALSE;
|
|
pEndpoint->GlobalEndpointListEntry.Flink = NULL;
|
|
|
|
RtlZeroMemory(
|
|
&pEndpoint->CleanupIrpContext,
|
|
sizeof(UL_IRP_CONTEXT)
|
|
);
|
|
|
|
pEndpoint->CleanupIrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
|
|
|
|
//
|
|
// build array of Listening Address Objects
|
|
//
|
|
|
|
ASSERT( g_TdiListenAddrCount > 0 );
|
|
pEndpoint->AddrIdleListCount = g_TdiListenAddrCount;
|
|
// Address Idle Lists immediately follow the UL_ENDPOINT object
|
|
pEndpoint->aAddrIdleLists = (PUL_ADDR_IDLE_LIST)(&pEndpoint[1]);
|
|
|
|
RtlZeroMemory(
|
|
pEndpoint->aAddrIdleLists,
|
|
AddrIdleListSize
|
|
);
|
|
|
|
FailedAddrIdleList = 0;
|
|
|
|
for ( i = 0; i < pEndpoint->AddrIdleListCount; i++ )
|
|
{
|
|
status = UlpInitializeAddrIdleList(
|
|
pEndpoint,
|
|
Port,
|
|
&g_pTdiListenAddresses[i],
|
|
&pEndpoint->aAddrIdleLists[i]
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// NOTE: STATUS_OBJECT_NOT_FOUND is returned if the underlying
|
|
// transport is not present on the system (e.g. IPv6).
|
|
//
|
|
|
|
if(status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
PUL_TRANSPORT_ADDRESS pTa;
|
|
|
|
pTa = &pEndpoint->aAddrIdleLists[i].LocalAddress;
|
|
|
|
BytesWritten =
|
|
HostAddressAndPortToStringW(
|
|
IpAddressString,
|
|
pTa->Ta.Address->Address,
|
|
pTa->Ta.Address->AddressType
|
|
);
|
|
|
|
ASSERT(BytesWritten <=
|
|
(MAX_IP_ADDR_AND_PORT_STRING_LEN * sizeof(WCHAR)));
|
|
|
|
UlEventLogOneStringEntry(
|
|
EVENT_HTTP_CREATE_ENDPOINT_FAILED,
|
|
IpAddressString,
|
|
TRUE,
|
|
status
|
|
);
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
FailedAddrIdleList++;
|
|
continue; // ignore; makes cleanup easier.
|
|
}
|
|
|
|
//
|
|
// Replenish the idle connection pool.
|
|
//
|
|
status = UlpReplenishAddrIdleList(
|
|
&pEndpoint->aAddrIdleLists[i],
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto fatal;
|
|
}
|
|
}
|
|
|
|
//
|
|
// see if we got at least one valid AO
|
|
//
|
|
|
|
if ( FailedAddrIdleList == pEndpoint->AddrIdleListCount )
|
|
{
|
|
// No valid AO's created; fail the endpoint creation!
|
|
status = STATUS_INVALID_ADDRESS;
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Put the endpoint onto the global list.
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
|
|
|
|
//
|
|
// Check if we have exceeded the g_UlMaxEndpoints limit.
|
|
//
|
|
|
|
if (g_TdiEndpointCount >= g_UlMaxEndpoints)
|
|
{
|
|
status = STATUS_ALLOTTED_SPACE_EXCEEDED;
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
goto fatal;
|
|
}
|
|
|
|
InsertTailList(
|
|
&g_TdiEndpointListHead,
|
|
&pEndpoint->GlobalEndpointListEntry
|
|
);
|
|
|
|
g_TdiEndpointCount++;
|
|
pEndpoint->Counted = TRUE;
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
|
|
//
|
|
// Now we have at least one endpoint, kick the idle timer to action.
|
|
//
|
|
|
|
UlAcquireSpinLock(&g_UlTrimTimer.SpinLock, &OldIrql);
|
|
if (g_UlTrimTimer.Started == FALSE)
|
|
{
|
|
UlpSetIdleListTrimTimer();
|
|
g_UlTrimTimer.Started = TRUE;
|
|
}
|
|
UlReleaseSpinLock(&g_UlTrimTimer.SpinLock, OldIrql);
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
UlTrace(TDI, (
|
|
"UlCreateListeningEndpoint: endpoint %p, port %d\n",
|
|
pEndpoint,
|
|
SWAP_SHORT(Port)
|
|
));
|
|
|
|
*ppListeningEndpoint = pEndpoint;
|
|
return STATUS_SUCCESS;
|
|
|
|
fatal:
|
|
|
|
ASSERT( !NT_SUCCESS(status) );
|
|
|
|
if (pEndpoint != NULL)
|
|
{
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList = pEndpoint->aAddrIdleLists;
|
|
|
|
//
|
|
// Remove connect event handler so we won't get any more
|
|
// indications that could add a reference.
|
|
//
|
|
// These calls could fail, but there's basically nothing
|
|
// we can do about it if they do.
|
|
//
|
|
|
|
for ( i = 0; i < pEndpoint->AddrIdleListCount; i++ )
|
|
{
|
|
if (pAddrIdleList->AddressObject.pDeviceObject)
|
|
{
|
|
//
|
|
// Close the TDI object.
|
|
//
|
|
|
|
UxCloseTdiObject( &pAddrIdleList->AddressObject );
|
|
}
|
|
|
|
pAddrIdleList++;
|
|
}
|
|
|
|
//
|
|
// Release the three references on the endpoint, which
|
|
// will cause it to destroy itself.
|
|
//
|
|
|
|
ASSERT( 3 == pEndpoint->ReferenceCount );
|
|
pEndpoint->UsageCount = 0; // to prevent assertions
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pEndpointUsageTraceLog,
|
|
REF_ACTION_DEREFERENCE_ENDPOINT_USAGE,
|
|
pEndpoint->UsageCount,
|
|
pEndpoint,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
DEREFERENCE_ENDPOINT_SELF(
|
|
pEndpoint,
|
|
REF_ACTION_ENDPOINT_EVENT_DEREFERENCE
|
|
);
|
|
DEREFERENCE_ENDPOINT_SELF(
|
|
pEndpoint, REF_ACTION_ENDPOINT_USAGE_DEREFERENCE
|
|
);
|
|
DEREFERENCE_ENDPOINT_SELF(
|
|
pEndpoint, REF_ACTION_FINAL_DEREF
|
|
);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlCreateListeningEndpoint
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Closes an existing listening endpoint.
|
|
|
|
Arguments:
|
|
|
|
pListeningEndpoint - Supplies a pointer to a listening endpoint
|
|
previously created with UlCreateListeningEndpoint().
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the listening endpoint is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlCloseListeningEndpoint(
|
|
IN PUL_ENDPOINT pListeningEndpoint,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
NTSTATUS status;
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList;
|
|
PUL_CONNECTION pConnection;
|
|
ULONG i;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pListeningEndpoint ) );
|
|
ASSERT( pCompletionRoutine != NULL );
|
|
|
|
UlTrace(TDI, (
|
|
"UlCloseListeningEndpoint: endpoint %p, completion %p, ctx %p\n",
|
|
pListeningEndpoint,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
));
|
|
|
|
//
|
|
// Remember completion information to be used when
|
|
// we're done cleaning up.
|
|
//
|
|
|
|
pIrpContext = &pListeningEndpoint->CleanupIrpContext;
|
|
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->pOwnIrp = NULL;
|
|
pIrpContext->OwnIrpContext = TRUE;
|
|
|
|
//
|
|
// Remove connect event handler so we won't get any more
|
|
// indications that could add a reference.
|
|
//
|
|
// These calls could fail, but there's basically nothing
|
|
// we can do about it if they do.
|
|
//
|
|
// Once we're done we remove the reference we held
|
|
// on the endpoint for the handlers.
|
|
//
|
|
|
|
pAddrIdleList = pListeningEndpoint->aAddrIdleLists;
|
|
for ( i = 0; i < pListeningEndpoint->AddrIdleListCount; i++ )
|
|
{
|
|
if ( pAddrIdleList->AddressObject.pDeviceObject )
|
|
{
|
|
//
|
|
// Close the TDI Address Object to flush all outstanding
|
|
// completions.
|
|
//
|
|
|
|
UxCloseTdiObject( &pAddrIdleList->AddressObject );
|
|
|
|
//
|
|
// Destroy as many idle connections as possible.
|
|
//
|
|
|
|
while ( NULL != ( pConnection = UlpDequeueIdleConnectionToDrain(
|
|
pAddrIdleList
|
|
) ) )
|
|
{
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
UlpDestroyConnection( pConnection );
|
|
}
|
|
}
|
|
|
|
pAddrIdleList++;
|
|
}
|
|
|
|
DEREFERENCE_ENDPOINT_SELF(
|
|
pListeningEndpoint,
|
|
REF_ACTION_ENDPOINT_EVENT_DEREFERENCE
|
|
);
|
|
|
|
//
|
|
// Let UlpDisconnectAllActiveConnections do the dirty work.
|
|
//
|
|
|
|
status = UlpDisconnectAllActiveConnections( pListeningEndpoint );
|
|
|
|
return status;
|
|
|
|
} // UlCloseListeningEndpoint
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Closes a previously accepted connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
AbortiveDisconnect - Supplies TRUE if the connection is to be abortively
|
|
disconnected, FALSE if it should be gracefully disconnected.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the connection is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlCloseConnection(
|
|
IN PUL_CONNECTION pConnection,
|
|
IN BOOLEAN AbortiveDisconnect,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlCloseConnection: connection %p, abort %lu\n",
|
|
pConnection,
|
|
(ULONG)AbortiveDisconnect
|
|
));
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
(USHORT) (AbortiveDisconnect
|
|
? REF_ACTION_CLOSE_UL_CONN_ABORTIVE
|
|
: REF_ACTION_CLOSE_UL_CONN_GRACEFUL),
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// We only send graceful disconnects through the filter
|
|
// process. There's also no point in going through the
|
|
// filter if the connection is already being closed or
|
|
// aborted.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel &&
|
|
!pConnection->ConnectionFlags.CleanupBegun &&
|
|
!pConnection->ConnectionFlags.AbortIndicated &&
|
|
!AbortiveDisconnect)
|
|
{
|
|
//
|
|
// Send graceful disconnect through the filter process.
|
|
//
|
|
status = UlFilterCloseHandler(
|
|
&pConnection->FilterInfo,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Really close the connection.
|
|
//
|
|
|
|
status = UlpCloseRawConnection(
|
|
pConnection,
|
|
AbortiveDisconnect,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlCloseConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Sends a block of data on the specified connection. If the connection
|
|
is filtered, the data will be sent to the filter first.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pMdlChain - Supplies a pointer to a MDL chain describing the
|
|
data buffers to send.
|
|
|
|
Length - Supplies the length of the data referenced by the MDL
|
|
chain.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the data is sent.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
InitiateDisconnect - Supplies TRUE if a graceful disconnect should
|
|
be initiated immediately after initiating the send (i.e. before
|
|
the send actually completes).
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlSendData(
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PMDL pMdlChain,
|
|
IN ULONG Length,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext,
|
|
IN PIRP pOwnIrp,
|
|
IN PUL_IRP_CONTEXT pOwnIrpContext,
|
|
IN BOOLEAN InitiateDisconnect,
|
|
IN BOOLEAN RequestComplete
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
ASSERT( pMdlChain != NULL );
|
|
ASSERT( Length > 0 );
|
|
ASSERT( pCompletionRoutine != NULL );
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: connection %p, mdl %p, length %lu\n",
|
|
pConnection,
|
|
pMdlChain,
|
|
Length
|
|
));
|
|
|
|
//
|
|
// Connection should be around until we make a call to close connection.
|
|
// Otherwise if the send completes inline, it may free
|
|
// up the connection reference when we enter this function, causing
|
|
// us to reference a stale connection pointer when calling disconnect.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
|
|
//
|
|
// Allocate & initialize a context structure if necessary.
|
|
//
|
|
|
|
if (pOwnIrpContext == NULL)
|
|
{
|
|
pIrpContext = UlPplAllocateIrpContext();
|
|
}
|
|
else
|
|
{
|
|
ASSERT( pOwnIrp != NULL );
|
|
pIrpContext = pOwnIrpContext;
|
|
}
|
|
|
|
if (pIrpContext == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID) pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->pOwnIrp = pOwnIrp;
|
|
pIrpContext->OwnIrpContext = (BOOLEAN) (pOwnIrpContext != NULL);
|
|
|
|
//
|
|
// Try to send the data. This send operation may complete inline
|
|
// fast, if the connection has already been aborted by the client
|
|
// In that case connection may gone away. To prevent this we
|
|
// keep additional refcount until we make a call to close connection
|
|
// below.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// First go through the filter.
|
|
//
|
|
|
|
Status = UlFilterSendHandler(
|
|
&pConnection->FilterInfo,
|
|
pMdlChain,
|
|
Length,
|
|
pIrpContext
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: sent filtered data, status = 0x%x\n",
|
|
Status
|
|
));
|
|
|
|
ASSERT( Status == STATUS_PENDING );
|
|
}
|
|
else
|
|
{
|
|
if (RequestComplete &&
|
|
pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
|
|
{
|
|
//
|
|
// Just send it directly to the network.
|
|
//
|
|
|
|
Status = UlpSendRawData(
|
|
pConnection,
|
|
pMdlChain,
|
|
Length,
|
|
pIrpContext,
|
|
InitiateDisconnect
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: sent raw data with disconnect, status = 0x%x\n",
|
|
Status
|
|
));
|
|
|
|
InitiateDisconnect = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just send it directly to the network.
|
|
//
|
|
|
|
Status = UlpSendRawData(
|
|
pConnection,
|
|
pMdlChain,
|
|
Length,
|
|
pIrpContext,
|
|
FALSE
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: sent raw data, status = 0x%x\n",
|
|
Status
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Now that the send is "in flight", initiate a disconnect if
|
|
// so requested.
|
|
//
|
|
|
|
if (InitiateDisconnect)
|
|
{
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_CLOSE_UL_CONN_GRACEFUL,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
(VOID) UlCloseConnection(
|
|
pConnection,
|
|
FALSE, // AbortiveDisconnect
|
|
NULL, // pCompletionRoutine
|
|
NULL // pCompletionContext
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: closed conn\n"
|
|
));
|
|
}
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
return STATUS_PENDING;
|
|
|
|
fatal:
|
|
|
|
ASSERT( !NT_SUCCESS(Status) );
|
|
|
|
if (pIrpContext != NULL && pIrpContext != pOwnIrpContext)
|
|
{
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
}
|
|
|
|
(VOID) UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE, // AbortiveDisconnect
|
|
NULL, // pCompletionRoutine
|
|
NULL // pCompletionContext
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: error occurred; closed raw conn\n"
|
|
));
|
|
|
|
Status = UlInvokeCompletionRoutine(
|
|
Status,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlSendData: finished completion routine: status = 0x%x\n",
|
|
Status
|
|
));
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
return Status;
|
|
|
|
} // UlSendData
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Receives data from the specified connection. This function is
|
|
typically used after a receive indication handler has failed to
|
|
consume all of the indicated data.
|
|
|
|
If the connection is filtered the data will be read from the
|
|
filter channel.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pBuffer - Supplies a pointer to the target buffer for the received
|
|
data.
|
|
|
|
BufferLength - Supplies the length of pBuffer.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the listening endpoint is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlReceiveData(
|
|
IN PVOID pConnectionContext,
|
|
IN PVOID pBuffer,
|
|
IN ULONG BufferLength,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION)pConnectionContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT(IS_VALID_CONNECTION(pConnection));
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// This is a filtered connection, get the data from the filter.
|
|
//
|
|
|
|
status = UlFilterReadHandler(
|
|
&pConnection->FilterInfo,
|
|
(PBYTE)pBuffer,
|
|
BufferLength,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is not a filtered connection. Get the data from TDI.
|
|
//
|
|
|
|
status = UlpReceiveRawData(
|
|
pConnectionContext,
|
|
pBuffer,
|
|
BufferLength,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlReceiveData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Either create a new endpoint for the specified URL or, if one
|
|
already exists, reference it.
|
|
|
|
We do not allow mixing protocols on the same endpoint (e.g. HTTP
|
|
and HTTPS).
|
|
|
|
Arguments:
|
|
|
|
pParsedUrl - fully decomposed and parsed URL.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlAddSiteToEndpointList(
|
|
PHTTP_PARSED_URL pParsedUrl
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_ENDPOINT pEndpoint;
|
|
KIRQL oldIrql;
|
|
USHORT Port;
|
|
BOOLEAN Secure;
|
|
|
|
//
|
|
// Even though this routine cannot be pageable
|
|
// (due to the spinlock aquisition), it must be called at
|
|
// low IRQL.
|
|
//
|
|
ASSERT( pParsedUrl );
|
|
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
|
|
|
|
//
|
|
// convert port to network byte order
|
|
//
|
|
|
|
Port = SWAP_SHORT(pParsedUrl->PortNumber);
|
|
Secure = pParsedUrl->Secure;
|
|
|
|
UlTrace(SITE, (
|
|
"UlAddSiteToEndpointList:"
|
|
" Scheme = '%s' Port = %d SiteType = %d FullUrl = %S\n",
|
|
Secure ? "https" : "http",
|
|
SWAP_SHORT(Port),
|
|
pParsedUrl->SiteType,
|
|
pParsedUrl->pFullUrl
|
|
));
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
|
|
|
|
//
|
|
// make sure we're not shutting down
|
|
//
|
|
|
|
if (g_TdiWaitingForEndpointDrain)
|
|
{
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Check whether this url is routable according to our
|
|
// listen scope - global listen address list - or not.
|
|
// If not fail the request. We do not need the create
|
|
// an endpoint and walk its idle list, endpoint's
|
|
// list is always the duplicate of the global one.
|
|
//
|
|
|
|
if (!UlpIsUrlRouteableInListenScope(pParsedUrl))
|
|
{
|
|
//
|
|
// ParsedUrl is allocated from stack, individual
|
|
// string pointers are allocated from paged pool
|
|
// do not touch them.
|
|
//
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
|
|
status = STATUS_HOST_UNREACHABLE;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Find an existing endpoint for this address.
|
|
//
|
|
|
|
pEndpoint = UlpFindEndpointForPort( Port );
|
|
|
|
//
|
|
// Did we find one?
|
|
//
|
|
|
|
if (pEndpoint == NULL)
|
|
{
|
|
//
|
|
// Didn't find it. Try to create one. Since we must release
|
|
// the TDI spinlock before we can create a new listening endpoint,
|
|
// there is the opportunity for a race condition with other
|
|
// threads creating endpoints.
|
|
//
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
|
|
UlTrace(SITE, (
|
|
"UlAddSiteToEndpointList: no endpoint for scheme '%s'"
|
|
" (port %d), creating\n",
|
|
Secure ? "https" : "http",
|
|
SWAP_SHORT(Port)
|
|
));
|
|
|
|
status = UlCreateListeningEndpoint(
|
|
pParsedUrl, // Port & Scheme (& opt Address)
|
|
&UlConnectionRequest, // callback functions
|
|
&UlConnectionComplete,
|
|
&UlConnectionDisconnect,
|
|
&UlConnectionDisconnectComplete,
|
|
&UlConnectionDestroyed,
|
|
&UlHttpReceive,
|
|
&pEndpoint
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// Maybe another thread has already created it?
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
|
|
|
|
//
|
|
// make sure we're not shutting down
|
|
//
|
|
if (g_TdiWaitingForEndpointDrain)
|
|
{
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Find an existing endpoint for this address.
|
|
//
|
|
|
|
pEndpoint = UlpFindEndpointForPort( Port );
|
|
|
|
if (pEndpoint != NULL)
|
|
{
|
|
//
|
|
// Check if the endpoint's protocol matches the protocol of the new url
|
|
//
|
|
|
|
if (Secure != pEndpoint->Secure)
|
|
{
|
|
status = STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Adjust the usage count.
|
|
//
|
|
|
|
pEndpoint->UsageCount++;
|
|
ASSERT( pEndpoint->UsageCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pEndpointUsageTraceLog,
|
|
REF_ACTION_REFERENCE_ENDPOINT_USAGE,
|
|
pEndpoint->UsageCount,
|
|
pEndpoint,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The endpoint doesn't exist. This is a "real" failure.
|
|
//
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if the endpoint's protocol matches the protocol of the new url
|
|
//
|
|
|
|
if (Secure != pEndpoint->Secure)
|
|
{
|
|
status = STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Adjust the usage count.
|
|
//
|
|
|
|
pEndpoint->UsageCount++;
|
|
ASSERT( pEndpoint->UsageCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pEndpointUsageTraceLog,
|
|
REF_ACTION_REFERENCE_ENDPOINT_USAGE,
|
|
pEndpoint->UsageCount,
|
|
pEndpoint,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
}
|
|
|
|
UlTrace(SITE, (
|
|
"UlAddSiteToEndpointList: using endpoint %p for scheme '%s' port %d\n",
|
|
pEndpoint,
|
|
SWAP_SHORT(Port)
|
|
));
|
|
|
|
cleanup:
|
|
|
|
RETURN(status);
|
|
|
|
} // UlAddSiteToEndpointList
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Dereference the endpoint corresponding to the specified address.
|
|
|
|
Arguments:
|
|
|
|
pSiteUrl - Supplies the URL specifying the site to remove.
|
|
|
|
UseIp6Wildcard - Indicates wildcard sites should be mapped to IPv6.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlRemoveSiteFromEndpointList(
|
|
IN BOOLEAN secure,
|
|
IN USHORT port
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PUL_ENDPOINT pEndpoint;
|
|
KIRQL oldIrql = PASSIVE_LEVEL;
|
|
BOOLEAN spinlockHeld = FALSE;
|
|
UL_STATUS_BLOCK ulStatus;
|
|
|
|
UNREFERENCED_PARAMETER(secure);
|
|
|
|
//
|
|
// N.B. pSiteUrl is paged and cannot be manipulated with the
|
|
// spinlock held. Even though this routine cannot be pageable
|
|
// (due to the spinlock aquisition), it must be called at
|
|
// low IRQL.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
|
|
|
|
//
|
|
// convert port to network byte order
|
|
//
|
|
|
|
port = SWAP_SHORT(port);
|
|
|
|
UlTrace(SITE, (
|
|
"UlRemoveSiteFromEndpointList: Scheme = '%s', Port = %d\n",
|
|
secure ? "https" : "http",
|
|
SWAP_SHORT(port)
|
|
));
|
|
|
|
//
|
|
// Find an existing endpoint for this address.
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
|
|
spinlockHeld = TRUE;
|
|
|
|
pEndpoint = UlpFindEndpointForPort( port );
|
|
|
|
//
|
|
// Did we find one?
|
|
//
|
|
|
|
if (pEndpoint == NULL)
|
|
{
|
|
//
|
|
// Ideally, this should never happen.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
status = STATUS_NOT_FOUND;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Adjust the usage count. If it drops to zero, blow away the
|
|
// endpoint.
|
|
//
|
|
|
|
ASSERT( pEndpoint->UsageCount > 0 );
|
|
pEndpoint->UsageCount--;
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pEndpointUsageTraceLog,
|
|
REF_ACTION_REFERENCE_ENDPOINT_USAGE,
|
|
pEndpoint->UsageCount,
|
|
pEndpoint,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
if (pEndpoint->UsageCount == 0)
|
|
{
|
|
//
|
|
// We can't call UlCloseListeningEndpoint() with the TDI spinlock
|
|
// held. If the endpoint is still on the global list, then go
|
|
// ahead and remove it now, release the TDI spinlock, and then
|
|
// close the endpoint.
|
|
//
|
|
|
|
if (! pEndpoint->Deleted)
|
|
{
|
|
ASSERT(NULL != pEndpoint->GlobalEndpointListEntry.Flink);
|
|
|
|
RemoveEntryList( &pEndpoint->GlobalEndpointListEntry );
|
|
|
|
InsertTailList(
|
|
&g_TdiDeletedEndpointListHead,
|
|
&pEndpoint->GlobalEndpointListEntry
|
|
);
|
|
pEndpoint->Deleted = TRUE;
|
|
}
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
spinlockHeld = FALSE;
|
|
|
|
DEREFERENCE_ENDPOINT_SELF(
|
|
pEndpoint,
|
|
REF_ACTION_ENDPOINT_USAGE_DEREFERENCE
|
|
);
|
|
|
|
UlTrace(SITE, (
|
|
"UlRemoveSiteFromEndpointList: closing endpoint %p for "
|
|
"scheme '%s' port %d\n",
|
|
pEndpoint,
|
|
secure ? "https" : "http",
|
|
port
|
|
));
|
|
|
|
//
|
|
// Initialize a status block. We'll pass a pointer to this as
|
|
// the completion context to UlCloseListeningEndpoint(). The
|
|
// completion routine will update the status block and signal
|
|
// the event.
|
|
//
|
|
|
|
UlInitializeStatusBlock( &ulStatus );
|
|
|
|
status = UlCloseListeningEndpoint(
|
|
pEndpoint,
|
|
&UlpSynchronousIoComplete,
|
|
&ulStatus
|
|
);
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
//
|
|
// Wait for it to finish.
|
|
//
|
|
|
|
UlWaitForStatusBlockEvent( &ulStatus );
|
|
|
|
//
|
|
// Retrieve the updated status.
|
|
//
|
|
|
|
status = ulStatus.IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (spinlockHeld)
|
|
{
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
}
|
|
|
|
#if DBG
|
|
if (status == STATUS_NOT_FOUND)
|
|
{
|
|
UlTrace(SITE, (
|
|
"UlRemoveSiteFromEndpointList: cannot find endpoint for "
|
|
"scheme '%s' port %d\n",
|
|
secure ? "https" : "http",
|
|
port
|
|
));
|
|
}
|
|
#endif
|
|
|
|
RETURN(status);
|
|
|
|
} // UlRemoveSiteFromEndpointList
|
|
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Destroys all resources allocated to an endpoint, including the
|
|
endpoint structure itself.
|
|
|
|
Arguments:
|
|
|
|
pEndpoint - Supplies the endpoint to destroy.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDestroyEndpoint(
|
|
IN PUL_ENDPOINT pEndpoint
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
ULONG EndpointCount = ULONG_MAX;
|
|
KIRQL oldIrql;
|
|
ULONG i;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
ASSERT(0 == pEndpoint->ReferenceCount);
|
|
ASSERT(0 == pEndpoint->UsageCount);
|
|
|
|
UlTrace(TDI, (
|
|
"UlpDestroyEndpoint: endpoint %p\n",
|
|
pEndpoint
|
|
));
|
|
|
|
//
|
|
// Purge the idle lists.
|
|
//
|
|
|
|
for ( i = 0; i < pEndpoint->AddrIdleListCount ; i++ )
|
|
{
|
|
UlpCleanupAddrIdleList( &pEndpoint->aAddrIdleLists[i] );
|
|
}
|
|
|
|
//
|
|
// Invoke the completion routine in the IRP context if specified.
|
|
//
|
|
|
|
pIrpContext = &pEndpoint->CleanupIrpContext;
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
(VOID) UlInvokeCompletionRoutine(
|
|
STATUS_SUCCESS,
|
|
0,
|
|
pIrpContext->pCompletionRoutine,
|
|
pIrpContext->pCompletionContext
|
|
);
|
|
|
|
//
|
|
// Remove the endpoint from g_TdiDeletedEndpointListHead
|
|
//
|
|
|
|
ASSERT( pEndpoint->Deleted );
|
|
ASSERT( NULL != pEndpoint->GlobalEndpointListEntry.Flink );
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &oldIrql );
|
|
|
|
RemoveEntryList( &pEndpoint->GlobalEndpointListEntry );
|
|
|
|
if (pEndpoint->Counted)
|
|
{
|
|
g_TdiEndpointCount--;
|
|
EndpointCount = g_TdiEndpointCount;
|
|
}
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, oldIrql );
|
|
|
|
//
|
|
// Free the endpoint structure.
|
|
//
|
|
|
|
pEndpoint->Signature = UL_ENDPOINT_SIGNATURE_X;
|
|
UL_FREE_POOL( pEndpoint, UL_ENDPOINT_POOL_TAG );
|
|
|
|
//
|
|
// Decrement the global endpoint count.
|
|
//
|
|
|
|
if (g_TdiWaitingForEndpointDrain && EndpointCount == 0)
|
|
{
|
|
KeSetEvent(&g_TdiEndpointDrainEvent, 0, FALSE);
|
|
}
|
|
|
|
} // UlpDestroyEndpoint
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Deferred cleanup routine for dead connections.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - Supplies a pointer to the work item queued. This should
|
|
point to the WORK_ITEM structure embedded in a UL_CONNECTION.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDestroyConnectionWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
pConnection = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_CONNECTION,
|
|
WorkItem
|
|
);
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
UlpDestroyConnection(pConnection);
|
|
} // UlpDestroyConnectionWorker
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Destroys all resources allocated to an connection, including the
|
|
connection structure itself.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to destroy.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDestroyConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
ULONG ConnectionCount;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT(pConnection->ConnListState == NoConnList);
|
|
ASSERT(UlpConnectionIsOnValidList(pConnection));
|
|
|
|
UlTraceVerbose(TDI, (
|
|
"UlpDestroyConnection: connection %p\n",
|
|
pConnection
|
|
));
|
|
|
|
//
|
|
// Close the TDI object. Do this first so we would not receive any
|
|
// TDI indications beyond this point. Otherwise, our UlpDisconnectHandler
|
|
// may get called later and it may try to clean up a stale pFilterChannel
|
|
// under race conditions.
|
|
//
|
|
|
|
UxCloseTdiObject( &pConnection->ConnectionObject );
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Release the RawConnection ID if we have allocated one.
|
|
//
|
|
UlpCleanupConnectionId(pConnection);
|
|
|
|
DEREFERENCE_FILTER_CHANNEL(pConnection->FilterInfo.pFilterChannel);
|
|
pConnection->FilterInfo.pFilterChannel = NULL;
|
|
}
|
|
|
|
// If OpaqueId is non-zero, then refCount should not be zero
|
|
ASSERT(HTTP_IS_NULL_ID(&pConnection->FilterInfo.ConnectionId));
|
|
|
|
//
|
|
// Free the accept IRP.
|
|
//
|
|
|
|
if (pConnection->pIrp != NULL)
|
|
{
|
|
UlFreeIrp( pConnection->pIrp );
|
|
}
|
|
|
|
//
|
|
// Remove from global list of connections
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
|
|
|
|
RemoveEntryList( &pConnection->GlobalConnectionListEntry );
|
|
|
|
g_TdiConnectionCount--;
|
|
ConnectionCount = g_TdiConnectionCount;
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
|
|
//
|
|
// Free the connection structure.
|
|
//
|
|
|
|
DESTROY_REF_TRACE_LOG( pConnection->pTraceLog,
|
|
UL_CONNECTION_REF_TRACE_LOG_POOL_TAG );
|
|
DESTROY_REF_TRACE_LOG( pConnection->pHttpTraceLog,
|
|
UL_HTTP_CONNECTION_REF_TRACE_LOG_POOL_TAG );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pTdiTraceLog,
|
|
REF_ACTION_FREE_UL_CONNECTION,
|
|
0,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
pConnection->Signature = UL_CONNECTION_SIGNATURE_X;
|
|
UL_FREE_POOL( pConnection, UL_CONNECTION_POOL_TAG );
|
|
|
|
// allow us to shut down
|
|
|
|
if (g_TdiWaitingForEndpointDrain && ConnectionCount == 0)
|
|
{
|
|
KeSetEvent(&g_TdiConnectionDrainEvent, 0, FALSE);
|
|
}
|
|
|
|
} // UlpDestroyConnection
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Dequeues any idle connection from the specified endpoint.
|
|
|
|
Arguments:
|
|
|
|
pAddrIdleList - Supplies the idle list to dequeue from.
|
|
|
|
Return Value:
|
|
|
|
PUL_CONNECTION - Pointer to an idle connection is successful,
|
|
NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PUL_CONNECTION
|
|
UlpDequeueIdleConnectionToDrain(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
)
|
|
{
|
|
PSLIST_ENTRY pSListEntry;
|
|
PUL_CONNECTION pConnection;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pAddrIdleList ) );
|
|
|
|
pConnection = NULL;
|
|
|
|
//
|
|
// Pop an entry off the list.
|
|
//
|
|
|
|
pSListEntry = PpslAllocateToDrain( pAddrIdleList->IdleConnectionSListsHandle );
|
|
|
|
if (pSListEntry != NULL)
|
|
{
|
|
pConnection = CONTAINING_RECORD(
|
|
pSListEntry,
|
|
UL_CONNECTION,
|
|
IdleSListEntry
|
|
);
|
|
|
|
pConnection->IdleSListEntry.Next = NULL;
|
|
pConnection->ConnListState = NoConnList;
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
ASSERT(pConnection->ConnectionFlags.Value == 0);
|
|
|
|
if ( pConnection->FilterInfo.pFilterChannel )
|
|
{
|
|
//
|
|
// If the idle connection has filter attached on it, it will have
|
|
// an additional refcount because of the opaque id assigned to the
|
|
// ul_connection, filter API uses this id to communicate with the
|
|
// filter app through various IOCTLs.
|
|
//
|
|
ASSERT( 2 == pConnection->ReferenceCount );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// As long as the connection doesn't get destroyed, it will sit
|
|
// in the idle list with one refcount on it.
|
|
//
|
|
ASSERT( 1 == pConnection->ReferenceCount );
|
|
}
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pAddrIdleList->pOwningEndpoint ) );
|
|
}
|
|
|
|
return pConnection;
|
|
|
|
} // UlpDequeueIdleConnectionToDrain
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Dequeues an idle connection from the specified endpoint.
|
|
|
|
Arguments:
|
|
|
|
pEndpoint - Supplies the endpoint to dequeue from.
|
|
|
|
Return Value:
|
|
|
|
PUL_CONNECTION - Pointer to an idle connection is successful,
|
|
NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PUL_CONNECTION
|
|
UlpDequeueIdleConnection(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
)
|
|
{
|
|
PSLIST_ENTRY pSListEntry;
|
|
PUL_CONNECTION pConnection;
|
|
BOOLEAN PerProcListReplenishNeeded = FALSE;
|
|
BOOLEAN BackingListReplenishNeeded = FALSE; // Replenish backing list only
|
|
USHORT Depth = 0;
|
|
USHORT MinDepth = 0;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pAddrIdleList ) );
|
|
|
|
//
|
|
// Pop an entry off the list.
|
|
//
|
|
|
|
pSListEntry = PpslAllocate( pAddrIdleList->IdleConnectionSListsHandle );
|
|
|
|
if (pSListEntry != NULL)
|
|
{
|
|
pConnection = CONTAINING_RECORD(
|
|
pSListEntry,
|
|
UL_CONNECTION,
|
|
IdleSListEntry
|
|
);
|
|
|
|
pConnection->IdleSListEntry.Next = NULL;
|
|
pConnection->ConnListState = NoConnList;
|
|
pConnection->OriginProcessor = KeGetCurrentProcessorNumber();
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->ConnectionFlags.Value == 0 );
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// If the idle connection has filter attached on it, it will have
|
|
// an additional refcount because of the opaque id assigned to the
|
|
// ul_connection, filter API uses this id to communicate with the
|
|
// filter app through various IOCTLs.
|
|
//
|
|
|
|
ASSERT( 2 == pConnection->ReferenceCount );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// As long as the connection doesn't get destroyed, it will sit
|
|
// in the idle list with one refcount on it.
|
|
//
|
|
|
|
ASSERT( 1 == pConnection->ReferenceCount );
|
|
}
|
|
|
|
//
|
|
// Generate more connections if necessary. At the beginning
|
|
// there will be nothing in the back list, so we will do the
|
|
// initial populate when the first conn served from the idle
|
|
// list. Later we will populate only when we hit to low mark.
|
|
//
|
|
|
|
Depth = PpslQueryDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
KeGetCurrentProcessorNumber()
|
|
);
|
|
|
|
if (Depth == 0)
|
|
{
|
|
//
|
|
// This will replenish a block of connections to the
|
|
// per-proc list.
|
|
//
|
|
|
|
PerProcListReplenishNeeded = TRUE;
|
|
}
|
|
|
|
Depth = PpslQueryBackingListDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle
|
|
);
|
|
|
|
MinDepth = PpslQueryBackingListMinDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle
|
|
);
|
|
|
|
if (Depth < MinDepth)
|
|
{
|
|
//
|
|
// This will replenish a block of connections to the
|
|
// backing list.
|
|
//
|
|
|
|
BackingListReplenishNeeded = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The idle list is empty. However, we need to schedule
|
|
// a replenish at this time. Actually, we're desperate
|
|
// since we have already scheduled one.
|
|
//
|
|
|
|
PerProcListReplenishNeeded = TRUE;
|
|
BackingListReplenishNeeded = TRUE;
|
|
pConnection = NULL;
|
|
}
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pTdiTraceLog,
|
|
REF_ACTION_DEQUEUE_UL_CONNECTION,
|
|
PerProcListReplenishNeeded || BackingListReplenishNeeded,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// Schedule a replenish if necessary.
|
|
//
|
|
|
|
if (PerProcListReplenishNeeded || BackingListReplenishNeeded)
|
|
{
|
|
//
|
|
// Add a reference to the endpoint to ensure that it doesn't
|
|
// disappear from under us. UlpReplenishAddrIdleListWorker will
|
|
// remove the reference once it's finished.
|
|
//
|
|
|
|
if (FALSE == InterlockedExchange(
|
|
&pAddrIdleList->WorkItemScheduled,
|
|
TRUE
|
|
))
|
|
{
|
|
REFERENCE_ENDPOINT(
|
|
pAddrIdleList->pOwningEndpoint,
|
|
REF_ACTION_REPLENISH
|
|
);
|
|
|
|
//
|
|
// Remember the proc on the adrlist.
|
|
//
|
|
|
|
if (PerProcListReplenishNeeded)
|
|
{
|
|
//
|
|
// Replenish per-proc list, and backing list
|
|
// if necessary
|
|
//
|
|
|
|
pAddrIdleList->CpuToReplenish =
|
|
(USHORT) KeGetCurrentProcessorNumber();
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Replenish backing list only
|
|
//
|
|
|
|
pAddrIdleList->CpuToReplenish =
|
|
(USHORT) g_UlNumberOfProcessors;
|
|
}
|
|
|
|
UL_QUEUE_HIGH_PRIORITY_ITEM(
|
|
&pAddrIdleList->WorkItem,
|
|
&UlpReplenishAddrIdleListWorker
|
|
);
|
|
}
|
|
}
|
|
|
|
return pConnection;
|
|
|
|
} // UlpDequeueIdleConnection
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Enqueues an active connection onto the specified endpoint.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to enqueue.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpEnqueueActiveConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
ASSERT(pConnection->ConnListState == NoConnList);
|
|
ASSERT(UlpConnectionIsOnValidList(pConnection));
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
ASSERT(UlDbgSpinLockOwned(&pConnection->ConnectionStateSpinLock));
|
|
|
|
REFERENCE_CONNECTION(pConnection);
|
|
|
|
pConnection->ConnListState = ActiveNoConnList;
|
|
|
|
ASSERT(UlpConnectionIsOnValidList(pConnection));
|
|
|
|
} // UlpEnqueueActiveConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for incoming connections.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ADDR_IDLE_LIST, which can
|
|
traverse back to a PUL_ENDPOINT.
|
|
|
|
RemoteAddressLength - Supplies the length of the remote (client-
|
|
side) address.
|
|
|
|
pRemoteAddress - Supplies a pointer to the remote address as
|
|
stored in a TRANSPORT_ADDRESS structure.
|
|
|
|
UserDataLength - Optionally supplies the length of any connect
|
|
data associated with the connection request.
|
|
|
|
pUserData - Optionally supplies a pointer to any connect data
|
|
associated with the connection request.
|
|
|
|
OptionsLength - Optionally supplies the length of any connect
|
|
options associated with the connection request.
|
|
|
|
pOptions - Optionally supplies a pointer to any connect options
|
|
associated with the connection request.
|
|
|
|
pConnectionContext - Receives the context to associate with this
|
|
connection. We'll always use a PUL_CONNECTION as the context.
|
|
|
|
pAcceptIrp - Receives an IRP that will be completed by the transport
|
|
when the incoming connection is fully accepted.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpConnectHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN LONG RemoteAddressLength,
|
|
IN PVOID pRemoteAddress,
|
|
IN LONG UserDataLength,
|
|
IN PVOID pUserData,
|
|
IN LONG OptionsLength,
|
|
IN PVOID pOptions,
|
|
OUT CONNECTION_CONTEXT *pConnectionContext,
|
|
OUT PIRP *pAcceptIrp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN result;
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList;
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_CONNECTION pConnection;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
BOOLEAN handlerCalled;
|
|
TRANSPORT_ADDRESS UNALIGNED *TAList;
|
|
PTA_ADDRESS TA;
|
|
|
|
UL_ENTER_DRIVER("UlpConnectHandler", NULL);
|
|
|
|
UL_INC_CONNECTION_STATS( TotalConnections );
|
|
|
|
UNREFERENCED_PARAMETER(UserDataLength);
|
|
UNREFERENCED_PARAMETER(pUserData);
|
|
UNREFERENCED_PARAMETER(OptionsLength);
|
|
UNREFERENCED_PARAMETER(pOptions);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pAddrIdleList = (PUL_ADDR_IDLE_LIST)pTdiEventContext;
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST(pAddrIdleList));
|
|
|
|
pEndpoint = pAddrIdleList->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
UlTrace(TDI,("UlpConnectHandler: AddrIdleList %p, endpoint %p\n",
|
|
pAddrIdleList,
|
|
pEndpoint));
|
|
|
|
//
|
|
// If the endpoint has been has been added to the global list, then
|
|
// pEndpoint->Counted will be set. If the endpoint has not been added
|
|
// to the global list, then fail this call.
|
|
//
|
|
|
|
if (!pEndpoint->Counted)
|
|
{
|
|
return STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on fatal exit.
|
|
//
|
|
|
|
pConnection = NULL;
|
|
handlerCalled = FALSE;
|
|
|
|
//
|
|
// make sure that we are not in the process of destroying this
|
|
// endpoint. UlRemoveSiteFromEndpointList will do that and
|
|
// start the cleanup process when UsageCount hits 0.
|
|
//
|
|
|
|
if (pEndpoint->UsageCount == 0)
|
|
{
|
|
UL_INC_CONNECTION_STATS( EndpointDying );
|
|
|
|
status = STATUS_CONNECTION_REFUSED;
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Try to pull an idle connection from the endpoint.
|
|
//
|
|
|
|
for (;;)
|
|
{
|
|
pConnection = UlpDequeueIdleConnection( pAddrIdleList );
|
|
|
|
if (pConnection == NULL )
|
|
{
|
|
UL_INC_CONNECTION_STATS( NoIdleConn );
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
//
|
|
// Establish a referenced pointer from the connection back
|
|
// to the endpoint.
|
|
//
|
|
|
|
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
|
|
ASSERT( pConnection->pOwningAddrIdleList == pAddrIdleList );
|
|
|
|
REFERENCE_ENDPOINT( pEndpoint, REF_ACTION_CONNECT );
|
|
|
|
//
|
|
// Make sure the filter settings are up to date.
|
|
//
|
|
if (UlValidateFilterChannel(
|
|
pConnection->FilterInfo.pFilterChannel,
|
|
pConnection->FilterInfo.SecureConnection
|
|
))
|
|
{
|
|
//
|
|
// We found a good connection.
|
|
// Break out of the loop and go on.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This connection doesn't have up to date filter
|
|
// settings. Destroy it and get a new connection.
|
|
//
|
|
|
|
UlpCleanupEarlyConnection(pConnection);
|
|
}
|
|
|
|
//
|
|
// We should have a good connection now.
|
|
//
|
|
|
|
ASSERT(IS_VALID_CONNECTION(pConnection));
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
|
|
//
|
|
// Store the remote address in the connection.
|
|
//
|
|
|
|
|
|
TAList = (TRANSPORT_ADDRESS UNALIGNED *) pRemoteAddress;
|
|
TA = (PTA_ADDRESS) TAList->Address;
|
|
|
|
ASSERT(TA->AddressType == pConnection->AddressType);
|
|
RtlCopyMemory(pConnection->RemoteAddress, TA->Address, TA->AddressLength);
|
|
|
|
//
|
|
// Invoke the client's handler to see if they can accept
|
|
// this connection. If they refuse it, bail.
|
|
//
|
|
|
|
result = (pEndpoint->pConnectionRequestHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection,
|
|
(PTRANSPORT_ADDRESS)(pRemoteAddress),
|
|
RemoteAddressLength,
|
|
&pConnection->pConnectionContext
|
|
);
|
|
|
|
if (!result)
|
|
{
|
|
//
|
|
// We expect UlConnectionRequest to call UL_INC_CONNECTION_STATS().
|
|
//
|
|
|
|
status = STATUS_CONNECTION_REFUSED;
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Remember that we've called the handler. If we hit a fatal
|
|
// condition (say, out of memory) after this point, we'll
|
|
// fake a "failed connection complete" indication to the client
|
|
// so they can cleanup their state.
|
|
//
|
|
|
|
handlerCalled = TRUE;
|
|
|
|
pConnection->pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
pConnection->pIrp->Tail.Overlay.OriginalFileObject = pTdiObject->pFileObject;
|
|
|
|
|
|
TdiBuildAccept(
|
|
pConnection->pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UlpRestartAccept, // CompletionRoutine
|
|
pConnection, // Context
|
|
&(pConnection->TdiConnectionInformation), // RequestConnectionInfo
|
|
NULL // ReturnConnectionInfo
|
|
);
|
|
|
|
//
|
|
// We must trace the IRP before we set the next stack location
|
|
// so the trace code can pull goodies from the IRP correctly.
|
|
//
|
|
|
|
TRACE_IRP( IRP_ACTION_CALL_DRIVER, pConnection->pIrp );
|
|
|
|
//
|
|
// Make the next stack location current. Normally, UlCallDriver would
|
|
// do this for us, but since we're bypassing UlCallDriver, we must do
|
|
// it ourselves.
|
|
//
|
|
|
|
IoSetNextIrpStackLocation( pConnection->pIrp );
|
|
|
|
//
|
|
// Return the IRP to the transport.
|
|
//
|
|
|
|
*pAcceptIrp = pConnection->pIrp;
|
|
|
|
//
|
|
// Establish the connection context.
|
|
//
|
|
|
|
*pConnectionContext = (CONNECTION_CONTEXT)pConnection;
|
|
UlpSetConnectionFlag( pConnection, MakeAcceptPendingFlag() );
|
|
|
|
//
|
|
// NOTE: As far as the cleanup connection state is concerned,
|
|
// we are still UlConnectStateConnectIdle until we've fully
|
|
// accepted the connection (Half-Open connection doesn't matter).
|
|
//
|
|
|
|
ASSERT( UlConnectStateConnectIdle == pConnection->ConnectionState );
|
|
|
|
//
|
|
// Reference the connection so it doesn't go away before
|
|
// the accept IRP completes.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
|
|
UL_LEAVE_DRIVER("UlpConnectHandler");
|
|
|
|
//
|
|
// Tell TDI that we gave it an IRP to complete.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
|
|
//
|
|
// Cleanup for fatal error conditions.
|
|
//
|
|
|
|
fatal:
|
|
|
|
UlTrace(TDI, (
|
|
"UlpConnectHandler: endpoint %p, failure 0x%x\n",
|
|
pTdiEventContext,
|
|
status
|
|
));
|
|
|
|
if (handlerCalled)
|
|
{
|
|
//
|
|
// Fake a "failed connection complete" indication.
|
|
//
|
|
|
|
(pEndpoint->pConnectionCompleteHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext,
|
|
status
|
|
);
|
|
}
|
|
|
|
//
|
|
// If we managed to pull a connection off the idle list, then
|
|
// put it back and remove the endpoint reference we added.
|
|
//
|
|
|
|
if (pConnection != NULL)
|
|
{
|
|
UlpCleanupEarlyConnection(pConnection);
|
|
}
|
|
|
|
UL_LEAVE_DRIVER("UlpConnectHandler");
|
|
|
|
return status;
|
|
|
|
} // UlpConnectHandler
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for disconnect requests.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ENDPOINT.
|
|
|
|
ConnectionContext - Supplies the context associated with the
|
|
connection object. This should be a PUL_CONNECTION.
|
|
|
|
DisconnectDataLength - Optionally supplies the length of any
|
|
disconnect data associated with the disconnect request.
|
|
|
|
pDisconnectData - Optionally supplies a pointer to any disconnect
|
|
data associated with the disconnect request.
|
|
|
|
DisconnectInformationLength - Optionally supplies the length of any
|
|
disconnect information associated with the disconnect request.
|
|
|
|
pDisconnectInformation - Optionally supplies a pointer to any
|
|
disconnect information associated with the disconnect request.
|
|
|
|
DisconnectFlags - Supplies the disconnect flags. This will be zero
|
|
or more TDI_DISCONNECT_* flags.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpDisconnectHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN LONG DisconnectDataLength,
|
|
IN PVOID pDisconnectData,
|
|
IN LONG DisconnectInformationLength,
|
|
IN PVOID pDisconnectInformation,
|
|
IN ULONG DisconnectFlags
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_CONNECTION pConnection;
|
|
LONG OldReference;
|
|
LONG NewReference;
|
|
|
|
UL_ENTER_DRIVER("UlpDisconnectHandler", NULL);
|
|
|
|
UNREFERENCED_PARAMETER(DisconnectDataLength);
|
|
UNREFERENCED_PARAMETER(pDisconnectData);
|
|
UNREFERENCED_PARAMETER(DisconnectInformationLength);
|
|
UNREFERENCED_PARAMETER(pDisconnectInformation);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
pConnection = (PUL_CONNECTION)ConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpDisconnectHandler: endpoint %p, connection %p, flags 0x%08lx, %s\n",
|
|
pTdiEventContext,
|
|
(PVOID)ConnectionContext,
|
|
DisconnectFlags,
|
|
(DisconnectFlags & TDI_DISCONNECT_ABORT) ? "abort" : "graceful"
|
|
));
|
|
|
|
//
|
|
// Add an extra reference to pConnection before we proceed to protect
|
|
// ourselves from having connection reference drops to 0 in the middle
|
|
// of this routine, unless the reference is already at 0 when we enter
|
|
// this routine, in which case we simply bail out.
|
|
//
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_DISCONNECT_HANDLER,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
for (;;)
|
|
{
|
|
OldReference = *((volatile LONG *) &pConnection->ReferenceCount);
|
|
|
|
if (0 == OldReference)
|
|
{
|
|
ASSERT( UlConnectStateConnectCleanup == pConnection->ConnectionState );
|
|
ASSERT( pConnection->ConnectionFlags.TdiConnectionInvalid );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NewReference = OldReference + 1;
|
|
|
|
if (InterlockedCompareExchange(
|
|
&pConnection->ReferenceCount,
|
|
NewReference,
|
|
OldReference
|
|
) == OldReference)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the connection state based on the type of disconnect.
|
|
//
|
|
|
|
if (DisconnectFlags & TDI_DISCONNECT_ABORT)
|
|
{
|
|
//
|
|
// If it's a filtered connection, make sure we stop passing
|
|
// on AppWrite data.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
UlDestroyFilterConnection(&pConnection->FilterInfo);
|
|
}
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeAbortIndicatedFlag() );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_ABORT_INDICATED,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// Since the client aborted the connection we
|
|
// can clean up our own state immediately.
|
|
// This will also change our state for us.
|
|
// REVIEW: is it okay to init a graceful disconnect when we receive a
|
|
// graceful disconnect?
|
|
//
|
|
|
|
UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE, // AbortiveDisconnect
|
|
NULL, // pCompletionRoutine
|
|
NULL // pCompletionContext
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
UlpSetConnectionFlag( pConnection, MakeDisconnectIndicatedFlag() );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_DISCONNECT_INDICATED,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
if (UlConnectStateConnectReady == pConnection->ConnectionState)
|
|
{
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
UlFilterDisconnectHandler(&pConnection->FilterInfo);
|
|
}
|
|
else
|
|
{
|
|
UlpDoDisconnectNotification(pConnection);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
UlTrace( TDI, (
|
|
"UlpDisconnectHandler: connection %p, NOT UlConnectStateConnectReady (%d)!\n",
|
|
(PVOID)ConnectionContext,
|
|
pConnection->ConnectionState
|
|
));
|
|
|
|
//
|
|
// If it's a filtered connection, make sure we stop passing
|
|
// on AppWrite data.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
UlDestroyFilterConnection(&pConnection->FilterInfo);
|
|
}
|
|
|
|
UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE, // AbortiveDisconnect
|
|
NULL, // pCompletionRoutine
|
|
NULL // pCompletionContext
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If cleanup has begun on the connection, remove the final reference.
|
|
//
|
|
|
|
UlpRemoveFinalReference( pConnection );
|
|
|
|
//
|
|
// Drop the extra reference we added when we enter this function.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
UL_LEAVE_DRIVER("UlpDisconnectHandler");
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlpDisconnectHandler
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Tells the ultdi client of a disconnect. The client is only
|
|
notified of graceful disconnects and then only if the client
|
|
has not itself attempted to disconnect the connection. If the
|
|
connection is filtered, the filter process will be notified
|
|
instead of the client directly.
|
|
|
|
Arguments:
|
|
|
|
pConnection - a pointer to the connection
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDoDisconnectNotification(
|
|
IN PVOID pConnectionContext
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
|
|
PUL_ENDPOINT pEndpoint;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(IS_VALID_CONNECTION(pConnection));
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
|
|
ASSERT(IS_VALID_ENDPOINT(pEndpoint));
|
|
|
|
UlAcquireSpinLock(&pConnection->ConnectionStateSpinLock, &OldIrql);
|
|
|
|
ASSERT(pConnection->ConnectionFlags.AcceptComplete);
|
|
|
|
if (UlConnectStateConnectReady == pConnection->ConnectionState)
|
|
{
|
|
(pEndpoint->pConnectionDisconnectHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext
|
|
);
|
|
}
|
|
|
|
UlReleaseSpinLock(&pConnection->ConnectionStateSpinLock, OldIrql);
|
|
|
|
} // UlpDoDisconnectNotification
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Closes a previously accepted connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
AbortiveDisconnect - Supplies TRUE if the connection is to be abortively
|
|
disconnected, FALSE if it should be gracefully disconnected.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the connection is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpCloseRawConnection(
|
|
IN PVOID pConnectionContext,
|
|
IN BOOLEAN AbortiveDisconnect,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
|
|
KIRQL OldIrql;
|
|
PIRP pIrp = NULL;
|
|
PUL_IRP_CONTEXT pIrpContext = NULL;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpCloseRawConnection: connection %p, %s disconnect\n",
|
|
pConnection,
|
|
(AbortiveDisconnect ? "Abortive" : "Graceful" )
|
|
));
|
|
|
|
//
|
|
// This is the final close handler for all types of connections
|
|
// filter, non filter. We will go through this multiple times,
|
|
// but we need to guard against re-using the Disconnect IRP and
|
|
// ensure one-time close actions done a maximum of once.
|
|
//
|
|
|
|
UlAcquireSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
&OldIrql
|
|
);
|
|
|
|
switch (pConnection->ConnectionState)
|
|
{
|
|
|
|
//
|
|
// First attempt to terminate connection
|
|
//
|
|
case UlConnectStateConnectReady:
|
|
{
|
|
BOOLEAN Ignore = FALSE;
|
|
|
|
if (pConnection->ConnectionFlags.AbortIndicated)
|
|
{
|
|
// Client aborted. Go directly to clean up state.
|
|
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
|
|
Ignore = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!AbortiveDisconnect)
|
|
{
|
|
//
|
|
// Allocate the disconnect IRP and IRP context for the
|
|
// graceful disconnect. If this fails, we will abort.
|
|
//
|
|
|
|
pIrpContext = UlPplAllocateIrpContext();
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
pIrp = UlAllocateIrp(
|
|
pTdiObject->pDeviceObject->StackSize,
|
|
FALSE
|
|
);
|
|
|
|
if (!pIrpContext || !pIrp)
|
|
{
|
|
if (pIrp)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
if (pIrpContext)
|
|
{
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
}
|
|
|
|
AbortiveDisconnect = TRUE;
|
|
}
|
|
}
|
|
|
|
if (AbortiveDisconnect)
|
|
{
|
|
// Change State
|
|
pConnection->ConnectionState = UlConnectStateAbortPending;
|
|
|
|
//
|
|
// Set the flag indicating that an abort is pending &
|
|
// we're cleaning up.
|
|
//
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeAbortPendingFlag() );
|
|
UlpSetConnectionFlag( pConnection, MakeCleanupBegunFlag() );
|
|
}
|
|
else
|
|
{
|
|
// Change State
|
|
pConnection->ConnectionState = UlConnectStateDisconnectPending;
|
|
|
|
//
|
|
// Set the flag indicating that a disconnect is pending &
|
|
// we're cleaning up.
|
|
//
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeDisconnectPendingFlag() );
|
|
UlpSetConnectionFlag( pConnection, MakeCleanupBegunFlag() );
|
|
}
|
|
}
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_CLOSE_UL_CONN_RAW_CLOSE,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// Do one-time only cleanup tasks
|
|
//
|
|
|
|
//
|
|
// Get rid of our opaque id if we're a filtered connection.
|
|
// Also make sure we stop delivering AppWrite data to the parser.
|
|
//
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
UlpCleanupConnectionId( pConnection );
|
|
UlDestroyFilterConnection(&pConnection->FilterInfo);
|
|
}
|
|
|
|
//
|
|
// Let the completion routines do the dirty work
|
|
//
|
|
|
|
if (Ignore)
|
|
{
|
|
//
|
|
// Client aborted. No need to send RST or FIN.
|
|
//
|
|
|
|
status = UlInvokeCompletionRoutine(
|
|
STATUS_SUCCESS,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
if (AbortiveDisconnect)
|
|
{
|
|
//
|
|
// Send RST
|
|
//
|
|
|
|
status = UlpBeginAbort(
|
|
pConnection,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Send FIN
|
|
//
|
|
|
|
status = UlpBeginDisconnect(
|
|
pIrp,
|
|
pIrpContext,
|
|
pConnection,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
// END case UlConnectStateConnectReady
|
|
|
|
//
|
|
// Waiting for disconnect indication
|
|
//
|
|
case UlConnectStateDisconnectComplete:
|
|
|
|
if ( pConnection->ConnectionFlags.AbortIndicated ||
|
|
pConnection->ConnectionFlags.DisconnectIndicated )
|
|
{
|
|
// Change State
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
}
|
|
else
|
|
{
|
|
if (AbortiveDisconnect)
|
|
{
|
|
// Change State
|
|
pConnection->ConnectionState = UlConnectStateAbortPending;
|
|
|
|
ASSERT( pConnection->ConnectionFlags.CleanupBegun );
|
|
UlpSetConnectionFlag( pConnection, MakeAbortPendingFlag() );
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
UlTraceVerbose( TDI, (
|
|
"UlpCloseRawConnection: connection %p, Abort after Disconnect\n",
|
|
pConnection
|
|
));
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_CLOSE_UL_CONN_FORCE_ABORT,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
status = UlpBeginAbort(
|
|
pConnection,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
goto UnlockAndIgnore;
|
|
// END: case UlConnectStateDisconnectComplete
|
|
|
|
//
|
|
// Graceful disconnect already occured
|
|
//
|
|
case UlConnectStateDisconnectPending:
|
|
|
|
ASSERT( !pConnection->ConnectionFlags.DisconnectComplete );
|
|
|
|
if (pConnection->ConnectionFlags.AbortIndicated)
|
|
{
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
}
|
|
else
|
|
if (AbortiveDisconnect)
|
|
{
|
|
//
|
|
// Flag the connection as aborting a disconnect and set the state
|
|
// to UlConnectStateAbortPending.
|
|
//
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeAbortDisconnectFlag() );
|
|
UlpSetConnectionFlag( pConnection, MakeAbortPendingFlag() );
|
|
|
|
pConnection->ConnectionState = UlConnectStateAbortPending;
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_CLOSE_UL_CONN_ABORT_DISCONNECT,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
status = UlpBeginAbort(
|
|
pConnection,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
return status;
|
|
}
|
|
|
|
goto UnlockAndIgnore;
|
|
// END case UlConnectStateDisconnectPending
|
|
|
|
//
|
|
// Invalid States
|
|
//
|
|
case UlConnectStateInvalid:
|
|
default:
|
|
//
|
|
// BUGBUG: Should never get here!
|
|
//
|
|
|
|
ASSERT( !"UlpCloseRawConnection: Invalid State!" );
|
|
|
|
//
|
|
// ...and then fall through to...
|
|
//
|
|
|
|
//
|
|
// Ignore any Aborts or Disconnects when in these states
|
|
//
|
|
case UlConnectStateConnectIdle: // Init'd
|
|
case UlConnectStateAbortPending: // Send RST
|
|
case UlConnectStateConnectCleanup: // Cleanup
|
|
|
|
UnlockAndIgnore:
|
|
//
|
|
// we've already done it. don't do it twice.
|
|
//
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql);
|
|
|
|
status = UlInvokeCompletionRoutine(
|
|
STATUS_SUCCESS,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlpCloseRawConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Sends a block of data on the specified connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pMdlChain - Supplies a pointer to a MDL chain describing the
|
|
data buffers to send.
|
|
|
|
Length - Supplies the length of the data referenced by the MDL
|
|
chain.
|
|
|
|
pIrpContext - used to indicate completion to the caller.
|
|
|
|
InitiateDisconnect - Supplies TRUE if a graceful disconnect should
|
|
be initiated immediately after initiating the send (i.e. before
|
|
the send actually completes).
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpSendRawData(
|
|
IN PVOID pConnectionContext,
|
|
IN PMDL pMdlChain,
|
|
IN ULONG Length,
|
|
IN PUL_IRP_CONTEXT pIrpContext,
|
|
IN BOOLEAN InitiateDisconnect
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIRP pIrp = NULL;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
|
|
PIRP pOwnIrp = pIrpContext->pOwnIrp;
|
|
PUL_TCPSEND_DISPATCH pDispatchRoutine;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
//
|
|
// Apply the disconnect state transitions if InitiateDisconnect is
|
|
// requested.
|
|
//
|
|
|
|
if (InitiateDisconnect)
|
|
{
|
|
UlAcquireSpinLock( &pConnection->ConnectionStateSpinLock, &OldIrql );
|
|
|
|
if (UlConnectStateConnectReady == pConnection->ConnectionState)
|
|
{
|
|
//
|
|
// No SendAndDisconnect if client has aborted already and
|
|
// transition directly to the clean up state.
|
|
//
|
|
|
|
if (pConnection->ConnectionFlags.AbortIndicated)
|
|
{
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
status = STATUS_CONNECTION_INVALID;
|
|
}
|
|
else
|
|
{
|
|
pConnection->ConnectionState = UlConnectStateDisconnectPending;
|
|
|
|
//
|
|
// Set the flag indicating that a disconnect is pending &
|
|
// we're cleaning up.
|
|
//
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeDisconnectPendingFlag() );
|
|
UlpSetConnectionFlag( pConnection, MakeCleanupBegunFlag() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_CONNECTION_INVALID;
|
|
}
|
|
|
|
UlReleaseSpinLock( &pConnection->ConnectionStateSpinLock, OldIrql );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_SEND_AND_DISCONNECT,
|
|
pConnection->ReferenceCount,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// Get rid of our opaque id if we're a filtered connection.
|
|
// Also make sure we stop delivering AppWrite data to the parser.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
UlpCleanupConnectionId( pConnection );
|
|
UlDestroyFilterConnection( &pConnection->FilterInfo );
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto fatal;
|
|
}
|
|
}
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
ASSERT( pMdlChain != NULL );
|
|
ASSERT( Length > 0 );
|
|
ASSERT( pIrpContext != NULL );
|
|
|
|
//
|
|
// Allocate an IRP.
|
|
//
|
|
|
|
if (pOwnIrp)
|
|
{
|
|
pIrp = pOwnIrp;
|
|
}
|
|
else
|
|
{
|
|
pIrp = UlAllocateIrp(
|
|
pTdiObject->pDeviceObject->StackSize, // StackSize
|
|
FALSE // ChargeQuota
|
|
);
|
|
|
|
if (pIrp == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the send IRP, call the transport.
|
|
//
|
|
|
|
pIrp->RequestorMode = KernelMode;
|
|
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
pIrp->Tail.Overlay.OriginalFileObject = pTdiObject->pFileObject;
|
|
|
|
pIrpContext->TdiSendFlag = InitiateDisconnect? TDI_SEND_AND_DISCONNECT: 0;
|
|
pIrpContext->SendLength = Length;
|
|
|
|
TdiBuildSend(
|
|
pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UlpRestartSendData, // CompletionRoutine
|
|
pIrpContext, // Context
|
|
pMdlChain, // MdlAddress
|
|
pIrpContext->TdiSendFlag, // Flags
|
|
Length // SendLength
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlpSendRawData: allocated irp %p for connection %p\n",
|
|
pIrp,
|
|
pConnection
|
|
));
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pMdlTraceLog,
|
|
REF_ACTION_SEND_MDL,
|
|
PtrToLong(pMdlChain->Next), // bugbug64
|
|
pMdlChain,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
#ifdef SPECIAL_MDL_FLAG
|
|
{
|
|
PMDL scan = pMdlChain;
|
|
|
|
while (scan != NULL)
|
|
{
|
|
ASSERT( (scan->MdlFlags & SPECIAL_MDL_FLAG) == 0 );
|
|
scan->MdlFlags |= SPECIAL_MDL_FLAG;
|
|
scan = scan->Next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
IF_DEBUG2BOTH(TDI, VERBOSE)
|
|
{
|
|
PMDL pMdl;
|
|
ULONG i, NumMdls = 0;
|
|
|
|
for (pMdl = pMdlChain; pMdl != NULL; pMdl = pMdl->Next)
|
|
{
|
|
++NumMdls;
|
|
}
|
|
|
|
UlTrace(TDI, (
|
|
"UlpSendRawData: irp %p, %lu MDLs, %lu bytes, [[[[.\n",
|
|
pIrp, NumMdls, Length
|
|
));
|
|
|
|
for (pMdl = pMdlChain, i = 1; pMdl != NULL; pMdl = pMdl->Next, ++i)
|
|
{
|
|
PVOID pBuffer;
|
|
|
|
UlTrace(TDI, (
|
|
"UlpSendRawData: irp %p, MDL[%lu of %lu], %lu bytes.\n",
|
|
pIrp, i, NumMdls, pMdl->ByteCount
|
|
));
|
|
|
|
pBuffer = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);
|
|
|
|
if (pBuffer != NULL)
|
|
UlDbgPrettyPrintBuffer((UCHAR*) pBuffer, pMdl->ByteCount);
|
|
}
|
|
|
|
UlTrace(TDI, (
|
|
"UlpSendRawData: irp %p ]]]].\n",
|
|
pIrp
|
|
));
|
|
}
|
|
|
|
if (pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
|
|
{
|
|
ASSERT( NULL != g_TcpFastSendIPv4 );
|
|
pDispatchRoutine = g_TcpFastSendIPv4;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( TDI_ADDRESS_TYPE_IP6 == pConnection->AddressType );
|
|
pDispatchRoutine = g_TcpFastSendIPv6;
|
|
|
|
//
|
|
// It's possible that g_TcpFastSendIPv6 wasn't initialized; e.g., if
|
|
// someone does an 'ipv6 install' and an 'iisreset', then http.sys is
|
|
// not restarted and so UlInitializeTdi() is not called and the
|
|
// function pointer doesn't get initialized.
|
|
//
|
|
|
|
if (NULL == pDispatchRoutine)
|
|
{
|
|
status = UlpQueryTcpFastSend(
|
|
DD_TCPV6_DEVICE_NAME,
|
|
&g_TcpFastSendIPv6
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto fatal;
|
|
|
|
pDispatchRoutine = g_TcpFastSendIPv6;
|
|
}
|
|
}
|
|
|
|
ASSERT( NULL != pDispatchRoutine );
|
|
|
|
//
|
|
// Add a reference to the connection, then call the driver to initiate
|
|
// the send.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
|
|
IoSetNextIrpStackLocation(pIrp);
|
|
|
|
(*pDispatchRoutine)(
|
|
pIrp,
|
|
IoGetCurrentIrpStackLocation(pIrp)
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlpSendRawData: called driver for irp %p; "
|
|
"returning STATUS_PENDING\n",
|
|
pIrp
|
|
));
|
|
|
|
//
|
|
// We don't return the status from calling Tcp's fast dispatch routine
|
|
// since the completion for Irp is guaranteed and will have the
|
|
// appropriate status.
|
|
//
|
|
|
|
return STATUS_PENDING;
|
|
|
|
fatal:
|
|
|
|
ASSERT( !NT_SUCCESS(status) );
|
|
|
|
if (pIrp != NULL && pIrp != pOwnIrp)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
(VOID) UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
return status;
|
|
|
|
} // UlpSendRawData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Receives data from the specified connection. This function is
|
|
typically used after a receive indication handler has failed to
|
|
consume all of the indicated data.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pBuffer - Supplies a pointer to the target buffer for the received
|
|
data.
|
|
|
|
BufferLength - Supplies the length of pBuffer.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the listening endpoint is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpReceiveRawData(
|
|
IN PVOID pConnectionContext,
|
|
IN PVOID pBuffer,
|
|
IN ULONG BufferLength,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PIRP pIrp;
|
|
PMDL pMdl;
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
ASSERT( pCompletionRoutine != NULL );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpReceiveRawData: connection %p, buffer %p, length %lu\n",
|
|
pConnection,
|
|
pBuffer,
|
|
BufferLength
|
|
));
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on failure.
|
|
//
|
|
|
|
pIrpContext = NULL;
|
|
pIrp = NULL;
|
|
pMdl = NULL;
|
|
|
|
//
|
|
// Create & initialize a receive IRP.
|
|
//
|
|
|
|
pIrp = UlAllocateIrp(
|
|
pTdiObject->pDeviceObject->StackSize, // StackSize
|
|
FALSE // ChargeQuota
|
|
);
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
//
|
|
// Snag an IRP context.
|
|
//
|
|
|
|
pIrpContext = UlPplAllocateIrpContext();
|
|
|
|
if (pIrpContext != NULL)
|
|
{
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->pOwnIrp = NULL;
|
|
pIrpContext->OwnIrpContext = FALSE;
|
|
|
|
//
|
|
// Create an MDL describing the client's buffer.
|
|
//
|
|
|
|
pMdl = UlAllocateMdl(
|
|
pBuffer, // VirtualAddress
|
|
BufferLength, // Length
|
|
FALSE, // SecondaryBuffer
|
|
FALSE, // ChargeQuota
|
|
NULL // Irp
|
|
);
|
|
|
|
if (pMdl != NULL)
|
|
{
|
|
//
|
|
// Adjust the MDL for our non-paged buffer.
|
|
//
|
|
|
|
MmBuildMdlForNonPagedPool( pMdl );
|
|
|
|
//
|
|
// Reference the connection, finish building the IRP.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
|
|
TdiBuildReceive(
|
|
pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UlpRestartClientReceive, // CompletionRoutine
|
|
pIrpContext, // CompletionContext
|
|
pMdl, // Mdl
|
|
TDI_RECEIVE_NORMAL, // Flags
|
|
BufferLength // Length
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlpReceiveRawData: allocated irp %p for connection %p\n",
|
|
pIrp,
|
|
pConnection
|
|
));
|
|
|
|
//
|
|
// Let the transport do the rest.
|
|
//
|
|
|
|
UlCallDriver( pTdiObject->pDeviceObject, pIrp );
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We only make it this point if we hit an allocation failure.
|
|
//
|
|
|
|
if (pMdl != NULL)
|
|
{
|
|
UlFreeMdl( pMdl );
|
|
}
|
|
|
|
if (pIrpContext != NULL)
|
|
{
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
}
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
status = UlInvokeCompletionRoutine(
|
|
STATUS_INSUFFICIENT_RESOURCES,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
return status;
|
|
|
|
} // UlpReceiveRawData
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
A Dummy handler that is called by the filter code. This just calls
|
|
back into UlHttpReceive.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpDummyReceiveHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN PVOID pTsdu,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesUnreceived,
|
|
OUT ULONG *pBytesTaken
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_CONNECTION pConnection;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT(pTdiEventContext == NULL);
|
|
ASSERT(BytesUnreceived == 0);
|
|
|
|
UNREFERENCED_PARAMETER(pTdiEventContext);
|
|
|
|
pConnection = (PUL_CONNECTION)ConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
return (pEndpoint->pDataReceiveHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
BytesUnreceived,
|
|
pBytesTaken
|
|
);
|
|
|
|
} // UlpDummyReceiveHandler
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for normal receive data.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ENDPOINT.
|
|
|
|
ConnectionContext - Supplies the context associated with the
|
|
connection object. This should be a PUL_CONNECTION.
|
|
|
|
ReceiveFlags - Supplies the receive flags. This will be zero or more
|
|
TDI_RECEIVE_* flags.
|
|
|
|
BytesIndicated - Supplies the number of bytes indicated in pTsdu.
|
|
|
|
BytesAvailable - Supplies the number of bytes available in this
|
|
TSDU.
|
|
|
|
pBytesTaken - Receives the number of bytes consumed by this handler.
|
|
|
|
pTsdu - Supplies a pointer to the indicated data.
|
|
|
|
pIrp - Receives an IRP if the handler needs more data than indicated.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpReceiveHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN ULONG ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT ULONG *pBytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PIRP *pIrp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_CONNECTION pConnection;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
KIRQL OldIrql;
|
|
|
|
|
|
UL_ENTER_DRIVER("UlpReceiveHandler", NULL);
|
|
|
|
UNREFERENCED_PARAMETER(ReceiveFlags);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
pConnection = (PUL_CONNECTION)ConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpReceiveHandler: endpoint %p, connection %p, length %lu,%lu\n",
|
|
pTdiEventContext,
|
|
(PVOID)ConnectionContext,
|
|
BytesIndicated,
|
|
BytesAvailable
|
|
));
|
|
|
|
//
|
|
// Clear the bytes taken output var
|
|
//
|
|
|
|
*pBytesTaken = 0;
|
|
|
|
//
|
|
// Bail out if this is an idle connection. On a checked build, we should
|
|
// assert because TCP should not indicate data after the disconnect.
|
|
// Bug related is 449527 which is fixed in build 3557.main.
|
|
//
|
|
|
|
if (0 == pConnection->ConnectionFlags.Value)
|
|
{
|
|
ASSERT( FALSE );
|
|
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Wait for the local address to be set just in case the receive happens
|
|
// before accept. This is possible (but rare) on MP machines even when
|
|
// using DIRECT_ACCEPT. We set the ReceivePending flag and reject
|
|
// the data if this ever happens. When accept is completed, we will build
|
|
// a receive IRP to flush the data if ReceivePending is set.
|
|
//
|
|
|
|
if (0 == pConnection->ConnectionFlags.LocalAddressValid)
|
|
{
|
|
UlAcquireSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
&OldIrql
|
|
);
|
|
|
|
if (0 == pConnection->ConnectionFlags.LocalAddressValid)
|
|
{
|
|
UlpSetConnectionFlag( pConnection, MakeReceivePendingFlag() );
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
goto end;
|
|
}
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
}
|
|
|
|
//
|
|
// Give the client a crack at the data.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Needs to go through a filter.
|
|
//
|
|
|
|
status = UlFilterReceiveHandler(
|
|
&pConnection->FilterInfo,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
BytesAvailable - BytesIndicated,
|
|
pBytesTaken
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Go directly to client (UlHttpReceive).
|
|
//
|
|
|
|
status = (pEndpoint->pDataReceiveHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
BytesAvailable - BytesIndicated,
|
|
pBytesTaken
|
|
);
|
|
}
|
|
|
|
ASSERT( *pBytesTaken <= BytesIndicated );
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
goto end;
|
|
}
|
|
else if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
ASSERT(!"How could this ever happen?");
|
|
|
|
//
|
|
// The client consumed part of the indicated data.
|
|
//
|
|
// A subsequent receive indication will be made to the client when
|
|
// additional data is available. This subsequent indication will
|
|
// include the unconsumed data from the current indication plus
|
|
// any additional data received.
|
|
//
|
|
// We need to allocate a receive buffer so we can pass an IRP back
|
|
// to the transport.
|
|
//
|
|
|
|
status = UlpBuildTdiReceiveBuffer(pTdiObject, pConnection, pIrp);
|
|
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
//
|
|
// Make the next stack location current. Normally, UlCallDriver
|
|
// would do this for us, but since we're bypassing UlCallDriver,
|
|
// we must do it ourselves.
|
|
//
|
|
|
|
IoSetNextIrpStackLocation( *pIrp );
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we made it this far, then we've hit a fatal condition. Either the
|
|
// client returned a status code other than STATUS_SUCCESS or
|
|
// STATUS_MORE_PROCESSING_REQUIRED, or we failed to allocation the
|
|
// receive IRP to pass back to the transport. In either case, we need
|
|
// to abort the connection.
|
|
//
|
|
|
|
UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE, // AbortiveDisconnect
|
|
NULL, // pCompletionRoutine
|
|
NULL // pCompletionContext
|
|
);
|
|
|
|
end:
|
|
|
|
UlTrace(TDI, (
|
|
"UlpReceiveHandler: endpoint %p, connection %p, length %lu,%lu, "
|
|
"taken %lu, status 0x%x\n",
|
|
pTdiEventContext,
|
|
(PVOID)ConnectionContext,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
*pBytesTaken,
|
|
status
|
|
));
|
|
|
|
UL_LEAVE_DRIVER("UlpReceiveHandler");
|
|
|
|
return status;
|
|
|
|
} // UlpReceiveHandler
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for expedited receive data.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ENDPOINT.
|
|
|
|
ConnectionContext - Supplies the context associated with the
|
|
connection object. This should be a PUL_CONNECTION.
|
|
|
|
ReceiveFlags - Supplies the receive flags. This will be zero or more
|
|
TDI_RECEIVE_* flags.
|
|
|
|
BytesIndiated - Supplies the number of bytes indicated in pTsdu.
|
|
|
|
BytesAvailable - Supplies the number of bytes available in this
|
|
TSDU.
|
|
|
|
pBytesTaken - Receives the number of bytes consumed by this handler.
|
|
|
|
pTsdu - Supplies a pointer to the indicated data.
|
|
|
|
ppIrp - Receives an IRP if the handler needs more data than indicated.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpReceiveExpeditedHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN ULONG ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT ULONG *pBytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PIRP *ppIrp
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_CONNECTION pConnection;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
|
|
|
|
UL_ENTER_DRIVER("UlpReceiveExpeditedHandler", NULL);
|
|
|
|
UNREFERENCED_PARAMETER(ReceiveFlags);
|
|
UNREFERENCED_PARAMETER(BytesIndicated);
|
|
UNREFERENCED_PARAMETER(pTsdu);
|
|
UNREFERENCED_PARAMETER(ppIrp);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pEndpoint = (PUL_ENDPOINT)pTdiEventContext;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
pConnection = (PUL_CONNECTION)ConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpReceiveExpeditedHandler: endpoint %p, connection %p, length %lu,%lu\n",
|
|
pTdiEventContext,
|
|
(PVOID)ConnectionContext,
|
|
BytesIndicated,
|
|
BytesAvailable
|
|
));
|
|
|
|
//
|
|
// We don't support expedited data, so just consume it all.
|
|
//
|
|
|
|
*pBytesTaken = BytesAvailable;
|
|
|
|
UL_LEAVE_DRIVER("UlpReceiveExpeditedHandler");
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlpReceiveExpeditedHandler
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for accept IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_CONNECTION.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartAccept(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection;
|
|
PUL_ENDPOINT pEndpoint;
|
|
BOOLEAN NeedDisconnect = FALSE;
|
|
BOOLEAN NeedAbort = FALSE;
|
|
BOOLEAN ReceivePending = FALSE;
|
|
NTSTATUS IrpStatus;
|
|
NTSTATUS Status;
|
|
PIRP pReceiveIrp;
|
|
PTRANSPORT_ADDRESS pAddress;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER( pDeviceObject );
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pConnection = (PUL_CONNECTION) pContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->ConnectionFlags.AcceptPending );
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartAccept: irp %p, endpoint %p, connection %p, status 0x%x\n",
|
|
pIrp,
|
|
pEndpoint,
|
|
pConnection,
|
|
pIrp->IoStatus.Status
|
|
));
|
|
|
|
//
|
|
// Capture the status from the IRP then free it.
|
|
//
|
|
|
|
IrpStatus = pIrp->IoStatus.Status;
|
|
|
|
//
|
|
// Assert for TCP bug 477465.
|
|
//
|
|
|
|
ASSERT( STATUS_CONNECTION_ACTIVE != IrpStatus );
|
|
|
|
//
|
|
// If the connection was fully accepted (successfully), then
|
|
// move it to the endpoint's active list.
|
|
//
|
|
|
|
if (NT_SUCCESS(IrpStatus))
|
|
{
|
|
//
|
|
// Get the Local Address Info.
|
|
//
|
|
|
|
pAddress = &(pConnection->Ta.Ta);
|
|
ASSERT( pAddress->Address[0].AddressType == pConnection->AddressType );
|
|
|
|
RtlCopyMemory(
|
|
pConnection->LocalAddress,
|
|
pAddress->Address[0].Address,
|
|
pAddress->Address[0].AddressLength
|
|
);
|
|
|
|
//
|
|
// Set the AcceptComplete flag. If a disconnect has
|
|
// already been indicated, then remember this fact so we can
|
|
// fake a call to the client's connection disconnect handler
|
|
// after we invoke the connection complete handler.
|
|
//
|
|
|
|
UlAcquireSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
&OldIrql
|
|
);
|
|
|
|
UlpEnqueueActiveConnection( pConnection );
|
|
|
|
//
|
|
// Mark we are done with the accept & change state.
|
|
//
|
|
|
|
ASSERT( UlConnectStateConnectIdle == pConnection->ConnectionState );
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeLocalAddressValidFlag() );
|
|
UlpSetConnectionFlag( pConnection, MakeAcceptCompleteFlag() );
|
|
|
|
if (pConnection->ConnectionFlags.AbortIndicated)
|
|
{
|
|
//
|
|
// We got reset before we are up, transition directly to
|
|
// UlConnectStateConnectCleanup since UlpDisconnectHandler
|
|
// does nothing when it got the abort indication as the
|
|
// connection was still idle at that point.
|
|
//
|
|
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
NeedAbort = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pConnection->ConnectionState = UlConnectStateConnectReady;
|
|
}
|
|
|
|
if (pConnection->ConnectionFlags.DisconnectIndicated)
|
|
{
|
|
NeedDisconnect = TRUE;
|
|
}
|
|
|
|
if (pConnection->ConnectionFlags.ReceivePending)
|
|
{
|
|
ReceivePending = TRUE;
|
|
}
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
}
|
|
|
|
//
|
|
// Tell the client that the connection is complete.
|
|
//
|
|
|
|
(pEndpoint->pConnectionCompleteHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext,
|
|
IrpStatus
|
|
);
|
|
|
|
//
|
|
// If the accept failed, then mark the connection so we know there is
|
|
// no longer an accept pending, enqueue the connection back onto the
|
|
// endpoint's idle list.
|
|
|
|
if (!NT_SUCCESS(IrpStatus))
|
|
{
|
|
//
|
|
// Need to get rid of our opaque id if we're a filtered connection.
|
|
//
|
|
|
|
pConnection->ConnectionFlags.AcceptPending = 0;
|
|
UlpCleanupEarlyConnection( pConnection );
|
|
}
|
|
else
|
|
{
|
|
if (ReceivePending && !NeedDisconnect && !NeedAbort)
|
|
{
|
|
//
|
|
// We may have pending receives that we rejected early on
|
|
// inside the receive handler. Build an IRP to flush the
|
|
// data now. Do this only after we have completed the connect.
|
|
// Otherwise we can either process receives without timer being
|
|
// properly initialized or we can initilize timer on a free/idle
|
|
// connection.
|
|
//
|
|
|
|
Status = UlpBuildTdiReceiveBuffer(
|
|
&pConnection->ConnectionObject,
|
|
pConnection,
|
|
&pReceiveIrp
|
|
);
|
|
|
|
if (Status != STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
{
|
|
UlCallDriver(
|
|
pConnection->ConnectionObject.pDeviceObject,
|
|
pReceiveIrp
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tell the client that the connection was disconnected.
|
|
//
|
|
|
|
if (NeedDisconnect)
|
|
{
|
|
ASSERT( !NeedAbort );
|
|
UlpDoDisconnectNotification( pConnection );
|
|
}
|
|
|
|
if (NeedAbort)
|
|
{
|
|
ASSERT( !NeedDisconnect );
|
|
|
|
//
|
|
// We now may be able to remove the final reference since
|
|
// we have now set the AcceptComplete flag.
|
|
//
|
|
|
|
UlpRemoveFinalReference( pConnection );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Drop the reference added in UlpConnectHandler.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UlpRestartAccept
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for send IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartSendData(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
BOOLEAN OwnIrpContext;
|
|
PIRP pOwnIrp;
|
|
PUL_COMPLETION_ROUTINE pCompletionRoutine;
|
|
PVOID pCompletionContext;
|
|
NTSTATUS SendStatus = pIrp->IoStatus.Status;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT)pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
ASSERT( pIrpContext->pCompletionRoutine != NULL );
|
|
|
|
OwnIrpContext = pIrpContext->OwnIrpContext;
|
|
pOwnIrp = pIrpContext->pOwnIrp;
|
|
pCompletionRoutine = pIrpContext->pCompletionRoutine;
|
|
pCompletionContext = pIrpContext->pCompletionContext;
|
|
|
|
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( IS_VALID_ENDPOINT( pConnection->pOwningEndpoint ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartSendData: irp %p, connection %p, status 0x%x, info %Iu\n",
|
|
pIrp,
|
|
pConnection,
|
|
pIrp->IoStatus.Status,
|
|
pIrp->IoStatus.Information
|
|
));
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pMdlTraceLog,
|
|
REF_ACTION_SEND_MDL_COMPLETE,
|
|
PtrToLong(pIrp->MdlAddress->Next), // bugbug64
|
|
pIrp->MdlAddress,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
#ifdef SPECIAL_MDL_FLAG
|
|
{
|
|
PMDL scan = pIrp->MdlAddress;
|
|
|
|
while (scan != NULL)
|
|
{
|
|
ASSERT( (scan->MdlFlags & SPECIAL_MDL_FLAG) != 0 );
|
|
scan->MdlFlags &= ~SPECIAL_MDL_FLAG;
|
|
scan = scan->Next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// As needed, fake a disconnect indication and a disconnect completion
|
|
// if this comes from SEND_AND_DISCONNECT.
|
|
//
|
|
|
|
if (TDI_SEND_AND_DISCONNECT == pIrpContext->TdiSendFlag)
|
|
{
|
|
//
|
|
// SendStatus needs to be adjusted to STATUS_SUCCESS if we have sent
|
|
// all bytes asked for. This is because SEND_AND_DISCONNECT completes
|
|
// with the status of *both* send and disconnect.
|
|
//
|
|
|
|
if (pIrp->IoStatus.Information == pIrpContext->SendLength)
|
|
{
|
|
SendStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// This is the last action on the connectio since send
|
|
// and disconnect completion means the connection has been
|
|
// closed by both local and remote sides.
|
|
//
|
|
|
|
if (!pConnection->ConnectionFlags.AbortIndicated &&
|
|
!pConnection->ConnectionFlags.DisconnectIndicated &&
|
|
!pConnection->ConnectionFlags.TdiConnectionInvalid)
|
|
{
|
|
//
|
|
// Fake a gracefull disconnect since it didn't happen.
|
|
//
|
|
// No need to sync with the disconnect handler since it
|
|
// will not run during or after the calling of this
|
|
// completion routine.
|
|
//
|
|
|
|
UlpDisconnectHandler(
|
|
pConnection->pOwningEndpoint,
|
|
pConnection,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
TDI_DISCONNECT_RELEASE
|
|
);
|
|
}
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = NULL;
|
|
pIrpContext->pCompletionContext = NULL;
|
|
pIrpContext->pOwnIrp = pIrp;
|
|
pIrpContext->OwnIrpContext = TRUE;
|
|
|
|
(VOID) UlpRestartDisconnect(
|
|
pDeviceObject,
|
|
pIrp,
|
|
pIrpContext
|
|
);
|
|
|
|
//
|
|
// UlpRestartDisconnect drops the reference we added in UlSendData().
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Remove the reference we added in UlSendData().
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
}
|
|
|
|
//
|
|
// Tell the client that the send is complete.
|
|
//
|
|
|
|
(VOID) UlInvokeCompletionRoutine(
|
|
SendStatus,
|
|
pIrp->IoStatus.Information,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
//
|
|
// Free the context & the IRP since we're done with them, then
|
|
// tell IO to stop processing the IRP.
|
|
//
|
|
|
|
if (!OwnIrpContext)
|
|
{
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
}
|
|
|
|
if (!pOwnIrp)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UlpRestartSendData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Increments the reference count on the specified endpoint.
|
|
|
|
Arguments:
|
|
|
|
pEndpoint - Supplies the endpoint to reference.
|
|
|
|
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
|
|
containing the calling function.
|
|
|
|
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
|
|
the calling function.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpReferenceEndpoint(
|
|
IN PUL_ENDPOINT pEndpoint,
|
|
IN REFTRACE_ACTION Action
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG RefCount;
|
|
|
|
UNREFERENCED_PARAMETER( Action );
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// Reference it.
|
|
//
|
|
|
|
RefCount = InterlockedIncrement( &pEndpoint->ReferenceCount );
|
|
ASSERT( RefCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pTdiTraceLog,
|
|
Action,
|
|
RefCount,
|
|
pEndpoint,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace2Both(TDI, REFCOUNT, (
|
|
"UlpReferenceEndpoint: endpoint %p, refcount %ld\n",
|
|
pEndpoint,
|
|
RefCount
|
|
));
|
|
|
|
} // UlpReferenceEndpoint
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Decrements the reference count on the specified endpoint.
|
|
|
|
Arguments:
|
|
|
|
pEndpoint - Supplies the endpoint to dereference.
|
|
|
|
pConnToEnqueue - if non-NULL, this routine will enqueue to the idle list.
|
|
|
|
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
|
|
containing the calling function.
|
|
|
|
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
|
|
the calling function.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDereferenceEndpoint(
|
|
IN PUL_ENDPOINT pEndpoint,
|
|
IN PUL_CONNECTION pConnToEnqueue,
|
|
IN REFTRACE_ACTION Action
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList;
|
|
LONG RefCount;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER( Action );
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// Enqueue the connection to the idle list if pConnToEnqueue is not NULL.
|
|
//
|
|
|
|
if (NULL != pConnToEnqueue)
|
|
{
|
|
ASSERT( IS_VALID_CONNECTION( pConnToEnqueue ) );
|
|
|
|
pAddrIdleList = pConnToEnqueue->pOwningAddrIdleList;
|
|
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pAddrIdleList ) );
|
|
ASSERT( pAddrIdleList->pOwningEndpoint == pEndpoint );
|
|
ASSERT( pConnToEnqueue->ConnectionFlags.Value == 0 );
|
|
ASSERT( UlpConnectionIsOnValidList( pConnToEnqueue ) );
|
|
ASSERT( pConnToEnqueue->OriginProcessor < g_UlNumberOfProcessors );
|
|
|
|
//
|
|
// The idle list holds a reference; the filter channel (if it exists)
|
|
// holds another reference.
|
|
//
|
|
|
|
ASSERT( pConnToEnqueue->ReferenceCount ==
|
|
1 + (pConnToEnqueue->FilterInfo.pFilterChannel != NULL) );
|
|
|
|
pConnToEnqueue->ConnListState = IdleConnList;
|
|
|
|
//
|
|
// CODEWORK: We should monitor the connection serving rate
|
|
// and start destroying freed connections if the rate is too
|
|
// small. We used to call PpslFree here which does some basic
|
|
// form of that test. (Delta check) however it wasn't good
|
|
// enough so we have disabled it for the time being.
|
|
//
|
|
|
|
if (FALSE == PpslFreeSpecifyList(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
&pConnToEnqueue->IdleSListEntry,
|
|
pConnToEnqueue->OriginProcessor
|
|
))
|
|
{
|
|
//
|
|
// We failed to free it to the per-processor list
|
|
// We definetely need to destroy this connection.
|
|
// Schedule a cleanup.
|
|
//
|
|
|
|
pConnToEnqueue->IdleSListEntry.Next = NULL;
|
|
pConnToEnqueue->ConnListState = NoConnList;
|
|
|
|
UL_QUEUE_WORK_ITEM(
|
|
&pConnToEnqueue->WorkItem,
|
|
&UlpDestroyConnectionWorker
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Dereference the endpoint.
|
|
//
|
|
|
|
RefCount = InterlockedDecrement( &pEndpoint->ReferenceCount );
|
|
ASSERT( RefCount >= 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pTdiTraceLog,
|
|
Action,
|
|
RefCount,
|
|
pEndpoint,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace2Both(TDI, REFCOUNT, (
|
|
"UlpDereferenceEndpoint: endpoint %p, refcount %ld\n",
|
|
pEndpoint,
|
|
RefCount
|
|
));
|
|
|
|
//
|
|
// Has the last external reference to the endpoint been removed?
|
|
//
|
|
|
|
if (RefCount == 0)
|
|
{
|
|
//
|
|
// The final references to the endpoint have been removed, so it's
|
|
// time to destroy the endpoint. We'll remove the endpoint from the
|
|
// global list and move it to the deleted list (if necessary),
|
|
// release the TDI spinlock, then destroy the endpoint.
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
|
|
|
|
if (!pEndpoint->Deleted)
|
|
{
|
|
//
|
|
// If this routine was called by the `fatal' section of
|
|
// UlCreateListeningEndpoint, then the endpoint was never
|
|
// added to g_TdiEndpointListHead.
|
|
//
|
|
|
|
if (NULL != pEndpoint->GlobalEndpointListEntry.Flink)
|
|
{
|
|
RemoveEntryList( &pEndpoint->GlobalEndpointListEntry );
|
|
}
|
|
|
|
InsertTailList(
|
|
&g_TdiDeletedEndpointListHead,
|
|
&pEndpoint->GlobalEndpointListEntry
|
|
);
|
|
pEndpoint->Deleted = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( NULL != pEndpoint->GlobalEndpointListEntry.Flink );
|
|
}
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
|
|
//
|
|
// The endpoint is going away. Do final cleanup & resource
|
|
// release at passive IRQL.
|
|
//
|
|
// There is no chance that a replenish could be
|
|
// currently scheduled on pEndpoint->WorkItem, because
|
|
// we add a reference to the endpoint before scheduling the
|
|
// replenish, so we can't be here too.
|
|
//
|
|
// We need to schedule a worker item for the cleanup because
|
|
// this can be called within the context of a TDI indication
|
|
// (such as accept completion) in which case UlpDestroyEndpoint
|
|
// can close the same connection we have just pushed to the
|
|
// idle list in UlpRestartAccept, causing a deadlock.
|
|
//
|
|
|
|
UL_QUEUE_WORK_ITEM(
|
|
&pEndpoint->WorkItem,
|
|
&UlpEndpointCleanupWorker
|
|
);
|
|
}
|
|
|
|
} // UlpDereferenceEndpoint
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Increments the reference count on the specified connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to reference.
|
|
|
|
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
|
|
containing the calling function.
|
|
|
|
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
|
|
the calling function.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlReferenceConnection(
|
|
IN PVOID pConnectionContext
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
LONG RefCount;
|
|
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// Reference it.
|
|
//
|
|
|
|
RefCount = InterlockedIncrement( &pConnection->ReferenceCount );
|
|
ASSERT( RefCount > 1 );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_REFERENCE_UL_CONNECTION,
|
|
RefCount,
|
|
pConnection,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace2Both(TDI, REFCOUNT, (
|
|
"UlReferenceConnection: connection %p, refcount %ld\n",
|
|
pConnection,
|
|
RefCount
|
|
));
|
|
|
|
} // UlReferenceConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Decrements the reference count on the specified connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to dereference.
|
|
|
|
pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
|
|
containing the calling function.
|
|
|
|
LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
|
|
the calling function.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDereferenceConnection(
|
|
IN PVOID pConnectionContext
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
LONG RefCount;
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pConnectionContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// Must postpone the actual InterlockedDecrement until after the
|
|
// WRITE_REF_TRACE_LOG2 to prevent a race condition, where the
|
|
// pConnection->TraceLog is destroyed by another thread on the last
|
|
// dereference before the WRITE_REF_TRACE_LOG2 completes. This means
|
|
// that there's a slight race condition in what gets written to the
|
|
// tracelog, but we can live with that.
|
|
//
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pTdiTraceLog,
|
|
pConnection->pTraceLog,
|
|
REF_ACTION_DEREFERENCE_UL_CONNECTION,
|
|
pConnection->ReferenceCount - 1,
|
|
pConnection,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
RefCount = InterlockedDecrement( &pConnection->ReferenceCount );
|
|
ASSERT( RefCount >= 0 );
|
|
|
|
UlTrace2Both(TDI, REFCOUNT, (
|
|
"UlDereferenceConnection: connection %p, refcount %ld\n",
|
|
pConnection,
|
|
RefCount
|
|
));
|
|
|
|
if (RefCount == 0)
|
|
{
|
|
//
|
|
// The final reference to the connection has been removed, so
|
|
// it's time to destroy the connection.
|
|
//
|
|
|
|
ASSERT( UlpConnectionIsOnValidList( pConnection ) );
|
|
|
|
//
|
|
// If there's a filter token associated with the connection,
|
|
// cleanup must run under the system process at passive level
|
|
// (i.e., on one of our worker threads). Otherwise, we can
|
|
// clean it up directly.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.SslInfo.Token != NULL)
|
|
{
|
|
UL_QUEUE_WORK_ITEM(
|
|
&pConnection->WorkItem,
|
|
&UlpConnectionCleanupWorker
|
|
);
|
|
}
|
|
else
|
|
{
|
|
UlpConnectionCleanupWorker( &pConnection->WorkItem );
|
|
}
|
|
}
|
|
|
|
} // UlDereferenceConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Deferred cleanup routine for dead endpoints.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - Supplies a pointer to the work item queued. This should
|
|
point to the WORK_ITEM structure embedded in a UL_ENDPOINT.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpEndpointCleanupWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
pEndpoint = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_ENDPOINT,
|
|
WorkItem
|
|
);
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// Nuke it.
|
|
//
|
|
|
|
UlpDestroyEndpoint( pEndpoint );
|
|
|
|
} // UlpEndpointCleanupWorker
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Removes the opaque id from a connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpCleanupConnectionId(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
HTTP_RAW_CONNECTION_ID ConnectionId;
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
//
|
|
// Pull the ID off the connection and set the connection's ID to 0.
|
|
//
|
|
|
|
ConnectionId = UlInterlockedExchange64(
|
|
(PLONGLONG) &pConnection->FilterInfo.ConnectionId,
|
|
HTTP_NULL_ID
|
|
);
|
|
|
|
if (!HTTP_IS_NULL_ID(&ConnectionId))
|
|
{
|
|
UlTrace(TDI, (
|
|
"UlpCleanupConnectionId: conn=%p id=%I64x\n",
|
|
pConnection, ConnectionId
|
|
));
|
|
|
|
UlFreeOpaqueId( ConnectionId, UlOpaqueIdTypeRawConnection );
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
}
|
|
|
|
} // UlpCleanupConnectionId
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function gets called if RestartAccept fails and we cannot establish
|
|
a connection on a secure endpoint, or if something goes wrong in
|
|
UlpConnectHandler.
|
|
|
|
The connections over secure endpoints keep an extra refcount to
|
|
the UL_CONNECTION because of their opaqueid. They normally
|
|
get removed after the CloseRawConnection happens but in the above case
|
|
close won't happen and we have to explicitly cleanup the id.
|
|
|
|
Arguments:
|
|
|
|
pConnection
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpCleanupEarlyConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
UL_CONNECTION_FLAGS Flags;
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
//
|
|
// If we are failing early we should never have been to
|
|
// FinalReferenceRemoved in the first place.
|
|
//
|
|
|
|
Flags.Value = *((volatile LONG *) &pConnection->ConnectionFlags.Value);
|
|
|
|
ASSERT( !Flags.FinalReferenceRemoved );
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Cleanup opaque id. And release the final refcount
|
|
//
|
|
|
|
UlpCleanupConnectionId( pConnection );
|
|
}
|
|
|
|
//
|
|
// Remove the final reference.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
} // UlpCleanupEarlyConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Deferred cleanup routine for dead connections. We have to be queued as a
|
|
work item and should be running on the passive IRQL. See below comment.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - Supplies a pointer to the work item queued. This should
|
|
point to the WORK_ITEM structure embedded in a UL_CONNECTION.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpConnectionCleanupWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection;
|
|
PUL_CONNECTION pConnToEnqueue = NULL; // don't reuse
|
|
PUL_ENDPOINT pEndpoint;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
pConnection = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_CONNECTION,
|
|
WorkItem
|
|
);
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( IS_VALID_ENDPOINT( pConnection->pOwningEndpoint ) );
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pConnection->pOwningAddrIdleList ) );
|
|
|
|
ASSERT( pConnection->ReferenceCount == 0 );
|
|
ASSERT( pConnection->HttpConnection.RefCount == 0 );
|
|
|
|
//
|
|
// Grab the endpoint.
|
|
//
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
|
|
//
|
|
// Now remove the connection from the active list
|
|
//
|
|
|
|
ASSERT( UlpConnectionIsOnValidList( pConnection ) );
|
|
|
|
pConnection->ConnListState = NoConnList;
|
|
|
|
ASSERT( UlpConnectionIsOnValidList( pConnection ) );
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Get rid of any buffers we allocated for
|
|
// certificate information.
|
|
//
|
|
if (pConnection->FilterInfo.SslInfo.pServerCertData)
|
|
{
|
|
UL_FREE_POOL(
|
|
pConnection->FilterInfo.SslInfo.pServerCertData,
|
|
UL_SSL_CERT_DATA_POOL_TAG
|
|
);
|
|
|
|
pConnection->FilterInfo.SslInfo.pServerCertData = NULL;
|
|
}
|
|
|
|
if (pConnection->FilterInfo.SslInfo.pCertEncoded)
|
|
{
|
|
UL_FREE_POOL(
|
|
pConnection->FilterInfo.SslInfo.pCertEncoded,
|
|
UL_SSL_CERT_DATA_POOL_TAG
|
|
);
|
|
|
|
pConnection->FilterInfo.SslInfo.pCertEncoded = NULL;
|
|
}
|
|
|
|
if (pConnection->FilterInfo.SslInfo.Token)
|
|
{
|
|
HANDLE Token = (HANDLE) pConnection->FilterInfo.SslInfo.Token;
|
|
|
|
pConnection->FilterInfo.SslInfo.Token = NULL;
|
|
|
|
//
|
|
// If we are not running under the system process. And if the
|
|
// thread we are running under has some APCs queued currently
|
|
// KeAttachProcess won't allow us to attach to another process
|
|
// and will bugcheck 5. We have to be queued as a work item and
|
|
// should be running on the passive IRQL.
|
|
//
|
|
|
|
ASSERT( PsGetCurrentProcess() == (PEPROCESS) g_pUlSystemProcess );
|
|
|
|
ZwClose(Token);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the disconnect/abort completed with an error.
|
|
//
|
|
|
|
if (pConnection->ConnectionFlags.TdiConnectionInvalid)
|
|
{
|
|
status = STATUS_CONNECTION_INVALID;
|
|
}
|
|
|
|
//
|
|
// If the connection is still ok and we're reusing
|
|
// connection objects, throw it back on the idle list.
|
|
//
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// Release the filter channel.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
if (!HTTP_IS_NULL_ID(&pConnection->FilterInfo.ConnectionId))
|
|
{
|
|
UlpCleanupConnectionId( pConnection );
|
|
}
|
|
|
|
DEREFERENCE_FILTER_CHANNEL( pConnection->FilterInfo.pFilterChannel );
|
|
pConnection->FilterInfo.pFilterChannel = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the connection for reuse.
|
|
//
|
|
|
|
status = UlpInitializeConnection( pConnection );
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// Stick the connection back on the idle list for reuse.
|
|
//
|
|
|
|
pConnToEnqueue = pConnection;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Active connections hold a reference to the ENDPOINT. Release
|
|
// that reference. See the comment on UlpCreateConnection.
|
|
//
|
|
|
|
DEREFERENCE_ENDPOINT_CONNECTION(
|
|
pEndpoint,
|
|
pConnToEnqueue,
|
|
REF_ACTION_CONN_CLEANUP
|
|
);
|
|
|
|
//
|
|
// If anything went amiss, blow away the connection.
|
|
//
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UL_QUEUE_WORK_ITEM(
|
|
&pConnection->WorkItem,
|
|
&UlpDestroyConnectionWorker
|
|
);
|
|
}
|
|
|
|
} // UlpConnectionCleanupWorker
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Associates the TDI connection object contained in the specified
|
|
connection to the TDI address object contained in the specified
|
|
endpoint.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to associate with the endpoint.
|
|
|
|
pEndpoint - Supplies the endpoint to associated with the connection.
|
|
|
|
pAddrIdleList - Supplies the address idle list owned by the endpoint
|
|
to associate with the connection.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpAssociateConnection(
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
HANDLE handle;
|
|
TDI_REQUEST_USER_ASSOCIATE associateInfo;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pAddrIdleList ) );
|
|
ASSERT( IS_VALID_ENDPOINT( pAddrIdleList->pOwningEndpoint ) );
|
|
ASSERT( pConnection->pOwningEndpoint == NULL );
|
|
|
|
//
|
|
// Associate the connection with the address object.
|
|
//
|
|
|
|
associateInfo.AddressHandle = pAddrIdleList->AddressObject.Handle;
|
|
ASSERT( associateInfo.AddressHandle != NULL );
|
|
|
|
handle = pConnection->ConnectionObject.Handle;
|
|
ASSERT( handle != NULL );
|
|
|
|
status = ZwDeviceIoControlFile(
|
|
handle, // FileHandle
|
|
NULL, // Event
|
|
NULL, // ApcRoutine
|
|
NULL, // ApcContext
|
|
&ioStatusBlock, // IoStatusBlock
|
|
IOCTL_TDI_ASSOCIATE_ADDRESS, // IoControlCode
|
|
&associateInfo, // InputBuffer
|
|
sizeof(associateInfo), // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
status = ZwWaitForSingleObject(
|
|
handle, // Handle
|
|
TRUE, // Alertable
|
|
NULL // Timeout
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
pConnection->pOwningEndpoint = pAddrIdleList->pOwningEndpoint;
|
|
pConnection->pOwningAddrIdleList = pAddrIdleList;
|
|
pConnection->pConnectionDestroyedHandler = pAddrIdleList->pOwningEndpoint->pConnectionDestroyedHandler;
|
|
pConnection->pListeningContext = pAddrIdleList->pOwningEndpoint->pListeningContext;
|
|
}
|
|
else
|
|
{
|
|
UlTrace(TDI, (
|
|
"UlpAssociateConnection conn=%p, endp=%p, addr idle list=%p, status = 0x%x\n",
|
|
pConnection,
|
|
pAddrIdleList->pOwningEndpoint,
|
|
pAddrIdleList,
|
|
status
|
|
));
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlpAssociateConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Disassociates the TDI connection object contained in the specified
|
|
connection from its TDI address object.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to disassociate.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpDisassociateConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
HANDLE handle;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( IS_VALID_ENDPOINT( pConnection->pOwningEndpoint ) );
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pConnection->pOwningAddrIdleList ) );
|
|
|
|
//
|
|
// Disassociate the connection from the address object.
|
|
//
|
|
|
|
handle = pConnection->ConnectionObject.Handle;
|
|
|
|
status = ZwDeviceIoControlFile(
|
|
handle, // FileHandle
|
|
NULL, // Event
|
|
NULL, // ApcRoutine
|
|
NULL, // ApcContext
|
|
&ioStatusBlock, // IoStatusBlock
|
|
IOCTL_TDI_DISASSOCIATE_ADDRESS, // IoControlCode
|
|
NULL, // InputBuffer
|
|
0, // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
status = ZwWaitForSingleObject(
|
|
handle, // Handle
|
|
TRUE, // Alertable
|
|
NULL // Timeout
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
//
|
|
// Proceed with the disassociate even if the IOCTL failed.
|
|
//
|
|
|
|
pConnection->pOwningEndpoint = NULL;
|
|
pConnection->pOwningAddrIdleList = NULL;
|
|
|
|
return status;
|
|
|
|
} // UlpDisassociateConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initalized a UL_ADDR_IDLE_LIST and opens TDI handles/file objects
|
|
|
|
Arguments:
|
|
|
|
pEndpoint - Endpoint to be associated with this UL_ADDR_IDLE_LIST
|
|
Port - port number in network order
|
|
pTA - Transport Address to use when creating UX_TDI_OBJECT
|
|
pAddrIdleList- Caller allocated structure to be initalized.
|
|
|
|
Returns:
|
|
|
|
STATUS_INVALID_PARAMETER - failed due to bad transport address or
|
|
bad parameter.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpInitializeAddrIdleList(
|
|
IN PUL_ENDPOINT pEndpoint,
|
|
IN USHORT Port,
|
|
IN PUL_TRANSPORT_ADDRESS pTa,
|
|
IN OUT PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG TASize;
|
|
|
|
#if DBG
|
|
USHORT PortHostOrder = SWAP_SHORT(Port);
|
|
UCHAR IpType = (pTa->Ta.Address[0].AddressType == TDI_ADDRESS_TYPE_IP6)
|
|
? '6' : '4';
|
|
#endif // DBG
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
ASSERT( pTa );
|
|
ASSERT( pTa->Ta.Address[0].AddressType == TDI_ADDRESS_TYPE_IP ||
|
|
pTa->Ta.Address[0].AddressType == TDI_ADDRESS_TYPE_IP6);
|
|
ASSERT( pAddrIdleList );
|
|
ASSERT( NULL == pAddrIdleList->AddressObject.pFileObject );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpInitializeAddrIdleList: "
|
|
"pEndpoint %p, pAddrIdleList %p, Port %hu, IPv%c\n.",
|
|
pEndpoint,
|
|
pAddrIdleList,
|
|
PortHostOrder,
|
|
IpType
|
|
));
|
|
|
|
pAddrIdleList->Signature = UL_ADDR_IDLE_LIST_SIGNATURE;
|
|
pAddrIdleList->pOwningEndpoint = pEndpoint;
|
|
UlInitializeWorkItem( &pAddrIdleList->WorkItem );
|
|
pAddrIdleList->WorkItemScheduled = FALSE;
|
|
|
|
//
|
|
// Set up the local transport address w/port
|
|
//
|
|
// This has to be done before we return from this routine -
|
|
// the caller expects the address to be initialized (even for failures).
|
|
|
|
RtlCopyMemory(
|
|
&pAddrIdleList->LocalAddress,
|
|
pTa,
|
|
sizeof(UL_TRANSPORT_ADDRESS)
|
|
);
|
|
|
|
if ( TDI_ADDRESS_TYPE_IP == pTa->Ta.Address[0].AddressType )
|
|
{
|
|
TASize = sizeof(TA_IP_ADDRESS);
|
|
pAddrIdleList->LocalAddress.TaIp.Address[0].Address[0].sin_port = Port;
|
|
}
|
|
else if ( TDI_ADDRESS_TYPE_IP6 == pTa->Ta.Address[0].AddressType )
|
|
{
|
|
TASize = sizeof(TA_IP6_ADDRESS);
|
|
pAddrIdleList->LocalAddress.TaIp6.Address[0].Address[0].sin6_port = Port;
|
|
}
|
|
else
|
|
{
|
|
// Bad Address!
|
|
ASSERT( !"UlpInitializeAddrIdleList: Bad TDI AddressType!" );
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto fatal;
|
|
}
|
|
|
|
pAddrIdleList->LocalAddressLength = TASize;
|
|
|
|
// Create idle connection list
|
|
pAddrIdleList->IdleConnectionSListsHandle =
|
|
PpslCreatePool(
|
|
UL_NONPAGED_DATA_POOL_TAG,
|
|
g_UlIdleConnectionsHighMark,
|
|
g_UlIdleConnectionsLowMark
|
|
);
|
|
|
|
if (pAddrIdleList->IdleConnectionSListsHandle == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Open the TDI address object for this endpoint.
|
|
//
|
|
|
|
status = UxOpenTdiAddressObject(
|
|
&pAddrIdleList->LocalAddress.Ta,
|
|
TASize,
|
|
&pAddrIdleList->AddressObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UlTraceError(TDI, (
|
|
"UlpInitializeAddrIdleList(%hu, IPv%c): "
|
|
"UxOpenTdiAddressObject failed, %s\n",
|
|
PortHostOrder, IpType, HttpStatusToString(status)
|
|
));
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
//
|
|
// Set the TDI event handlers.
|
|
//
|
|
|
|
status = UxSetEventHandler(
|
|
&pAddrIdleList->AddressObject,
|
|
TDI_EVENT_CONNECT,
|
|
(ULONG_PTR) &UlpConnectHandler,
|
|
pAddrIdleList
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UlTraceError(TDI, (
|
|
"UlpInitializeAddrIdleList(%hu, IPv%c): "
|
|
"UxSetEventHandler(CONNECT) failed, %s\n",
|
|
PortHostOrder, IpType, HttpStatusToString(status)
|
|
));
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
status = UxSetEventHandler(
|
|
&pAddrIdleList->AddressObject,
|
|
TDI_EVENT_DISCONNECT,
|
|
(ULONG_PTR) &UlpDisconnectHandler,
|
|
pEndpoint
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UlTraceError(TDI, (
|
|
"UlpInitializeAddrIdleList(%hu, IPv%c): "
|
|
"UxSetEventHandler(DISCONNECT) failed, %s\n",
|
|
PortHostOrder, IpType, HttpStatusToString(status)
|
|
));
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
status = UxSetEventHandler(
|
|
&pAddrIdleList->AddressObject,
|
|
TDI_EVENT_RECEIVE,
|
|
(ULONG_PTR) &UlpReceiveHandler,
|
|
pEndpoint
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UlTraceError(TDI, (
|
|
"UlpInitializeAddrIdleList(%hu, IPv%c): "
|
|
"UxSetEventHandler(RECEIVE) failed, %s\n",
|
|
PortHostOrder, IpType, HttpStatusToString(status)
|
|
));
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
status = UxSetEventHandler(
|
|
&pAddrIdleList->AddressObject,
|
|
TDI_EVENT_RECEIVE_EXPEDITED,
|
|
(ULONG_PTR) &UlpReceiveExpeditedHandler,
|
|
pEndpoint
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UlTraceError(TDI, (
|
|
"UlpInitializeAddrIdleList(%hu, IPv%c): "
|
|
"UxSetEventHandler(RECEIVE_EXPEDITED) failed, "
|
|
"%s\n",
|
|
PortHostOrder, IpType, HttpStatusToString(status)
|
|
));
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
return status;
|
|
|
|
fatal:
|
|
|
|
//
|
|
// Disable connect events if we failed for any reason
|
|
//
|
|
|
|
if (pAddrIdleList && pAddrIdleList->AddressObject.pFileObject)
|
|
{
|
|
UxSetEventHandler(
|
|
&pAddrIdleList->AddressObject,
|
|
TDI_EVENT_CONNECT,
|
|
(ULONG_PTR) NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlpInitializeEndpointTdiObject
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up a UL_ADDR_IDLE_LIST and closes TDI handles/file objects
|
|
|
|
Arguments:
|
|
|
|
pAddrIdleList - Caller allocated structure to be cleaned up.
|
|
|
|
Returns:
|
|
|
|
STATUS_INVALID_PARAMETER - failed due to bad parameter
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpCleanupAddrIdleList(
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection;
|
|
|
|
ASSERT( pAddrIdleList );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpCleanupAddrIdleList: pAddrIdleList %p\n",
|
|
pAddrIdleList
|
|
));
|
|
|
|
//
|
|
// Clean up idle list
|
|
//
|
|
if ( pAddrIdleList->IdleConnectionSListsHandle )
|
|
{
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST(pAddrIdleList) );
|
|
|
|
do
|
|
{
|
|
pConnection = UlpDequeueIdleConnectionToDrain( pAddrIdleList );
|
|
|
|
if (pConnection)
|
|
{
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
UlpDestroyConnection( pConnection );
|
|
}
|
|
|
|
} while ( pConnection );
|
|
|
|
PpslDestroyPool(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
UL_NONPAGED_DATA_POOL_TAG
|
|
);
|
|
pAddrIdleList->IdleConnectionSListsHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// Mark as cleaned up
|
|
//
|
|
|
|
pAddrIdleList->Signature = UL_ADDR_IDLE_LIST_SIGNATURE_X;
|
|
|
|
}// UlpCleanupAddrIdleList
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Creates a block of idle connections on a specified proc or current proc.
|
|
|
|
Arguments:
|
|
|
|
pAddrIdleList - Supplies the idle list to replenish.
|
|
|
|
Proc - Only for this per-proc list
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
UlpPopulateIdleList(
|
|
IN OUT PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
IN ULONG Proc
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUL_CONNECTION pConnection;
|
|
ULONG i;
|
|
USHORT BlockSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Create a block of connections.
|
|
//
|
|
|
|
BlockSize = PpslQueryMinDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
);
|
|
|
|
ASSERT(BlockSize);
|
|
|
|
for (i = 0; i < BlockSize; i++)
|
|
{
|
|
Status = UlpCreateConnection(
|
|
pAddrIdleList,
|
|
&pConnection
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = UlpInitializeConnection(pConnection);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
UlpDestroyConnection(pConnection);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Free the connection back to the idle list. But do not
|
|
// overflow the backing list.
|
|
//
|
|
|
|
pConnection->ConnListState = IdleConnList;
|
|
|
|
if ( FALSE ==
|
|
PpslFreeSpecifyList(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
&pConnection->IdleSListEntry,
|
|
Proc
|
|
) )
|
|
{
|
|
pConnection->ConnListState = NoConnList;
|
|
UlpDestroyConnection(pConnection);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Replenishes the idle connection pool in the specified endpoint.
|
|
|
|
Arguments:
|
|
|
|
pAddrIdleList - Supplies the idle list to replenish.
|
|
|
|
PopulateAll - Whether or not to replenish all per-proc idle connection
|
|
lists or just the current processor's idle connection list.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpReplenishAddrIdleList(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
IN BOOLEAN PopulateAll
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IS_VALID_ADDR_IDLE_LIST(pAddrIdleList));
|
|
ASSERT(IS_VALID_ENDPOINT(pAddrIdleList->pOwningEndpoint));
|
|
|
|
if (PopulateAll)
|
|
{
|
|
//
|
|
// We are initializing the idle list for the very first time.
|
|
// Populate the backing list to its LowDepth.
|
|
//
|
|
|
|
Status = UlpPopulateIdleList(pAddrIdleList, g_UlNumberOfProcessors);
|
|
|
|
SHOW_LIST_INFO(
|
|
"REPLENISH",
|
|
"INIT ",
|
|
pAddrIdleList,
|
|
g_UlNumberOfProcessors
|
|
);
|
|
}
|
|
else
|
|
{
|
|
SHOW_LIST_INFO(
|
|
"REPLENISH",
|
|
"BEFORE",
|
|
pAddrIdleList,
|
|
pAddrIdleList->CpuToReplenish
|
|
);
|
|
|
|
//
|
|
// Add a "block" of Idle connections to per-proc list/backing list
|
|
// if possible.
|
|
//
|
|
|
|
Status = UlpPopulateIdleList(
|
|
pAddrIdleList,
|
|
pAddrIdleList->CpuToReplenish
|
|
);
|
|
|
|
SHOW_LIST_INFO(
|
|
"REPLENISH",
|
|
"AFTER ",
|
|
pAddrIdleList,
|
|
pAddrIdleList->CpuToReplenish
|
|
);
|
|
|
|
//
|
|
// If a per-proc list replenishment was requested, also
|
|
// check backing list and replenish it if necessary.
|
|
//
|
|
|
|
if(pAddrIdleList->CpuToReplenish < g_UlNumberOfProcessors)
|
|
{
|
|
ULONG Depth, MinDepth;
|
|
|
|
Depth =
|
|
PpslQueryBackingListDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle
|
|
);
|
|
|
|
MinDepth =
|
|
PpslQueryBackingListMinDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle
|
|
);
|
|
|
|
if(Depth < MinDepth)
|
|
{
|
|
SHOW_LIST_INFO(
|
|
"REPLENISH",
|
|
"BEFORE",
|
|
pAddrIdleList,
|
|
g_UlNumberOfProcessors
|
|
);
|
|
|
|
Status = UlpPopulateIdleList(
|
|
pAddrIdleList,
|
|
g_UlNumberOfProcessors
|
|
);
|
|
|
|
SHOW_LIST_INFO(
|
|
"REPLENISH",
|
|
"AFTER ",
|
|
pAddrIdleList,
|
|
g_UlNumberOfProcessors
|
|
);
|
|
}
|
|
}
|
|
|
|
} // !PopulateAll
|
|
|
|
return Status;
|
|
|
|
} // UlpReplenishAddrIdleList
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Deferred endpoint replenish routine.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - Supplies a pointer to the work item queued. This should
|
|
point to the WORK_ITEM structure embedded in a UL_ADDR_IDLE_LIST.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpReplenishAddrIdleListWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList;
|
|
PUL_ENDPOINT pEndpoint;
|
|
KIRQL OldIrql;
|
|
PUL_DEFERRED_REMOVE_ITEM pRemoveItem;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
pAddrIdleList = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_ADDR_IDLE_LIST,
|
|
WorkItem
|
|
);
|
|
|
|
UlTrace(TDI_STATS, (
|
|
"UlpReplenishAddrIdleListWorker: Address Idle List %p\n",
|
|
pAddrIdleList
|
|
));
|
|
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pAddrIdleList ) );
|
|
|
|
pEndpoint = pAddrIdleList->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// Allocate a UL_DEFERRED_REMOVE_ITEM to be used later to drop the usage
|
|
// count we will be adding.
|
|
//
|
|
|
|
pRemoveItem = UL_ALLOCATE_STRUCT(
|
|
PagedPool,
|
|
UL_DEFERRED_REMOVE_ITEM,
|
|
UL_DEFERRED_REMOVE_ITEM_POOL_TAG
|
|
);
|
|
|
|
if (NULL == pRemoveItem)
|
|
{
|
|
//
|
|
// Can't replenish this time.
|
|
//
|
|
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Initialize the structure.
|
|
//
|
|
|
|
pRemoveItem->Signature = UL_DEFERRED_REMOVE_ITEM_POOL_TAG;
|
|
pRemoveItem->UrlSecure = pEndpoint->Secure;
|
|
pRemoveItem->UrlPort = SWAP_SHORT(pEndpoint->LocalPort);
|
|
|
|
//
|
|
// Make sure pAddrIdleList is valid by testing and adding a temporary
|
|
// usage count to the endpoint we are replenishing.
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
|
|
|
|
if (pEndpoint->UsageCount == 0 || g_TdiWaitingForEndpointDrain)
|
|
{
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
goto end;
|
|
}
|
|
|
|
pEndpoint->UsageCount++;
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pEndpointUsageTraceLog,
|
|
REF_ACTION_REFERENCE_ENDPOINT_USAGE,
|
|
pEndpoint->UsageCount,
|
|
pEndpoint,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
|
|
//
|
|
// Let UlpReplenishAddrIdleList() do the dirty work.
|
|
//
|
|
|
|
UlpReplenishAddrIdleList( pAddrIdleList, FALSE );
|
|
|
|
//
|
|
// Drop the temporary usage count through UlRemoveSite. This is
|
|
// arranged this way because UlRemoveSiteFromEndpointList has to
|
|
// happen on a special wait thread pool. Or it is possible to have
|
|
// another replenish worker scheduled for another address list on the
|
|
// same endpoint which takes an endpoint reference. This means
|
|
// UlRemoveSiteFromEndpointList will wait indefinitely if we call it
|
|
// directly from this routine.
|
|
//
|
|
|
|
UlRemoveSite( pRemoveItem );
|
|
|
|
//
|
|
// Set pRemoveItem to NULL so we won't double-free.
|
|
//
|
|
|
|
pRemoveItem = NULL;
|
|
|
|
end:
|
|
|
|
if (pRemoveItem)
|
|
{
|
|
UL_FREE_POOL_WITH_SIG( pRemoveItem, UL_DEFERRED_REMOVE_ITEM_POOL_TAG );
|
|
}
|
|
|
|
InterlockedExchange( &pAddrIdleList->WorkItemScheduled, FALSE );
|
|
|
|
//
|
|
// Remove the reference that UlpDequeueIdleConnection added for
|
|
// this call.
|
|
//
|
|
|
|
DEREFERENCE_ENDPOINT_SELF(
|
|
pAddrIdleList->pOwningEndpoint,
|
|
REF_ACTION_REPLENISH
|
|
);
|
|
|
|
} // UlpReplenishAddrIdleListWorker
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Queues a passive worker for the lowered irql.
|
|
|
|
Arguments:
|
|
|
|
Ignored
|
|
|
|
--***************************************************************************/
|
|
|
|
VOID
|
|
UlpIdleListTrimTimerDpcRoutine(
|
|
PKDPC Dpc,
|
|
PVOID DeferredContext,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2
|
|
)
|
|
{
|
|
ULONG Index;
|
|
PLIST_ENTRY pLink;
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList;
|
|
|
|
|
|
//
|
|
// Parameters are ignored.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(DeferredContext);
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
|
|
//
|
|
// Drop trimming the idle lists of endpoints if there is another
|
|
// one still running.
|
|
//
|
|
|
|
if (FALSE != InterlockedExchange(
|
|
&g_UlTrimTimer.WorkItemScheduled,
|
|
TRUE
|
|
))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Since there are no trim workers running around, the zombie list
|
|
// must be empty.
|
|
//
|
|
|
|
ASSERT(IsListEmpty(&g_UlTrimTimer.ZombieConnectionListHead));
|
|
|
|
UlAcquireSpinLockAtDpcLevel(&g_UlTrimTimer.SpinLock);
|
|
|
|
if (g_UlTrimTimer.Initialized == TRUE)
|
|
{
|
|
UlAcquireSpinLockAtDpcLevel(&g_TdiSpinLock);
|
|
|
|
for (pLink = g_TdiEndpointListHead.Flink;
|
|
pLink != &g_TdiEndpointListHead;
|
|
pLink = pLink->Flink
|
|
)
|
|
{
|
|
pEndpoint = CONTAINING_RECORD(
|
|
pLink,
|
|
UL_ENDPOINT,
|
|
GlobalEndpointListEntry
|
|
);
|
|
|
|
ASSERT(IS_VALID_ENDPOINT(pEndpoint));
|
|
|
|
for (Index = 0; Index < pEndpoint->AddrIdleListCount; Index++)
|
|
{
|
|
pAddrIdleList = &pEndpoint->aAddrIdleLists[Index];
|
|
|
|
//
|
|
// Trim the idle list if there was no replenish running at
|
|
// this time. We can certainly drop the trim if a replenish
|
|
// is running on the adr-list.
|
|
//
|
|
|
|
if (FALSE == InterlockedExchange(
|
|
&pAddrIdleList->WorkItemScheduled,
|
|
TRUE
|
|
))
|
|
{
|
|
UlpTrimAddrIdleList(
|
|
pAddrIdleList,
|
|
&g_UlTrimTimer.ZombieConnectionListHead
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
UlReleaseSpinLockFromDpcLevel(&g_TdiSpinLock);
|
|
|
|
//
|
|
// Destroy the zombie list at high priority to release the memory asap.
|
|
// We cannot cleanup this list here, since the UlpDestroyConnection
|
|
// must work at passive level because of UxCloseTdiObject call. That's
|
|
// why we are having the zombie list here to pass it to the passive
|
|
// worker.
|
|
//
|
|
if (!IsListEmpty(&g_UlTrimTimer.ZombieConnectionListHead))
|
|
{
|
|
UL_QUEUE_HIGH_PRIORITY_ITEM(
|
|
&g_UlTrimTimer.WorkItem,
|
|
&UlpIdleListTrimTimerWorker
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Worker won't get called. Reset the workitem scheduled field.
|
|
//
|
|
|
|
InterlockedExchange(&g_UlTrimTimer.WorkItemScheduled, FALSE);
|
|
}
|
|
}
|
|
|
|
UlReleaseSpinLockFromDpcLevel(&g_UlTrimTimer.SpinLock);
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the zombie idle connection list at passive level.
|
|
|
|
Arguments:
|
|
|
|
Work Item.
|
|
|
|
--***************************************************************************/
|
|
|
|
VOID
|
|
UlpIdleListTrimTimerWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_TRIM_TIMER pTimer;
|
|
PLIST_ENTRY pListEntry;
|
|
PUL_CONNECTION pConnection;
|
|
|
|
PAGED_CODE();
|
|
|
|
pTimer = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_TRIM_TIMER,
|
|
WorkItem
|
|
);
|
|
|
|
UlTrace(TDI_STATS,("UlpIdleListTrimTimerWorker: destroying [%d] connections ...\n",
|
|
UlpZombieListDepth(&g_UlTrimTimer.ZombieConnectionListHead)
|
|
));
|
|
|
|
//
|
|
// We shouldn't be called if the list was empty.
|
|
//
|
|
|
|
ASSERT(!IsListEmpty(&g_UlTrimTimer.ZombieConnectionListHead));
|
|
|
|
//
|
|
// Drain the list, and close all of the idle connections.
|
|
//
|
|
|
|
while (!IsListEmpty(&g_UlTrimTimer.ZombieConnectionListHead))
|
|
{
|
|
//
|
|
// Remove the connection from the zombie connection list.
|
|
//
|
|
|
|
pListEntry = RemoveHeadList(
|
|
&g_UlTrimTimer.ZombieConnectionListHead
|
|
);
|
|
|
|
//
|
|
// Validate the connection.
|
|
//
|
|
|
|
pConnection = CONTAINING_RECORD(
|
|
pListEntry,
|
|
UL_CONNECTION,
|
|
GlobalConnectionListEntry
|
|
);
|
|
|
|
ASSERT(IS_VALID_CONNECTION(pConnection));
|
|
ASSERT(pConnection->IdleSListEntry.Next == NULL);
|
|
ASSERT(pConnection->ConnListState == NoConnList);
|
|
|
|
UlpDestroyConnection(pConnection);
|
|
}
|
|
|
|
//
|
|
// Now we are done we can let other trim timers to work.
|
|
//
|
|
|
|
InterlockedExchange(&g_UlTrimTimer.WorkItemScheduled, FALSE);
|
|
|
|
TRACE_IDLE_CONNECTIONS();
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Simple function to move an idle connection to the zombie list
|
|
Arguments:
|
|
|
|
pSListEntry - the idle connection to be moved.
|
|
pZombieList - the trimmed connection to be moved to this zombie list.
|
|
|
|
--***************************************************************************/
|
|
|
|
__inline
|
|
VOID
|
|
UlpZombifyIdleConnection(
|
|
PSLIST_ENTRY pSListEntry,
|
|
PLIST_ENTRY pZombieList
|
|
)
|
|
{
|
|
PUL_CONNECTION pConnection;
|
|
|
|
ASSERT(pSListEntry);
|
|
ASSERT(pZombieList);
|
|
ASSERT(UlDbgSpinLockOwned(&g_TdiSpinLock));
|
|
|
|
pConnection = CONTAINING_RECORD(
|
|
pSListEntry,
|
|
UL_CONNECTION,
|
|
IdleSListEntry
|
|
);
|
|
|
|
ASSERT(IS_VALID_CONNECTION(pConnection));
|
|
|
|
pConnection->IdleSListEntry.Next = NULL;
|
|
pConnection->ConnListState = NoConnList;
|
|
|
|
//
|
|
// Move it from global connection list to the zombie list.
|
|
//
|
|
|
|
RemoveEntryList(
|
|
&pConnection->GlobalConnectionListEntry
|
|
);
|
|
|
|
InsertTailList(
|
|
pZombieList,
|
|
&pConnection->GlobalConnectionListEntry
|
|
);
|
|
|
|
//
|
|
// Once the zombie list is ready, a passive worker will call
|
|
// UlpDestroyConnection(pConnection); for the whole zombie list.
|
|
//
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function will decide on cleaning up the idle connections on the adr-
|
|
list.
|
|
|
|
Arguments:
|
|
|
|
pAddrIdleList - the list to be trimmed
|
|
pZombieList - the trimmed connection to be moved to this zombie list.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpTrimAddrIdleList(
|
|
IN OUT PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
OUT PLIST_ENTRY pZombieList
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
PSLIST_ENTRY pSListEntry;
|
|
LONG i;
|
|
ULONG Proc;
|
|
|
|
//
|
|
// We are messing with the GlobalConnectionListEntry of the connection.
|
|
// You better hold the tdi spinlock while calling this.
|
|
//
|
|
|
|
ASSERT(UlDbgSpinLockOwned(&g_TdiSpinLock));
|
|
ASSERT(IS_VALID_ADDR_IDLE_LIST(pAddrIdleList));
|
|
|
|
pEndpoint = pAddrIdleList->pOwningEndpoint;
|
|
ASSERT(IS_VALID_ENDPOINT(pEndpoint));
|
|
|
|
//
|
|
// Visit each per-proc list as well as the backing list to update
|
|
// connection serv rate. Also if we have a slowing delta, trim the
|
|
// list.
|
|
//
|
|
|
|
for (Proc = 0; Proc <= g_UlNumberOfProcessors; Proc++)
|
|
{
|
|
USHORT LowMark;
|
|
LONG BlockSize;
|
|
LONG Delta, PrevServed;
|
|
|
|
PrevServed = PpslQueryPrevServed(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
);
|
|
|
|
Delta = PpslAdjustActivityStats(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
);
|
|
|
|
ASSERT((Delta > 0) || (-Delta <= PrevServed));
|
|
|
|
//
|
|
// If the connection serving rate has dropped more than 10% since
|
|
// the last time we woke up. Trim upto half of the distance to low
|
|
// water mark.
|
|
//
|
|
|
|
if (Delta <= 0 && -Delta >= PrevServed/10)
|
|
{
|
|
LowMark = (Proc == g_UlNumberOfProcessors) ?
|
|
PpslQueryBackingListMinDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle
|
|
) :
|
|
0;
|
|
|
|
BlockSize = PpslQueryDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
)
|
|
-
|
|
LowMark;
|
|
|
|
//
|
|
// Some funky logic to decide on trim size.
|
|
//
|
|
|
|
BlockSize = MAX(0, BlockSize);
|
|
|
|
if(PrevServed == 0)
|
|
{
|
|
ASSERT(Delta == 0);
|
|
BlockSize /= 2;
|
|
}
|
|
else
|
|
{
|
|
LONG Temp = (LONG)(((LONGLONG)BlockSize)*(-Delta)/PrevServed);
|
|
ASSERT(Temp <= BlockSize);
|
|
BlockSize = MIN(Temp, BlockSize/2);
|
|
}
|
|
|
|
for (i = 0; i < BlockSize; i++)
|
|
{
|
|
pSListEntry =
|
|
PpslAllocateToTrim(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
);
|
|
|
|
if (pSListEntry != NULL)
|
|
{
|
|
UlpZombifyIdleConnection(pSListEntry,pZombieList);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Let the replenish to do its work.
|
|
//
|
|
|
|
InterlockedExchange(&pAddrIdleList->WorkItemScheduled, FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new UL_CONNECTION object and opens the corresponding
|
|
TDI connection object.
|
|
|
|
Note: The connection returned from this function will contain
|
|
an unreferenced pointer to the owning endpoint. Only active
|
|
connections have references to the endpoint because the refcount
|
|
is used to decide when to clean up the list of idle connections.
|
|
|
|
Arguments:
|
|
|
|
pAddrIdleList - Supplies the Endpoint's idle connection list for
|
|
a particular TDI address.
|
|
|
|
ppConnection - Receives the pointer to a new UL_CONNECTION if
|
|
successful.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status. *ppConnection is undefined if the
|
|
return value is not STATUS_SUCCESS.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpCreateConnection(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
OUT PUL_CONNECTION *ppConnection
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_CONNECTION pConnection;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT( IS_VALID_ADDR_IDLE_LIST( pAddrIdleList ) );
|
|
ASSERT(NULL != ppConnection);
|
|
|
|
//
|
|
// Allocate the pool for the connection structure.
|
|
//
|
|
|
|
pConnection = UL_ALLOCATE_STRUCT(
|
|
NonPagedPool,
|
|
UL_CONNECTION,
|
|
UL_CONNECTION_POOL_TAG
|
|
);
|
|
|
|
if (pConnection == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pTdiTraceLog,
|
|
REF_ACTION_ALLOC_UL_CONNECTION,
|
|
0,
|
|
pConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// One time field initialization.
|
|
//
|
|
|
|
pConnection->Signature = UL_CONNECTION_SIGNATURE;
|
|
|
|
pConnection->pConnectionContext = NULL;
|
|
pConnection->pOwningEndpoint = NULL;
|
|
|
|
pConnection->FilterInfo.pFilterChannel = NULL;
|
|
HTTP_SET_NULL_ID( &pConnection->FilterInfo.ConnectionId );
|
|
pConnection->pIrp = NULL;
|
|
|
|
UlInitializeWorkItem(&pConnection->WorkItem);
|
|
|
|
pConnection->IdleSListEntry.Next = NULL;
|
|
pConnection->ConnListState = NoConnList;
|
|
|
|
ASSERT(UlpConnectionIsOnValidList(pConnection));
|
|
|
|
//
|
|
// Init the Connection State spin lock
|
|
//
|
|
|
|
UlInitializeSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
"ConnectionState"
|
|
);
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
|
|
|
|
InsertTailList(
|
|
&g_TdiConnectionListHead,
|
|
&pConnection->GlobalConnectionListEntry
|
|
);
|
|
|
|
g_TdiConnectionCount++;
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
|
|
//
|
|
// Initialize a private trace log.
|
|
//
|
|
|
|
CREATE_REF_TRACE_LOG( pConnection->pTraceLog,
|
|
96 - REF_TRACE_OVERHEAD, 0, TRACELOG_LOW_PRIORITY,
|
|
UL_CONNECTION_REF_TRACE_LOG_POOL_TAG );
|
|
CREATE_REF_TRACE_LOG( pConnection->pHttpTraceLog,
|
|
32 - REF_TRACE_OVERHEAD, 0, TRACELOG_LOW_PRIORITY,
|
|
UL_HTTP_CONNECTION_REF_TRACE_LOG_POOL_TAG );
|
|
|
|
//
|
|
// Open the TDI connection object for this connection.
|
|
//
|
|
|
|
status = UxOpenTdiConnectionObject(
|
|
pAddrIdleList->LocalAddress.Ta.Address[0].AddressType,
|
|
(CONNECTION_CONTEXT)pConnection,
|
|
&pConnection->ConnectionObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto fatal;
|
|
}
|
|
|
|
ASSERT( IS_VALID_TDI_OBJECT( &pConnection->ConnectionObject ) );
|
|
|
|
//
|
|
// Associate the connection with the endpoint.
|
|
//
|
|
|
|
status = UlpAssociateConnection( pConnection, pAddrIdleList );
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto fatal;
|
|
}
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
|
|
pConnection->pIrp = UlAllocateIrp(
|
|
pTdiObject->pDeviceObject->StackSize, // StackSize
|
|
FALSE // ChargeQuota
|
|
);
|
|
|
|
if (pConnection->pIrp == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
pConnection->pIrp->RequestorMode = KernelMode;
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
UlTraceVerbose(TDI, (
|
|
"UlpCreateConnection: created %p\n",
|
|
pConnection
|
|
));
|
|
|
|
*ppConnection = pConnection;
|
|
return STATUS_SUCCESS;
|
|
|
|
fatal:
|
|
|
|
UlTrace(TDI, (
|
|
"UlpCreateConnection: failure 0x%x\n",
|
|
status
|
|
));
|
|
|
|
ASSERT( !NT_SUCCESS(status) );
|
|
|
|
if (pConnection != NULL)
|
|
{
|
|
UlpDestroyConnection( pConnection );
|
|
}
|
|
|
|
*ppConnection = NULL;
|
|
return status;
|
|
|
|
} // UlpCreateConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a UL_CONNECTION for use.
|
|
Note: inactive connections do not have a reference to the endpoint,
|
|
so the caller to this function *must* have a reference.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Pointer to the UL_CONNECTION to initialize.
|
|
|
|
SecureConnection - TRUE if this connection is for a secure endpoint.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpInitializeConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN SecureConnection;
|
|
PUL_FILTER_CHANNEL pChannel;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT(pConnection);
|
|
ASSERT(IS_VALID_ENDPOINT(pConnection->pOwningEndpoint));
|
|
ASSERT(IS_VALID_ADDR_IDLE_LIST(pConnection->pOwningAddrIdleList));
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
SecureConnection = pConnection->pOwningEndpoint->Secure;
|
|
|
|
//
|
|
// Initialize the easy parts.
|
|
//
|
|
|
|
pConnection->ReferenceCount = 1;
|
|
pConnection->ConnectionFlags.Value = 0;
|
|
pConnection->ConnectionState = UlConnectStateConnectIdle;
|
|
|
|
pConnection->IdleSListEntry.Next = NULL;
|
|
pConnection->ConnListState = NoConnList;
|
|
|
|
ASSERT(UlpConnectionIsOnValidList(pConnection));
|
|
|
|
pConnection->AddressType =
|
|
pConnection->pOwningAddrIdleList->LocalAddress.Ta.Address[0].AddressType;
|
|
pConnection->AddressLength =
|
|
pConnection->pOwningAddrIdleList->LocalAddress.Ta.Address[0].AddressLength;
|
|
|
|
//
|
|
// Setup the Tdi Connection Information space to be filled with
|
|
// Local Address Information at the completion of the Accept Irp.
|
|
//
|
|
|
|
pConnection->TdiConnectionInformation.UserDataLength = 0;
|
|
pConnection->TdiConnectionInformation.UserData = NULL;
|
|
pConnection->TdiConnectionInformation.OptionsLength = 0;
|
|
pConnection->TdiConnectionInformation.Options = NULL;
|
|
pConnection->TdiConnectionInformation.RemoteAddressLength =
|
|
pConnection->pOwningAddrIdleList->LocalAddressLength;
|
|
pConnection->TdiConnectionInformation.RemoteAddress =
|
|
&(pConnection->Ta);
|
|
|
|
//
|
|
// Init the Inteface/Link IDs.
|
|
//
|
|
pConnection->bRoutingLookupDone = FALSE;
|
|
|
|
//
|
|
// Init the IrpContext.
|
|
//
|
|
|
|
pConnection->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
|
|
|
|
//
|
|
// Init the HTTP_CONNECTION.
|
|
//
|
|
|
|
pConnection->HttpConnection.RefCount = 0;
|
|
|
|
pChannel = UxRetrieveServerFilterChannel(SecureConnection);
|
|
|
|
//
|
|
// pChannel will be non NULL if HTTPFilter is running and the user has
|
|
// asked for a SecureConnection or if they have enabled RAW ISAPI filtering.
|
|
//
|
|
//
|
|
|
|
// If pChannel is NULL and it's a secure connection, we should fail it.
|
|
// SSL will not work if HTTPFilter is not running.
|
|
//
|
|
|
|
if(SecureConnection && pChannel == NULL)
|
|
{
|
|
return STATUS_NO_TRACKING_SERVICE;
|
|
}
|
|
|
|
if (pChannel)
|
|
{
|
|
status = UxInitializeFilterConnection(
|
|
&pConnection->FilterInfo,
|
|
pChannel,
|
|
SecureConnection,
|
|
&UlReferenceConnection,
|
|
&UlDereferenceConnection,
|
|
&UlpCloseRawConnection,
|
|
&UlpSendRawData,
|
|
&UlpReceiveRawData,
|
|
&UlpDummyReceiveHandler,
|
|
&UlpComputeHttpRawConnectionLength,
|
|
&UlpGenerateHttpRawConnectionInfo,
|
|
NULL,
|
|
&UlpDoDisconnectNotification,
|
|
pConnection->pOwningEndpoint,
|
|
pConnection
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pConnection->FilterInfo.pFilterChannel = NULL;
|
|
pConnection->FilterInfo.SecureConnection = FALSE;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlpInitializeConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initiates a graceful disconnect on the specified connection.
|
|
Uses the UL_CONNECTION's pre-alloc'd Disconnect Irp, which may be used
|
|
for only one driver call at a time. Caller guarantees exclusive control
|
|
before calling. See UlpCloseRawConnection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to disconnect.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the connection is disconnected.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
CleaningUp - TRUE if we're cleaning up the connection.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpBeginDisconnect(
|
|
IN PIRP pIrp,
|
|
IN PUL_IRP_CONTEXT pIrpContext,
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
UlTrace(TDI, (
|
|
"UlpBeginDisconnect: connection %p\n",
|
|
pConnection
|
|
));
|
|
|
|
ASSERT( pIrp );
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
|
|
ASSERT( pConnection->ConnectionFlags.DisconnectPending );
|
|
|
|
//
|
|
// Initialize the IRP context for this request. Set pOwnIrp to NULL
|
|
// and OwnIrpContext to FALSE so they will be freed in the completion
|
|
// routine UlpRestartDisconnect().
|
|
//
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->pOwnIrp = NULL;
|
|
pIrpContext->OwnIrpContext = FALSE;
|
|
|
|
//
|
|
// Initialize the disconnect IRP.
|
|
//
|
|
|
|
UxInitializeDisconnectIrp(
|
|
pIrp,
|
|
&pConnection->ConnectionObject,
|
|
TDI_DISCONNECT_RELEASE,
|
|
&UlpRestartDisconnect,
|
|
pIrpContext
|
|
);
|
|
|
|
//
|
|
// Add a reference to the connection
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
|
|
//
|
|
// Then call the driver to initiate the disconnect.
|
|
//
|
|
|
|
UlCallDriver( pConnection->ConnectionObject.pDeviceObject, pIrp );
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} // UlpBeginDisconnect
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for graceful disconnect IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartDisconnect(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PUL_CONNECTION pConnection;
|
|
UL_CONNECTION_FLAGS Flags;
|
|
PUL_ENDPOINT pEndpoint;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT)pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
IoStatus = pIrp->IoStatus;
|
|
|
|
pConnection = pIrpContext->pConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->ConnectionFlags.DisconnectPending );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartDisconnect: connection %p, IoStatus %d\n",
|
|
pConnection,
|
|
IoStatus.Status
|
|
));
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
|
|
Flags.Value = *((volatile LONG *)&pConnection->ConnectionFlags.Value);
|
|
|
|
if (!Flags.DisconnectIndicated && !Flags.AbortIndicated)
|
|
{
|
|
//
|
|
// Only try to drain if it is not already aborted or disconnect
|
|
// indication is not already happened.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Put a reference on filter connection until the drain
|
|
// is done.
|
|
//
|
|
REFERENCE_FILTER_CONNECTION(&pConnection->FilterInfo);
|
|
|
|
UL_QUEUE_WORK_ITEM(
|
|
&pConnection->FilterInfo.WorkItem,
|
|
&UlFilterDrainIndicatedData
|
|
);
|
|
}
|
|
else
|
|
{
|
|
(pEndpoint->pConnectionDisconnectCompleteHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Set the flag indicating that a disconnect has completed.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
|
|
|
|
UlAcquireSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
&OldIrql
|
|
);
|
|
|
|
ASSERT( pConnection->ConnectionFlags.CleanupBegun );
|
|
|
|
//
|
|
// Mark Disconnect completed.
|
|
//
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeDisconnectCompleteFlag() );
|
|
|
|
if (pConnection->ConnectionFlags.AbortIndicated ||
|
|
pConnection->ConnectionFlags.DisconnectIndicated ||
|
|
pConnection->ConnectionFlags.AbortDisconnect)
|
|
{
|
|
//
|
|
// Move directly to state UlConnectStateConnectCleanup.
|
|
//
|
|
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Change state to UlConnectStateDisconnectComplete.
|
|
//
|
|
|
|
pConnection->ConnectionState = UlConnectStateDisconnectComplete;
|
|
}
|
|
|
|
if (!NT_SUCCESS(IoStatus.Status) &&
|
|
IoStatus.Status != STATUS_CONNECTION_RESET)
|
|
{
|
|
UlpSetConnectionFlag( pConnection, MakeTdiConnectionInvalidFlag() );
|
|
}
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
//
|
|
// Check to see if we're ready to clean up
|
|
//
|
|
|
|
UlpRemoveFinalReference( pConnection );
|
|
|
|
//
|
|
// Invoke the user's completion routine
|
|
//
|
|
|
|
(VOID) UlInvokeCompletionRoutine(
|
|
IoStatus.Status,
|
|
IoStatus.Information,
|
|
pIrpContext->pCompletionRoutine,
|
|
pIrpContext->pCompletionContext
|
|
);
|
|
|
|
//
|
|
// Free the IRP and IRP_CONTEXT if pOwnIrp is set to NULL or OwnIrpContext
|
|
// is set to FALSE, which means they were not pre-built as part of the
|
|
// connection object.
|
|
//
|
|
|
|
if (!pIrpContext->pOwnIrp)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
if (!pIrpContext->OwnIrpContext)
|
|
{
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
}
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
//
|
|
// There is no need to free the IRP context, since it's part
|
|
// of the UL_CONNECTION object.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UlpRestartDisconnect
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initiates an abortive disconnect on the specified connection.
|
|
Uses the UL_CONNECTION's pre-alloc'd Disconnect Irp, which may be used
|
|
for only one driver call at a time. Caller guarantees exclusive control
|
|
before calling. See UlpCloseRawConnection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to disconnect.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the connection is disconnected.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpBeginAbort(
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
PIRP pIrp;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpBeginAbort: connection %p\n",
|
|
pConnection
|
|
));
|
|
|
|
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
|
|
ASSERT( pConnection->ConnectionFlags.AbortPending );
|
|
|
|
//
|
|
// Use pre-alloc'd IRP context for this request.
|
|
//
|
|
|
|
pIrpContext = &pConnection->IrpContext;
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
|
|
//
|
|
// Initialize pre-alloc'd IRP for the disconnect.
|
|
//
|
|
|
|
pIrp = pConnection->pIrp;
|
|
|
|
UxInitializeDisconnectIrp(
|
|
pIrp,
|
|
&pConnection->ConnectionObject,
|
|
TDI_DISCONNECT_ABORT,
|
|
&UlpRestartAbort,
|
|
pIrpContext
|
|
);
|
|
|
|
//
|
|
// Add a reference to the connection,
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
|
|
//
|
|
// Then call the driver to initiate the disconnect.
|
|
//
|
|
|
|
UlCallDriver( pConnection->ConnectionObject.pDeviceObject, pIrp );
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} // UlpBeginAbort
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for abortive disconnect IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartAbort(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PUL_CONNECTION pConnection;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT)pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->ConnectionFlags.AbortPending );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartAbort: connection %p\n",
|
|
pConnection
|
|
));
|
|
|
|
//
|
|
// Set the flag indicating that an abort has completed. If we're in the
|
|
// midst of cleaning up this endpoint and we've already received a
|
|
// disconnect (graceful or abortive) from the client, then remove the
|
|
// final reference to the connection.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
|
|
|
|
UlAcquireSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
&OldIrql
|
|
);
|
|
|
|
ASSERT( pConnection->ConnectionFlags.CleanupBegun );
|
|
|
|
pConnection->ConnectionState = UlConnectStateConnectCleanup;
|
|
UlpSetConnectionFlag( pConnection, MakeAbortCompleteFlag() );
|
|
|
|
if (!NT_SUCCESS(pIrp->IoStatus.Status))
|
|
{
|
|
UlpSetConnectionFlag( pConnection, MakeTdiConnectionInvalidFlag() );
|
|
}
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
UlpRemoveFinalReference( pConnection );
|
|
|
|
//
|
|
// Invoke the user's completion routine, then free the IRP context.
|
|
//
|
|
|
|
(VOID) UlInvokeCompletionRoutine(
|
|
pIrp->IoStatus.Status,
|
|
pIrp->IoStatus.Information,
|
|
pIrpContext->pCompletionRoutine,
|
|
pIrpContext->pCompletionContext
|
|
);
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UlpRestartAbort
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Removes the final reference from a connection if the conditions are
|
|
right. See comments within this function for details on the conditions
|
|
required.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to dereference.
|
|
|
|
Flags - Supplies the connection flags from the most recent update.
|
|
|
|
Note:
|
|
|
|
It is very important that the caller of this routine has established
|
|
its own reference to the connection. If necessary, this reference
|
|
can be immediately removed after calling this routine, but not before.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpRemoveFinalReference(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
UlAcquireSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
&OldIrql
|
|
);
|
|
|
|
//
|
|
// We can only remove the final reference if:
|
|
//
|
|
// We've begun connection cleanup.
|
|
//
|
|
// We've completed an accept.
|
|
//
|
|
// We've received a disconnect or abort indication or we've
|
|
// issued & completed an abort.
|
|
//
|
|
// We don't have a disconnect or abort pending.
|
|
//
|
|
// We haven't already removed it.
|
|
//
|
|
|
|
if ( UlConnectStateConnectCleanup == pConnection->ConnectionState )
|
|
{
|
|
if (!pConnection->ConnectionFlags.FinalReferenceRemoved)
|
|
{
|
|
UlTrace(TDI, (
|
|
"UlpRemoveFinalReference: connection %p\n",
|
|
pConnection
|
|
));
|
|
|
|
//
|
|
// We're it. Set the flag.
|
|
//
|
|
|
|
UlpSetConnectionFlag( pConnection, MakeFinalReferenceRemovedFlag() );
|
|
|
|
//
|
|
// Unbind from the endpoint if we're still attached.
|
|
// This allows it to release any refs it has on the connection.
|
|
//
|
|
|
|
UlpUnbindConnectionFromEndpoint(pConnection);
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
//
|
|
// Tell the client that the connection is now fully destroyed.
|
|
//
|
|
|
|
(pConnection->pConnectionDestroyedHandler)(
|
|
pConnection->pListeningContext,
|
|
pConnection->pConnectionContext
|
|
);
|
|
|
|
//
|
|
// Release the filter channel.
|
|
// This allows it to release any refs it has on the connection.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
UlUnbindConnectionFromFilter(&pConnection->FilterInfo);
|
|
}
|
|
|
|
//
|
|
// Remove the final reference.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UlTrace(TDI, (
|
|
"UlpRemoveFinalReference: cannot remove %p\n",
|
|
pConnection
|
|
));
|
|
}
|
|
|
|
UlReleaseSpinLock(
|
|
&pConnection->ConnectionStateSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
} // UlpRemoveFinalReference
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for receive IRPs passed back to the transport from
|
|
our receive indication handler.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_RECEIVE_BUFFER.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartReceive(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_RECEIVE_BUFFER pBuffer;
|
|
PUL_CONNECTION pConnection;
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
ULONG bytesTaken = 0;
|
|
ULONG bytesRemaining;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pBuffer = (PUL_RECEIVE_BUFFER) pContext;
|
|
ASSERT( IS_VALID_RECEIVE_BUFFER( pBuffer ) );
|
|
|
|
pConnection = (PUL_CONNECTION) pBuffer->pConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
pTdiObject = &pConnection->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
|
|
//
|
|
// The connection could be destroyed before we get a chance to
|
|
// receive the completion for the receive IRP. In that case the
|
|
// irp status won't be success but STATUS_CONNECTION_RESET or similar.
|
|
// We should not attempt to pass this case to the client.
|
|
//
|
|
|
|
status = pBuffer->pIrp->IoStatus.Status;
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// The HttpConnection has already been destroyed
|
|
// or receive completion failed for some reason.
|
|
// No need to go to client.
|
|
//
|
|
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Fake a receive indication to the client.
|
|
//
|
|
|
|
pBuffer->UnreadDataLength += (ULONG)pBuffer->pIrp->IoStatus.Information;
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartReceive: endpoint %p, connection %p, length %lu\n",
|
|
pEndpoint,
|
|
pConnection,
|
|
pBuffer->UnreadDataLength
|
|
));
|
|
|
|
//
|
|
// Pass the data on.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Needs to go through a filter.
|
|
//
|
|
|
|
status = UlFilterReceiveHandler(
|
|
&pConnection->FilterInfo,
|
|
pBuffer->pDataArea,
|
|
pBuffer->UnreadDataLength,
|
|
0,
|
|
&bytesTaken
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Go directly to client.
|
|
//
|
|
|
|
status = (pEndpoint->pDataReceiveHandler)(
|
|
pEndpoint->pListeningContext,
|
|
pConnection->pConnectionContext,
|
|
pBuffer->pDataArea,
|
|
pBuffer->UnreadDataLength,
|
|
0,
|
|
&bytesTaken
|
|
);
|
|
}
|
|
|
|
ASSERT( bytesTaken <= pBuffer->UnreadDataLength );
|
|
|
|
//
|
|
// Note that this basically duplicates the logic that's currently in
|
|
// UlpReceiveHandler.
|
|
//
|
|
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
//
|
|
// The client consumed part of the indicated data.
|
|
//
|
|
// We'll need to copy the untaken data forward within the receive
|
|
// buffer, build an MDL describing the remaining part of the buffer,
|
|
// then repost the receive IRP.
|
|
//
|
|
|
|
bytesRemaining = pBuffer->UnreadDataLength - bytesTaken;
|
|
|
|
//
|
|
// Do we have enough buffer space for more?
|
|
//
|
|
|
|
if (bytesRemaining < g_UlReceiveBufferSize)
|
|
{
|
|
//
|
|
// Move the unread portion of the buffer to the beginning.
|
|
//
|
|
|
|
RtlMoveMemory(
|
|
pBuffer->pDataArea,
|
|
(PUCHAR)pBuffer->pDataArea + bytesTaken,
|
|
bytesRemaining
|
|
);
|
|
|
|
pBuffer->UnreadDataLength = bytesRemaining;
|
|
|
|
//
|
|
// Build a partial mdl representing the remainder of the
|
|
// buffer.
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
pBuffer->pMdl, // SourceMdl
|
|
pBuffer->pPartialMdl, // TargetMdl
|
|
(PUCHAR)pBuffer->pDataArea + bytesRemaining,// VirtualAddress
|
|
g_UlReceiveBufferSize - bytesRemaining // Length
|
|
);
|
|
|
|
//
|
|
// Finish initializing the IRP.
|
|
//
|
|
|
|
TdiBuildReceive(
|
|
pBuffer->pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UlpRestartReceive, // CompletionRoutine
|
|
pBuffer, // CompletionContext
|
|
pBuffer->pPartialMdl, // MdlAddress
|
|
TDI_RECEIVE_NORMAL, // Flags
|
|
g_UlReceiveBufferSize - bytesRemaining // Length
|
|
);
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartReceive: connection %p, reusing irp %p to grab more data\n",
|
|
pConnection,
|
|
pBuffer->pIrp
|
|
));
|
|
|
|
//
|
|
// Call the driver.
|
|
//
|
|
|
|
UlCallDriver( pTdiObject->pDeviceObject, pIrp );
|
|
|
|
//
|
|
// Tell IO to stop processing this request.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
end:
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// The client failed the indication. Abort the connection.
|
|
//
|
|
|
|
//
|
|
// BUGBUG need to add code to return a response
|
|
//
|
|
|
|
UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE, // AbortiveDisconnect
|
|
NULL, // pCompletionRoutine
|
|
NULL // pCompletionContext
|
|
);
|
|
}
|
|
|
|
if (pTdiObject->pDeviceObject->StackSize > DEFAULT_IRP_STACK_SIZE)
|
|
{
|
|
UlFreeReceiveBufferPool( pBuffer );
|
|
}
|
|
else
|
|
{
|
|
UlPplFreeReceiveBuffer( pBuffer );
|
|
}
|
|
|
|
//
|
|
// Remove the connection we added in the receive indication handler,
|
|
// free the receive buffer, then tell IO to stop processing the IRP.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartReceive: endpoint %p, connection %p, length %lu, "
|
|
"taken %lu, status 0x%x\n",
|
|
pEndpoint,
|
|
pConnection,
|
|
pBuffer->UnreadDataLength,
|
|
bytesTaken,
|
|
status
|
|
));
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UlpRestartReceive
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for receive IRPs initiated from UlReceiveData().
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartClientReceive(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PUL_CONNECTION pConnection;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext= (PUL_IRP_CONTEXT)pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pConnection = (PUL_CONNECTION)pIrpContext->pConnectionContext;
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpRestartClientReceive: irp %p, connection %p, status 0x%x\n",
|
|
pIrp,
|
|
pConnection,
|
|
pIrp->IoStatus.Status
|
|
));
|
|
|
|
//
|
|
// Invoke the client's completion handler.
|
|
//
|
|
|
|
(VOID) UlInvokeCompletionRoutine(
|
|
pIrp->IoStatus.Status,
|
|
pIrp->IoStatus.Information,
|
|
pIrpContext->pCompletionRoutine,
|
|
pIrpContext->pCompletionContext
|
|
);
|
|
|
|
//
|
|
// Free the IRP context we allocated.
|
|
//
|
|
|
|
UlPplFreeIrpContext(pIrpContext);
|
|
|
|
//
|
|
// IO can't handle completing an IRP with a non-paged MDL attached
|
|
// to it, so we'll free the MDL here.
|
|
//
|
|
|
|
ASSERT( pIrp->MdlAddress != NULL );
|
|
UlFreeMdl( pIrp->MdlAddress );
|
|
pIrp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Remove the connection we added in UlReceiveData()
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
|
|
//
|
|
// Free the IRP since we're done with it, then tell IO to
|
|
// stop processing the IRP.
|
|
//
|
|
|
|
UlFreeIrp(pIrp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UlpRestartClientReceive
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Removes all active connections from the specified endpoint and initiates
|
|
abortive disconnects.
|
|
|
|
Arguments:
|
|
|
|
pEndpoint - Supplies the endpoint to purge.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - completion status
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpDisconnectAllActiveConnections(
|
|
IN PUL_ENDPOINT pEndpoint
|
|
)
|
|
{
|
|
LIST_ENTRY RetiringList;
|
|
PLIST_ENTRY pListEntry;
|
|
PUL_CONNECTION pConnection;
|
|
PUL_IRP_CONTEXT pIrpContext = &pEndpoint->CleanupIrpContext;
|
|
NTSTATUS Status;
|
|
UL_STATUS_BLOCK StatusBlock;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
UlTrace(TDI, (
|
|
"UlpDisconnectAllActiveConnections: endpoint %p\n",
|
|
pEndpoint
|
|
));
|
|
|
|
//
|
|
// This routine is not pageable because it must acquire a spinlock.
|
|
// However, it must be called at passive IRQL because it must
|
|
// block on an event object.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
|
|
|
|
//
|
|
// Initialize a status block. We'll pass a pointer to this as
|
|
// the completion context to UlpCloseRawConnection(). The
|
|
// completion routine will update the status block and signal
|
|
// the event.
|
|
//
|
|
|
|
UlInitializeStatusBlock( &StatusBlock );
|
|
|
|
//
|
|
// Loop through all of the active connections.
|
|
//
|
|
|
|
InitializeListHead( &RetiringList );
|
|
|
|
UlAcquireSpinLock( &g_TdiSpinLock, &OldIrql );
|
|
|
|
for (pListEntry = g_TdiConnectionListHead.Flink;
|
|
pListEntry != &g_TdiConnectionListHead;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
pConnection = CONTAINING_RECORD(
|
|
pListEntry,
|
|
UL_CONNECTION,
|
|
GlobalConnectionListEntry
|
|
);
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
if (pConnection->ConnListState == ActiveNoConnList &&
|
|
pConnection->pOwningEndpoint == pEndpoint)
|
|
{
|
|
UlAcquireSpinLockAtDpcLevel(
|
|
&pConnection->ConnectionStateSpinLock
|
|
);
|
|
|
|
if (pConnection->ConnListState == ActiveNoConnList)
|
|
{
|
|
pConnection->ConnListState = NoConnList;
|
|
|
|
InsertTailList(
|
|
&RetiringList,
|
|
&pConnection->RetiringListEntry
|
|
);
|
|
}
|
|
|
|
UlReleaseSpinLockFromDpcLevel(
|
|
&pConnection->ConnectionStateSpinLock
|
|
);
|
|
}
|
|
}
|
|
|
|
UlReleaseSpinLock( &g_TdiSpinLock, OldIrql );
|
|
|
|
while (!IsListEmpty(&RetiringList))
|
|
{
|
|
//
|
|
// Remove the connection from the retiring connection list.
|
|
//
|
|
|
|
pListEntry = RemoveHeadList( &RetiringList );
|
|
|
|
//
|
|
// Validate the connection.
|
|
//
|
|
|
|
pConnection = CONTAINING_RECORD(
|
|
pListEntry,
|
|
UL_CONNECTION,
|
|
RetiringListEntry
|
|
);
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
ASSERT( pConnection->pOwningEndpoint == pEndpoint );
|
|
|
|
//
|
|
// Abort it.
|
|
//
|
|
|
|
UlResetStatusBlockEvent( &StatusBlock );
|
|
|
|
Status = UlpCloseRawConnection(
|
|
pConnection,
|
|
TRUE,
|
|
&UlpSynchronousIoComplete,
|
|
&StatusBlock
|
|
);
|
|
|
|
ASSERT( Status == STATUS_PENDING );
|
|
|
|
//
|
|
// Wait for it to complete.
|
|
//
|
|
|
|
UlWaitForStatusBlockEvent( &StatusBlock );
|
|
|
|
//
|
|
// Remove the connection reference for ActiveNoConnList.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pConnection );
|
|
}
|
|
|
|
//
|
|
// Purge all zombie connections that belong to this endpoint.
|
|
//
|
|
|
|
UlPurgeZombieConnections(
|
|
&UlPurgeListeningEndpoint,
|
|
(PVOID) pEndpoint
|
|
);
|
|
|
|
//
|
|
// No active connections, nuke the endpoint.
|
|
//
|
|
// We must set the IRP context in the endpoint so that the
|
|
// completion will be invoked when the endpoint's reference
|
|
// count drops to zero. Since the completion routine may be
|
|
// invoked at a later time, we always return STATUS_PENDING.
|
|
//
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pEndpoint;
|
|
|
|
DEREFERENCE_ENDPOINT_SELF( pEndpoint, REF_ACTION_DISCONN_ACTIVE );
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} // UlpDisconnectAllActiveConnections
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Unbinds an active connection from the endpoint. If the connection
|
|
is on the active list this routine removes it and drops the
|
|
list's reference to the connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - the connection to unbind
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpUnbindConnectionFromEndpoint(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT(IS_VALID_CONNECTION(pConnection));
|
|
|
|
ASSERT(pConnection->IdleSListEntry.Next == NULL);
|
|
pEndpoint = pConnection->pOwningEndpoint;
|
|
ASSERT(IS_VALID_ENDPOINT(pEndpoint));
|
|
|
|
ASSERT(UlDbgSpinLockOwned(&pConnection->ConnectionStateSpinLock));
|
|
|
|
if (pConnection->ConnListState == ActiveNoConnList)
|
|
{
|
|
pConnection->ConnListState = RetiringNoConnList;
|
|
DEREFERENCE_CONNECTION(pConnection);
|
|
}
|
|
|
|
ASSERT(UlpConnectionIsOnValidList(pConnection));
|
|
|
|
} // UlpUnbindConnectionFromEndpoint
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Scan the endpoint list looking for one corresponding to the supplied
|
|
address.
|
|
|
|
Note: This routine assumes the TDI spinlock is held.
|
|
|
|
Arguments:
|
|
|
|
pAddress - Supplies the address to search for.
|
|
|
|
Return Value:
|
|
|
|
PUL_ENDPOINT - The corresponding endpoint if successful, NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PUL_ENDPOINT
|
|
UlpFindEndpointForPort(
|
|
IN USHORT Port
|
|
)
|
|
{
|
|
PUL_ENDPOINT pEndpoint;
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockOwned( &g_TdiSpinLock ) );
|
|
ASSERT( Port > 0 );
|
|
|
|
//
|
|
// Scan the endpoint list.
|
|
//
|
|
// CODEWORK: linear searches are BAD, if the list grows long.
|
|
// May need to augment this with a hash table or something.
|
|
//
|
|
|
|
for (pListEntry = g_TdiEndpointListHead.Flink ;
|
|
pListEntry != &g_TdiEndpointListHead ;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
pEndpoint = CONTAINING_RECORD(
|
|
pListEntry,
|
|
UL_ENDPOINT,
|
|
GlobalEndpointListEntry
|
|
);
|
|
|
|
if (pEndpoint->LocalPort == Port)
|
|
{
|
|
//
|
|
// Found the address; return it.
|
|
//
|
|
|
|
UlTrace(TDI,(
|
|
"UlpFindEndpointForPort: found endpoint %p for port %d\n",
|
|
pEndpoint,
|
|
SWAP_SHORT(Port)
|
|
));
|
|
|
|
return pEndpoint;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we made it this far, then we did not find the address.
|
|
//
|
|
|
|
UlTrace(TDI,(
|
|
"UlpFindEndpointForPort: DID NOT find endpoint for port %d\n",
|
|
SWAP_SHORT(Port)
|
|
));
|
|
|
|
|
|
return NULL;
|
|
|
|
} // UlpFindEndpointForAddress
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for synthetic synchronous IRPs.
|
|
|
|
Arguments:
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value
|
|
as passed to the asynchronous API. In this case, this is
|
|
a pointer to a UL_STATUS_BLOCK structure.
|
|
|
|
Status - Supplies the final completion status of the
|
|
asynchronous API.
|
|
|
|
Information - Optionally supplies additional information about
|
|
the completed operation, such as the number of bytes
|
|
transferred. This field is unused for UlCloseListeningEndpoint().
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpSynchronousIoComplete(
|
|
IN PVOID pCompletionContext,
|
|
IN NTSTATUS Status,
|
|
IN ULONG_PTR Information
|
|
)
|
|
{
|
|
PUL_STATUS_BLOCK pStatus;
|
|
|
|
//
|
|
// Snag the status block pointer.
|
|
//
|
|
|
|
pStatus = (PUL_STATUS_BLOCK)pCompletionContext;
|
|
|
|
//
|
|
// Update the completion status and signal the event.
|
|
//
|
|
|
|
UlSignalStatusBlock( pStatus, Status, Information );
|
|
|
|
} // UlpSynchronousIoComplete
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Enable optimization for interrpt moderation on a Address Object (Listening
|
|
Endpoint)
|
|
|
|
Arguments:
|
|
|
|
pTdiObject - Supplies the TDI address object to manipulate.
|
|
|
|
Flag - Supplies TRUE to enable Optimization, FALSE to disable it.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpOptimizeForInterruptModeration(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN BOOLEAN Flag
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PTCP_REQUEST_SET_INFORMATION_EX pSetInfoEx;
|
|
ULONG value;
|
|
UCHAR buffer[sizeof(*pSetInfoEx) - sizeof(pSetInfoEx->Buffer) + sizeof(value)];
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
|
|
value = (ULONG) Flag;
|
|
|
|
//
|
|
// Setup the buffer.
|
|
//
|
|
|
|
pSetInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)buffer;
|
|
|
|
pSetInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY;
|
|
pSetInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE;
|
|
pSetInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL;
|
|
pSetInfoEx->ID.toi_type = INFO_TYPE_ADDRESS_OBJECT;
|
|
pSetInfoEx->ID.toi_id = AO_OPTION_SCALE_CWIN;
|
|
pSetInfoEx->BufferSize = sizeof(value);
|
|
RtlCopyMemory( pSetInfoEx->Buffer, &value, sizeof(value) );
|
|
|
|
status = ZwDeviceIoControlFile(
|
|
pTdiObject->Handle, // FileHandle
|
|
NULL, // Event
|
|
NULL, // ApcRoutine
|
|
NULL, // ApcContext
|
|
&ioStatusBlock, // IoStatusBlock
|
|
IOCTL_TCP_SET_INFORMATION_EX, // IoControlCode
|
|
pSetInfoEx, // InputBuffer
|
|
sizeof(buffer), // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
status = ZwWaitForSingleObject(
|
|
pTdiObject->Handle, // Handle
|
|
TRUE, // Alertable
|
|
NULL // Timeout
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlpOptimizeForInterruptModeration
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Enable/disable Nagle's Algorithm on the specified TDI connection object.
|
|
|
|
Arguments:
|
|
|
|
pTdiObject - Supplies the TDI connection object to manipulate.
|
|
|
|
Flag - Supplies TRUE to enable Nagling, FALSE to disable it.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpSetNagling(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN BOOLEAN Flag
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PTCP_REQUEST_SET_INFORMATION_EX pSetInfoEx;
|
|
ULONG value;
|
|
UCHAR buffer[sizeof(*pSetInfoEx) - sizeof(pSetInfoEx->Buffer) + sizeof(value)];
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
//
|
|
// Note: NODELAY semantics are inverted from the usual enable/disable
|
|
// semantics.
|
|
//
|
|
|
|
value = (ULONG)!Flag;
|
|
|
|
//
|
|
// Setup the buffer.
|
|
//
|
|
|
|
pSetInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)buffer;
|
|
|
|
pSetInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY;
|
|
pSetInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE;
|
|
pSetInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL;
|
|
pSetInfoEx->ID.toi_type = INFO_TYPE_CONNECTION;
|
|
pSetInfoEx->ID.toi_id = TCP_SOCKET_NODELAY;
|
|
pSetInfoEx->BufferSize = sizeof(value);
|
|
RtlCopyMemory( pSetInfoEx->Buffer, &value, sizeof(value) );
|
|
|
|
status = ZwDeviceIoControlFile(
|
|
pTdiObject->Handle, // FileHandle
|
|
NULL, // Event
|
|
NULL, // ApcRoutine
|
|
NULL, // ApcContext
|
|
&ioStatusBlock, // IoStatusBlock
|
|
IOCTL_TCP_SET_INFORMATION_EX, // IoControlCode
|
|
pSetInfoEx, // InputBuffer
|
|
sizeof(buffer), // InputBufferLength
|
|
NULL, // OutputBuffer
|
|
0 // OutputBufferLength
|
|
);
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
status = ZwWaitForSingleObject(
|
|
pTdiObject->Handle, // Handle
|
|
TRUE, // Alertable
|
|
NULL // Timeout
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(status) );
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlpSetNagling
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Query the TDI fast send routine to see if fast send is possible.
|
|
|
|
Arguments:
|
|
DeviceName - DD_TCP_DEVICE_NAME or DD_TCPV6_DEVICE_NAME
|
|
|
|
pDispatchRoutine - where the function pointer is deposited
|
|
|
|
Return Value:
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpQueryTcpFastSend(
|
|
PWSTR DeviceName,
|
|
OUT PUL_TCPSEND_DISPATCH* pDispatchRoutine
|
|
)
|
|
{
|
|
UNICODE_STRING TCPDeviceName;
|
|
PFILE_OBJECT pTCPFileObject;
|
|
PDEVICE_OBJECT pTCPDeviceObject;
|
|
PIRP Irp;
|
|
IO_STATUS_BLOCK StatusBlock;
|
|
KEVENT Event;
|
|
NTSTATUS status;
|
|
|
|
status = UlInitUnicodeStringEx(&TCPDeviceName, DeviceName);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = IoGetDeviceObjectPointer(
|
|
&TCPDeviceName,
|
|
FILE_ALL_ACCESS,
|
|
&pTCPFileObject,
|
|
&pTCPDeviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
Irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER,
|
|
pTCPDeviceObject,
|
|
pDispatchRoutine,
|
|
sizeof(*pDispatchRoutine),
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
&Event,
|
|
&StatusBlock
|
|
);
|
|
|
|
if (Irp)
|
|
{
|
|
status = UlCallDriver(pTCPDeviceObject, Irp);
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
status = StatusBlock.Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
ObDereferenceObject(pTCPFileObject);
|
|
|
|
return status;
|
|
} // UlpQueryTcpFastSend
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Build a receive buffer and IRP to TDI to get any pending data.
|
|
|
|
Arguments:
|
|
|
|
pTdiObject - Supplies the TDI connection object to manipulate.
|
|
|
|
pConnection - Supplies the UL_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpBuildTdiReceiveBuffer(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN PUL_CONNECTION pConnection,
|
|
OUT PIRP *pIrp
|
|
)
|
|
{
|
|
PUL_RECEIVE_BUFFER pBuffer;
|
|
|
|
if (pTdiObject->pDeviceObject->StackSize > DEFAULT_IRP_STACK_SIZE)
|
|
{
|
|
pBuffer = UlAllocateReceiveBuffer(
|
|
pTdiObject->pDeviceObject->StackSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pBuffer = UlPplAllocateReceiveBuffer();
|
|
}
|
|
|
|
if (pBuffer != NULL)
|
|
{
|
|
//
|
|
// Finish initializing the buffer and the IRP.
|
|
//
|
|
|
|
REFERENCE_CONNECTION( pConnection );
|
|
pBuffer->pConnectionContext = pConnection;
|
|
pBuffer->UnreadDataLength = 0;
|
|
|
|
TdiBuildReceive(
|
|
pBuffer->pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UlpRestartReceive, // CompletionRoutine
|
|
pBuffer, // CompletionContext
|
|
pBuffer->pMdl, // MdlAddress
|
|
TDI_RECEIVE_NORMAL, // Flags
|
|
g_UlReceiveBufferSize // Length
|
|
);
|
|
|
|
|
|
UlTrace(TDI, (
|
|
"UlpBuildTdiReceiveBuffer: connection %p, "
|
|
"allocated irp %p to grab more data\n",
|
|
pConnection,
|
|
pBuffer->pIrp
|
|
));
|
|
|
|
//
|
|
// We must trace the IRP before we set the next stack
|
|
// location so the trace code can pull goodies from the
|
|
// IRP correctly.
|
|
//
|
|
|
|
TRACE_IRP( IRP_ACTION_CALL_DRIVER, pBuffer->pIrp );
|
|
|
|
//
|
|
// Pass the IRP back to the transport.
|
|
//
|
|
|
|
*pIrp = pBuffer->pIrp;
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
} // UlpBuildTdiReceiveBuffer
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Returns the length required for HTTP_RAW_CONNECTION
|
|
|
|
Arguments:
|
|
|
|
pConnectionContext - Pointer to the UL_CONNECTION
|
|
|
|
--***************************************************************************/
|
|
ULONG
|
|
UlpComputeHttpRawConnectionLength(
|
|
IN PVOID pConnectionContext
|
|
)
|
|
{
|
|
C_ASSERT(SOCKADDR_ADDRESS_LENGTH_IP6 >= SOCKADDR_ADDRESS_LENGTH_IP);
|
|
|
|
UNREFERENCED_PARAMETER(pConnectionContext);
|
|
|
|
return (sizeof(HTTP_RAW_CONNECTION_INFO) +
|
|
2 * ALIGN_UP(SOCKADDR_ADDRESS_LENGTH_IP6, PVOID));
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Builds the HTTP_RAW_CONNECTION structure
|
|
|
|
Arguments:
|
|
|
|
pContext - Pointer to the UL_CONNECTION
|
|
pKernelBuffer - Pointer to kernel buffer
|
|
pUserBuffer - Pointer to user buffer
|
|
OutputBufferLength - Length of output buffer
|
|
pBuffer - Buffer for holding any data
|
|
InitialLength - Size of input data.
|
|
|
|
--***************************************************************************/
|
|
ULONG
|
|
UlpGenerateHttpRawConnectionInfo(
|
|
IN PVOID pContext,
|
|
IN PUCHAR pKernelBuffer,
|
|
IN PVOID pUserBuffer,
|
|
IN ULONG OutputBufferLength,
|
|
IN PUCHAR pBuffer,
|
|
IN ULONG InitialLength
|
|
)
|
|
{
|
|
PHTTP_RAW_CONNECTION_INFO pConnInfo;
|
|
PUCHAR pLocalAddress;
|
|
PUCHAR pRemoteAddress;
|
|
PHTTP_TRANSPORT_ADDRESS pAddress;
|
|
ULONG BytesCopied = 0;
|
|
PUCHAR pInitialData;
|
|
PUL_CONNECTION pConnection = (PUL_CONNECTION) pContext;
|
|
USHORT AlignedAddressLength;
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
pConnInfo = (PHTTP_RAW_CONNECTION_INFO) pKernelBuffer;
|
|
|
|
// We've allocated enough space for two SOCKADDR_IN6s, so use that
|
|
AlignedAddressLength = (USHORT) ALIGN_UP(SOCKADDR_ADDRESS_LENGTH_IP6, PVOID);
|
|
pLocalAddress = (PUCHAR)( pConnInfo + 1 );
|
|
pRemoteAddress = pLocalAddress + AlignedAddressLength;
|
|
|
|
pInitialData = pRemoteAddress + AlignedAddressLength;
|
|
|
|
//
|
|
// Now fill in the raw connection data structure.
|
|
//
|
|
pConnInfo->ConnectionId = pConnection->FilterInfo.ConnectionId;
|
|
|
|
pAddress = &pConnInfo->Address;
|
|
|
|
pAddress->pRemoteAddress = FIXUP_PTR(
|
|
PVOID,
|
|
pUserBuffer,
|
|
pKernelBuffer,
|
|
pRemoteAddress,
|
|
OutputBufferLength
|
|
);
|
|
|
|
CopyTdiAddrToSockAddr(
|
|
pConnection->AddressType,
|
|
pConnection->RemoteAddress,
|
|
(struct sockaddr*) pRemoteAddress
|
|
);
|
|
|
|
|
|
pAddress->pLocalAddress = FIXUP_PTR(
|
|
PVOID,
|
|
pUserBuffer,
|
|
pKernelBuffer,
|
|
pLocalAddress,
|
|
OutputBufferLength
|
|
);
|
|
|
|
CopyTdiAddrToSockAddr(
|
|
pConnection->AddressType,
|
|
pConnection->LocalAddress,
|
|
(struct sockaddr*) pLocalAddress
|
|
);
|
|
|
|
//
|
|
// Copy any initial data.
|
|
//
|
|
if (InitialLength)
|
|
{
|
|
ASSERT(pBuffer);
|
|
|
|
pConnInfo->InitialDataSize = InitialLength;
|
|
|
|
pConnInfo->pInitialData = FIXUP_PTR(
|
|
PVOID, // Type
|
|
pUserBuffer, // pUserPtr
|
|
pKernelBuffer, // pKernelPtr
|
|
pInitialData, // pOffsetPtr
|
|
OutputBufferLength // BufferLength
|
|
);
|
|
|
|
RtlCopyMemory(pInitialData, pBuffer, InitialLength);
|
|
|
|
BytesCopied += InitialLength;
|
|
}
|
|
else
|
|
{
|
|
pConnInfo->InitialDataSize = 0;
|
|
pConnInfo->pInitialData = NULL;
|
|
}
|
|
|
|
return BytesCopied;
|
|
|
|
} // UlpGenerateHttpRawConnectionInfo
|
|
|
|
|
|
BOOLEAN
|
|
UlpConnectionIsOnValidList(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
BOOLEAN Valid = TRUE;
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
switch (pConnection->ConnListState)
|
|
{
|
|
case NoConnList:
|
|
case RetiringNoConnList:
|
|
case ActiveNoConnList:
|
|
ASSERT( pConnection->IdleSListEntry.Next == NULL );
|
|
break;
|
|
case IdleConnList:
|
|
// If this is the last connection in the idle list, then
|
|
// IdleSListEntry.Next==NULL. There's no easy way to tell.
|
|
break;
|
|
default:
|
|
ASSERT(!"Invalid ConnListState");
|
|
Valid = FALSE;
|
|
break;
|
|
}
|
|
|
|
return Valid;
|
|
|
|
} // UlpConnectionIsOnValidList
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Converts a RegMultiSz value to an array of UL_TRANSPORT_ADDRESS's.
|
|
If successful, caller must free with UlFreeUlAddrArray( *ppTa ).
|
|
|
|
The list of string address may contain both IPv4 and IPv6 addresses.
|
|
The IPv6 addresses should be bracketed. e.g.:
|
|
1.1.1.1
|
|
[FE80::1]
|
|
[::]
|
|
2.2.2.2
|
|
|
|
Arguments:
|
|
|
|
MultiSz - RegMultiSz returned from UlReadGenericParameter
|
|
|
|
ppTa - pointer to location to receive pointer to newly alloc'd array of
|
|
UL_TRANSPORT_ADDRESS structs
|
|
|
|
pAddrCount - pointer to location to receive the count of valid elements
|
|
in *ppTa.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if we were able to allocate the TRANSPORT_ADDRESS struct
|
|
and fill in at least one address from the MultiSz list.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlRegMultiSzToUlAddrArray(
|
|
IN PWSTR MultiSz,
|
|
OUT PUL_TRANSPORT_ADDRESS *ppTa,
|
|
OUT ULONG *pAddrCount
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG count, i;
|
|
PWSTR wszCurrent;
|
|
PWSTR wszTerm;
|
|
PWSTR wszSave;
|
|
ULONG dataLength;
|
|
PUL_TRANSPORT_ADDRESS pTa, pTaCurrent;
|
|
struct in_addr IPv4Addr;
|
|
BOOLEAN BracketSeen;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
|
|
if ( !MultiSz || !wcslen(MultiSz) || !ppTa || !pAddrCount )
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppTa = NULL;
|
|
*pAddrCount = 0;
|
|
|
|
// first pass: count number of entries in list
|
|
count = 0;
|
|
wszCurrent = MultiSz;
|
|
|
|
while ( *wszCurrent )
|
|
{
|
|
// step over current string
|
|
wszCurrent += (wcslen( wszCurrent ) + 1);
|
|
count++;
|
|
}
|
|
|
|
ASSERT( count );
|
|
|
|
if ( 0 >= count )
|
|
{
|
|
// We have yet to allocate any resources.
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Alloc space for all converted addresses, even if some fail.
|
|
//
|
|
|
|
pTa = UL_ALLOCATE_ARRAY(
|
|
NonPagedPool,
|
|
UL_TRANSPORT_ADDRESS,
|
|
count,
|
|
UL_TRANSPORT_ADDRESS_POOL_TAG
|
|
);
|
|
|
|
if (pTa == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
dataLength = count * sizeof(UL_TRANSPORT_ADDRESS);
|
|
RtlZeroMemory( pTa, dataLength );
|
|
|
|
// second pass: convert and shove into TA.
|
|
wszCurrent = MultiSz;
|
|
i = 0;
|
|
pTaCurrent = pTa;
|
|
|
|
while ( *wszCurrent )
|
|
{
|
|
// Preserve for event log message.
|
|
wszSave = wszCurrent;
|
|
|
|
// First try IPv4
|
|
pTaCurrent->TaIp.TAAddressCount = 1;
|
|
pTaCurrent->TaIp.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
|
|
pTaCurrent->TaIp.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
|
|
|
status = RtlIpv4StringToAddressW(
|
|
wszCurrent,
|
|
FALSE, // Strict
|
|
&wszTerm, // Terminator
|
|
&IPv4Addr // IPv4Addr
|
|
);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
|
|
// Left hand side in_addr is unaligned, since it is a field
|
|
// in a packed structure and it is coming after an USHORT.
|
|
// Need to be careful here.
|
|
|
|
* (struct in_addr UNALIGNED *)
|
|
&pTaCurrent->TaIp.Address[0].Address[0].in_addr
|
|
= IPv4Addr;
|
|
}
|
|
else
|
|
{
|
|
// Now try IPv6
|
|
pTaCurrent->TaIp6.TAAddressCount = 1;
|
|
pTaCurrent->TaIp6.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP6;
|
|
pTaCurrent->TaIp6.Address[0].AddressType = TDI_ADDRESS_TYPE_IP6;
|
|
|
|
// Step over leading L'['
|
|
if (L'[' == *wszCurrent)
|
|
{
|
|
BracketSeen = TRUE;
|
|
wszCurrent++;
|
|
}
|
|
else
|
|
{
|
|
BracketSeen = FALSE;
|
|
}
|
|
|
|
// CODEWORK: replace with full-blown IPv6 w/Scope-ID conversion
|
|
// function when it becomes available.
|
|
|
|
status = RtlIpv6StringToAddressW(
|
|
wszCurrent,
|
|
&wszTerm, // Terminator
|
|
(struct in6_addr *)
|
|
&pTaCurrent->TaIp6.Address[0].Address[0].sin6_addr
|
|
);
|
|
|
|
if ( NT_SUCCESS(status) && L'%' == *wszTerm )
|
|
{
|
|
ULONG scope_id;
|
|
|
|
// step past '%'
|
|
wszTerm++;
|
|
|
|
status = HttpWideStringToULong(
|
|
wszTerm, // string
|
|
0, // string is NULL terminated
|
|
FALSE,
|
|
10,
|
|
NULL,
|
|
&scope_id
|
|
);
|
|
|
|
if ( NT_SUCCESS(status) )
|
|
{
|
|
// TDI_ADDRESS_IP6 is a packed struct. sin6_scope_id
|
|
// may be unaligned.
|
|
|
|
// Scope ID does not get swapped to Network Byte Order
|
|
*(UNALIGNED64 ULONG *)&
|
|
pTaCurrent->TaIp6.Address[0].Address[0].sin6_scope_id =
|
|
scope_id;
|
|
}
|
|
|
|
// step past digits
|
|
while ((*wszTerm) >= L'0' && (*wszTerm) <= L'9')
|
|
{
|
|
wszTerm++;
|
|
}
|
|
}
|
|
|
|
// check for L']'
|
|
if ( BracketSeen && L']' != *wszTerm )
|
|
{
|
|
// Invalid IPv6 Address Format, skip this one
|
|
status = STATUS_INVALID_ADDRESS;
|
|
}
|
|
}
|
|
|
|
// only move on to the next slot if we successfuly
|
|
// converted the address
|
|
if ( NT_SUCCESS(status) )
|
|
{
|
|
i++;
|
|
pTaCurrent++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Write Event log message that wszSave could not
|
|
// be converted.
|
|
//
|
|
|
|
UlEventLogOneStringEntry(
|
|
EVENT_HTTP_LISTEN_ONLY_CONVERT_FAILED,
|
|
wszSave,
|
|
TRUE,
|
|
status
|
|
);
|
|
}
|
|
|
|
wszCurrent += (wcslen( wszCurrent ) + 1);
|
|
}
|
|
|
|
if ( 0 == i )
|
|
{
|
|
// nothing converted successfully.
|
|
status = STATUS_INVALID_PARAMETER;
|
|
if ( pTa )
|
|
{
|
|
UlFreeUlAddr( pTa );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// CODEWORK: When we do dynamic adding/removing of addresses, we'll
|
|
// need to sort the list (to make it easier to insert & remove).
|
|
|
|
status = STATUS_SUCCESS;
|
|
*pAddrCount = i;
|
|
*ppTa = pTa;
|
|
}
|
|
|
|
return status;
|
|
|
|
}// UlRegMultiSzToUlAddrArray
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Checks if pConnection->pOwningEndpoint has any active addresses.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection object to check.
|
|
|
|
Return Value:
|
|
|
|
FALSE if there is no AO or TRUE otherwise.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlCheckListeningEndpointState(
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
if (pConnection->pOwningEndpoint->UsageCount)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
} // UlCheckListeningEndpointState
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Does a routing lookup & returns the Interface ID & Link ID.
|
|
|
|
Arguments:
|
|
|
|
pConnection - The connection object.
|
|
|
|
Return Value:
|
|
|
|
FALSE if there is no AO or TRUE otherwise.
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
UlGetConnectionRoutingInfo(
|
|
IN PUL_CONNECTION pConnection,
|
|
OUT PULONG pInterfaceId,
|
|
OUT PULONG pLinkId
|
|
)
|
|
{
|
|
PTDI_ROUTING_INFO pTdiRoutingInfo;
|
|
ULONG TdiRoutingInfoSize;
|
|
TDI_REQUEST_KERNEL_QUERY_INFORMATION TdiRequestQueryInformation;
|
|
NTSTATUS Status;
|
|
|
|
if(pConnection->bRoutingLookupDone)
|
|
{
|
|
*pInterfaceId = pConnection->InterfaceId;
|
|
*pLinkId = pConnection->LinkId;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
*pInterfaceId = 0;
|
|
*pLinkId = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Do a routing lookup to get the interface that we are going to send
|
|
// the packet out on. The flow has to be installed on that interface.
|
|
//
|
|
|
|
TdiRequestQueryInformation.QueryType = TDI_QUERY_ROUTING_INFO;
|
|
TdiRequestQueryInformation.RequestConnectionInformation = NULL;
|
|
|
|
TdiRoutingInfoSize = sizeof(TDI_ROUTING_INFO) + 2 * sizeof(TDI_ADDRESS_IP);
|
|
pTdiRoutingInfo = UL_ALLOCATE_POOL(
|
|
PagedPool,
|
|
TdiRoutingInfoSize,
|
|
UL_TCI_GENERIC_POOL_TAG
|
|
);
|
|
|
|
if(NULL == pTdiRoutingInfo)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = UlIssueDeviceControl(
|
|
&pConnection->ConnectionObject,
|
|
&TdiRequestQueryInformation,
|
|
sizeof(TdiRequestQueryInformation),
|
|
pTdiRoutingInfo,
|
|
TdiRoutingInfoSize,
|
|
TDI_QUERY_INFORMATION
|
|
);
|
|
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
*pInterfaceId = pTdiRoutingInfo->InterfaceId;
|
|
*pLinkId = pTdiRoutingInfo->LinkId;
|
|
|
|
//
|
|
// Cache it on the connection for subsequent lookups.
|
|
//
|
|
|
|
pConnection->InterfaceId = pTdiRoutingInfo->InterfaceId;
|
|
pConnection->LinkId = pTdiRoutingInfo->LinkId;
|
|
pConnection->bRoutingLookupDone = TRUE;
|
|
|
|
}
|
|
|
|
UL_FREE_POOL(pTdiRoutingInfo, UL_TCI_GENERIC_POOL_TAG);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
If pParsedUrl is an IP constrained site, AND the g_pTdiListenAddresses list
|
|
doesn't contain either a matching ADDR_ANY/in6addr_any or an exact IP match
|
|
then return FALSE.
|
|
|
|
Otherwise, return TRUE.
|
|
|
|
Arguments:
|
|
|
|
pParsedUrl -- fully cooked down url from UlAddUrl*
|
|
|
|
Return Value:
|
|
|
|
TRUE if routeable, FALSE if NOT routeable.
|
|
|
|
--*/
|
|
BOOLEAN
|
|
UlpIsUrlRouteableInListenScope(
|
|
IN PHTTP_PARSED_URL pParsedUrl
|
|
)
|
|
{
|
|
PUL_TRANSPORT_ADDRESS pListenTa;
|
|
USHORT Family;
|
|
PUCHAR pAddr;
|
|
USHORT AddrLen;
|
|
BOOLEAN Routeable = FALSE;
|
|
PSOCKADDR pAddrAny;
|
|
SOCKADDR_IN6 DummyAddr;
|
|
ULONG i;
|
|
|
|
|
|
//
|
|
// Sanity Check
|
|
//
|
|
|
|
ASSERT( pParsedUrl );
|
|
|
|
//
|
|
// Check if routing is even an issue for this URL
|
|
//
|
|
|
|
if ( HttpUrlSite_IP != pParsedUrl->SiteType &&
|
|
HttpUrlSite_NamePlusIP != pParsedUrl->SiteType )
|
|
{
|
|
// no IP routing issues
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Fail the call if the listen addr list is not present.
|
|
//
|
|
|
|
if (!g_pTdiListenAddresses || (0 == g_TdiListenAddrCount))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Grab relevant address pointers & lengths
|
|
//
|
|
|
|
if ( HttpUrlSite_IP == pParsedUrl->SiteType )
|
|
{
|
|
// Grab address & family info from SockAddr
|
|
Family = pParsedUrl->SockAddr.sa_family;
|
|
|
|
if (TDI_ADDRESS_TYPE_IP == Family)
|
|
{
|
|
pAddr = (PUCHAR) &pParsedUrl->SockAddr4.sin_addr;
|
|
AddrLen = sizeof(ULONG);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(TDI_ADDRESS_TYPE_IP6 == Family);
|
|
|
|
pAddr = (PUCHAR) &pParsedUrl->SockAddr6.sin6_addr;
|
|
AddrLen = sizeof(IN6_ADDR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT( HttpUrlSite_NamePlusIP == pParsedUrl->SiteType );
|
|
|
|
// Grab address & family info from RoutingAddr
|
|
Family = pParsedUrl->RoutingAddr.sa_family;
|
|
|
|
if (TDI_ADDRESS_TYPE_IP == Family)
|
|
{
|
|
pAddr = (PUCHAR) &pParsedUrl->RoutingAddr4.sin_addr;
|
|
AddrLen = sizeof(ULONG);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(TDI_ADDRESS_TYPE_IP6 == Family);
|
|
|
|
pAddr = (PUCHAR) &pParsedUrl->RoutingAddr6.sin6_addr;
|
|
AddrLen = sizeof(IN6_ADDR);
|
|
}
|
|
}
|
|
|
|
//
|
|
// set up INADDR_ANY/ip6addr_any
|
|
//
|
|
|
|
pAddrAny = (PSOCKADDR)&DummyAddr;
|
|
RtlZeroMemory((PVOID) pAddrAny, sizeof(SOCKADDR_IN6));
|
|
|
|
//
|
|
// Walk the list of global listen address entries
|
|
//
|
|
|
|
#define TDI_ADDR_FROM_FAMILY( f, a ) ((TDI_ADDRESS_TYPE_IP == (f) ? \
|
|
(PUCHAR)&((a)->TaIp.Address[0].Address[0].in_addr) : \
|
|
(PUCHAR)((a)->TaIp6.Address[0].Address[0].sin6_addr)))
|
|
|
|
pListenTa = g_pTdiListenAddresses;
|
|
|
|
for ( i = 0; i < g_TdiListenAddrCount; i++ )
|
|
{
|
|
if (pListenTa->Ta.Address[0].AddressType == Family)
|
|
{
|
|
// see if this entry is INADDR_ANY/ip6addr_any
|
|
if (AddrLen == RtlCompareMemory(
|
|
TDI_ADDR_FROM_FAMILY(Family, pListenTa),
|
|
pAddrAny->sa_data,
|
|
AddrLen
|
|
))
|
|
{
|
|
Routeable = TRUE;
|
|
goto Done;
|
|
}
|
|
|
|
// see if we have an exact match
|
|
if (AddrLen == RtlCompareMemory(
|
|
TDI_ADDR_FROM_FAMILY(Family, pListenTa),
|
|
pAddr,
|
|
AddrLen
|
|
))
|
|
{
|
|
// CODEWORK: If IPv6, check sin6_scope_id too...
|
|
Routeable = TRUE;
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
pListenTa++;
|
|
}
|
|
|
|
Done:
|
|
return Routeable;
|
|
}
|
|
|