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