Support Windows result uploads via WinHTTP
Windows builds previously stubbed out --upload entirely. WinHTTP handles TLS and certificate verification against the system trust store natively, so unlike the POSIX path this needs no OpenSSL/embedded CA bundle - just -lwinhttp, a system DLL that ships with Windows.
This commit is contained in:
@@ -42,8 +42,9 @@
|
||||
#
|
||||
# Windows binaries are built with the MinGW-w64 cross toolchain (package
|
||||
# mingw-w64-gcc on Arch/Debian/Fedora), statically linked so the .exe needs no
|
||||
# accompanying DLLs. Result upload (TLS) is not built for Windows - main.c
|
||||
# stubs it out - so no OpenSSL dependency is needed for these targets.
|
||||
# accompanying DLLs. Result upload (TLS) uses WinHTTP - a system component
|
||||
# present on every Windows install - instead of OpenSSL, so no OpenSSL
|
||||
# dependency is needed for these targets.
|
||||
# make windows-amd64 CC_WINDOWS_AMD64=x86_64-w64-mingw32-gcc-12
|
||||
# make windows-i386 CC_WINDOWS_I386=i686-w64-mingw32-gcc-12
|
||||
|
||||
@@ -201,16 +202,17 @@ $(DIST)/fossbench-macos-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST)
|
||||
|
||||
# Windows binaries are statically linked (-static) so the .exe is
|
||||
# self-contained: no libwinpthread/libgcc DLLs need to ship alongside it.
|
||||
# Result upload (TLS) is stubbed out for Windows in main.c, so unlike every
|
||||
# other target here, these don't need $(TLS_CFLAGS)/$(TLS_LDLIBS)/OpenSSL, and
|
||||
# $(LDFLAGS) is deliberately not used since it may carry a host-specific
|
||||
# -no-pie meant for a native i386 Linux build, not this cross target.
|
||||
# Result upload uses WinHTTP (-lwinhttp) instead of OpenSSL for TLS, so unlike
|
||||
# every other target here, these don't need $(TLS_CFLAGS)/$(TLS_LDLIBS); -static
|
||||
# doesn't affect winhttp.dll, which ships with Windows itself. $(LDFLAGS) is
|
||||
# deliberately not used since it may carry a host-specific -no-pie meant for a
|
||||
# native i386 Linux build, not this cross target.
|
||||
$(DIST)/fossbench-windows-amd64.exe: $(DRIVER) $(ASM_AMD64) | $(DIST)
|
||||
$(CC_WINDOWS_AMD64) $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_AMD64) -lm
|
||||
$(CC_WINDOWS_AMD64) $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_AMD64) -lm -lwinhttp
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-windows-i386.exe: $(DRIVER) $(ASM_I386) | $(DIST)
|
||||
$(CC_WINDOWS_I386) -march=pentium4 $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_I386) -lm
|
||||
$(CC_WINDOWS_I386) -march=pentium4 $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_I386) -lm -lwinhttp
|
||||
@echo "built $@"
|
||||
|
||||
# When the host is Linux/ARM64 or Linux/AMD64, the native binary IS one of the
|
||||
|
||||
+111
-11
@@ -47,7 +47,7 @@
|
||||
#ifndef FB_API_BASE_URL
|
||||
# define FB_API_BASE_URL "https://fossbench.net"
|
||||
#endif
|
||||
#define FB_VERSION "0.1.5-hotfix2"
|
||||
#define FB_VERSION "0.1.5-hotfix3"
|
||||
|
||||
/* ---------- platform identification (for the banner only) ---------- */
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
#if defined(_WIN32)
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# include <winhttp.h>
|
||||
static double now_seconds(void)
|
||||
{
|
||||
LARGE_INTEGER f, t;
|
||||
@@ -893,18 +894,116 @@ static int load_embedded_ca_bundle(SSL_CTX *ctx)
|
||||
ERR_clear_error(); /* PEM_read_bio_X509's final EOF "failure" is expected */
|
||||
return loaded > 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* WinHTTP reports a TLS handshake/certificate problem as one of several
|
||||
* specific ERROR_WINHTTP_SECURE_* codes (untrusted root, expired cert,
|
||||
* hostname mismatch, ...) rather than a single sentinel value. */
|
||||
static int is_winhttp_secure_error(DWORD err)
|
||||
{
|
||||
switch (err) {
|
||||
case ERROR_WINHTTP_SECURE_CERT_DATE_INVALID:
|
||||
case ERROR_WINHTTP_SECURE_CERT_CN_INVALID:
|
||||
case ERROR_WINHTTP_SECURE_INVALID_CA:
|
||||
case ERROR_WINHTTP_SECURE_CERT_REV_FAILED:
|
||||
case ERROR_WINHTTP_SECURE_CHANNEL_ERROR:
|
||||
case ERROR_WINHTTP_SECURE_INVALID_CERT:
|
||||
case ERROR_WINHTTP_SECURE_CERT_REVOKED:
|
||||
case ERROR_WINHTTP_SECURE_FAILURE:
|
||||
case ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE:
|
||||
case ERROR_WINHTTP_SECURE_FAILURE_PROXY:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* WinHTTP does TLS (and certificate verification, against the system trust
|
||||
* store) itself, so unlike the POSIX+OpenSSL path below, Windows needs
|
||||
* neither an embedded CA bundle nor a hand-rolled HTTP/1.1 request. */
|
||||
static int winhttp_post(const char *host, const char *port, const char *path,
|
||||
int use_tls, const char *payload, int payload_len,
|
||||
const char *auth_header, int *out_status)
|
||||
{
|
||||
wchar_t whost[256], wpath[512], wheaders[700];
|
||||
char header_buf[700];
|
||||
HINTERNET hsession = NULL, hconnect = NULL, hrequest = NULL;
|
||||
INTERNET_PORT wport = (INTERNET_PORT)atoi(port);
|
||||
DWORD status = 0, status_size = sizeof(status);
|
||||
int ok = 0;
|
||||
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, host, -1, whost, sizeof whost / sizeof whost[0]) == 0 ||
|
||||
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof wpath / sizeof wpath[0]) == 0)
|
||||
return 0;
|
||||
snprintf(header_buf, sizeof(header_buf), "Content-Type: application/json\r\n%s", auth_header);
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, header_buf, -1, wheaders, sizeof wheaders / sizeof wheaders[0]) == 0)
|
||||
return 0;
|
||||
|
||||
hsession = WinHttpOpen(L"fossbench", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hsession) {
|
||||
fprintf(stderr, " upload error: cannot initialize WinHTTP\n");
|
||||
return 0;
|
||||
}
|
||||
hconnect = WinHttpConnect(hsession, whost, wport, 0);
|
||||
if (!hconnect) {
|
||||
fprintf(stderr, " upload error: cannot connect to %s:%s\n", host, port);
|
||||
goto done;
|
||||
}
|
||||
hrequest = WinHttpOpenRequest(hconnect, L"POST", wpath, NULL, WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
||||
use_tls ? WINHTTP_FLAG_SECURE : 0);
|
||||
if (!hrequest) {
|
||||
fprintf(stderr, " upload error: cannot create HTTP request\n");
|
||||
goto done;
|
||||
}
|
||||
if (!WinHttpSendRequest(hrequest, wheaders, (DWORD)-1L, (LPVOID)payload,
|
||||
(DWORD)payload_len, (DWORD)payload_len, 0)) {
|
||||
if (is_winhttp_secure_error(GetLastError()))
|
||||
fprintf(stderr, " upload error: TLS connection or certificate verification failed\n");
|
||||
else
|
||||
fprintf(stderr, " upload error: send failed\n");
|
||||
goto done;
|
||||
}
|
||||
if (!WinHttpReceiveResponse(hrequest, NULL)) {
|
||||
if (is_winhttp_secure_error(GetLastError()))
|
||||
fprintf(stderr, " upload error: TLS connection or certificate verification failed\n");
|
||||
else
|
||||
fprintf(stderr, " upload error: no server response\n");
|
||||
goto done;
|
||||
}
|
||||
if (!WinHttpQueryHeaders(hrequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX, &status, &status_size,
|
||||
WINHTTP_NO_HEADER_INDEX)) {
|
||||
fprintf(stderr, " upload error: no server response\n");
|
||||
goto done;
|
||||
}
|
||||
*out_status = (int)status;
|
||||
ok = 1;
|
||||
done:
|
||||
if (hrequest) WinHttpCloseHandle(hrequest);
|
||||
if (hconnect) WinHttpCloseHandle(hconnect);
|
||||
WinHttpCloseHandle(hsession);
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int upload_results(const struct system_info *info, double score,
|
||||
uint64_t duration_ms, const char *token)
|
||||
{
|
||||
char host[256], port[16], path[512], payload[2048], request[4096];
|
||||
char host[256], port[16], path[512], payload[2048];
|
||||
char auth_header[600];
|
||||
char cpu[512], model[512], os[512], compiler[256], response[512];
|
||||
char cpu[512], model[512], os[512], compiler[256];
|
||||
const char *base = FB_API_BASE_URL, *p, *slash, *colon;
|
||||
int use_tls, status = 0, payload_len;
|
||||
#if !defined(_WIN32)
|
||||
char request[4096], response[512];
|
||||
struct addrinfo hints, *addresses = NULL, *a;
|
||||
SSL_CTX *tls_ctx = NULL;
|
||||
SSL *tls = NULL;
|
||||
int use_tls, fd = -1, status = 0, payload_len, request_len;
|
||||
int fd = -1, request_len;
|
||||
#endif
|
||||
|
||||
if (!strncmp(base, "https://", 8)) {
|
||||
use_tls = 1; p = base + 8; strcpy(port, "443");
|
||||
@@ -954,6 +1053,7 @@ static int upload_results(const struct system_info *info, double score,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#if !defined(_WIN32)
|
||||
request_len = snprintf(request, sizeof(request),
|
||||
"POST %s HTTP/1.1\r\nHost: %s:%s\r\nContent-Type: application/json\r\n"
|
||||
"Content-Length: %d\r\nConnection: close\r\n%s\r\n%s",
|
||||
@@ -1009,6 +1109,10 @@ static int upload_results(const struct system_info *info, double score,
|
||||
if (tls) { SSL_shutdown(tls); SSL_free(tls); }
|
||||
if (tls_ctx) SSL_CTX_free(tls_ctx);
|
||||
close(fd);
|
||||
#else
|
||||
if (!winhttp_post(host, port, path, use_tls, payload, payload_len, auth_header, &status))
|
||||
return 0;
|
||||
#endif
|
||||
if (status == 401) {
|
||||
fprintf(stderr, " upload failed: API token was rejected (HTTP 401)\n");
|
||||
return 0;
|
||||
@@ -1024,13 +1128,14 @@ static int upload_results(const struct system_info *info, double score,
|
||||
printf(" Results uploaded, pending administrator review (HTTP %d).\n", status);
|
||||
return 1;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
upload_failed:
|
||||
if (tls) SSL_free(tls);
|
||||
if (tls_ctx) SSL_CTX_free(tls_ctx);
|
||||
if (fd >= 0) close(fd);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------- output ---------- */
|
||||
|
||||
@@ -1193,13 +1298,8 @@ int main(int argc, char **argv)
|
||||
printf(" Result was not uploaded.\n");
|
||||
}
|
||||
|
||||
if (do_upload) {
|
||||
#if defined(_WIN32)
|
||||
fprintf(stderr, " Upload is not yet supported on Windows.\n");
|
||||
#else
|
||||
if (do_upload)
|
||||
upload_results(&system_info, multicore_score, duration_ms, token);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user