1387 lines
34 KiB
C
1387 lines
34 KiB
C
/*++
|
|
|
|
Copyright (c) 1998-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ultdip.h
|
|
|
|
Abstract:
|
|
|
|
This module contains declarations private to the TDI component. These
|
|
declarations are placed in a separate .H file to make it easier to access
|
|
them from within the kernel debugger extension DLL.
|
|
|
|
The TDI package manages two major object types: UL_ENDPOINT and
|
|
UL_CONNECTION.
|
|
|
|
A UL_ENDPOINT is basically a wrapper around a TDI address object. Each
|
|
endpoint has a list of associated UL_CONNECTION objects for
|
|
idle (non-connected) connections
|
|
|
|
Active (connected) connections are on the global connection list.
|
|
|
|
A UL_CONNECTION is basically a wrapper around a TDI connection object.
|
|
Its main purpose is to manage TDI connection state. See the description
|
|
of UL_CONNECTION_FLAGS below for the gory details.
|
|
|
|
The relationship between these two objects is illustrated in the
|
|
following diagram:
|
|
|
|
+-----------+
|
|
| |
|
|
|UL_ENDPOINT|
|
|
| |
|
|
+---+----+--+
|
|
|
|
|
|
|
|
| Idle Connections
|
|
| +-------------+ +-------------+ +-------------+
|
|
| | | | | | |
|
|
+->|UL_CONNECTION|-->|UL_CONNECTION|-->|UL_CONNECTION|-->...
|
|
| | | | | |
|
|
+-------------+ +-------------+ +-------------+
|
|
|
|
|
|
|
|
Note: Idle connections do not hold references to their owning endpoint,
|
|
but active connections do. When a listening endpoint is shutdown, all
|
|
idle connections are simply purged, but active connections must be
|
|
forcibly disconnected first.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 15-Jun-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#ifndef _ULTDIP_H_
|
|
#define _ULTDIP_H_
|
|
|
|
|
|
//
|
|
// Forward references.
|
|
//
|
|
|
|
typedef struct _UL_ENDPOINT *PUL_ENDPOINT;
|
|
typedef union _UL_CONNECTION_FLAGS *PUL_CONNECTION_FLAGS;
|
|
typedef struct _UL_CONNECTION *PUL_CONNECTION;
|
|
typedef struct _UL_RECEIVE_BUFFER *PUL_RECEIVE_BUFFER;
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
#define MAX_ADDRESS_EA_BUFFER_LENGTH \
|
|
(sizeof(FILE_FULL_EA_INFORMATION) - 1 + \
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 + \
|
|
sizeof(TA_IP6_ADDRESS))
|
|
|
|
#define MAX_CONNECTION_EA_BUFFER_LENGTH \
|
|
(sizeof(FILE_FULL_EA_INFORMATION) - 1 + \
|
|
TDI_CONNECTION_CONTEXT_LENGTH + 1 + \
|
|
sizeof(CONNECTION_CONTEXT))
|
|
|
|
#define TL_INSTANCE 0
|
|
|
|
//
|
|
// Private types.
|
|
//
|
|
|
|
|
|
//
|
|
// A generic IRP context. This is useful for storing additional completion
|
|
// information associated with a pending IRP.
|
|
//
|
|
// WARNING! All fields of this structure must be explicitly initialized.
|
|
//
|
|
|
|
typedef struct _UL_IRP_CONTEXT
|
|
{
|
|
//
|
|
// This MUST be the first field in the structure. This is the linkage
|
|
// used by the lookaside package for storing entries in the lookaside
|
|
// list.
|
|
//
|
|
|
|
SLIST_ENTRY LookasideEntry;
|
|
|
|
//
|
|
// Structure signature.
|
|
//
|
|
|
|
ULONG Signature;
|
|
|
|
//
|
|
// Either the endpoint or endpoint associated with the IRP.
|
|
//
|
|
|
|
PVOID pConnectionContext;
|
|
|
|
//
|
|
// Completion information.
|
|
//
|
|
|
|
PUL_COMPLETION_ROUTINE pCompletionRoutine;
|
|
PVOID pCompletionContext;
|
|
|
|
//
|
|
// Our own allocated IRP if set.
|
|
//
|
|
|
|
PIRP pOwnIrp;
|
|
|
|
//
|
|
// The TDI send flag (0 or TDI_SEND_AND_DISCONNECT).
|
|
//
|
|
|
|
USHORT TdiSendFlag;
|
|
|
|
//
|
|
// Our own allocated UL_IRP_CONTEXT if set.
|
|
//
|
|
|
|
BOOLEAN OwnIrpContext;
|
|
|
|
//
|
|
// Total send length we passed to TDI_SEND.
|
|
//
|
|
|
|
ULONG_PTR SendLength;
|
|
|
|
} UL_IRP_CONTEXT, *PUL_IRP_CONTEXT;
|
|
|
|
#define UL_IRP_CONTEXT_SIGNATURE MAKE_SIGNATURE('IRPC')
|
|
#define UL_IRP_CONTEXT_SIGNATURE_X MAKE_FREE_SIGNATURE(UL_IRP_CONTEXT_SIGNATURE)
|
|
|
|
#define IS_VALID_IRP_CONTEXT(pIrpContext) \
|
|
HAS_VALID_SIGNATURE(pIrpContext, UL_IRP_CONTEXT_SIGNATURE)
|
|
|
|
|
|
typedef enum _CONN_LIST_STATE
|
|
{
|
|
NoConnList = 1,
|
|
IdleConnList,
|
|
ActiveNoConnList,
|
|
RetiringNoConnList
|
|
|
|
} CONN_LIST_STATE;
|
|
|
|
|
|
//
|
|
// A TDI Address Object and it's pre-allocated lists of idle connections.
|
|
// This is allocated together with a UL_ENDPOINT, which always has at
|
|
// least one of these objects.
|
|
//
|
|
// This does not need a ref count since it's "contained" as part of
|
|
// a UL_ENDPOINT object.
|
|
//
|
|
// CODEWORK: When we want to do dynamic addition/removal of this object,
|
|
// we'll need to add ref counting & add list-linkage to the endpoint,
|
|
// rather than as an array tacked on the end.
|
|
//
|
|
// Methods on this pseudo-class:
|
|
// UlpInitializeAddrIdleList
|
|
// UlpCleanupAddrIdleList
|
|
// UlpReplenishAddrIdleList
|
|
// UlpReplenishAddrIdleListWorker
|
|
// UlpTrimAddrIdleListWorker
|
|
//
|
|
|
|
typedef struct _UL_ADDR_IDLE_LIST
|
|
{
|
|
//
|
|
// Structure signature: UL_ADDR_IDLE_LIST_SIGNATURE
|
|
//
|
|
|
|
ULONG Signature;
|
|
|
|
//
|
|
// The TDI address object.
|
|
//
|
|
|
|
UX_TDI_OBJECT AddressObject;
|
|
|
|
//
|
|
// The local address we're bound to.
|
|
//
|
|
|
|
UL_TRANSPORT_ADDRESS LocalAddress;
|
|
ULONG LocalAddressLength;
|
|
|
|
//
|
|
// Heads of the per-address object connection lists.
|
|
// Idle connections have a weak reference to 'this', the owning endpoint
|
|
//
|
|
|
|
HANDLE IdleConnectionSListsHandle;
|
|
|
|
//
|
|
// When replenish is scheduled, we need to remember the cpu.
|
|
//
|
|
|
|
USHORT CpuToReplenish;
|
|
|
|
//
|
|
// The owning endpoint
|
|
//
|
|
|
|
PUL_ENDPOINT pOwningEndpoint;
|
|
|
|
//
|
|
// Work item for replenishing
|
|
//
|
|
|
|
UL_WORK_ITEM WorkItem;
|
|
LONG WorkItemScheduled;
|
|
|
|
} UL_ADDR_IDLE_LIST, *PUL_ADDR_IDLE_LIST;
|
|
|
|
#define UL_ADDR_IDLE_LIST_SIGNATURE MAKE_SIGNATURE('UlAI')
|
|
#define UL_ADDR_IDLE_LIST_SIGNATURE_X MAKE_FREE_SIGNATURE(UL_ADDR_IDLE_LIST_SIGNATURE)
|
|
|
|
#define IS_VALID_ADDR_IDLE_LIST(pAddrIdleList) \
|
|
HAS_VALID_SIGNATURE(pAddrIdleList, UL_ADDR_IDLE_LIST_SIGNATURE)
|
|
|
|
typedef struct _UL_TRIM_TIMER
|
|
{
|
|
//
|
|
// Timer itself and the corresponding Dpc object.
|
|
//
|
|
|
|
KTIMER Timer;
|
|
KDPC DpcObject;
|
|
UL_WORK_ITEM WorkItem;
|
|
LONG WorkItemScheduled;
|
|
|
|
LIST_ENTRY ZombieConnectionListHead;
|
|
|
|
//
|
|
// Spinlock to protect the following state parameters
|
|
//
|
|
|
|
UL_SPIN_LOCK SpinLock;
|
|
|
|
BOOLEAN Initialized;
|
|
BOOLEAN Started;
|
|
|
|
} UL_TRIM_TIMER, *PUL_TRIM_TIMER;
|
|
|
|
//
|
|
// An endpoint is basically our wrapper around TDI address objects.
|
|
// There is one UL_ENDPOINT per TCP Port. In the common case, there will
|
|
// be three ports: 80 (HTTP), 443 (HTTPS), and a random port for the
|
|
// IIS Admin site.
|
|
//
|
|
|
|
typedef struct _UL_ENDPOINT
|
|
{
|
|
//
|
|
// Structure signature: UL_ENDPOINT_SIGNATURE
|
|
//
|
|
|
|
ULONG Signature;
|
|
|
|
//
|
|
// Reference count.
|
|
//
|
|
|
|
LONG ReferenceCount;
|
|
|
|
//
|
|
// Usage count. This is used by the "URL-site-to-endpoint" thingie.
|
|
//
|
|
|
|
LONG UsageCount;
|
|
|
|
//
|
|
// Links onto the global endpoint list.
|
|
//
|
|
// GlobalEndpointListEntry.Flink is NULL if the endpoint is not
|
|
// on the global list, g_TdiEndpointListHead, or the
|
|
// to-be-deleted-soon list, g_TdiDeletedEndpointListHead.
|
|
//
|
|
|
|
LIST_ENTRY GlobalEndpointListEntry;
|
|
|
|
//
|
|
// Array of TDI Address Object + Connection Objects.
|
|
// One per entry on the global "Listen Only" list, or one entry
|
|
// representing INADDR_ANY/in6addr_any. Allocated at endpoint
|
|
// creation time, directly after the UL_ENDPOINT.
|
|
//
|
|
|
|
ULONG AddrIdleListCount;
|
|
// REVIEW: what's the team's hungarian notation for an array?
|
|
PUL_ADDR_IDLE_LIST aAddrIdleLists;
|
|
|
|
// CODEWORK: ability to change from INADDR_ANY to Listen Only List
|
|
// and vice versa.
|
|
// CODEWORK: ability to dynamicly add/remove AO's. (need spinlock)
|
|
|
|
//
|
|
// Indication handlers & user context.
|
|
//
|
|
|
|
PUL_CONNECTION_REQUEST pConnectionRequestHandler;
|
|
PUL_CONNECTION_COMPLETE pConnectionCompleteHandler;
|
|
PUL_CONNECTION_DISCONNECT pConnectionDisconnectHandler;
|
|
PUL_CONNECTION_DISCONNECT_COMPLETE pConnectionDisconnectCompleteHandler;
|
|
PUL_CONNECTION_DESTROYED pConnectionDestroyedHandler;
|
|
PUL_DATA_RECEIVE pDataReceiveHandler;
|
|
PVOID pListeningContext;
|
|
|
|
//
|
|
// The local TCP Port we're bound to.
|
|
//
|
|
|
|
USHORT LocalPort;
|
|
|
|
//
|
|
// Is this a secure endpoint?
|
|
//
|
|
|
|
BOOLEAN Secure;
|
|
|
|
//
|
|
// Thread work item for deferred actions.
|
|
//
|
|
|
|
UL_WORK_ITEM WorkItem;
|
|
|
|
LONG WorkItemScheduled;
|
|
|
|
//
|
|
// An IRP context containing completion information necessary
|
|
// while shutting down a listening endpoint.
|
|
//
|
|
|
|
UL_IRP_CONTEXT CleanupIrpContext;
|
|
|
|
//
|
|
// Has this endpoint taken a g_TdiEndpointCount?
|
|
//
|
|
|
|
BOOLEAN Counted;
|
|
|
|
//
|
|
// Has this endpoint been moved to the deleted list,
|
|
// g_TdiDeletedEndpointListHead?
|
|
//
|
|
|
|
BOOLEAN Deleted;
|
|
|
|
} UL_ENDPOINT;
|
|
|
|
#define UL_ENDPOINT_SIGNATURE MAKE_SIGNATURE('ENDP')
|
|
#define UL_ENDPOINT_SIGNATURE_X MAKE_FREE_SIGNATURE(UL_ENDPOINT_SIGNATURE)
|
|
|
|
#define IS_VALID_ENDPOINT(pEndpoint) \
|
|
HAS_VALID_SIGNATURE(pEndpoint, UL_ENDPOINT_SIGNATURE)
|
|
|
|
|
|
//
|
|
// Connection flags/state. These flags indicate the current state of a
|
|
// connection.
|
|
//
|
|
// Some of these flags may be simply updated directly. Others require
|
|
// UlInterlockedCompareExchange() to avoid race conditions.
|
|
//
|
|
// The following flags may be updated directly:
|
|
//
|
|
// AcceptPending - SET in the TDI connection handler, just before the
|
|
// accept IRP is returned to the transport. RESET only if the accept
|
|
// IRP fails.
|
|
//
|
|
// The following flags must be updated using UlInterlockedCompareExchange():
|
|
//
|
|
// AcceptComplete - SET in the accept IRP completion handler if the IRP
|
|
// completed successfully. Once this flag is set, the connection must
|
|
// be either gracefully disconnected or aborted before the connection
|
|
// can be closed or reused.
|
|
//
|
|
// DisconnectPending - SET just before a graceful disconnect IRP is
|
|
// issued.
|
|
//
|
|
// DisconnectComplete - SET in the graceful disconnect IRP completion
|
|
// handler.
|
|
//
|
|
// AbortPending - SET just before an abortive disconnect IRP is issued.
|
|
//
|
|
// AbortComplete - SET in the abortive disconnect IRP completion handler.
|
|
//
|
|
// DisconnectIndicated - SET in the TDI disconnect handler for graceful
|
|
// disconnects issued by the remote client.
|
|
//
|
|
// AbortIndicated - SET in the TDI disconnect handler for abortive
|
|
// disconnects issued by the remote client.
|
|
//
|
|
// CleanupPending - SET when cleanup is begun for a connection. This
|
|
// is necessary to know when the final reference to the connection
|
|
// can be removed.
|
|
//
|
|
// CODEWORK: We can get rid of the CleanupPending flag. It is
|
|
// only set when either a graceful or abortive disconnect is
|
|
// issued, and only tested in UlpRemoveFinalReference(). The
|
|
// test in UlpRemoveFinalReference() can just test for either
|
|
// (DisconnectPending | AbortPending) instead.
|
|
//
|
|
// FinalReferenceRemoved - SET when the final (i.e. "connected")
|
|
// reference is removed from the connection.
|
|
//
|
|
// Note that the flags requiring UlInterlockedCompareExchange() are only SET,
|
|
// never RESET. This makes the implementation a bit simpler.
|
|
//
|
|
// And now a few words about connection management, TDI, and other mysteries.
|
|
//
|
|
// Some of the more annoying "features" of TDI are related to connection
|
|
// management and lifetime. Two of the most onerous issues are:
|
|
//
|
|
// 1. Knowing when a connection object handle can be closed without
|
|
// causing an unwanted connection reset.
|
|
//
|
|
// 2. Knowing when TDI has given its last indication on a connection
|
|
// so that resources can be released, reused, recycled, whatever.
|
|
//
|
|
// And, of course, this is further complicated by the inherent asynchronous
|
|
// nature of the NT I/O architecture and the parallelism of SMP systems.
|
|
//
|
|
// There are a few points worth keeping in mind while reading/modifying this
|
|
// source code or writing clients of this code:
|
|
//
|
|
// 1. As soon as an accept IRP is returned from the TDI connection
|
|
// handler to the transport, the TDI client must be prepared for
|
|
// any incoming indications, including data receive and disconnect.
|
|
// In other words, incoming data & disconnect may occur *before* the
|
|
// accept IRP actually completes.
|
|
//
|
|
// 2. A connection is considered "in use" until either both sides have
|
|
// gracefully disconnected OR either side has aborted the connection.
|
|
// Closing an "in use" connection will usually result in an abortive
|
|
// disconnect.
|
|
//
|
|
// 3. The various flavors of disconnect (initiated by the local server,
|
|
// initiated by the remote client, graceful, abortive, etc) may occur
|
|
// in any order.
|
|
//
|
|
|
|
typedef union _UL_CONNECTION_FLAGS
|
|
{
|
|
//
|
|
// This field overlays all of the settable flags. This allows us to
|
|
// update all flags in a thread-safe manner using the
|
|
// UlInterlockedCompareExchange() API.
|
|
//
|
|
|
|
ULONG Value;
|
|
|
|
struct
|
|
{
|
|
ULONG AcceptPending:1; // 00000001 Recv SYN
|
|
ULONG AcceptComplete:1; // 00000002 Accepted
|
|
ULONG :2;
|
|
ULONG DisconnectPending:1; // 00000010 Send FIN
|
|
ULONG DisconnectComplete:1; // 00000020 Send FIN
|
|
ULONG :2;
|
|
ULONG AbortPending:1; // 00000100 Send RST
|
|
ULONG AbortComplete:1; // 00000200 Send RST
|
|
ULONG :2;
|
|
ULONG DisconnectIndicated:1; // 00001000 Recv FIN
|
|
ULONG AbortIndicated:1; // 00002000 Recv RST
|
|
ULONG :2;
|
|
ULONG CleanupBegun:1; // 00010000
|
|
ULONG FinalReferenceRemoved:1; // 00020000
|
|
ULONG AbortDisconnect:1; // 00040000 Send RST after Send FIN
|
|
ULONG :1;
|
|
ULONG LocalAddressValid:1; // 00100000
|
|
ULONG ReceivePending:1; // 00200000
|
|
ULONG :2;
|
|
ULONG TdiConnectionInvalid:1; // 01000000
|
|
};
|
|
|
|
} UL_CONNECTION_FLAGS;
|
|
|
|
C_ASSERT( sizeof(UL_CONNECTION_FLAGS) == sizeof(ULONG) );
|
|
|
|
#define MAKE_CONNECTION_FLAG_ROUTINE(name) \
|
|
__inline ULONG Make##name##Flag() \
|
|
{ \
|
|
UL_CONNECTION_FLAGS flags = { 0 }; \
|
|
flags.name = 1; \
|
|
return flags.Value; \
|
|
}
|
|
|
|
MAKE_CONNECTION_FLAG_ROUTINE( AcceptPending );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( AcceptComplete );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( DisconnectPending );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( DisconnectComplete );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( AbortPending );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( AbortComplete );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( DisconnectIndicated );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( AbortIndicated );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( CleanupBegun );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( FinalReferenceRemoved );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( AbortDisconnect );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( LocalAddressValid );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( ReceivePending );
|
|
MAKE_CONNECTION_FLAG_ROUTINE( TdiConnectionInvalid );
|
|
|
|
|
|
typedef enum _UL_CONNECTION_STATE
|
|
{
|
|
UlConnectStateConnectIdle, // Idle
|
|
UlConnectStateConnectCleanup, // Cleanup
|
|
UlConnectStateConnectReady, // In Use
|
|
UlConnectStateDisconnectPending, // Sent FIN
|
|
UlConnectStateDisconnectComplete, // FIN Completes
|
|
UlConnectStateAbortPending, // Send RST
|
|
|
|
UlConnectStateInvalid // TBD
|
|
|
|
} UL_CONNECTION_STATE;
|
|
|
|
|
|
//
|
|
// A connection is basically our wrapper around a TDI connection object.
|
|
//
|
|
|
|
typedef struct _UL_CONNECTION
|
|
{
|
|
//
|
|
// Link onto the per-endpoint idle connection list.
|
|
//
|
|
|
|
SLIST_ENTRY IdleSListEntry;
|
|
|
|
//
|
|
// Structure signature: UL_CONNECTION_SIGNATURE
|
|
//
|
|
|
|
ULONG Signature;
|
|
|
|
//
|
|
// Reference count.
|
|
//
|
|
|
|
LONG ReferenceCount;
|
|
|
|
//
|
|
// Connection flags.
|
|
//
|
|
|
|
UL_CONNECTION_FLAGS ConnectionFlags;
|
|
|
|
//
|
|
// To synchronize the RawCloseHandler
|
|
//
|
|
|
|
UL_CONNECTION_STATE ConnectionState;
|
|
UL_SPIN_LOCK ConnectionStateSpinLock;
|
|
|
|
//
|
|
// Cached Irp
|
|
//
|
|
|
|
PIRP pIrp;
|
|
|
|
//
|
|
// Addresses and ports. These are in host order.
|
|
//
|
|
|
|
USHORT AddressType;
|
|
USHORT AddressLength;
|
|
|
|
union
|
|
{
|
|
UCHAR RemoteAddress[0];
|
|
TDI_ADDRESS_IP RemoteAddrIn;
|
|
TDI_ADDRESS_IP6 RemoteAddrIn6;
|
|
};
|
|
|
|
union
|
|
{
|
|
UCHAR LocalAddress[0];
|
|
TDI_ADDRESS_IP LocalAddrIn;
|
|
TDI_ADDRESS_IP6 LocalAddrIn6;
|
|
};
|
|
|
|
//
|
|
// Structure to get LocalAddress when Accept completes
|
|
//
|
|
|
|
TDI_CONNECTION_INFORMATION TdiConnectionInformation;
|
|
UL_TRANSPORT_ADDRESS Ta;
|
|
|
|
//
|
|
// The Inteface & Link IDs as reported by TCP. These are filled
|
|
// only on demand.
|
|
//
|
|
ULONG InterfaceId;
|
|
ULONG LinkId;
|
|
BOOLEAN bRoutingLookupDone;
|
|
|
|
//
|
|
//
|
|
// On the endpoint's idle, active, or retiring connections list
|
|
//
|
|
|
|
CONN_LIST_STATE ConnListState;
|
|
|
|
//
|
|
// The TDI connection object.
|
|
//
|
|
|
|
UX_TDI_OBJECT ConnectionObject;
|
|
|
|
//
|
|
// User context.
|
|
//
|
|
|
|
PVOID pConnectionContext;
|
|
|
|
//
|
|
// The endpoint associated with this connection. Note that this
|
|
// ALWAYS points to a valid endpoint. For idle connections, it's
|
|
// a weak (non referenced) pointer. For active connections, it's
|
|
// a strong (referenced) pointer.
|
|
//
|
|
|
|
PUL_ENDPOINT pOwningEndpoint;
|
|
|
|
//
|
|
// TDI wrapper & list managment object associated with the
|
|
// pOwningEndpoint.
|
|
//
|
|
|
|
PUL_ADDR_IDLE_LIST pOwningAddrIdleList;
|
|
|
|
//
|
|
// The processor where this connection was allocated from
|
|
// the idle list
|
|
//
|
|
|
|
ULONG OriginProcessor;
|
|
|
|
//
|
|
// Thread work item for deferred actions.
|
|
//
|
|
|
|
UL_WORK_ITEM WorkItem;
|
|
|
|
//
|
|
// Data captured from the listening endpoint at the time the
|
|
// connection is created. This is captured to reduce references
|
|
// to the listening endpoint.
|
|
//
|
|
|
|
PUL_CONNECTION_DESTROYED pConnectionDestroyedHandler;
|
|
PVOID pListeningContext;
|
|
|
|
//
|
|
// Pre-allocated IrpContext for disconnect.
|
|
//
|
|
|
|
UL_IRP_CONTEXT IrpContext;
|
|
|
|
//
|
|
// HTTP connection.
|
|
//
|
|
|
|
UL_HTTP_CONNECTION HttpConnection;
|
|
|
|
//
|
|
// Filter related info.
|
|
//
|
|
|
|
UX_FILTER_CONNECTION FilterInfo;
|
|
|
|
//
|
|
// We've had too many problems with orphaned UL_CONNECTIONs.
|
|
// Let's make it easy to find them all in the debugger.
|
|
//
|
|
|
|
LIST_ENTRY GlobalConnectionListEntry;
|
|
|
|
//
|
|
// Link to the short-lived retiring list in
|
|
// UlpDisconnectAllActiveConnections.
|
|
//
|
|
|
|
LIST_ENTRY RetiringListEntry;
|
|
|
|
#if REFERENCE_DEBUG
|
|
//
|
|
// Private Reference trace log.
|
|
//
|
|
|
|
PTRACE_LOG pTraceLog;
|
|
PTRACE_LOG pHttpTraceLog;
|
|
#endif // REFERENCE_DEBUG
|
|
|
|
} UL_CONNECTION, *PUL_CONNECTION;
|
|
|
|
#define UL_CONNECTION_SIGNATURE MAKE_SIGNATURE('CONN')
|
|
#define UL_CONNECTION_SIGNATURE_X MAKE_FREE_SIGNATURE(UL_CONNECTION_SIGNATURE)
|
|
|
|
#define IS_VALID_CONNECTION(pConnection) \
|
|
HAS_VALID_SIGNATURE(pConnection, UL_CONNECTION_SIGNATURE)
|
|
|
|
|
|
//
|
|
// A buffer, containing a precreated receive IRP, a precreated MDL, and
|
|
// sufficient space for a partial MDL. These buffers are typically used
|
|
// when passing a receive IRP back to the transport from within our receive
|
|
// indication handler.
|
|
//
|
|
// The buffer structure, IRP, MDLs, and data area are all allocated in a
|
|
// single pool block. The layout of the block is:
|
|
//
|
|
// +-------------------+
|
|
// | |
|
|
// | UL_RECEIVE_BUFFER |
|
|
// | |
|
|
// +-------------------+
|
|
// | |
|
|
// | IRP |
|
|
// | |
|
|
// +-------------------+
|
|
// | |
|
|
// | MDL |
|
|
// | |
|
|
// +-------------------+
|
|
// | |
|
|
// | Partial MDL |
|
|
// | |
|
|
// +-------------------+
|
|
// | |
|
|
// | Data Area |
|
|
// | |
|
|
// +-------------------+
|
|
//
|
|
// WARNING! All fields of this structure must be explicitly initialized.
|
|
//
|
|
|
|
typedef struct _UL_RECEIVE_BUFFER
|
|
{
|
|
//
|
|
// This MUST be the first field in the structure. This is the linkage
|
|
// used by the lookaside package for storing entries in the lookaside
|
|
// list.
|
|
//
|
|
|
|
SLIST_ENTRY LookasideEntry;
|
|
|
|
//
|
|
// Structure signature: UL_RECEIVE_BUFFER_SIGNATURE
|
|
//
|
|
|
|
ULONG Signature;
|
|
|
|
//
|
|
// Amount of unread data in the data area.
|
|
//
|
|
|
|
ULONG UnreadDataLength;
|
|
|
|
//
|
|
// The pre-built receive IRP.
|
|
//
|
|
|
|
PIRP pIrp;
|
|
|
|
//
|
|
// The pre-built MDL describing the entire data area.
|
|
//
|
|
|
|
PMDL pMdl;
|
|
|
|
//
|
|
// A secondary MDL describing part of the data area.
|
|
//
|
|
|
|
PMDL pPartialMdl;
|
|
|
|
//
|
|
// Pointer to the data area for this buffer.
|
|
//
|
|
|
|
PVOID pDataArea;
|
|
|
|
//
|
|
// Pointer to the connection referencing this buffer.
|
|
//
|
|
|
|
PVOID pConnectionContext;
|
|
|
|
} UL_RECEIVE_BUFFER;
|
|
|
|
#define UL_RECEIVE_BUFFER_SIGNATURE MAKE_SIGNATURE('RBUF')
|
|
#define UL_RECEIVE_BUFFER_SIGNATURE_X MAKE_FREE_SIGNATURE(UL_RECEIVE_BUFFER_SIGNATURE)
|
|
|
|
#define IS_VALID_RECEIVE_BUFFER(pBuffer) \
|
|
HAS_VALID_SIGNATURE(pBuffer, UL_RECEIVE_BUFFER_SIGNATURE)
|
|
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
VOID
|
|
UlpDestroyEndpoint(
|
|
IN PUL_ENDPOINT pEndpoint
|
|
);
|
|
|
|
VOID
|
|
UlpDestroyConnectionWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
);
|
|
|
|
VOID
|
|
UlpDestroyConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
PUL_CONNECTION
|
|
UlpDequeueIdleConnection(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
);
|
|
|
|
PUL_CONNECTION
|
|
UlpDequeueIdleConnectionToDrain(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
);
|
|
|
|
VOID
|
|
UlpEnqueueActiveConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
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
|
|
UlpDisconnectHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN LONG DisconnectDataLength,
|
|
IN PVOID pDisconnectData,
|
|
IN LONG DisconnectInformationLength,
|
|
IN PVOID pDisconnectInformation,
|
|
IN ULONG DisconnectFlags
|
|
);
|
|
|
|
VOID
|
|
UlpDoDisconnectNotification(
|
|
IN PVOID pConnectionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpCloseRawConnection(
|
|
IN PVOID pConnectionContext,
|
|
IN BOOLEAN AbortiveDisconnect,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpSendRawData(
|
|
IN PVOID pConnectionContext,
|
|
IN PMDL pMdlChain,
|
|
IN ULONG Length,
|
|
IN PUL_IRP_CONTEXT pIrpContext,
|
|
IN BOOLEAN InitiateDisconnect
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpReceiveRawData(
|
|
IN PVOID pConnectionContext,
|
|
IN PVOID pBuffer,
|
|
IN ULONG BufferLength,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpDummyReceiveHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN PVOID ConnectionContext,
|
|
IN PVOID pTsdu,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesUnreceived,
|
|
OUT ULONG *pBytesTaken
|
|
);
|
|
|
|
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
|
|
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 *pIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartAccept(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartSendData(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
VOID
|
|
UlpReferenceEndpoint(
|
|
IN PUL_ENDPOINT pEndpoint,
|
|
IN REFTRACE_ACTION Action
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
);
|
|
|
|
VOID
|
|
UlpDereferenceEndpoint(
|
|
IN PUL_ENDPOINT pEndpoint,
|
|
IN PUL_CONNECTION pConnToEnqueue,
|
|
IN REFTRACE_ACTION Action
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
);
|
|
|
|
#define REFERENCE_ENDPOINT(endp, action) \
|
|
UlpReferenceEndpoint( \
|
|
(endp), \
|
|
(action) \
|
|
REFERENCE_DEBUG_ACTUAL_PARAMS \
|
|
)
|
|
|
|
#define DEREFERENCE_ENDPOINT_SELF(endp, action) \
|
|
UlpDereferenceEndpoint( \
|
|
(endp), \
|
|
NULL, \
|
|
(action) \
|
|
REFERENCE_DEBUG_ACTUAL_PARAMS \
|
|
)
|
|
|
|
#define DEREFERENCE_ENDPOINT_CONNECTION(endp, conn, action) \
|
|
UlpDereferenceEndpoint( \
|
|
(endp), \
|
|
(conn), \
|
|
(action) \
|
|
REFERENCE_DEBUG_ACTUAL_PARAMS \
|
|
)
|
|
|
|
VOID
|
|
UlpEndpointCleanupWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
);
|
|
|
|
VOID
|
|
UlpCleanupConnectionId(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
VOID
|
|
UlpConnectionCleanupWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpAssociateConnection(
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpDisassociateConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpInitializeAddrIdleList(
|
|
IN PUL_ENDPOINT pEndpoint,
|
|
IN USHORT Port,
|
|
IN PUL_TRANSPORT_ADDRESS pTa,
|
|
IN OUT PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
);
|
|
|
|
VOID
|
|
UlpCleanupAddrIdleList(
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpReplenishAddrIdleList(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
IN BOOLEAN PopulateAll
|
|
);
|
|
|
|
VOID
|
|
UlpReplenishAddrIdleListWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
);
|
|
|
|
VOID
|
|
UlpTrimAddrIdleListWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpCreateConnection(
|
|
IN PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
OUT PUL_CONNECTION *ppConnection
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpInitializeConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
__inline
|
|
VOID
|
|
UlpSetConnectionFlag(
|
|
IN OUT PUL_CONNECTION pConnection,
|
|
IN ULONG NewFlag
|
|
)
|
|
{
|
|
UL_CONNECTION_FLAGS oldFlags;
|
|
UL_CONNECTION_FLAGS newFlags;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( IS_VALID_CONNECTION( pConnection ) );
|
|
|
|
for (;;)
|
|
{
|
|
//
|
|
// Capture the current value and initialize the new value.
|
|
//
|
|
|
|
newFlags.Value = oldFlags.Value =
|
|
*((volatile LONG *) &pConnection->ConnectionFlags.Value);
|
|
|
|
newFlags.Value |= NewFlag;
|
|
|
|
if (InterlockedCompareExchange(
|
|
(PLONG) &pConnection->ConnectionFlags.Value,
|
|
(LONG) newFlags.Value,
|
|
(LONG) oldFlags.Value
|
|
) == (LONG) oldFlags.Value)
|
|
{
|
|
break;
|
|
}
|
|
|
|
PAUSE_PROCESSOR;
|
|
|
|
}
|
|
|
|
} // UlpSetConnectionFlag
|
|
|
|
NTSTATUS
|
|
UlpBeginDisconnect(
|
|
IN PIRP pIrp,
|
|
IN PUL_IRP_CONTEXT pIrpContext,
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartDisconnect(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpBeginAbort(
|
|
IN PUL_CONNECTION pConnection,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartAbort(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
VOID
|
|
UlpRemoveFinalReference(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartReceive(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartClientReceive(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpDisconnectAllActiveConnections(
|
|
IN PUL_ENDPOINT pEndpoint
|
|
);
|
|
|
|
VOID
|
|
UlpUnbindConnectionFromEndpoint(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
VOID
|
|
UlpSynchronousIoComplete(
|
|
IN PVOID pCompletionContext,
|
|
IN NTSTATUS Status,
|
|
IN ULONG_PTR Information
|
|
);
|
|
|
|
PUL_ENDPOINT
|
|
UlpFindEndpointForPort(
|
|
IN USHORT Port
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpOptimizeForInterruptModeration(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN BOOLEAN Flag
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpSetNagling(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN BOOLEAN Flag
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpRestartQueryAddress(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
VOID
|
|
UlpCleanupEarlyConnection(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpQueryTcpFastSend(
|
|
PWSTR DeviceName,
|
|
OUT PUL_TCPSEND_DISPATCH* pDispatchRoutine
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpBuildTdiReceiveBuffer(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN PUL_CONNECTION pConnection,
|
|
OUT PIRP *pIrp
|
|
);
|
|
|
|
BOOLEAN
|
|
UlpConnectionIsOnValidList(
|
|
IN PUL_CONNECTION pConnection
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpPopulateIdleList(
|
|
IN OUT PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
IN ULONG Proc
|
|
);
|
|
|
|
VOID
|
|
UlpTrimAddrIdleList(
|
|
IN OUT PUL_ADDR_IDLE_LIST pAddrIdleList,
|
|
OUT PLIST_ENTRY pZombieList
|
|
);
|
|
|
|
VOID
|
|
UlpIdleListTrimTimerWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
);
|
|
|
|
VOID
|
|
UlpIdleListTrimTimerDpcRoutine(
|
|
PKDPC Dpc,
|
|
PVOID DeferredContext,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2
|
|
);
|
|
|
|
BOOLEAN
|
|
UlpIsUrlRouteableInListenScope(
|
|
IN PHTTP_PARSED_URL pParsedUrl
|
|
);
|
|
|
|
#if DBG
|
|
|
|
#define SHOW_LIST_INFO(Caller,Info,List,Proc) \
|
|
UlTrace(TDI_STATS, \
|
|
("%s: %s List %p Endp %p Proc %d " \
|
|
"Delta %6d Conn Served P/C %5d:%5d PD/BD BLC %d [%5d]:[%5d]\n", \
|
|
##Caller, \
|
|
Info, \
|
|
List, \
|
|
List->pOwningEndpoint, \
|
|
Proc, \
|
|
PpslQueryDelta( \
|
|
List->IdleConnectionSListsHandle, \
|
|
Proc \
|
|
), \
|
|
PpslQueryPrevServed( \
|
|
List->IdleConnectionSListsHandle, \
|
|
Proc \
|
|
), \
|
|
PpslQueryServed( \
|
|
List->IdleConnectionSListsHandle, \
|
|
Proc \
|
|
), \
|
|
PpslQueryTotalServed( \
|
|
List->IdleConnectionSListsHandle \
|
|
), \
|
|
PpslQueryDepth( \
|
|
List->IdleConnectionSListsHandle, \
|
|
Proc \
|
|
), \
|
|
PpslQueryBackingListDepth( \
|
|
List->IdleConnectionSListsHandle \
|
|
) \
|
|
))
|
|
|
|
__inline
|
|
VOID
|
|
UlpTraceIdleConnections(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG Proc;
|
|
ULONG Index;
|
|
PLIST_ENTRY pLink;
|
|
PUL_ENDPOINT pEndpoint;
|
|
PUL_ADDR_IDLE_LIST pAddrIdleList;
|
|
|
|
for (pLink = g_TdiEndpointListHead.Flink;
|
|
pLink != &g_TdiEndpointListHead;
|
|
pLink = pLink->Flink
|
|
)
|
|
{
|
|
pEndpoint = CONTAINING_RECORD(
|
|
pLink,
|
|
UL_ENDPOINT,
|
|
GlobalEndpointListEntry
|
|
);
|
|
|
|
ASSERT(IS_VALID_ENDPOINT(pEndpoint));
|
|
|
|
UlTrace(TDI_STATS,("ENDPOINT: %p AFTER TRIM\n",pEndpoint));
|
|
|
|
for (Index = 0; Index < pEndpoint->AddrIdleListCount; Index++)
|
|
{
|
|
pAddrIdleList = &pEndpoint->aAddrIdleLists[Index];
|
|
|
|
for (Proc = 0; Proc <= g_UlNumberOfProcessors; Proc++)
|
|
{
|
|
UlTrace(TDI_STATS,
|
|
("\tList %p Proc %d Delta %6d P/C [%5d]/[%5d] BLC %d Depth [%5d]\n",
|
|
pAddrIdleList,
|
|
Proc,
|
|
PpslQueryDelta(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
),
|
|
PpslQueryPrevServed(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
),
|
|
PpslQueryServed(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
),
|
|
PpslQueryTotalServed(
|
|
pAddrIdleList->IdleConnectionSListsHandle
|
|
),
|
|
PpslQueryDepth(
|
|
pAddrIdleList->IdleConnectionSListsHandle,
|
|
Proc
|
|
)
|
|
));
|
|
}
|
|
UlTrace(TDI_STATS,("\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
#define TRACE_IDLE_CONNECTIONS() \
|
|
IF_DEBUG(TDI_STATS) \
|
|
{ \
|
|
UlpTraceIdleConnections(); \
|
|
}
|
|
|
|
__inline
|
|
ULONG
|
|
UlpZombieListDepth(
|
|
IN PLIST_ENTRY pList
|
|
)
|
|
{
|
|
PLIST_ENTRY pLink = pList;
|
|
ULONG Depth = 0;
|
|
|
|
while (pLink->Flink != pList)
|
|
{
|
|
Depth++;
|
|
pLink = pLink->Flink;
|
|
}
|
|
|
|
return Depth;
|
|
}
|
|
|
|
#else
|
|
|
|
#define SHOW_LIST_INFO(Caller,Info,List,Proc)
|
|
|
|
#define TRACE_IDLE_CONNECTIONS()
|
|
|
|
#endif // DBG
|
|
|
|
|
|
#endif // _ULTDIP_H_
|