|
|
/****************************************************************************/ /* tdapi.c */ /* */ /* Transport driver - portable specific API */ /* */ /* Copyright (C) 1997-1999 Microsoft Corporation */ /****************************************************************************/
#include <adcg.h>
extern "C" { #define TRC_FILE "tdapi"
#define TRC_GROUP TRC_GROUP_NETWORK
#include <atrcapi.h>
}
#include "autil.h"
#include "td.h"
#include "xt.h"
#include "nl.h"
CTD::CTD(CObjs* objs) { _pClientObjects = objs; }
CTD::~CTD() { }
/****************************************************************************/ /* Name: TD_Init */ /* */ /* Purpose: Initializes the transport driver. This is called on the */ /* receiver thread. */ /****************************************************************************/ DCVOID DCAPI CTD::TD_Init(DCVOID) { DC_BEGIN_FN("TD_Init");
_pNl = _pClientObjects->_pNlObject; _pUt = _pClientObjects->_pUtObject; _pXt = _pClientObjects->_pXTObject; _pUi = _pClientObjects->_pUiObject; _pCd = _pClientObjects->_pCdObject;
/************************************************************************/ /* Initialize the global data and set the initial FSM state. */ /************************************************************************/ DC_MEMSET(&_TD, 0, sizeof(_TD)); _TD.fsmState = TD_ST_NOTINIT;
/************************************************************************/ /* Call the FSM. */ /************************************************************************/ TDConnectFSMProc(TD_EVT_TDINIT, 0);
DC_END_FN(); } /* TD_Init */
/****************************************************************************/ /* Name: TD_Term */ /* */ /* Purpose: Terminates the transport driver. This is called on the */ /* receiver thread. */ /****************************************************************************/ DCVOID DCAPI CTD::TD_Term(DCVOID) { DC_BEGIN_FN("TD_Term");
/************************************************************************/ /* Call the FSM. */ /************************************************************************/ TDConnectFSMProc(TD_EVT_TDTERM, 0);
DC_END_FN(); } /* TD_Term */
/****************************************************************************/ /* Name: TD_Connect */ /* */ /* Purpose: Connects to a remote server. Called on the receiver thread. */ /* */ /* Params: */ /* IN bInitateConnect : TRUE if we are making connection, */ /* FALSE if connect with already connected socket */ /* */ /****************************************************************************/ DCVOID DCAPI CTD::TD_Connect(BOOL bInitateConnect, PDCTCHAR pServerAddress) { DCUINT i; DCUINT errorCode; u_long addr; DCUINT nextEvent; ULONG_PTR eventData; DCACHAR ansiBuffer[256];
DC_BEGIN_FN("TD_Connect");
/************************************************************************/ /* Check that the string is not null. */ /************************************************************************/ if( bInitateConnect ) { TRC_ASSERT((0 != *pServerAddress), (TB, _T("Server address is NULL"))); }
/************************************************************************/ /* Check that all the buffers are not in-use. */ /************************************************************************/ for (i = 0; i < TD_SNDBUF_PUBNUM; i++) { if (_TD.pubSndBufs[i].inUse) { TD_TRACE_SENDINFO(TRC_LEVEL_ERR); TRC_ABORT((TB, _T("Public buffer %u still in-use"), i)); } }
for (i = 0; i < TD_SNDBUF_PRINUM; i++) { if (_TD.priSndBufs[i].inUse) { TD_TRACE_SENDINFO(TRC_LEVEL_ERR); TRC_ABORT((TB, _T("Private buffer %u still in-use"), i)); } }
/************************************************************************/ /* Trace out the send buffer information. */ /************************************************************************/ TD_TRACE_SENDINFO(TRC_LEVEL_NRM);
if( FALSE == bInitateConnect ) { TDConnectFSMProc(TD_EVT_CONNECTWITHENDPOINT, NULL); DC_QUIT; // all we need is the buffer.
}
#ifdef UNICODE
/************************************************************************/ /* WinSock 1.1 only supports ANSI, so we need to convert any Unicode */ /* strings at this point. */ /************************************************************************/ if (!WideCharToMultiByte(CP_ACP, 0, pServerAddress, -1, ansiBuffer, 256, NULL, NULL)) { /********************************************************************/ /* Conversion failed */ /********************************************************************/ TRC_ERR((TB, _T("Failed to convert address to ANSI")));
/********************************************************************/ /* Generate the error code. */ /********************************************************************/ errorCode = NL_MAKE_DISCONNECT_ERR(NL_ERR_TDANSICONVERT);
TRC_ASSERT((HIWORD(errorCode) == 0), (TB, _T("disconnect reason code unexpectedly using 32 bits"))); _pXt->XT_OnTDDisconnected(errorCode); }
#else
DC_ASTRCPY(ansiBuffer, pServerAddress); #endif /* UNICODE */
/************************************************************************/ /* Check that the address is not the limited broadcast address */ /* (255.255.255.255). */ /************************************************************************/ if (0 == DC_ASTRCMP(ansiBuffer, TD_LIMITED_BROADCAST_ADDRESS)) { TRC_ALT((TB, _T("Cannot connect to the limited broadcast address")));
/********************************************************************/ /* Generate the error code. */ /********************************************************************/ errorCode = NL_MAKE_DISCONNECT_ERR(NL_ERR_TDBADIPADDRESS);
TRC_ASSERT((HIWORD(errorCode) == 0), (TB, _T("disconnect reason code unexpectedly using 32 bits"))); _pXt->XT_OnTDDisconnected(errorCode); DC_QUIT; }
/************************************************************************/ /* Now determine whether a DNS lookup is required. */ /************************************************************************/ TRC_NRM((TB, _T("ServerAddress:%s"), ansiBuffer));
/************************************************************************/ /* Check that we have a string and that the address is not the limited */ /* broadcast address. */ /************************************************************************/ TRC_ASSERT((NULL != ansiBuffer), (TB, _T("ansiBuffer is NULL"))); TRC_ASSERT(('\0' != ansiBuffer[0]), (TB, _T("Empty server address string"))); TRC_ASSERT((0 != DC_ASTRCMP(ansiBuffer, TD_LIMITED_BROADCAST_ADDRESS)), (TB, _T("Cannot connect to the limited broadcast address")));
/************************************************************************/ /* Check for a dotted-IP address string. */ /************************************************************************/ addr = inet_addr(ansiBuffer); TRC_NRM((TB, _T("Address returned is %#lx"), addr));
/************************************************************************/ /* Now determine whether this is an address string or a host name. */ /* Note that inet_addr doesn't distinguish between an invalid IP */ /* address and the limited broadcast address (255.255.255.255). */ /* However since we don't allow the limited broadcast address and have */ /* already checked explicitly for it we don't need to worry about this */ /* case. */ /************************************************************************/ if (INADDR_NONE == addr) { /********************************************************************/ /* This looks like a host name so call the FSM with the current */ /* address. */ /********************************************************************/ TRC_NRM((TB, _T("%s looks like a hostname - need DNS lookup"), ansiBuffer)); nextEvent = TD_EVT_TDCONNECT_DNS; eventData = (ULONG_PTR) ansiBuffer; } else { /********************************************************************/ /* If we get here then it appears to be a dotted-IP address. Call */ /* the FSM with the updated address. */ /********************************************************************/ TRC_NRM((TB, _T("%s looks like a dotted-IP address:%lu"), ansiBuffer, addr)); nextEvent = TD_EVT_TDCONNECT_IP; eventData = addr; }
/************************************************************************/ /* Now call the FSM with the appropriate parameters. */ /************************************************************************/ TDConnectFSMProc(nextEvent, eventData);
DC_EXIT_POINT: DC_END_FN(); } /* TD_Connect */
/****************************************************************************/ /* Name: TD_Disconnect */ /* */ /* Purpose: Disconnects from the server. This is called on the receiver */ /* thread. */ /****************************************************************************/ DCVOID DCAPI CTD::TD_Disconnect(DCVOID) { DC_BEGIN_FN("TD_Disconnect");
/************************************************************************/ /* Call the FSM. We pass NL_DISCONNECT_LOCAL which will be used as the */ /* return code to XT in the cases where we are waiting for the DNS */ /* lookup to return or the socket to connect. */ /************************************************************************/ TDConnectFSMProc(TD_EVT_TDDISCONNECT, NL_DISCONNECT_LOCAL);
DC_END_FN(); } /* TD_Disconnect */
//
// TD_DropLink - drops the link immediately (ungracefully)
//
VOID CTD::TD_DropLink(DCVOID) { DC_BEGIN_FN("TD_DropLink");
TDConnectFSMProc(TD_EVT_DROPLINK, NL_DISCONNECT_LOCAL);
DC_END_FN(); }
/****************************************************************************/ /* Name: TD_GetPublicBuffer */ /* */ /* Purpose: Attempts to allocate a buffer from the public TD buffer pool. */ /* */ /* Returns: If the allocation succeeds then this function returns TRUE */ /* otherwise it returns FALSE. */ /* */ /* Params: IN dataLength - length of the buffer requested. */ /* OUT ppBuffer - a pointer to a pointer to the buffer. */ /* OUT pBufHandle - a pointer to a buffer handle. */ /****************************************************************************/ DCBOOL DCAPI CTD::TD_GetPublicBuffer(DCUINT dataLength, PPDCUINT8 ppBuffer, PTD_BUFHND pBufHandle) { DCUINT i; DCBOOL rc = FALSE; DCUINT lastfree = TD_SNDBUF_PUBNUM; PDCUINT8 pbOldBuffer; DCUINT cbOldBuffer;
DC_BEGIN_FN("TD_GetPublicBuffer");
// Check that we're in the correct state. If we're disconnected
// then fail the call. Note that the FSM state is maintained by the
// receiver thread - but we're on the sender thread.
if (_TD.fsmState == TD_ST_CONNECTED) { TRC_DBG((TB, _T("Searching for a buffer big enough for %u bytes"), dataLength));
// Trace out the send buffer information.
TD_TRACE_SENDINFO(TRC_LEVEL_DBG);
// Search the array of buffers looking for the first free one that is
// large enough.
for (i = 0; i < TD_SNDBUF_PUBNUM; i++) { TRC_DBG((TB, _T("Trying buf:%u inUse:%s size:%u"), i, _TD.pubSndBufs[i].inUse ? "TRUE" : "FALSE", _TD.pubSndBufs[i].size));
if(!_TD.pubSndBufs[i].inUse) { lastfree = i; }
if ((!(_TD.pubSndBufs[i].inUse)) && (_TD.pubSndBufs[i].size >= dataLength)) { TRC_DBG((TB, _T("bufHandle:%p (idx:%u) free - size:%u (req:%u)"), &_TD.pubSndBufs[i], i, _TD.pubSndBufs[i].size, dataLength));
// Now mark this buffer as being in use and set up the return
// values. The handle is just a pointer to the buffer
// information structure.
_TD.pubSndBufs[i].inUse = TRUE; *ppBuffer = _TD.pubSndBufs[i].pBuffer;
*pBufHandle = (TD_BUFHND) (PDCVOID)&_TD.pubSndBufs[i];
// Set a good return code.
rc = TRUE;
// Check that the other fields are set correctly.
TRC_ASSERT((_TD.pubSndBufs[i].pNext == NULL), (TB, _T("Buf:%u next non-zero"), i)); TRC_ASSERT((_TD.pubSndBufs[i].bytesLeftToSend == 0), (TB, _T("Buf:%u bytesLeftToSend non-zero"), i)); TRC_ASSERT((_TD.pubSndBufs[i].pDataLeftToSend == NULL), (TB, _T("Buf:%u pDataLeftToSend non-null"), i));
// Update the performance counter.
PRF_INC_COUNTER(PERF_PKTS_ALLOCATED);
// That's all we need to do so just quit.
DC_QUIT; } }
// check if we need to re-allocate
if(lastfree < TD_SNDBUF_PUBNUM) { pbOldBuffer = _TD.pubSndBufs[lastfree].pBuffer; cbOldBuffer = _TD.pubSndBufs[lastfree].size;
// reallocate space
TDAllocBuf( &_TD.pubSndBufs[lastfree], dataLength );
// TDAllocBuf() return DCVOID with UI_FatalError()
if( NULL != _TD.pubSndBufs[lastfree].pBuffer ) { UT_Free( _pUt, pbOldBuffer );
// Now mark this buffer as being in use and set up the return
// values. The handle is just a pointer to the buffer
// information structure.
_TD.pubSndBufs[lastfree].inUse = TRUE; *ppBuffer = _TD.pubSndBufs[lastfree].pBuffer; *pBufHandle = (TD_BUFHND) (PDCVOID)&_TD.pubSndBufs[lastfree];
// Set a good return code.
rc = TRUE;
// Update the performance counter.
PRF_INC_COUNTER(PERF_PKTS_ALLOCATED);
// That's all we need to do so just quit.
DC_QUIT; } else { // restore pointer and size.
_TD.pubSndBufs[lastfree].pBuffer = pbOldBuffer; _TD.pubSndBufs[lastfree].size = cbOldBuffer; } }
// We failed to find a free buffer. Trace the send buffer info.
_TD.getBufferFailed = TRUE; TRC_ALT((TB, _T("Failed to find a free buffer (req dataLength:%u) Bufs:"), dataLength)); TD_TRACE_SENDINFO(TRC_LEVEL_ALT); } else { TRC_NRM((TB, _T("Not connected therefore fail get buffer call"))); }
DC_EXIT_POINT: DC_END_FN(); return rc; } /* TD_GetPublicBuffer */
/****************************************************************************/ /* Name: TD_GetPrivateBuffer */ /* */ /* Purpose: Attempts to allocate a buffer from the private TD buffer */ /* pool. */ /* */ /* Returns: If the allocation succeeds then this function returns TRUE */ /* otherwise it returns FALSE. */ /* */ /* Params: IN dataLength - length of the buffer requested. */ /* OUT ppBuffer - a pointer to a pointer to the buffer. */ /* OUT pBufHandle - a pointer to a buffer handle. */ /* */ /* Operation: This function should always return a buffer - it it up to */ /* the network layer to ensure that it does not allocate more */ /* buffers than are available in the private list. */ /****************************************************************************/ DCBOOL DCAPI CTD::TD_GetPrivateBuffer(DCUINT dataLength, PPDCUINT8 ppBuffer, PTD_BUFHND pBufHandle) { DCUINT i; DCBOOL rc = FALSE;
DC_BEGIN_FN("TD_GetPrivateBuffer");
// Check that we're in the correct state. If we're disconnected
// then fail the call. Note that the FSM state is maintained by the
// receiver thread - but we're on the sender thread.
if (_TD.fsmState == TD_ST_CONNECTED) { TRC_DBG((TB, _T("Searching for a buffer big enough for %u bytes"), dataLength));
// Trace out the send buffer information.
TD_TRACE_SENDINFO(TRC_LEVEL_DBG);
// Search the array of buffers looking for the first free one that is
// large enough.
for (i = 0; i < TD_SNDBUF_PRINUM; i++) { TRC_DBG((TB, _T("Trying buf:%u inUse:%s size:%u"), i, _TD.priSndBufs[i].inUse ? "TRUE" : "FALSE", _TD.priSndBufs[i].size));
if ((!(_TD.priSndBufs[i].inUse)) && (_TD.priSndBufs[i].size >= dataLength)) { TRC_DBG((TB, _T("bufHandle:%p (idx:%u) free - size:%u (req:%u)"), &_TD.priSndBufs[i], i, _TD.priSndBufs[i].size, dataLength));
// Now mark this buffer as being in use and set up the return
// values. The handle is just a pointer to the buffer
// information structure.
_TD.priSndBufs[i].inUse = TRUE; *ppBuffer = _TD.priSndBufs[i].pBuffer; *pBufHandle = (TD_BUFHND) (PDCVOID)&_TD.priSndBufs[i];
// Set a good return code.
rc = TRUE;
// Check that the other fields are set correctly.
TRC_ASSERT((_TD.priSndBufs[i].pNext == NULL), (TB, _T("Buf:%u next non-zero"), i)); TRC_ASSERT((_TD.priSndBufs[i].bytesLeftToSend == 0), (TB, _T("Buf:%u bytesLeftToSend non-zero"), i)); TRC_ASSERT((_TD.priSndBufs[i].pDataLeftToSend == NULL), (TB, _T("Buf:%u pDataLeftToSend non-null"), i));
// That's all we need to do so just quit.
DC_QUIT; } }
// We failed to find a free buffer - flag this internal error by
// tracing out the entire buffer structure and then aborting.
TD_TRACE_SENDINFO(TRC_LEVEL_ERR); TRC_ABORT((TB, _T("Failed to find a free buffer (req dataLength:%u)"), dataLength)); } else { TRC_NRM((TB, _T("Not connected therefore fail get buffer call"))); }
DC_EXIT_POINT: DC_END_FN(); return(rc); } /* TD_GetPrivateBuffer */
/****************************************************************************/ /* Name: TD_SendBuffer */ /* */ /* Purpose: Sends a buffer. The buffer is added to the end of the */ /* pending queue and all data on the pending queue is then */ /* sent. */ /* */ /* Params: IN pData - pointer to the start of the data. */ /* IN dataLength - amount of the buffer used. */ /* IN bufHandle - handle to a buffer. */ /****************************************************************************/ DCVOID DCAPI CTD::TD_SendBuffer(PDCUINT8 pData, DCUINT dataLength, TD_BUFHND bufHandle) { PTD_SNDBUF_INFO pNext; PTD_SNDBUF_INFO pHandle = (PTD_SNDBUF_INFO) bufHandle;
DC_BEGIN_FN("TD_SendBuffer");
// Trace out the function parameters.
TRC_DBG((TB, _T("bufHandle:%p dataLength:%u pData:%p"), bufHandle, dataLength, pData));
// Check that the handle is valid.
TRC_ASSERT((((pHandle >= &_TD.pubSndBufs[0]) && (pHandle <= &_TD.pubSndBufs[TD_SNDBUF_PUBNUM - 1])) || ((pHandle >= &_TD.priSndBufs[0]) && (pHandle <= &_TD.priSndBufs[TD_SNDBUF_PRINUM - 1]))), (TB, _T("Invalid buffer handle:%p"), bufHandle));
// Verify buffer contents.
TRC_ASSERT((0 == pHandle->bytesLeftToSend), (TB, _T("pHandle->bytesLeftToSend non-zero (pHandle:%p)"), pHandle)); TRC_ASSERT((NULL == pHandle->pDataLeftToSend), (TB, _T("pHandle->pDataLeftToSend non NULL (pHandle:%p)"), pHandle)); TRC_ASSERT((NULL != pHandle->pBuffer), (TB, _T("pHandle->pBuffer is NULL (pHandle:%p)"), pHandle)); TRC_ASSERT((NULL == pHandle->pNext), (TB, _T("pHandle->pNext (pHandle:%p) non NULL"), pHandle)); TRC_ASSERT((pHandle->inUse), (TB, _T("pHandle %p is not in-use"), pHandle));
// Check that pData lies within the buffer and pData+dataLength does not
// overrun the end.
TRC_ASSERT(((pData >= pHandle->pBuffer) && (pData < (pHandle->pBuffer + pHandle->size))), (TB, _T("pData lies outwith range"))); TRC_ASSERT(((pData + dataLength) <= (pHandle->pBuffer + pHandle->size)), (TB, _T("pData + dataLength over the end of the buffer")));
//
// Update the fields in the buffer information structure and add to the
// pending buffer queue.
//
pHandle->pDataLeftToSend = pData; pHandle->bytesLeftToSend = dataLength; if (NULL == _TD.pFQBuf) { TRC_DBG((TB, _T("Inserted buffer:%p at queue head"), pHandle)); _TD.pFQBuf = pHandle; } else { // OK - the queue is not empty. We need to scan through the queue
// looking for the first empty slot to insert this buffer in at.
pNext = _TD.pFQBuf; while (NULL != pNext->pNext) pNext = pNext->pNext;
// Update the next field of the this buffer information structure.
pNext->pNext = pHandle; TRC_DBG((TB, _T("Inserted buffer:%p"), pHandle)); }
// Finally attempt to flush the send queue.
TDFlushSendQueue(0);
DC_END_FN(); } /* TD_SendBuffer */
/****************************************************************************/ /* Name: TD_FreeBuffer */ /* */ /* Purpose: Frees the passed buffer. */ /****************************************************************************/ DCVOID DCAPI CTD::TD_FreeBuffer(TD_BUFHND bufHandle) { PTD_SNDBUF_INFO pHandle = (PTD_SNDBUF_INFO) bufHandle;
DC_BEGIN_FN("TD_FreeBuffer");
// Trace out the function parameters.
TRC_DBG((TB, _T("bufHandle:%p"), bufHandle));
// Check that the handle is valid.
TRC_ASSERT((((pHandle >= &_TD.pubSndBufs[0]) && (pHandle <= &_TD.pubSndBufs[TD_SNDBUF_PUBNUM - 1])) || ((pHandle >= &_TD.priSndBufs[0]) && (pHandle <= &_TD.priSndBufs[TD_SNDBUF_PRINUM - 1]))), (TB, _T("Invalid buffer handle:%p"), bufHandle));
// Verify the buffer contents. InUse does not matter, we can legitimately
// free a non-in-use buffer.
TRC_ASSERT((0 == pHandle->bytesLeftToSend), (TB, _T("pHandle->bytesLeftToSend non-zero (pHandle:%p)"), pHandle)); TRC_ASSERT((NULL == pHandle->pDataLeftToSend), (TB, _T("pHandle->pDataLeftToSend non NULL (pHandle:%p)"), pHandle)); TRC_ASSERT((NULL != pHandle->pBuffer), (TB, _T("pHandle->pBuffer is NULL (pHandle:%p)"), pHandle)); TRC_ASSERT((NULL == pHandle->pNext), (TB, _T("pHandle->pNext (pHandle:%p) non NULL"), pHandle));
// Free the buffer.
pHandle->inUse = FALSE;
// Update the performance counter.
PRF_INC_COUNTER(PERF_PKTS_FREED);
DC_END_FN(); } /* TD_FreeBuffer */
#ifdef DC_DEBUG
/****************************************************************************/ /* Name: TD_SetBufferOwner */ /* */ /* Purpose: Note the owner of a TD buffer */ /* */ /* Params: bufHandle - handle to the buffer */ /* pOwner - name of the 'owner' */ /****************************************************************************/ DCVOID DCAPI CTD::TD_SetBufferOwner(TD_BUFHND bufHandle, PDCTCHAR pOwner) { PTD_SNDBUF_INFO pHandle = (PTD_SNDBUF_INFO) bufHandle;
DC_BEGIN_FN("TD_SetBufferOwner");
/************************************************************************/ /* Trace out the function parameters. */ /************************************************************************/ TRC_DBG((TB, _T("bufHandle:%p owner %s"), bufHandle, pOwner));
/************************************************************************/ /* Check that the handle is valid. */ /************************************************************************/ TRC_ASSERT((((pHandle >= &_TD.pubSndBufs[0]) && (pHandle <= &_TD.pubSndBufs[TD_SNDBUF_PUBNUM - 1])) || ((pHandle >= &_TD.priSndBufs[0]) && (pHandle <= &_TD.priSndBufs[TD_SNDBUF_PRINUM - 1]))), (TB, _T("Invalid buffer handle:%p"), bufHandle));
/************************************************************************/ /* Save the owner */ /************************************************************************/ pHandle->pOwner = pOwner;
DC_END_FN(); } /* TD_SetBufferOwner */
#endif/* DC_DEBUG */
|