/*++ Copyright (c) 2000-2002 Microsoft Corporation Module Name: clientconn.h Abstract: This file contains the header defintions for the HTTP.SYS client connection structures Author: Henry Sanders (henrysa) 14-Aug-2000 Revision History: --*/ #ifndef _CLIENTCONN_H_ #define _CLIENTCONN_H_ // // Forward references. // // // Private constants. // #define CLIENT_CONN_TDI_LIST_MAX 30 // // Private types. // // // Private prototypes. // // // Public constants // // // Public types // // // 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 indiction 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 enum _UC_CONNECTION_STATE { /* 0 */ UcConnectStateConnectCleanup, /* 1 */ UcConnectStateConnectCleanupBegin, /* 2 */ UcConnectStateConnectIdle, /* 3 */ UcConnectStateConnectPending, /* 4 */ UcConnectStateIssueFilterClose, // we send a FIN /* 5 */ UcConnectStateIssueFilterDisconnect, // we recv a FIN /* 6 */ UcConnectStateConnectComplete, /* 7 */ UcConnectStateProxySslConnect, /* 8 */ UcConnectStateProxySslConnectComplete, /* 9 */ UcConnectStatePerformingSslHandshake, /* a */ UcConnectStateConnectReady, /* b */ UcConnectStateDisconnectIndicatedPending, /* c */ UcConnectStateDisconnectPending, /* d */ UcConnectStateDisconnectComplete, /* e */ UcConnectStateAbortPending } UC_CONNECTION_STATE; typedef enum _UC_CONNECTION_WORKER_TYPE { UcConnectionPassive, UcConnectionWorkItem } UC_CONNECTION_WORKER_TYPE, *PUC_CONNECTION_WORKER_TYPE; // // The states of SSL state machine // // Informal description: // // NoSslState - Every connection is initialized to this state. // // ConnectionDelivered - Ssl connection was delivered to the filter by // completing its accept irp // // ServerCertReceived - Certificate was attached to this connection // // ValidatingServerCert - Waiting for app's approval of the certificate // // HandshakeComplete - OK to send request on this connection // typedef enum _UC_SSL_CONNECTION_STATE { UcSslStateNoSslState, UcSslStateConnectionDelivered, UcSslStateServerCertReceived, UcSslStateValidatingServerCert, UcSslStateHandshakeComplete, UcSslStateConnMaximum } UC_SSL_CONNECTION_STATE; // // This wraps the TDI address object & Connection objects. // typedef struct _UC_TDI_OBJECTS { LIST_ENTRY Linkage; UX_TDI_OBJECT ConnectionObject; UX_TDI_OBJECT AddressObject; TDI_CONNECTION_INFORMATION TdiInfo; USHORT ConnectionType; // either TDI_ADDRESS_TYPE_IP or // TDI_ADDRESS_TYPE_IP6 PIRP pIrp; UL_IRP_CONTEXT IrpContext; PUC_CLIENT_CONNECTION pConnection; } UC_TDI_OBJECTS, *PUC_TDI_OBJECTS; // // The structure that represents a TCP connection to us. This // is a wrapper for the UX_TDI_OBJECT plus some associated state. // typedef struct _UC_CLIENT_CONNECTION { ULONG Signature; // Structure signature UL_SPIN_LOCK SpinLock; ULONG ConnectionIndex; // What is the index of this // connection on servinfo LIST_ENTRY PendingRequestList; // List of unsent requests. LIST_ENTRY SentRequestList; // List of sent but // uncompleted requests LIST_ENTRY ProcessedRequestList; // List of requests // for which we have // completly processed // the response. // // A back pointer to the server information structure on which this // connection is linked. We don't explictly reference the server // information using REFERENCE_SERVER_INFORMATION. This is because // the server information is implictly referenced by requests, in the // following fashion. The server information structure is explictly // referenced by the file object, and the file object won't go away and // dereference the server information until we complete a cleanup IRP. We // won't complete the cleanup IRP until all outstanding requests on the // file object have been completed. Therefore the server information // pointer in this structure is guaranteed to be valid *only as long as // there are pending requests queued on this structure*. // PUC_PROCESS_SERVER_INFORMATION pServerInfo; LONG RefCount; UC_CONNECTION_STATE ConnectionState; UC_SSL_CONNECTION_STATE SslState; NTSTATUS ConnectionStatus; ULONG Flags; struct { PUCHAR pBuffer; ULONG BytesWritten; ULONG BytesAvailable; ULONG BytesAllocated; } MergeIndication; #if REFERENCE_DEBUG // // Private Reference trace log. // PTRACE_LOG pTraceLog; #endif // REFERENCE_DEBUG PUC_TDI_OBJECTS pTdiObjects; // // TDI wants us to pass a TRANSPORT_ADDRESS structure. Make sure that // we have a structure that can hold a IP4 or IP6 address. // union { TA_IP_ADDRESS V4Address; TA_IP6_ADDRESS V6Address; TRANSPORT_ADDRESS GenericTransportAddress; } RemoteAddress; // // Thread work item for deferred actions. // BOOLEAN bWorkItemQueued; UL_WORK_ITEM WorkItem; // // Pointer to a event that will get set when the client // ref drops to 0. // PKEVENT pEvent; UX_FILTER_CONNECTION FilterInfo; HTTP_SSL_SERVER_CERT_INFO ServerCertInfo; LONG NextAddressCount; PTA_ADDRESS pNextAddress; } UC_CLIENT_CONNECTION, *PUC_CLIENT_CONNECTION; #define UC_CLIENT_CONNECTION_SIGNATURE MAKE_SIGNATURE('HCON') #define UC_CLIENT_CONNECTION_SIGNATURE_X MAKE_FREE_SIGNATURE(\ UC_CLIENT_CONNECTION_SIGNATURE) #define DEFAULT_REMOTE_ADDR_SIZE MAX(TDI_ADDRESS_LENGTH_IP, \ TDI_ADDRESS_LENGTH_IP6) #define UC_IS_VALID_CLIENT_CONNECTION(pConnection) \ HAS_VALID_SIGNATURE(pConnection, UC_CLIENT_CONNECTION_SIGNATURE) #define REFERENCE_CLIENT_CONNECTION(s) \ UcReferenceClientConnection( \ (s) \ REFERENCE_DEBUG_ACTUAL_PARAMS \ ) #define DEREFERENCE_CLIENT_CONNECTION(s) \ UcDereferenceClientConnection( \ (s) \ REFERENCE_DEBUG_ACTUAL_PARAMS \ ) #define UC_CLOSE_CONNECTION(pConn, Abortive, Status) \ do \ { \ UC_WRITE_TRACE_LOG( \ g_pUcTraceLog, \ UC_ACTION_CONNECTION_CLOSE, \ (pConn), \ UlongToPtr(Abortive), \ UlongToPtr((pConn)->ConnectionState), \ UlongToPtr((pConn)->Flags) \ ); \ \ UcCloseConnection((pConn), (Abortive), NULL, NULL, Status); \ } while(FALSE, FALSE) #define CLIENT_CONN_FLAG_SEND_BUSY 0x00000002 #define CLIENT_CONN_FLAG_FILTER_CLEANUP 0x00000004 #define CLIENT_CONN_FLAG_TDI_ALLOCATE 0x00000008 #define CLIENT_CONN_FLAG_CONNECT_READY 0x00000010 #define CLIENT_CONN_FLAG_ABORT_RECEIVED 0x00000020 #define CLIENT_CONN_FLAG_ABORT_PENDING 0x00000040 #define CLIENT_CONN_FLAG_DISCONNECT_RECEIVED 0x00000080 #define CLIENT_CONN_FLAG_PROXY_SSL_CONNECTION 0x00000100 #define CLIENT_CONN_FLAG_DISCONNECT_COMPLETE 0x00000200 #define CLIENT_CONN_FLAG_ABORT_COMPLETE 0x00000400 #define CLIENT_CONN_FLAG_CONNECT_COMPLETE 0x00000800 #define CLIENT_CONN_FLAG_CLEANUP_PENDED 0x00001000 #define CLIENT_CONN_FLAG_FILTER_CLOSED 0x00002000 #define CLIENT_CONN_FLAG_RECV_BUSY 0x00008000 // // Private prototypes. // NTSTATUS UcpOpenTdiObjects( IN PUC_TDI_OBJECTS pTdiObjects ); NTSTATUS UcpAllocateTdiObject( OUT PUC_TDI_OBJECTS *ppTdiObjects, IN USHORT AddressType ); VOID UcpFreeTdiObject( IN PUC_TDI_OBJECTS pTdiObjects ); PUC_TDI_OBJECTS UcpPopTdiObject( IN USHORT AddressType ); VOID UcpPushTdiObject( IN PUC_TDI_OBJECTS pTdiObjects, IN USHORT AddressType ); NTSTATUS UcpCleanupConnection( IN PUC_CLIENT_CONNECTION pConnection, IN KIRQL OldIrql, IN BOOLEAN Final ); VOID UcpCancelPendingRequest( PDEVICE_OBJECT pDeviceObject, PIRP Irp ); VOID UcpCancelConnectingRequest( PDEVICE_OBJECT pDeviceObject, PIRP Irp ); NTSTATUS UcpInitializeConnection( IN PUC_CLIENT_CONNECTION pConnection, IN PUC_PROCESS_SERVER_INFORMATION pInfo ); NTSTATUS UcpAssociateClientConnection( IN PUC_CLIENT_CONNECTION pUcConnection ); VOID UcpTerminateClientConnectionsHelper( IN USHORT AddressType ); VOID UcpRestartEntityMdlSend( IN PVOID pCompletionContext, IN NTSTATUS Status, IN ULONG_PTR Information ); VOID UcCancelConnectingRequest( PDEVICE_OBJECT pDeviceObject, PIRP Irp ); VOID UcpConnectionStateMachineWorker( IN PUL_WORK_ITEM pWorkItem ); BOOLEAN UcpCompareServerCert( IN PUC_CLIENT_CONNECTION pConnection ); PUC_HTTP_REQUEST UcpFindRequestToFail( IN PUC_CLIENT_CONNECTION pConnection ); // // Public prototypes // NTSTATUS UcInitializeClientConnections( VOID ); VOID UcTerminateClientConnections( VOID ); NTSTATUS UcOpenClientConnection( IN PUC_PROCESS_SERVER_INFORMATION pServInfo, OUT PUC_CLIENT_CONNECTION *pUcConnection ); VOID UcReferenceClientConnection( PVOID pObject REFERENCE_DEBUG_FORMAL_PARAMS ); VOID UcDereferenceClientConnection( PVOID pObject REFERENCE_DEBUG_FORMAL_PARAMS ); NTSTATUS UcSendRequestOnConnection( PUC_CLIENT_CONNECTION pConnection, PUC_HTTP_REQUEST pRequest, KIRQL OldIrql ); VOID UcIssueRequests( PUC_CLIENT_CONNECTION pConnection, KIRQL OldIrql ); BOOLEAN UcIssueEntities( PUC_HTTP_REQUEST pRequest, PUC_CLIENT_CONNECTION pConnection, PKIRQL OldIrql ); VOID UcCleanupConnection( IN PUC_CLIENT_CONNECTION pConnection, IN NTSTATUS Status ); NTSTATUS UcSendEntityBody( IN PUC_HTTP_REQUEST pRequest, IN PUC_HTTP_SEND_ENTITY_BODY pEntity, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp, OUT PBOOLEAN bDontFail, IN BOOLEAN bLast ); VOID UcKickOffConnectionStateMachine( IN PUC_CLIENT_CONNECTION pConnection, IN KIRQL OldIrql, IN UC_CONNECTION_WORKER_TYPE WorkerType ); ULONG UcGenerateHttpRawConnectionInfo( IN PVOID pContext, IN PUCHAR pKernelBuffer, IN PVOID pUserBuffer, IN ULONG OutLength, IN PUCHAR pBuffer, IN ULONG InitialLength ); ULONG UcComputeHttpRawConnectionLength( IN PVOID pConnectionContext ); VOID UcServerCertificateInstalled( IN PVOID pConnectionContext, IN NTSTATUS Status ); VOID UcConnectionStateMachine( IN PUC_CLIENT_CONNECTION pConnection, IN KIRQL OldIrql ); VOID UcRestartClientConnect( IN PUC_CLIENT_CONNECTION pConnection, IN NTSTATUS Status ); VOID UcRestartMdlSend( IN PVOID pCompletionContext, IN NTSTATUS Status, IN ULONG_PTR Information ); VOID UcCancelSentRequest( PDEVICE_OBJECT pDeviceObject, PIRP Irp ); VOID UcClearConnectionBusyFlag( IN PUC_CLIENT_CONNECTION pConnection, IN ULONG Flag, IN KIRQL OldIrql, IN BOOLEAN bCloseConnection ); BOOLEAN UcpCheckForPipelining( IN PUC_CLIENT_CONNECTION pConnection ); NTSTATUS UcAddServerCertInfoToConnection( IN PUX_FILTER_CONNECTION pConnection, IN PHTTP_SSL_SERVER_CERT_INFO pServerCert ); #endif