mirror of
https://github.com/microsoft/vcpkg.git
synced 2024-11-30 23:11:16 +08:00
[vcpkg] Add sources for TLS 1.2 downloader tool. (#15516)
This commit is contained in:
parent
c239e82510
commit
4da47f758f
@ -36,6 +36,7 @@ Push-Location $toolsrc
|
||||
try
|
||||
{
|
||||
$files = Get-ChildItem -Recurse -LiteralPath "$toolsrc/src" -Filter '*.cpp'
|
||||
$files += Get-ChildItem -Recurse -LiteralPath "$toolsrc/src" -Filter '*.c'
|
||||
$files += Get-ChildItem -Recurse -LiteralPath "$toolsrc/include/vcpkg" -Filter '*.h'
|
||||
$files += Get-ChildItem -Recurse -LiteralPath "$toolsrc/include/vcpkg-test" -Filter '*.h'
|
||||
$files += Get-Item "$toolsrc/include/pch.h"
|
||||
|
@ -52,6 +52,10 @@ jobs:
|
||||
inputs:
|
||||
InputType: 'CommandLine'
|
||||
arguments: 'analyze "$(Build.StagingDirectory)\vcpkg.exe"'
|
||||
- task: BinSkim@3
|
||||
inputs:
|
||||
InputType: 'CommandLine'
|
||||
arguments: 'analyze "$(Build.StagingDirectory)\tls12-download.exe"'
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish vcpkg.exe'
|
||||
inputs:
|
||||
@ -64,6 +68,18 @@ jobs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\vcpkg.pdb'
|
||||
ArtifactName: 'Windows'
|
||||
publishLocation: 'Container'
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish tls12-download.exe'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\tls12-download.exe'
|
||||
ArtifactName: 'Windows'
|
||||
publishLocation: 'Container'
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish tls12-download.pdb'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\tls12-download.pdb'
|
||||
ArtifactName: 'Windows'
|
||||
publishLocation: 'Container'
|
||||
- task: MicroBuildCleanup@1
|
||||
condition: succeededOrFailed()
|
||||
displayName: MicroBuild Cleanup
|
||||
|
@ -14,6 +14,9 @@
|
||||
<FilesToSign Include="$(IntermediateOutputPath)\vcpkg.exe">
|
||||
<Authenticode>Microsoft400</Authenticode>
|
||||
</FilesToSign>
|
||||
<FilesToSign Include="$(IntermediateOutputPath)\tls12-download.exe">
|
||||
<Authenticode>Microsoft400</Authenticode>
|
||||
</FilesToSign>
|
||||
</ItemGroup>
|
||||
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
@ -1,6 +1,11 @@
|
||||
if(WIN32)
|
||||
# 3.16 for MSVC_RUNTIME_LIBRARY
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
endif()
|
||||
|
||||
project(vcpkg CXX)
|
||||
project(vcpkg C CXX)
|
||||
include(cmake/utilities.cmake)
|
||||
|
||||
# ===============
|
||||
@ -178,6 +183,18 @@ if(VCPKG_BUILD_FUZZING)
|
||||
vcpkg_target_add_warning_options(vcpkg-fuzz)
|
||||
endif()
|
||||
|
||||
|
||||
# === Target: tls12-download ===
|
||||
|
||||
set(TLS12_DOWNLOAD_SOURCES src/tls12-download.c)
|
||||
if(WIN32)
|
||||
add_executable(tls12-download ${TLS12_DOWNLOAD_SOURCES})
|
||||
set_property(TARGET tls12-download PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
|
||||
set_property(TARGET tls12-download APPEND PROPERTY LINK_OPTIONS "$<IF:$<CONFIG:Debug>,,/ENTRY:entry>")
|
||||
target_link_libraries(tls12-download winhttp)
|
||||
endif()
|
||||
|
||||
|
||||
# === Target: format ===
|
||||
|
||||
find_program(CLANG_FORMAT clang-format)
|
||||
@ -196,5 +213,7 @@ if(CLANG_FORMAT)
|
||||
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_SOURCES}
|
||||
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_INCLUDES}
|
||||
|
||||
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_FUZZ_SOURCES})
|
||||
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_FUZZ_SOURCES}
|
||||
COMMAND ${CLANG_FORMAT} -i -verbose ${TLS12_DOWNLOAD_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
311
toolsrc/src/tls12-download.c
Normal file
311
toolsrc/src/tls12-download.c
Normal file
@ -0,0 +1,311 @@
|
||||
#include <Windows.h>
|
||||
#include <process.h>
|
||||
#include <winhttp.h>
|
||||
/*
|
||||
* This program must be as small as possible, because it is committed in binary form to the
|
||||
* vcpkg github repo to enable downloading the main vcpkg program on Windows 7, where TLS 1.2 is
|
||||
* unavailable to PowerShell.
|
||||
* To that end it avoids using C runtime functions (beyond the vcruntime ones the compiler
|
||||
* injects itself).
|
||||
* (In testing as of 2021-01-07, this version that doesn't link with the CRT is ~8kb, whereas a
|
||||
* hello world program that does link with the CRT is ~300kb)
|
||||
*/
|
||||
|
||||
static void __declspec(noreturn) win32_abort()
|
||||
{
|
||||
/*
|
||||
* Note that TerminateProcess does not return when called from the terminated process, see
|
||||
* https://github.com/MicrosoftDocs/sdk-api/pull/626
|
||||
*/
|
||||
TerminateProcess(GetCurrentProcess(), 3);
|
||||
}
|
||||
|
||||
static size_t wide_length(const wchar_t* str)
|
||||
{
|
||||
size_t answer = 0;
|
||||
while (*str)
|
||||
{
|
||||
++answer;
|
||||
++str;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
static void write_message(const HANDLE std_out, const wchar_t* msg)
|
||||
{
|
||||
size_t wchars_to_write = wide_length(msg);
|
||||
if (wchars_to_write == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (wchars_to_write > 65535)
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
if (WriteConsoleW(std_out, msg, wchars_to_write, 0, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// this happens if output has been redirected
|
||||
int narrow_chars = WideCharToMultiByte(CP_ACP, 0, msg, (int)wchars_to_write, 0, 0, 0, 0);
|
||||
if (narrow_chars == 0)
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
char* narrow_buffer = HeapAlloc(GetProcessHeap(), 0, (size_t)narrow_chars);
|
||||
if (WideCharToMultiByte(CP_ACP, 0, msg, (int)wchars_to_write, narrow_buffer, narrow_chars, 0, 0) == 0)
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
while (narrow_chars != 0)
|
||||
{
|
||||
DWORD chars_written;
|
||||
if (!WriteFile(std_out, narrow_buffer, (DWORD)narrow_chars, &chars_written, 0))
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
narrow_chars -= (int)chars_written;
|
||||
}
|
||||
|
||||
if (!HeapFree(GetProcessHeap(), 0, narrow_buffer))
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void write_number(const HANDLE std_out, DWORD number)
|
||||
{
|
||||
wchar_t buffer[11]; // 4294967295\0
|
||||
wchar_t* first_digit = buffer + 11;
|
||||
*--first_digit = L'\0';
|
||||
if (number == 0)
|
||||
{
|
||||
*--first_digit = L'0';
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
*--first_digit = L'0' + number % 10;
|
||||
number /= 10;
|
||||
} while (number != 0);
|
||||
}
|
||||
|
||||
write_message(std_out, first_digit);
|
||||
}
|
||||
|
||||
static void write_hex(const HANDLE std_out, DWORD number)
|
||||
{
|
||||
wchar_t buffer[] = L"0x00000000";
|
||||
wchar_t* first_digit = buffer + (sizeof(buffer) / sizeof(wchar_t)) - 1;
|
||||
while (number != 0)
|
||||
{
|
||||
*--first_digit = L"0123456789ABCDEF"[number % 16];
|
||||
number /= 16;
|
||||
}
|
||||
|
||||
write_message(std_out, buffer);
|
||||
}
|
||||
|
||||
static void __declspec(noreturn) abort_api_failure(const HANDLE std_out, const wchar_t* api_name)
|
||||
{
|
||||
DWORD last_error = GetLastError();
|
||||
write_message(std_out, L"While calling Windows API function ");
|
||||
write_message(std_out, api_name);
|
||||
write_message(std_out, L" got error ");
|
||||
write_hex(std_out, last_error);
|
||||
write_message(std_out, L":\r\n");
|
||||
wchar_t* message;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
GetModuleHandleW(L"winhttp.dll"),
|
||||
last_error,
|
||||
0,
|
||||
(LPWSTR)&message,
|
||||
0,
|
||||
0))
|
||||
{
|
||||
write_message(std_out, message);
|
||||
// intentionally leaks the message buffer
|
||||
}
|
||||
else
|
||||
{
|
||||
last_error = GetLastError();
|
||||
write_message(std_out, L"(unknown error, FormatMessageW failed with ");
|
||||
write_hex(std_out, last_error);
|
||||
write_message(std_out, L")");
|
||||
}
|
||||
|
||||
write_message(std_out, L"\r\n");
|
||||
FlushFileBuffers(std_out);
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
int main()
|
||||
#else // ^^^ debug // !debug vvv
|
||||
int __stdcall entry()
|
||||
#endif // ^^^ !debug
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
__security_init_cookie();
|
||||
#endif // ^^^ release
|
||||
|
||||
const HANDLE std_out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (std_out == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
int argc;
|
||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc); // intentionally leaks argv
|
||||
if (argv == 0)
|
||||
{
|
||||
win32_abort();
|
||||
}
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
write_message(std_out, L"Usage: tls12-download.exe DOMAIN RELATIVE-PATH OUT-FILE\r\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const wchar_t* const domain = argv[1];
|
||||
const wchar_t* const relative_path = argv[2];
|
||||
const wchar_t* const out_file_path = argv[3];
|
||||
write_message(std_out, L"Downloading https://");
|
||||
write_message(std_out, domain);
|
||||
write_message(std_out, relative_path);
|
||||
write_message(std_out, L" -> ");
|
||||
write_message(std_out, out_file_path);
|
||||
|
||||
wchar_t https_proxy_env[32767];
|
||||
DWORD access_type;
|
||||
const wchar_t* proxy_setting;
|
||||
const wchar_t* proxy_bypass_setting;
|
||||
if (GetEnvironmentVariableW(L"HTTPS_PROXY", https_proxy_env, sizeof(https_proxy_env) / sizeof(wchar_t)))
|
||||
{
|
||||
access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
|
||||
proxy_setting = https_proxy_env;
|
||||
proxy_bypass_setting = L"<local>";
|
||||
write_message(std_out, L" (using proxy: ");
|
||||
write_message(std_out, proxy_setting);
|
||||
write_message(std_out, L")");
|
||||
}
|
||||
else if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
|
||||
{
|
||||
access_type = WINHTTP_ACCESS_TYPE_NO_PROXY;
|
||||
proxy_setting = WINHTTP_NO_PROXY_NAME;
|
||||
proxy_bypass_setting = WINHTTP_NO_PROXY_BYPASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
abort_api_failure(std_out, L"GetEnvironmentVariableW");
|
||||
}
|
||||
|
||||
write_message(std_out, L"\r\n");
|
||||
|
||||
const HANDLE out_file = CreateFileW(out_file_path, FILE_WRITE_DATA, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (out_file == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
abort_api_failure(std_out, L"CreateFileW");
|
||||
}
|
||||
|
||||
BOOL results = FALSE;
|
||||
const HINTERNET session = WinHttpOpen(L"tls12-download/1.0", access_type, proxy_setting, proxy_bypass_setting, 0);
|
||||
if (!session)
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpOpen");
|
||||
}
|
||||
|
||||
unsigned long secure_protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
|
||||
if (!WinHttpSetOption(session, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(DWORD)))
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpSetOption");
|
||||
}
|
||||
|
||||
const HINTERNET connect = WinHttpConnect(session, domain, INTERNET_DEFAULT_HTTPS_PORT, 0);
|
||||
if (!connect)
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpConnect");
|
||||
}
|
||||
|
||||
const HINTERNET request = WinHttpOpenRequest(
|
||||
connect, L"GET", relative_path, 0, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
|
||||
if (!request)
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpOpenRequest");
|
||||
}
|
||||
|
||||
if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpSendRequest");
|
||||
}
|
||||
|
||||
if (!WinHttpReceiveResponse(request, 0))
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpReceiveResponse");
|
||||
}
|
||||
|
||||
DWORD http_code = 0;
|
||||
DWORD query_headers_buffer_size = sizeof(http_code);
|
||||
if (!WinHttpQueryHeaders(request,
|
||||
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
&http_code,
|
||||
&query_headers_buffer_size,
|
||||
WINHTTP_NO_HEADER_INDEX))
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpQueryHeaders");
|
||||
}
|
||||
|
||||
if (http_code != 200)
|
||||
{
|
||||
write_message(std_out, L"Download failed, server returned HTTP status: ");
|
||||
write_number(std_out, http_code);
|
||||
write_message(std_out, L"\r\n");
|
||||
FlushFileBuffers(std_out);
|
||||
TerminateProcess(GetCurrentProcess(), 2);
|
||||
}
|
||||
|
||||
char buffer[32768];
|
||||
for (;;)
|
||||
{
|
||||
DWORD received_bytes;
|
||||
if (!WinHttpReadData(request, buffer, sizeof(buffer), &received_bytes))
|
||||
{
|
||||
abort_api_failure(std_out, L"WinHttpReadData");
|
||||
}
|
||||
|
||||
if (received_bytes == 0)
|
||||
{
|
||||
break; // end of response
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
DWORD written_bytes;
|
||||
if (!WriteFile(out_file, buffer, received_bytes, &written_bytes, 0))
|
||||
{
|
||||
abort_api_failure(std_out, L"WriteFile");
|
||||
}
|
||||
|
||||
received_bytes -= written_bytes;
|
||||
} while (received_bytes != 0);
|
||||
}
|
||||
|
||||
WinHttpCloseHandle(request);
|
||||
WinHttpCloseHandle(connect);
|
||||
WinHttpCloseHandle(session);
|
||||
CloseHandle(out_file);
|
||||
|
||||
write_message(std_out, L"Done.\r\n");
|
||||
FlushFileBuffers(std_out);
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user