[vcpkg] Add sources for TLS 1.2 downloader tool. (#15516)

This commit is contained in:
Billy O'Neal 2021-01-13 14:05:38 -08:00 committed by GitHub
parent c239e82510
commit 4da47f758f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 352 additions and 2 deletions

View File

@ -36,6 +36,7 @@ Push-Location $toolsrc
try try
{ {
$files = Get-ChildItem -Recurse -LiteralPath "$toolsrc/src" -Filter '*.cpp' $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" -Filter '*.h'
$files += Get-ChildItem -Recurse -LiteralPath "$toolsrc/include/vcpkg-test" -Filter '*.h' $files += Get-ChildItem -Recurse -LiteralPath "$toolsrc/include/vcpkg-test" -Filter '*.h'
$files += Get-Item "$toolsrc/include/pch.h" $files += Get-Item "$toolsrc/include/pch.h"

View File

@ -52,6 +52,10 @@ jobs:
inputs: inputs:
InputType: 'CommandLine' InputType: 'CommandLine'
arguments: 'analyze "$(Build.StagingDirectory)\vcpkg.exe"' arguments: 'analyze "$(Build.StagingDirectory)\vcpkg.exe"'
- task: BinSkim@3
inputs:
InputType: 'CommandLine'
arguments: 'analyze "$(Build.StagingDirectory)\tls12-download.exe"'
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
displayName: 'Publish vcpkg.exe' displayName: 'Publish vcpkg.exe'
inputs: inputs:
@ -64,6 +68,18 @@ jobs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)\vcpkg.pdb' PathtoPublish: '$(Build.ArtifactStagingDirectory)\vcpkg.pdb'
ArtifactName: 'Windows' ArtifactName: 'Windows'
publishLocation: 'Container' 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 - task: MicroBuildCleanup@1
condition: succeededOrFailed() condition: succeededOrFailed()
displayName: MicroBuild Cleanup displayName: MicroBuild Cleanup

View File

@ -14,6 +14,9 @@
<FilesToSign Include="$(IntermediateOutputPath)\vcpkg.exe"> <FilesToSign Include="$(IntermediateOutputPath)\vcpkg.exe">
<Authenticode>Microsoft400</Authenticode> <Authenticode>Microsoft400</Authenticode>
</FilesToSign> </FilesToSign>
<FilesToSign Include="$(IntermediateOutputPath)\tls12-download.exe">
<Authenticode>Microsoft400</Authenticode>
</FilesToSign>
</ItemGroup> </ItemGroup>
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -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) cmake_minimum_required(VERSION 3.14)
endif()
project(vcpkg CXX) project(vcpkg C CXX)
include(cmake/utilities.cmake) include(cmake/utilities.cmake)
# =============== # ===============
@ -178,6 +183,18 @@ if(VCPKG_BUILD_FUZZING)
vcpkg_target_add_warning_options(vcpkg-fuzz) vcpkg_target_add_warning_options(vcpkg-fuzz)
endif() 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 === # === Target: format ===
find_program(CLANG_FORMAT clang-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_SOURCES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_INCLUDES} 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() endif()

View 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;
}