/**********************************************************************/
/**                       Microsoft Windows NT                       **/
/**                Copyright(c) Microsoft Corp., 1994                **/
/**********************************************************************/

/*
    conn.hxx

    This module contains the connection class


    FILE HISTORY:
        Johnl       15-Aug-1994 Created

*/

#ifndef _CONN_HXX_
#define _CONN_HXX_

# include "w3inst.hxx"
# include "rdns.hxx"
# include <reftrace.h>

//
//  Determines if client connection reference count tracking is enabled
//
#if DBG && !defined(_WIN64)
#define CC_REF_TRACKING         1
#else
#define CC_REF_TRACKING         0
#endif

//
//  The default timeout to wait for the completion of an Atq IO request (in sec)
//
#define W3_IO_TIMEOUT           (5*60)

//
//  The initial size of the buffer to receive the client request
//
#define W3_DEFAULT_BUFFSIZE     4096

//
//  The end of a line is considered to be the linefeed character
//
#define W3_EOL                  0x0A

//
//  Valid signature of a CLIENT_CONN_STATE
//
#define CLIENT_CONN_SIGNATURE       ((DWORD) ' SCC')

//
//  Invalid signature of a CLIENT_CONN_STATE
//
#define CLIENT_CONN_SIGNATURE_FREE  ((DWORD) 'xssc')

//
//  The various states a CLIENT_CONN object can be in
//
enum CLIENT_CONN_STATE
{
    //
    //  We've just accepted the TCP connection from the client but we have
    //  no other information.
    //
    CCS_STARTUP = 0,

    //
    //  We are in the process of receiving the client's HTTP request buffer
    //
    CCS_GETTING_CLIENT_REQ,

    //
    //  We are reading data from the client socket meant for a gateway
    //
    CCS_GATHERING_GATEWAY_DATA,

    //
    //  We're executing the clients HTTP request
    //
    CCS_PROCESSING_CLIENT_REQ,

    //
    //  The server or client has initiated a disconnect.  We have to
    //  wait till all outstanding IO requests are completed before
    //  cleaning up
    //
    CCS_DISCONNECTING,

    //
    //  All pending requests have been completed.  Finish cleaning
    //  up.
    //
    CCS_SHUTDOWN
};

//
// parameter block used to initialize connections
//

typedef struct _CLIENT_CONN_PARAMS
{
    SOCKET                  sClient;
    PSOCKADDR               pAddrLocal;
    PSOCKADDR               pAddrRemote;
    PATQ_CONTEXT            pAtqContext;
    PVOID                   pEndpointObject;
    PIIS_ENDPOINT           pEndpoint;
    PVOID                   pvInitialBuff;
    DWORD                   cbInitialBuff;

} CLIENT_CONN_PARAMS, *PCLIENT_CONN_PARAMS;

class CLIENT_CONN
{
public:

    BOOL IsValid( VOID )
        { return _fIsValid; }

    //
    //  This is the work entry point that is driven by the completion of the
    //  async IO.
    //

    BOOL DoWork( DWORD        BytesWritten,
                 DWORD        CompletionStatus,
                 BOOL         fIOCompletion);

#if CC_REF_TRACKING

    //
    // ATQ notification trace
    //
    // Notification of ATQ completion, for debugging purpose only.
    //


    VOID NotifyAtqProcessContext( DWORD BytesWritten,
                                  DWORD CompletionStatus,
                                  DWORD dwSig );

#endif

    //
    //  Optionally sends a status response then initiates the disconnect
    //
    //  HTResponse - HTTP status code
    //  ErrorResponse - System error or resource ID of response
    //
    //  If this function fails, then the connection will be aborted
    //

    dllexp
    VOID Disconnect( HTTP_REQ_BASE * pRequest = NULL,
                     DWORD           HTResponse    = 0,
                     DWORD           ErrorResponse = NO_ERROR,
                     BOOL            fDoShutdown   = TRUE,
                     LPBOOL          pfFinished =NULL );

    dllexp
    BOOL OnSessionStartup( BOOL  *pfDoAgain,
                           PVOID pvInitial = NULL,
                           DWORD cbInitial = 0,
                           BOOL  fFirst = FALSE);

    //
    //  Walks the connection list and calls disconnect on each connection
    //

    static VOID DisconnectAllUsers( PIIS_SERVER_INSTANCE Instance = NULL );

    //
    //  Increments and decrements the reference count
    //

    UINT Reference( VOID );

    UINT Dereference( VOID );

    UINT QueryRefCount( VOID ) const
        { return _cRef; }

    PIIS_ENDPOINT QueryW3Endpoint( VOID ) const
        { return m_pW3Endpoint; }

    PW3_SERVER_INSTANCE QueryW3Instance( VOID ) const
        { return m_pInstance; }

    BOOL IsW3Instance( VOID ) const
        { return m_pInstance != NULL; }

    VOID SetW3Instance( IN PW3_SERVER_INSTANCE pInstance )
        { DBG_ASSERT(m_pInstance == NULL); m_pInstance = pInstance; }

    W3_SERVER_STATISTICS * QueryW3StatsObj( VOID ) const
        { return m_pW3Stats; }

    VOID SetW3StatsObj( IN LPW3_SERVER_STATISTICS pW3Stats )
        { m_pW3Stats = pW3Stats; }

    DWORD QueryLocalIPAddress( VOID ) const
        { return m_localIpAddress; }

    DWORD QueryRemoteIPAddress( VOID ) const
        { return m_remoteIpAddress; }

    BOOL RequestAbortiveClose( VOID );
    
    BOOL CloseConnection( VOID );

    //
    //  Simple wrappers to the corresponding Atq functions
    //

    BOOL ReadFile( LPVOID       lpBuffer,
                   DWORD        nBytesToRead );
    BOOL WriteFile( LPVOID       lpBuffer,
                    DWORD        nBytesToRead );
    BOOL SyncWsaSend( WSABUF *    rgWsaBuffers,
                      DWORD       cWsaBuffers,
                      LPDWORD     pcbWritten );
    BOOL TransmitFile( HANDLE       hFile,
                       DWORD        Offset,
                       DWORD        BytesToWrite,
                       DWORD        dwFlags,
                       PVOID        pHead      = NULL,
                       DWORD        HeadLength = 0,
                       PVOID        pTail      = NULL,
                       DWORD        TailLength = 0 );
    BOOL TransmitFileAndRecv( HANDLE       hFile,
                              DWORD        Offset,
                              DWORD        BytesToWrite,
                              DWORD        dwFlags,
                              PVOID        pHead = NULL,
                              DWORD        HeadLength = 0,
                              PVOID        pTail = NULL,
                              DWORD        TailLength = 0,
                              LPVOID       lpBuffer = NULL,
                              DWORD        BytesToRead = 0);
    BOOL WriteFileAndRecv( LPVOID       lpSendBuffer,
                           DWORD        BytesToWrite,
                           LPVOID       lpRecvBuffer = NULL,
                           DWORD        BytesToRead = 0);


    BOOL PostCompletionStatus( DWORD        BytesTransferred );

    //
    //  This list entry is put on the client connection list
    //

    LIST_ENTRY ListEntry;

    SOCKET QuerySocket( VOID ) const
        { return _sClient; }

    PATQ_CONTEXT QueryAtqContext( VOID ) const
        { return _AtqContext; }

    USHORT QueryPort( VOID ) const
        { return _sPort; }

    USHORT QueryRemotePort( VOID ) const
        { return _sRemotePort; }

    BOOL IsSecurePort( VOID ) const
        { return _fSecurePort; }

    //
    //  Make sure the next state is set before an async IO call is made
    //

    enum CLIENT_CONN_STATE QueryState( VOID ) const
        { return _ccState; }

    VOID SetState( CLIENT_CONN_STATE  ccState )
        { _ccState = ccState; }

    BOOL CheckSignature( VOID ) const
        { return _Signature == CLIENT_CONN_SIGNATURE; }

    TCHAR * QueryRemoteAddr( VOID ) const
        { return (TCHAR *) _achRemoteAddr; }

    TCHAR * QueryLocalAddr( VOID ) const
        { return (TCHAR *) _achLocalAddr; }

    VOID SetAtqReuseContextFlag( BOOL fReuseContext )
        { _fReuseContext = fReuseContext; }
        
    static DWORD QueryFreeListSize( VOID )
        { return _cFree; }

    static DWORD Initialize( VOID );
    static VOID  Terminate( VOID );
    static CLIENT_CONN * Alloc( PCLIENT_CONN_PARAMS );
    dllexp static VOID Free( CLIENT_CONN * pConn );
    static VOID TrimFreeList( VOID );

    BOOL QueryDnsName(
        LPBOOL           pfSync,
        ADDRCHECKFUNCEX  pFunc,
        ADDRCHECKARG     pArg,
        LPSTR *          ppName
        )
        {
            return _acCheck.QueryDnsName( pfSync, pFunc, pArg, ppName );
        }

    AC_RESULT CheckIpAccess( LPBOOL pfNeedDns )
        { return _acCheck.CheckIpAccess( pfNeedDns ); }

    AC_RESULT CheckDnsAccess()
        { return _acCheck.CheckDnsAccess(); }

    BOOL BindAccessCheckList( LPBYTE p, DWORD dw )
        { return _acCheck.BindCheckList( p, dw ); }

    VOID UnbindAccessCheckList()
        { _acCheck.UnbindCheckList(); }

    LPSTR QueryResolvedDnsName()
        { return _acCheck.QueryResolvedDnsName(); }

    BOOL IsDnsResolved()
        { return _acCheck.IsDnsResolved(); }

    HTTP_REQ_BASE * QueryHttpReq( VOID) const
        { return (_phttpReq);  }


protected:

    //
    //  Constructor and destructor
    //

    CLIENT_CONN( PCLIENT_CONN_PARAMS );
    VOID Initialize( PCLIENT_CONN_PARAMS );

    ~CLIENT_CONN( VOID );

    VOID Reset( VOID );

    BOOL IsCleaningUp( VOID ) const
        { return QueryState() == CCS_DISCONNECTING ||
                 QueryState() == CCS_SHUTDOWN;
        }

private:

    //
    //  Contains the CLIENT_CONN signature
    //

    ULONG  _Signature;

    //
    //  Construction success indicator
    //

    BOOL   _fIsValid;

    //
    //  Contains the client socket connection openned by the connection thread
    //

    SOCKET _sClient;

    enum CLIENT_CONN_STATE  _ccState;

    //
    //  Reference count.  Can't go away until the count reaches 0
    //

    LONG   _cRef;

    //
    //  Contains an ASCII representation of the client's remote address
    //  and the adapter local address
    //

    CHAR _achRemoteAddr[20];
    CHAR _achLocalAddr[20];

    //
    //  Port this connection is on
    //

    USHORT _sPort;

    //
    //  Remote port
    //

    USHORT _sRemotePort;

    //
    // This request came in through the secure port
    //

    BOOL _fSecurePort;

    //
    //  Parses the data and determines the appropriate action
    //

    HTTP_REQ_BASE * _phttpReq;

    //
    // server instance.  Both are referenced.
    //

    PW3_SERVER_INSTANCE m_pInstance;
    PIIS_ENDPOINT       m_pW3Endpoint;

    //
    // server instance - statistics object.
    //

    LPW3_SERVER_STATISTICS m_pW3Stats;

    //
    // local ip address
    //

    DWORD               m_localIpAddress;

    //
    // remote sockaddr
    //

    DWORD               m_remoteIpAddress;

    //
    // context returned on a non-acceptex completion
    //

    PVOID               m_atqEndpointObject;

    //
    //  Initial receive buffer if we're doing AcceptEx processing
    //

    PVOID           _pvInitial;
    DWORD           _cbInitial;

    PATQ_CONTEXT _AtqContext;

    //
    //  If FALSE, the Atq Context should not be reused because the calling
    //  thread will be exiting soon
    //

    BOOL         _fReuseContext:1;

    BOOL         _fAbortiveClose:1;

    //
    //  Response string for disconnect notifications.
    //
    //  NOTE: If the server handles non-serial requests, then two request completing at
    //  the same time could use this string
    //

    STR _strResponse;

    //
    //  These are for the lookaside buffer list
    //

    static CRITICAL_SECTION _csBuffList;
    static LIST_ENTRY       _BuffListHead;
    static BOOL             _fGlobalInit;
    static DWORD            _cFree;
    static DWORD            _FreeListScavengerCookie;

    LIST_ENTRY              _BuffListEntry;

    //
    // Address check object for IP / DNS
    //

    ADDRESS_CHECK           _acCheck;

#if CC_REF_TRACKING
public:

    //
    //  For object local refcount tracing
    //

    PTRACE_LOG              _pDbgCCRefTraceLog;

private:
#endif
};

//
//  Functions for connection reference counts
//

inline VOID ReferenceConn( CLIENT_CONN * pConn )
{
    pConn->Reference();
}

inline VOID DereferenceConn( CLIENT_CONN * pConn )
{
    if ( !pConn->Dereference() ) {

        CLIENT_CONN::Free( pConn );
    }
}

/*******************************************************************

    Support for CLIENT_CONN debug ref trace logging

    SYNOPSIS:   Macro for writing to debug ref trace log.

    HISTORY:
        DaveK       10-Sep-1997 Created

********************************************************************/

//
//  NOTE we avoid compile failure by hokey double typecast, below
//  (since htrState is an enum, we must first cast it to int).
//

#define SHARED_LOG_REF_COUNT(   \
            cRefs               \
            , pClientConn       \
            , pHttpRequest      \
            , pWamRequest       \
            , htrState          \
            )                   \
                                \
    if( g_pDbgCCRefTraceLog != NULL ) {     \
                                            \
        ULONG_PTR i = (ULONG_PTR)htrState;    \
        PVOID pv = (PVOID) i;               \
                                            \
        WriteRefTraceLogEx(                 \
            g_pDbgCCRefTraceLog             \
            , cRefs                         \
            , pClientConn                   \
            , pHttpRequest                  \
            , pWamRequest                   \
            , pv                            \
        );                                  \
    }                                       \

//
//  This macro logs the CLIENT_CONN specific ref trace log
//

#define LOCAL_LOG_REF_COUNT(    \
            cRefs               \
            , pClientConn       \
            , pHttpRequest      \
            , pWamRequest       \
            , htrState          \
            )                   \
                                \
    if( _pDbgCCRefTraceLog != NULL ) {      \
                                            \
        ULONG_PTR i = (ULONG_PTR)htrState;    \
        PVOID pv = (PVOID) i;               \
                                            \
        WriteRefTraceLogEx(                 \
            _pDbgCCRefTraceLog              \
            , cRefs                         \
            , pClientConn                   \
            , pHttpRequest                  \
            , pWamRequest                   \
            , pv                            \
        );                                  \
    }                                       \

#if CC_REF_TRACKING
inline
VOID
LogRefCountCCLocal(
    IN LONG cRefs
    , IN PVOID pClientConn
    , IN PVOID pHttpRequest
    , IN PVOID pWamRequest
    , IN int htrState
)
{
    if( pClientConn && ((CLIENT_CONN *)pClientConn)->_pDbgCCRefTraceLog )
    {
        WriteRefTraceLogEx(
            ((CLIENT_CONN *) pClientConn)->_pDbgCCRefTraceLog
            , cRefs
            , pClientConn
            , pHttpRequest
            , pWamRequest
            , (PVOID) htrState
        );
    }
}
#endif

#if DBG
VOID DBG_CHECK_UnbalancedThreadToken(
    IN const char *         pszFilePath,
    IN int                  nLineNum
    );
#endif //DBG

#endif // !_CONN_HXX_