agent_win.h (7790B)
1 // Copyright 2022 The Chromium Authors. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CONTENT_ANALYSIS_AGENT_SRC_AGENT_WIN_H_ 6 #define CONTENT_ANALYSIS_AGENT_SRC_AGENT_WIN_H_ 7 8 #include <windows.h> 9 10 #include <memory> 11 #include <sstream> 12 #include <string> 13 #include <vector> 14 15 #include "agent_base.h" 16 17 namespace content_analysis { 18 namespace sdk { 19 20 // Agent implementaton for Windows. 21 class AgentWin : public AgentBase { 22 public: 23 // Creates a new agent given the specific configuration and handler. 24 // If an error occurs during creation, `rc` is set to the specific 25 // error. Otherwise `rc` is ResultCode::OK. 26 AgentWin(Config config, std::unique_ptr<AgentEventHandler> handler, 27 ResultCode* rc); 28 ~AgentWin() override; 29 30 // Agent: 31 ResultCode HandleEvents() override; 32 ResultCode Stop() override; 33 std::string DebugString() const override; 34 35 // Handles one pipe event and returns. Used only in tests. 36 ResultCode HandleOneEventForTesting(); 37 38 // Returns true if there is at least one client connected to this agent. 39 bool IsAClientConnectedForTesting(); 40 41 private: 42 // Represents one connection to a Google Chrome browser, or one pipe 43 // listening for a Google Chrome browser to connect. 44 class Connection { 45 public: 46 // Starts listening on a pipe with the given name. `is_first_pipe` should 47 // be true only for the first pipe created by the agent. If an error 48 // occurs while creating the connction object it is returned in `rc`. 49 // If no errors occur then rc is set to ResultCode::OK. 50 // 51 // When `user_specific` is true there is a different agent instance per OS 52 // user. 53 // 54 // `Connection` objects cannot be copied or moved because the OVERLAPPED 55 // structure cannot be changed or moved in memory while an I/O operation 56 // is in progress. 57 Connection(const std::string& pipename, 58 bool user_specific, 59 AgentEventHandler* handler, 60 bool is_first_pipe, 61 ResultCode* rc); 62 Connection(const Connection& other) = delete; 63 Connection(Connection&& other) = delete; 64 Connection& operator=(const Connection& other) = delete; 65 Connection& operator=(Connection&& other) = delete; 66 ~Connection(); 67 68 bool IsValid() const { return handle_ != INVALID_HANDLE_VALUE; } 69 bool IsConnected() const { return is_connected_; } 70 HANDLE GetWaitHandle() const { return overlapped_.hEvent; } 71 72 // Resets this connection object to listen for a new Google Chrome browser. 73 // When `user_specific` is true there is a different agent instance per OS 74 // user. 75 ResultCode Reset(const std::string& pipename, bool user_specific); 76 77 // Hnadles an event for this connection. `wait_handle` corresponds to 78 // this connections wait handle. 79 ResultCode HandleEvent(HANDLE wait_handle); 80 81 // Append debuf information to the string stream. 82 void AppendDebugString(std::stringstream& state) const; 83 84 private: 85 // Listens for a new connection from Google Chrome. 86 ResultCode ConnectPipe(); 87 88 // Resets this connection object to listen for a new Google Chrome browser. 89 // When `user_specific` is true there is a different agent instance per OS 90 // user. 91 ResultCode ResetInternal(const std::string& pipename, 92 bool user_specific, 93 bool is_first_pipe); 94 95 // Cleans up this connection object so that it can be reused with a new 96 // Google Chroem browser instance. The handles assocated with this object 97 // are not closed. On return, this object is neither connected nor 98 // listening and any buffer used to hold browser messages are cleared. 99 void Cleanup(); 100 101 // Queues a read on the pipe to receive a message from Google Chrome. 102 // ERROR_SUCCESS, ERROR_IO_PENDING, and ERROR_MORE_DATA are successful 103 // return values. Other values represent an error with the connection. 104 // If `reset_cursor` is true the internal read buffer cursor is reset to 105 // the start of the buffer, otherwise it is unchanged. 106 ResultCode QueueReadFile(bool reset_cursor); 107 108 // Called when data from Google Chrome is available for reading from the 109 // pipe. ERROR_SUCCESS and ERROR_MORE_DATA are both successful return 110 // values. Other values represent an error with the connection. 111 // 112 // `done_reading` is true if the code has finished reading an entire message 113 // from chrome. Regardless of whether reading is done, `count` contains 114 // the number of bytes read. 115 // 116 // If `done_reading` is true, the data received from the browser is parsed 117 // as if it were a `ChromeToAgent` proto message and the handler is called 118 // as needed. 119 // 120 // If `done_reading` is false, the data received from the browser is 121 // appended to the data already received from the browser. `buffer_` is 122 // resized to allow reading more data from the browser. 123 // 124 // In all cases the caller is expected to use QueueReadFile() to continue 125 // reading data from the browser. 126 ResultCode OnReadFile(BOOL done_reading, DWORD count); 127 128 // Calls the appropriate method the handler depending on the message 129 // received from Google Chrome. 130 ResultCode CallHandler(); 131 132 // Fills in the browser_info_ member of this Connection. Assumes 133 // IsConnected() is true. 134 ResultCode BuildBrowserInfo(); 135 136 // Notifies the handler of the given error iff `rc` is not equal to 137 // ResultCode::OK. Appends the Google Chrome browser process id to the 138 // context before calling the handler. Also append `err` to the context 139 // if it is not ERROR_SUCCESS. 140 // 141 // Returns the error passed into the method. 142 ResultCode NotifyIfError(const char* context, 143 ResultCode rc, 144 DWORD err=ERROR_SUCCESS); 145 146 // The handler to call for various agent events. 147 AgentEventHandler* handler_ = nullptr; 148 149 // Members used to communicate with Google Chrome. 150 HANDLE handle_ = INVALID_HANDLE_VALUE; 151 OVERLAPPED overlapped_; 152 153 // True if this connection is assigned to a specific Google Chrome browser, 154 // otherwise this connection is listening for a new browser. 155 bool is_connected_ = false; 156 157 // Information about the Google Chrome browser process. 158 BrowserInfo browser_info_; 159 160 // Members used to read messages from Google Chrome. 161 std::vector<char> buffer_; 162 char* cursor_ = nullptr; 163 DWORD read_size_ = 0; 164 DWORD final_size_ = 0; 165 }; 166 167 // Returns handles that can be used to wait for events from all handles 168 // managed by this agent. This includes all connection objects and the 169 // stop event. The stop event is always last in the list. 170 void GetHandles(std::vector<HANDLE>& wait_handles) const; 171 172 // Handles one pipe event and returns. If the return value is 173 // ResultCode::OK, the `stopped` argument is set to true if the agent 174 // should stop handling more events. If the return value is not 175 // ResultCode::OK, `stopped` is undefined. 176 ResultCode HandleOneEvent(std::vector<HANDLE>& wait_handles, bool* stopped); 177 178 // Performs a clean shutdown of the agent. 179 void Shutdown(); 180 181 // Name used to create the pipes between the agent and Google Chrome browsers. 182 std::string pipename_; 183 184 // A list of pipes to already connected Google Chrome browsers. 185 // The first kMinNumListeningPipeInstances pipes in the list correspond to 186 // listening pipes. 187 std::vector<std::unique_ptr<Connection>> connections_; 188 189 // An event that is set when the agent should stop. Set in Stop(). 190 HANDLE stop_event_ = nullptr; 191 }; 192 193 } // namespace sdk 194 } // namespace content_analysis 195 196 #endif // CONTENT_ANALYSIS_AGENT_SRC_AGENT_WIN_H_