/* -*- 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 #include #include #include #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(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