Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2023 lines
54 KiB

/*
* asynconn.cpp
*
* Purpose:
* implementation of the async connection class
*
* Owner:
* EricAn
*
* History:
* Apr 96: Created.
*
* Copyright (C) Microsoft Corp. 1996
*/
#include <pch.hxx>
#include <process.h>
#include "imnxport.h"
#include "dllmain.h"
#include "asynconn.h"
#include "thorsspi.h"
#include "resource.h"
#include "strconst.h"
#include "lookup.h"
#include <demand.h>
#include <shlwapi.h>
ASSERTDATA
#define STREAM_BUFSIZE 8192
#define FLOGSESSION (m_pLogFile && TRUE /* profile setting to enable logging should be here */)
// These are the notification messages that we register for the asynchronous
// socket operations that we use.
#define SPM_WSA_SELECT (WM_USER + 1)
// Async Timer Message used for doing Timeouts
#define SPM_ASYNCTIMER (WM_USER + 3)
#ifdef DEBUG
#define EnterCS(_pcs) \
{ \
EnterCriticalSection(_pcs); \
m_cLock++; \
IxpAssert(m_cLock > 0); \
}
#define LeaveCS(_pcs) \
{ \
m_cLock--; \
IxpAssert(m_cLock >= 0); \
LeaveCriticalSection(_pcs); \
}
#else
#define EnterCS(_pcs) \
EnterCriticalSection(_pcs);
#define LeaveCS(_pcs) \
LeaveCriticalSection(_pcs);
#endif
BOOL FEndLine(char *psz, int iLen);
static const char s_szConnWndClass[] = "ThorConnWndClass";
extern LPSRVIGNORABLEERROR g_pSrvErrRoot;
// This function try to find server and ignorable error, assigned to this server
// if not found then add to list and set ignorable error to S_OK
LPSRVIGNORABLEERROR FindOrAddServer(TCHAR * pchServerName, LPSRVIGNORABLEERROR pSrvErr, LPSRVIGNORABLEERROR *ppSrv)
{
int i = 0;
// if we already had entry in tree then recurse search
if(pSrvErr)
{
i = lstrcmpi(pchServerName, pSrvErr->pchServerName);
if(i > 0)
{
pSrvErr->pRight = FindOrAddServer(pchServerName, pSrvErr->pRight, ppSrv);
return(pSrvErr);
}
else if(i < 0)
{
pSrvErr->pLeft = FindOrAddServer(pchServerName, pSrvErr->pLeft, ppSrv);
return(pSrvErr);
}
else
{
*ppSrv = pSrvErr;
return(pSrvErr);
}
}
// if we don't have node, create it
i = lstrlen(pchServerName);
// if server name is empty return
if(i == 0)
return(NULL);
// Allocate memory for structure
if (!MemAlloc((LPVOID*)&pSrvErr, sizeof(SRVIGNORABLEERROR)))
return(NULL);
pSrvErr->pRight = NULL;
pSrvErr->pLeft = NULL;
pSrvErr->hrError = S_OK;
if (!MemAlloc((LPVOID*)&(pSrvErr->pchServerName), (i+1) * sizeof(pSrvErr->pchServerName[0]) ))
{
MemFree(pSrvErr);
return(NULL);
}
StrCpyN(pSrvErr->pchServerName, pchServerName, (i+1));
*ppSrv = pSrvErr;
return(pSrvErr);
}
void FreeSrvErr(LPSRVIGNORABLEERROR pSrvErr)
{
// if structure NULL return immediately
if(!pSrvErr)
return;
FreeSrvErr(pSrvErr->pRight);
FreeSrvErr(pSrvErr->pLeft);
if(pSrvErr->pchServerName)
{
MemFree(pSrvErr->pchServerName);
pSrvErr->pchServerName = NULL;
}
MemFree(pSrvErr);
pSrvErr = NULL;
}
/////////////////////////////////////////////////////////////////////////////
//
// PUBLIC METHODS - these need to be synchronized as they are accessed by the
// owning thread and the asynchronous socket pump thread.
//
/////////////////////////////////////////////////////////////////////////////
CAsyncConn::CAsyncConn(ILogFile *pLogFile, IAsyncConnCB *pCB, IAsyncConnPrompt *pPrompt)
{
m_cRef = 1;
m_chPrev = '\0';
m_fStuffDots = FALSE;
m_sock = INVALID_SOCKET;
m_fLookup = FALSE;
m_state = AS_DISCONNECTED;
m_fCachedAddr = FALSE;
m_fRedoLookup = FALSE;
m_pszServer = NULL;
m_iDefaultPort = 0;
m_iLastError = 0;
InitializeCriticalSection(&m_cs);
m_pLogFile = pLogFile;
if (m_pLogFile)
m_pLogFile->AddRef();
Assert(pCB);
m_pCB = pCB;
m_cbQueued = 0;
m_lpbQueued = m_lpbQueueCur = NULL;
m_pStream = NULL;
m_pRecvHead = m_pRecvTail = NULL;
m_iRecvOffset = 0;
m_fNeedRecvNotify = FALSE;
m_hwnd = NULL;
m_fNegotiateSecure = FALSE;
m_fSecure = FALSE;
ZeroMemory(&m_hContext, sizeof(m_hContext));
m_iCurSecPkg = 0; // current security package being tried
m_pbExtra = NULL;
m_cbExtra = 0;
m_cbSent = 0;
m_pPrompt = pPrompt;
m_dwLastActivity = 0;
m_dwTimeout = 0;
m_uiTimer = 0;
#ifdef DEBUG
m_cLock = 0;
#endif
m_fPaused = FALSE;
m_dwEventMask = 0;
}
CAsyncConn::~CAsyncConn()
{
DOUT("CAsyncConn::~CAsyncConn %lx: m_cRef = %d", this, m_cRef);
// Bug #22622 - We need to make sure there isn't a timer pending
StopWatchDog();
Assert(!m_fLookup);
SafeMemFree(m_pszServer);
SafeRelease(m_pLogFile);
CleanUp();
if ((NULL != m_hwnd) && (FALSE != IsWindow(m_hwnd)))
SendMessage(m_hwnd, WM_CLOSE, 0, 0);
DeleteCriticalSection(&m_cs);
#ifdef DEBUG
IxpAssert(m_cLock == 0);
#endif
}
ULONG CAsyncConn::AddRef(void)
{
ULONG cRefNew;
EnterCS(&m_cs);
DOUT("CAsyncConn::AddRef %lx ==> %d", this, m_cRef+1);
cRefNew = ++m_cRef;
LeaveCS(&m_cs);
return cRefNew;
}
ULONG CAsyncConn::Release(void)
{
ULONG cRefNew;
EnterCS(&m_cs);
DOUT("CAsyncConn::Release %lx ==> %d", this, m_cRef-1);
cRefNew = --m_cRef;
LeaveCS(&m_cs);
if (cRefNew == 0)
delete this;
return cRefNew;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::HrInit
//
// Allocates recv buffer, sets servername, servicename, and port
//
HRESULT CAsyncConn::HrInit(char *szServer, int iDefaultPort, BOOL fSecure, DWORD dwTimeout)
{
HRESULT hr = NOERROR;
EnterCS(&m_cs);
if (!m_hwnd && !CreateWnd())
{
hr = E_FAIL;
goto error;
}
if (m_state != AS_DISCONNECTED)
{
hr = IXP_E_ALREADY_CONNECTED;
goto error;
}
Assert(szServer);
// if nothing has changed, then use the current settings
if (m_pszServer &&
!lstrcmpi(m_pszServer, szServer) &&
(iDefaultPort == m_iDefaultPort) &&
(fSecure == m_fNegotiateSecure))
goto error;
m_fCachedAddr = FALSE;
m_fRedoLookup = FALSE;
SafeMemFree(m_pszServer);
DWORD cchSize = (lstrlen(szServer)+1);
if (!MemAlloc((LPVOID*)&m_pszServer, cchSize * sizeof(m_pszServer[0])))
{
hr = E_OUTOFMEMORY;
goto error;
}
StrCpyN(m_pszServer, szServer, cchSize);
Assert(iDefaultPort > 0);
m_iDefaultPort = (u_short) iDefaultPort;
m_fNegotiateSecure = fSecure;
// If dwTimeout == 0, no timeout detection will be installed.
m_dwTimeout = dwTimeout;
error:
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::SetWindow
//
// creates a window used by async. winsock. ResetWindow()
// must be called before invoking this function, so as to avoid
// window handle leakage.
//
HRESULT CAsyncConn::SetWindow(void)
{
HRESULT hr = NOERROR;
EnterCS(&m_cs);
if (NULL != m_hwnd && IsWindow(m_hwnd) &&
GetWindowThreadProcessId(m_hwnd, NULL) == GetCurrentThreadId())
{
// no need to create the new window for this thread
goto error;
}
else if (NULL != m_hwnd && IsWindow(m_hwnd))
{
// leaks one window handle; the previous worker thread
// didn't call ResetWindow().
Assert(FALSE);
}
if (!CreateWnd())
{
hr = E_FAIL;
goto error;
}
if (m_sock != INVALID_SOCKET)
{
if (SOCKET_ERROR == WSAAsyncSelect(m_sock, m_hwnd, SPM_WSA_SELECT, FD_READ|FD_WRITE|FD_CLOSE))
{
m_iLastError = WSAGetLastError();
hr = IXP_E_CONN;
goto error;
}
}
error:
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::ResetWindow
//
// closes the window used by async. winsock
//
HRESULT CAsyncConn::ResetWindow(void)
{
HRESULT hr = NOERROR;
EnterCS(&m_cs);
if ((NULL == m_hwnd) || (FALSE == IsWindow(m_hwnd)))
goto error;
if (GetWindowThreadProcessId(m_hwnd, NULL) == GetCurrentThreadId())
{
SendMessage(m_hwnd, WM_CLOSE, 0, 0);
m_hwnd = NULL;
}
else
{
// A caller forgot to call ResetWindow. Only the owner thread can destroy
// the window.
Assert(FALSE);
}
error:
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::Connect
//
// starts the name lookup and connection process
//
HRESULT CAsyncConn::Connect()
{
HRESULT hr;
HANDLE hThreadLookup;
BOOL fNotify = FALSE;
BOOL fAsync = FALSE;
EnterCS(&m_cs);
if (m_state != AS_DISCONNECTED && m_state != AS_RECONNECTING)
{
hr = IXP_E_ALREADY_CONNECTED;
goto error;
}
Assert(m_pszServer);
if (FLOGSESSION)
{
char szBuffer[512], lb[256];
if (LoadString(g_hLocRes, idsNlogIConnect, lb, 256))
{
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_pszServer, m_iDefaultPort);
m_pLogFile->DebugLog(szBuffer);
}
}
ZeroMemory(&m_sa, sizeof(m_sa));
m_sa.sin_port = htons(m_iDefaultPort);
m_sa.sin_family = AF_INET;
m_sa.sin_addr.s_addr = inet_addr(m_pszServer);
if (m_sa.sin_addr.s_addr != -1)
// server name is dotted decimal, so no need to look it up
fAsync = TRUE;
else
{
// Start a name lookup on a separate thread because WinSock caches the DNS server in TLS.
// The separate thread enables us to connect to a LAN DNS and a RAS DNS in the same session.
hr = LookupHostName(m_pszServer, m_hwnd, &(m_sa.sin_addr.s_addr), &m_fCachedAddr, m_fRedoLookup);
if (SUCCEEDED(hr))
{
m_fLookup = fNotify = !m_fCachedAddr;
fAsync = m_fCachedAddr;
}
else
{
m_iLastError = WSAENOBUFS;
hr = IXP_E_CONN;
}
}
error:
LeaveCS(&m_cs);
if (fAsync)
hr = AsyncConnect();
if (fNotify)
ChangeState(AS_LOOKUPINPROG, AE_NONE);
return hr;
}
void CAsyncConn::StartWatchDog(void)
{
if (m_dwTimeout < 5) m_dwTimeout = 30;
m_dwLastActivity = GetTickCount();
Assert(m_hwnd);
StopWatchDog();
m_uiTimer = SetTimer(m_hwnd, SPM_ASYNCTIMER, 5000, NULL);
}
void CAsyncConn::StopWatchDog(void)
{
if (m_uiTimer)
{
KillTimer(m_hwnd, SPM_ASYNCTIMER);
m_uiTimer = 0;
}
}
void CAsyncConn::OnWatchDogTimer(void)
{
BOOL fNotify = FALSE;
ASYNCSTATE as;
EnterCS(&m_cs);
if (((GetTickCount() - m_dwLastActivity) / 1000) >= m_dwTimeout)
{
fNotify = TRUE;
as = m_state;
}
LeaveCS(&m_cs);
if (fNotify)
ChangeState(as, AE_TIMEOUT);
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::Close
//
// closes the connection
//
HRESULT CAsyncConn::Close()
{
BOOL fNotify = FALSE;
BOOL fClose = FALSE;
EnterCS(&m_cs);
if (m_fLookup)
{
CancelLookup(m_pszServer, m_hwnd);
m_fLookup = FALSE;
fNotify = TRUE;
}
fClose = (m_sock != INVALID_SOCKET);
LeaveCS(&m_cs);
if (fNotify)
ChangeState(AS_DISCONNECTED, AE_LOOKUPDONE);
if (fClose)
OnClose(AS_DISCONNECTED);
return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::ReadLine
//
// Purpose: retrieves a single complete line from the buffered data
//
// Args: ppszBuf - pointer to receive allocated buffer, caller must free
// pcbRead - pointer to receive line length
//
// Returns: NOERROR - a complete line was read
// IXP_E_INCOMPLETE - a complete line is not available
// E_OUTOFMEMORY - mem error
//
// Comments:
// If IXP_E_INCOMPLETE is returned, the caller will recieve an AE_RECV event
// the next time a complete line is available.
//
HRESULT CAsyncConn::ReadLine(char **ppszBuf, int *pcbRead)
{
HRESULT hr;
int iLines;
EnterCS(&m_cs);
hr = IReadLines(ppszBuf, pcbRead, &iLines, TRUE);
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::ReadLines
//
// Purpose: retrieves all available complete lines from the buffered data
//
// Args: ppszBuf - pointer to receive allocated buffer, caller must free
// pcbRead - pointer to receive line length
// pcLines - pointer to receive number or lines read
//
// Returns: NOERROR - a complete line was read
// IXP_E_INCOMPLETE - a complete line is not available
// E_OUTOFMEMORY - mem error
//
// Comments:
// If IXP_E_INCOMPLETE is returned or if there is extra data buffered after the
// the last complete line, the caller will recieve an AE_RECV event
// the next time a complete line is available.
//
HRESULT CAsyncConn::ReadLines(char **ppszBuf, int *pcbRead, int *pcLines)
{
HRESULT hr;
EnterCS(&m_cs);
hr = IReadLines(ppszBuf, pcbRead, pcLines, FALSE);
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::ReadBytes
//
// Purpose:
// This function returns up to the number of bytes requested, from the
// current head buffer.
//
// Arguments:
// char **ppszBuf [out] - this function returns a pointer to an allocated
// buffer, if successful. It is the caller's responsibility to MemFree
// this buffer.
// int cbBytesWanted [in] - the number of bytes requested by the caller.
// The requested number of bytes may be returned, or less.
// int *pcbRead [out] - the number of bytes returned in ppszBuf.
//
// Returns: NOERROR - success. Either the remainder of the current buffer
// was returned, or the number of bytes asked for.
// IXP_E_INCOMPLETE - a complete line is not available
// E_OUTOFMEMORY - mem error
// E_INVALIDARG - NULL arguments
//
// Comments:
// If the caller wishes to receive an AE_RECV event the next time data has
// been received from the server, he must either call ReadLines (once), or
// he must continue to call ReadBytes or ReadLine until IXP_E_INCOMPLETE is
// returned.
//
HRESULT CAsyncConn::ReadBytes(char **ppszBuf, int cbBytesWanted, int *pcbRead)
{
int iNumBytesToReturn, i;
char *pResult, *p;
HRESULT hrResult;
BOOL bResult;
// Check arguments
if (NULL == ppszBuf || NULL == pcbRead) {
AssertSz(FALSE, "Check your arguments, buddy");
return E_INVALIDARG;
}
// Initialize variables
*ppszBuf = NULL;
*pcbRead = 0;
hrResult = NOERROR;
EnterCS(&m_cs);
if (NULL == m_pRecvHead) {
hrResult = IXP_E_INCOMPLETE;
goto exit;
}
// Get a buffer to return the results in and fill it in
iNumBytesToReturn = min(m_pRecvHead->cbLen - m_iRecvOffset, cbBytesWanted);
bResult = MemAlloc((void **)&pResult, iNumBytesToReturn + 1); // Leave room for null-term
if (FALSE == bResult) {
hrResult = E_OUTOFMEMORY;
goto exit;
}
CopyMemory(pResult, m_pRecvHead->szBuf + m_iRecvOffset, iNumBytesToReturn);
*(pResult + iNumBytesToReturn) = '\0'; // Null-terminate the buffer
// The null-term should never be read, but doing so allows us to return a
// buffer for when 0 bytes are requested, instead of returning a NULL pointer.
// Advance our position in the current buffer
m_iRecvOffset += iNumBytesToReturn;
if (m_iRecvOffset >= m_pRecvHead->cbLen) {
PRECVBUFQ pTemp;
Assert(m_iRecvOffset == m_pRecvHead->cbLen);
// This buffer's done, advance to the next buffer in the chain
pTemp = m_pRecvHead;
m_pRecvHead = m_pRecvHead->pNext;
if (NULL == m_pRecvHead)
m_pRecvTail = NULL;
m_iRecvOffset = 0;
MemFree(pTemp);
}
// Search and destroy nulls: apparently some servers can send these,
// and most parsing code can't handle it
for (i = 0, p = pResult; i < iNumBytesToReturn; i++, p++)
if (*p == '\0')
*p = ' ';
exit:
// This is the only time we reset the AE_RECV trigger
if (IXP_E_INCOMPLETE == hrResult)
m_fNeedRecvNotify = TRUE;
LeaveCS(&m_cs);
if (NOERROR == hrResult) {
*ppszBuf = pResult;
*pcbRead = iNumBytesToReturn;
}
return hrResult;
} // ReadBytes
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::UlGetSendByteCount
//
ULONG CAsyncConn::UlGetSendByteCount(VOID)
{
return m_cbSent;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::HrStuffDots
//
// Makes sure that leading dots are stuffed
//
#define CB_STUFF_GROW 256
HRESULT CAsyncConn::HrStuffDots(CHAR *pchPrev, LPSTR pszIn, INT cbIn, LPSTR *ppszOut,
INT *pcbOut)
{
// Locals
HRESULT hr=S_OK;
int iIn=0;
int iOut=0;
LPSTR pszOut=NULL;
int cbOut=0;
// Invalid Arg
Assert(pchPrev);
Assert(pszIn);
Assert(cbIn);
Assert(ppszOut);
Assert(pcbOut);
if (!pchPrev || !pszIn || !ppszOut || !pcbOut || (cbIn <= 0))
{
return E_INVALIDARG;
}
// Set cbOut
cbOut = cbIn;
// Allocate
CHECKHR(hr = HrAlloc((LPVOID *)&pszOut, cbIn));
// Setup Loop
while (iIn < cbIn)
{
// Need a realloc
if (iOut + 3 > cbOut)
{
// Allocate a buffer
CHECKHR(hr = HrRealloc((LPVOID *)&pszOut, cbOut + CB_STUFF_GROW));
// Set cbAlloc
cbOut += CB_STUFF_GROW;
}
// Dot at the start of a line...
if ('.' == pszIn[iIn] && ('\0' == *pchPrev || '\r' == *pchPrev || '\n' == *pchPrev))
{
// Write this dot across
pszOut[iOut++] = pszIn[iIn++];
// Stuff the dot
pszOut[iOut++] = '.';
// Set pchPrev
*pchPrev = '.';
}
else
{
// Remember Previous Character
*pchPrev = pszIn[iIn];
// Write
pszOut[iOut++] = pszIn[iIn++];
}
}
// Set Source
*ppszOut = pszOut;
*pcbOut = iOut;
exit:
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::SendBytes
//
// sends data to the socket
//
HRESULT CAsyncConn::SendBytes(const char *pszIn, int cbIn, int *pcbSent,
BOOL fStuffDots /* FALSE */, CHAR *pchPrev /* NULL */)
{
HRESULT hr = S_OK;
int iSent=0;
int iSentTotal=0;
LPSTR pszBuf=NULL;
LPSTR pszFree=NULL;
LPSTR pszFree2=NULL;
LPSTR pszSource=(LPSTR)pszIn;
LPSTR pszStuffed=NULL;
int cbStuffed;
int cbBuf;
int cbSource=cbIn;
EnterCS(&m_cs);
Assert(pszSource && cbSource);
#ifdef DEBUG
if (m_cbQueued)
{
DebugBreak();
}
#endif
// Assert(!m_cbQueued);
Assert(!m_lpbQueued);
Assert(!m_lpbQueueCur);
if (m_state < AS_CONNECTED)
{
hr = IXP_E_NOT_CONNECTED;
goto error;
}
if (fStuffDots)
{
if (FAILED(HrStuffDots(pchPrev, pszSource, cbSource, &pszStuffed, &cbStuffed)))
{
hr = E_FAIL;
goto error;
}
pszSource = pszStuffed;
cbSource = cbStuffed;
}
if (m_fSecure)
{
SECURITY_STATUS scRet;
scRet = EncryptData(&m_hContext, (LPVOID)pszSource, cbSource, (LPVOID*)&pszBuf, &cbBuf);
if (scRet != ERROR_SUCCESS)
{
hr = E_FAIL;
goto error;
}
pszFree = pszBuf;
}
else
{
pszBuf = (LPSTR)pszSource;
cbBuf = cbSource;
}
while (cbBuf && pszBuf && ((iSent = send(m_sock, pszBuf, cbBuf, 0)) != SOCKET_ERROR))
{
iSentTotal += iSent;
pszBuf += iSent;
cbBuf -= iSent;
}
if (cbBuf)
{
m_iLastError = WSAGetLastError();
hr = IXP_E_CONN_SEND;
if (WSAEWOULDBLOCK == m_iLastError)
{
if (MemAlloc((LPVOID*)&m_lpbQueued, cbBuf))
{
m_cbQueued = cbBuf;
m_lpbQueueCur = m_lpbQueued;
CopyMemory(m_lpbQueued, pszBuf, cbBuf);
hr = IXP_E_WOULD_BLOCK;
}
else
hr = E_OUTOFMEMORY;
}
}
else
hr = NOERROR;
error:
*pcbSent = iSentTotal;
LeaveCS(&m_cs);
if (pszFree)
g_pMalloc->Free(pszFree);
if (pszStuffed)
g_pMalloc->Free(pszStuffed);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::SendStream
//
// sends data to the socket
//
HRESULT CAsyncConn::SendStream(LPSTREAM pStream, int *pcbSent, BOOL fStuffDots /* FALSE */)
{
HRESULT hr;
char rgb[STREAM_BUFSIZE]; //$REVIEW - should we heap allocate this instead?
DWORD cbRead;
int iSent, iSentTotal = 0;
EnterCS(&m_cs);
Assert(pStream && pcbSent);
Assert(!m_cbQueued);
Assert(!m_lpbQueued);
Assert(!m_lpbQueueCur);
Assert(!m_pStream);
if (m_state < AS_CONNECTED)
{
hr = IXP_E_NOT_CONNECTED;
goto error;
}
HrRewindStream(pStream);
m_chPrev = '\0';
m_fStuffDots = fStuffDots;
while (SUCCEEDED(hr = pStream->Read(rgb, STREAM_BUFSIZE, &cbRead)) && cbRead)
{
hr = SendBytes(rgb, cbRead, &iSent, m_fStuffDots, &m_chPrev);
iSentTotal += iSent;
if (FAILED(hr))
{
if (WSAEWOULDBLOCK == m_iLastError)
{
// hang onto the stream
m_pStream = pStream;
m_pStream->AddRef();
}
break;
}
}
error:
*pcbSent = iSentTotal;
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnNotify
//
// called for network events that we have registered interest in
//
void CAsyncConn::OnNotify(UINT msg, WPARAM wParam, LPARAM lParam)
{
DWORD dwLookupThreadId;
SOCKET sock;
ASYNCSTATE state;
EnterCS(&m_cs);
sock = m_sock;
state = m_state;
LeaveCS(&m_cs);
switch (msg)
{
case SPM_WSA_GETHOSTBYNAME:
EnterCS(&m_cs);
m_sa.sin_addr.s_addr = (ULONG)lParam;
m_iLastError = (int)wParam;
if (FLOGSESSION)
{
char szBuffer[512];
if (m_iLastError)
{
char lb[256];
if (LoadString(g_hLocRes, idsErrConnLookup, lb, 256))
{
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_iLastError);
m_pLogFile->DebugLog(szBuffer);
}
}
else
{
wnsprintf(szBuffer, ARRAYSIZE(szBuffer),
"srv_name = \"%.200s\" srv_addr = %.200s\r\n",
m_pszServer,
inet_ntoa(m_sa.sin_addr));
m_pLogFile->DebugLog(szBuffer);
}
}
LeaveCS(&m_cs);
OnLookupDone((int)wParam);
break;
case SPM_WSA_SELECT:
if (wParam == (WPARAM)sock)
{
EnterCS(&m_cs);
m_iLastError = WSAGETSELECTERROR(lParam);
if (m_iLastError && FLOGSESSION)
{
char szBuffer[256], lb[256];
if (LoadString(g_hLocRes, idsErrConnSelect, lb, 256))
{
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, WSAGETSELECTEVENT(lParam), m_iLastError);
m_pLogFile->DebugLog(szBuffer);
}
}
if (m_fPaused)
{
m_dwEventMask |= WSAGETSELECTEVENT(lParam);
LeaveCS(&m_cs);
break;
}
LeaveCS(&m_cs);
switch (WSAGETSELECTEVENT(lParam))
{
case FD_CONNECT:
OnConnect();
break;
case FD_CLOSE:
if (AS_HANDSHAKING == state)
OnSSLError();
else
OnClose(AS_DISCONNECTED);
break;
case FD_READ:
OnRead();
break;
case FD_WRITE:
OnWrite();
break;
}
}
else
DOUTL(2,
"Got notify for old socket = %x, evt = %x, err = %x",
wParam,
WSAGETSELECTEVENT(lParam),
WSAGETSELECTERROR(lParam));
break;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::GetConnectStatusString
//
// returns the string ID for the status
//
int CAsyncConn::GetConnectStatusString()
{
return idsNotConnected + (m_state - AS_DISCONNECTED);
}
/////////////////////////////////////////////////////////////////////////////
//
// PRIVATE METHODS
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::AsyncConnect
//
// starts the connection process
//
HRESULT CAsyncConn::AsyncConnect()
{
HRESULT hr = NOERROR;
BOOL fConnect = FALSE;
EnterCS(&m_cs);
if (!(AS_DISCONNECTED == m_state || AS_RECONNECTING == m_state || AS_LOOKUPDONE == m_state))
{
hr = IXP_E_INVALID_STATE;
goto exitCS;
}
Assert(m_sa.sin_addr.s_addr != -1);
if (m_sock == INVALID_SOCKET)
{
if ((m_sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
m_iLastError = WSAGetLastError();
hr = IXP_E_CONN;
goto exitCS;
}
}
if (SOCKET_ERROR == WSAAsyncSelect(m_sock, m_hwnd, SPM_WSA_SELECT, FD_CONNECT))
{
m_iLastError = WSAGetLastError();
hr = IXP_E_CONN;
goto exitCS;
}
LeaveCS(&m_cs);
ChangeState(AS_CONNECTING, AE_NONE);
EnterCS(&m_cs);
if (connect(m_sock, (struct sockaddr *)&m_sa, sizeof(m_sa)) == SOCKET_ERROR)
{
m_iLastError = WSAGetLastError();
if (WSAEWOULDBLOCK == m_iLastError)
{
// this is the expected result
m_iLastError = 0;
}
else
{
if (FLOGSESSION)
{
char szBuffer[256], lb[256];
if (LoadString(g_hLocRes, idsNlogErrConnError, lb, 256))
{
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_iLastError);
m_pLogFile->DebugLog(szBuffer);
}
}
}
}
else
{
Assert(m_iLastError == 0);
fConnect = TRUE;
}
LeaveCS(&m_cs);
if (m_iLastError)
{
ChangeState(AS_DISCONNECTED, AE_NONE);
return IXP_E_CONN;
}
else if (fConnect)
OnConnect();
return NOERROR;
exitCS:
LeaveCS(&m_cs);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnLookupDone
//
// called once an async database lookup finishes
//
HRESULT CAsyncConn::OnLookupDone(int iLastError)
{
ASYNCSTATE as;
EnterCS(&m_cs);
Assert(AS_LOOKUPINPROG == m_state);
m_fLookup = FALSE;
LeaveCS(&m_cs);
if (iLastError)
ChangeState(AS_DISCONNECTED, AE_LOOKUPDONE);
else
{
ChangeState(AS_LOOKUPDONE, AE_LOOKUPDONE);
AsyncConnect();
}
return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnConnect
//
// called once a connection is established
//
HRESULT CAsyncConn::OnConnect()
{
BOOL fConnect = FALSE;
EnterCS(&m_cs);
Assert(AS_CONNECTING == m_state);
if (!m_iLastError)
{
BOOL fTrySecure = m_fNegotiateSecure && FIsSecurityEnabled();
if (SOCKET_ERROR == WSAAsyncSelect(m_sock, m_hwnd, SPM_WSA_SELECT, FD_READ|FD_WRITE|FD_CLOSE))
{
m_iLastError = WSAGetLastError();
LeaveCS(&m_cs);
return IXP_E_CONN;
}
LeaveCS(&m_cs);
if (fTrySecure)
TryNextSecurityPkg();
else
ChangeState(AS_CONNECTED, AE_CONNECTDONE);
}
else
{
LeaveCS(&m_cs);
ChangeState(AS_DISCONNECTED, AE_CONNECTDONE);
EnterCS(&m_cs);
if (m_fCachedAddr && !m_fRedoLookup)
{
// maybe our cached address went bad - try one more time
m_fRedoLookup = TRUE;
fConnect = TRUE;
}
LeaveCS(&m_cs);
if (fConnect)
Connect();
return IXP_E_CONN;
}
return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnClose
//
// called when a connection is dropped
//
HRESULT CAsyncConn::OnClose(ASYNCSTATE asNew)
{
MSG msg;
EnterCS(&m_cs);
// unregister and clean up the socket
Assert(m_sock != INVALID_SOCKET);
closesocket(m_sock);
m_sock = INVALID_SOCKET;
if (FLOGSESSION && m_pszServer)
{
char szBuffer[256], lb[256];
if (LoadString(g_hLocRes, idsNlogErrConnClosed, lb, 256))
{
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_iLastError);
m_pLogFile->DebugLog(szBuffer);
}
}
while (PeekMessage(&msg, m_hwnd, SPM_WSA_SELECT, SPM_WSA_GETHOSTBYNAME, PM_REMOVE))
{
DOUTL(2, "Flushing pending socket messages...");
}
LeaveCS(&m_cs);
ChangeState(asNew, AE_CLOSE);
EnterCS(&m_cs);
CleanUp();
LeaveCS(&m_cs);
return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnRead
//
// called when an FD_READ notification is received
//
HRESULT CAsyncConn::OnRead()
{
HRESULT hr;
int iRecv;
char szRecv[STREAM_BUFSIZE];
EnterCS(&m_cs);
if (m_state < AS_CONNECTED)
{
Assert(FALSE);
LeaveCS(&m_cs);
return IXP_E_NOT_CONNECTED;
}
iRecv = recv(m_sock, szRecv, sizeof(szRecv), 0);
m_iLastError = WSAGetLastError();
LeaveCS(&m_cs);
if (SOCKET_ERROR == iRecv)
{
hr = IXP_E_CONN_RECV;
}
else if (iRecv == 0)
{
// this means the server has disconnected us.
//$TODO - not sure what we should do here
hr = IXP_E_NOT_CONNECTED;
}
else
{
hr = OnDataAvail(szRecv, iRecv, FALSE);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnDataAvail
//
// called when there is incoming data to be queued
//
HRESULT CAsyncConn::OnDataAvail(LPSTR pszRecv, int iRecv, BOOL fIncomplete)
{
HRESULT hr = NOERROR;
BOOL fNotify = FALSE, fHandshake = FALSE, fClose = FALSE;
PRECVBUFQ pNew;
int iQueue = 0;
LPSTR pszFree = NULL;
ASYNCSTATE as;
EnterCS(&m_cs);
if (m_state < AS_CONNECTED)
{
Assert(FALSE);
hr = IXP_E_NOT_CONNECTED;
goto error;
}
if (m_fSecure)
{
SECURITY_STATUS scRet;
int cbEaten = 0;
if (m_cbExtra)
{
Assert(m_pbExtra);
// there's data left over from the last call to DecryptData
if (MemAlloc((LPVOID*)&pszFree, m_cbExtra + iRecv))
{
// combine the extra and new buffers
CopyMemory(pszFree, m_pbExtra, m_cbExtra);
CopyMemory(pszFree + m_cbExtra, pszRecv, iRecv);
pszRecv = pszFree;
iRecv += m_cbExtra;
MemFree(m_pbExtra);
m_pbExtra = NULL;
m_cbExtra = 0;
}
else
{
hr = E_OUTOFMEMORY;
goto error;
}
}
scRet = DecryptData(&m_hContext, pszRecv, iRecv, &iQueue, &cbEaten);
if (scRet == ERROR_SUCCESS || scRet == SEC_E_INCOMPLETE_MESSAGE)
{
if (cbEaten != iRecv)
{
// we need to save away the extra bytes until we receive more data
Assert(cbEaten < iRecv);
DOUTL(2, "cbEaten = %d, iRecv = %d, cbExtra = %d", cbEaten, iRecv, iRecv - cbEaten);
if (MemAlloc((LPVOID*)&m_pbExtra, iRecv - cbEaten))
{
m_cbExtra = iRecv - cbEaten;
CopyMemory(m_pbExtra, pszRecv + cbEaten, m_cbExtra);
}
else
{
hr = E_OUTOFMEMORY;
goto error;
}
}
if (scRet == SEC_E_INCOMPLETE_MESSAGE)
goto error;
}
else
{
// security error, so disconnect.
fClose = TRUE;
hr = E_FAIL;
goto error;
}
}
else
iQueue = iRecv;
if (MemAlloc((LPVOID*)&pNew, sizeof(RECVBUFQ) + iQueue - sizeof(char)))
{
pNew->pNext = NULL;
pNew->cbLen = iQueue;
CopyMemory(pNew->szBuf, pszRecv, iQueue);
if (m_pRecvTail)
{
m_pRecvTail->pNext = pNew;
if ((AS_CONNECTED == m_state) && m_fNeedRecvNotify)
fNotify = FEndLine(pszRecv, iQueue);
}
else
{
Assert(!m_pRecvHead);
m_pRecvHead = pNew;
if (AS_CONNECTED == m_state)
{
fNotify = FEndLine(pszRecv, iQueue);
if (!fNotify)
m_fNeedRecvNotify = TRUE;
}
}
m_pRecvTail = pNew;
hr = NOERROR;
}
else
{
//$TODO - we should disconnect here and notify the caller
hr = E_OUTOFMEMORY;
}
// notify the owner that there is at least one line of data available
if (fNotify)
{
m_fNeedRecvNotify = FALSE;
as = m_state;
}
else if (AS_HANDSHAKING == m_state && SUCCEEDED(hr) && !fIncomplete)
{
Assert(!m_fSecure);
fHandshake = TRUE;
}
LeaveCS(&m_cs);
if (fNotify)
ChangeState(as, AE_RECV);
else if (fHandshake)
OnRecvHandshakeData();
EnterCS(&m_cs);
error:
LeaveCS(&m_cs);
SafeMemFree(pszFree);
if (fClose)
Close();
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::OnWrite
//
// called when an FD_WRITE notification is received
//
HRESULT CAsyncConn::OnWrite()
{
int iSent;
ASYNCEVENT ae;
ASYNCSTATE as;
EnterCS(&m_cs);
m_cbSent = 0;
if (m_state < AS_CONNECTED)
{
Assert(FALSE);
LeaveCS(&m_cs);
return IXP_E_NOT_CONNECTED;
}
if (m_cbQueued)
{
// send some more data from the queued buffer
while (m_cbQueued && ((iSent = send(m_sock, m_lpbQueueCur, m_cbQueued, 0)) != SOCKET_ERROR))
{
m_cbSent += iSent;
m_lpbQueueCur += iSent;
m_cbQueued -= iSent;
}
if (m_cbQueued)
{
m_iLastError = WSAGetLastError();
if (WSAEWOULDBLOCK != m_iLastError)
{
//$TODO - handle this error somehow
Assert(FALSE);
}
}
else
{
MemFree(m_lpbQueued);
m_lpbQueued = m_lpbQueueCur = NULL;
}
}
if (m_pStream && !m_cbQueued)
{
char rgb[STREAM_BUFSIZE]; //$REVIEW - should we heap allocate this instead?
DWORD cbRead;
HRESULT hr;
// send some more data from the queued stream
while (SUCCEEDED(hr = m_pStream->Read(rgb, STREAM_BUFSIZE, &cbRead)) && cbRead)
{
hr = SendBytes(rgb, cbRead, &iSent, m_fStuffDots, &m_chPrev);
if (FAILED(hr))
{
if (WSAEWOULDBLOCK != m_iLastError)
{
//$TODO - handle this error somehow, probably free the stream
Assert(FALSE);
}
break;
}
else
m_cbSent += iSent;
}
if (!cbRead)
{
m_pStream->Release();
m_pStream = NULL;
}
}
as = m_state;
if (!m_cbQueued)
ae = AE_SENDDONE;
else
ae = AE_WRITE;
LeaveCS(&m_cs);
ChangeState(as, ae);
return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::ChangeState
//
// changes the connection state, notifies the owner
//
void CAsyncConn::ChangeState(ASYNCSTATE asNew, ASYNCEVENT ae)
{
ASYNCSTATE asOld;
IAsyncConnCB *pCB;
EnterCS(&m_cs);
asOld = m_state;
m_state = asNew;
m_dwLastActivity = GetTickCount();
pCB = m_pCB;
// pCB->AddRef(); $BUGBUG - we REALLY need to handle this, but IMAP4 doesn't call close before it's destructor
#ifdef DEBUG
IxpAssert(m_cLock == 1);
#endif
LeaveCS(&m_cs);
pCB->OnNotify(asOld, asNew, ae);
// pCB->Release();
}
/////////////////////////////////////////////////////////////////////////////
//
// CAsyncConn::IReadLines
//
// Purpose: retrieves one or all available complete lines from the buffer
//
// Args: ppszBuf - pointer to receive allocated buffer, caller must free
// pcbRead - pointer to receive line length
// pcLines - pointer to receive number or lines read
// fOne - TRUE if only reading one line
//
// Returns: NOERROR - a complete line was read
// IXP_E_INCOMPLETE - a complete line is not available
// E_OUTOFMEMORY - mem error
//
// Comments:
// If IXP_E_INCOMPLETE is returned or if there is extra data buffered after the
// the last complete line, the caller will recieve an AE_RECV event
// the next time a complete line is available.
//
HRESULT CAsyncConn::IReadLines(char **ppszBuf, int *pcbRead, int *pcLines, BOOL fOne)
{
HRESULT hr;
int iRead = 0, iScan = 0, iLines = 0;
char * pszBuf = NULL;
char * psz;
int iOffset, iLeft;
PRECVBUFQ pRecv;
BOOL fFound = FALSE;
if (!m_pRecvHead)
{
hr = IXP_E_INCOMPLETE;
goto error;
}
pRecv = m_pRecvHead;
iOffset = m_iRecvOffset;
while (pRecv)
{
psz = pRecv->szBuf + iOffset;
iLeft = pRecv->cbLen - iOffset;
while (iLeft--)
{
iScan++;
if (*psz++ == '\n')
{
iRead = iScan;
iLines++;
if (fOne)
{
#if 0
// One-eyed t-crash fix
while (iLeft > 0 && (*psz == '\r' || *psz == '\n'))
{
iLeft--;
iScan++;
psz++;
iRead++;
}
#endif
break;
}
}
}
if (iLines && fOne)
break;
iOffset = 0;
pRecv = pRecv->pNext;
}
if (iLines)
{
int iCopy = 0, cb;
if (!MemAlloc((LPVOID*)&pszBuf, iRead + 1))
{
hr = E_OUTOFMEMORY;
goto error;
}
while (iCopy < iRead)
{
cb = min(iRead-iCopy, m_pRecvHead->cbLen - m_iRecvOffset);
CopyMemory(pszBuf + iCopy, m_pRecvHead->szBuf + m_iRecvOffset, cb);
iCopy += cb;
if (cb == (m_pRecvHead->cbLen - m_iRecvOffset))
{
PRECVBUFQ pTemp = m_pRecvHead;
m_pRecvHead = m_pRecvHead->pNext;
if (!m_pRecvHead)
m_pRecvTail = NULL;
m_iRecvOffset = 0;
MemFree(pTemp);
}
else
{
Assert(iCopy == iRead);
m_iRecvOffset += cb;
}
}
for (iScan = 0, psz = pszBuf; iScan < iCopy; iScan++, psz++)
if (*psz == 0)
*psz = ' ';
pszBuf[iCopy] = 0;
hr = NOERROR;
}
else
hr = IXP_E_INCOMPLETE;
// set the flag to notify when a complete line is received
if ((IXP_E_INCOMPLETE == hr) || (m_pRecvHead && !fOne))
m_fNeedRecvNotify = TRUE;
error:
*ppszBuf = pszBuf;
*pcbRead = iRead;
*pcLines = iLines;
return hr;
}
HRESULT CAsyncConn::ReadAllBytes(char **ppszBuf, int *pcbRead)
{
HRESULT hr = S_OK;
int iRead = 0, iCopy = 0, cb;
char * pszBuf = NULL;
int iOffset;
PRECVBUFQ pTemp;
if (!m_pRecvHead)
{
hr = IXP_E_INCOMPLETE;
goto error;
}
// calculate how much to copy
pTemp = m_pRecvHead;
iOffset = m_iRecvOffset;
while (pTemp)
{
iCopy += pTemp->cbLen - iOffset;
iOffset = 0;
pTemp = pTemp->pNext;
}
if (!MemAlloc((LPVOID*)&pszBuf, iCopy))
{
hr = E_OUTOFMEMORY;
goto error;
}
while (m_pRecvHead)
{
cb = min(iCopy-iRead, m_pRecvHead->cbLen - m_iRecvOffset);
CopyMemory(pszBuf + iRead, m_pRecvHead->szBuf + m_iRecvOffset, cb);
iRead += cb;
pTemp = m_pRecvHead;
m_pRecvHead = m_pRecvHead->pNext;
if (!m_pRecvHead)
m_pRecvTail = NULL;
m_iRecvOffset = 0;
MemFree(pTemp);
}
Assert(!m_pRecvHead && !m_iRecvOffset);
error:
*ppszBuf = pszBuf;
*pcbRead = iRead;
return hr;
}
HWND CAsyncConn::CreateWnd()
{
WNDCLASS wc;
if (!GetClassInfo(g_hLocRes, s_szConnWndClass, &wc))
{
wc.style = 0;
wc.lpfnWndProc = CAsyncConn::SockWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hLocRes;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = s_szConnWndClass;
RegisterClass(&wc);
}
m_hwnd = CreateWindowEx(0,
s_szConnWndClass,
s_szConnWndClass,
WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
g_hLocRes,
(LPVOID)this);
return m_hwnd;
}
LRESULT CALLBACK CAsyncConn::SockWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CAsyncConn *pThis = (CAsyncConn*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (msg)
{
case WM_TIMER:
Assert(pThis);
if (SPM_ASYNCTIMER == wParam && pThis)
pThis->OnWatchDogTimer();
break;
case WM_NCCREATE:
pThis = (CAsyncConn*)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pThis);
break;
case WM_NCDESTROY:
pThis->m_hwnd = NULL;
break;
case SPM_WSA_SELECT:
case SPM_WSA_GETHOSTBYNAME:
pThis->OnNotify(msg, wParam, lParam);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HRESULT CAsyncConn::TryNextSecurityPkg()
{
HRESULT hr = NOERROR;
SecBuffer OutBuffer;
SECURITY_STATUS sc;
EnterCS(&m_cs);
sc = InitiateSecConnection(m_pszServer,
FALSE,
&m_iCurSecPkg,
&m_hContext,
&OutBuffer);
LeaveCS(&m_cs);
if (SEC_I_CONTINUE_NEEDED == sc)
{
if (FLOGSESSION)
{
char szBuffer[256], lb[256];
if (LoadString(g_hLocRes, idsNegotiatingSSL, lb, 256))
{
EnterCS(&m_cs);
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, s_SecProviders[m_iCurSecPkg].pszName);
m_pLogFile->DebugLog(szBuffer);
LeaveCS(&m_cs);
}
}
ChangeState(AS_HANDSHAKING, AE_CONNECTDONE);
if (OutBuffer.cbBuffer && OutBuffer.pvBuffer)
{
int iSent;
hr = SendBytes((char *)OutBuffer.pvBuffer, OutBuffer.cbBuffer, &iSent);
g_FreeContextBuffer(OutBuffer.pvBuffer);
}
else
{
AssertSz(0, "Preventing a NULL, 0 sized call to send");
}
}
else
{
// we can't connect securely, so error out and disconnect
Close();
}
return hr;
}
HRESULT CAsyncConn::OnSSLError()
{
HRESULT hr = NOERROR;
BOOL fReconnect;
EnterCS(&m_cs);
if (m_iCurSecPkg + 1 < g_cSSLProviders)
{
m_iCurSecPkg++;
fReconnect = TRUE;
}
else
{
m_iCurSecPkg = 0;
fReconnect = FALSE;
}
LeaveCS(&m_cs);
if (fReconnect)
{
OnClose(AS_RECONNECTING);
Connect();
}
else
OnClose(AS_DISCONNECTED);
return hr;
}
HRESULT CAsyncConn::OnRecvHandshakeData()
{
HRESULT hr;
LPSTR pszBuf;
int cbRead, cbEaten;
SECURITY_STATUS sc;
SecBuffer OutBuffer;
if (SUCCEEDED(hr = ReadAllBytes(&pszBuf, &cbRead)))
{
EnterCS(&m_cs);
sc = ContinueHandshake(m_iCurSecPkg, &m_hContext, pszBuf, cbRead, &cbEaten, &OutBuffer);
LeaveCS(&m_cs);
// if there's a response to send, then do it
if (OutBuffer.cbBuffer && OutBuffer.pvBuffer)
{
int iSent;
hr = SendBytes((char *)OutBuffer.pvBuffer, OutBuffer.cbBuffer, &iSent);
g_FreeContextBuffer(OutBuffer.pvBuffer);
}
if (sc == SEC_E_OK)
{
HRESULT hrCert;
EnterCS(&m_cs);
m_fSecure = TRUE;
LPSRVIGNORABLEERROR pIgnorerror = NULL;
g_pSrvErrRoot = FindOrAddServer(m_pszServer, g_pSrvErrRoot, &pIgnorerror);
hrCert = ChkCertificateTrust(&m_hContext, m_pszServer);
LeaveCS(&m_cs);
if (hrCert && (!pIgnorerror || (hrCert != pIgnorerror->hrError)))
{
TCHAR szError[CCHMAX_RES + CCHMAX_RES],
szPrompt[CCHMAX_RES],
szCaption[CCHMAX_RES];
IAsyncConnPrompt *pPrompt;
DWORD dw;
const DWORD cLineWidth = 64;
LoadString(g_hLocRes, idsSecurityErr, szCaption, ARRAYSIZE(szCaption));
LoadString(g_hLocRes, idsInvalidCert, szError, ARRAYSIZE(szError));
LoadString(g_hLocRes, idsIgnoreSecureErr, szPrompt, ARRAYSIZE(szPrompt));
StrCatBuff(szError, c_szCRLFCRLF, ARRAYSIZE(szError));
dw = lstrlen(szError);
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | cLineWidth,
NULL, hrCert, 0, szError+dw, ARRAYSIZE(szError)-dw, NULL))
{
TCHAR szErrNum[16];
wnsprintf(szErrNum, ARRAYSIZE(szErrNum), "0x%x", hrCert);
StrCatBuff(szError, szErrNum, ARRAYSIZE(szError));
}
Assert(lstrlen(szError) + lstrlen(szPrompt) + lstrlen("\r\n\r\n") < ARRAYSIZE(szError));
StrCatBuff(szError, c_szCRLFCRLF, ARRAYSIZE(szError));
StrCatBuff(szError, szPrompt, ARRAYSIZE(szError));
EnterCS(&m_cs);
pPrompt = m_pPrompt;
if (pPrompt)
pPrompt->AddRef();
LeaveCS(&m_cs);
EnterPausedState();
if (pPrompt && IDYES == pPrompt->OnPrompt(hrCert, szError, szCaption, MB_YESNO | MB_ICONEXCLAMATION | MB_SETFOREGROUND))
{
// Set ignorable error
if(pIgnorerror)
pIgnorerror->hrError = hrCert;
ChangeState(AS_CONNECTED, AE_CONNECTDONE);
if (cbEaten < cbRead)
{
// there were bytes left over, so hold onto them
hr = OnDataAvail(pszBuf + cbEaten, cbRead - cbEaten, sc == SEC_E_INCOMPLETE_MESSAGE);
}
LeavePausedState();
}
else
Close();
if (pPrompt)
pPrompt->Release();
MemFree(pszBuf);
return hr;
}
ChangeState(AS_CONNECTED, AE_CONNECTDONE);
}
else if (sc != SEC_I_CONTINUE_NEEDED && sc != SEC_E_INCOMPLETE_MESSAGE)
{
// unexpected error - we should reset the socket and try the next package
DOUTL(2, "unexpected error from ContinueHandshake() - closing socket.");
return OnSSLError();
}
else
{
Assert(sc == SEC_I_CONTINUE_NEEDED || sc == SEC_E_INCOMPLETE_MESSAGE);
// stay inside the handshake loop, waiting for more data to arrive
}
if (cbEaten < cbRead)
{
// there were bytes left over, so hold onto them
hr = OnDataAvail(pszBuf + cbEaten, cbRead - cbEaten, sc == SEC_E_INCOMPLETE_MESSAGE);
}
MemFree(pszBuf);
}
return hr;
}
void CAsyncConn::CleanUp()
{
PRECVBUFQ pRecv = m_pRecvHead, pTemp;
SafeMemFree(m_lpbQueued);
m_lpbQueueCur = NULL;
m_cbQueued = 0;
SafeMemFree(m_pbExtra);
m_cbExtra = 0;
SafeRelease(m_pStream);
while (pRecv)
{
pTemp = pRecv;
pRecv = pRecv->pNext;
MemFree(pTemp);
}
m_pRecvHead = m_pRecvTail = NULL;
m_iRecvOffset = 0;
m_iLastError = 0;
m_fNeedRecvNotify = FALSE;
m_fSecure = FALSE;
m_fPaused = FALSE;
}
void CAsyncConn::EnterPausedState()
{
EnterCS(&m_cs);
m_fPaused = TRUE;
m_dwEventMask = 0;
StopWatchDog();
LeaveCS(&m_cs);
}
void CAsyncConn::LeavePausedState()
{
DWORD dwEventMask;
EnterCS(&m_cs);
m_fPaused = FALSE;
dwEventMask = m_dwEventMask;
LeaveCS(&m_cs);
if (dwEventMask & FD_CLOSE)
Close();
else
{
if (dwEventMask & FD_READ)
OnRead();
if (dwEventMask & FD_WRITE)
OnWrite();
}
}
/////////////////////////////////////////////////////////////////////////////
//
// UTILITY FUNCTIONS
//
/////////////////////////////////////////////////////////////////////////////
BOOL FEndLine(char *psz, int iLen)
{
while (iLen--)
{
if (*psz++ == '\n')
return TRUE;
}
return FALSE;
}