Merge from master (#124)
* Add certificates for the sovereign clouds (#106) * On Windows, work around OpenSSL not hooking into the Windows Automatic Root Certificates Update process. (#112) * Add retry logic if we experience Curl-related errors * Allow overriding CURLOPT_CONNECTTIMEOUT via environment variable * Fix compiler errors for non-windows platform (#122) * - Wrap WinHttpDeleter structure with _WIN32 macro. - Use _WIN32 instead of WIN32 * More fixes for compiler errors on non-windows platform: - Include missing headers ( thread, chrono ) - Wrap EnsureRootCertsArePopulated routine with _WIN32 macro
This commit is contained in:
Родитель
bdeeb170f1
Коммит
cc0892da56
|
@ -1,9 +1,17 @@
|
|||
#if _MSC_VER
|
||||
// The use of std::getenv generates a warning on Windows.
|
||||
// Since the aim is to have portable code, suppress the warnings.
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#include "SoftwareEntitlementClient.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <curl/curl.h>
|
||||
|
@ -34,7 +42,8 @@
|
|||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Wincrypt.h"
|
||||
#include <Wincrypt.h>
|
||||
#include <winhttp.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -282,6 +291,28 @@ class Curl
|
|||
char _errbuf[CURL_ERROR_SIZE];
|
||||
std::string _response;
|
||||
|
||||
public:
|
||||
class CurlException : public Exception
|
||||
{
|
||||
CURLcode _code;
|
||||
|
||||
public:
|
||||
CurlException(CURLcode code, const std::string& message)
|
||||
: Exception(message)
|
||||
, _code(code)
|
||||
{}
|
||||
|
||||
CurlException(CURLcode code, const char *message)
|
||||
: Exception(message)
|
||||
, _code(code)
|
||||
{}
|
||||
|
||||
CURLcode GetCode() const
|
||||
{
|
||||
return _code;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
void ThrowIfCurlError(CURLcode res)
|
||||
{
|
||||
|
@ -292,7 +323,7 @@ private:
|
|||
|
||||
std::ostringstream what;
|
||||
what << "libcurl_error " << res << ": " << _errbuf;
|
||||
throw Exception(what.str());
|
||||
throw CurlException(res, what.str());
|
||||
}
|
||||
|
||||
static size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* context)
|
||||
|
@ -454,6 +485,19 @@ public:
|
|||
#ifdef _WIN32
|
||||
ThrowIfCurlError(curl_easy_setopt(_curl.get(), CURLOPT_SSL_CTX_FUNCTION, OpenSSLContextCallback));
|
||||
#endif // _WIN32
|
||||
|
||||
// Allow overriding the default connection timeout of 300 seconds.
|
||||
const char* env = std::getenv("AZ_BATCH_SES_CURLOPT_CONNECTTIMEOUT");
|
||||
long timeout = 0;
|
||||
if (env != nullptr)
|
||||
{
|
||||
timeout = std::strtol(env, nullptr, 10);
|
||||
}
|
||||
if (timeout <= 0 || timeout == LONG_MAX)
|
||||
{
|
||||
timeout = 300L;
|
||||
}
|
||||
ThrowIfCurlError(curl_easy_setopt(_curl.get(), CURLOPT_CONNECTTIMEOUT, timeout));
|
||||
}
|
||||
|
||||
void Post(
|
||||
|
@ -571,8 +615,99 @@ public:
|
|||
|
||||
throw Exception(GetErrorMessage(code));
|
||||
}
|
||||
|
||||
static std::unique_ptr<Entitlement> GetEntitlement(
|
||||
const std::string& url,
|
||||
const std::string& entitlement_token,
|
||||
const std::string& requested_entitlement)
|
||||
{
|
||||
Curl curl;
|
||||
curl.Post(url + "softwareEntitlements?api-version=2017-05-01.5.0", entitlement_token, requested_entitlement);
|
||||
|
||||
curl.VerifyIntermediateCertificate(url);
|
||||
|
||||
return curl.GetEntitlement();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
struct WinHttpDeleter
|
||||
{
|
||||
void operator()(HINTERNET h)
|
||||
{
|
||||
WinHttpCloseHandle(h);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<void, WinHttpDeleter> WinHttpHandle;
|
||||
|
||||
//
|
||||
// OpenSSL does not hook into the Windows Automatic Root Certificates Update process.
|
||||
// This results in certificate validation failures, so we perform a dummy connection
|
||||
// using WinHTTP which will setup the root cert store correctly.
|
||||
//
|
||||
void EnsureRootCertsArePopulated(const std::string& url)
|
||||
{
|
||||
std::string prefix("https://");
|
||||
size_t start = url.rfind(prefix, 0);
|
||||
if (start == std::string::npos)
|
||||
{
|
||||
throw Exception("Malformed URL: " + url);
|
||||
}
|
||||
|
||||
start += prefix.length();
|
||||
size_t end = url.find("/", start);
|
||||
std::string temp;
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
temp = url.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = url.substr(start, end - start);
|
||||
}
|
||||
|
||||
std::wstring hostname(temp.cbegin(), temp.cend());
|
||||
|
||||
WinHttpHandle hSession(
|
||||
WinHttpOpen(
|
||||
L"Azure Batch Software Entitlement Service client",
|
||||
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS,
|
||||
0)
|
||||
);
|
||||
ThrowIfWin32Error(hSession == nullptr);
|
||||
|
||||
WinHttpHandle hConn(
|
||||
WinHttpConnect(hSession.get(), hostname.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0)
|
||||
);
|
||||
ThrowIfWin32Error(hConn == nullptr);
|
||||
|
||||
WinHttpHandle hRequest(
|
||||
WinHttpOpenRequest(
|
||||
hConn.get(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
||||
WINHTTP_FLAG_SECURE)
|
||||
);
|
||||
ThrowIfWin32Error(hRequest == nullptr);
|
||||
|
||||
ThrowIfWin32Error(TRUE != WinHttpSendRequest(
|
||||
hRequest.get(),
|
||||
WINHTTP_NO_ADDITIONAL_HEADERS,
|
||||
0,
|
||||
WINHTTP_NO_REQUEST_DATA,
|
||||
0,
|
||||
0,
|
||||
0)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
@ -628,7 +763,8 @@ void Cleanup()
|
|||
std::unique_ptr<Entitlement> GetEntitlement(
|
||||
std::string url,
|
||||
const std::string& entitlement_token,
|
||||
const std::string& requested_entitlement)
|
||||
const std::string& requested_entitlement,
|
||||
unsigned int retries)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_lock);
|
||||
|
||||
|
@ -657,12 +793,32 @@ std::unique_ptr<Entitlement> GetEntitlement(
|
|||
url += '/';
|
||||
}
|
||||
|
||||
Curl curl;
|
||||
curl.Post(url + "softwareEntitlements?api-version=2017-05-01.5.0", entitlement_token, requested_entitlement);
|
||||
for (unsigned int retry = 1; retry <= retries; ++retry)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Curl::GetEntitlement(url, entitlement_token, requested_entitlement);
|
||||
}
|
||||
catch (const Curl::CurlException& e)
|
||||
{
|
||||
if (e.GetCode() == CURLE_OPERATION_TIMEDOUT)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(retry));
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else if (e.GetCode() == CURLE_SSL_CACERT)
|
||||
{
|
||||
EnsureRootCertsArePopulated(url);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curl.VerifyIntermediateCertificate(url);
|
||||
|
||||
return curl.GetEntitlement();
|
||||
return Curl::GetEntitlement(url, entitlement_token, requested_entitlement);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,8 @@ public:
|
|||
std::unique_ptr<Entitlement> GetEntitlement(
|
||||
std::string url,
|
||||
const std::string& entitlement_token,
|
||||
const std::string& requested_entitlement
|
||||
const std::string& requested_entitlement,
|
||||
unsigned int retries = 5
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>crypt32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winhttp.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
|
@ -116,7 +116,7 @@
|
|||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>crypt32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winhttp.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
|
@ -134,7 +134,7 @@
|
|||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winhttp.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
@ -152,7 +152,7 @@
|
|||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winhttp.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
Загрузка…
Ссылка в новой задаче