From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../include/content_analysis/sdk/analysis_client.h | 84 ++++ .../browser/src/client_base.cc | 21 + .../content_analysis_sdk/browser/src/client_base.h | 34 ++ .../content_analysis_sdk/browser/src/client_mac.cc | 33 ++ .../content_analysis_sdk/browser/src/client_mac.h | 28 ++ .../browser/src/client_posix.cc | 33 ++ .../browser/src/client_posix.h | 28 ++ .../content_analysis_sdk/browser/src/client_win.cc | 432 +++++++++++++++++++++ .../content_analysis_sdk/browser/src/client_win.h | 39 ++ 9 files changed, 732 insertions(+) create mode 100644 third_party/content_analysis_sdk/browser/include/content_analysis/sdk/analysis_client.h create mode 100644 third_party/content_analysis_sdk/browser/src/client_base.cc create mode 100644 third_party/content_analysis_sdk/browser/src/client_base.h create mode 100644 third_party/content_analysis_sdk/browser/src/client_mac.cc create mode 100644 third_party/content_analysis_sdk/browser/src/client_mac.h create mode 100644 third_party/content_analysis_sdk/browser/src/client_posix.cc create mode 100644 third_party/content_analysis_sdk/browser/src/client_posix.h create mode 100644 third_party/content_analysis_sdk/browser/src/client_win.cc create mode 100644 third_party/content_analysis_sdk/browser/src/client_win.h (limited to 'third_party/content_analysis_sdk/browser') diff --git a/third_party/content_analysis_sdk/browser/include/content_analysis/sdk/analysis_client.h b/third_party/content_analysis_sdk/browser/include/content_analysis/sdk/analysis_client.h new file mode 100644 index 0000000000..c7c5da8520 --- /dev/null +++ b/third_party/content_analysis_sdk/browser/include/content_analysis/sdk/analysis_client.h @@ -0,0 +1,84 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_ANALYSIS_BROWSER_INCLUDE_CONTENT_ANALYSIS_SDK_ANALYSIS_CLIENT_H_ +#define CONTENT_ANALYSIS_BROWSER_INCLUDE_CONTENT_ANALYSIS_SDK_ANALYSIS_CLIENT_H_ + +#include +#include + +#include "content_analysis/sdk/analysis.pb.h" + +// This is the main include file for code using Content Analysis Connector +// Client SDK. No other include is needed. +// +// A browser begins by creating an instance of Client using the factory +// function Client::Create(). This instance should live as long as the browser +// intends to send content analysis requests to the content analysis agent. + +namespace content_analysis { +namespace sdk { + +// Represents information about one instance of a content analysis agent +// process that is connected to the browser. +struct AgentInfo { + unsigned long pid = 0; // Process ID of content analysis agent process. + std::string binary_path; // The full path to the process's main binary. +}; + +// Represents a client that can request content analysis for locally running +// agent. This class holds the client endpoint that the browser connects +// with when content analysis is required. +// +// See the demo directory for an example of how to use this class. +class Client { + public: + // Configuration options where creating an agent. `name` is used to create + // a channel between the agent and Google Chrome. + struct Config { + // Used to create a channel between the agent and Google Chrome. Both must + // use the same name to properly rendezvous with each other. The channel + // is platform specific. + std::string name; + + // Set to true if there is a different agent instance per OS user. Defaults + // to false. + bool user_specific = false; + }; + + // Returns a new client instance and calls Start(). + static std::unique_ptr Create(Config config); + + virtual ~Client() = default; + + // Returns the configuration parameters used to create the client. + virtual const Config& GetConfig() const = 0; + + // Retrives information about the agent that is connected to the browser. + virtual const AgentInfo& GetAgentInfo() const = 0; + + // Sends an analysis request to the agent and waits for a response. + virtual int Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) = 0; + + // Sends an response acknowledgment back to the agent. + virtual int Acknowledge(const ContentAnalysisAcknowledgement& ack) = 0; + + // Ask the agent to cancel all requests matching the criteria in `cancel`. + // This is a best effort only, the agent may cancel some, all, or no requests + // that match. + virtual int CancelRequests(const ContentAnalysisCancelRequests& cancel) = 0; + + protected: + Client() = default; + Client(const Client& rhs) = delete; + Client(Client&& rhs) = delete; + Client& operator=(const Client& rhs) = delete; + Client& operator=(Client&& rhs) = delete; +}; + +} // namespace sdk +} // namespace content_analysis + +#endif // CONTENT_ANALYSIS_BROWSER_INCLUDE_CONTENT_ANALYSIS_SDK_ANALYSIS_CLIENT_H_ diff --git a/third_party/content_analysis_sdk/browser/src/client_base.cc b/third_party/content_analysis_sdk/browser/src/client_base.cc new file mode 100644 index 0000000000..0147c0cbee --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_base.cc @@ -0,0 +1,21 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "client_base.h" + +namespace content_analysis { +namespace sdk { + +ClientBase::ClientBase(Config config) : config_(config) {} + +const Client::Config& ClientBase::GetConfig() const { + return config_; +} + +const AgentInfo& ClientBase::GetAgentInfo() const { + return agent_info_; +} + +} // namespace sdk +} // namespace content_analysis diff --git a/third_party/content_analysis_sdk/browser/src/client_base.h b/third_party/content_analysis_sdk/browser/src/client_base.h new file mode 100644 index 0000000000..f8d7849394 --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_base.h @@ -0,0 +1,34 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_BASE_H_ +#define CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_BASE_H_ + +#include "content_analysis/sdk/analysis_client.h" + +namespace content_analysis { +namespace sdk { + +// Base Client class with code common to all platforms. +class ClientBase : public Client { + public: + // Client: + const Config& GetConfig() const override; + const AgentInfo& GetAgentInfo() const override; + + protected: + ClientBase(Config config); + + const Config& configuration() const { return config_; } + AgentInfo& agent_info() { return agent_info_; } + +private: + Config config_; + AgentInfo agent_info_; +}; + +} // namespace sdk +} // namespace content_analysis + +#endif // CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_BASE_H_ \ No newline at end of file diff --git a/third_party/content_analysis_sdk/browser/src/client_mac.cc b/third_party/content_analysis_sdk/browser/src/client_mac.cc new file mode 100644 index 0000000000..c6f5a798c1 --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_mac.cc @@ -0,0 +1,33 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "client_mac.h" + +namespace content_analysis { +namespace sdk { + +// static +std::unique_ptr Client::Create(Config config) { + return std::make_unique(std::move(config)); +} + +ClientMac::ClientMac(Config config) : ClientBase(std::move(config)) {} + +int ClientMac::Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) { + return -1; +} + +int ClientMac::Acknowledge(const ContentAnalysisAcknowledgement& ack) { + return -1; +} + +int ClientMac::CancelRequests(const ContentAnalysisCancelRequests& cancel) { + return -1; +} + +} // namespace sdk +} // namespace content_analysis \ No newline at end of file diff --git a/third_party/content_analysis_sdk/browser/src/client_mac.h b/third_party/content_analysis_sdk/browser/src/client_mac.h new file mode 100644 index 0000000000..f3c640dd89 --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_mac.h @@ -0,0 +1,28 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_MAC_H_ +#define CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_MAC_H_ + +#include "client_base.h" + +namespace content_analysis { +namespace sdk { + +// Client implementaton for macOS. +class ClientMac : public ClientBase { + public: + ClientMac(Config config); + + // Client: + int Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) override; + int Acknowledge(const ContentAnalysisAcknowledgement& ack) override; + int CancelRequests(const ContentAnalysisCancelRequests& cancel) override; +}; + +} // namespace sdk +} // namespace content_analysis + +#endif // CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_MAC_H_ \ No newline at end of file diff --git a/third_party/content_analysis_sdk/browser/src/client_posix.cc b/third_party/content_analysis_sdk/browser/src/client_posix.cc new file mode 100644 index 0000000000..14277724fd --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_posix.cc @@ -0,0 +1,33 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "client_posix.h" + +namespace content_analysis { +namespace sdk { + +// static +std::unique_ptr Client::Create(Config config) { + return std::make_unique(std::move(config)); +} + +ClientPosix::ClientPosix(Config config) : ClientBase(std::move(config)) {} + +int ClientPosix::Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) { + return -1; +} + +int ClientPosix::Acknowledge(const ContentAnalysisAcknowledgement& ack) { + return -1; +} + +int ClientPosix::CancelRequests(const ContentAnalysisCancelRequests& cancel) { + return -1; +} + +} // namespace sdk +} // namespace content_analysis diff --git a/third_party/content_analysis_sdk/browser/src/client_posix.h b/third_party/content_analysis_sdk/browser/src/client_posix.h new file mode 100644 index 0000000000..9e7666d681 --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_posix.h @@ -0,0 +1,28 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_POSIX_H_ +#define CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_POSIX_H_ + +#include "client_base.h" + +namespace content_analysis { +namespace sdk { + +// Client implementaton for Posix. +class ClientPosix : public ClientBase { + public: + ClientPosix(Config config); + + // Client: + int Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) override; + int Acknowledge(const ContentAnalysisAcknowledgement& ack) override; + int CancelRequests(const ContentAnalysisCancelRequests& cancel) override; +}; + +} // namespace sdk +} // namespace content_analysis + +#endif // CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_POSIX_H_ \ No newline at end of file diff --git a/third_party/content_analysis_sdk/browser/src/client_win.cc b/third_party/content_analysis_sdk/browser/src/client_win.cc new file mode 100644 index 0000000000..039946d131 --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_win.cc @@ -0,0 +1,432 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include +#include +#include +#include + +#include "common/utils_win.h" + +#include "client_win.h" + +namespace content_analysis { +namespace sdk { + +const DWORD kBufferSize = 4096; + +// Use the same default timeout value (50ms) as CreateNamedPipeA(), expressed +// in 100ns intervals. +constexpr LONGLONG kDefaultTimeout = 500000; + +// The following #defines and struct are copied from the official Microsoft +// Windows Driver Kit headers because they are not available in the official +// Microsoft Windows user mode SDK headers. + +#define FSCTL_PIPE_WAIT 0x110018 +#define STATUS_SUCCESS ((NTSTATUS)0) +#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xc00000ac) +#define STATUS_IO_TIMEOUT ((NTSTATUS)0xc00000b5) + +typedef struct _FILE_PIPE_WAIT_FOR_BUFFER { + LARGE_INTEGER Timeout; + ULONG NameLength; + BOOLEAN TimeoutSpecified; + WCHAR Name[1]; +} FILE_PIPE_WAIT_FOR_BUFFER, *PFILE_PIPE_WAIT_FOR_BUFFER; + +namespace { + +using NtCreateFileFn = decltype(&::NtCreateFile); + +NtCreateFileFn GetNtCreateFileFn() { + static NtCreateFileFn fnNtCreateFile = []() { + NtCreateFileFn fn = nullptr; + HMODULE h = LoadLibraryA("NtDll.dll"); + if (h != nullptr) { + fn = reinterpret_cast(GetProcAddress(h, "NtCreateFile")); + FreeLibrary(h); + } + return fn; + }(); + + return fnNtCreateFile; +} + + +using NtFsControlFileFn = NTSTATUS (NTAPI *)( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG IoControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength); + +NtFsControlFileFn GetNtFsControlFileFn() { + static NtFsControlFileFn fnNtFsControlFile = []() { + NtFsControlFileFn fn = nullptr; + HMODULE h = LoadLibraryA("NtDll.dll"); + if (h != nullptr) { + fn = reinterpret_cast(GetProcAddress(h, "NtFsControlFile")); + FreeLibrary(h); + } + return fn; + }(); + + return fnNtFsControlFile; +} + +NTSTATUS WaitForPipeAvailability(const UNICODE_STRING& path) { + NtCreateFileFn fnNtCreateFile = GetNtCreateFileFn(); + if (fnNtCreateFile == nullptr) { + return false; + } + NtFsControlFileFn fnNtFsControlFile = GetNtFsControlFileFn(); + if (fnNtFsControlFile == nullptr) { + return false; + } + + // Build the device name. This is the initial part of `path` which is + // assumed to start with the string `kPipePrefixForClient`. The `Length` + // field is measured in bytes, not characters, and does not include the null + // terminator. It's important that the device name ends with a trailing + // backslash. + size_t device_name_char_length = std::strlen(internal::kPipePrefixForClient); + UNICODE_STRING device_name; + device_name.Buffer = path.Buffer; + device_name.Length = device_name_char_length * sizeof(wchar_t); + device_name.MaximumLength = device_name.Length; + + // Build the pipe name. This is the remaining part of `path` after the device + // name. + UNICODE_STRING pipe_name; + pipe_name.Buffer = path.Buffer + device_name_char_length; + pipe_name.Length = path.Length - device_name.Length; + pipe_name.MaximumLength = pipe_name.Length; + + // Build the ioctl input buffer. This buffer is the size of + // FILE_PIPE_WAIT_FOR_BUFFER plus the length of the pipe name. Since + // FILE_PIPE_WAIT_FOR_BUFFER includes one WCHAR this includes space for + // the terminating null character of the name which wcsncpy() copies. + size_t buffer_size = sizeof(FILE_PIPE_WAIT_FOR_BUFFER) + pipe_name.Length; + std::vector buffer(buffer_size); + FILE_PIPE_WAIT_FOR_BUFFER* wait_buffer = + reinterpret_cast(buffer.data()); + wait_buffer->Timeout.QuadPart = kDefaultTimeout; + wait_buffer->NameLength = pipe_name.Length; + wait_buffer->TimeoutSpecified = TRUE; + std::wcsncpy(wait_buffer->Name, pipe_name.Buffer, wait_buffer->NameLength / + sizeof(wchar_t)); + + OBJECT_ATTRIBUTES attr; + InitializeObjectAttributes(&attr, &device_name, OBJ_CASE_INSENSITIVE, nullptr, + nullptr); + + IO_STATUS_BLOCK io; + HANDLE h = INVALID_HANDLE_VALUE; + NTSTATUS sts = fnNtCreateFile(&h, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &attr, &io, /*AllocationSize=*/nullptr, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, /*EaBuffer=*/nullptr, /*EaLength=*/0); + if (sts != STATUS_SUCCESS) { + return false; + } + + IO_STATUS_BLOCK io2; + sts = fnNtFsControlFile(h, /*Event=*/nullptr, /*ApcRoutine=*/nullptr, + /*ApcContext*/nullptr, &io2, FSCTL_PIPE_WAIT, buffer.data(), + buffer.size(), nullptr, 0); + CloseHandle(h); + return sts; +} + +// Reads the next message from the pipe and returns a buffer of chars. +// This function is synchronous. +std::vector ReadNextMessageFromPipe( + HANDLE pipe, + OVERLAPPED* overlapped) { + DWORD err = ERROR_SUCCESS; + std::vector buffer(kBufferSize); + char* p = buffer.data(); + int final_size = 0; + while (true) { + DWORD read; + + // Even though the pipe is opened for overlapped IO, the read operation + // could still completely synchronously. For example, a server's response + // message could already be available in the pipe's internal buffer. + // If ReadFile() does complete synchronously, TRUE is returned. In this + // case update the final size and exit the loop. + if (ReadFile(pipe, p, kBufferSize, &read, overlapped)) { + final_size += read; + break; + } else { + // Reaching here means that ReadFile() will either complete async or + // an error has occurred. The former case is detected if the error code + // is "IO pending", in which case GetOverlappedResult() is called to wait + // for the IO to complete. If that function returns TRUE then the read + // operation completed successfully and the code simply updates the final + // size and exits the loop. + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + if (GetOverlappedResult(pipe, overlapped, &read, /*wait=*/TRUE)) { + final_size += read; + break; + } else { + err = GetLastError(); + } + } + + // Reaching here means an error has occurred. One error is recoverable: + // "more data". For any other type of error break out of the loop. + if (err != ERROR_MORE_DATA) { + final_size = 0; + break; + } + + // Reaching here means the error is "more data", that is, the buffer + // specified in ReadFile() was too small to contain the entire response + // message from the server. ReadFile() has placed the start of the + // message in the specified buffer but ReadFile() needs to be called + // again to read the remaining part. + // + // The buffer size is increased and the current pointer into the buffer + // `p` is adjusted so that when the loop re-runs, it calls ReadFile() + // with the correct point in the buffer. It's possible that this loop + // might have to run many times if the response message is rather large. + buffer.resize(buffer.size() + kBufferSize); + p = buffer.data() + buffer.size() - kBufferSize; + } + } + + buffer.resize(final_size); + return buffer; +} + +// Writes a string to the pipe. Returns true if successful, false otherwise. +// This function is synchronous. +bool WriteMessageToPipe( + HANDLE pipe, + const std::string& message, + OVERLAPPED* overlapped) { + if (message.empty()) + return false; + + // Even though the pipe is opened for overlapped IO, the write operation + // could still completely synchronously. If it does, TRUE is returned. + // In this case the function is done. + bool ok = WriteFile(pipe, message.data(), message.size(), nullptr, overlapped); + if (!ok) { + // Reaching here means that WriteFile() will either complete async or + // an error has occurred. The former case is detected if the error code + // is "IO pending", in which case GetOverlappedResult() is called to wait + // for the IO to complete. Whether the operation completes sync or async, + // return true if the operation succeeded and false otherwise. + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + DWORD written; + ok = GetOverlappedResult(pipe, overlapped, &written, /*wait=*/TRUE); + } + } + + return ok; +} + +} // namespace + +// static +std::unique_ptr Client::Create(Config config) { + int rc; + auto client = std::make_unique(std::move(config), &rc); + return rc == 0 ? std::move(client) : nullptr; +} + +ClientWin::ClientWin(Config config, int* rc) : ClientBase(std::move(config)) { + *rc = -1; + + std::string pipename = + internal::GetPipeNameForClient(configuration().name, + configuration().user_specific); + if (!pipename.empty()) { + unsigned long pid = 0; + if (ConnectToPipe(pipename, &hPipe_) == ERROR_SUCCESS && + GetNamedPipeServerProcessId(hPipe_, &pid)) { + agent_info().pid = pid; + + // Getting the process path is best effort. + *rc = 0; + std::string binary_path; + if (internal::GetProcessPath(pid, &binary_path)) { + agent_info().binary_path = std::move(binary_path); + } + } + } + + if (*rc != 0) { + Shutdown(); + } +} + +ClientWin::~ClientWin() { + Shutdown(); +} + +int ClientWin::Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) { + ChromeToAgent chrome_to_agent; + *chrome_to_agent.mutable_request() = std::move(request); + + internal::ScopedOverlapped overlapped; + if (!overlapped.is_valid()) { + return -1; + } + + bool success = WriteMessageToPipe(hPipe_, + chrome_to_agent.SerializeAsString(), + overlapped); + if (success) { + std::vector buffer = ReadNextMessageFromPipe(hPipe_, overlapped); + AgentToChrome agent_to_chrome; + success = buffer.size() > 0 && + agent_to_chrome.ParseFromArray(buffer.data(), buffer.size()); + if (success) { + *response = std::move(*agent_to_chrome.mutable_response()); + } + } + + return success ? 0 : -1; +} + +int ClientWin::Acknowledge(const ContentAnalysisAcknowledgement& ack) { + // TODO: could avoid a copy by changing argument to be + // `ContentAnalysisAcknowledgement ack` and then using std::move() below and + // at call site. + ChromeToAgent chrome_to_agent; + *chrome_to_agent.mutable_ack() = ack; + + internal::ScopedOverlapped overlapped; + if (!overlapped.is_valid()) { + return -1; + } + + return WriteMessageToPipe(hPipe_, chrome_to_agent.SerializeAsString(), overlapped) + ? 0 : -1; +} + +int ClientWin::CancelRequests(const ContentAnalysisCancelRequests& cancel) { + // TODO: could avoid a copy by changing argument to be + // `ContentAnalysisCancelRequests cancel` and then using std::move() below and + // at call site. + ChromeToAgent chrome_to_agent; + *chrome_to_agent.mutable_cancel() = cancel; + + internal::ScopedOverlapped overlapped; + if (!overlapped.is_valid()) { + return -1; + } + + return WriteMessageToPipe(hPipe_, chrome_to_agent.SerializeAsString(), overlapped) + ? 0 : -1; +} + +// static +DWORD ClientWin::ConnectToPipe(const std::string& pipename, HANDLE* handle) { + // Get pointers to the Ntxxx functions. This is required to use absolute + // pipe names from the Windows NT Object Manager's namespace. This protects + // against the "\\.\pipe" symlink being redirected. + + NtCreateFileFn fnNtCreateFile = GetNtCreateFileFn(); + if (fnNtCreateFile == nullptr) { + return ERROR_INVALID_FUNCTION; + } + + // Convert the path to a wchar_t string. Pass pipename.size() as the + // `cbMultiByte` argument instead of -1 since the terminating null should not + // be counted. NtCreateFile() does not expect the object name to be + // terminated. Note that `buffer` and hence `name` created from it are both + // unterminated strings. + int wlen = MultiByteToWideChar(CP_ACP, 0, pipename.c_str(), pipename.size(), + nullptr, 0); + if (wlen == 0) { + return GetLastError(); + } + std::vector buffer(wlen); + MultiByteToWideChar(CP_ACP, 0, pipename.c_str(), pipename.size(), + buffer.data(), wlen); + + UNICODE_STRING name; + name.Buffer = buffer.data(); + name.Length = wlen * sizeof(wchar_t); // Length in bytes, not characters. + name.MaximumLength = name.Length; + + OBJECT_ATTRIBUTES attr; + InitializeObjectAttributes(&attr, &name, OBJ_CASE_INSENSITIVE, nullptr, + nullptr); + + // Open the named pipe for overlapped IO, i.e. do not specify either of the + // FILE_SYNCHRONOUS_IO_xxxALERT in the creation option flags. If the pipe + // is not opened for overlapped IO, then the Send() method will block if + // called from different threads since only one read or write operation would + // be allowed at a time. + IO_STATUS_BLOCK io; + HANDLE h = INVALID_HANDLE_VALUE; + NTSTATUS sts = STATUS_IO_TIMEOUT; + while (sts == STATUS_IO_TIMEOUT) { + sts = fnNtCreateFile(&h, GENERIC_READ | GENERIC_WRITE | + SYNCHRONIZE, &attr, &io, /*AllocationSize=*/nullptr, + FILE_ATTRIBUTE_NORMAL, /*ShareAccess=*/0, FILE_OPEN, + FILE_NON_DIRECTORY_FILE, + /*EaBuffer=*/nullptr, /*EaLength=*/0); + if (sts != STATUS_SUCCESS) { + if (sts != STATUS_PIPE_NOT_AVAILABLE) { + break; + } + + sts = WaitForPipeAvailability(name); + if (sts != STATUS_SUCCESS && sts != STATUS_IO_TIMEOUT) { + break; + } + } + } + + if (sts != STATUS_SUCCESS) { + return ERROR_PIPE_NOT_CONNECTED; + } + + // Change to message read mode to match server side. Max connection count + // and timeout must be null if client and server are on the same machine. + DWORD mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(h, &mode, + /*maxCollectionCount=*/nullptr, + /*connectionTimeout=*/nullptr)) { + DWORD err = GetLastError(); + CloseHandle(h); + return err; + } + + *handle = h; + return ERROR_SUCCESS; +} + +void ClientWin::Shutdown() { + if (hPipe_ != INVALID_HANDLE_VALUE) { + // TODO: This trips the LateWriteObserver. We could move this earlier + // (before the LateWriteObserver is created) or just remove it, although + // the later could mean an ACK message is not processed by the agent + // in time. + // FlushFileBuffers(hPipe_); + CloseHandle(hPipe_); + hPipe_ = INVALID_HANDLE_VALUE; + } +} + +} // namespace sdk +} // namespace content_analysis \ No newline at end of file diff --git a/third_party/content_analysis_sdk/browser/src/client_win.h b/third_party/content_analysis_sdk/browser/src/client_win.h new file mode 100644 index 0000000000..f4bdd834fc --- /dev/null +++ b/third_party/content_analysis_sdk/browser/src/client_win.h @@ -0,0 +1,39 @@ +// Copyright 2022 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_WIN_H_ +#define CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_WIN_H_ + +#include + +#include "client_base.h" + +namespace content_analysis { +namespace sdk { + +// Client implementaton for Windows. +class ClientWin : public ClientBase { + public: + ClientWin(Config config, int* rc); + ~ClientWin() override; + + // Client: + int Send(ContentAnalysisRequest request, + ContentAnalysisResponse* response) override; + int Acknowledge(const ContentAnalysisAcknowledgement& ack) override; + int CancelRequests(const ContentAnalysisCancelRequests& cancel) override; + + private: + static DWORD ConnectToPipe(const std::string& pipename, HANDLE* handle); + + // Performs a clean shutdown of the client. + void Shutdown(); + + HANDLE hPipe_ = INVALID_HANDLE_VALUE; +}; + +} // namespace sdk +} // namespace content_analysis + +#endif // CONTENT_ANALYSIS_BROWSER_SRC_CLIENT_WIN_H_ -- cgit v1.2.3