summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/pingsender/pingsender_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/telemetry/pingsender/pingsender_win.cpp')
-rw-r--r--toolkit/components/telemetry/pingsender/pingsender_win.cpp180
1 files changed, 180 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/pingsender/pingsender_win.cpp b/toolkit/components/telemetry/pingsender/pingsender_win.cpp
new file mode 100644
index 0000000000..c0c250d7c5
--- /dev/null
+++ b/toolkit/components/telemetry/pingsender/pingsender_win.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string>
+
+#include <direct.h>
+#include <windows.h>
+#include <wininet.h>
+
+#include "pingsender.h"
+
+namespace PingSender {
+
+using std::string;
+
+/**
+ * A helper to automatically close internet handles when they go out of scope
+ */
+class ScopedHInternet {
+ public:
+ explicit ScopedHInternet(HINTERNET handle) : mHandle(handle) {}
+
+ ~ScopedHInternet() {
+ if (mHandle) {
+ InternetCloseHandle(mHandle);
+ }
+ }
+
+ bool empty() { return (mHandle == nullptr); }
+ HINTERNET get() { return mHandle; }
+
+ private:
+ HINTERNET mHandle;
+};
+
+const size_t kUrlComponentsSchemeLength = 256;
+const size_t kUrlComponentsHostLength = 256;
+const size_t kUrlComponentsPathLength = 256;
+
+/**
+ * Post the specified payload to a telemetry server
+ *
+ * @param url The URL of the telemetry server
+ * @param payload The ping payload
+ */
+bool Post(const string& url, const string& payload) {
+ char scheme[kUrlComponentsSchemeLength];
+ char host[kUrlComponentsHostLength];
+ char path[kUrlComponentsPathLength];
+
+ URL_COMPONENTS components = {};
+ components.dwStructSize = sizeof(components);
+ components.lpszScheme = scheme;
+ components.dwSchemeLength = kUrlComponentsSchemeLength;
+ components.lpszHostName = host;
+ components.dwHostNameLength = kUrlComponentsHostLength;
+ components.lpszUrlPath = path;
+ components.dwUrlPathLength = kUrlComponentsPathLength;
+
+ if (!InternetCrackUrl(url.c_str(), url.size(), 0, &components)) {
+ PINGSENDER_LOG("ERROR: Could not separate the URL components\n");
+ return false;
+ }
+
+ if (!IsValidDestination(host)) {
+ PINGSENDER_LOG("ERROR: Invalid destination host '%s'\n", host);
+ return false;
+ }
+
+ ScopedHInternet internet(InternetOpen(kUserAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ /* lpszProxyName */ NULL,
+ /* lpszProxyBypass */ NULL,
+ /* dwFlags */ 0));
+
+ if (internet.empty()) {
+ PINGSENDER_LOG("ERROR: Could not open wininet internet handle\n");
+ return false;
+ }
+
+ DWORD timeout = static_cast<DWORD>(kConnectionTimeoutMs);
+ bool rv = InternetSetOption(internet.get(), INTERNET_OPTION_CONNECT_TIMEOUT,
+ &timeout, sizeof(timeout));
+ if (!rv) {
+ PINGSENDER_LOG("ERROR: Could not set the connection timeout\n");
+ return false;
+ }
+
+ ScopedHInternet connection(
+ InternetConnect(internet.get(), host, components.nPort,
+ /* lpszUsername */ NULL,
+ /* lpszPassword */ NULL, INTERNET_SERVICE_HTTP,
+ /* dwFlags */ 0,
+ /* dwContext */ NULL));
+
+ if (connection.empty()) {
+ PINGSENDER_LOG("ERROR: Could not connect\n");
+ return false;
+ }
+
+ DWORD flags = ((strcmp(scheme, "https") == 0) ? INTERNET_FLAG_SECURE : 0) |
+ INTERNET_FLAG_NO_COOKIES;
+ ScopedHInternet request(HttpOpenRequest(connection.get(), "POST", path,
+ /* lpszVersion */ NULL,
+ /* lpszReferer */ NULL,
+ /* lplpszAcceptTypes */ NULL, flags,
+ /* dwContext */ NULL));
+
+ if (request.empty()) {
+ PINGSENDER_LOG("ERROR: Could not open HTTP POST request\n");
+ return false;
+ }
+
+ // Build a string containing all the headers.
+ std::string headers = GenerateDateHeader() + "\r\n";
+ headers += kCustomVersionHeader;
+ headers += "\r\n";
+ headers += kContentEncodingHeader;
+
+ rv = HttpSendRequest(request.get(), headers.c_str(), -1L,
+ (LPVOID)payload.c_str(), payload.size());
+ if (!rv) {
+ PINGSENDER_LOG("ERROR: Could not execute HTTP POST request\n");
+ return false;
+ }
+
+ // HttpSendRequest doesn't fail if we hit an HTTP error, so manually check
+ // for errors. Please note that this is not needed on the Linux/MacOS version
+ // of the pingsender, as libcurl already automatically fails on HTTP errors.
+ DWORD statusCode = 0;
+ DWORD bufferLength = sizeof(DWORD);
+ rv = HttpQueryInfo(
+ request.get(),
+ /* dwInfoLevel */ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
+ /* lpvBuffer */ &statusCode,
+ /* lpdwBufferLength */ &bufferLength,
+ /* lpdwIndex */ NULL);
+ if (!rv) {
+ PINGSENDER_LOG("ERROR: Could not get the HTTP status code\n");
+ return false;
+ }
+
+ if (statusCode != 200) {
+ PINGSENDER_LOG("ERROR: Error submitting the HTTP request: code %lu\n",
+ statusCode);
+ return false;
+ }
+
+ return rv;
+}
+
+void ChangeCurrentWorkingDirectory(const string& pingPath) {
+ char fullPath[MAX_PATH + 1] = {};
+ if (!_fullpath(fullPath, pingPath.c_str(), sizeof(fullPath))) {
+ PINGSENDER_LOG("Could not build the full path to the ping\n");
+ return;
+ }
+
+ char drive[_MAX_DRIVE] = {};
+ char dir[_MAX_DIR] = {};
+ if (_splitpath_s(fullPath, drive, sizeof(drive), dir, sizeof(dir), nullptr, 0,
+ nullptr, 0)) {
+ PINGSENDER_LOG("Could not split the current path\n");
+ return;
+ }
+
+ char cwd[MAX_PATH + 1] = {};
+ if (_makepath_s(cwd, sizeof(cwd), drive, dir, nullptr, nullptr)) {
+ PINGSENDER_LOG("Could not assemble the path for the new cwd\n");
+ return;
+ }
+
+ if (_chdir(cwd) == -1) {
+ PINGSENDER_LOG("Could not change the current working directory\n");
+ }
+}
+
+} // namespace PingSender