// The minimum number of pipe in listening mode. This is greater than one to // handle the case of multiple instance of Google Chrome browser starting // at the same time. const DWORD kMinNumListeningPipeInstances = 2;
// The minimum number of handles to wait on. This is the minimum number // of pipes in listening mode plus the stop event. const DWORD kMinNumWaitHandles = kMinNumListeningPipeInstances + 1;
AgentWin::Connection::Connection(const std::string& pipename, bool user_specific,
AgentEventHandler* handler, bool is_first_pipe,
ResultCode* rc)
: handler_(handler) {
*rc = ResultCode::OK;
memset(&overlapped_, 0, sizeof(overlapped_)); // Create a manual reset event as specified for overlapped IO. // Use default security attriutes and no name since this event is not // shared with other processes.
overlapped_.hEvent = CreateEvent(/*securityAttr=*/nullptr, /*manualReset=*/TRUE, /*initialState=*/FALSE, /*name=*/nullptr); if (!overlapped_.hEvent) {
*rc = ResultCode::ERR_CANNOT_CREATE_CHANNEL_IO_EVENT; return;
}
ResultCode AgentWin::Connection::HandleEvent(HANDLE handle) { auto rc = ResultCode::OK;
DWORD count; BOOL success = GetOverlappedResult(handle, &overlapped_, &count, /*wait=*/FALSE); if (!is_connected_) { // This connection is currently listing for a new connection from a Google // Chrome browser. If the result is a success, this means the browser has // connected as expected. Otherwise an error occured so report it to the // caller. if (success) { // A Google Chrome browser connected to the agent. Reset this // connection object to handle communication with the browser and then // tell the handler about it.
rc = BuildBrowserInfo(); if (rc == ResultCode::OK) {
handler_->OnBrowserConnected(browser_info_);
}
} else {
rc = ErrorToResultCode(GetLastError());
NotifyIfError("GetOverlappedResult", rc);
}
} else { // Some data has arrived from Google Chrome. This data is (part of) an // instance of the proto message `ChromeToAgent`. // // If the message is small it is received in by one call to ReadFile(). // If the message is larger it is received in by multiple calls to // ReadFile(). // // `success` is true if the data just read is the last bytes for a message. // Otherwise it is false.
rc = OnReadFile(success, count);
}
// If all data has been read, queue another read. if (rc == ResultCode::OK || rc == ResultCode::ERR_MORE_DATA) {
rc = QueueReadFile(rc == ResultCode::OK);
}
if (rc != ResultCode::OK && rc != ResultCode::ERR_IO_PENDING &&
rc != ResultCode::ERR_MORE_DATA) {
Cleanup();
} else { // Don't propagate all the "success" error codes to the called to keep // this simpler.
rc = ResultCode::OK;
}
return rc;
}
void AgentWin::Connection::AppendDebugString(std::stringstream& state) const {
state << "{handle=" << handle_;
state << " connected=" << is_connected_;
state << " pid=" << browser_info_.pid;
state << " rsize=" << read_size_;
state << " fsize=" << final_size_;
state << "}";
}
ResultCode AgentWin::Connection::ConnectPipe() { // In overlapped mode, connecting to a named pipe always returns false. if (ConnectNamedPipe(handle_, &overlapped_)) { return ErrorToResultCode(GetLastError());
}
DWORD err = GetLastError(); if (err == ERROR_IO_PENDING) { // Waiting for a Google Chrome Browser to connect. return ResultCode::OK;
} elseif (err == ERROR_PIPE_CONNECTED) { // A Google Chrome browser is already connected. Make sure event is in // signaled state in order to process the connection. if (SetEvent(overlapped_.hEvent)) {
err = ERROR_SUCCESS;
} else {
err = GetLastError();
}
}
// If this is the not the first time, disconnect from any existing Google // Chrome browser. Otherwise creater a new pipe. if (handle_ != INVALID_HANDLE_VALUE) { if (!DisconnectNamedPipe(handle_)) {
rc = ErrorToResultCode(GetLastError());
}
} else {
rc = ErrorToResultCode(
internal::CreatePipe(pipename, user_specific, is_first_pipe, &handle_));
}
// Make sure event starts in reset state. if (rc == ResultCode::OK && !ResetEvent(overlapped_.hEvent)) {
rc = ErrorToResultCode(GetLastError());
}
if (rc == ResultCode::OK) {
rc = ConnectPipe();
}
if (rc != ResultCode::OK) {
Cleanup();
handle_ = INVALID_HANDLE_VALUE;
}
return rc;
}
void AgentWin::Connection::Cleanup() { if (is_connected_ && handler_) {
handler_->OnBrowserDisconnected(browser_info_);
}
if (handle_ != INVALID_HANDLE_VALUE) { // Cancel all outstanding IO requests on this pipe by using a null for // overlapped.
CancelIoEx(handle_, /*overlapped=*/nullptr);
}
// This function does not close `handle_` or the event in `overlapped` so // that the server can resuse the pipe with an new Google Chrome browser // instance.
}
// When this function is called there are the following possiblities: // // 1/ Data is already available and the buffer is filled in. ReadFile() // return TRUE and the event is set. // 2/ Data is not avaiable yet. ReadFile() returns FALSE and the last error // is ERROR_IO_PENDING(997) and the event is reset. // 3/ Some error occurred, like for example Google Chrome stops. ReadFile() // returns FALSE and the last error is something other than // ERROR_IO_PENDING, for example ERROR_BROKEN_PIPE(109). The event // state is unchanged. auto rc = ResultCode::OK;
DWORD count; if (!ReadFile(handle_, cursor_, read_size_, &count, &overlapped_)) {
DWORD err = GetLastError();
rc = ErrorToResultCode(err);
// IO pending is not an error so don't notify. // // Ignore broken pipes for notifications since that happens when the Google // Chrome browser shuts down. The agent will be notified of a browser // disconnect in that case. // // More data means that `buffer_` was too small to read the entire message // from the browser. The buffer has already been resized. Another call to // ReadFile() is needed to get the remainder. if (rc != ResultCode::ERR_IO_PENDING &&
rc != ResultCode::ERR_BROKEN_PIPE &&
rc != ResultCode::ERR_MORE_DATA) {
NotifyIfError("QueueReadFile", rc, err);
}
}
// If `done_reading` is TRUE, this means the full message has been read. // Call the appropriate handler method. if (done_reading) { return CallHandler();
}
// Otherwise there are two possibilities: // // 1/ The last error is ERROR_MORE_DATA(234). This means there are more // bytes to read before the request message is complete. Resize the // buffer and adjust the cursor. The caller will queue up another read // and wait. don't notify the handler since this is not an error. // 2/ Some error occured. In this case notify the handler and return the // error.
if (message.has_request()) { // This is a request from Google Chrome to perform a content analysis // request. // // Move the request from `message` to the event to reduce the amount // of memory allocation/copying and also because the the handler takes // ownership of the event. auto event = std::make_unique<ContentAnalysisEventWin>(
handle_, browser_info_, std::move(*message.mutable_request()));
rc = event->Init(); if (rc == ResultCode::OK) {
handler_->OnAnalysisRequested(std::move(event));
} else {
NotifyIfError("RequestValidation", rc);
}
} elseif (message.has_ack()) { // This is an ack from Google Chrome that it has received a content // analysis response from the agent.
handler_->OnResponseAcknowledged(message.ack());
} elseif (message.has_cancel()) { // Google Chrome is informing the agent that the content analysis // request(s) associated with the given user action id have been // canceled by the user.
handler_->OnCancelRequests(message.cancel());
} else { // Malformed message.
rc = NotifyIfError("NoRequestOrAck",
ResultCode::ERR_INVALID_REQUEST_FROM_BROWSER);
}
std::string AgentWin::DebugString() const {
std::stringstream state;
state.setf(std::ios::boolalpha);
state << "AgentWin{pipe=\"" << pipename_;
state << "\" stop=" << stop_event_;
for (size_t i = 0; i < connections_.size(); ++i) {
state << " conn@" << i;
connections_[i]->AppendDebugString(state);
}
state << "}" << std::ends; return state.str();
}
void AgentWin::GetHandles(std::vector<HANDLE>& wait_handles) const { // Reserve enough space in the handles vector to include the stop event plus // all connections.
wait_handles.clear();
wait_handles.reserve(1 + connections_.size());
for (auto& state : connections_) {
HANDLE wait_handle = state->GetWaitHandle(); if (!wait_handle) {
wait_handles.clear(); break;
}
wait_handles.push_back(wait_handle);
}
// Push the stop event last so that connections_ index calculations in // HandleOneEvent() don't have to account for this handle.
wait_handles.push_back(stop_event_);
}
// Wait on the specified handles for an event to occur.
GetHandles(wait_handles); if (wait_handles.size() < kMinNumWaitHandles) { return NotifyError("GetHandles", ResultCode::ERR_AGENT_NOT_INITIALIZED);
}
DWORD index = WaitForMultipleObjects(
wait_handles.size(), wait_handles.data(), /*waitAll=*/FALSE, /*timeoutMs=*/INFINITE); if (index == WAIT_FAILED) { return NotifyError("WaitForMultipleObjects",
ErrorToResultCode(GetLastError()));
}
// If the index of signaled handle is the last one in wait_handles, then the // stop event was signaled.
index -= WAIT_OBJECT_0; if (index == wait_handles.size() - 1) {
*stopped = true; return ResultCode::OK;
}
auto& connection = connections_[index]; bool was_listening = !connection->IsConnected(); auto rc = connection->HandleEvent(wait_handles[index]); if (rc != ResultCode::OK) { // If `connection` was not listening and there are more than // kMinNumListeningPipeInstances pipes, delete this connection. Otherwise // reset it so that it becomes a listener. if (!was_listening &&
connections_.size() > kMinNumListeningPipeInstances) {
connections_.erase(connections_.begin() + index);
} else {
rc = connection->Reset(pipename_, configuration().user_specific);
}
}
// If `connection` was listening and is now connected, create a new // one so that there are always kMinNumListeningPipeInstances listening. if (rc == ResultCode::OK && was_listening && connection->IsConnected()) {
connections_.emplace_back(
std::make_unique<Connection>(pipename_, configuration().user_specific,
handler(), false, &rc));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.