/*++

    Copyright (C) Microsoft Corporation, 1996 - 1999

    Module Name:

        Protocol.hxx

    Abstract:

        The Protocol object definitions

    Author:

        Kamen Moutafov    [KamenM]

    Revision History:

        KamenM      12/22/1998   Creation

--*/

#if _MSC_VER >= 1200
#pragma once
#endif

#ifndef __PROTOCOL_HXX_
#define __PROTOCOL_HXX_

// uncomment this to turn on PNP debug spew
//#define MAJOR_PNP_DEBUG

enum TransportProtocolStates
{
    ProtocolNotLoaded,
    ProtocolLoadedWithoutAddress,
    ProtocolWasLoadedOrNeedsActivation,
    ProtocolLoaded,
    ProtocolWasLoadedOrNeedsActivationWithoutAddress,
    ProtocolLoadedAndMonitored
};

struct BASE_ASYNC_OBJECT;        // forwards
extern RPC_ADDRESS_CHANGE_FN * AddressChangeFn;
void RPC_ENTRY NullAddressChangeFn( PVOID arg );

class TransportProtocol
/*
 This object encapsulates a protocol on the transport level. Used primarily for PnP support.
 */
{
public:
    TransportProtocol(void)
    {
        State = ProtocolNotLoaded;
        RpcpInitializeListHead(&ObjectList);
        addressChangeSocket = 0;
        // provide arbitrary non-zero code, to indicate that this query has not succeeded
        addressChangeOverlapped.Internal = (ULONG_PTR)-1;
    }

    // If a piece of code detects that a protocol is functional through means other
    // than PnP, it may call this function to notify the PnP code that the protocol is functional
    // The only effect will be speedier processing of a potential PnP notification
    void DetectedAsFunctional(PROTOCOL_ID ProtocolId);

    // Whenever a PnP notification arrives, this method needs to be called for each protocol.
    // The method is idempotent - it saves the previous state and will no-op redundant calls
    // However, this method should not be called before the runtime is ready to accept
    // calls on transport addresses
    void HandleProtocolChange(IN WSAPROTOCOL_INFO *lpProtocolBuffer, IN int ProtocolCount, 
        IN PROTOCOL_ID thisProtocolId);

    // Adds an object to the list of objects for this protocol. The list is used to track down
    // all instances of objects belonging to this protocol when a PnP notification arrives.
    void AddObjectToList(IN OUT BASE_ASYNC_OBJECT *pObj);

    // Removes an object from the list of objects on this protocol
    void RemoveObjectFromList(IN OUT BASE_ASYNC_OBJECT *pObj);

    // If a NewAddress query fails asynchronously, this function needs to be called which will
    // retry submitting all failed query for this protocol. For non-failed, or non-submitted queries,
    // this is a no-op
    BOOL ResubmitQueriesIfNecessary(PROTOCOL_ID ProtocolId);

    // Handle a PnP notification. Does all necessary processing
    static BOOL HandlePnPStateChange(void);

    // Calls ResubmitQueriesIfNecessary(PROTOCOL_ID ProtocolId) on all protocols.
    static BOOL ResubmitQueriesIfNecessary(void);

    // Same as the non-static FunctionalProtocolDetected, except that this one operates
    // without this pointer - it will retrieve the appropriate object and will call
    // FunctionalProtocolDetected on it.
    static void FunctionalProtocolDetected(PROTOCOL_ID ProtocolId);

    // Same as AddObjectToList except that it will call AddObjectToList on the appropriate
    // protocol
    static void AddObjectToProtocolList(IN OUT BASE_ASYNC_OBJECT *pObj);

    // Same as RemoveObjectFromList except that it will call RemoveObjectFromList on the appropriate
    // protocol
    static void RemoveObjectFromProtocolList(IN OUT BASE_ASYNC_OBJECT *pObj);

#if defined(DBG) || defined(_DEBUG)
    // will ASSERT that all protocols are in a consistent state
    static void AssertTransportProtocolState(void);

    // will ASSSERT that this protocol is in a consistent state
    void AssertState(PROTOCOL_ID ProtocolId);

#endif

private:
    // attempts to verify that a protocol is functional, and if found so, advances the state of
    // the protocol
    BOOL VerifyProtocolIsFunctional(PROTOCOL_ID ProtocolId);

    // checks whether the given protocol has an address
    BOOL DoesAddressSocketHaveAddress(void);

    // If there is a pending address change request (WSAIoctl), it cancels it
    // if fForceCancel is FALSE, monitored protocols don't have their
    // address change requests canceled. If fForceCancel is TRUE,
    // all protocols get their address change requests cancelled
    void CancelAddressChangeRequestIfNecessary(IN BOOL fForceCancel, IN PROTOCOL_ID ProtocolId);

    // Restarts a protocol that was unloaded. Currently not used.
    void RestartProtocol(PROTOCOL_ID ProtocolId);

    // Unloads a protocol that was loaded.
    void UnloadProtocol(PROTOCOL_ID ProtocolId);

    // Submits an address change query (WSAIoctl)
    BOOL SubmitAddressChangeQuery(void);

    // Sets the state of the given protocol. Other code should never directly set the State - it should
    // always go through this function. This allows for derivative protocols (like HTTP) to mirror
    // the state of their base protocols - TCP
    void SetState(TransportProtocolStates newState, PROTOCOL_ID ProtocolId);

    // will return whether address change monitoring is required for this protocol. Currently this
    // is used only by RPCSS for SPX (in order to start sending updated SAP broadcasts)
    BOOL IsAddressChangeMonitoringOn(PROTOCOL_ID ProtocolId)
    {
        // if this is TCP & SPX, and somebody actually cares about this (i.e. somebody has
        // registered a notification callback), then turn on address monitoring
        return (
                (
#ifdef SPX_ON
                 (ProtocolId == SPX) 
                   || 
#endif
                 (ProtocolId == TCP)
                ) 
                && 
                (AddressChangeFn != NullAddressChangeFn)
                && 
                (AddressChangeFn != NULL)
               );
    }

    // Trailing protocols are protocols that don't have their own independent transport provider
    // They use the transport provider of another protocol, but for some reason we present it
    // to the upper layers as different protocols. We call them trailing, because their
    // state trails the state of the protocol whose transport they use.
    // Currently we have only one of those - HTTP which trails TCP
    BOOL IsTrailingProtocol(PROTOCOL_ID ProtocolId)
    {
        return (ProtocolId == HTTP);
    }

    void SetStateToLoadedAndMonitorProtocolIfNecessary(PROTOCOL_ID ProtocolId);

    // checks if there is an address change request pending, and if there isn't
    // submits one. Will also open an address change socket if necessary
    BOOL MonitorFunctionalProtocolForAddressChange(PROTOCOL_ID ProtocolId);

    // checks if these's an address change request socket, and if there
    // isn't it opens one
    BOOL OpenAddressChangeRequestSocket(PROTOCOL_ID ProtocolId);

    // if ThisProtocolsId is in ProtocolLoadedAndMonitored or ProtocolLoaded state
    // move the Mirrored protocol to the corresponding state
    void 
    MirrorProtocolState (
        IN PROTOCOL_ID MirrorProtocolId
        );

#ifdef MAJOR_PNP_DEBUG
    // Dumps the protocol state on the debugger for the current protocol
    void DumpProtocolState(PROTOCOL_ID ProtocolId);
    // Dumps the protocol state on the debugger for all protocols
    static void DumpProtocolState(void);
#endif

    // the current state of this protocol
    TransportProtocolStates State;

    // the list of opened objects for this protocol
    LIST_ENTRY ObjectList;
    
    // iff there's address change request submitted on this socket, it will be non-null
    SOCKET addressChangeSocket;

    // the OVERLAPPED for the address change request (WSAIoctl). It's Internal member will be -1
    // if no request has been submitted. Other data members are as normal.
    OVERLAPPED addressChangeOverlapped;

    // NOTE: A TransportProtocol object is not aware of its PROTOCOL_ID - it must be
    // passed externally to it for all methods that require it. This is done to save memory.
};

extern TransportProtocol *TransportProtocolArray;

// returns the TransportProtocol object for the ProtocolId. Ownership of the object does not pass
// to the caller
inline TransportProtocol *GetTransportProtocol(IN PROTOCOL_ID ProtocolId)
{
    ASSERT(ProtocolId >= 1);
    ASSERT(ProtocolId < MAX_PROTOCOLS);

    return &TransportProtocolArray[ProtocolId];
}

inline void TransportProtocol::FunctionalProtocolDetected(PROTOCOL_ID ProtocolId)
{
    GetTransportProtocol(ProtocolId)->DetectedAsFunctional(ProtocolId);
}

RPC_STATUS InitTransportProtocols(void);

#if defined(DBG) || defined(_DEBUG)
#define ASSERT_ALL_TRANSPORT_PROTOCOL_STATE()      TransportProtocol::AssertTransportProtocolState()
#define ASSERT_TRANSPORT_PROTOCOL_STATE(p)          AssertState(p)
#else
#define ASSERT_ALL_TRANSPORT_PROTOCOL_STATE()
#define ASSERT_TRANSPORT_PROTOCOL_STATE(p)
#endif

#endif