summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp2
-rw-r--r--base/sync_socket.h66
-rw-r--r--base/sync_socket_win.cc144
-rw-r--r--ipc/ipc.gyp5
-rw-r--r--ipc/ipc_tests.cc9
-rw-r--r--ipc/ipc_tests.h4
-rw-r--r--ipc/sync_socket_unittest.cc211
7 files changed, 440 insertions, 1 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 75ce295..86e4996 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -300,6 +300,8 @@
'string_util.cc',
'string_util.h',
'string_util_win.h',
+ 'sync_socket.h',
+ 'sync_socket_win.cc',
'sys_info.h',
'sys_info_chromeos.cc',
'sys_info_freebsd.cc',
diff --git a/base/sync_socket.h b/base/sync_socket.h
new file mode 100644
index 0000000..ad181ff
--- /dev/null
+++ b/base/sync_socket.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNC_SOCKET_H_
+#define BASE_SYNC_SOCKET_H_
+
+// A socket abstraction used for sending and receiving plain
+// data. Because they are blocking, they can be used to perform
+// rudimentary cross-process synchronization with low latency.
+
+#include "base/basictypes.h"
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <sys/types.h>
+
+namespace base {
+
+class SyncSocket {
+ public:
+#if defined(OS_WIN)
+ typedef HANDLE Handle;
+#else
+ typedef int Handle;
+#endif
+
+ // Creates a SyncSocket from a Handle. Used in transport.
+ explicit SyncSocket(Handle handle) : handle_(handle) { }
+ ~SyncSocket() { Close(); }
+
+ // Creates an unnamed pair of connected sockets.
+ // pair is a pointer to an array of two SyncSockets in which connected socket
+ // descriptors are returned. Returns true on success, false on failure.
+ static bool CreatePair(SyncSocket* pair[2]);
+
+ // Closes the SyncSocket. Returns true on success, false on failure.
+ bool Close();
+
+ // Sends the message to the remote peer of the SyncSocket.
+ // Note it is not safe to send messages from the same socket handle by
+ // multiple threads simultaneously.
+ // buffer is a pointer to the data to send.
+ // length is the length of the data to send (must be non-zero).
+ // Returns the number of bytes sent, or 0 upon failure.
+ size_t Send(const void* buffer, size_t length);
+
+ // Receives a message from an SyncSocket.
+ // buffer is a pointer to the buffer to receive data.
+ // length is the number of bytes of data to receive (must be non-zero).
+ // Returns the number of bytes received, or 0 upon failure.
+ size_t Receive(void* buffer, size_t length);
+
+ // Extracts the contained handle. Used for transferring between
+ // processes.
+ Handle handle() const { return handle_; }
+
+ private:
+ Handle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSocket);
+};
+
+} // namespace base
+
+#endif // BASE_SYNC_SOCKET_H_
diff --git a/base/sync_socket_win.cc b/base/sync_socket_win.cc
new file mode 100644
index 0000000..b591bb0
--- /dev/null
+++ b/base/sync_socket_win.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sync_socket.h"
+#include <limits.h>
+#include <stdio.h>
+#include <windows.h>
+#include <sys/types.h>
+#include "base/atomicops.h"
+#include "base/logging.h"
+
+
+namespace base {
+
+namespace {
+// This prefix used to be appended to pipe names for pipes
+// created in CreatePair.
+const wchar_t kPipePrefix[] = L"\\\\.\\pipe\\chrome.sync.";
+const size_t kPipePrefixSize = arraysize(kPipePrefix);
+const size_t kPathMax = 28; // print length of process id + pair count.
+const size_t kPipePathMax = kPipePrefixSize + kPathMax + 1;
+
+// To avoid users sending negative message lengths to Send/Receive
+// we clamp message lengths, which are size_t, to no more than INT_MAX.
+const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);
+
+const int kOutBufferSize = 4096;
+const int kInBufferSize = 4096;
+const int kDefaultTimeoutMilliSeconds = 1000;
+
+static const SyncSocket::Handle kInvalidHandle = INVALID_HANDLE_VALUE;
+
+} // namespace
+
+bool SyncSocket::CreatePair(SyncSocket* pair[2]) {
+ Handle handles[2];
+ SyncSocket* tmp_sockets[2];
+
+ // Create the two SyncSocket objects first to avoid ugly cleanup issues.
+ tmp_sockets[0] = new SyncSocket(kInvalidHandle);
+ if (tmp_sockets[0] == NULL) {
+ return false;
+ }
+ tmp_sockets[1] = new SyncSocket(kInvalidHandle);
+ if (tmp_sockets[1] == NULL) {
+ delete tmp_sockets[0];
+ return false;
+ }
+
+ wchar_t name[kPipePathMax];
+ do {
+ unsigned int rnd_name;
+ if (rand_s(&rnd_name) != 0)
+ return false;
+ swprintf(name, kPipePathMax, L"%s%u.%lu",
+ kPipePrefix, GetCurrentProcessId(),
+ rnd_name);
+ handles[0] = CreateNamedPipeW(
+ name,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ kOutBufferSize,
+ kInBufferSize,
+ kDefaultTimeoutMilliSeconds,
+ NULL);
+ if (handles[0] == INVALID_HANDLE_VALUE &&
+ GetLastError() != ERROR_ACCESS_DENIED &&
+ GetLastError() != ERROR_PIPE_BUSY) {
+ return false;
+ }
+ } while (handles[0] == INVALID_HANDLE_VALUE);
+ handles[1] = CreateFileW(name,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, // no sharing.
+ NULL, // default security attributes.
+ OPEN_EXISTING, // opens existing pipe.
+ SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
+ // no impersonation.
+ NULL); // no template file.
+ if (handles[1] == INVALID_HANDLE_VALUE) {
+ CloseHandle(handles[0]);
+ return false;
+ }
+ if (ConnectNamedPipe(handles[0], NULL) == FALSE) {
+ DWORD error = GetLastError();
+ if (error != ERROR_PIPE_CONNECTED) {
+ CloseHandle(handles[0]);
+ CloseHandle(handles[1]);
+ return false;
+ }
+ }
+ // Copy the handles out for successful return.
+ tmp_sockets[0]->handle_ = handles[0];
+ pair[0] = tmp_sockets[0];
+ tmp_sockets[1]->handle_ = handles[1];
+ pair[1] = tmp_sockets[1];
+ return true;
+}
+
+bool SyncSocket::Close() {
+ if (handle_ == kInvalidHandle) {
+ return false;
+ }
+ BOOL retval = CloseHandle(handle_);
+ handle_ = kInvalidHandle;
+ return retval ? true : false;
+}
+
+size_t SyncSocket::Send(const void* buffer, size_t length) {
+ DCHECK(length <= kMaxMessageLength);
+ size_t count = 0;
+ while (count < length) {
+ DWORD len;
+ // The following statement is for 64 bit portability.
+ DWORD chunk = static_cast<DWORD>(
+ ((length - count) <= UINT_MAX) ? (length - count) : UINT_MAX);
+ if (WriteFile(handle_, static_cast<const char*>(buffer) + count,
+ chunk, &len, NULL) == FALSE) {
+ return (0 < count) ? count : 0;
+ }
+ count += len;
+ }
+ return count;
+}
+
+size_t SyncSocket::Receive(void* buffer, size_t length) {
+ DCHECK(length <= kMaxMessageLength);
+ size_t count = 0;
+ while (count < length) {
+ DWORD len;
+ DWORD chunk = static_cast<DWORD>(
+ ((length - count) <= UINT_MAX) ? (length - count) : UINT_MAX);
+ if (ReadFile(handle_, static_cast<char*>(buffer) + count,
+ chunk, &len, NULL) == FALSE) {
+ return (0 < count) ? count : 0;
+ }
+ count += len;
+ }
+ return count;
+}
+
+} // namespace base
diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp
index 8e393be..eecf1df 100644
--- a/ipc/ipc.gyp
+++ b/ipc/ipc.gyp
@@ -101,6 +101,11 @@
'../views/views.gyp:views',
],
}],
+ ['OS=="win"', {
+ 'sources': [
+ 'sync_socket_unittest.cc',
+ ],
+ }],
],
},
]
diff --git a/ipc/ipc_tests.cc b/ipc/ipc_tests.cc
index 901427d..a7546c3 100644
--- a/ipc/ipc_tests.cc
+++ b/ipc/ipc_tests.cc
@@ -42,6 +42,7 @@
const char kTestClientChannel[] = "T1";
const char kReflectorChannel[] = "T2";
const char kFuzzerChannel[] = "F3";
+const char kSyncSocketChannel[] = "S4";
const size_t kLongMessageStringNumBytes = 50000;
@@ -78,6 +79,9 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
case FUZZER_SERVER:
return MultiProcessTest::SpawnChild(L"RunFuzzServer", debug_on_start);
break;
+ case SYNC_SOCKET_SERVER:
+ return MultiProcessTest::SpawnChild(L"RunSyncSocketServer", debug_on_start);
+ break;
default:
return NULL;
break;
@@ -123,6 +127,11 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
fds_to_map,
debug_on_start);
break;
+ case SYNC_SOCKET_SERVER:
+ ret = MultiProcessTest::SpawnChild(L"RunSyncSocketServer",
+ fds_to_map,
+ debug_on_start);
+ break;
default:
return NULL;
break;
diff --git a/ipc/ipc_tests.h b/ipc/ipc_tests.h
index e800883..755dafd 100644
--- a/ipc/ipc_tests.h
+++ b/ipc/ipc_tests.h
@@ -15,13 +15,15 @@ enum ChildType {
TEST_DESCRIPTOR_CLIENT,
TEST_DESCRIPTOR_CLIENT_SANDBOXED,
TEST_REFLECTOR,
- FUZZER_SERVER
+ FUZZER_SERVER,
+ SYNC_SOCKET_SERVER
};
// The different channel names for the child processes.
extern const char kTestClientChannel[];
extern const char kReflectorChannel[];
extern const char kFuzzerChannel[];
+extern const char kSyncSocketChannel[];
class MessageLoopForIO;
namespace IPC {
diff --git a/ipc/sync_socket_unittest.cc b/ipc/sync_socket_unittest.cc
new file mode 100644
index 0000000..47a907c
--- /dev/null
+++ b/ipc/sync_socket_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <iostream>
+#include <string>
+#include <sstream>
+
+#include "base/message_loop.h"
+#include "base/platform_thread.h"
+#include "base/process_util.h"
+#include "base/sync_socket.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+
+// We don't actually use the messages defined in this file, but we do this
+// to get to the IPC macros.
+#define MESSAGES_INTERNAL_FILE "ipc/ipc_sync_message_unittest.h"
+#include "ipc/ipc_message_macros.h"
+
+enum IPCMessageIds {
+ UNUSED_IPC_TYPE,
+ SERVER_FIRST_IPC_TYPE, // SetHandle message sent to server.
+ SERVER_SECOND_IPC_TYPE, // Shutdown message sent to server.
+ CLIENT_FIRST_IPC_TYPE // Response message sent to client.
+};
+
+namespace {
+const char kHelloString[] = "Hello, SyncSocket Client";
+const size_t kHelloStringLength = arraysize(kHelloString);
+} // namespace
+
+// Message class to pass a HANDLE to another process.
+class MsgClassSetHandle
+ : public IPC::MessageWithTuple< Tuple1<base::SyncSocket::Handle> > {
+ public:
+ enum { ID = SERVER_FIRST_IPC_TYPE };
+ explicit MsgClassSetHandle(const base::SyncSocket::Handle arg1)
+ : IPC::MessageWithTuple< Tuple1<base::SyncSocket::Handle> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle);
+};
+
+// Message class to pass a response to the server.
+class MsgClassResponse
+ : public IPC::MessageWithTuple< Tuple1<std::string> > {
+ public:
+ enum { ID = CLIENT_FIRST_IPC_TYPE };
+ explicit MsgClassResponse(const std::string& arg1)
+ : IPC::MessageWithTuple< Tuple1<std::string> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassResponse);
+};
+
+// Message class to tell the server to shut down.
+class MsgClassShutdown
+ : public IPC::MessageWithTuple< Tuple0 > {
+ public:
+ enum { ID = SERVER_SECOND_IPC_TYPE };
+ MsgClassShutdown()
+ : IPC::MessageWithTuple< Tuple0 >(
+ MSG_ROUTING_CONTROL, ID, MakeTuple()) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassShutdown);
+};
+
+// The SyncSocket server listener class processes two sorts of
+// messages from the client.
+class SyncSocketServerListener : public IPC::Channel::Listener {
+ public:
+ SyncSocketServerListener() : chan_(NULL) {
+ }
+
+ void Init(IPC::Channel* chan) {
+ chan_ = chan;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ IPC_BEGIN_MESSAGE_MAP(SyncSocketServerListener, msg)
+ IPC_MESSAGE_HANDLER(MsgClassSetHandle, OnMsgClassSetHandle)
+ IPC_MESSAGE_HANDLER(MsgClassShutdown, OnMsgClassShutdown)
+ IPC_END_MESSAGE_MAP()
+ }
+ }
+
+ private:
+ // This sort of message is sent first, causing the transfer of
+ // the handle for the SyncSocket. This message sends a buffer
+ // on the SyncSocket and then sends a response to the client.
+ void OnMsgClassSetHandle(const base::SyncSocket::Handle handle) {
+ base::SyncSocket sync_socket(handle);
+ EXPECT_EQ(sync_socket.Send(static_cast<const void*>(kHelloString),
+ kHelloStringLength), kHelloStringLength);
+ IPC::Message* msg = new MsgClassResponse(kHelloString);
+ EXPECT_NE(msg, reinterpret_cast<IPC::Message*>(NULL));
+ EXPECT_TRUE(chan_->Send(msg));
+ }
+
+ // When the client responds, it sends back a shutdown message,
+ // which causes the message loop to exit.
+ void OnMsgClassShutdown() {
+ MessageLoop::current()->Quit();
+ }
+
+ IPC::Channel* chan_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSocketServerListener);
+};
+
+// Runs the fuzzing server child mode. Returns when the preset number
+// of messages have been received.
+MULTIPROCESS_TEST_MAIN(RunSyncSocketServer) {
+ MessageLoopForIO main_message_loop;
+ SyncSocketServerListener listener;
+ IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_CLIENT, &listener);
+ EXPECT_TRUE(chan.Connect());
+ listener.Init(&chan);
+ MessageLoop::current()->Run();
+ return 0;
+}
+
+// The SyncSocket client listener only processes one sort of message,
+// a response from the server.
+class SyncSocketClientListener : public IPC::Channel::Listener {
+ public:
+ SyncSocketClientListener() {
+ }
+
+ void Init(base::SyncSocket* socket, IPC::Channel* chan) {
+ socket_ = socket;
+ chan_ = chan;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ IPC_BEGIN_MESSAGE_MAP(SyncSocketClientListener, msg)
+ IPC_MESSAGE_HANDLER(MsgClassResponse, OnMsgClassResponse)
+ IPC_END_MESSAGE_MAP()
+ }
+ }
+
+ private:
+ // When a response is received from the server, it sends the same
+ // string as was written on the SyncSocket. These are compared
+ // and a shutdown message is sent back to the server.
+ void OnMsgClassResponse(const std::string& str) {
+ char buf[kHelloStringLength];
+ socket_->Receive(static_cast<void*>(buf), kHelloStringLength);
+ EXPECT_EQ(strcmp(str.c_str(), buf), 0);
+ IPC::Message* msg = new MsgClassShutdown();
+ EXPECT_NE(msg, reinterpret_cast<IPC::Message*>(NULL));
+ EXPECT_TRUE(chan_->Send(msg));
+ MessageLoop::current()->Quit();
+ }
+
+ base::SyncSocket* socket_;
+ IPC::Channel* chan_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSocketClientListener);
+};
+
+class SyncSocketTest : public IPCChannelTest {
+};
+
+TEST_F(SyncSocketTest, SanityTest) {
+ SyncSocketClientListener listener;
+ IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(SYNC_SOCKET_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ // Create a pair of SyncSockets.
+ base::SyncSocket* pair[2];
+ base::SyncSocket::CreatePair(pair);
+ base::SyncSocket::Handle target_handle;
+#if defined(OS_WIN)
+ // On windows we need to duplicate the handle into the server process.
+ BOOL retval = DuplicateHandle(GetCurrentProcess(), pair[1]->handle(),
+ server_process, &target_handle,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ EXPECT_TRUE(retval);
+#else
+ target_handle = pair[1]->handle();
+#endif // defined(OS_WIN)
+ // Connect the channel and listener.
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(pair[0], &chan);
+ // Set up a message to pass the handle to the server.
+ IPC::Message* msg = new MsgClassSetHandle(target_handle);
+ EXPECT_NE(msg, reinterpret_cast<IPC::Message*>(NULL));
+ EXPECT_TRUE(chan.Send(msg));
+ // Use the current thread as the I/O thread.
+ MessageLoop::current()->Run();
+ // Shut down.
+ delete pair[0];
+ delete pair[1];
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+