ipc/chromium/src/chrome/common/ipc_channel_win.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include "chrome/common/ipc_channel_win.h"
michael@0 6
michael@0 7 #include <windows.h>
michael@0 8 #include <sstream>
michael@0 9
michael@0 10 #include "base/compiler_specific.h"
michael@0 11 #include "base/logging.h"
michael@0 12 #include "base/non_thread_safe.h"
michael@0 13 #include "base/stats_counters.h"
michael@0 14 #include "base/win_util.h"
michael@0 15 #include "chrome/common/ipc_logging.h"
michael@0 16 #include "chrome/common/ipc_message_utils.h"
michael@0 17 #include "mozilla/ipc/ProtocolUtils.h"
michael@0 18
michael@0 19 namespace IPC {
michael@0 20 //------------------------------------------------------------------------------
michael@0 21
michael@0 22 Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
michael@0 23 memset(&context.overlapped, 0, sizeof(context.overlapped));
michael@0 24 context.handler = channel;
michael@0 25 }
michael@0 26
michael@0 27 Channel::ChannelImpl::State::~State() {
michael@0 28 COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
michael@0 29 starts_with_io_context);
michael@0 30 }
michael@0 31
michael@0 32 //------------------------------------------------------------------------------
michael@0 33
michael@0 34 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
michael@0 35 Listener* listener)
michael@0 36 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
michael@0 37 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
michael@0 38 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
michael@0 39 Init(mode, listener);
michael@0 40
michael@0 41 if (!CreatePipe(channel_id, mode)) {
michael@0 42 // The pipe may have been closed already.
michael@0 43 CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
michael@0 44 "\" in " << (mode == 0 ? "server" : "client") << " mode.";
michael@0 45 }
michael@0 46 }
michael@0 47
michael@0 48 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id,
michael@0 49 HANDLE server_pipe,
michael@0 50 Mode mode, Listener* listener)
michael@0 51 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
michael@0 52 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
michael@0 53 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
michael@0 54 Init(mode, listener);
michael@0 55
michael@0 56 if (mode == MODE_SERVER) {
michael@0 57 // Use the existing handle that was dup'd to us
michael@0 58 pipe_ = server_pipe;
michael@0 59 EnqueueHelloMessage();
michael@0 60 } else {
michael@0 61 // Take the normal init path to connect to the server pipe
michael@0 62 CreatePipe(channel_id, mode);
michael@0 63 }
michael@0 64 }
michael@0 65
michael@0 66 void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
michael@0 67 pipe_ = INVALID_HANDLE_VALUE;
michael@0 68 listener_ = listener;
michael@0 69 waiting_connect_ = (mode == MODE_SERVER);
michael@0 70 processing_incoming_ = false;
michael@0 71 closed_ = false;
michael@0 72 output_queue_length_ = 0;
michael@0 73 }
michael@0 74
michael@0 75 void Channel::ChannelImpl::OutputQueuePush(Message* msg)
michael@0 76 {
michael@0 77 output_queue_.push(msg);
michael@0 78 output_queue_length_++;
michael@0 79 }
michael@0 80
michael@0 81 void Channel::ChannelImpl::OutputQueuePop()
michael@0 82 {
michael@0 83 output_queue_.pop();
michael@0 84 output_queue_length_--;
michael@0 85 }
michael@0 86
michael@0 87 HANDLE Channel::ChannelImpl::GetServerPipeHandle() const {
michael@0 88 return pipe_;
michael@0 89 }
michael@0 90
michael@0 91 void Channel::ChannelImpl::Close() {
michael@0 92 if (thread_check_.get()) {
michael@0 93 DCHECK(thread_check_->CalledOnValidThread());
michael@0 94 }
michael@0 95
michael@0 96 bool waited = false;
michael@0 97 if (input_state_.is_pending || output_state_.is_pending) {
michael@0 98 CancelIo(pipe_);
michael@0 99 waited = true;
michael@0 100 }
michael@0 101
michael@0 102 // Closing the handle at this point prevents us from issuing more requests
michael@0 103 // form OnIOCompleted().
michael@0 104 if (pipe_ != INVALID_HANDLE_VALUE) {
michael@0 105 CloseHandle(pipe_);
michael@0 106 pipe_ = INVALID_HANDLE_VALUE;
michael@0 107 }
michael@0 108
michael@0 109 while (input_state_.is_pending || output_state_.is_pending) {
michael@0 110 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
michael@0 111 }
michael@0 112
michael@0 113 while (!output_queue_.empty()) {
michael@0 114 Message* m = output_queue_.front();
michael@0 115 OutputQueuePop();
michael@0 116 delete m;
michael@0 117 }
michael@0 118
michael@0 119 if (thread_check_.get())
michael@0 120 thread_check_.reset();
michael@0 121
michael@0 122 closed_ = true;
michael@0 123 }
michael@0 124
michael@0 125 bool Channel::ChannelImpl::Send(Message* message) {
michael@0 126 if (thread_check_.get()) {
michael@0 127 DCHECK(thread_check_->CalledOnValidThread());
michael@0 128 }
michael@0 129 #ifdef IPC_MESSAGE_DEBUG_EXTRA
michael@0 130 DLOG(INFO) << "sending message @" << message << " on channel @" << this
michael@0 131 << " with type " << message->type()
michael@0 132 << " (" << output_queue_.size() << " in queue)";
michael@0 133 #endif
michael@0 134
michael@0 135 #ifdef IPC_MESSAGE_LOG_ENABLED
michael@0 136 Logging::current()->OnSendMessage(message, L"");
michael@0 137 #endif
michael@0 138
michael@0 139 if (closed_) {
michael@0 140 if (mozilla::ipc::LoggingEnabled()) {
michael@0 141 fprintf(stderr, "Can't send message %s, because this channel is closed.\n",
michael@0 142 message->name());
michael@0 143 }
michael@0 144 delete message;
michael@0 145 return false;
michael@0 146 }
michael@0 147
michael@0 148 OutputQueuePush(message);
michael@0 149 // ensure waiting to write
michael@0 150 if (!waiting_connect_) {
michael@0 151 if (!output_state_.is_pending) {
michael@0 152 if (!ProcessOutgoingMessages(NULL, 0))
michael@0 153 return false;
michael@0 154 }
michael@0 155 }
michael@0 156
michael@0 157 return true;
michael@0 158 }
michael@0 159
michael@0 160 const std::wstring Channel::ChannelImpl::PipeName(
michael@0 161 const std::wstring& channel_id) const {
michael@0 162 std::wostringstream ss;
michael@0 163 // XXX(darin): get application name from somewhere else
michael@0 164 ss << L"\\\\.\\pipe\\chrome." << channel_id;
michael@0 165 return ss.str();
michael@0 166 }
michael@0 167
michael@0 168 bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
michael@0 169 Mode mode) {
michael@0 170 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
michael@0 171 const std::wstring pipe_name = PipeName(channel_id);
michael@0 172 if (mode == MODE_SERVER) {
michael@0 173 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
michael@0 174 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
michael@0 175 FILE_FLAG_FIRST_PIPE_INSTANCE,
michael@0 176 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
michael@0 177 1, // number of pipe instances
michael@0 178 // output buffer size (XXX tune)
michael@0 179 Channel::kReadBufferSize,
michael@0 180 // input buffer size (XXX tune)
michael@0 181 Channel::kReadBufferSize,
michael@0 182 5000, // timeout in milliseconds (XXX tune)
michael@0 183 NULL);
michael@0 184 } else {
michael@0 185 pipe_ = CreateFileW(pipe_name.c_str(),
michael@0 186 GENERIC_READ | GENERIC_WRITE,
michael@0 187 0,
michael@0 188 NULL,
michael@0 189 OPEN_EXISTING,
michael@0 190 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
michael@0 191 FILE_FLAG_OVERLAPPED,
michael@0 192 NULL);
michael@0 193 }
michael@0 194 if (pipe_ == INVALID_HANDLE_VALUE) {
michael@0 195 // If this process is being closed, the pipe may be gone already.
michael@0 196 CHROMIUM_LOG(WARNING) << "failed to create pipe: " << GetLastError();
michael@0 197 return false;
michael@0 198 }
michael@0 199
michael@0 200 // Create the Hello message to be sent when Connect is called
michael@0 201 return EnqueueHelloMessage();
michael@0 202 }
michael@0 203
michael@0 204 bool Channel::ChannelImpl::EnqueueHelloMessage() {
michael@0 205 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
michael@0 206 HELLO_MESSAGE_TYPE,
michael@0 207 IPC::Message::PRIORITY_NORMAL));
michael@0 208 if (!m->WriteInt(GetCurrentProcessId())) {
michael@0 209 CloseHandle(pipe_);
michael@0 210 pipe_ = INVALID_HANDLE_VALUE;
michael@0 211 return false;
michael@0 212 }
michael@0 213
michael@0 214 OutputQueuePush(m.release());
michael@0 215 return true;
michael@0 216 }
michael@0 217
michael@0 218 bool Channel::ChannelImpl::Connect() {
michael@0 219 if (!thread_check_.get())
michael@0 220 thread_check_.reset(new NonThreadSafe());
michael@0 221
michael@0 222 if (pipe_ == INVALID_HANDLE_VALUE)
michael@0 223 return false;
michael@0 224
michael@0 225 MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
michael@0 226
michael@0 227 // Check to see if there is a client connected to our pipe...
michael@0 228 if (waiting_connect_)
michael@0 229 ProcessConnection();
michael@0 230
michael@0 231 if (!input_state_.is_pending) {
michael@0 232 // Complete setup asynchronously. By not setting input_state_.is_pending
michael@0 233 // to true, we indicate to OnIOCompleted that this is the special
michael@0 234 // initialization signal.
michael@0 235 MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod(
michael@0 236 &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
michael@0 237 }
michael@0 238
michael@0 239 if (!waiting_connect_)
michael@0 240 ProcessOutgoingMessages(NULL, 0);
michael@0 241 return true;
michael@0 242 }
michael@0 243
michael@0 244 bool Channel::ChannelImpl::ProcessConnection() {
michael@0 245 DCHECK(thread_check_->CalledOnValidThread());
michael@0 246 if (input_state_.is_pending)
michael@0 247 input_state_.is_pending = false;
michael@0 248
michael@0 249 // Do we have a client connected to our pipe?
michael@0 250 if (INVALID_HANDLE_VALUE == pipe_)
michael@0 251 return false;
michael@0 252
michael@0 253 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
michael@0 254
michael@0 255 DWORD err = GetLastError();
michael@0 256 if (ok) {
michael@0 257 // Uhm, the API documentation says that this function should never
michael@0 258 // return success when used in overlapped mode.
michael@0 259 NOTREACHED();
michael@0 260 return false;
michael@0 261 }
michael@0 262
michael@0 263 switch (err) {
michael@0 264 case ERROR_IO_PENDING:
michael@0 265 input_state_.is_pending = true;
michael@0 266 break;
michael@0 267 case ERROR_PIPE_CONNECTED:
michael@0 268 waiting_connect_ = false;
michael@0 269 break;
michael@0 270 default:
michael@0 271 NOTREACHED();
michael@0 272 return false;
michael@0 273 }
michael@0 274
michael@0 275 return true;
michael@0 276 }
michael@0 277
michael@0 278 bool Channel::ChannelImpl::ProcessIncomingMessages(
michael@0 279 MessageLoopForIO::IOContext* context,
michael@0 280 DWORD bytes_read) {
michael@0 281 DCHECK(thread_check_->CalledOnValidThread());
michael@0 282 if (input_state_.is_pending) {
michael@0 283 input_state_.is_pending = false;
michael@0 284 DCHECK(context);
michael@0 285
michael@0 286 if (!context || !bytes_read)
michael@0 287 return false;
michael@0 288 } else {
michael@0 289 // This happens at channel initialization.
michael@0 290 DCHECK(!bytes_read && context == &input_state_.context);
michael@0 291 }
michael@0 292
michael@0 293 for (;;) {
michael@0 294 if (bytes_read == 0) {
michael@0 295 if (INVALID_HANDLE_VALUE == pipe_)
michael@0 296 return false;
michael@0 297
michael@0 298 // Read from pipe...
michael@0 299 BOOL ok = ReadFile(pipe_,
michael@0 300 input_buf_,
michael@0 301 Channel::kReadBufferSize,
michael@0 302 &bytes_read,
michael@0 303 &input_state_.context.overlapped);
michael@0 304 if (!ok) {
michael@0 305 DWORD err = GetLastError();
michael@0 306 if (err == ERROR_IO_PENDING) {
michael@0 307 input_state_.is_pending = true;
michael@0 308 return true;
michael@0 309 }
michael@0 310 CHROMIUM_LOG(ERROR) << "pipe error: " << err;
michael@0 311 return false;
michael@0 312 }
michael@0 313 input_state_.is_pending = true;
michael@0 314 return true;
michael@0 315 }
michael@0 316 DCHECK(bytes_read);
michael@0 317
michael@0 318 // Process messages from input buffer.
michael@0 319
michael@0 320 const char* p, *end;
michael@0 321 if (input_overflow_buf_.empty()) {
michael@0 322 p = input_buf_;
michael@0 323 end = p + bytes_read;
michael@0 324 } else {
michael@0 325 if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
michael@0 326 input_overflow_buf_.clear();
michael@0 327 CHROMIUM_LOG(ERROR) << "IPC message is too big";
michael@0 328 return false;
michael@0 329 }
michael@0 330 input_overflow_buf_.append(input_buf_, bytes_read);
michael@0 331 p = input_overflow_buf_.data();
michael@0 332 end = p + input_overflow_buf_.size();
michael@0 333 }
michael@0 334
michael@0 335 while (p < end) {
michael@0 336 const char* message_tail = Message::FindNext(p, end);
michael@0 337 if (message_tail) {
michael@0 338 int len = static_cast<int>(message_tail - p);
michael@0 339 const Message m(p, len);
michael@0 340 #ifdef IPC_MESSAGE_DEBUG_EXTRA
michael@0 341 DLOG(INFO) << "received message on channel @" << this <<
michael@0 342 " with type " << m.type();
michael@0 343 #endif
michael@0 344 if (m.routing_id() == MSG_ROUTING_NONE &&
michael@0 345 m.type() == HELLO_MESSAGE_TYPE) {
michael@0 346 // The Hello message contains only the process id.
michael@0 347 listener_->OnChannelConnected(MessageIterator(m).NextInt());
michael@0 348 } else {
michael@0 349 listener_->OnMessageReceived(m);
michael@0 350 }
michael@0 351 p = message_tail;
michael@0 352 } else {
michael@0 353 // Last message is partial.
michael@0 354 break;
michael@0 355 }
michael@0 356 }
michael@0 357 input_overflow_buf_.assign(p, end - p);
michael@0 358
michael@0 359 bytes_read = 0; // Get more data.
michael@0 360 }
michael@0 361
michael@0 362 return true;
michael@0 363 }
michael@0 364
michael@0 365 bool Channel::ChannelImpl::ProcessOutgoingMessages(
michael@0 366 MessageLoopForIO::IOContext* context,
michael@0 367 DWORD bytes_written) {
michael@0 368 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
michael@0 369 // no connection?
michael@0 370 DCHECK(thread_check_->CalledOnValidThread());
michael@0 371
michael@0 372 if (output_state_.is_pending) {
michael@0 373 DCHECK(context);
michael@0 374 output_state_.is_pending = false;
michael@0 375 if (!context || bytes_written == 0) {
michael@0 376 DWORD err = GetLastError();
michael@0 377 CHROMIUM_LOG(ERROR) << "pipe error: " << err;
michael@0 378 return false;
michael@0 379 }
michael@0 380 // Message was sent.
michael@0 381 DCHECK(!output_queue_.empty());
michael@0 382 Message* m = output_queue_.front();
michael@0 383 OutputQueuePop();
michael@0 384 delete m;
michael@0 385 }
michael@0 386
michael@0 387 if (output_queue_.empty())
michael@0 388 return true;
michael@0 389
michael@0 390 if (INVALID_HANDLE_VALUE == pipe_)
michael@0 391 return false;
michael@0 392
michael@0 393 // Write to pipe...
michael@0 394 Message* m = output_queue_.front();
michael@0 395 BOOL ok = WriteFile(pipe_,
michael@0 396 m->data(),
michael@0 397 m->size(),
michael@0 398 &bytes_written,
michael@0 399 &output_state_.context.overlapped);
michael@0 400 if (!ok) {
michael@0 401 DWORD err = GetLastError();
michael@0 402 if (err == ERROR_IO_PENDING) {
michael@0 403 output_state_.is_pending = true;
michael@0 404
michael@0 405 #ifdef IPC_MESSAGE_DEBUG_EXTRA
michael@0 406 DLOG(INFO) << "sent pending message @" << m << " on channel @" <<
michael@0 407 this << " with type " << m->type();
michael@0 408 #endif
michael@0 409
michael@0 410 return true;
michael@0 411 }
michael@0 412 CHROMIUM_LOG(ERROR) << "pipe error: " << err;
michael@0 413 return false;
michael@0 414 }
michael@0 415
michael@0 416 #ifdef IPC_MESSAGE_DEBUG_EXTRA
michael@0 417 DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
michael@0 418 " with type " << m->type();
michael@0 419 #endif
michael@0 420
michael@0 421 output_state_.is_pending = true;
michael@0 422 return true;
michael@0 423 }
michael@0 424
michael@0 425 void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
michael@0 426 DWORD bytes_transfered, DWORD error) {
michael@0 427 bool ok;
michael@0 428 DCHECK(thread_check_->CalledOnValidThread());
michael@0 429 if (context == &input_state_.context) {
michael@0 430 if (waiting_connect_) {
michael@0 431 if (!ProcessConnection())
michael@0 432 return;
michael@0 433 // We may have some messages queued up to send...
michael@0 434 if (!output_queue_.empty() && !output_state_.is_pending)
michael@0 435 ProcessOutgoingMessages(NULL, 0);
michael@0 436 if (input_state_.is_pending)
michael@0 437 return;
michael@0 438 // else, fall-through and look for incoming messages...
michael@0 439 }
michael@0 440 // we don't support recursion through OnMessageReceived yet!
michael@0 441 DCHECK(!processing_incoming_);
michael@0 442 processing_incoming_ = true;
michael@0 443 ok = ProcessIncomingMessages(context, bytes_transfered);
michael@0 444 processing_incoming_ = false;
michael@0 445 } else {
michael@0 446 DCHECK(context == &output_state_.context);
michael@0 447 ok = ProcessOutgoingMessages(context, bytes_transfered);
michael@0 448 }
michael@0 449 if (!ok && INVALID_HANDLE_VALUE != pipe_) {
michael@0 450 // We don't want to re-enter Close().
michael@0 451 Close();
michael@0 452 listener_->OnChannelError();
michael@0 453 }
michael@0 454 }
michael@0 455
michael@0 456 bool Channel::ChannelImpl::Unsound_IsClosed() const
michael@0 457 {
michael@0 458 return closed_;
michael@0 459 }
michael@0 460
michael@0 461 uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const
michael@0 462 {
michael@0 463 return output_queue_length_;
michael@0 464 }
michael@0 465
michael@0 466 //------------------------------------------------------------------------------
michael@0 467 // Channel's methods simply call through to ChannelImpl.
michael@0 468 Channel::Channel(const std::wstring& channel_id, Mode mode,
michael@0 469 Listener* listener)
michael@0 470 : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
michael@0 471 }
michael@0 472
michael@0 473 Channel::Channel(const std::wstring& channel_id, void* server_pipe,
michael@0 474 Mode mode, Listener* listener)
michael@0 475 : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
michael@0 476 }
michael@0 477
michael@0 478 Channel::~Channel() {
michael@0 479 delete channel_impl_;
michael@0 480 }
michael@0 481
michael@0 482 bool Channel::Connect() {
michael@0 483 return channel_impl_->Connect();
michael@0 484 }
michael@0 485
michael@0 486 void Channel::Close() {
michael@0 487 channel_impl_->Close();
michael@0 488 }
michael@0 489
michael@0 490 void* Channel::GetServerPipeHandle() const {
michael@0 491 return channel_impl_->GetServerPipeHandle();
michael@0 492 }
michael@0 493
michael@0 494 Channel::Listener* Channel::set_listener(Listener* listener) {
michael@0 495 return channel_impl_->set_listener(listener);
michael@0 496 }
michael@0 497
michael@0 498 bool Channel::Send(Message* message) {
michael@0 499 return channel_impl_->Send(message);
michael@0 500 }
michael@0 501
michael@0 502 bool Channel::Unsound_IsClosed() const {
michael@0 503 return channel_impl_->Unsound_IsClosed();
michael@0 504 }
michael@0 505
michael@0 506 uint32_t Channel::Unsound_NumQueuedMessages() const {
michael@0 507 return channel_impl_->Unsound_NumQueuedMessages();
michael@0 508 }
michael@0 509
michael@0 510 } // namespace IPC

mercurial