Windows2003-3790/termsrv/newclient/core/wtdint.cpp
2020-09-30 16:53:55 +02:00

1367 lines
59 KiB
C++

/****************************************************************************/
// 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 */