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

  1. /*
  2. * asynconn.cpp
  3. *
  4. * Purpose:
  5. * implementation of the async connection class
  6. *
  7. * Owner:
  8. * EricAn
  9. *
  10. * History:
  11. * Apr 96: Created.
  12. *
  13. * Copyright (C) Microsoft Corp. 1996
  14. */
  15. #include <pch.hxx>
  16. #include <process.h>
  17. #include "imnxport.h"
  18. #include "dllmain.h"
  19. #include "asynconn.h"
  20. #include "thorsspi.h"
  21. #include "resource.h"
  22. #include "strconst.h"
  23. #include "lookup.h"
  24. #include <demand.h>
  25. #include <shlwapi.h>
  26. ASSERTDATA
  27. #define STREAM_BUFSIZE 8192
  28. #define FLOGSESSION (m_pLogFile && TRUE /* profile setting to enable logging should be here */)
  29. // These are the notification messages that we register for the asynchronous
  30. // socket operations that we use.
  31. #define SPM_WSA_SELECT (WM_USER + 1)
  32. // Async Timer Message used for doing Timeouts
  33. #define SPM_ASYNCTIMER (WM_USER + 3)
  34. #ifdef DEBUG
  35. #define EnterCS(_pcs) \
  36. { \
  37. EnterCriticalSection(_pcs); \
  38. m_cLock++; \
  39. IxpAssert(m_cLock > 0); \
  40. }
  41. #define LeaveCS(_pcs) \
  42. { \
  43. m_cLock--; \
  44. IxpAssert(m_cLock >= 0); \
  45. LeaveCriticalSection(_pcs); \
  46. }
  47. #else
  48. #define EnterCS(_pcs) \
  49. EnterCriticalSection(_pcs);
  50. #define LeaveCS(_pcs) \
  51. LeaveCriticalSection(_pcs);
  52. #endif
  53. BOOL FEndLine(char *psz, int iLen);
  54. static const char s_szConnWndClass[] = "ThorConnWndClass";
  55. extern LPSRVIGNORABLEERROR g_pSrvErrRoot;
  56. // This function try to find server and ignorable error, assigned to this server
  57. // if not found then add to list and set ignorable error to S_OK
  58. LPSRVIGNORABLEERROR FindOrAddServer(TCHAR * pchServerName, LPSRVIGNORABLEERROR pSrvErr, LPSRVIGNORABLEERROR *ppSrv)
  59. {
  60. int i = 0;
  61. // if we already had entry in tree then recurse search
  62. if(pSrvErr)
  63. {
  64. i = lstrcmpi(pchServerName, pSrvErr->pchServerName);
  65. if(i > 0)
  66. {
  67. pSrvErr->pRight = FindOrAddServer(pchServerName, pSrvErr->pRight, ppSrv);
  68. return(pSrvErr);
  69. }
  70. else if(i < 0)
  71. {
  72. pSrvErr->pLeft = FindOrAddServer(pchServerName, pSrvErr->pLeft, ppSrv);
  73. return(pSrvErr);
  74. }
  75. else
  76. {
  77. *ppSrv = pSrvErr;
  78. return(pSrvErr);
  79. }
  80. }
  81. // if we don't have node, create it
  82. i = lstrlen(pchServerName);
  83. // if server name is empty return
  84. if(i == 0)
  85. return(NULL);
  86. // Allocate memory for structure
  87. if (!MemAlloc((LPVOID*)&pSrvErr, sizeof(SRVIGNORABLEERROR)))
  88. return(NULL);
  89. pSrvErr->pRight = NULL;
  90. pSrvErr->pLeft = NULL;
  91. pSrvErr->hrError = S_OK;
  92. if (!MemAlloc((LPVOID*)&(pSrvErr->pchServerName), (i+1) * sizeof(pSrvErr->pchServerName[0]) ))
  93. {
  94. MemFree(pSrvErr);
  95. return(NULL);
  96. }
  97. StrCpyN(pSrvErr->pchServerName, pchServerName, (i+1));
  98. *ppSrv = pSrvErr;
  99. return(pSrvErr);
  100. }
  101. void FreeSrvErr(LPSRVIGNORABLEERROR pSrvErr)
  102. {
  103. // if structure NULL return immediately
  104. if(!pSrvErr)
  105. return;
  106. FreeSrvErr(pSrvErr->pRight);
  107. FreeSrvErr(pSrvErr->pLeft);
  108. if(pSrvErr->pchServerName)
  109. {
  110. MemFree(pSrvErr->pchServerName);
  111. pSrvErr->pchServerName = NULL;
  112. }
  113. MemFree(pSrvErr);
  114. pSrvErr = NULL;
  115. }
  116. /////////////////////////////////////////////////////////////////////////////
  117. //
  118. // PUBLIC METHODS - these need to be synchronized as they are accessed by the
  119. // owning thread and the asynchronous socket pump thread.
  120. //
  121. /////////////////////////////////////////////////////////////////////////////
  122. CAsyncConn::CAsyncConn(ILogFile *pLogFile, IAsyncConnCB *pCB, IAsyncConnPrompt *pPrompt)
  123. {
  124. m_cRef = 1;
  125. m_chPrev = '\0';
  126. m_fStuffDots = FALSE;
  127. m_sock = INVALID_SOCKET;
  128. m_fLookup = FALSE;
  129. m_state = AS_DISCONNECTED;
  130. m_fCachedAddr = FALSE;
  131. m_fRedoLookup = FALSE;
  132. m_pszServer = NULL;
  133. m_iDefaultPort = 0;
  134. m_iLastError = 0;
  135. InitializeCriticalSection(&m_cs);
  136. m_pLogFile = pLogFile;
  137. if (m_pLogFile)
  138. m_pLogFile->AddRef();
  139. Assert(pCB);
  140. m_pCB = pCB;
  141. m_cbQueued = 0;
  142. m_lpbQueued = m_lpbQueueCur = NULL;
  143. m_pStream = NULL;
  144. m_pRecvHead = m_pRecvTail = NULL;
  145. m_iRecvOffset = 0;
  146. m_fNeedRecvNotify = FALSE;
  147. m_hwnd = NULL;
  148. m_fNegotiateSecure = FALSE;
  149. m_fSecure = FALSE;
  150. ZeroMemory(&m_hContext, sizeof(m_hContext));
  151. m_iCurSecPkg = 0; // current security package being tried
  152. m_pbExtra = NULL;
  153. m_cbExtra = 0;
  154. m_cbSent = 0;
  155. m_pPrompt = pPrompt;
  156. m_dwLastActivity = 0;
  157. m_dwTimeout = 0;
  158. m_uiTimer = 0;
  159. #ifdef DEBUG
  160. m_cLock = 0;
  161. #endif
  162. m_fPaused = FALSE;
  163. m_dwEventMask = 0;
  164. }
  165. CAsyncConn::~CAsyncConn()
  166. {
  167. DOUT("CAsyncConn::~CAsyncConn %lx: m_cRef = %d", this, m_cRef);
  168. // Bug #22622 - We need to make sure there isn't a timer pending
  169. StopWatchDog();
  170. Assert(!m_fLookup);
  171. SafeMemFree(m_pszServer);
  172. SafeRelease(m_pLogFile);
  173. CleanUp();
  174. if ((NULL != m_hwnd) && (FALSE != IsWindow(m_hwnd)))
  175. SendMessage(m_hwnd, WM_CLOSE, 0, 0);
  176. DeleteCriticalSection(&m_cs);
  177. #ifdef DEBUG
  178. IxpAssert(m_cLock == 0);
  179. #endif
  180. }
  181. ULONG CAsyncConn::AddRef(void)
  182. {
  183. ULONG cRefNew;
  184. EnterCS(&m_cs);
  185. DOUT("CAsyncConn::AddRef %lx ==> %d", this, m_cRef+1);
  186. cRefNew = ++m_cRef;
  187. LeaveCS(&m_cs);
  188. return cRefNew;
  189. }
  190. ULONG CAsyncConn::Release(void)
  191. {
  192. ULONG cRefNew;
  193. EnterCS(&m_cs);
  194. DOUT("CAsyncConn::Release %lx ==> %d", this, m_cRef-1);
  195. cRefNew = --m_cRef;
  196. LeaveCS(&m_cs);
  197. if (cRefNew == 0)
  198. delete this;
  199. return cRefNew;
  200. }
  201. /////////////////////////////////////////////////////////////////////////////
  202. //
  203. // CAsyncConn::HrInit
  204. //
  205. // Allocates recv buffer, sets servername, servicename, and port
  206. //
  207. HRESULT CAsyncConn::HrInit(char *szServer, int iDefaultPort, BOOL fSecure, DWORD dwTimeout)
  208. {
  209. HRESULT hr = NOERROR;
  210. EnterCS(&m_cs);
  211. if (!m_hwnd && !CreateWnd())
  212. {
  213. hr = E_FAIL;
  214. goto error;
  215. }
  216. if (m_state != AS_DISCONNECTED)
  217. {
  218. hr = IXP_E_ALREADY_CONNECTED;
  219. goto error;
  220. }
  221. Assert(szServer);
  222. // if nothing has changed, then use the current settings
  223. if (m_pszServer &&
  224. !lstrcmpi(m_pszServer, szServer) &&
  225. (iDefaultPort == m_iDefaultPort) &&
  226. (fSecure == m_fNegotiateSecure))
  227. goto error;
  228. m_fCachedAddr = FALSE;
  229. m_fRedoLookup = FALSE;
  230. SafeMemFree(m_pszServer);
  231. DWORD cchSize = (lstrlen(szServer)+1);
  232. if (!MemAlloc((LPVOID*)&m_pszServer, cchSize * sizeof(m_pszServer[0])))
  233. {
  234. hr = E_OUTOFMEMORY;
  235. goto error;
  236. }
  237. StrCpyN(m_pszServer, szServer, cchSize);
  238. Assert(iDefaultPort > 0);
  239. m_iDefaultPort = (u_short) iDefaultPort;
  240. m_fNegotiateSecure = fSecure;
  241. // If dwTimeout == 0, no timeout detection will be installed.
  242. m_dwTimeout = dwTimeout;
  243. error:
  244. LeaveCS(&m_cs);
  245. return hr;
  246. }
  247. /////////////////////////////////////////////////////////////////////////////
  248. //
  249. // CAsyncConn::SetWindow
  250. //
  251. // creates a window used by async. winsock. ResetWindow()
  252. // must be called before invoking this function, so as to avoid
  253. // window handle leakage.
  254. //
  255. HRESULT CAsyncConn::SetWindow(void)
  256. {
  257. HRESULT hr = NOERROR;
  258. EnterCS(&m_cs);
  259. if (NULL != m_hwnd && IsWindow(m_hwnd) &&
  260. GetWindowThreadProcessId(m_hwnd, NULL) == GetCurrentThreadId())
  261. {
  262. // no need to create the new window for this thread
  263. goto error;
  264. }
  265. else if (NULL != m_hwnd && IsWindow(m_hwnd))
  266. {
  267. // leaks one window handle; the previous worker thread
  268. // didn't call ResetWindow().
  269. Assert(FALSE);
  270. }
  271. if (!CreateWnd())
  272. {
  273. hr = E_FAIL;
  274. goto error;
  275. }
  276. if (m_sock != INVALID_SOCKET)
  277. {
  278. if (SOCKET_ERROR == WSAAsyncSelect(m_sock, m_hwnd, SPM_WSA_SELECT, FD_READ|FD_WRITE|FD_CLOSE))
  279. {
  280. m_iLastError = WSAGetLastError();
  281. hr = IXP_E_CONN;
  282. goto error;
  283. }
  284. }
  285. error:
  286. LeaveCS(&m_cs);
  287. return hr;
  288. }
  289. /////////////////////////////////////////////////////////////////////////////
  290. //
  291. // CAsyncConn::ResetWindow
  292. //
  293. // closes the window used by async. winsock
  294. //
  295. HRESULT CAsyncConn::ResetWindow(void)
  296. {
  297. HRESULT hr = NOERROR;
  298. EnterCS(&m_cs);
  299. if ((NULL == m_hwnd) || (FALSE == IsWindow(m_hwnd)))
  300. goto error;
  301. if (GetWindowThreadProcessId(m_hwnd, NULL) == GetCurrentThreadId())
  302. {
  303. SendMessage(m_hwnd, WM_CLOSE, 0, 0);
  304. m_hwnd = NULL;
  305. }
  306. else
  307. {
  308. // A caller forgot to call ResetWindow. Only the owner thread can destroy
  309. // the window.
  310. Assert(FALSE);
  311. }
  312. error:
  313. LeaveCS(&m_cs);
  314. return hr;
  315. }
  316. /////////////////////////////////////////////////////////////////////////////
  317. //
  318. // CAsyncConn::Connect
  319. //
  320. // starts the name lookup and connection process
  321. //
  322. HRESULT CAsyncConn::Connect()
  323. {
  324. HRESULT hr;
  325. HANDLE hThreadLookup;
  326. BOOL fNotify = FALSE;
  327. BOOL fAsync = FALSE;
  328. EnterCS(&m_cs);
  329. if (m_state != AS_DISCONNECTED && m_state != AS_RECONNECTING)
  330. {
  331. hr = IXP_E_ALREADY_CONNECTED;
  332. goto error;
  333. }
  334. Assert(m_pszServer);
  335. if (FLOGSESSION)
  336. {
  337. char szBuffer[512], lb[256];
  338. if (LoadString(g_hLocRes, idsNlogIConnect, lb, 256))
  339. {
  340. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_pszServer, m_iDefaultPort);
  341. m_pLogFile->DebugLog(szBuffer);
  342. }
  343. }
  344. ZeroMemory(&m_sa, sizeof(m_sa));
  345. m_sa.sin_port = htons(m_iDefaultPort);
  346. m_sa.sin_family = AF_INET;
  347. m_sa.sin_addr.s_addr = inet_addr(m_pszServer);
  348. if (m_sa.sin_addr.s_addr != -1)
  349. // server name is dotted decimal, so no need to look it up
  350. fAsync = TRUE;
  351. else
  352. {
  353. // Start a name lookup on a separate thread because WinSock caches the DNS server in TLS.
  354. // The separate thread enables us to connect to a LAN DNS and a RAS DNS in the same session.
  355. hr = LookupHostName(m_pszServer, m_hwnd, &(m_sa.sin_addr.s_addr), &m_fCachedAddr, m_fRedoLookup);
  356. if (SUCCEEDED(hr))
  357. {
  358. m_fLookup = fNotify = !m_fCachedAddr;
  359. fAsync = m_fCachedAddr;
  360. }
  361. else
  362. {
  363. m_iLastError = WSAENOBUFS;
  364. hr = IXP_E_CONN;
  365. }
  366. }
  367. error:
  368. LeaveCS(&m_cs);
  369. if (fAsync)
  370. hr = AsyncConnect();
  371. if (fNotify)
  372. ChangeState(AS_LOOKUPINPROG, AE_NONE);
  373. return hr;
  374. }
  375. void CAsyncConn::StartWatchDog(void)
  376. {
  377. if (m_dwTimeout < 5) m_dwTimeout = 30;
  378. m_dwLastActivity = GetTickCount();
  379. Assert(m_hwnd);
  380. StopWatchDog();
  381. m_uiTimer = SetTimer(m_hwnd, SPM_ASYNCTIMER, 5000, NULL);
  382. }
  383. void CAsyncConn::StopWatchDog(void)
  384. {
  385. if (m_uiTimer)
  386. {
  387. KillTimer(m_hwnd, SPM_ASYNCTIMER);
  388. m_uiTimer = 0;
  389. }
  390. }
  391. void CAsyncConn::OnWatchDogTimer(void)
  392. {
  393. BOOL fNotify = FALSE;
  394. ASYNCSTATE as;
  395. EnterCS(&m_cs);
  396. if (((GetTickCount() - m_dwLastActivity) / 1000) >= m_dwTimeout)
  397. {
  398. fNotify = TRUE;
  399. as = m_state;
  400. }
  401. LeaveCS(&m_cs);
  402. if (fNotify)
  403. ChangeState(as, AE_TIMEOUT);
  404. }
  405. /////////////////////////////////////////////////////////////////////////////
  406. //
  407. // CAsyncConn::Close
  408. //
  409. // closes the connection
  410. //
  411. HRESULT CAsyncConn::Close()
  412. {
  413. BOOL fNotify = FALSE;
  414. BOOL fClose = FALSE;
  415. EnterCS(&m_cs);
  416. if (m_fLookup)
  417. {
  418. CancelLookup(m_pszServer, m_hwnd);
  419. m_fLookup = FALSE;
  420. fNotify = TRUE;
  421. }
  422. fClose = (m_sock != INVALID_SOCKET);
  423. LeaveCS(&m_cs);
  424. if (fNotify)
  425. ChangeState(AS_DISCONNECTED, AE_LOOKUPDONE);
  426. if (fClose)
  427. OnClose(AS_DISCONNECTED);
  428. return NOERROR;
  429. }
  430. /////////////////////////////////////////////////////////////////////////////
  431. //
  432. // CAsyncConn::ReadLine
  433. //
  434. // Purpose: retrieves a single complete line from the buffered data
  435. //
  436. // Args: ppszBuf - pointer to receive allocated buffer, caller must free
  437. // pcbRead - pointer to receive line length
  438. //
  439. // Returns: NOERROR - a complete line was read
  440. // IXP_E_INCOMPLETE - a complete line is not available
  441. // E_OUTOFMEMORY - mem error
  442. //
  443. // Comments:
  444. // If IXP_E_INCOMPLETE is returned, the caller will recieve an AE_RECV event
  445. // the next time a complete line is available.
  446. //
  447. HRESULT CAsyncConn::ReadLine(char **ppszBuf, int *pcbRead)
  448. {
  449. HRESULT hr;
  450. int iLines;
  451. EnterCS(&m_cs);
  452. hr = IReadLines(ppszBuf, pcbRead, &iLines, TRUE);
  453. LeaveCS(&m_cs);
  454. return hr;
  455. }
  456. /////////////////////////////////////////////////////////////////////////////
  457. //
  458. // CAsyncConn::ReadLines
  459. //
  460. // Purpose: retrieves all available complete lines from the buffered data
  461. //
  462. // Args: ppszBuf - pointer to receive allocated buffer, caller must free
  463. // pcbRead - pointer to receive line length
  464. // pcLines - pointer to receive number or lines read
  465. //
  466. // Returns: NOERROR - a complete line was read
  467. // IXP_E_INCOMPLETE - a complete line is not available
  468. // E_OUTOFMEMORY - mem error
  469. //
  470. // Comments:
  471. // If IXP_E_INCOMPLETE is returned or if there is extra data buffered after the
  472. // the last complete line, the caller will recieve an AE_RECV event
  473. // the next time a complete line is available.
  474. //
  475. HRESULT CAsyncConn::ReadLines(char **ppszBuf, int *pcbRead, int *pcLines)
  476. {
  477. HRESULT hr;
  478. EnterCS(&m_cs);
  479. hr = IReadLines(ppszBuf, pcbRead, pcLines, FALSE);
  480. LeaveCS(&m_cs);
  481. return hr;
  482. }
  483. /////////////////////////////////////////////////////////////////////////////
  484. //
  485. // CAsyncConn::ReadBytes
  486. //
  487. // Purpose:
  488. // This function returns up to the number of bytes requested, from the
  489. // current head buffer.
  490. //
  491. // Arguments:
  492. // char **ppszBuf [out] - this function returns a pointer to an allocated
  493. // buffer, if successful. It is the caller's responsibility to MemFree
  494. // this buffer.
  495. // int cbBytesWanted [in] - the number of bytes requested by the caller.
  496. // The requested number of bytes may be returned, or less.
  497. // int *pcbRead [out] - the number of bytes returned in ppszBuf.
  498. //
  499. // Returns: NOERROR - success. Either the remainder of the current buffer
  500. // was returned, or the number of bytes asked for.
  501. // IXP_E_INCOMPLETE - a complete line is not available
  502. // E_OUTOFMEMORY - mem error
  503. // E_INVALIDARG - NULL arguments
  504. //
  505. // Comments:
  506. // If the caller wishes to receive an AE_RECV event the next time data has
  507. // been received from the server, he must either call ReadLines (once), or
  508. // he must continue to call ReadBytes or ReadLine until IXP_E_INCOMPLETE is
  509. // returned.
  510. //
  511. HRESULT CAsyncConn::ReadBytes(char **ppszBuf, int cbBytesWanted, int *pcbRead)
  512. {
  513. int iNumBytesToReturn, i;
  514. char *pResult, *p;
  515. HRESULT hrResult;
  516. BOOL bResult;
  517. // Check arguments
  518. if (NULL == ppszBuf || NULL == pcbRead) {
  519. AssertSz(FALSE, "Check your arguments, buddy");
  520. return E_INVALIDARG;
  521. }
  522. // Initialize variables
  523. *ppszBuf = NULL;
  524. *pcbRead = 0;
  525. hrResult = NOERROR;
  526. EnterCS(&m_cs);
  527. if (NULL == m_pRecvHead) {
  528. hrResult = IXP_E_INCOMPLETE;
  529. goto exit;
  530. }
  531. // Get a buffer to return the results in and fill it in
  532. iNumBytesToReturn = min(m_pRecvHead->cbLen - m_iRecvOffset, cbBytesWanted);
  533. bResult = MemAlloc((void **)&pResult, iNumBytesToReturn + 1); // Leave room for null-term
  534. if (FALSE == bResult) {
  535. hrResult = E_OUTOFMEMORY;
  536. goto exit;
  537. }
  538. CopyMemory(pResult, m_pRecvHead->szBuf + m_iRecvOffset, iNumBytesToReturn);
  539. *(pResult + iNumBytesToReturn) = '\0'; // Null-terminate the buffer
  540. // The null-term should never be read, but doing so allows us to return a
  541. // buffer for when 0 bytes are requested, instead of returning a NULL pointer.
  542. // Advance our position in the current buffer
  543. m_iRecvOffset += iNumBytesToReturn;
  544. if (m_iRecvOffset >= m_pRecvHead->cbLen) {
  545. PRECVBUFQ pTemp;
  546. Assert(m_iRecvOffset == m_pRecvHead->cbLen);
  547. // This buffer's done, advance to the next buffer in the chain
  548. pTemp = m_pRecvHead;
  549. m_pRecvHead = m_pRecvHead->pNext;
  550. if (NULL == m_pRecvHead)
  551. m_pRecvTail = NULL;
  552. m_iRecvOffset = 0;
  553. MemFree(pTemp);
  554. }
  555. // Search and destroy nulls: apparently some servers can send these,
  556. // and most parsing code can't handle it
  557. for (i = 0, p = pResult; i < iNumBytesToReturn; i++, p++)
  558. if (*p == '\0')
  559. *p = ' ';
  560. exit:
  561. // This is the only time we reset the AE_RECV trigger
  562. if (IXP_E_INCOMPLETE == hrResult)
  563. m_fNeedRecvNotify = TRUE;
  564. LeaveCS(&m_cs);
  565. if (NOERROR == hrResult) {
  566. *ppszBuf = pResult;
  567. *pcbRead = iNumBytesToReturn;
  568. }
  569. return hrResult;
  570. } // ReadBytes
  571. /////////////////////////////////////////////////////////////////////////////
  572. //
  573. // CAsyncConn::UlGetSendByteCount
  574. //
  575. ULONG CAsyncConn::UlGetSendByteCount(VOID)
  576. {
  577. return m_cbSent;
  578. }
  579. /////////////////////////////////////////////////////////////////////////////
  580. //
  581. // CAsyncConn::HrStuffDots
  582. //
  583. // Makes sure that leading dots are stuffed
  584. //
  585. #define CB_STUFF_GROW 256
  586. HRESULT CAsyncConn::HrStuffDots(CHAR *pchPrev, LPSTR pszIn, INT cbIn, LPSTR *ppszOut,
  587. INT *pcbOut)
  588. {
  589. // Locals
  590. HRESULT hr=S_OK;
  591. int iIn=0;
  592. int iOut=0;
  593. LPSTR pszOut=NULL;
  594. int cbOut=0;
  595. // Invalid Arg
  596. Assert(pchPrev);
  597. Assert(pszIn);
  598. Assert(cbIn);
  599. Assert(ppszOut);
  600. Assert(pcbOut);
  601. if (!pchPrev || !pszIn || !ppszOut || !pcbOut || (cbIn <= 0))
  602. {
  603. return E_INVALIDARG;
  604. }
  605. // Set cbOut
  606. cbOut = cbIn;
  607. // Allocate
  608. CHECKHR(hr = HrAlloc((LPVOID *)&pszOut, cbIn));
  609. // Setup Loop
  610. while (iIn < cbIn)
  611. {
  612. // Need a realloc
  613. if (iOut + 3 > cbOut)
  614. {
  615. // Allocate a buffer
  616. CHECKHR(hr = HrRealloc((LPVOID *)&pszOut, cbOut + CB_STUFF_GROW));
  617. // Set cbAlloc
  618. cbOut += CB_STUFF_GROW;
  619. }
  620. // Dot at the start of a line...
  621. if ('.' == pszIn[iIn] && ('\0' == *pchPrev || '\r' == *pchPrev || '\n' == *pchPrev))
  622. {
  623. // Write this dot across
  624. pszOut[iOut++] = pszIn[iIn++];
  625. // Stuff the dot
  626. pszOut[iOut++] = '.';
  627. // Set pchPrev
  628. *pchPrev = '.';
  629. }
  630. else
  631. {
  632. // Remember Previous Character
  633. *pchPrev = pszIn[iIn];
  634. // Write
  635. pszOut[iOut++] = pszIn[iIn++];
  636. }
  637. }
  638. // Set Source
  639. *ppszOut = pszOut;
  640. *pcbOut = iOut;
  641. exit:
  642. return(hr);
  643. }
  644. /////////////////////////////////////////////////////////////////////////////
  645. //
  646. // CAsyncConn::SendBytes
  647. //
  648. // sends data to the socket
  649. //
  650. HRESULT CAsyncConn::SendBytes(const char *pszIn, int cbIn, int *pcbSent,
  651. BOOL fStuffDots /* FALSE */, CHAR *pchPrev /* NULL */)
  652. {
  653. HRESULT hr = S_OK;
  654. int iSent=0;
  655. int iSentTotal=0;
  656. LPSTR pszBuf=NULL;
  657. LPSTR pszFree=NULL;
  658. LPSTR pszFree2=NULL;
  659. LPSTR pszSource=(LPSTR)pszIn;
  660. LPSTR pszStuffed=NULL;
  661. int cbStuffed;
  662. int cbBuf;
  663. int cbSource=cbIn;
  664. EnterCS(&m_cs);
  665. Assert(pszSource && cbSource);
  666. #ifdef DEBUG
  667. if (m_cbQueued)
  668. {
  669. DebugBreak();
  670. }
  671. #endif
  672. // Assert(!m_cbQueued);
  673. Assert(!m_lpbQueued);
  674. Assert(!m_lpbQueueCur);
  675. if (m_state < AS_CONNECTED)
  676. {
  677. hr = IXP_E_NOT_CONNECTED;
  678. goto error;
  679. }
  680. if (fStuffDots)
  681. {
  682. if (FAILED(HrStuffDots(pchPrev, pszSource, cbSource, &pszStuffed, &cbStuffed)))
  683. {
  684. hr = E_FAIL;
  685. goto error;
  686. }
  687. pszSource = pszStuffed;
  688. cbSource = cbStuffed;
  689. }
  690. if (m_fSecure)
  691. {
  692. SECURITY_STATUS scRet;
  693. scRet = EncryptData(&m_hContext, (LPVOID)pszSource, cbSource, (LPVOID*)&pszBuf, &cbBuf);
  694. if (scRet != ERROR_SUCCESS)
  695. {
  696. hr = E_FAIL;
  697. goto error;
  698. }
  699. pszFree = pszBuf;
  700. }
  701. else
  702. {
  703. pszBuf = (LPSTR)pszSource;
  704. cbBuf = cbSource;
  705. }
  706. while (cbBuf && pszBuf && ((iSent = send(m_sock, pszBuf, cbBuf, 0)) != SOCKET_ERROR))
  707. {
  708. iSentTotal += iSent;
  709. pszBuf += iSent;
  710. cbBuf -= iSent;
  711. }
  712. if (cbBuf)
  713. {
  714. m_iLastError = WSAGetLastError();
  715. hr = IXP_E_CONN_SEND;
  716. if (WSAEWOULDBLOCK == m_iLastError)
  717. {
  718. if (MemAlloc((LPVOID*)&m_lpbQueued, cbBuf))
  719. {
  720. m_cbQueued = cbBuf;
  721. m_lpbQueueCur = m_lpbQueued;
  722. CopyMemory(m_lpbQueued, pszBuf, cbBuf);
  723. hr = IXP_E_WOULD_BLOCK;
  724. }
  725. else
  726. hr = E_OUTOFMEMORY;
  727. }
  728. }
  729. else
  730. hr = NOERROR;
  731. error:
  732. *pcbSent = iSentTotal;
  733. LeaveCS(&m_cs);
  734. if (pszFree)
  735. g_pMalloc->Free(pszFree);
  736. if (pszStuffed)
  737. g_pMalloc->Free(pszStuffed);
  738. return hr;
  739. }
  740. /////////////////////////////////////////////////////////////////////////////
  741. //
  742. // CAsyncConn::SendStream
  743. //
  744. // sends data to the socket
  745. //
  746. HRESULT CAsyncConn::SendStream(LPSTREAM pStream, int *pcbSent, BOOL fStuffDots /* FALSE */)
  747. {
  748. HRESULT hr;
  749. char rgb[STREAM_BUFSIZE]; //$REVIEW - should we heap allocate this instead?
  750. DWORD cbRead;
  751. int iSent, iSentTotal = 0;
  752. EnterCS(&m_cs);
  753. Assert(pStream && pcbSent);
  754. Assert(!m_cbQueued);
  755. Assert(!m_lpbQueued);
  756. Assert(!m_lpbQueueCur);
  757. Assert(!m_pStream);
  758. if (m_state < AS_CONNECTED)
  759. {
  760. hr = IXP_E_NOT_CONNECTED;
  761. goto error;
  762. }
  763. HrRewindStream(pStream);
  764. m_chPrev = '\0';
  765. m_fStuffDots = fStuffDots;
  766. while (SUCCEEDED(hr = pStream->Read(rgb, STREAM_BUFSIZE, &cbRead)) && cbRead)
  767. {
  768. hr = SendBytes(rgb, cbRead, &iSent, m_fStuffDots, &m_chPrev);
  769. iSentTotal += iSent;
  770. if (FAILED(hr))
  771. {
  772. if (WSAEWOULDBLOCK == m_iLastError)
  773. {
  774. // hang onto the stream
  775. m_pStream = pStream;
  776. m_pStream->AddRef();
  777. }
  778. break;
  779. }
  780. }
  781. error:
  782. *pcbSent = iSentTotal;
  783. LeaveCS(&m_cs);
  784. return hr;
  785. }
  786. /////////////////////////////////////////////////////////////////////////////
  787. //
  788. // CAsyncConn::OnNotify
  789. //
  790. // called for network events that we have registered interest in
  791. //
  792. void CAsyncConn::OnNotify(UINT msg, WPARAM wParam, LPARAM lParam)
  793. {
  794. DWORD dwLookupThreadId;
  795. SOCKET sock;
  796. ASYNCSTATE state;
  797. EnterCS(&m_cs);
  798. sock = m_sock;
  799. state = m_state;
  800. LeaveCS(&m_cs);
  801. switch (msg)
  802. {
  803. case SPM_WSA_GETHOSTBYNAME:
  804. EnterCS(&m_cs);
  805. m_sa.sin_addr.s_addr = (ULONG)lParam;
  806. m_iLastError = (int)wParam;
  807. if (FLOGSESSION)
  808. {
  809. char szBuffer[512];
  810. if (m_iLastError)
  811. {
  812. char lb[256];
  813. if (LoadString(g_hLocRes, idsErrConnLookup, lb, 256))
  814. {
  815. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_iLastError);
  816. m_pLogFile->DebugLog(szBuffer);
  817. }
  818. }
  819. else
  820. {
  821. wnsprintf(szBuffer, ARRAYSIZE(szBuffer),
  822. "srv_name = \"%.200s\" srv_addr = %.200s\r\n",
  823. m_pszServer,
  824. inet_ntoa(m_sa.sin_addr));
  825. m_pLogFile->DebugLog(szBuffer);
  826. }
  827. }
  828. LeaveCS(&m_cs);
  829. OnLookupDone((int)wParam);
  830. break;
  831. case SPM_WSA_SELECT:
  832. if (wParam == (WPARAM)sock)
  833. {
  834. EnterCS(&m_cs);
  835. m_iLastError = WSAGETSELECTERROR(lParam);
  836. if (m_iLastError && FLOGSESSION)
  837. {
  838. char szBuffer[256], lb[256];
  839. if (LoadString(g_hLocRes, idsErrConnSelect, lb, 256))
  840. {
  841. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, WSAGETSELECTEVENT(lParam), m_iLastError);
  842. m_pLogFile->DebugLog(szBuffer);
  843. }
  844. }
  845. if (m_fPaused)
  846. {
  847. m_dwEventMask |= WSAGETSELECTEVENT(lParam);
  848. LeaveCS(&m_cs);
  849. break;
  850. }
  851. LeaveCS(&m_cs);
  852. switch (WSAGETSELECTEVENT(lParam))
  853. {
  854. case FD_CONNECT:
  855. OnConnect();
  856. break;
  857. case FD_CLOSE:
  858. if (AS_HANDSHAKING == state)
  859. OnSSLError();
  860. else
  861. OnClose(AS_DISCONNECTED);
  862. break;
  863. case FD_READ:
  864. OnRead();
  865. break;
  866. case FD_WRITE:
  867. OnWrite();
  868. break;
  869. }
  870. }
  871. else
  872. DOUTL(2,
  873. "Got notify for old socket = %x, evt = %x, err = %x",
  874. wParam,
  875. WSAGETSELECTEVENT(lParam),
  876. WSAGETSELECTERROR(lParam));
  877. break;
  878. }
  879. }
  880. /////////////////////////////////////////////////////////////////////////////
  881. //
  882. // CAsyncConn::GetConnectStatusString
  883. //
  884. // returns the string ID for the status
  885. //
  886. int CAsyncConn::GetConnectStatusString()
  887. {
  888. return idsNotConnected + (m_state - AS_DISCONNECTED);
  889. }
  890. /////////////////////////////////////////////////////////////////////////////
  891. //
  892. // PRIVATE METHODS
  893. //
  894. /////////////////////////////////////////////////////////////////////////////
  895. /////////////////////////////////////////////////////////////////////////////
  896. //
  897. // CAsyncConn::AsyncConnect
  898. //
  899. // starts the connection process
  900. //
  901. HRESULT CAsyncConn::AsyncConnect()
  902. {
  903. HRESULT hr = NOERROR;
  904. BOOL fConnect = FALSE;
  905. EnterCS(&m_cs);
  906. if (!(AS_DISCONNECTED == m_state || AS_RECONNECTING == m_state || AS_LOOKUPDONE == m_state))
  907. {
  908. hr = IXP_E_INVALID_STATE;
  909. goto exitCS;
  910. }
  911. Assert(m_sa.sin_addr.s_addr != -1);
  912. if (m_sock == INVALID_SOCKET)
  913. {
  914. if ((m_sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
  915. {
  916. m_iLastError = WSAGetLastError();
  917. hr = IXP_E_CONN;
  918. goto exitCS;
  919. }
  920. }
  921. if (SOCKET_ERROR == WSAAsyncSelect(m_sock, m_hwnd, SPM_WSA_SELECT, FD_CONNECT))
  922. {
  923. m_iLastError = WSAGetLastError();
  924. hr = IXP_E_CONN;
  925. goto exitCS;
  926. }
  927. LeaveCS(&m_cs);
  928. ChangeState(AS_CONNECTING, AE_NONE);
  929. EnterCS(&m_cs);
  930. if (connect(m_sock, (struct sockaddr *)&m_sa, sizeof(m_sa)) == SOCKET_ERROR)
  931. {
  932. m_iLastError = WSAGetLastError();
  933. if (WSAEWOULDBLOCK == m_iLastError)
  934. {
  935. // this is the expected result
  936. m_iLastError = 0;
  937. }
  938. else
  939. {
  940. if (FLOGSESSION)
  941. {
  942. char szBuffer[256], lb[256];
  943. if (LoadString(g_hLocRes, idsNlogErrConnError, lb, 256))
  944. {
  945. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_iLastError);
  946. m_pLogFile->DebugLog(szBuffer);
  947. }
  948. }
  949. }
  950. }
  951. else
  952. {
  953. Assert(m_iLastError == 0);
  954. fConnect = TRUE;
  955. }
  956. LeaveCS(&m_cs);
  957. if (m_iLastError)
  958. {
  959. ChangeState(AS_DISCONNECTED, AE_NONE);
  960. return IXP_E_CONN;
  961. }
  962. else if (fConnect)
  963. OnConnect();
  964. return NOERROR;
  965. exitCS:
  966. LeaveCS(&m_cs);
  967. return hr;
  968. }
  969. /////////////////////////////////////////////////////////////////////////////
  970. //
  971. // CAsyncConn::OnLookupDone
  972. //
  973. // called once an async database lookup finishes
  974. //
  975. HRESULT CAsyncConn::OnLookupDone(int iLastError)
  976. {
  977. ASYNCSTATE as;
  978. EnterCS(&m_cs);
  979. Assert(AS_LOOKUPINPROG == m_state);
  980. m_fLookup = FALSE;
  981. LeaveCS(&m_cs);
  982. if (iLastError)
  983. ChangeState(AS_DISCONNECTED, AE_LOOKUPDONE);
  984. else
  985. {
  986. ChangeState(AS_LOOKUPDONE, AE_LOOKUPDONE);
  987. AsyncConnect();
  988. }
  989. return NOERROR;
  990. }
  991. /////////////////////////////////////////////////////////////////////////////
  992. //
  993. // CAsyncConn::OnConnect
  994. //
  995. // called once a connection is established
  996. //
  997. HRESULT CAsyncConn::OnConnect()
  998. {
  999. BOOL fConnect = FALSE;
  1000. EnterCS(&m_cs);
  1001. Assert(AS_CONNECTING == m_state);
  1002. if (!m_iLastError)
  1003. {
  1004. BOOL fTrySecure = m_fNegotiateSecure && FIsSecurityEnabled();
  1005. if (SOCKET_ERROR == WSAAsyncSelect(m_sock, m_hwnd, SPM_WSA_SELECT, FD_READ|FD_WRITE|FD_CLOSE))
  1006. {
  1007. m_iLastError = WSAGetLastError();
  1008. LeaveCS(&m_cs);
  1009. return IXP_E_CONN;
  1010. }
  1011. LeaveCS(&m_cs);
  1012. if (fTrySecure)
  1013. TryNextSecurityPkg();
  1014. else
  1015. ChangeState(AS_CONNECTED, AE_CONNECTDONE);
  1016. }
  1017. else
  1018. {
  1019. LeaveCS(&m_cs);
  1020. ChangeState(AS_DISCONNECTED, AE_CONNECTDONE);
  1021. EnterCS(&m_cs);
  1022. if (m_fCachedAddr && !m_fRedoLookup)
  1023. {
  1024. // maybe our cached address went bad - try one more time
  1025. m_fRedoLookup = TRUE;
  1026. fConnect = TRUE;
  1027. }
  1028. LeaveCS(&m_cs);
  1029. if (fConnect)
  1030. Connect();
  1031. return IXP_E_CONN;
  1032. }
  1033. return NOERROR;
  1034. }
  1035. /////////////////////////////////////////////////////////////////////////////
  1036. //
  1037. // CAsyncConn::OnClose
  1038. //
  1039. // called when a connection is dropped
  1040. //
  1041. HRESULT CAsyncConn::OnClose(ASYNCSTATE asNew)
  1042. {
  1043. MSG msg;
  1044. EnterCS(&m_cs);
  1045. // unregister and clean up the socket
  1046. Assert(m_sock != INVALID_SOCKET);
  1047. closesocket(m_sock);
  1048. m_sock = INVALID_SOCKET;
  1049. if (FLOGSESSION && m_pszServer)
  1050. {
  1051. char szBuffer[256], lb[256];
  1052. if (LoadString(g_hLocRes, idsNlogErrConnClosed, lb, 256))
  1053. {
  1054. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, m_iLastError);
  1055. m_pLogFile->DebugLog(szBuffer);
  1056. }
  1057. }
  1058. while (PeekMessage(&msg, m_hwnd, SPM_WSA_SELECT, SPM_WSA_GETHOSTBYNAME, PM_REMOVE))
  1059. {
  1060. DOUTL(2, "Flushing pending socket messages...");
  1061. }
  1062. LeaveCS(&m_cs);
  1063. ChangeState(asNew, AE_CLOSE);
  1064. EnterCS(&m_cs);
  1065. CleanUp();
  1066. LeaveCS(&m_cs);
  1067. return NOERROR;
  1068. }
  1069. /////////////////////////////////////////////////////////////////////////////
  1070. //
  1071. // CAsyncConn::OnRead
  1072. //
  1073. // called when an FD_READ notification is received
  1074. //
  1075. HRESULT CAsyncConn::OnRead()
  1076. {
  1077. HRESULT hr;
  1078. int iRecv;
  1079. char szRecv[STREAM_BUFSIZE];
  1080. EnterCS(&m_cs);
  1081. if (m_state < AS_CONNECTED)
  1082. {
  1083. Assert(FALSE);
  1084. LeaveCS(&m_cs);
  1085. return IXP_E_NOT_CONNECTED;
  1086. }
  1087. iRecv = recv(m_sock, szRecv, sizeof(szRecv), 0);
  1088. m_iLastError = WSAGetLastError();
  1089. LeaveCS(&m_cs);
  1090. if (SOCKET_ERROR == iRecv)
  1091. {
  1092. hr = IXP_E_CONN_RECV;
  1093. }
  1094. else if (iRecv == 0)
  1095. {
  1096. // this means the server has disconnected us.
  1097. //$TODO - not sure what we should do here
  1098. hr = IXP_E_NOT_CONNECTED;
  1099. }
  1100. else
  1101. {
  1102. hr = OnDataAvail(szRecv, iRecv, FALSE);
  1103. }
  1104. return hr;
  1105. }
  1106. /////////////////////////////////////////////////////////////////////////////
  1107. //
  1108. // CAsyncConn::OnDataAvail
  1109. //
  1110. // called when there is incoming data to be queued
  1111. //
  1112. HRESULT CAsyncConn::OnDataAvail(LPSTR pszRecv, int iRecv, BOOL fIncomplete)
  1113. {
  1114. HRESULT hr = NOERROR;
  1115. BOOL fNotify = FALSE, fHandshake = FALSE, fClose = FALSE;
  1116. PRECVBUFQ pNew;
  1117. int iQueue = 0;
  1118. LPSTR pszFree = NULL;
  1119. ASYNCSTATE as;
  1120. EnterCS(&m_cs);
  1121. if (m_state < AS_CONNECTED)
  1122. {
  1123. Assert(FALSE);
  1124. hr = IXP_E_NOT_CONNECTED;
  1125. goto error;
  1126. }
  1127. if (m_fSecure)
  1128. {
  1129. SECURITY_STATUS scRet;
  1130. int cbEaten = 0;
  1131. if (m_cbExtra)
  1132. {
  1133. Assert(m_pbExtra);
  1134. // there's data left over from the last call to DecryptData
  1135. if (MemAlloc((LPVOID*)&pszFree, m_cbExtra + iRecv))
  1136. {
  1137. // combine the extra and new buffers
  1138. CopyMemory(pszFree, m_pbExtra, m_cbExtra);
  1139. CopyMemory(pszFree + m_cbExtra, pszRecv, iRecv);
  1140. pszRecv = pszFree;
  1141. iRecv += m_cbExtra;
  1142. MemFree(m_pbExtra);
  1143. m_pbExtra = NULL;
  1144. m_cbExtra = 0;
  1145. }
  1146. else
  1147. {
  1148. hr = E_OUTOFMEMORY;
  1149. goto error;
  1150. }
  1151. }
  1152. scRet = DecryptData(&m_hContext, pszRecv, iRecv, &iQueue, &cbEaten);
  1153. if (scRet == ERROR_SUCCESS || scRet == SEC_E_INCOMPLETE_MESSAGE)
  1154. {
  1155. if (cbEaten != iRecv)
  1156. {
  1157. // we need to save away the extra bytes until we receive more data
  1158. Assert(cbEaten < iRecv);
  1159. DOUTL(2, "cbEaten = %d, iRecv = %d, cbExtra = %d", cbEaten, iRecv, iRecv - cbEaten);
  1160. if (MemAlloc((LPVOID*)&m_pbExtra, iRecv - cbEaten))
  1161. {
  1162. m_cbExtra = iRecv - cbEaten;
  1163. CopyMemory(m_pbExtra, pszRecv + cbEaten, m_cbExtra);
  1164. }
  1165. else
  1166. {
  1167. hr = E_OUTOFMEMORY;
  1168. goto error;
  1169. }
  1170. }
  1171. if (scRet == SEC_E_INCOMPLETE_MESSAGE)
  1172. goto error;
  1173. }
  1174. else
  1175. {
  1176. // security error, so disconnect.
  1177. fClose = TRUE;
  1178. hr = E_FAIL;
  1179. goto error;
  1180. }
  1181. }
  1182. else
  1183. iQueue = iRecv;
  1184. if (MemAlloc((LPVOID*)&pNew, sizeof(RECVBUFQ) + iQueue - sizeof(char)))
  1185. {
  1186. pNew->pNext = NULL;
  1187. pNew->cbLen = iQueue;
  1188. CopyMemory(pNew->szBuf, pszRecv, iQueue);
  1189. if (m_pRecvTail)
  1190. {
  1191. m_pRecvTail->pNext = pNew;
  1192. if ((AS_CONNECTED == m_state) && m_fNeedRecvNotify)
  1193. fNotify = FEndLine(pszRecv, iQueue);
  1194. }
  1195. else
  1196. {
  1197. Assert(!m_pRecvHead);
  1198. m_pRecvHead = pNew;
  1199. if (AS_CONNECTED == m_state)
  1200. {
  1201. fNotify = FEndLine(pszRecv, iQueue);
  1202. if (!fNotify)
  1203. m_fNeedRecvNotify = TRUE;
  1204. }
  1205. }
  1206. m_pRecvTail = pNew;
  1207. hr = NOERROR;
  1208. }
  1209. else
  1210. {
  1211. //$TODO - we should disconnect here and notify the caller
  1212. hr = E_OUTOFMEMORY;
  1213. }
  1214. // notify the owner that there is at least one line of data available
  1215. if (fNotify)
  1216. {
  1217. m_fNeedRecvNotify = FALSE;
  1218. as = m_state;
  1219. }
  1220. else if (AS_HANDSHAKING == m_state && SUCCEEDED(hr) && !fIncomplete)
  1221. {
  1222. Assert(!m_fSecure);
  1223. fHandshake = TRUE;
  1224. }
  1225. LeaveCS(&m_cs);
  1226. if (fNotify)
  1227. ChangeState(as, AE_RECV);
  1228. else if (fHandshake)
  1229. OnRecvHandshakeData();
  1230. EnterCS(&m_cs);
  1231. error:
  1232. LeaveCS(&m_cs);
  1233. SafeMemFree(pszFree);
  1234. if (fClose)
  1235. Close();
  1236. return hr;
  1237. }
  1238. /////////////////////////////////////////////////////////////////////////////
  1239. //
  1240. // CAsyncConn::OnWrite
  1241. //
  1242. // called when an FD_WRITE notification is received
  1243. //
  1244. HRESULT CAsyncConn::OnWrite()
  1245. {
  1246. int iSent;
  1247. ASYNCEVENT ae;
  1248. ASYNCSTATE as;
  1249. EnterCS(&m_cs);
  1250. m_cbSent = 0;
  1251. if (m_state < AS_CONNECTED)
  1252. {
  1253. Assert(FALSE);
  1254. LeaveCS(&m_cs);
  1255. return IXP_E_NOT_CONNECTED;
  1256. }
  1257. if (m_cbQueued)
  1258. {
  1259. // send some more data from the queued buffer
  1260. while (m_cbQueued && ((iSent = send(m_sock, m_lpbQueueCur, m_cbQueued, 0)) != SOCKET_ERROR))
  1261. {
  1262. m_cbSent += iSent;
  1263. m_lpbQueueCur += iSent;
  1264. m_cbQueued -= iSent;
  1265. }
  1266. if (m_cbQueued)
  1267. {
  1268. m_iLastError = WSAGetLastError();
  1269. if (WSAEWOULDBLOCK != m_iLastError)
  1270. {
  1271. //$TODO - handle this error somehow
  1272. Assert(FALSE);
  1273. }
  1274. }
  1275. else
  1276. {
  1277. MemFree(m_lpbQueued);
  1278. m_lpbQueued = m_lpbQueueCur = NULL;
  1279. }
  1280. }
  1281. if (m_pStream && !m_cbQueued)
  1282. {
  1283. char rgb[STREAM_BUFSIZE]; //$REVIEW - should we heap allocate this instead?
  1284. DWORD cbRead;
  1285. HRESULT hr;
  1286. // send some more data from the queued stream
  1287. while (SUCCEEDED(hr = m_pStream->Read(rgb, STREAM_BUFSIZE, &cbRead)) && cbRead)
  1288. {
  1289. hr = SendBytes(rgb, cbRead, &iSent, m_fStuffDots, &m_chPrev);
  1290. if (FAILED(hr))
  1291. {
  1292. if (WSAEWOULDBLOCK != m_iLastError)
  1293. {
  1294. //$TODO - handle this error somehow, probably free the stream
  1295. Assert(FALSE);
  1296. }
  1297. break;
  1298. }
  1299. else
  1300. m_cbSent += iSent;
  1301. }
  1302. if (!cbRead)
  1303. {
  1304. m_pStream->Release();
  1305. m_pStream = NULL;
  1306. }
  1307. }
  1308. as = m_state;
  1309. if (!m_cbQueued)
  1310. ae = AE_SENDDONE;
  1311. else
  1312. ae = AE_WRITE;
  1313. LeaveCS(&m_cs);
  1314. ChangeState(as, ae);
  1315. return NOERROR;
  1316. }
  1317. /////////////////////////////////////////////////////////////////////////////
  1318. //
  1319. // CAsyncConn::ChangeState
  1320. //
  1321. // changes the connection state, notifies the owner
  1322. //
  1323. void CAsyncConn::ChangeState(ASYNCSTATE asNew, ASYNCEVENT ae)
  1324. {
  1325. ASYNCSTATE asOld;
  1326. IAsyncConnCB *pCB;
  1327. EnterCS(&m_cs);
  1328. asOld = m_state;
  1329. m_state = asNew;
  1330. m_dwLastActivity = GetTickCount();
  1331. pCB = m_pCB;
  1332. // pCB->AddRef(); $BUGBUG - we REALLY need to handle this, but IMAP4 doesn't call close before it's destructor
  1333. #ifdef DEBUG
  1334. IxpAssert(m_cLock == 1);
  1335. #endif
  1336. LeaveCS(&m_cs);
  1337. pCB->OnNotify(asOld, asNew, ae);
  1338. // pCB->Release();
  1339. }
  1340. /////////////////////////////////////////////////////////////////////////////
  1341. //
  1342. // CAsyncConn::IReadLines
  1343. //
  1344. // Purpose: retrieves one or all available complete lines from the buffer
  1345. //
  1346. // Args: ppszBuf - pointer to receive allocated buffer, caller must free
  1347. // pcbRead - pointer to receive line length
  1348. // pcLines - pointer to receive number or lines read
  1349. // fOne - TRUE if only reading one line
  1350. //
  1351. // Returns: NOERROR - a complete line was read
  1352. // IXP_E_INCOMPLETE - a complete line is not available
  1353. // E_OUTOFMEMORY - mem error
  1354. //
  1355. // Comments:
  1356. // If IXP_E_INCOMPLETE is returned or if there is extra data buffered after the
  1357. // the last complete line, the caller will recieve an AE_RECV event
  1358. // the next time a complete line is available.
  1359. //
  1360. HRESULT CAsyncConn::IReadLines(char **ppszBuf, int *pcbRead, int *pcLines, BOOL fOne)
  1361. {
  1362. HRESULT hr;
  1363. int iRead = 0, iScan = 0, iLines = 0;
  1364. char * pszBuf = NULL;
  1365. char * psz;
  1366. int iOffset, iLeft;
  1367. PRECVBUFQ pRecv;
  1368. BOOL fFound = FALSE;
  1369. if (!m_pRecvHead)
  1370. {
  1371. hr = IXP_E_INCOMPLETE;
  1372. goto error;
  1373. }
  1374. pRecv = m_pRecvHead;
  1375. iOffset = m_iRecvOffset;
  1376. while (pRecv)
  1377. {
  1378. psz = pRecv->szBuf + iOffset;
  1379. iLeft = pRecv->cbLen - iOffset;
  1380. while (iLeft--)
  1381. {
  1382. iScan++;
  1383. if (*psz++ == '\n')
  1384. {
  1385. iRead = iScan;
  1386. iLines++;
  1387. if (fOne)
  1388. {
  1389. #if 0
  1390. // One-eyed t-crash fix
  1391. while (iLeft > 0 && (*psz == '\r' || *psz == '\n'))
  1392. {
  1393. iLeft--;
  1394. iScan++;
  1395. psz++;
  1396. iRead++;
  1397. }
  1398. #endif
  1399. break;
  1400. }
  1401. }
  1402. }
  1403. if (iLines && fOne)
  1404. break;
  1405. iOffset = 0;
  1406. pRecv = pRecv->pNext;
  1407. }
  1408. if (iLines)
  1409. {
  1410. int iCopy = 0, cb;
  1411. if (!MemAlloc((LPVOID*)&pszBuf, iRead + 1))
  1412. {
  1413. hr = E_OUTOFMEMORY;
  1414. goto error;
  1415. }
  1416. while (iCopy < iRead)
  1417. {
  1418. cb = min(iRead-iCopy, m_pRecvHead->cbLen - m_iRecvOffset);
  1419. CopyMemory(pszBuf + iCopy, m_pRecvHead->szBuf + m_iRecvOffset, cb);
  1420. iCopy += cb;
  1421. if (cb == (m_pRecvHead->cbLen - m_iRecvOffset))
  1422. {
  1423. PRECVBUFQ pTemp = m_pRecvHead;
  1424. m_pRecvHead = m_pRecvHead->pNext;
  1425. if (!m_pRecvHead)
  1426. m_pRecvTail = NULL;
  1427. m_iRecvOffset = 0;
  1428. MemFree(pTemp);
  1429. }
  1430. else
  1431. {
  1432. Assert(iCopy == iRead);
  1433. m_iRecvOffset += cb;
  1434. }
  1435. }
  1436. for (iScan = 0, psz = pszBuf; iScan < iCopy; iScan++, psz++)
  1437. if (*psz == 0)
  1438. *psz = ' ';
  1439. pszBuf[iCopy] = 0;
  1440. hr = NOERROR;
  1441. }
  1442. else
  1443. hr = IXP_E_INCOMPLETE;
  1444. // set the flag to notify when a complete line is received
  1445. if ((IXP_E_INCOMPLETE == hr) || (m_pRecvHead && !fOne))
  1446. m_fNeedRecvNotify = TRUE;
  1447. error:
  1448. *ppszBuf = pszBuf;
  1449. *pcbRead = iRead;
  1450. *pcLines = iLines;
  1451. return hr;
  1452. }
  1453. HRESULT CAsyncConn::ReadAllBytes(char **ppszBuf, int *pcbRead)
  1454. {
  1455. HRESULT hr = S_OK;
  1456. int iRead = 0, iCopy = 0, cb;
  1457. char * pszBuf = NULL;
  1458. int iOffset;
  1459. PRECVBUFQ pTemp;
  1460. if (!m_pRecvHead)
  1461. {
  1462. hr = IXP_E_INCOMPLETE;
  1463. goto error;
  1464. }
  1465. // calculate how much to copy
  1466. pTemp = m_pRecvHead;
  1467. iOffset = m_iRecvOffset;
  1468. while (pTemp)
  1469. {
  1470. iCopy += pTemp->cbLen - iOffset;
  1471. iOffset = 0;
  1472. pTemp = pTemp->pNext;
  1473. }
  1474. if (!MemAlloc((LPVOID*)&pszBuf, iCopy))
  1475. {
  1476. hr = E_OUTOFMEMORY;
  1477. goto error;
  1478. }
  1479. while (m_pRecvHead)
  1480. {
  1481. cb = min(iCopy-iRead, m_pRecvHead->cbLen - m_iRecvOffset);
  1482. CopyMemory(pszBuf + iRead, m_pRecvHead->szBuf + m_iRecvOffset, cb);
  1483. iRead += cb;
  1484. pTemp = m_pRecvHead;
  1485. m_pRecvHead = m_pRecvHead->pNext;
  1486. if (!m_pRecvHead)
  1487. m_pRecvTail = NULL;
  1488. m_iRecvOffset = 0;
  1489. MemFree(pTemp);
  1490. }
  1491. Assert(!m_pRecvHead && !m_iRecvOffset);
  1492. error:
  1493. *ppszBuf = pszBuf;
  1494. *pcbRead = iRead;
  1495. return hr;
  1496. }
  1497. HWND CAsyncConn::CreateWnd()
  1498. {
  1499. WNDCLASS wc;
  1500. if (!GetClassInfo(g_hLocRes, s_szConnWndClass, &wc))
  1501. {
  1502. wc.style = 0;
  1503. wc.lpfnWndProc = CAsyncConn::SockWndProc;
  1504. wc.cbClsExtra = 0;
  1505. wc.cbWndExtra = 0;
  1506. wc.hInstance = g_hLocRes;
  1507. wc.hIcon = NULL;
  1508. wc.hCursor = NULL;
  1509. wc.hbrBackground = NULL;
  1510. wc.lpszMenuName = NULL;
  1511. wc.lpszClassName = s_szConnWndClass;
  1512. RegisterClass(&wc);
  1513. }
  1514. m_hwnd = CreateWindowEx(0,
  1515. s_szConnWndClass,
  1516. s_szConnWndClass,
  1517. WS_POPUP,
  1518. CW_USEDEFAULT,
  1519. CW_USEDEFAULT,
  1520. CW_USEDEFAULT,
  1521. CW_USEDEFAULT,
  1522. NULL,
  1523. NULL,
  1524. g_hLocRes,
  1525. (LPVOID)this);
  1526. return m_hwnd;
  1527. }
  1528. LRESULT CALLBACK CAsyncConn::SockWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1529. {
  1530. CAsyncConn *pThis = (CAsyncConn*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  1531. switch (msg)
  1532. {
  1533. case WM_TIMER:
  1534. Assert(pThis);
  1535. if (SPM_ASYNCTIMER == wParam && pThis)
  1536. pThis->OnWatchDogTimer();
  1537. break;
  1538. case WM_NCCREATE:
  1539. pThis = (CAsyncConn*)((LPCREATESTRUCT)lParam)->lpCreateParams;
  1540. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pThis);
  1541. break;
  1542. case WM_NCDESTROY:
  1543. pThis->m_hwnd = NULL;
  1544. break;
  1545. case SPM_WSA_SELECT:
  1546. case SPM_WSA_GETHOSTBYNAME:
  1547. pThis->OnNotify(msg, wParam, lParam);
  1548. return 0;
  1549. }
  1550. return DefWindowProc(hwnd, msg, wParam, lParam);
  1551. }
  1552. HRESULT CAsyncConn::TryNextSecurityPkg()
  1553. {
  1554. HRESULT hr = NOERROR;
  1555. SecBuffer OutBuffer;
  1556. SECURITY_STATUS sc;
  1557. EnterCS(&m_cs);
  1558. sc = InitiateSecConnection(m_pszServer,
  1559. FALSE,
  1560. &m_iCurSecPkg,
  1561. &m_hContext,
  1562. &OutBuffer);
  1563. LeaveCS(&m_cs);
  1564. if (SEC_I_CONTINUE_NEEDED == sc)
  1565. {
  1566. if (FLOGSESSION)
  1567. {
  1568. char szBuffer[256], lb[256];
  1569. if (LoadString(g_hLocRes, idsNegotiatingSSL, lb, 256))
  1570. {
  1571. EnterCS(&m_cs);
  1572. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), lb, s_SecProviders[m_iCurSecPkg].pszName);
  1573. m_pLogFile->DebugLog(szBuffer);
  1574. LeaveCS(&m_cs);
  1575. }
  1576. }
  1577. ChangeState(AS_HANDSHAKING, AE_CONNECTDONE);
  1578. if (OutBuffer.cbBuffer && OutBuffer.pvBuffer)
  1579. {
  1580. int iSent;
  1581. hr = SendBytes((char *)OutBuffer.pvBuffer, OutBuffer.cbBuffer, &iSent);
  1582. g_FreeContextBuffer(OutBuffer.pvBuffer);
  1583. }
  1584. else
  1585. {
  1586. AssertSz(0, "Preventing a NULL, 0 sized call to send");
  1587. }
  1588. }
  1589. else
  1590. {
  1591. // we can't connect securely, so error out and disconnect
  1592. Close();
  1593. }
  1594. return hr;
  1595. }
  1596. HRESULT CAsyncConn::OnSSLError()
  1597. {
  1598. HRESULT hr = NOERROR;
  1599. BOOL fReconnect;
  1600. EnterCS(&m_cs);
  1601. if (m_iCurSecPkg + 1 < g_cSSLProviders)
  1602. {
  1603. m_iCurSecPkg++;
  1604. fReconnect = TRUE;
  1605. }
  1606. else
  1607. {
  1608. m_iCurSecPkg = 0;
  1609. fReconnect = FALSE;
  1610. }
  1611. LeaveCS(&m_cs);
  1612. if (fReconnect)
  1613. {
  1614. OnClose(AS_RECONNECTING);
  1615. Connect();
  1616. }
  1617. else
  1618. OnClose(AS_DISCONNECTED);
  1619. return hr;
  1620. }
  1621. HRESULT CAsyncConn::OnRecvHandshakeData()
  1622. {
  1623. HRESULT hr;
  1624. LPSTR pszBuf;
  1625. int cbRead, cbEaten;
  1626. SECURITY_STATUS sc;
  1627. SecBuffer OutBuffer;
  1628. if (SUCCEEDED(hr = ReadAllBytes(&pszBuf, &cbRead)))
  1629. {
  1630. EnterCS(&m_cs);
  1631. sc = ContinueHandshake(m_iCurSecPkg, &m_hContext, pszBuf, cbRead, &cbEaten, &OutBuffer);
  1632. LeaveCS(&m_cs);
  1633. // if there's a response to send, then do it
  1634. if (OutBuffer.cbBuffer && OutBuffer.pvBuffer)
  1635. {
  1636. int iSent;
  1637. hr = SendBytes((char *)OutBuffer.pvBuffer, OutBuffer.cbBuffer, &iSent);
  1638. g_FreeContextBuffer(OutBuffer.pvBuffer);
  1639. }
  1640. if (sc == SEC_E_OK)
  1641. {
  1642. HRESULT hrCert;
  1643. EnterCS(&m_cs);
  1644. m_fSecure = TRUE;
  1645. LPSRVIGNORABLEERROR pIgnorerror = NULL;
  1646. g_pSrvErrRoot = FindOrAddServer(m_pszServer, g_pSrvErrRoot, &pIgnorerror);
  1647. hrCert = ChkCertificateTrust(&m_hContext, m_pszServer);
  1648. LeaveCS(&m_cs);
  1649. if (hrCert && (!pIgnorerror || (hrCert != pIgnorerror->hrError)))
  1650. {
  1651. TCHAR szError[CCHMAX_RES + CCHMAX_RES],
  1652. szPrompt[CCHMAX_RES],
  1653. szCaption[CCHMAX_RES];
  1654. IAsyncConnPrompt *pPrompt;
  1655. DWORD dw;
  1656. const DWORD cLineWidth = 64;
  1657. LoadString(g_hLocRes, idsSecurityErr, szCaption, ARRAYSIZE(szCaption));
  1658. LoadString(g_hLocRes, idsInvalidCert, szError, ARRAYSIZE(szError));
  1659. LoadString(g_hLocRes, idsIgnoreSecureErr, szPrompt, ARRAYSIZE(szPrompt));
  1660. StrCatBuff(szError, c_szCRLFCRLF, ARRAYSIZE(szError));
  1661. dw = lstrlen(szError);
  1662. if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | cLineWidth,
  1663. NULL, hrCert, 0, szError+dw, ARRAYSIZE(szError)-dw, NULL))
  1664. {
  1665. TCHAR szErrNum[16];
  1666. wnsprintf(szErrNum, ARRAYSIZE(szErrNum), "0x%x", hrCert);
  1667. StrCatBuff(szError, szErrNum, ARRAYSIZE(szError));
  1668. }
  1669. Assert(lstrlen(szError) + lstrlen(szPrompt) + lstrlen("\r\n\r\n") < ARRAYSIZE(szError));
  1670. StrCatBuff(szError, c_szCRLFCRLF, ARRAYSIZE(szError));
  1671. StrCatBuff(szError, szPrompt, ARRAYSIZE(szError));
  1672. EnterCS(&m_cs);
  1673. pPrompt = m_pPrompt;
  1674. if (pPrompt)
  1675. pPrompt->AddRef();
  1676. LeaveCS(&m_cs);
  1677. EnterPausedState();
  1678. if (pPrompt && IDYES == pPrompt->OnPrompt(hrCert, szError, szCaption, MB_YESNO | MB_ICONEXCLAMATION | MB_SETFOREGROUND))
  1679. {
  1680. // Set ignorable error
  1681. if(pIgnorerror)
  1682. pIgnorerror->hrError = hrCert;
  1683. ChangeState(AS_CONNECTED, AE_CONNECTDONE);
  1684. if (cbEaten < cbRead)
  1685. {
  1686. // there were bytes left over, so hold onto them
  1687. hr = OnDataAvail(pszBuf + cbEaten, cbRead - cbEaten, sc == SEC_E_INCOMPLETE_MESSAGE);
  1688. }
  1689. LeavePausedState();
  1690. }
  1691. else
  1692. Close();
  1693. if (pPrompt)
  1694. pPrompt->Release();
  1695. MemFree(pszBuf);
  1696. return hr;
  1697. }
  1698. ChangeState(AS_CONNECTED, AE_CONNECTDONE);
  1699. }
  1700. else if (sc != SEC_I_CONTINUE_NEEDED && sc != SEC_E_INCOMPLETE_MESSAGE)
  1701. {
  1702. // unexpected error - we should reset the socket and try the next package
  1703. DOUTL(2, "unexpected error from ContinueHandshake() - closing socket.");
  1704. return OnSSLError();
  1705. }
  1706. else
  1707. {
  1708. Assert(sc == SEC_I_CONTINUE_NEEDED || sc == SEC_E_INCOMPLETE_MESSAGE);
  1709. // stay inside the handshake loop, waiting for more data to arrive
  1710. }
  1711. if (cbEaten < cbRead)
  1712. {
  1713. // there were bytes left over, so hold onto them
  1714. hr = OnDataAvail(pszBuf + cbEaten, cbRead - cbEaten, sc == SEC_E_INCOMPLETE_MESSAGE);
  1715. }
  1716. MemFree(pszBuf);
  1717. }
  1718. return hr;
  1719. }
  1720. void CAsyncConn::CleanUp()
  1721. {
  1722. PRECVBUFQ pRecv = m_pRecvHead, pTemp;
  1723. SafeMemFree(m_lpbQueued);
  1724. m_lpbQueueCur = NULL;
  1725. m_cbQueued = 0;
  1726. SafeMemFree(m_pbExtra);
  1727. m_cbExtra = 0;
  1728. SafeRelease(m_pStream);
  1729. while (pRecv)
  1730. {
  1731. pTemp = pRecv;
  1732. pRecv = pRecv->pNext;
  1733. MemFree(pTemp);
  1734. }
  1735. m_pRecvHead = m_pRecvTail = NULL;
  1736. m_iRecvOffset = 0;
  1737. m_iLastError = 0;
  1738. m_fNeedRecvNotify = FALSE;
  1739. m_fSecure = FALSE;
  1740. m_fPaused = FALSE;
  1741. }
  1742. void CAsyncConn::EnterPausedState()
  1743. {
  1744. EnterCS(&m_cs);
  1745. m_fPaused = TRUE;
  1746. m_dwEventMask = 0;
  1747. StopWatchDog();
  1748. LeaveCS(&m_cs);
  1749. }
  1750. void CAsyncConn::LeavePausedState()
  1751. {
  1752. DWORD dwEventMask;
  1753. EnterCS(&m_cs);
  1754. m_fPaused = FALSE;
  1755. dwEventMask = m_dwEventMask;
  1756. LeaveCS(&m_cs);
  1757. if (dwEventMask & FD_CLOSE)
  1758. Close();
  1759. else
  1760. {
  1761. if (dwEventMask & FD_READ)
  1762. OnRead();
  1763. if (dwEventMask & FD_WRITE)
  1764. OnWrite();
  1765. }
  1766. }
  1767. /////////////////////////////////////////////////////////////////////////////
  1768. //
  1769. // UTILITY FUNCTIONS
  1770. //
  1771. /////////////////////////////////////////////////////////////////////////////
  1772. BOOL FEndLine(char *psz, int iLen)
  1773. {
  1774. while (iLen--)
  1775. {
  1776. if (*psz++ == '\n')
  1777. return TRUE;
  1778. }
  1779. return FALSE;
  1780. }