|
|
/****************************************************************************/ // wtdint.cpp
//
// Transport driver - Windows specific internal functions.
//
// Copyright (C) 1997-1999 Microsoft Corp.
/****************************************************************************/
#include <adcg.h>
extern "C" { #define TRC_FILE "wtdint"
#define TRC_GROUP TRC_GROUP_NETWORK
#include <atrcapi.h>
}
#include "autil.h"
#include "td.h"
#include "xt.h"
#include "nl.h"
#include "wui.h"
#include "objs.h"
/****************************************************************************/ /* Name: TDInit */ /* */ /* Purpose: Initializes _TD. This function allocates the send buffers, */ /* creates the TD window and then initializes WinSock. */ /* */ /* Operation: On error this function calls the UT fatal error handler. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDInit(DCVOID) { DCUINT i; DCUINT pubSndBufSizes[TD_SNDBUF_PUBNUM] = TD_SNDBUF_PUBSIZES; DCUINT priSndBufSizes[TD_SNDBUF_PRINUM] = TD_SNDBUF_PRISIZES; WORD versionRequested; WSADATA wsaData; int intRC;
DC_BEGIN_FN("TDInit");
/************************************************************************/ /* Allocate a buffer into which data will be recieved from Winsock. */ /************************************************************************/ _TD.recvBuffer.pData = (PDCUINT8)UT_Malloc( _pUt, TD_RECV_BUFFER_SIZE); if (NULL != _TD.recvBuffer.pData) { // Got the buffer memory. Record the buffer size. Note we need to
// record slightly less than the allocated size to account for
// the fact that the current MPPC decompression code looks ahead one
// byte, which means that a fault can occur if this lookahead goes
// over the page boundary of this buffer. Leaving a couple of bytes
// at the end of the buffer prevents this. This does not seriously
// affect decoding efficiency -- the server itself sends less than
// a full 8K buffer per send.
TRC_NRM((TB, _T("Allocated %u bytes for recv buffer"), TD_RECV_BUFFER_SIZE)); _TD.recvBuffer.size = TD_RECV_BUFFER_SIZE - 2; } else { /********************************************************************/ /* Didn't get the memory. We can live without it, just keep the */ /* size set at zero. */ /********************************************************************/ TRC_ALT((TB, _T("Failed to alloc %u bytes for recv buffer"), TD_RECV_BUFFER_SIZE)); }
/************************************************************************/ /* Now loop through the public send buffer array and initialize the */ /* array members. */ /************************************************************************/ for (i = 0; i < TD_SNDBUF_PUBNUM; i++) { /********************************************************************/ /* Initialize the buffer information structure and allocate memory */ /* for the actual buffer. */ /********************************************************************/ TDInitBufInfo(&_TD.pubSndBufs[i]); TDAllocBuf(&_TD.pubSndBufs[i], pubSndBufSizes[i]);
TRC_DBG((TB, _T("Initialised public buffer:%u size:%u"), i, pubSndBufSizes[i])); }
/************************************************************************/ /* Loop through the private send buffer array and initialize the array */ /* members. */ /************************************************************************/ for (i = 0; i < TD_SNDBUF_PRINUM; i++) { /********************************************************************/ /* Initialize the buffer. */ /********************************************************************/ TDInitBufInfo(&_TD.priSndBufs[i]); TDAllocBuf(&_TD.priSndBufs[i], priSndBufSizes[i]);
TRC_DBG((TB, _T("Initialised private buffer:%u size:%u"), i, priSndBufSizes[i])); }
/************************************************************************/ /* Create the TD window. */ /************************************************************************/ TDCreateWindow();
#ifdef OS_WINCE
#if (_WIN32_WCE > 300)
if (NULL == (_TD.hevtAddrChange = CreateEvent(NULL, TRUE, FALSE, NULL))) { TRC_ABORT((TB, _T("Failed to create addr change notify event:%d"), GetLastError())); _pUi->UI_FatalError(DC_ERR_OUTOFMEMORY); DC_QUIT; } #endif
#endif
/************************************************************************/ /* We want to use version 1.1 of WinSock. */ /************************************************************************/ versionRequested = MAKEWORD(1, 1);
/************************************************************************/ /* Initialize WinSock. */ /************************************************************************/ intRC = WSAStartup(versionRequested, &wsaData);
if (intRC != 0) { /********************************************************************/ // Trace out the error code - note that we can't use WSAGetLastError
// at this point as WinSock has failed to initialize and so
// WSAGetLastError will just fail.
/********************************************************************/ TRC_ABORT((TB, _T("Failed to initialize WinSock rc:%d"), intRC)); _pUi->UI_FatalError(DC_ERR_WINSOCKINITFAILED); DC_QUIT; }
/************************************************************************/ /* Now confirm that this WinSock supports version 1.1. Note that if */ /* the DLL supports versions greater than 1.1 in addition to 1.1 then */ /* it will still return 1.1 in the version information as that is the */ /* version requested. */ /************************************************************************/ if ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1)) { /********************************************************************/ /* Oops - this WinSock doesn't support version 1.1. */ /********************************************************************/ WSACleanup();
TRC_ABORT((TB, _T("This WinSock doesn't support version 1.1"))); _pUi->UI_FatalError(DC_ERR_WINSOCKINITFAILED); DC_QUIT; }
TRC_NRM((TB, _T("WinSock init version %u:%u"), HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion)));
TRC_NRM((TB, _T("TD successfully initialized")));
DC_EXIT_POINT: DC_END_FN(); } /* TDInit */
/****************************************************************************/ /* Name: TDTerm */ /* */ /* Purpose: Terminates _TD. It frees the send buffers, cleans up WinSock, */ /* destroys the TD window and then unregisters the TD window */ /* class. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDTerm(DCVOID) { DCUINT i; int intRC;
DC_BEGIN_FN("TDTerm");
/************************************************************************/ /* Loop through the public and private send buffers and free the */ /* memory. */ /************************************************************************/ for (i = 0; i < TD_SNDBUF_PUBNUM; i++) { UT_Free( _pUt, _TD.pubSndBufs[i].pBuffer); }
for (i = 0; i < TD_SNDBUF_PRINUM; i++) { UT_Free( _pUt, _TD.priSndBufs[i].pBuffer); }
/************************************************************************/ /* Cleanup WinSock. */ /************************************************************************/ intRC = WSACleanup();
if (SOCKET_ERROR == intRC) { TRC_ALT((TB, _T("Failed to cleanup WinSock:%d"), WSAGetLastError())); }
#ifdef OS_WINCE
#if (_WIN32_WCE > 300)
TRC_ASSERT((_TD.hevtAddrChange), (TB, _T("hevtAddrChange is null")));
CloseHandle(_TD.hevtAddrChange); _TD.hevtAddrChange = NULL; #endif
#endif
/************************************************************************/ /* Destroy the window. */ /************************************************************************/ intRC = DestroyWindow(_TD.hWnd); _TD.hWnd = NULL;
if (0 == intRC) { TRC_SYSTEM_ERROR("Destroy Window"); }
/************************************************************************/ /* Unregister the class. */ /************************************************************************/ UnregisterClass(TD_WNDCLASSNAME, _pUi->UI_GetInstanceHandle());
/************************************************************************/ /* Release the recv buffer (if allocated). */ /************************************************************************/ if (0 != _TD.recvBuffer.size) { TRC_ASSERT((!IsBadWritePtr(_TD.recvBuffer.pData, _TD.recvBuffer.size)), (TB, _T("recv buffer %p size %u is invalid"), _TD.recvBuffer.pData, _TD.recvBuffer.size));
UT_Free( _pUt, _TD.recvBuffer.pData); _TD.recvBuffer.pData = NULL; }
TRC_NRM((TB, _T("TD successfully terminated")));
DC_END_FN(); } /* TDTerm */
LRESULT CALLBACK CTD::StaticTDWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { CTD* pTD = (CTD*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if(WM_CREATE == message) { //pull out the this pointer and stuff it in the window class
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam; pTD = (CTD*)lpcs->lpCreateParams;
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)pTD); } //
// Delegate the message to the appropriate instance
//
return pTD->TDWndProc(hwnd, message, wParam, lParam); }
/****************************************************************************/ /* Name: TDWndProc */ /* */ /* Purpose: The TD window procedure. */ /* */ /* Params: See Windows documentation. */ /****************************************************************************/ LRESULT CALLBACK CTD::TDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT rc = 0; WORD eventWSA; WORD errorWSA; u_long address;
DC_BEGIN_FN("TDWndProc");
// Trace the interesting parameters.
TRC_DBG((TB, _T("uMsg:%u wP:%u lP:%lu"), uMsg, wParam, lParam));
// Special-case FD_READ (most important) and FD_WRITE (happens often).
if (uMsg == TD_WSA_ASYNC) { if (WSAGETSELECTEVENT(lParam) == FD_READ) { TRC_DBG((TB, _T("FD_READ recvd")));
// Check for an error.
if (WSAGETSELECTERROR(lParam) == 0) { // If we're no longer connected, we just ignore the data.
if (_TD.fsmState == TD_ST_CONNECTED) { // There is now some data available so set the
// global variable.
_TD.dataInTD = TRUE;
#ifdef OS_WINCE
// Enable Winsock receive. We perform only one
// WinSock recv per FD_READ.
TD_EnableWSRecv(); #endif // OS_WINCE
// Tell XT.
_pXt->XT_OnTDDataAvailable(); } else { TRC_NRM((TB, _T("FD_READ when not connected"))); } } else { TRC_ALT((TB, _T("WSA_ASYNC error:%hu"), WSAGETSELECTERROR(lParam))); }
DC_QUIT; } else if (WSAGETSELECTEVENT(lParam) == FD_WRITE) { TRC_NRM((TB, _T("FD_WRITE received")));
// Check for an error.
if (WSAGETSELECTERROR(lParam) == 0) { // Make sure we're still connected.
if (_TD.fsmState == TD_ST_CONNECTED) { // We're on the receiver thread, notify sender
// thread to flush the send queue.
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CTD,TDFlushSendQueue), 0);
// Call up to XT to inform the higher layers
// that the back pressure situation has been
// relieved.
_pXt->XT_OnTDBufferAvailable(); } else { TRC_ALT((TB, _T("FD_WRITE when not connected"))); } } else { TRC_ALT((TB, _T("WSA_ASYNC error:%hu"), WSAGETSELECTERROR(lParam))); }
DC_QUIT; } } // Now switch on the message type for other messages.
switch (uMsg) { case WM_TIMER: /****************************************************************/ /* Check that the ID of the timer is as expected. */ /****************************************************************/ if (TD_TIMERID == wParam) { /************************************************************/ /* OK it is our connection time out timer, so call the */ /* state machine. */ /************************************************************/ TRC_NRM((TB, _T("WM_TIMER recvd"))); TDConnectFSMProc(TD_EVT_WMTIMER, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDTIMEOUT)); } #ifdef DC_DEBUG
else if (TD_THROUGHPUTTIMERID == wParam) { /************************************************************/ /* This is the throughput throttling timer. Reset the */ /* byte counts. */ /************************************************************/ TRC_DBG((TB, _T("Throughput timer, reset byte counts to:%u"), _TD.currentThroughput)); _TD.periodSendBytesLeft = _TD.currentThroughput; _TD.periodRecvBytesLeft = _TD.currentThroughput;
/************************************************************/ /* If we're connected then generate FD_READ and FD_WRITE */ /* messages to get the network layer running along. */ /************************************************************/ if (TD_ST_CONNECTED == _TD.fsmState) { PostMessage(_TD.hWnd, TD_WSA_ASYNC, (WPARAM)0, (LPARAM)MAKELONG(FD_READ, 0)); PostMessage(_TD.hWnd, TD_WSA_ASYNC, (WPARAM)0, (LPARAM)MAKELONG(FD_WRITE, 0)); } } #endif /* DC_DEBUG */
else { TRC_ALT((TB, _T("Unexpected timer message id:%u"), wParam)); } break;
case TD_WSA_ASYNC: /****************************************************************/ /* We've received a WSAAsyncSelect() FD_x notification message. */ /* Parse the message to extract the FD_ value and error value */ /* (if there is one). */ /****************************************************************/ eventWSA = WSAGETSELECTEVENT(lParam); errorWSA = WSAGETSELECTERROR(lParam);
TRC_DBG((TB, _T("WSA_ASYNC event:%#hx error:%hu"), eventWSA, errorWSA));
/****************************************************************/ /* Everything is OK so now switch on the event. */ /****************************************************************/ switch (eventWSA) {
case FD_CONNECT: TRC_NRM((TB, _T("FD_CONNECT recvd"))); /********************************************************/ /* Under some circumstances, we can receive FD_CONNECT */ /* for a socket which we have lost interest in. */ /********************************************************/ if (wParam != _TD.hSocket) { TRC_ALT((TB, _T("FD_CONNECT for socket %d, using %d"), wParam, _TD.hSocket)); DC_QUIT; }
/********************************************************/ /* Check for an error. */ /********************************************************/ if (0 != errorWSA) { TRC_ALT((TB, _T("WSA_ASYNC error:%hu"), errorWSA)); TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDSKTCONNECTFAILED)); DC_QUIT; }
/********************************************************/ /* Advance the state machine. */ /********************************************************/ TDConnectFSMProc(TD_EVT_OK, 0); break;
case FD_CLOSE: { DCBOOL keepOnReceiving = TRUE; int intRC;
TRC_NRM((TB, _T("FD_CLOSE recvd")));
/********************************************************/ /* Check for the remote system aborting the connection. */ /********************************************************/ if (0 != errorWSA) { /****************************************************/ /* The server sends a TCP RST instead of a FIN, */ /* even when a clean disconnection is made. */ /* However, this is handled by the UI (see */ /* UIGoDisconnected). */ /****************************************************/ TRC_ALT((TB, _T("Abortive server close:%hu"), errorWSA));
TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDFDCLOSE));
DC_QUIT; }
/********************************************************/ /* If we get here then this a response to a graceful */ /* close (i.e. we made a call to shutdown(SD_SEND) */ /* earlier). */ /* */ /* All of the data should have already been read from */ /* the socket before WinSock posted the FD_CLOSE, but */ /* to be safe we loop on recv. */ /********************************************************/ while (keepOnReceiving) { intRC = recv(_TD.hSocket, (char *)_TD.priSndBufs[0].pBuffer, _TD.priSndBufs[0].size, 0);
if ((0 == intRC) || (SOCKET_ERROR == intRC)) { keepOnReceiving = FALSE; TRC_ALT((TB, _T("No more data in WS (rc:%d)"), intRC)); } else { TRC_ALT((TB, _T("Throwing away %d bytes from WS"), intRC)); } }
/********************************************************/ /* Finally call the FSM. */ /********************************************************/ TDConnectFSMProc(TD_EVT_OK, NL_DISCONNECT_LOCAL); } break;
default: TRC_ALT((TB, _T("Unknown FD event %hu recvd"), eventWSA)); break; } break;
case TD_WSA_GETHOSTBYNAME: /****************************************************************/ /* We've received the result of a WSAAsyncGetHostByName */ /* operation. Split the message apart and call the FSM. */ /****************************************************************/ errorWSA = WSAGETASYNCERROR(lParam);
if (0 != errorWSA) { TRC_ALT((TB, _T("GHBN failed:%hu"), errorWSA));
/************************************************************/ /* Call the state machine with the error event. */ /************************************************************/ TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDGHBNFAILED)); break; }
/****************************************************************/ /* Now get the primary interface address. */ /****************************************************************/ address = *((u_long DCPTR) (((struct hostent DCPTR)_TD.priSndBufs[0].pBuffer)->h_addr));
TRC_ASSERT((address != 0), (TB, _T("GetHostByName returned success but 0 address")));
TRC_NRM((TB, _T("GHBN - address is:%#lx"), address));
TDConnectFSMProc(TD_EVT_OK, (DCUINT32)address); break;
#if (defined(OS_WINCE) && (_WIN32_WCE > 300))
case TD_WSA_NETDOWN: TRC_NRM((TB, _T("TD_WSA_NETDOWN recvd"))); TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDONCALLTOSEND)); break; #endif
default: rc = DefWindowProc(hWnd, uMsg, wParam, lParam); break; }
DC_EXIT_POINT: DC_END_FN(); return(rc); } /* TDWndProc */
/****************************************************************************/ /* Name: TDCreateWindow */ /* */ /* Purpose: Creates the TD window. This function registers the TD */ /* window class and then creates a window of that class. On */ /* error it calls UI_FatalError. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDCreateWindow(DCVOID) { WNDCLASS wc; WNDCLASS tmpWndClass; ATOM intRC;
DC_BEGIN_FN("TDCreateWindow");
if(!GetClassInfo(_pUi->UI_GetInstanceHandle(),TD_WNDCLASSNAME, &tmpWndClass)) { /************************************************************************/ /* Fill in the class structure. */ /************************************************************************/ wc.style = 0; wc.lpfnWndProc = StaticTDWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(void*); //for instance pointer
wc.hInstance = _pUi->UI_GetInstanceHandle(); wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = TD_WNDCLASSNAME; /************************************************************************/ /* Register the class used by the TD window. */ /************************************************************************/ intRC = RegisterClass(&wc);
if (0 == intRC) { TRC_ERR((TB, _T("Failed to register WinSock window class"))); _pUi->UI_FatalError(DC_ERR_CLASSREGISTERFAILED); DC_QUIT; } }
/************************************************************************/ /* Now create the window. */ /************************************************************************/ _TD.hWnd = CreateWindow(TD_WNDCLASSNAME, /* class name */ NULL, /* window title */ 0, /* window style */ 0, /* x-pos */ 0, /* y-pos */ 0, /* width */ 0, /* height */ NULL, /* parent */ NULL, /* menu */ _pUi->UI_GetInstanceHandle(), /* instance */ this); /* ptr to creation data */
if (NULL == _TD.hWnd) { TRC_ERR((TB, _T("Failed to create TD window"))); _pUi->UI_FatalError(DC_ERR_WINDOWCREATEFAILED); DC_QUIT; }
TRC_NRM((TB, _T("Created window:%p"), _TD.hWnd));
DC_EXIT_POINT: DC_END_FN(); } /* TDCreateWindow */
/****************************************************************************/ /* Name: TDBeginDNSLookup */ /* */ /* Purpose: Starts the address resolution process. On error this */ /* function calls into the state machine with an error code. */ /* */ /* Params: IN pServerAddress - pointer to the server address name. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDBeginDNSLookup(PDCACHAR pServerAddress) { DC_BEGIN_FN("TDBeginDNSLookup");
/************************************************************************/ /* This is an asynchronous operation and will result in us getting a */ /* callback sometime later. We need to provide a buffer which can be */ /* filled in with DNS information, so we make use of the first private */ /* send buffer. */ /************************************************************************/ TRC_ASSERT((_TD.priSndBufs[0].size >= MAXGETHOSTSTRUCT), (TB, _T("Private snd buf size (%u) too small for DNS lookup (need:%u)"), _TD.priSndBufs[0].size, MAXGETHOSTSTRUCT));
/************************************************************************/ /* Issue the call. */ /************************************************************************/ _TD.hGHBN = WSAAsyncGetHostByName(_TD.hWnd, TD_WSA_GETHOSTBYNAME, pServerAddress, (char DCPTR) _TD.priSndBufs[0].pBuffer, MAXGETHOSTSTRUCT); if (0 == _TD.hGHBN) { /********************************************************************/ /* We failed to initiate the operation - so find out what went */ /* wrong. */ /********************************************************************/ TRC_ALT((TB, _T("Failed to initiate GetHostByName - GLE:%d"), WSAGetLastError()));
/********************************************************************/ /* Call the state machine with an error. */ /********************************************************************/ TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDDNSLOOKUPFAILED)); DC_QUIT; }
TRC_NRM((TB, _T("Initiated GetHostByName OK")));
DC_EXIT_POINT: DC_END_FN(); } /* TDBeginDNSLookup */
/****************************************************************************/ /* Name: TDBeginSktConnectWithConnectedEndpoint */ /* */ /* Purpose: Establish connection with server already connect */ /* on some socket */ /* */ /* Params: */ /* */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDBeginSktConnectWithConnectedEndpoint() { DCBOOL failure = FALSE; #ifndef OS_WINCE
int lastError; #endif
int intRC;
DC_BEGIN_FN("TDBeginSktConnectWithConnectedEndpoint");
/************************************************************************/ /* Socket already connect, setup FD_XXX event with our window */ /* we are assuming mstscax client already make necessary error checking */ /************************************************************************/ _TD.hSocket = _pUi->UI_GetTDSocket();
TRC_ASSERT( (_TD.hSocket != INVALID_SOCKET), (TB, _T("Connected socket not setup properly")) );
if( INVALID_SOCKET == _TD.hSocket ) { failure = TRUE; DC_QUIT; }
/************************************************************************/ /* Set the required options on this socket. We do the following: */ /* */ /* - disable the NAGLE algorithm. */ /* - enable the don't linger option. This means the closesocket call */ /* will return immediately while any data queued for transmission */ /* will be sent, if possible, before the underlying socket is */ /* closed. */ /* */ /* Note that further options are set when the connection is */ /* established. */ /************************************************************************/ TDSetSockOpt(IPPROTO_TCP, TCP_NODELAY, 1); TDSetSockOpt(SOL_SOCKET, SO_DONTLINGER, 1);
/************************************************************************/ /* Now request async notifications for all events on this socket. */ /************************************************************************/ intRC = WSAAsyncSelect(_TD.hSocket, _TD.hWnd, TD_WSA_ASYNC, FD_READ | FD_WRITE | FD_CLOSE);
if (SOCKET_ERROR == intRC) { TRC_ERR((TB, _T("Failed to select async - GLE:%d"), WSAGetLastError())); failure = TRUE; DC_QUIT; }
DC_EXIT_POINT: if (failure) { TRC_ALT((TB, _T("Failed to begin socket connection process")));
/********************************************************************/ /* Call the FSM. */ /********************************************************************/ TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDSKTCONNECTFAILED)); } else { // use existing code path to setup rest.
PostMessage(_TD.hWnd, TD_WSA_ASYNC, (WPARAM) _TD.hSocket, (LPARAM)MAKELONG(FD_CONNECT, 0)); }
DC_END_FN(); } /* TDBeginSktConnectWithConnectedEndpoint */
/****************************************************************************/ /* Name: TDBeginSktConnect */ /* */ /* Purpose: Issues a connect at the WinSock socket level. */ /* */ /* Params: IN address - the address to call (this is a numeric value */ /* in network (big-endian) byte order). */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDBeginSktConnect(u_long address) { DCBOOL failure = FALSE; int intRC; int lastError; SOCKADDR_IN stDstAddr;
DC_BEGIN_FN("TDBeginSktConnect");
/************************************************************************/ /* First of all get a socket. */ /************************************************************************/ _TD.hSocket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == _TD.hSocket) { TRC_ERR((TB, _T("Failed to get a socket - GLE:%d"), WSAGetLastError())); DC_QUIT; }
_pUi->UI_SetTDSocket(_TD.hSocket);
TRC_NRM((TB, _T("Acquired socket:%#x"), _TD.hSocket));
/************************************************************************/ /* Set the required options on this socket. We do the following: */ /* */ /* - disable the NAGLE algorithm. */ /* - enable the don't linger option. This means the closesocket call */ /* will return immediately while any data queued for transmission */ /* will be sent, if possible, before the underlying socket is */ /* closed. */ /* */ /* Note that further options are set when the connection is */ /* established. */ /************************************************************************/ TDSetSockOpt(IPPROTO_TCP, TCP_NODELAY, 1); TDSetSockOpt(SOL_SOCKET, SO_DONTLINGER, 1);
/************************************************************************/ /* Now request async notifications for all events on this socket. */ /************************************************************************/ intRC = WSAAsyncSelect(_TD.hSocket, _TD.hWnd, TD_WSA_ASYNC, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
if (SOCKET_ERROR == intRC) { TRC_ERR((TB, _T("Failed to select async - GLE:%d"), WSAGetLastError())); failure = TRUE; DC_QUIT; }
/************************************************************************/ /* Now kick off a timer - if the connect does not complete before we */ /* get the WM_TIMER message then we'll abort the connection attempt. */ /************************************************************************/ /* _TD.hTimer = TDSetTimer(TD_CONNECTTIMEOUT);
if (0 == _TD.hTimer) { failure = TRUE; DC_QUIT; } */ /************************************************************************/ /* Fill in the address of the remote system we want to connect to. */ /************************************************************************/ stDstAddr.sin_family = PF_INET; stDstAddr.sin_port = htons(_pUi->UI_GetMCSPort()); stDstAddr.sin_addr.s_addr = (u_long) address;
#ifdef OS_WINCE
#if (_WIN32_WCE > 300)
TRC_ASSERT((_TD.hevtAddrChange), (TB, _T("hevtAddrChange is null"))); TRC_ASSERT((_TD.hAddrChangeThread == NULL), (TB, _T("hAddrChangeThread is not null"))); _TD.hAddrChangeThread = CreateThread(NULL, 0, TDAddrChangeProc, &_TD, 0, NULL); if (_TD.hAddrChangeThread == NULL) { TRC_ERR((TB, _T("CreatThread failed - GLE:%d"), GetLastError())); failure = TRUE; DC_QUIT; }
#endif
#endif
/************************************************************************/ /* We're now in a state where we can try to connect to the remote */ /* system so issue the connect now. We expect this call to fail with */ /* an error code of WSAEWOULDBLOCK - any other error code is a genuine */ /* problem. */ /************************************************************************/ intRC = connect(_TD.hSocket, (struct sockaddr DCPTR) &stDstAddr, sizeof(stDstAddr));
if (SOCKET_ERROR == intRC) { /********************************************************************/ /* Get the last error. */ /********************************************************************/ lastError = WSAGetLastError();
/********************************************************************/ /* We expect the connect to return an error of WSAEWOULDBLOCK - */ /* anything else indicates a genuine error. */ /********************************************************************/ if (lastError != WSAEWOULDBLOCK) { TRC_ERR((TB, _T("Connect failed - GLE:%d"), lastError)); failure = TRUE; DC_QUIT; } }
/************************************************************************/ /* We've done as much as we can at this point - all we can do now is */ /* wait for the socket connection to complete. */ /************************************************************************/ TRC_NRM((TB, _T("Waiting for connect to complete...")));
DC_EXIT_POINT: if (failure) { TRC_ALT((TB, _T("Failed to begin socket connection process")));
/********************************************************************/ /* Call the FSM. */ /********************************************************************/ TDConnectFSMProc(TD_EVT_ERROR, NL_MAKE_DISCONNECT_ERR(NL_ERR_TDSKTCONNECTFAILED)); }
DC_END_FN(); } /* TDBeginSktConnect */
/****************************************************************************/ /* Name: TDSetSockOpt */ /* */ /* Purpose: Sets a given WinSock socket option. Note that this function */ /* does not return an error if it fails to set the option as */ /* TD can still continue successfully despite failing to set */ /* the options to the desired values. */ /* */ /* Params: IN level - the level at which the option is defined (see */ /* docs for setsockopt). */ /* IN optName - the socket option for which the value is to be */ /* set. */ /* IN value - the value to set the option to. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDSetSockOpt(DCINT level, DCINT optName, DCINT value) { int intRC; DCINT size = sizeof(DCINT); #ifdef DC_DEBUG
DCINT oldVal; DCINT newVal; #endif /* DC_DEBUG */
DC_BEGIN_FN("TDSetSockOpt");
#ifdef DC_DEBUG
/************************************************************************/ /* For the debug build trace out the current value of the option */ /* before setting it. */ /************************************************************************/ getsockopt(_TD.hSocket, level, optName, (char DCPTR) &oldVal, &size); #endif /* DC_DEBUG */
/************************************************************************/ /* Now set the option. */ /************************************************************************/ intRC = setsockopt(_TD.hSocket, level, optName, (char DCPTR) &value, size);
if (SOCKET_ERROR == intRC) { TRC_ALT((TB, _T("Failed to set socket option:%d rc:%d (level:%d val:%d)"), optName, WSAGetLastError(), level, value)); DC_QUIT; }
#ifdef DC_DEBUG
/************************************************************************/ /* Get the new value of the option. */ /************************************************************************/ getsockopt(_TD.hSocket, level, optName, (char DCPTR) &newVal, &size);
TRC_NRM((TB, _T("Mod socket option %d:%d from %d to %d"), level, optName, oldVal, newVal)); #endif /* DC_DEBUG */
DC_EXIT_POINT: DC_END_FN(); } /* TDSetSockOpt */
/****************************************************************************/ /* Name: TDDisconnect */ /* */ /* Purpose: Disconnects the transport driver. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDDisconnect(DCVOID) { int intRC; SOCKET socket;
DC_BEGIN_FN("TDDisconnect");
/************************************************************************/ /* Kill the timer. */ /************************************************************************/ TDKillTimer();
/************************************************************************/ /* Ensure the data-in-TD flag is cleared. */ /************************************************************************/ _TD.dataInTD = FALSE;
/************************************************************************/ /* Cancel the outstanding DNS lookup. We can't be sure that the async */ /* operation has completed already and the message is already sitting */ /* on our queue (or being processed by the receive thread). If that is */ /* the case then WSACancelAsyncRequest will fail, but it doesn't */ /* matter. */ /************************************************************************/ intRC = WSACancelAsyncRequest(_TD.hGHBN); if (SOCKET_ERROR == intRC) { TRC_NRM((TB, _T("Failed to cancel async DNS request"))); }
/************************************************************************/ /* Decouple to the sender thread and clear the send queue. */ /************************************************************************/ _pCd->CD_DecoupleSyncNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CTD,TDClearSendQueue), 0);
#ifdef OS_WINCE
#if (_WIN32_WCE > 300)
SetEvent(_TD.hevtAddrChange);
if (_TD.hAddrChangeThread) { WaitForSingleObject(_TD.hAddrChangeThread, INFINITE); CloseHandle(_TD.hAddrChangeThread); _TD.hAddrChangeThread = NULL; } ResetEvent(_TD.hevtAddrChange); #endif
#endif
if (INVALID_SOCKET == _TD.hSocket) { TRC_NRM((TB, _T("_TD.hSocket is NULL so just quit"))); DC_QUIT; }
/************************************************************************/ /* Now close the socket. */ /************************************************************************/ TRC_NRM((TB, _T("Close the socket")));
socket = _TD.hSocket; _TD.hSocket = INVALID_SOCKET; intRC = closesocket(socket); if (SOCKET_ERROR == intRC) { TRC_ALT((TB, _T("closesocket rc:%d"), WSAGetLastError())); }
DC_EXIT_POINT: DC_END_FN(); } /* TDDisconnect */
/****************************************************************************/ /* Name: TDSetTimer */ /* */ /* Purpose: Sets the timer. */ /* */ /* Returns: TRUE on success and FALSE otherwise. */ /* */ /* Params: IN timeInterval - the time interval of the timer. */ /****************************************************************************/ DCBOOL DCINTERNAL CTD::TDSetTimer(DCUINT timeInterval) { DCBOOL rc;
DC_BEGIN_FN("TDSetTimer");
/************************************************************************/ /* Set the timer with the passed time interval. */ /************************************************************************/ _TD.hTimer = SetTimer(_TD.hWnd, TD_TIMERID, timeInterval, NULL); if (_TD.hTimer != 0) { // Everything went OK, so set a successful return code.
rc = TRUE; TRC_NRM((TB, _T("Set timer with interval:%u"), timeInterval)); } else { TRC_SYSTEM_ERROR("SetTimer"); rc = FALSE; }
DC_END_FN(); return rc; } /* TDSetTimer */
/****************************************************************************/ /* Name: TDKillTimer */ /* */ /* Purpose: Cleans up the timer which is used to time-out connect */ /* and disconnect attempts. */ /****************************************************************************/ DCVOID DCINTERNAL CTD::TDKillTimer(DCVOID) { BOOL rc;
DC_BEGIN_FN("TDKillTimer");
/************************************************************************/ /* Destroy the connection timeout timer. If we fail to get rid of this */ /* timer then there's not much we can do - we'll continue to get */ /* WM_TIMER messages which we'll ignore in retail and assert on in the */ /* debug version. */ /************************************************************************/ if (_TD.hTimer != 0) { rc = KillTimer(_TD.hWnd, TD_TIMERID); _TD.hTimer = 0;
TRC_NRM((TB, _T("Timer has %s been killed"), rc ? _T("") : _T(" NOT"))); } else { TRC_NRM((TB, _T("Timer has not been set"))); }
DC_END_FN(); } /* TDKillTimer */
/****************************************************************************/ // TDFlushSendQueue
//
// Tries to send all the packets which are waiting in the send queue.
// Must be called on the sender thread, Can be called through direct calls
// or through a CD_Decouple call.
/****************************************************************************/ void DCINTERNAL CTD::TDFlushSendQueue(ULONG_PTR unused) { DCUINT bytesSent; PTD_SNDBUF_INFO pOldBuf; int bytesToSend; DCBOOL sentABuffer = FALSE; int WSAErr;
DC_BEGIN_FN("TDFlushSendQueue");
DC_IGNORE_PARAMETER(unused);
// Check for rare re-entrancy.
if (!_TD.inFlushSendQueue) { _TD.inFlushSendQueue = TRUE;
// Check that there are some buffers waiting to be sent.
if (_TD.pFQBuf != NULL) { // Run along the send queue and try to send the data.
// Check for buffers that are inUse because ClearSendQueue could
// potentially clear some buffers before this call comes in
//
while (NULL != _TD.pFQBuf && _TD.pFQBuf->inUse) { // Check that the buffer is in use and that the count of
// bytes waiting to be sent is more than zero.
TRC_ASSERT((_TD.pFQBuf->inUse), (TB, _T("Buffer is not in use"))); TRC_ASSERT((_TD.pFQBuf->bytesLeftToSend > 0), (TB, _T("No bytes waiting to be sent")));
// Trace out the send buffer information.
TD_TRACE_SENDINFO(TRC_LEVEL_DBG);
TRC_DBG((TB, _T("Sending buffer:%p (waiting:%u)"), _TD.pFQBuf, _TD.pFQBuf->bytesLeftToSend));
// Call WinSock to send the buffer. We expect the call to:
// - succeed and send all the bytes requested. In this case
// there is little for us to do.
// - send some of the bytes we asked to be sent. This
// indicates that WinSock is applying back-pressure to us,
// so we update our count of bytes sent for this buffer
// and then quit. We will get a FD_WRITE later and retry
// the send.
// - send none of the bytes that we asked to be sent and
// return SOCKET_ERROR instead. We then use
// WSAGetLastError to determine why the call failed. If
// the reason is WSAEWOULDBLOCK then WinSock has decided
// to fail the call due to back-pressure - this is fine
// by us, so we just quit. Once again we will get an
// FD_WRITE to tell us that back-pressure has been
// relieved. Any other reason code is a genuine error
// so we decouple a call into the state table with an
// error code.
#ifdef DC_DEBUG
// Calculate how many bytes we can send and then decrement
// the count of bytes left to send in this period.
if (0 == _TD.hThroughputTimer) { bytesToSend = (int)_TD.pFQBuf->bytesLeftToSend; } else { bytesToSend = (int) DC_MIN(_TD.pFQBuf->bytesLeftToSend, _TD.periodSendBytesLeft); TRC_DBG((TB, _T("periodSendBytesLeft:%u"), _TD.periodSendBytesLeft)); if (0 == bytesToSend) { TRC_ALT((TB, _T("Constrained SEND network throughput"))); }
_TD.periodSendBytesLeft -= bytesToSend; } #else
bytesToSend = (int)_TD.pFQBuf->bytesLeftToSend; #endif
bytesSent = (DCUINT)send(_TD.hSocket, (char *)_TD.pFQBuf->pDataLeftToSend, bytesToSend, 0); if (SOCKET_ERROR != bytesSent) { TRC_DBG((TB, _T("Sent %u bytes of %u waiting"), bytesSent, _TD.pFQBuf->bytesLeftToSend));
// Update the performance counter.
PRF_ADD_COUNTER(PERF_BYTES_SENT, bytesSent);
// Update the count of bytesWaiting and shuffle the
// pointer to the data along as well.
_TD.pFQBuf->pDataLeftToSend += bytesSent; _TD.pFQBuf->bytesLeftToSend -= bytesSent;
// Check to determine if we managed to send all the data.
if (_TD.pFQBuf->bytesLeftToSend == 0) { // We managed to send all the data in this buffer -
// so it is no longer in use. Get a pointer to this
// buffer.
pOldBuf = _TD.pFQBuf;
// Now update the head of the send queue with the
// next buffer and reset the next field of the buffer
// we've just sent.
_TD.pFQBuf = pOldBuf->pNext;
// Finally update the fields in the old buffer.
pOldBuf->pNext = NULL; pOldBuf->inUse = FALSE; pOldBuf->pDataLeftToSend = NULL; sentABuffer = TRUE;
// Update the performance counter.
PRF_INC_COUNTER(PERF_PKTS_FREED);
TRC_DBG((TB, _T("Sent buffer completely - move to next"))); } else { // We didn't manage to send all the data so trace and
// quit.
TRC_NRM((TB, _T("Didn't send all data in buffer - quit"))); DC_QUIT; } } else { WSAErr = WSAGetLastError();
if (WSAErr == WSAEWOULDBLOCK || WSAErr == WSAENOBUFS) { // WSAEWOULDBLOCK means that the network system is out
// of buffer space so we should wait until we receive
// a FD_WRITE notification indicating that more buffer
// space is available.
//
// WSAENOBUFS means that no buffer space is available
// and indicates a shortage of resources on the
// system.
bytesSent = 0; PRF_INC_COUNTER(PERF_WINSOCK_SEND_FAIL); TRC_NRM((TB, _T("WinSock send returns WSAEWOULDBLOCK")));
// We haven't sent any data, time to get out.
DC_QUIT; } else { bytesSent = 0;
// If this is not a WSAEWOULDBLOCK and it is not a
// WSAENOBUFS error then call the FSM to begin
// disconnect processing.
// Trace out the buffer structure.
TD_TRACE_SENDINFO(TRC_LEVEL_ALT);
// We failed to send any data and the socket returned
// an error. The connection has probably failed or
// ended.
TRC_ALT((TB, _T("Failed to send any data, rc:%d"), WSAErr));
// Decouple across to the recv side event handler at
// this point. It will call the TD FSM.
_pCd->CD_DecoupleSimpleNotification(CD_RCV_COMPONENT, this, CD_NOTIFICATION_FUNC(CTD,TDSendError), 0);
DC_QUIT; } } } } else { TRC_NRM((TB, _T("No buffers waiting to be sent"))); } } else { TRC_ABORT((TB, _T("Re-entered TDFlushSendQueue"))); goto RealExit; }
DC_EXIT_POINT: _TD.inFlushSendQueue = FALSE;
// If we previously failed TD_GetPublicBuffer, and we just
// succeeded in sending a buffer, call the OnBufferAvailable
// callbacks now.
TRC_DBG((TB, _T("Sent a buffer? %d, GetBuffer failed? %d"), sentABuffer, _TD.getBufferFailed)); if (sentABuffer && _TD.getBufferFailed) { TRC_NRM((TB, _T("Signal buffer available"))); _pXt->XT_OnTDBufferAvailable(); _TD.getBufferFailed = FALSE; }
RealExit: DC_END_FN(); } /* TDFlushSendQueue */
|