/*
**		Direct Network Protocol
**
**		This file contains internal prototypes and global definitions.
*/

// Protocol Version History /////////////////////////////////////////////////////////////
//
//	1.0 - DPlay8 original
//	1.1 - Fix SACK frame bogus 2 bytes caused by bad packing (DPlay 8.1 beta period only)
//	1.2 - Revert to original sack behavior with packing fixed, ie same as DPlay 8.0 (shipped in DPlay 8.1)
//	1.3 - Increment for PocketPC release (RTM prior to DX9 Beta1)
//	1.4 - DX9 Beta1 only
//	1.5 - Add coalescence and hard disconnect support
//	1.5 - .NET Server RTM
//	1.6 - Added packet signing, new style keep alive and connection spoof prevention
//	1.6 - DX9 Beta2 through RTM
/////////////////////////////////////////////////////////////////////////////////////////

//	Global Constants
#define DNET_VERSION_NUMBER                             0x00010005      // The current protocol version
#define DNET_COALESCE_VERSION                           0x00010005      // The first version with coalescence
#define DNET_SIGNING_VERSION                            0x00010006      // The first version with signing

#define DELAYED_ACK_TIMEOUT					100			// Delay before sending dedicated ACK packet
#define SHORT_DELAYED_ACK_TIMEOUT			20			// Delay before sending dedicated NACK packet
#define DELAYED_SEND_TIMEOUT				40			// Delay before sending dedicated SEND_INFO packet

#define CONNECT_DEFAULT_TIMEOUT				(200)		// At .1 we saw too many retries, users can set in SetCaps
#define CONNECT_DEFAULT_RETRIES				14			// Users can set in SetCaps
#define DEFAULT_MAX_RECV_MSG_SIZE			0xFFFFFFFF	//default maximum packet we accept
#define DEFAULT_SEND_RETRIES_TO_DROP_LINK	10			//default number of send retries we attempt before link is dead
#define DEFAULT_SEND_RETRY_INTERVAL_LIMIT	5000		//limit on period in msec between retries
#define DEFAULT_HARD_DISCONNECT_SENDS		3			//default number of hard disconnect frames we send out
														//The value must be at least 2.
#define DEFAULT_HARD_DISCONNECT_MAX_PERIOD	500		//The default for the maximum period we allow between
														//sending out hard disconnect frames
#define DEFAULT_INITIAL_FRAME_WINDOW_SIZE		2		//The default initial frame window size
#define LAN_INITIAL_FRAME_WINDOW_SIZE			32		//The initial frame window size if we asssume a LAN connection

#define STANDARD_LONG_TIMEOUT_VALUE		30000
#define DEFAULT_KEEPALIVE_INTERVAL		60000
#define ENDPOINT_BACKGROUND_INTERVAL	STANDARD_LONG_TIMEOUT_VALUE		// this is really what its for...

#define CONNECT_SECRET_CHANGE_INTERVAL		60000		//period between creations of new connect secrets

#define	DEFAULT_THROTTLE_BACK_OFF_RATE          25              // Percent throttle (backoff) rate
#define	DEFAULT_THROTTLE_THRESHOLD_RATE         7               // Percent packets dropped (out of 32)

#define DPF_TIMER_LVL			9 // The level at which to spew calls into the Protocol

#define DPF_CALLIN_LVL			2 // The level at which to spew calls into the Protocol
#define DPF_CALLOUT_LVL			3 // The level at which to spew calls out of the Protocol

#define DPF_ADAPTIVE_LVL		6 // The level at which to spew Adaptive Algorithm spew
#define DPF_FRAMECNT_LVL		7 // The level at which to spew Adaptive Algorithm spew

#define DPF_REFCNT_LVL			8 // The level at which to spew ref counts
#define DPF_REFCNT_FINAL_LVL	5 // The level at which to spew creation and destruction ref counts

// Separate ones for endpoints
#define DPF_EP_REFCNT_LVL		8 // The level at which to spew ref counts
#define DPF_EP_REFCNT_FINAL_LVL	2 // The level at which to spew creation and destruction ref counts

#undef DPF_SUBCOMP
#define DPF_SUBCOMP DN_SUBCOMP_PROTOCOL

	//handy macro for dumping a ULONGLONG out to a debug line
#define DPFX_OUTPUT_ULL(ull)  ((DWORD ) ull) , ((DWORD ) (ull>>32))

typedef	void CALLBACK LPCB(UINT, UINT, DWORD, DWORD, DWORD);

//	Global Variable definitions

extern CFixedPool	ChkPtPool;
extern CFixedPool	EPDPool;
extern CFixedPool	MSDPool;
extern CFixedPool	FMDPool;
extern CFixedPool	RCDPool;
extern CFixedPool	BufPool;
extern CFixedPool	MedBufPool;
extern CFixedPool	BigBufPool;

// Pool functions
BOOL			Buf_Allocate(PVOID, PVOID pvContext);
VOID			Buf_Get(PVOID, PVOID pvContext);
VOID			Buf_GetMed(PVOID, PVOID pvContext);
VOID			Buf_GetBig(PVOID, PVOID pvContext);
BOOL			EPD_Allocate(PVOID, PVOID pvContext);
VOID			EPD_Get(PVOID, PVOID pvContext);
VOID			EPD_Release(PVOID);
VOID			EPD_Free(PVOID);
BOOL			FMD_Allocate(PVOID, PVOID pvContext);
VOID			FMD_Get(PVOID, PVOID pvContext);
VOID			FMD_Release(PVOID);
VOID			FMD_Free(PVOID);
BOOL			MSD_Allocate(PVOID, PVOID pvContext);
VOID			MSD_Get(PVOID, PVOID pvContext);
VOID			MSD_Release(PVOID);
VOID			MSD_Free(PVOID);
BOOL			RCD_Allocate(PVOID, PVOID pvContext);
VOID			RCD_Get(PVOID, PVOID pvContext);
VOID			RCD_Release(PVOID);
VOID			RCD_Free(PVOID);


#ifdef DBG
extern CBilink		g_blProtocolCritSecsHeld;
#endif // DBG

// Internal function prototypes /////////////////////////////////

// Timers
VOID CALLBACK	ConnectRetryTimeout(void * const pvUser, void * const pvHandle, const UINT uiUnique);
VOID CALLBACK	DelayedAckTimeout(void * const pvUser, void * const uID, const UINT uMsg);
VOID CALLBACK	EndPointBackgroundProcess(void * const pvUser, void * const pvTimerData, const UINT uiTimerUnique);
VOID CALLBACK	RetryTimeout(void * const pvUser, void * const uID, const UINT Unique);
VOID CALLBACK	ScheduledSend(void * const pvUser, void * const pvTimerData, const UINT uiTimerUnique);
VOID CALLBACK	TimeoutSend(void * const pvUser, void * const uID, const UINT uMsg);
VOID CALLBACK 	HardDisconnectResendTimeout(void * const pvUser, void * const pvTimerData, const UINT uiTimerUnique);


VOID				AbortSendsOnConnection(PEPD);
SPRECEIVEDBUFFER * 	AbortRecvsOnConnection(PEPD);
VOID			CancelEpdTimers(PEPD);
ULONG WINAPI 	BackgroundThread(PVOID);
HRESULT			DoCancel(PMSD, HRESULT);
VOID 			CompleteConnect(PMSD, PSPD, PEPD, HRESULT);
VOID			CompleteDisconnect(PMSD pMSD, PSPD pSPD, PEPD pEPD);
VOID 			CompleteHardDisconnect(PEPD pEPD);
VOID 			CompleteDatagramSend(PSPD, PMSD, HRESULT);
VOID			CompleteReliableSend(PSPD, PMSD, HRESULT);
VOID			CompleteSPConnect(PMSD, PSPD, HRESULT);
VOID			DisconnectConnection(PEPD);
VOID			DropLink(PEPD);
PMSD			BuildDisconnectFrame(PEPD);
VOID			EndPointDroppedFrame(PEPD, DWORD);
VOID			EnqueueMessage(PMSD, PEPD);
VOID 			FlushCheckPoints(PEPD);
VOID 			InitLinkParameters(PEPD, UINT, DWORD);
PCHKPT			LookupCheckPoint(PEPD, BYTE);
PEPD			NewEndPoint(PSPD, HANDLE);
VOID			SendKeepAlive(PEPD pEPD);
VOID			ReceiveComplete(PEPD);
VOID			SendAckFrame(PEPD, BOOL, BOOL fFinalAck = FALSE);
HRESULT			SendCommandFrame(PEPD, BYTE, BYTE, ULONG, BOOL);
HRESULT 		SendConnectedSignedFrame(PEPD pEPD, CFRAME_CONNECTEDSIGNED * pCFrameRecv, DWORD tNow);
ULONG WINAPI 	SendThread(PVOID);
VOID			ServiceCmdTraffic(PSPD);
VOID			ServiceEPD(PSPD, PEPD);
VOID 			UpdateEndPoint(PEPD, UINT, DWORD);
VOID			UpdateXmitState(PEPD, BYTE, ULONG, ULONG, DWORD);
VOID			RejectInvalidPacket(PEPD);

ULONGLONG GenerateConnectSig(DWORD dwSessID, DWORD dwAddressHash, ULONGLONG ullConnectSecret);
ULONGLONG GenerateOutgoingFrameSig(PFMD pFMD, ULONGLONG ullSecret);
ULONGLONG GenerateIncomingFrameSig(BYTE * pbyFrame, DWORD dwFrameSize, ULONGLONG ullSecret);
ULONGLONG GenerateNewSecret(ULONGLONG ullCurrentSecret, ULONGLONG ullSecretModifier);
ULONGLONG GenerateLocalSecretModifier(BUFFERDESC * pBuffers, DWORD dwNumBuffers);
ULONGLONG GenerateRemoteSecretModifier(BYTE * pbyData, DWORD dwDataSize);

	//returns TRUE if supplied protocol version number indicates signing is supported
inline BOOL VersionSupportsSigning(DWORD dwVersion)
{
	return (((dwVersion>>16)==1) && ((dwVersion & 0xFFFF) >= (DNET_SIGNING_VERSION & 0xFFFF)));
}

	//returns TRUE if supplied protocol version number indicates coalescence is supported
inline BOOL VersionSupportsCoalescence(DWORD dwVersion)
{
	return (((dwVersion>>16)==1) && ((dwVersion & 0xFFFF) >= (DNET_COALESCE_VERSION & 0xFFFF)));
}

#ifndef DPNBUILD_NOPROTOCOLTESTITF
extern PFNASSERTFUNC g_pfnAssertFunc;
extern PFNMEMALLOCFUNC g_pfnMemAllocFunc;
#endif // !DPNBUILD_NOPROTOCOLTESTITF

//	Internal Macro definitions

#undef ASSERT

#ifndef DBG
#define	ASSERT(EXP)		DNASSERT(EXP)
#else // DBG
#define	ASSERT(EXP) \
	if (!(EXP)) \
	{ \
		if (g_pfnAssertFunc) \
		{ \
			g_pfnAssertFunc(#EXP); \
		} \
		DNASSERT(EXP); \
	}
#endif // !DBG

#ifdef DPNBUILD_NOPROTOCOLTESTITF

#define MEMALLOC(memid, dwpSize) DNMalloc(dwpSize)
#define POOLALLOC(memid, pool) (pool)->Get()

#else // !DPNBUILD_NOPROTOCOLTESTITF

#define MEMALLOC(memid, dwpSize) MemAlloc(memid, dwpSize)
#define POOLALLOC(memid, pool) PoolAlloc(memid, pool)
__inline VOID* MemAlloc(ULONG ulAllocID, DWORD_PTR dwpSize)
{
	if (g_pfnMemAllocFunc)
	{
		if (!g_pfnMemAllocFunc(ulAllocID))
		{
			return NULL;
		}
	}
	return DNMalloc(dwpSize);
}		
__inline VOID* PoolAlloc(ULONG ulAllocID, CFixedPool* pPool)
{
	if (g_pfnMemAllocFunc)
	{
		if (!g_pfnMemAllocFunc(ulAllocID))
		{
			return NULL;
		}
	}
	return pPool->Get();
}

#endif // DPNBUILD_NOPROTOCOLTESTITF

#define	Lock(P)			DNEnterCriticalSection(P)
#define	Unlock(P)		DNLeaveCriticalSection(P)

#define	ASSERT_PPD(PTR)	ASSERT((PTR) != NULL); ASSERT((PTR)->Sign == PPD_SIGN)
#define	ASSERT_SPD(PTR)	ASSERT((PTR) != NULL); ASSERT((PTR)->Sign == SPD_SIGN)
#define	ASSERT_EPD(PTR)	ASSERT((PTR) != NULL); ASSERT((PTR)->Sign == EPD_SIGN)
#define	ASSERT_MSD(PTR)	ASSERT((PTR) != NULL); ASSERT((PTR)->Sign == MSD_SIGN)
#define	ASSERT_FMD(PTR)	ASSERT((PTR) != NULL); ASSERT((PTR)->Sign == FMD_SIGN)
#define	ASSERT_RCD(PTR)	ASSERT((PTR) != NULL); ASSERT((PTR)->Sign == RCD_SIGN)

#define	INTER_INC(PTR)	DNInterlockedIncrement(&(PTR)->lRefCnt)
#define	INTER_DEC(PTR)	DNInterlockedDecrement(&(PTR)->lRefCnt)

#ifdef DBG

VOID	LockEPD(PEPD, PTSTR);
VOID	ReleaseEPD(PEPD, PTSTR);
VOID	DecrementEPD(PEPD, PTSTR);
VOID	LockMSD(PMSD, PTSTR);
VOID	ReleaseMSD(PMSD, PTSTR);
VOID	DecrementMSD(PMSD, PTSTR);
VOID	ReleaseFMD(PFMD, PTSTR);
VOID	LockFMD(PFMD, PTSTR);

#define	LOCK_EPD(a, b)				LockEPD(a, _T(b))
#define	RELEASE_EPD(a, b)			ReleaseEPD(a, _T(b))
#define	DECREMENT_EPD(a, b)			DecrementEPD(a, _T(b))
#define	LOCK_MSD(a, b)				LockMSD(a, _T(b))
#define RELEASE_MSD(a, b)			ReleaseMSD(a, _T(b))
#define DECREMENT_MSD(a, b)			DecrementMSD(a, _T(b))
#define	RELEASE_FMD(a, b)			ReleaseFMD(a, _T(b))
#define	LOCK_FMD(a, b)				LockFMD(a, _T(b))

#else // !DBG

VOID	LockEPD(PEPD);
VOID	ReleaseEPD(PEPD);
VOID	DecrementEPD(PEPD);
VOID	LockMSD(PMSD);
VOID	ReleaseMSD(PMSD);
VOID	DecrementMSD(PMSD);
VOID	ReleaseFMD(PFMD);
VOID	LockFMD(PFMD);

#define	LOCK_EPD(a, b)				LockEPD(a)
#define	RELEASE_EPD(a, b)			ReleaseEPD(a)
#define	DECREMENT_EPD(a, b)			DecrementEPD(a)
#define	LOCK_MSD(a, b)				LockMSD(a)
#define RELEASE_MSD(a, b)			ReleaseMSD(a)
#define DECREMENT_MSD(a, b)			DecrementMSD(a)
#define	RELEASE_FMD(a, b)			ReleaseFMD(a)
#define	LOCK_FMD(a, b)				LockFMD(a)

#endif // DBG

#define	LOCK_RCD(PTR)		(INTER_INC(PTR))
#define	RELEASE_RCD(PTR)	ASSERT((PTR)->lRefCnt > 0); if( INTER_DEC(PTR) == 0) { RCDPool.Release((PTR)); }

// This links the passed in pRcvBuff onto a passed in list
#define	RELEASE_SP_BUFFER(LIST, PTR) if((PTR) != NULL) { (PTR)->pNext = (LIST); (LIST) = (PTR); (PTR) = NULL;}

#define	RIGHT_SHIFT_64(HIGH_MASK, LOW_MASK) { ((LOW_MASK) >>= 1); if((HIGH_MASK) & 1){ (LOW_MASK) |= 0x80000000; } ((HIGH_MASK) >>= 1); }

//	CONVERT TO AND FROM 16.16 FIXED POINT REPRESENTATION

#define	TO_FP(X)		(((X) << 16) & 0xFFFF0000)
#define	FP_INT(X)		(((X) >> 16) & 0x0000FFFF)