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.

2701 lines
77 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. downloader.cpp
  5. Abstract :
  6. Main Source file for downloader.
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #include "malloc.h"
  12. #if !defined(BITS_V12_ON_NT4)
  13. #include "downloader.tmh"
  14. #endif
  15. BOOL bLanManHashDisabled;
  16. //
  17. // Maximum request size. Larger is more efficient because the HTTP headers are not repeated;
  18. // multiples of the disk block size are probably more efficient to write.
  19. //
  20. // I chose 128kb because throughput degrades by a factor of 5 with 144kb, 256kb and 1024kb.
  21. // 96kb is OK but not quite as good on my machine.
  22. //
  23. #define MAX_IIS_SEND_SIZE (128*1024)
  24. void SafeCloseInternetHandle( HINTERNET & h )
  25. {
  26. if (h)
  27. {
  28. InternetCloseHandle( h );
  29. h = NULL;
  30. }
  31. }
  32. #define ACCEPT_ENCODING_STRING _T("Accept-encoding: identity")
  33. BOOL NeedRetry( QMErrInfo * );
  34. bool NeedCredentials( HINTERNET hRequest, DWORD err );
  35. bool IsPossibleProxyFailure( DWORD err );
  36. DWORD GetRequestStatus( HINTERNET hRequest ) throw( ComError );
  37. struct CredentialsApplied
  38. {
  39. bool bIsDefault; // TRUE if the default credentials where applied.
  40. DWORD dwTarget; // proxy or server
  41. DWORD dwScheme; // the type of security required
  42. };
  43. bool
  44. ApplyCredentials(
  45. HINTERNET hRequest,
  46. const CCredentialsContainer * Credentials,
  47. WCHAR UserName[],
  48. WCHAR Password[],
  49. CredentialsApplied *pCredentials=NULL
  50. ) throw( ComError );
  51. bool
  52. ApplySchemeCredentials(
  53. HINTERNET hRequest,
  54. DWORD dwTarget,
  55. DWORD dwScheme,
  56. const CCredentialsContainer * Credentials,
  57. WCHAR UserName[],
  58. WCHAR Password[]
  59. ) throw( ComError );
  60. void
  61. EnablePassport(
  62. HINTERNET hInternet
  63. ) throw( ComError );
  64. HRESULT
  65. CheckReplyRange(
  66. HINTERNET hRequest,
  67. UINT64 CorrectStart,
  68. UINT64 CorrectEnd,
  69. UINT64 CorrectTotal
  70. );
  71. HRESULT
  72. CheckReplyLength(
  73. HINTERNET hRequest,
  74. UINT64 CorrectOffset,
  75. UINT64 CorrectTotal
  76. );
  77. #ifndef USE_WININET
  78. VOID CALLBACK
  79. HttpRequestCallback(
  80. IN HINTERNET hInternet,
  81. IN DWORD_PTR dwContext,
  82. IN DWORD dwInternetStatus,
  83. IN LPVOID lpvStatusInformation OPTIONAL,
  84. IN DWORD dwStatusInformationLength
  85. );
  86. DWORD
  87. MapSecureHttpErrorCode(
  88. DWORD flags
  89. );
  90. #endif
  91. CACHED_AUTOPROXY * g_ProxyCache;
  92. BYTE g_FileDataBuffer[ FILE_DATA_BUFFER_LEN ];
  93. HRESULT
  94. CreateHttpDownloader(
  95. Downloader **ppDownloader,
  96. QMErrInfo *pErrInfo
  97. )
  98. {
  99. Downloader * pDownloader = 0;
  100. try
  101. {
  102. *ppDownloader = NULL;
  103. g_ProxyCache = new CACHED_AUTOPROXY;
  104. pDownloader = new CProgressiveDL( pErrInfo );
  105. *ppDownloader = pDownloader;
  106. return S_OK;
  107. }
  108. catch ( ComError err )
  109. {
  110. delete g_ProxyCache; g_ProxyCache = 0;
  111. delete pDownloader;
  112. return err.Error();
  113. }
  114. }
  115. void
  116. DeleteHttpDownloader(
  117. Downloader *pDownloader
  118. )
  119. {
  120. CProgressiveDL * ptr = (CProgressiveDL *) pDownloader;
  121. delete ptr;
  122. delete g_ProxyCache; g_ProxyCache = 0;
  123. }
  124. /////////////////////////////////////////////////////////////////////////////
  125. // CProgressiveDL
  126. CProgressiveDL::CProgressiveDL(
  127. QMErrInfo *pErrInfo
  128. ) :
  129. m_bThrottle( TRUE ),
  130. m_wupdinfo( NULL ),
  131. m_hOpenRequest( NULL ),
  132. m_hFile( INVALID_HANDLE_VALUE )
  133. {
  134. m_pQMInfo = pErrInfo;
  135. }
  136. CProgressiveDL::~CProgressiveDL()
  137. {
  138. ASSERT( m_hFile == INVALID_HANDLE_VALUE );
  139. }
  140. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  141. // Public function Download()
  142. // Accepts a URL and destination to download, callback to report various status
  143. // Input: url, destination, flags, callback for status
  144. // Output: todo handle
  145. // Return: hresult
  146. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  147. HRESULT
  148. CProgressiveDL::Download(
  149. LPCTSTR szURL,
  150. LPCTSTR szDest,
  151. UINT64 Offset,
  152. ITransferCallback * Callbacks,
  153. QMErrInfo *pQMErrInfo,
  154. HANDLE hToken,
  155. BOOL bThrottle,
  156. const PROXY_SETTINGS * ProxySettings,
  157. const CCredentialsContainer * Credentials,
  158. const StringHandle HostId
  159. )
  160. {
  161. HRESULT hr = S_OK;
  162. DWORD dwThreadID;
  163. m_Callbacks = Callbacks;
  164. m_pQMInfo = pQMErrInfo;
  165. m_bThrottle = bThrottle;
  166. m_pQMInfo->result = QM_FILE_FATAL_ERROR;
  167. ASSERT( Callbacks );
  168. ASSERT( pQMErrInfo );
  169. if (!m_pQMInfo)
  170. {
  171. return E_FAIL;
  172. }
  173. if ((!szURL) || (!szDest))
  174. {
  175. SetError( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_HRESULT, E_INVALIDARG, "NULL file name" );
  176. return E_FAIL;
  177. }
  178. ASSERT( wcslen( szURL ) <= INTERNET_MAX_URL_LENGTH );
  179. ASSERT( wcslen( szDest ) <= MAX_PATH );
  180. LogDl( "---------------------------------------------------------------------" );
  181. LogDl( "Downloading file %!ts! offset %d", szDest, DWORD(Offset) );
  182. m_pQMInfo->result = QM_IN_PROGRESS;
  183. do
  184. {
  185. hr = DownloadFile( hToken, ProxySettings, Credentials, szURL, szDest, Offset, HostId );
  186. }
  187. while ( hr == E_RETRY );
  188. if (hr == S_OK)
  189. {
  190. m_pQMInfo->result = QM_FILE_DONE;
  191. LogDl( "Done file %!ts!", szDest );
  192. }
  193. else if ( hr == S_FALSE )
  194. {
  195. m_pQMInfo->result = QM_FILE_ABORTED;
  196. LogDl( "File %!ts! aborted", szDest );
  197. }
  198. else if ( m_pQMInfo->result != QM_SERVER_FILE_CHANGED )
  199. {
  200. ASSERT( IsErrorSet() );
  201. if (NeedRetry(m_pQMInfo))
  202. {
  203. m_pQMInfo->result = QM_FILE_TRANSIENT_ERROR;
  204. }
  205. else
  206. {
  207. m_pQMInfo->result = QM_FILE_FATAL_ERROR;
  208. }
  209. }
  210. ASSERT( m_pQMInfo->result != QM_IN_PROGRESS );
  211. LogDl( "---------------------------------------------------------------------" );
  212. // if abort request came in after file failed, overwrite failed flag.
  213. if ( (QM_FILE_DONE != m_pQMInfo->result) && IsAbortRequested() )
  214. {
  215. m_pQMInfo->result = QM_FILE_ABORTED;
  216. }
  217. ASSERT( m_hFile == INVALID_HANDLE_VALUE );
  218. return hr;
  219. }
  220. HRESULT
  221. CProgressiveDL::DownloadFile(
  222. HANDLE hToken,
  223. const PROXY_SETTINGS * ProxySettings,
  224. const CCredentialsContainer * Credentials,
  225. LPCTSTR Url,
  226. LPCWSTR Path,
  227. UINT64 Offset,
  228. StringHandle HostId
  229. )
  230. {
  231. HRESULT hr = S_OK;
  232. ASSERT( m_wupdinfo == NULL );
  233. ASSERT( m_hOpenRequest == NULL );
  234. m_pQMInfo->Clear();
  235. try
  236. {
  237. THROW_HRESULT( CheckLanManHashDisabled() );
  238. CNestedImpersonation imp( hToken );
  239. //
  240. // Open a connection to the server, and use that data for our first estimate of the line speed.
  241. //
  242. m_wupdinfo = ConnectToUrl( Url, ProxySettings, Credentials, (const TCHAR*)HostId, m_pQMInfo );
  243. if (!m_wupdinfo)
  244. {
  245. ASSERT( IsErrorSet() );
  246. throw ComError( E_FAIL );
  247. }
  248. //
  249. // Get file size and time stamp.
  250. //
  251. if (! GetRemoteResourceInformation( m_wupdinfo, m_pQMInfo ))
  252. {
  253. ASSERT( IsErrorSet() );
  254. throw ComError( E_FAIL );
  255. }
  256. FILETIME FileTime = UINT64ToFILETIME( 0 );
  257. if (!OpenLocalDownloadFile(Path, Offset, m_wupdinfo->FileSize, m_wupdinfo->UrlModificationTime, &FileTime))
  258. {
  259. ASSERT( IsErrorSet() );
  260. throw ComError( E_FAIL );
  261. }
  262. if (m_wupdinfo->UrlModificationTime == UINT64ToFILETIME( 0 ))
  263. {
  264. ASSERT( FileTime != UINT64ToFILETIME( 0 ));
  265. m_wupdinfo->UrlModificationTime = FileTime;
  266. }
  267. // Be sure to check for an end of file before attempting
  268. // to download more bytes.
  269. if (IsFileComplete())
  270. {
  271. LogDl( "File is done already.\n" );
  272. if (!SetFileTimes())
  273. {
  274. ASSERT( IsErrorSet() );
  275. hr = E_FAIL;
  276. }
  277. hr = S_OK;
  278. }
  279. //
  280. // Transfer data from the server.
  281. //
  282. else if ( !m_bThrottle )
  283. {
  284. hr = DownloadForegroundFile();
  285. }
  286. else
  287. {
  288. if (IsAbortRequested())
  289. {
  290. throw ComError( S_FALSE );
  291. }
  292. //
  293. // Use either the server's host name or the proxy's host name to find the right network adapter..
  294. //
  295. hr = m_Network.SetInterfaceIndex( m_wupdinfo->fProxy ? m_wupdinfo->ProxyHost.get() : m_wupdinfo->HostName );
  296. if (FAILED(hr))
  297. {
  298. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "GetInterfaceIndex" );
  299. throw ComError( E_FAIL );
  300. }
  301. while (1)
  302. {
  303. hr = GetNextBlock();
  304. if ( S_FALSE == hr )
  305. break;
  306. if (FAILED(hr) )
  307. {
  308. ASSERT( hr == E_RETRY || IsErrorSet() );
  309. break;
  310. }
  311. if (IsFileComplete())
  312. {
  313. if (!SetFileTimes())
  314. {
  315. ASSERT( IsErrorSet() );
  316. hr = E_FAIL;
  317. break;
  318. }
  319. hr = S_OK;
  320. break;
  321. }
  322. }
  323. }
  324. }
  325. catch( ComError exception )
  326. {
  327. hr = exception.Error();
  328. ASSERT( IsErrorSet() || (hr == S_FALSE) );
  329. }
  330. if ( m_bThrottle )
  331. {
  332. m_Network.StopTimer();
  333. }
  334. CloseLocalFile();
  335. delete m_wupdinfo; m_wupdinfo = NULL;
  336. SafeCloseInternetHandle( m_hOpenRequest );
  337. if (FAILED(hr))
  338. {
  339. ASSERT( hr == E_RETRY || IsErrorSet() );
  340. }
  341. return hr;
  342. }
  343. HRESULT
  344. CProgressiveDL::DownloadForegroundFile()
  345. {
  346. HRESULT hr = E_FAIL;
  347. LogDl( "Starting foreground file download" );
  348. while( 1 )
  349. {
  350. //
  351. // Loop of HTTP requests. This is to work around a wininet/winhttp limitation
  352. // where request sizes can be a maximum of 4GB.
  353. //
  354. UINT64 BlockSize64 = m_wupdinfo->FileSize - m_CurrentOffset;
  355. DWORD BlockSize = (DWORD)min( BlockSize64, 2147483648 );
  356. LogDl( "Starting foreground file download request block: file size %d, offset %d, block %d",
  357. DWORD( m_wupdinfo->FileSize ), DWORD(m_CurrentOffset), BlockSize );
  358. //
  359. // Send a block request and read the reply headers.
  360. //
  361. hr = StartRangeRequest( BlockSize );
  362. if (FAILED(hr))
  363. {
  364. ASSERT( IsErrorSet() );
  365. return hr;
  366. }
  367. const DWORD MIN_FOREGROUND_BLOCK = 4096;
  368. const DWORD MAX_FOREGROUND_BLOCK = FILE_DATA_BUFFER_LEN;
  369. const DWORD FOREGROUND_BLOCK_INCREMENT = 1024;
  370. const DWORD FOREGROUND_UPDATE_RATE = 2000;
  371. DWORD ForegroundBlockSize = min( MIN_FOREGROUND_BLOCK, BlockSize );
  372. DWORD dwPrevTick = GetTickCount();
  373. while( 1 )
  374. {
  375. //
  376. // Loop of read blocks inside an individual request.
  377. //
  378. if (IsAbortRequested())
  379. {
  380. return S_FALSE;
  381. }
  382. if ( IsFileComplete() )
  383. {
  384. LogDl( "File is done, exiting.\n" );
  385. if (!SetFileTimes())
  386. {
  387. ASSERT( IsErrorSet() );
  388. return E_FAIL;
  389. }
  390. return S_OK;
  391. }
  392. BYTE *p = g_FileDataBuffer;
  393. DWORD dwTotalBytesRead = 0;
  394. DWORD dwBytesToRead = ForegroundBlockSize;
  395. DWORD dwRead;
  396. while( 1 )
  397. {
  398. if (! InternetReadFile(m_hOpenRequest, p, dwBytesToRead, &dwRead) )
  399. {
  400. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "InternetReadFile" );
  401. LogWarning( "InternetReadFile failed: len=%d, offset=%I64d, err=%d",
  402. ForegroundBlockSize, m_CurrentOffset, GetLastError());
  403. return E_FAIL;
  404. }
  405. if ( !dwRead )
  406. break;
  407. dwTotalBytesRead += dwRead;
  408. dwBytesToRead -= dwRead;
  409. p += dwRead;
  410. if ( !dwBytesToRead )
  411. break;
  412. if (m_Callbacks->PollAbort())
  413. {
  414. LogInfo("time slice has run out");
  415. return S_FALSE;
  416. }
  417. }
  418. if (!WriteBlockToCache( (LPBYTE) g_FileDataBuffer, dwTotalBytesRead ))
  419. {
  420. ASSERT( IsErrorSet() );
  421. return hr;
  422. }
  423. if (dwTotalBytesRead != ForegroundBlockSize &&
  424. m_CurrentOffset != m_wupdinfo->FileSize)
  425. {
  426. LogError("Download block : EOF after %d", dwRead );
  427. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, ERROR_INTERNET_CONNECTION_RESET, "DownloadBlock" );
  428. return E_FAIL;
  429. }
  430. if (m_Callbacks->DownloaderProgress( m_CurrentOffset, m_wupdinfo->FileSize ))
  431. {
  432. // abort was requested
  433. return S_FALSE;
  434. }
  435. DWORD dwNewTick = GetTickCount();
  436. DWORD dwTimeDelta = dwNewTick - dwPrevTick;
  437. if ( dwTimeDelta < FOREGROUND_UPDATE_RATE )
  438. ForegroundBlockSize = min( ForegroundBlockSize + FOREGROUND_BLOCK_INCREMENT, MAX_FOREGROUND_BLOCK );
  439. else if ( dwTimeDelta > FOREGROUND_UPDATE_RATE )
  440. ForegroundBlockSize = max( ForegroundBlockSize - FOREGROUND_BLOCK_INCREMENT, MIN_FOREGROUND_BLOCK );
  441. ForegroundBlockSize = min( ForegroundBlockSize, ( m_wupdinfo->FileSize - m_CurrentOffset ) );
  442. dwPrevTick = dwNewTick;
  443. //
  444. // End loop of read blocks
  445. //
  446. }
  447. //
  448. // End loop of requests
  449. //
  450. }
  451. }
  452. HRESULT
  453. CProgressiveDL::GetNextBlock()
  454. {
  455. HRESULT hr = E_FAIL;
  456. LogDl( "file size %d, offset %d", DWORD(m_wupdinfo->FileSize), DWORD(m_CurrentOffset) );
  457. m_Network.CalculateIntervalAndBlockSize( min( m_wupdinfo->FileSize - m_CurrentOffset, MAX_IIS_SEND_SIZE) );
  458. DWORD BlockSize = m_Network.m_BlockSize;
  459. if (BlockSize == 0)
  460. {
  461. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_START );
  462. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_END );
  463. }
  464. else
  465. {
  466. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_START );
  467. //
  468. // Request a block from the server.
  469. //
  470. hr = StartRangeRequest( BlockSize );
  471. if (FAILED(hr))
  472. {
  473. ASSERT( IsErrorSet() );
  474. return hr;
  475. }
  476. //
  477. // A single call to InternetReadFile may return only part of the requested data,
  478. // so loop until the entire block has arrived.
  479. //
  480. DWORD dwBytesRead = 0;
  481. while (dwBytesRead < BlockSize )
  482. {
  483. DWORD dwSize = min( (BlockSize - dwBytesRead) , FILE_DATA_BUFFER_LEN );
  484. DWORD dwRead = 0;
  485. if (! InternetReadFile( m_hOpenRequest,
  486. g_FileDataBuffer,
  487. dwSize,
  488. &dwRead ))
  489. {
  490. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "InternetReadFile" );
  491. LogWarning( "InternetReadFile failed: len=%d, offset=%I64d, err=%d",
  492. dwSize, m_CurrentOffset, GetLastError());
  493. return E_FAIL;
  494. }
  495. else if (dwRead == 0)
  496. {
  497. break;
  498. }
  499. dwBytesRead += dwRead;
  500. //
  501. // Save the data.
  502. //
  503. if (!WriteBlockToCache( (LPBYTE) g_FileDataBuffer, dwRead ))
  504. {
  505. ASSERT( IsErrorSet() );
  506. return hr;
  507. }
  508. if (m_Callbacks->PollAbort())
  509. {
  510. LogInfo("an abort was requested before the whole block was read.");
  511. return S_FALSE;
  512. }
  513. }
  514. if (dwBytesRead != BlockSize)
  515. {
  516. LogError("Download block : EOF after %d", dwBytesRead );
  517. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, ERROR_INTERNET_CONNECTION_RESET, "DownloadBlock" );
  518. return E_FAIL;
  519. }
  520. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_END );
  521. if (m_Callbacks->DownloaderProgress( m_CurrentOffset, m_wupdinfo->FileSize ))
  522. {
  523. LogInfo("an abort was requested during the progress update.");
  524. // abort was requested
  525. return S_FALSE;
  526. }
  527. }
  528. //
  529. // Allow other apps to use the network for the rest of the time interval,
  530. // then take the end-of-interval snapshot.
  531. //
  532. m_Network.Wait();
  533. hr = m_Network.TakeSnapshot( CNetworkInterface::BLOCK_INTERVAL_END );
  534. if (SUCCEEDED(hr))
  535. {
  536. m_Network.SetInterfaceSpeed();
  537. }
  538. else if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_DATA ))
  539. {
  540. //
  541. // If the snapshot fails with ERROR_INVALID_DATA and the downloads
  542. // keep working, then our NIC has been removed and the networking
  543. // layer has silently transferred our connection to another available
  544. // NIC. We need to identify the NIC that we are now using.
  545. //
  546. LogWarning("NIC is no longer valid. Requesting retry.");
  547. hr = E_RETRY;
  548. }
  549. else
  550. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "TakeSnapshot" );
  551. return hr;
  552. }
  553. URL_INFO::URL_INFO(
  554. LPCTSTR Url,
  555. const PROXY_SETTINGS * a_ProxySettings,
  556. const CCredentialsContainer * a_Credentials,
  557. LPCTSTR HostId
  558. ) :
  559. hInternet( 0 ),
  560. hConnect( 0 ),
  561. FileSize( 0 ),
  562. dwFlags( 0 ),
  563. bHttp11( true ),
  564. ProxySettings( 0 ),
  565. fProxy( false ),
  566. Credentials( a_Credentials )
  567. {
  568. try
  569. {
  570. LogInfo("new URL_INFO at %p", this );
  571. //
  572. // Split the URL into server, path, name, and password components.
  573. //
  574. URL_COMPONENTS UrlComponents;
  575. ZeroMemory(&UrlComponents, sizeof(UrlComponents));
  576. UrlComponents.dwStructSize = sizeof(UrlComponents);
  577. UrlComponents.lpszHostName = HostName;
  578. UrlComponents.dwHostNameLength = RTL_NUMBER_OF(HostName);
  579. UrlComponents.lpszUrlPath = UrlPath;
  580. UrlComponents.dwUrlPathLength = RTL_NUMBER_OF(UrlPath);
  581. UrlComponents.lpszUserName = UserName;
  582. UrlComponents.dwUserNameLength = RTL_NUMBER_OF(UserName);
  583. UrlComponents.lpszPassword = Password;
  584. UrlComponents.dwPasswordLength = RTL_NUMBER_OF(Password);
  585. if (! InternetCrackUrl(Url, 0, 0, &UrlComponents))
  586. {
  587. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  588. {
  589. THROW_HRESULT( HRESULT_FROM_WIN32( ERROR_WINHTTP_INVALID_URL ));
  590. }
  591. ThrowLastError();
  592. }
  593. if (-1 == UrlComponents.dwHostNameLength ||
  594. -1 == UrlComponents.dwUrlPathLength ||
  595. -1 == UrlComponents.dwUserNameLength ||
  596. -1 == UrlComponents.dwPasswordLength)
  597. {
  598. THROW_HRESULT( HRESULT_FROM_WIN32( ERROR_WINHTTP_INVALID_URL ));
  599. }
  600. Port = UrlComponents.nPort;
  601. if (0 == _tcslen(HostName))
  602. {
  603. THROW_HRESULT( E_INVALIDARG );
  604. }
  605. if ( HostId && *HostId )
  606. {
  607. // redirected to another host.
  608. THROW_HRESULT( StringCbCopy( HostName, sizeof(HostName), HostId ));
  609. LogDl( "Stuck to %!ts!...", UrlComponents.lpszHostName );
  610. }
  611. //
  612. // Set the connect flags.
  613. //
  614. dwFlags = WINHTTP_FLAG_ESCAPE_DISABLE_QUERY;
  615. if(UrlComponents.nScheme == INTERNET_SCHEME_HTTPS)
  616. {
  617. dwFlags |= WINHTTP_FLAG_SECURE;
  618. }
  619. ProxySettings = new PROXY_SETTINGS_CONTAINER( Url, a_ProxySettings );
  620. }
  621. catch ( ComError err )
  622. {
  623. Cleanup();
  624. throw;
  625. }
  626. }
  627. //
  628. // Splitting the explicit cleanup into a separate function allows the constructor to re-use the code.
  629. // The constructor *cannot* explicitly call the destructor if it is going to throw an exception,
  630. // because the destructor will call the StringHandle member destructors and then the constructor
  631. // will call them again (because it is throwing an exception and cleans up the already-constructed
  632. // members).
  633. //
  634. URL_INFO::~URL_INFO()
  635. {
  636. Cleanup();
  637. }
  638. void URL_INFO::Cleanup()
  639. {
  640. LogInfo("deleting URL_INFO at %p", this );
  641. Disconnect();
  642. SecureZeroMemory( UserName, sizeof(UserName) );
  643. SecureZeroMemory( Password, sizeof(Password) );
  644. delete ProxySettings; ProxySettings = NULL;
  645. }
  646. void
  647. URL_INFO::Disconnect()
  648. {
  649. SafeCloseInternetHandle( hConnect );
  650. SafeCloseInternetHandle( hInternet );
  651. }
  652. QMErrInfo
  653. URL_INFO::Connect()
  654. {
  655. try
  656. {
  657. //
  658. // The proxy stuff is ignored because we will set an explicit proxy on each request.
  659. //
  660. hInternet = WinHttpOpen( C_BITS_USER_AGENT,
  661. WINHTTP_ACCESS_TYPE_NO_PROXY,
  662. NULL,
  663. NULL,
  664. 0 );
  665. if (! hInternet )
  666. {
  667. ThrowLastError();
  668. }
  669. //
  670. // Enable passport authentication, which will only work if the user provides
  671. // explicit passport credentials.
  672. //
  673. // We have to enable the passport support here and not "as needed" right before
  674. // we apply the credentials because winhttp needs to know this before we send our first request.
  675. // If the site if passport enabled, the server will return 302 Object Moved,
  676. // which will be handled appropriately by winhttp only if this flag is set.
  677. // If everything goes fine, winhttp will hide the 302 from us and we will see in fact
  678. // a 401 respond -- to which we will approprietely respond with passport credentials.
  679. // In the case where we are not given explicit credentials, we will fail and we will
  680. // propagate the access denied error.
  681. //
  682. EnablePassport( hInternet );
  683. if (! (hConnect = WinHttpConnect( hInternet,
  684. HostName,
  685. Port,
  686. 0))) //context
  687. {
  688. ThrowLastError();
  689. }
  690. QMErrInfo Success;
  691. return Success;
  692. }
  693. catch ( ComError err )
  694. {
  695. LogError( "error %x connecting to server", err.Error() );
  696. QMErrInfo QmError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, err.Error() );
  697. return QmError;
  698. }
  699. }
  700. void
  701. EnablePassport(
  702. HINTERNET hInternet
  703. )
  704. {
  705. DWORD dwPassportFlags = WINHTTP_ENABLE_PASSPORT_AUTH | WINHTTP_DISABLE_PASSPORT_KEYRING;
  706. //
  707. // Enable Passport authentication scheme and
  708. // make sure we will NOT support the keyring scenario.
  709. // With disable keyring flag, implicit credentials will not be accepted and
  710. // the client application will need to specify explicit passport
  711. // credentials in order to be authorized.
  712. //
  713. if (!WinHttpSetOption( hInternet,
  714. WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH,
  715. &dwPassportFlags,
  716. sizeof(DWORD)
  717. ))
  718. {
  719. LogError("Couldn't set passport flags");
  720. ThrowLastError();
  721. }
  722. }
  723. URL_INFO *
  724. ConnectToUrl(
  725. LPCTSTR Url,
  726. const PROXY_SETTINGS * ProxySettings,
  727. const CCredentialsContainer * Credentials,
  728. const TCHAR * HostId,
  729. QMErrInfo * pErrInfo
  730. )
  731. {
  732. //
  733. // Open a connection to the server.
  734. //
  735. LogDl( "Connecting to %!ts!...", Url);
  736. //
  737. // This should have been checked by the caller.
  738. //
  739. ASSERT( HostId == NULL || wcslen(HostId) < INTERNET_MAX_HOST_NAME_LENGTH );
  740. try
  741. {
  742. URL_INFO * Info = new URL_INFO( Url, ProxySettings, Credentials, HostId );
  743. *pErrInfo = Info->Connect();
  744. if (pErrInfo->IsSet())
  745. {
  746. delete Info;
  747. return NULL;
  748. }
  749. return Info;
  750. }
  751. catch ( ComError err )
  752. {
  753. pErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, err.Error(), "untracked API" );
  754. return NULL;
  755. }
  756. }
  757. BOOL
  758. CProgressiveDL::GetRemoteResourceInformation(
  759. URL_INFO * Info,
  760. QMErrInfo *pQMErrInfo
  761. )
  762. /*
  763. We begin with an HTTP 1.1 HEAD request.
  764. If the server replies with version 1.1, we have a persistent connection and the proxy, if present, can cache our requests.
  765. If the server replies with version 1.0, we do not have either characteristic. Our GET requests will add "Connection: keep-alive"
  766. but it may not do any good. The proxy server, if present, may not understand ranges and if we allow caching then it will
  767. cache a range request as if it were the entire file.
  768. If the server replies with any other version or the call fails, then we should bail with BG_E_INSUFFICIENT_SERVER_SUPPORT.
  769. If an error occurs, we report it and bail.
  770. */
  771. {
  772. HRESULT FailureCode = 0;
  773. unsigned FailureLine = 0;
  774. #define CHECK_HRESULT( x ) \
  775. { \
  776. HRESULT _hr_ = x; \
  777. if (FAILED(_hr_)) \
  778. { \
  779. FailureCode = _hr_; \
  780. FailureLine = __LINE__; \
  781. goto exit; \
  782. } \
  783. }
  784. #define CHECK_BOOL( x ) \
  785. { \
  786. if (! x ) \
  787. { \
  788. FailureCode = HRESULT_FROM_WIN32( GetLastError() ); \
  789. FailureLine = __LINE__; \
  790. goto exit; \
  791. } \
  792. }
  793. // Assume HTTP1.1 with no proxy until we determine otherwise.
  794. //
  795. Info->bHttp11 = TRUE;
  796. Info->bRange = TRUE;
  797. Info->fProxy = FALSE;
  798. BOOL b = FALSE;
  799. HRESULT hr;
  800. HINTERNET hRequest = NULL;
  801. DWORD dwErr, dwLength = 0, dwStatus = 0, dwState = 0;
  802. CHECK_HRESULT( OpenHttpRequest( _T("HEAD"), _T("HTTP/1.1"), *Info, &hRequest ) );
  803. CHECK_HRESULT( SendRequest( hRequest, Info ));
  804. // check status
  805. dwLength = sizeof(dwStatus);
  806. CHECK_BOOL( HttpQueryInfo( hRequest,
  807. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  808. (LPVOID)&dwStatus,
  809. &dwLength,
  810. NULL));
  811. if (dwStatus != HTTP_STATUS_OK)
  812. {
  813. pQMErrInfo->Set( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  814. goto exit;
  815. }
  816. //
  817. // We know that the server replied with a success code. Now determine the HTTP version.
  818. //
  819. unsigned MajorVersion;
  820. unsigned MinorVersion;
  821. CHECK_HRESULT( GetResponseVersion( hRequest, &MajorVersion, &MinorVersion ));
  822. if (MajorVersion != 1)
  823. {
  824. LogWarning("server version %d.%d is outside our supported range", MajorVersion, MinorVersion );
  825. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_HTTP_SUPPORT );
  826. goto exit;
  827. }
  828. if (MinorVersion < 1)
  829. {
  830. Info->bHttp11 = FALSE;
  831. Info->dwFlags |= WINHTTP_FLAG_REFRESH;
  832. }
  833. //
  834. // Now determine the proxy server.
  835. //
  836. CHECK_BOOL( Info->GetProxyUsage( hRequest, pQMErrInfo ));
  837. // check file size
  838. WCHAR FileSizeText[ INT64_DIGITS+1 ];
  839. dwLength = sizeof( FileSizeText );
  840. CHECK_BOOL( HttpQueryInfo( hRequest,
  841. HTTP_QUERY_CONTENT_LENGTH,
  842. FileSizeText,
  843. &dwLength,
  844. NULL));
  845. if ( 1 != swscanf( FileSizeText, L"%I64u", &Info->FileSize ) )
  846. {
  847. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_MISSING_FILE_SIZE, "swscanf: content length" );
  848. goto exit;
  849. }
  850. LogDl( "File size of %!ts! = %I64u", Info->UrlPath, Info->FileSize);
  851. // check file time
  852. //
  853. SYSTEMTIME sysTime;
  854. dwLength = sizeof(sysTime);
  855. if (HttpQueryInfo( hRequest,
  856. HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME,
  857. (LPVOID)&sysTime,
  858. &dwLength,
  859. NULL))
  860. {
  861. CHECK_BOOL( SystemTimeToFileTime(&sysTime, &Info->UrlModificationTime ));
  862. }
  863. else
  864. {
  865. //
  866. // If the header is invalid, fail.
  867. //
  868. DWORD s = GetLastError();
  869. if (s != ERROR_WINHTTP_HEADER_NOT_FOUND)
  870. {
  871. FailureCode = HRESULT_FROM_WIN32(s); FailureLine = __LINE__; goto exit;
  872. }
  873. // The header is missing; allow it and set set modification time to zero.
  874. //
  875. LogWarning("server did not provide a Last-Modified header");
  876. Info->UrlModificationTime = UINT64ToFILETIME( 0 );
  877. }
  878. b = TRUE;
  879. exit:
  880. if (FailureCode)
  881. {
  882. //
  883. // If a header is missing or invalid, map it to something more understandable.
  884. //
  885. if (FailureCode == HRESULT_FROM_WIN32( ERROR_WINHTTP_INVALID_QUERY_REQUEST) ||
  886. FailureCode == HRESULT_FROM_WIN32( ERROR_WINHTTP_HEADER_NOT_FOUND) ||
  887. FailureCode == HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER) ||
  888. FailureCode == HRESULT_FROM_WIN32( ERROR_INTERNET_INTERNAL_ERROR) ||
  889. FailureCode == HRESULT_FROM_WIN32( ERROR_WINHTTP_INVALID_SERVER_RESPONSE) ||
  890. FailureCode == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER))
  891. {
  892. FailureCode = BG_E_INVALID_SERVER_RESPONSE;
  893. }
  894. LogError("failure at line %d; hresult = %x", FailureLine, FailureCode );
  895. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, FailureCode );
  896. }
  897. // release allocated objects
  898. //
  899. SafeCloseInternetHandle( hRequest );
  900. return b;
  901. #undef CHECK_HRESULT
  902. #undef CHECK_BOOL
  903. }
  904. BOOL
  905. URL_INFO::GetProxyUsage(
  906. HINTERNET hRequest,
  907. QMErrInfo *ErrInfo
  908. )
  909. /*
  910. This function determines whether the completed request in <hRequest> used a proxy server,
  911. and if so which one. In BITS 1.0 (Windows XP), it looked in the HTTP 1.1 Via header, but
  912. that header isn't present in HTTP 1.0 replies, and a server is allowed to return a fake host name.
  913. (see RFC 2616 section 14.45 for details.)
  914. The current version parses the current proxy value in this->ProxySettings, which was calculated
  915. by the HTTP layer. The format of a proxy-server entry is as follows:
  916. ([<scheme>=][<scheme>"://"]<server>[":"<port>])
  917. this->ProxyHost should include only the server name.
  918. On exit:
  919. if TRUE, fProxy and ProxyHost are set.
  920. if FALSE, fProxy and ProxyHost are unchanged and ErrInfo is set.
  921. */
  922. {
  923. try
  924. {
  925. LPCWSTR p = ProxySettings->GetCurrentProxy();
  926. if (!p)
  927. {
  928. fProxy = FALSE;
  929. return TRUE;
  930. }
  931. LPCWSTR p2;
  932. //
  933. // Skip past the [<scheme>=] segment.
  934. //
  935. p2 = wcschr( p, '=' );
  936. if (p2)
  937. {
  938. ++p2;
  939. p = p2;
  940. }
  941. //
  942. // Skip past the [<scheme>"://"] segment.
  943. //
  944. p2 = wcschr( p, '/' );
  945. if (p2)
  946. {
  947. ++p2;
  948. if (*p2 == '/')
  949. {
  950. ++p2;
  951. }
  952. p = p2;
  953. }
  954. //
  955. // p now points to the beginning of the server name. Copy it.
  956. //
  957. ProxyHost = CAutoString( CopyString( p ));
  958. //
  959. // Find the [":"<port>] segment.
  960. //
  961. LPWSTR pColon = wcschr( ProxyHost.get(), ':' );
  962. if (pColon)
  963. {
  964. *pColon = '\0';
  965. }
  966. fProxy = TRUE;
  967. return TRUE;
  968. }
  969. catch ( ComError err )
  970. {
  971. ErrInfo->Set( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_HRESULT, err.Error() );
  972. return FALSE;
  973. }
  974. }
  975. HRESULT
  976. CProgressiveDL::StartRangeRequest(
  977. DWORD Length
  978. )
  979. {
  980. HRESULT hr;
  981. DWORD dwBegin, dwEnd, dwTotalRead = 0, dwRead = 0, dwErr, dwLength, dwStatus;
  982. UINT64 Offset = m_CurrentOffset;
  983. //todo cleanup by goto exit and close handles
  984. if ( !m_hOpenRequest )
  985. {
  986. HINTERNET hRequest;
  987. hr = OpenHttpRequest( NULL, // default is "GET"
  988. m_wupdinfo->bHttp11 ? _T("HTTP/1.1") : _T("HTTP/1.0"),
  989. *m_wupdinfo,
  990. &hRequest
  991. );
  992. if (FAILED(hr))
  993. {
  994. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "CreateHttpRequest");
  995. return E_FAIL;
  996. }
  997. m_hOpenRequest = hRequest;
  998. //
  999. // These headers are constant for a particular file download attempt.
  1000. //
  1001. hr = AddIf_Unmodified_SinceHeader( m_hOpenRequest, m_wupdinfo->UrlModificationTime );
  1002. if (FAILED(hr))
  1003. {
  1004. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr );
  1005. LogError( "unable to add If-Unmodified-Since header: %x", hr);
  1006. return E_FAIL;
  1007. }
  1008. if (! HttpAddRequestHeaders(m_hOpenRequest, ACCEPT_ENCODING_STRING, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
  1009. {
  1010. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "add header: accept-encoding" );
  1011. return E_FAIL;
  1012. }
  1013. }
  1014. hr = AddRangeHeader( m_hOpenRequest, Offset, Offset + Length - 1 );
  1015. if (FAILED(hr))
  1016. {
  1017. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "AddRangeHeader" );
  1018. return E_FAIL;
  1019. }
  1020. hr = SendRequest( m_hOpenRequest, m_wupdinfo );
  1021. if (FAILED(hr))
  1022. {
  1023. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "HttpSendRequest" );
  1024. LogError( "HttpSendRequest failed in progressive download loop - offset=%I64d",
  1025. m_CurrentOffset );
  1026. return E_FAIL;
  1027. }
  1028. //
  1029. // The server sent a reply. See if it was successful.
  1030. //
  1031. dwLength = sizeof(dwStatus);
  1032. if (! HttpQueryInfo(m_hOpenRequest,
  1033. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  1034. (LPVOID)&dwStatus,
  1035. &dwLength,
  1036. NULL))
  1037. {
  1038. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "HttpQueryInfo" );
  1039. return E_FAIL;
  1040. }
  1041. //
  1042. // If the server file changed, stop downloading and indicate that to the caller.
  1043. //
  1044. if ( HTTP_STATUS_PRECOND_FAILED == dwStatus )
  1045. {
  1046. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  1047. m_pQMInfo->result = QM_SERVER_FILE_CHANGED;
  1048. return E_FAIL;
  1049. }
  1050. //
  1051. // If the server sent an error, fail.
  1052. //
  1053. if ( dwStatus != HTTP_STATUS_PARTIAL_CONTENT &&
  1054. dwStatus != HTTP_STATUS_OK)
  1055. {
  1056. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  1057. return E_FAIL;
  1058. }
  1059. if (dwStatus == HTTP_STATUS_PARTIAL_CONTENT)
  1060. {
  1061. //
  1062. // Now see whether the server understood the range request.
  1063. // If it understands ranges, then it should have responded with a Content-Range header
  1064. // matching our request.
  1065. //
  1066. hr = CheckReplyRange( m_hOpenRequest,
  1067. m_CurrentOffset,
  1068. m_CurrentOffset + Length - 1,
  1069. m_wupdinfo->FileSize
  1070. );
  1071. if (FAILED(hr))
  1072. {
  1073. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "Reply range" );
  1074. return hr;
  1075. }
  1076. //
  1077. // If the server appears not to support ranges, give up.
  1078. //
  1079. if (S_FALSE == hr)
  1080. {
  1081. m_wupdinfo->Disconnect();
  1082. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_RANGE_SUPPORT );
  1083. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1084. }
  1085. }
  1086. else
  1087. {
  1088. //
  1089. // The server replied with status 200. This could mean that the server doesn't understand
  1090. // range requests, or that the request encompassed the entire file.
  1091. // (In this situation, IIS 5.0 and 6.0 return 206, but some Apache versions return 200.)
  1092. // To distinguish them, make sure the starting offset of the request was zero and the
  1093. // file length is equal to the original request length.
  1094. //
  1095. hr = CheckReplyLength( m_hOpenRequest, m_CurrentOffset, Length );
  1096. if (FAILED(hr))
  1097. {
  1098. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "content length" );
  1099. return hr;
  1100. }
  1101. //
  1102. // If the server did not include a Content-Length header, give up.
  1103. //
  1104. if (S_FALSE == hr)
  1105. {
  1106. m_wupdinfo->Disconnect();
  1107. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_RANGE_SUPPORT );
  1108. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1109. }
  1110. }
  1111. //
  1112. // Here is the code to switch to encoded range format, in case we need it later.
  1113. //
  1114. // if (S_FALSE == hr)
  1115. // {
  1116. // LogDl( "server does not support ranges." );
  1117. //
  1118. // m_wupdinfo->bHttp11 = FALSE;
  1119. // m_wupdinfo->bRange = FALSE;
  1120. //
  1121. // //
  1122. // // We can't just drain the rest of the server response and send again, because the server
  1123. // // response is very likely the entire file. Closing the connection will prevent the server
  1124. // // from writing, at max, any more than the client socket buffer size (16K).
  1125. // //
  1126. // m_wupdinfo->Disconnect();
  1127. //
  1128. // *m_pQMInfo = m_wupdinfo->Connect();
  1129. // if (m_pQMInfo->IsSet())
  1130. // {
  1131. // return E_FAIL;
  1132. // }
  1133. //
  1134. // HRESULT HrReadUrl = StartEncodedRangeRequest( Length );
  1135. //
  1136. // if ( BG_E_INSUFFICIENT_HTTP_SUPPORT == HrReadUrl )
  1137. // {
  1138. // SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_RANGE_SUPPORT );
  1139. // return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1140. // }
  1141. //
  1142. // return HrReadUrl;
  1143. // }
  1144. //
  1145. // Getting here means the range request succeeded.
  1146. //
  1147. return S_OK;
  1148. }
  1149. bool
  1150. CProgressiveDL::DoesErrorIndicateNoISAPI(
  1151. DWORD dwHttpError
  1152. )
  1153. {
  1154. // This function is used on the HTTP return code on an attept
  1155. // to use the isapi dll to estimate if the isapi is installed.
  1156. // Note, that the ISAPI should only be used after trying
  1157. // native HTTP/1.1 and this table assume 1.1 was tried first.
  1158. // From RFC 2616
  1159. switch( dwHttpError )
  1160. {
  1161. case 100: return false; // Continue
  1162. case 101: return false; // Switching Protocols
  1163. case 200: return false; // OK
  1164. case 201: return false; // Created
  1165. case 202: return false; // Accepted
  1166. case 203: return false; // Non-Authoritative
  1167. case 204: return false; // No Content
  1168. case 205: return false; // Reset Context
  1169. case 206: return false; // Partial Content
  1170. case 300: return false; // Multiple Choices
  1171. case 301: return false; // Moved Permanently
  1172. case 302: return false; // Found
  1173. case 303: return false; // See other
  1174. case 304: return false; // Not Modified
  1175. case 305: return false; // Use Proxy
  1176. case 306: return false; // Unused
  1177. case 307: return false; // Temporary Redirect
  1178. case 400: return true; // Bad Request
  1179. case 401: return false; // Unauthorized
  1180. case 402: return false; // Payment Required
  1181. case 403: return false; // Forbidden
  1182. case 404: return true; // Not Found
  1183. case 405: return false; // Method Not Allowed
  1184. case 406: return false; // Not Acceptable
  1185. case 407: return false; // Proxy Authentication Required
  1186. case 408: return false; // Request Timeout
  1187. case 409: return false; // Conflict
  1188. case 410: return true; // Gone
  1189. case 411: return false; // Length Required
  1190. case 412: return false; // Precondition Failed
  1191. case 413: return false; // Request Entity Too Large
  1192. case 414: return false; // Request URI too long
  1193. case 415: return false; // Unsupported Media Type
  1194. case 416: return false; // Requested Range Not Satisfiable
  1195. case 417: return false; // Expectation Failed
  1196. case 500: return true; // Internal Server Error
  1197. case 501: return true; // Not Implemented
  1198. case 502: return true; // Bad Gateway
  1199. case 503: return false; // Service Unavailable
  1200. case 504: return false; // Gateway Timeout
  1201. case 505: return false; // HTTP Version Not Supported
  1202. default:
  1203. // As indicated in the spec, map unknown codes
  1204. // to first code in the catagory
  1205. if ( dwHttpError >= 100 && dwHttpError < 200 )
  1206. return DoesErrorIndicateNoISAPI( 100 );
  1207. else if ( dwHttpError >= 200 && dwHttpError < 300 )
  1208. return DoesErrorIndicateNoISAPI( 200 );
  1209. else if ( dwHttpError >= 300 && dwHttpError < 400 )
  1210. return DoesErrorIndicateNoISAPI( 300 );
  1211. else if ( dwHttpError >= 400 && dwHttpError < 500 )
  1212. return DoesErrorIndicateNoISAPI( 400 );
  1213. else if ( dwHttpError >= 500 && dwHttpError < 500 )
  1214. return DoesErrorIndicateNoISAPI( 500 );
  1215. else
  1216. // No clue what the error is, assume this has nothing to do with the ISAPI
  1217. return false;
  1218. }
  1219. }
  1220. BOOL
  1221. NeedRetry(
  1222. QMErrInfo * ErrInfo
  1223. )
  1224. {
  1225. BOOL bRetry = FALSE;
  1226. if (ErrInfo->Source == SOURCE_HTTP_SERVER)
  1227. {
  1228. // Almost all of the 400 series HTTP errors( client errors ) are
  1229. // fatal. A few such as request timeout may happen during
  1230. // stress conditions...
  1231. // Note that RFC 2616 says to handle unknown 400 errors as error 400.
  1232. if ( ( ErrInfo->Code >= 400 ) &&
  1233. ( ErrInfo->Code < 500 ) )
  1234. {
  1235. switch( ErrInfo->Code )
  1236. {
  1237. case 408: // request timeout
  1238. case 409: // Conflict - Isn't really clear what this is about...
  1239. return TRUE; // retry these error
  1240. default:
  1241. return FALSE; // don't retry other 400
  1242. }
  1243. }
  1244. }
  1245. if ( ErrInfo->Style == ERROR_STYLE_HRESULT )
  1246. {
  1247. switch( LONG(ErrInfo->Code) )
  1248. {
  1249. // These codes indicate dynamic content or
  1250. // an unsupported server so no retries are necessary.
  1251. case BG_E_INVALID_SERVER_RESPONSE:
  1252. case BG_E_MISSING_FILE_SIZE:
  1253. case BG_E_INSUFFICIENT_HTTP_SUPPORT:
  1254. case BG_E_INSUFFICIENT_RANGE_SUPPORT:
  1255. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_ERRORS ):
  1256. case HRESULT_FROM_WIN32( ERROR_INTERNET_INVALID_CA ):
  1257. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_CN_INVALID ):
  1258. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_DATE_INVALID ):
  1259. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_REV_FAILED ):
  1260. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_REVOKED ):
  1261. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_NO_REV ):
  1262. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_INVALID_CERT ):
  1263. return FALSE;
  1264. }
  1265. }
  1266. if (COMPONENT_TRANS == (ErrInfo->Source & COMPONENT_MASK))
  1267. {
  1268. return TRUE;
  1269. }
  1270. switch (ErrInfo->Style)
  1271. {
  1272. case ERROR_STYLE_WIN32:
  1273. {
  1274. switch (ErrInfo->Code)
  1275. {
  1276. case ERROR_NOT_ENOUGH_MEMORY:
  1277. return TRUE;
  1278. }
  1279. }
  1280. }
  1281. return FALSE;
  1282. }
  1283. HRESULT
  1284. CProgressiveDL::GetRemoteFileInformation(
  1285. HANDLE hToken,
  1286. LPCTSTR szURL,
  1287. UINT64 * pFileSize,
  1288. FILETIME *pFileTime,
  1289. QMErrInfo *pErrInfo,
  1290. const PROXY_SETTINGS * pProxySettings,
  1291. const CCredentialsContainer * Credentials,
  1292. StringHandle HostId
  1293. )
  1294. {
  1295. *pFileSize = 0;
  1296. memset( pFileTime, 0, sizeof(FILETIME) );
  1297. pErrInfo->result = QM_IN_PROGRESS;
  1298. HRESULT Hr = S_OK;
  1299. try
  1300. {
  1301. CNestedImpersonation imp( hToken );
  1302. auto_ptr<URL_INFO> UrlInfo = auto_ptr<URL_INFO>( ConnectToUrl( szURL, pProxySettings, Credentials, (const WCHAR*)HostId, pErrInfo ));
  1303. if (!UrlInfo.get())
  1304. {
  1305. ASSERT( pErrInfo->IsSet() );
  1306. throw ComError( E_FAIL );
  1307. }
  1308. //
  1309. // Get file size and time stamp.
  1310. //
  1311. if (! GetRemoteResourceInformation( UrlInfo.get(), pErrInfo ))
  1312. {
  1313. ASSERT( pErrInfo->IsSet() );
  1314. throw ComError( E_FAIL );
  1315. }
  1316. *pFileTime = UrlInfo.get()->UrlModificationTime;
  1317. *pFileSize = UrlInfo.get()->FileSize;
  1318. pErrInfo->result = QM_FILE_DONE;
  1319. return S_OK;
  1320. }
  1321. catch( ComError Error )
  1322. {
  1323. Hr = Error.Error();
  1324. if (!pErrInfo->IsSet())
  1325. {
  1326. pErrInfo->Set( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_HRESULT, Hr );
  1327. }
  1328. if (NeedRetry(pErrInfo))
  1329. {
  1330. pErrInfo->result = QM_FILE_TRANSIENT_ERROR;
  1331. }
  1332. else
  1333. {
  1334. pErrInfo->result = QM_FILE_FATAL_ERROR;
  1335. }
  1336. return E_FAIL;
  1337. }
  1338. return Hr;
  1339. }
  1340. void
  1341. CProgressiveDL::SetError(
  1342. ERROR_SOURCE Source,
  1343. ERROR_STYLE Style,
  1344. UINT64 Code,
  1345. char * comment
  1346. )
  1347. {
  1348. m_pQMInfo->Set( Source, Style, Code, comment );
  1349. }
  1350. void QMErrInfo::Log()
  1351. {
  1352. LogDl( "errinfo: result=%d, error style=%d, code=0x%x, source=%x, description='%S'",
  1353. result, (DWORD) Style, (DWORD) Code, (DWORD) Source, Description ? Description : L"" );
  1354. }
  1355. QMErrInfo::QMErrInfo(
  1356. ERROR_SOURCE Source,
  1357. ERROR_STYLE Style,
  1358. UINT64 Code,
  1359. char * comment
  1360. )
  1361. {
  1362. result = QM_FILE_TRANSIENT_ERROR;
  1363. Description = NULL;
  1364. Set( Source, Style, Code, comment );
  1365. }
  1366. void
  1367. QMErrInfo::Set(
  1368. ERROR_SOURCE Source,
  1369. ERROR_STYLE Style,
  1370. UINT64 Code,
  1371. char * comment
  1372. )
  1373. {
  1374. this->Source = Source;
  1375. this->Style = Style;
  1376. this->Code = Code;
  1377. LogWarning( " errinfo: error %s %s : style %d, source %x, code 0x%x",
  1378. comment ? "in" : "",
  1379. comment ? comment : "",
  1380. (DWORD) Style,
  1381. (DWORD) Source,
  1382. (DWORD) Code
  1383. );
  1384. }
  1385. HRESULT
  1386. OpenHttpRequest(
  1387. LPCTSTR Verb,
  1388. LPCTSTR Protocol,
  1389. URL_INFO & Info,
  1390. HINTERNET * phRequest
  1391. )
  1392. {
  1393. HINTERNET hRequest = 0;
  1394. *phRequest = 0;
  1395. try
  1396. {
  1397. LPCTSTR AcceptTypes[] = {_T("*/*"), NULL};
  1398. if (! (hRequest = WinHttpOpenRequest( Info.hConnect, Verb,
  1399. Info.UrlPath,
  1400. Protocol,
  1401. NULL, //referer
  1402. AcceptTypes,
  1403. Info.dwFlags
  1404. )))
  1405. {
  1406. ThrowLastError();
  1407. }
  1408. //
  1409. // Initially disallow default credentials, to allow explicit credentials to take precedence.
  1410. //
  1411. DWORD flag = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
  1412. if (!WinHttpSetOption( hRequest,
  1413. WINHTTP_OPTION_AUTOLOGON_POLICY,
  1414. &flag,
  1415. sizeof(DWORD)
  1416. ))
  1417. {
  1418. ThrowLastError();
  1419. }
  1420. LogInfo("blocked default credentials");
  1421. *phRequest = hRequest;
  1422. return S_OK;
  1423. }
  1424. catch ( ComError err )
  1425. {
  1426. SafeCloseInternetHandle( hRequest );
  1427. return err.Error();
  1428. }
  1429. }
  1430. HRESULT
  1431. SendRequest(
  1432. HINTERNET hRequest,
  1433. URL_INFO * Info,
  1434. CAbstractDataReader * Reader
  1435. )
  1436. {
  1437. DWORD err = 0;
  1438. PVOID Address = &err;
  1439. try
  1440. {
  1441. if (!WinHttpSetOption( hRequest,
  1442. WINHTTP_OPTION_CONTEXT_VALUE,
  1443. &Address,
  1444. sizeof(PVOID)
  1445. ))
  1446. {
  1447. err = GetLastError();
  1448. LogWarning( "can't set context option: %!winerr!", err );
  1449. throw ComError( HRESULT_FROM_WIN32( err ));
  1450. }
  1451. //
  1452. // Catch errors in the server certificate.
  1453. //
  1454. if (WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback( hRequest,
  1455. HttpRequestCallback,
  1456. WINHTTP_CALLBACK_FLAG_SECURE_FAILURE,
  1457. NULL
  1458. ))
  1459. {
  1460. err = GetLastError();
  1461. LogError("WinHttpSetStatusCallback failed %d", err );
  1462. throw ComError( HRESULT_FROM_WIN32( err ));
  1463. }
  1464. bool fProxyCredentials = false;
  1465. bool fServerCredentials = false;
  1466. int AuthChallenges = 0;
  1467. CredentialsApplied ProxyCredentials;
  1468. new_proxy:
  1469. RETURN_HRESULT( SetRequestProxy( Info->hInternet, Info->ProxySettings ));
  1470. retry:
  1471. if (AuthChallenges > 6)
  1472. {
  1473. //
  1474. // The client received several 401 and/or 407 statuses, but has not
  1475. // reached the server yet. Either the server is broken, or a denial-of-service
  1476. // attack is in progress.
  1477. //
  1478. // Return S_OK; the request status is still 401 or 407 and that will become the
  1479. // error code from the download.
  1480. //
  1481. return S_OK;
  1482. }
  1483. if ( fProxyCredentials & fServerCredentials )
  1484. {
  1485. // This code is needed since Winhttp will clear credentials after every request.
  1486. // It will reapply the proxy credentials. The server credentials were already applied.
  1487. // Default credentials do not need to be reapplied since the logon policy is
  1488. // retain for every request.
  1489. if ( !ProxyCredentials.bIsDefault )
  1490. {
  1491. ApplySchemeCredentials( hRequest, ProxyCredentials.dwTarget, ProxyCredentials.dwScheme,
  1492. Info->Credentials, Info->UserName, Info->Password );
  1493. }
  1494. }
  1495. err = 0;
  1496. BOOL b;
  1497. if (Reader)
  1498. {
  1499. b = WinHttpSendRequest( hRequest,
  1500. NULL,
  1501. 0,
  1502. NULL,
  1503. WINHTTP_NO_REQUEST_DATA,
  1504. Reader->GetLength(),
  1505. 0
  1506. );
  1507. }
  1508. else
  1509. {
  1510. b = WinHttpSendRequest( hRequest,
  1511. NULL,
  1512. 0,
  1513. NULL,
  1514. 0,
  1515. 0,
  1516. 0
  1517. );
  1518. if (b)
  1519. {
  1520. b = WinHttpReceiveResponse( hRequest, 0 );
  1521. }
  1522. }
  1523. // err is modified by the callback routine if something was wrong with the server certificate
  1524. if (!b)
  1525. {
  1526. if (!err)
  1527. {
  1528. err = GetLastError();
  1529. }
  1530. LogError("SendRequest failed %!winerr!", err );
  1531. }
  1532. //
  1533. // If the proxy failed, try the next proxy in the list.
  1534. //
  1535. if (IsPossibleProxyFailure( err ))
  1536. {
  1537. LogInfo("trying next proxy");
  1538. if (Info->ProxySettings->UseNextProxy())
  1539. {
  1540. fProxyCredentials = false;
  1541. goto new_proxy;
  1542. }
  1543. throw ComError( HRESULT_FROM_WIN32( err ));
  1544. }
  1545. if (err == ERROR_INTERNET_FORCE_RETRY)
  1546. {
  1547. goto retry;
  1548. }
  1549. //
  1550. // If the request wasn't sent or the security callback routine reported an error, fail.
  1551. //
  1552. if (err)
  1553. {
  1554. throw ComError( HRESULT_FROM_WIN32( err ));
  1555. }
  1556. //
  1557. // Send the bulk data if present.
  1558. //
  1559. if (Reader)
  1560. {
  1561. DWORD Total = 0;
  1562. while ( Total < Reader->GetLength())
  1563. {
  1564. DWORD ReadLength = min( Reader->GetLength() - Total, FILE_DATA_BUFFER_LEN);
  1565. DWORD BytesMoved;
  1566. THROW_HRESULT( Reader->Read( g_FileDataBuffer, ReadLength, &BytesMoved ));
  1567. if (ReadLength != BytesMoved)
  1568. {
  1569. THROW_HRESULT( BG_E_LOCAL_FILE_CHANGED );
  1570. }
  1571. LogInfo("sending %d bytes", ReadLength);
  1572. BytesMoved = 0;
  1573. if (!InternetWriteFile( hRequest, g_FileDataBuffer, ReadLength, &BytesMoved))
  1574. {
  1575. DWORD s = GetLastError();
  1576. LogError("InternetWriteFile failed %!winerr!", s);
  1577. THROW_HRESULT( HRESULT_FROM_WIN32( s ));
  1578. }
  1579. ASSERT(ReadLength == BytesMoved);
  1580. Total += ReadLength;
  1581. }
  1582. if (! HttpEndRequest( hRequest, NULL, NULL, 0 ))
  1583. {
  1584. DWORD s = GetLastError();
  1585. if (s == ERROR_INTERNET_FORCE_RETRY)
  1586. {
  1587. THROW_HRESULT( Reader->Rewind() );
  1588. goto retry;
  1589. }
  1590. LogError("HttpEndRequest failed %!winerr!", s);
  1591. THROW_HRESULT( HRESULT_FROM_WIN32( s ));
  1592. }
  1593. }
  1594. //
  1595. // If the server or proxy server asked for auth information and we haven't already set it,
  1596. // find matching credentials and try again. If bulk data was sent, the reader must be rewound.
  1597. //
  1598. switch (GetRequestStatus( hRequest ))
  1599. {
  1600. case HTTP_STATUS_PROXY_AUTH_REQ:
  1601. {
  1602. LogInfo("server returned HTTP_STATUS_PROXY_AUTH_REQ" );
  1603. ++AuthChallenges;
  1604. if (ApplyCredentials( hRequest, Info->Credentials, Info->UserName,
  1605. Info->Password, &ProxyCredentials ))
  1606. {
  1607. fProxyCredentials = true;
  1608. if (Reader)
  1609. {
  1610. THROW_HRESULT( Reader->Rewind() );
  1611. }
  1612. goto retry;
  1613. }
  1614. else
  1615. {
  1616. // return S_OK and the caller will find the status code
  1617. }
  1618. break;
  1619. }
  1620. case HTTP_STATUS_DENIED:
  1621. {
  1622. LogInfo("server returned HTTP_STATUS_DENIED");
  1623. ++AuthChallenges;
  1624. if (ApplyCredentials( hRequest, Info->Credentials, Info->UserName, Info->Password ))
  1625. {
  1626. fServerCredentials = true;
  1627. if (Reader)
  1628. {
  1629. THROW_HRESULT( Reader->Rewind() );
  1630. }
  1631. goto retry;
  1632. }
  1633. else
  1634. {
  1635. // return S_OK and the caller will find the status code
  1636. }
  1637. break;
  1638. }
  1639. }
  1640. return S_OK;
  1641. }
  1642. catch ( ComError err )
  1643. {
  1644. LogError("exception %x", err.Error() );
  1645. return err.Error();
  1646. }
  1647. }
  1648. HRESULT
  1649. GetResponseVersion(
  1650. HINTERNET hRequest,
  1651. unsigned * MajorVersion,
  1652. unsigned * MinorVersion
  1653. )
  1654. {
  1655. HRESULT hr;
  1656. CAutoString Value;
  1657. wchar_t Template[] = L"HTTP/%u.%u";
  1658. const MaxChars = RTL_NUMBER_OF( Template ) + INT_DIGITS + INT_DIGITS;
  1659. hr = GetRequestHeader( hRequest,
  1660. WINHTTP_QUERY_VERSION,
  1661. WINHTTP_HEADER_NAME_BY_INDEX,
  1662. Value,
  1663. MaxChars
  1664. );
  1665. if (FAILED(hr))
  1666. {
  1667. LogError("error %x retrieving the response version", hr);
  1668. return BG_E_INVALID_SERVER_RESPONSE;
  1669. }
  1670. if (hr == S_FALSE)
  1671. {
  1672. LogError("no response version!");
  1673. return BG_E_INVALID_SERVER_RESPONSE;
  1674. }
  1675. if (2 != swscanf(Value.get(), Template, MajorVersion, MinorVersion ))
  1676. {
  1677. LogError("invalid response version");
  1678. return BG_E_INVALID_SERVER_RESPONSE;
  1679. }
  1680. LogInfo("server HTTP version is %d.%d", *MajorVersion, *MinorVersion );
  1681. return S_OK;
  1682. }
  1683. HRESULT
  1684. CheckReplyLength(
  1685. HINTERNET hRequest,
  1686. UINT64 CorrectOffset,
  1687. UINT64 CorrectTotal
  1688. )
  1689. {
  1690. HRESULT hr;
  1691. UINT64 ReplyTotal;
  1692. CAutoString Value;
  1693. if (CorrectOffset != 0)
  1694. {
  1695. LogError( "received a 200 reply when the requested offset is nonzero: %I64d", CorrectOffset );
  1696. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1697. }
  1698. wchar_t Template[] = L"%I64d";
  1699. const MaxChars = RTL_NUMBER_OF( Template ) + INT64_DIGITS;
  1700. hr = GetRequestHeader( hRequest,
  1701. WINHTTP_QUERY_CONTENT_LENGTH,
  1702. WINHTTP_HEADER_NAME_BY_INDEX,
  1703. Value,
  1704. MaxChars
  1705. );
  1706. if (FAILED(hr))
  1707. {
  1708. LogError("error %x retrieving the content-length header", hr);
  1709. return hr;
  1710. }
  1711. if (hr == S_FALSE)
  1712. {
  1713. LogWarning("no content-length header");
  1714. return S_FALSE;
  1715. }
  1716. if (1 != swscanf(Value.get(), Template, &ReplyTotal))
  1717. {
  1718. LogError("invalid content-length header");
  1719. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1720. }
  1721. if (ReplyTotal != CorrectTotal)
  1722. {
  1723. LogError("incorrect content-length header: %S", Value.get());
  1724. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1725. }
  1726. return S_OK;
  1727. }
  1728. HRESULT
  1729. CheckReplyRange(
  1730. HINTERNET hRequest,
  1731. UINT64 CorrectStart,
  1732. UINT64 CorrectEnd,
  1733. UINT64 CorrectTotal
  1734. )
  1735. {
  1736. HRESULT hr;
  1737. UINT64 RangeStart;
  1738. UINT64 RangeEnd;
  1739. UINT64 RangeTotal;
  1740. CAutoString Value;
  1741. wchar_t Template[] = L"bytes %I64d-%I64d/%I64d";
  1742. const MaxChars = RTL_NUMBER_OF( Template ) + INT64_DIGITS + INT64_DIGITS + INT64_DIGITS;
  1743. hr = GetRequestHeader( hRequest,
  1744. WINHTTP_QUERY_CONTENT_RANGE,
  1745. WINHTTP_HEADER_NAME_BY_INDEX,
  1746. Value,
  1747. MaxChars
  1748. );
  1749. if (FAILED(hr))
  1750. {
  1751. LogError("error %x retrieving the content-range header", hr);
  1752. return hr;
  1753. }
  1754. if (hr == S_FALSE)
  1755. {
  1756. LogWarning("no reply range header");
  1757. return S_FALSE;
  1758. }
  1759. if (3 != swscanf(Value.get(), Template, &RangeStart, &RangeEnd, &RangeTotal))
  1760. {
  1761. LogError("invalid reply range header");
  1762. return BG_E_INVALID_SERVER_RESPONSE;
  1763. }
  1764. if (RangeStart != CorrectStart ||
  1765. RangeEnd != CorrectEnd ||
  1766. RangeTotal != CorrectTotal)
  1767. {
  1768. LogError("incorrect reply range header: %I64d-%I64d/%I64d", RangeStart, RangeEnd, RangeTotal);
  1769. return BG_E_INVALID_SERVER_RESPONSE;
  1770. }
  1771. return S_OK;
  1772. }
  1773. HRESULT
  1774. GetRequestHeader(
  1775. HINTERNET hRequest,
  1776. DWORD HeaderIndex,
  1777. LPCWSTR HeaderName,
  1778. CAutoString & Destination,
  1779. size_t MaxChars
  1780. )
  1781. /*
  1782. Fetch an arbitrary header's value from the request, allocating a string to hold it.
  1783. Input:
  1784. HeaderIndex and HeaderName follow the rules for WinHttpQueryHeaders() parameters
  1785. dwInfoLevel and pwszName respectively.
  1786. Returns:
  1787. S_OK: header found, and Destination holds the value.
  1788. S_FALSE: header not found
  1789. all others: an error occurred along the way
  1790. */
  1791. {
  1792. try
  1793. {
  1794. DWORD s;
  1795. HRESULT hr;
  1796. DWORD ValueLength;
  1797. CAutoString Value;
  1798. WinHttpQueryHeaders( hRequest, HeaderIndex, HeaderName, NULL, &ValueLength, WINHTTP_NO_HEADER_INDEX );
  1799. s = GetLastError();
  1800. if (s == ERROR_WINHTTP_HEADER_NOT_FOUND)
  1801. {
  1802. return S_FALSE;
  1803. }
  1804. if (s != ERROR_INSUFFICIENT_BUFFER)
  1805. {
  1806. return HRESULT_FROM_WIN32( s );
  1807. }
  1808. if (ValueLength > ((MaxChars+1) * sizeof(wchar_t)))
  1809. {
  1810. return BG_E_INVALID_SERVER_RESPONSE;
  1811. }
  1812. Value = CAutoString( new wchar_t[ ValueLength ] );
  1813. if (!WinHttpQueryHeaders( hRequest, HeaderIndex, HeaderName, Value.get(), &ValueLength, WINHTTP_NO_HEADER_INDEX ))
  1814. {
  1815. return HRESULT_FROM_WIN32( GetLastError() );
  1816. }
  1817. Destination = Value;
  1818. return S_OK;
  1819. }
  1820. catch ( ComError err )
  1821. {
  1822. return err.Error();
  1823. }
  1824. }
  1825. HRESULT
  1826. AddRangeHeader(
  1827. HINTERNET hRequest,
  1828. UINT64 Start,
  1829. UINT64 End
  1830. )
  1831. {
  1832. static const TCHAR RangeTemplate[] =_T("Range: bytes=%I64d-%I64d\r\n");
  1833. HRESULT hr;
  1834. TCHAR szHeader[ RTL_NUMBER_OF(RangeTemplate) + INT64_DIGITS + INT64_DIGITS ];
  1835. hr = StringCbPrintf(szHeader, sizeof(szHeader), RangeTemplate, Start, End);
  1836. if (FAILED(hr))
  1837. {
  1838. LogError( "range header is too large for its buffer. start %I64d, end %I64d", Start, End );
  1839. return hr;
  1840. }
  1841. if (! HttpAddRequestHeaders( hRequest, szHeader, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE))
  1842. {
  1843. return HRESULT_FROM_WIN32( GetLastError() );
  1844. }
  1845. return S_OK;
  1846. }
  1847. HRESULT
  1848. AddIf_Unmodified_SinceHeader(
  1849. HINTERNET hRequest,
  1850. const FILETIME &Time
  1851. )
  1852. {
  1853. const TCHAR szIfModifiedTemplate[] = _T("If-Unmodified-Since: %s\r\n");
  1854. static TCHAR szIfModifiedHeader[ (sizeof(szIfModifiedTemplate) / sizeof(TCHAR)) + INTERNET_RFC1123_BUFSIZE*2 ];
  1855. static TCHAR szIfModifiedTime[ INTERNET_RFC1123_BUFSIZE*2 ];
  1856. HRESULT hr;
  1857. SYSTEMTIME stFileCreationTime;
  1858. if ( !FileTimeToSystemTime( &Time, &stFileCreationTime ) )
  1859. {
  1860. return HRESULT_FROM_WIN32( GetLastError() );
  1861. }
  1862. if ( !InternetTimeFromSystemTime( &stFileCreationTime, INTERNET_RFC1123_FORMAT, szIfModifiedTime,
  1863. sizeof( szIfModifiedTime ) ) )
  1864. {
  1865. return HRESULT_FROM_WIN32( GetLastError() );
  1866. }
  1867. hr = StringCbPrintf( szIfModifiedHeader, sizeof(szIfModifiedHeader), szIfModifiedTemplate, szIfModifiedTime );
  1868. if (FAILED(hr))
  1869. {
  1870. return HRESULT_FROM_WIN32( GetLastError() );
  1871. }
  1872. if (! HttpAddRequestHeaders( hRequest, szIfModifiedHeader, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
  1873. {
  1874. return HRESULT_FROM_WIN32( GetLastError() );
  1875. }
  1876. return S_OK;
  1877. }
  1878. bool
  1879. ApplyCredentials(
  1880. HINTERNET hRequest,
  1881. const CCredentialsContainer * Credentials,
  1882. WCHAR UserName[],
  1883. WCHAR Password[],
  1884. CredentialsApplied *pAppliedCreds
  1885. )
  1886. {
  1887. HRESULT hr;
  1888. DWORD dwSupportedSchemes;
  1889. DWORD dwPreferredScheme;
  1890. DWORD dwTarget;
  1891. if (!WinHttpQueryAuthSchemes( hRequest,
  1892. &dwSupportedSchemes,
  1893. &dwPreferredScheme,
  1894. &dwTarget ))
  1895. {
  1896. if (GetLastError() == ERROR_INVALID_OPERATION)
  1897. {
  1898. // no schemes available at all
  1899. LogWarning("the server listed no auth schemes");
  1900. return false;
  1901. }
  1902. ThrowLastError();
  1903. }
  1904. LogInfo("target %d, preferred scheme %x, supported schemes %x", dwTarget, dwPreferredScheme, dwSupportedSchemes );
  1905. //
  1906. // First look for credentials supporting the preferred scheme.
  1907. //
  1908. if (ApplySchemeCredentials( hRequest, dwTarget, dwPreferredScheme, Credentials, UserName, Password ))
  1909. {
  1910. if ( pAppliedCreds )
  1911. {
  1912. pAppliedCreds->bIsDefault = false;
  1913. pAppliedCreds->dwTarget = dwTarget;
  1914. pAppliedCreds->dwScheme = dwPreferredScheme;
  1915. }
  1916. return true;
  1917. }
  1918. //
  1919. // Look for any other credential scheme supported by both sides.
  1920. //
  1921. signed bit;
  1922. for (bit=31; bit >= 0; --bit)
  1923. {
  1924. DWORD dwScheme = (1 << bit);
  1925. if (0 != (dwSupportedSchemes & dwScheme))
  1926. {
  1927. if (ApplySchemeCredentials( hRequest, dwTarget, dwScheme, Credentials, UserName, Password ))
  1928. {
  1929. if ( pAppliedCreds )
  1930. {
  1931. pAppliedCreds->bIsDefault = false;
  1932. pAppliedCreds->dwTarget = dwTarget;
  1933. pAppliedCreds->dwScheme = dwScheme;
  1934. }
  1935. return true;
  1936. }
  1937. }
  1938. }
  1939. //
  1940. // No matching explicit security credential. Try default credentials, if we are allowed to.
  1941. //
  1942. if (bLanManHashDisabled)
  1943. {
  1944. //
  1945. // The WinHTTP MEDIUM level doesn't work with auto-proxy detection; it always
  1946. // believes that the client is directly connected to the Internet and disallows
  1947. // default credentials. Since BITS doesn't know reliably either, we allow it only
  1948. // when the unsecure pre-NT logon protocol is disabled.
  1949. //
  1950. LogInfo("Enabling default credentials");
  1951. DWORD flag = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
  1952. if (!WinHttpSetOption( hRequest,
  1953. WINHTTP_OPTION_AUTOLOGON_POLICY,
  1954. &flag,
  1955. sizeof(DWORD)
  1956. ))
  1957. {
  1958. ThrowLastError();
  1959. }
  1960. if ( pAppliedCreds )
  1961. {
  1962. pAppliedCreds->bIsDefault = true;
  1963. pAppliedCreds->dwTarget = 0;
  1964. pAppliedCreds->dwScheme = 0;
  1965. }
  1966. return true;
  1967. }
  1968. return false;
  1969. }
  1970. DWORD GetRequestStatus( HINTERNET hRequest )
  1971. {
  1972. DWORD Status;
  1973. DWORD dwLength = sizeof(Status);
  1974. if (! HttpQueryInfo( hRequest,
  1975. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  1976. (LPVOID)&Status,
  1977. &dwLength,
  1978. NULL))
  1979. {
  1980. ThrowLastError();
  1981. }
  1982. return Status;
  1983. }
  1984. bool
  1985. SchemeFromWinHttp(
  1986. DWORD Scheme,
  1987. BG_AUTH_SCHEME * pScheme
  1988. )
  1989. {
  1990. switch (Scheme)
  1991. {
  1992. case WINHTTP_AUTH_SCHEME_BASIC: *pScheme = BG_AUTH_SCHEME_BASIC; return true;
  1993. case WINHTTP_AUTH_SCHEME_DIGEST: *pScheme = BG_AUTH_SCHEME_DIGEST; return true;
  1994. case WINHTTP_AUTH_SCHEME_NTLM: *pScheme = BG_AUTH_SCHEME_NTLM; return true;
  1995. case WINHTTP_AUTH_SCHEME_NEGOTIATE: *pScheme = BG_AUTH_SCHEME_NEGOTIATE; return true;
  1996. case WINHTTP_AUTH_SCHEME_PASSPORT: *pScheme = BG_AUTH_SCHEME_PASSPORT; return true;
  1997. default:
  1998. LogWarning("unknown WinHttp scheme 0x%x", Scheme );
  1999. return false;
  2000. }
  2001. }
  2002. BG_AUTH_TARGET TargetFromWinHttp( DWORD Target )
  2003. {
  2004. if (Target == WINHTTP_AUTH_TARGET_PROXY)
  2005. {
  2006. return BG_AUTH_TARGET_PROXY;
  2007. }
  2008. if (Target == WINHTTP_AUTH_TARGET_SERVER)
  2009. {
  2010. return BG_AUTH_TARGET_SERVER;
  2011. }
  2012. LogWarning("unknown WinHttp target 0x%x", Target );
  2013. ASSERT( 0 );
  2014. return BG_AUTH_TARGET_SERVER;
  2015. }
  2016. bool
  2017. ApplySchemeCredentials(
  2018. HINTERNET hRequest,
  2019. DWORD dwTarget,
  2020. DWORD dwScheme,
  2021. const CCredentialsContainer * Credentials,
  2022. WCHAR UserName[],
  2023. WCHAR Password[]
  2024. )
  2025. {
  2026. BG_AUTH_TARGET BitsTarget;
  2027. BG_AUTH_SCHEME BitsScheme;
  2028. BG_AUTH_CREDENTIALS * cred = 0;
  2029. BitsTarget = TargetFromWinHttp( dwTarget );
  2030. //
  2031. // Translate the scheme into the BITS ID and see if a matching credential is available.
  2032. //
  2033. if (!SchemeFromWinHttp( dwScheme, &BitsScheme ))
  2034. {
  2035. // BITS doesn't understand this scheme.
  2036. LogInfo("skipping unknown scheme 0x%x", dwScheme);
  2037. return false;
  2038. }
  2039. if (BitsScheme == BG_AUTH_SCHEME_BASIC && UserName && UserName[0])
  2040. {
  2041. // use credentials embedded in URL
  2042. //
  2043. }
  2044. else
  2045. {
  2046. HRESULT hr;
  2047. THROW_HRESULT( hr = Credentials->Find( BitsTarget, BitsScheme, &cred ));
  2048. if (hr != S_OK)
  2049. {
  2050. // no credential available for this scheme.
  2051. return false;
  2052. }
  2053. // use the credential in the dictionary.
  2054. //
  2055. UserName = cred->Credentials.Basic.UserName;
  2056. Password = cred->Credentials.Basic.Password;
  2057. }
  2058. //
  2059. // Apply a workaround for WinHTTP bug 754006, which is present in Windows XP earlier than SP4
  2060. // and Windows 2003 RTM.
  2061. //
  2062. if (UserName == NULL &&
  2063. Password == NULL &&
  2064. (dwScheme == WINHTTP_AUTH_SCHEME_NTLM ||
  2065. dwScheme == WINHTTP_AUTH_SCHEME_NEGOTIATE))
  2066. {
  2067. LogInfo("Enabling default credentials to support the explicitly specified implicit user");
  2068. DWORD flag = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
  2069. if (!WinHttpSetOption( hRequest,
  2070. WINHTTP_OPTION_AUTOLOGON_POLICY,
  2071. &flag,
  2072. sizeof(DWORD)
  2073. ))
  2074. {
  2075. if (cred)
  2076. {
  2077. ScrubCredentials( *cred );
  2078. delete [] cred->Credentials.Basic.UserName;
  2079. delete [] cred->Credentials.Basic.Password;
  2080. delete cred;
  2081. cred = NULL;
  2082. }
  2083. ThrowLastError();
  2084. }
  2085. }
  2086. //
  2087. // Apply the credentials we found.
  2088. //
  2089. LogInfo("found credentials for target %d scheme 0x%x (BITS scheme %d)", dwTarget, dwScheme, BitsScheme );
  2090. if (!WinHttpSetCredentials( hRequest,
  2091. dwTarget,
  2092. dwScheme,
  2093. UserName,
  2094. Password,
  2095. NULL
  2096. ))
  2097. {
  2098. if (cred)
  2099. {
  2100. ScrubCredentials( *cred );
  2101. delete [] cred->Credentials.Basic.UserName;
  2102. delete [] cred->Credentials.Basic.Password;
  2103. delete cred;
  2104. cred = NULL;
  2105. }
  2106. ThrowLastError();
  2107. }
  2108. if (cred)
  2109. {
  2110. ScrubCredentials( *cred );
  2111. delete [] cred->Credentials.Basic.UserName;
  2112. delete [] cred->Credentials.Basic.Password;
  2113. delete cred;
  2114. cred = NULL;
  2115. }
  2116. return true;
  2117. }
  2118. #ifndef USE_WININET
  2119. DWORD
  2120. MapSecureHttpErrorCode(
  2121. DWORD flags
  2122. )
  2123. {
  2124. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)
  2125. {
  2126. return ERROR_INTERNET_SEC_CERT_ERRORS;
  2127. }
  2128. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)
  2129. {
  2130. return ERROR_INTERNET_INVALID_CA;
  2131. }
  2132. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)
  2133. {
  2134. return ERROR_INTERNET_SEC_CERT_CN_INVALID;
  2135. }
  2136. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)
  2137. {
  2138. return ERROR_INTERNET_SEC_CERT_DATE_INVALID;
  2139. }
  2140. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)
  2141. {
  2142. return ERROR_ACCESS_DENIED;
  2143. }
  2144. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)
  2145. {
  2146. return ERROR_INTERNET_INTERNAL_ERROR;
  2147. }
  2148. //
  2149. // Since we have not asked for revocation checking, this shouldn't happen.
  2150. //
  2151. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)
  2152. {
  2153. ASSERT( 0 );
  2154. return 0;
  2155. }
  2156. ASSERT( flags );
  2157. if (flags)
  2158. {
  2159. return ERROR_ACCESS_DENIED;
  2160. }
  2161. return 0;
  2162. }
  2163. VOID CALLBACK
  2164. HttpRequestCallback(
  2165. IN HINTERNET hInternet,
  2166. IN DWORD_PTR dwContext,
  2167. IN DWORD dwInternetStatus,
  2168. IN LPVOID lpvStatusInformation OPTIONAL,
  2169. IN DWORD dwStatusInformationLength
  2170. )
  2171. {
  2172. switch (dwInternetStatus)
  2173. {
  2174. case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
  2175. {
  2176. DWORD * pErr = LPDWORD( dwContext );
  2177. DWORD * pFlags = LPDWORD( lpvStatusInformation );
  2178. ASSERT( pErr != NULL );
  2179. LogWarning("SSL error: flags %x", *pFlags );
  2180. *pErr = MapSecureHttpErrorCode( *pFlags );
  2181. break;
  2182. }
  2183. default:
  2184. LogWarning("bogus HTTP notification %x", dwInternetStatus );
  2185. break;
  2186. }
  2187. }
  2188. #endif
  2189. // N.B. This value must agree with the key specified in the system.adm file
  2190. static const WCHAR LM_COMPATIBILITY_LEVEL_KEY[] =
  2191. L"System\\Currentcontrolset\\Control\\Lsa";
  2192. HRESULT CheckLanManHashDisabled ()
  2193. /*++
  2194. Routine Description:
  2195. Check in the registry whether the lan man hash was disabled.
  2196. Arguments:
  2197. Disabled - on successful output, if true, the lan man hash was disabled.
  2198. Undefined on failure.
  2199. --*/
  2200. {
  2201. HKEY h = 0;
  2202. DWORD DwordSize;
  2203. DWORD Type;
  2204. DWORD Result;
  2205. char Buffer[20];
  2206. bLanManHashDisabled = FALSE;
  2207. DWORD Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  2208. LM_COMPATIBILITY_LEVEL_KEY,
  2209. 0,
  2210. KEY_READ,
  2211. &h);
  2212. if (Status == ERROR_FILE_NOT_FOUND)
  2213. {
  2214. // no key, proceed as if the hash is enabled
  2215. return S_OK;
  2216. }
  2217. else if (Status != ERROR_SUCCESS)
  2218. {
  2219. return HRESULT_FROM_WIN32( Status );
  2220. }
  2221. DwordSize = sizeof(DWORD);
  2222. Status = RegQueryValueExW(
  2223. h,
  2224. L"lmcompatibilitylevel",
  2225. 0,
  2226. &Type,
  2227. (LPBYTE) &Result,
  2228. &DwordSize
  2229. );
  2230. if (Status == ERROR_FILE_NOT_FOUND)
  2231. {
  2232. if (h)
  2233. {
  2234. RegCloseKey(h);
  2235. }
  2236. // no key, proceed as if hash is enabled
  2237. return S_OK;
  2238. }
  2239. if (Status == ERROR_SUCCESS
  2240. && Type == REG_DWORD
  2241. && Result >= 2)
  2242. {
  2243. bLanManHashDisabled = TRUE;
  2244. }
  2245. // if the type was not REG_DWORD or out of range, probably registry is corrupted
  2246. // in this case, assume hash is enabled
  2247. if (h)
  2248. {
  2249. RegCloseKey(h);
  2250. }
  2251. if (Status != ERROR_SUCCESS)
  2252. {
  2253. return HRESULT_FROM_WIN32( Status );
  2254. }
  2255. return S_OK;
  2256. }