ipc_channel_posix_unittest.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// These tests are POSIX only.
6
7#include "ipc/ipc_channel_posix.h"
8
9#include <fcntl.h>
10#include <sys/socket.h>
11#include <sys/un.h>
12#include <unistd.h>
13
14#include "base/basictypes.h"
15#include "base/file_util.h"
16#include "base/files/file_path.h"
17#include "base/memory/scoped_ptr.h"
18#include "base/message_loop/message_loop.h"
19#include "base/path_service.h"
20#include "base/posix/eintr_wrapper.h"
21#include "base/test/multiprocess_test.h"
22#include "base/test/test_timeouts.h"
23#include "ipc/ipc_listener.h"
24#include "ipc/unix_domain_socket_util.h"
25#include "testing/multiprocess_func_list.h"
26
27namespace {
28
29static const uint32 kQuitMessage = 47;
30
31class IPCChannelPosixTestListener : public IPC::Listener {
32 public:
33  enum STATUS {
34    DISCONNECTED,
35    MESSAGE_RECEIVED,
36    CHANNEL_ERROR,
37    CONNECTED,
38    DENIED,
39    LISTEN_ERROR
40  };
41
42  IPCChannelPosixTestListener(bool quit_only_on_message)
43      : status_(DISCONNECTED), quit_only_on_message_(quit_only_on_message) {}
44
45  virtual ~IPCChannelPosixTestListener() {}
46
47  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
48    EXPECT_EQ(message.type(), kQuitMessage);
49    status_ = MESSAGE_RECEIVED;
50    QuitRunLoop();
51    return true;
52  }
53
54  virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
55    status_ = CONNECTED;
56    if (!quit_only_on_message_) {
57      QuitRunLoop();
58    }
59  }
60
61  virtual void OnChannelError() OVERRIDE {
62    status_ = CHANNEL_ERROR;
63    if (!quit_only_on_message_) {
64      QuitRunLoop();
65    }
66  }
67
68  virtual void OnChannelDenied() OVERRIDE {
69    status_ = DENIED;
70    if (!quit_only_on_message_) {
71      QuitRunLoop();
72    }
73  }
74
75  virtual void OnChannelListenError() OVERRIDE {
76    status_ = LISTEN_ERROR;
77    if (!quit_only_on_message_) {
78      QuitRunLoop();
79    }
80  }
81
82  STATUS status() { return status_; }
83
84  void QuitRunLoop() {
85    base::MessageLoopForIO::current()->QuitNow();
86  }
87
88 private:
89  // The current status of the listener.
90  STATUS status_;
91  // If |quit_only_on_message_| then the listener will only break out of
92  // the run loop when kQuitMessage is received.
93  bool quit_only_on_message_;
94};
95
96class IPCChannelPosixTest : public base::MultiProcessTest {
97 public:
98  static void SetUpSocket(IPC::ChannelHandle *handle,
99                          IPC::Channel::Mode mode);
100  static void SpinRunLoop(base::TimeDelta delay);
101  static const std::string GetConnectionSocketName();
102  static const std::string GetChannelDirName();
103
104 protected:
105  virtual void SetUp();
106  virtual void TearDown();
107
108 private:
109  scoped_ptr<base::MessageLoopForIO> message_loop_;
110};
111
112const std::string IPCChannelPosixTest::GetChannelDirName() {
113#if defined(OS_ANDROID)
114  base::FilePath tmp_dir;
115  PathService::Get(base::DIR_CACHE, &tmp_dir);
116  return tmp_dir.value();
117#else
118  return "/var/tmp";
119#endif
120}
121
122const std::string IPCChannelPosixTest::GetConnectionSocketName() {
123  return GetChannelDirName() + "/chrome_IPCChannelPosixTest__ConnectionSocket";
124}
125
126void IPCChannelPosixTest::SetUp() {
127  MultiProcessTest::SetUp();
128  // Construct a fresh IO Message loop for the duration of each test.
129  message_loop_.reset(new base::MessageLoopForIO());
130}
131
132void IPCChannelPosixTest::TearDown() {
133  message_loop_.reset(NULL);
134  MultiProcessTest::TearDown();
135}
136
137// Create up a socket and bind and listen to it, or connect it
138// depending on the |mode|.
139void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle,
140                                      IPC::Channel::Mode mode) {
141  const std::string& name = handle->name;
142
143  int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
144  ASSERT_GE(socket_fd, 0) << name;
145  ASSERT_GE(fcntl(socket_fd, F_SETFL, O_NONBLOCK), 0);
146  struct sockaddr_un server_address = { 0 };
147  memset(&server_address, 0, sizeof(server_address));
148  server_address.sun_family = AF_UNIX;
149  int path_len = snprintf(server_address.sun_path, IPC::kMaxSocketNameLength,
150                          "%s", name.c_str());
151  DCHECK_EQ(static_cast<int>(name.length()), path_len);
152  size_t server_address_len = offsetof(struct sockaddr_un,
153                                       sun_path) + path_len + 1;
154
155  if (mode == IPC::Channel::MODE_NAMED_SERVER) {
156    // Only one server at a time. Cleanup garbage if it exists.
157    unlink(name.c_str());
158    // Make sure the path we need exists.
159    base::FilePath path(name);
160    base::FilePath dir_path = path.DirName();
161    ASSERT_TRUE(file_util::CreateDirectory(dir_path));
162    ASSERT_GE(bind(socket_fd,
163                   reinterpret_cast<struct sockaddr *>(&server_address),
164                   server_address_len), 0) << server_address.sun_path
165                                           << ": " << strerror(errno)
166                                           << "(" << errno << ")";
167    ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << server_address.sun_path
168                                               << ": " << strerror(errno)
169                                               << "(" << errno << ")";
170  } else if (mode == IPC::Channel::MODE_NAMED_CLIENT) {
171    ASSERT_GE(connect(socket_fd,
172                      reinterpret_cast<struct sockaddr *>(&server_address),
173                      server_address_len), 0) << server_address.sun_path
174                                              << ": " << strerror(errno)
175                                              << "(" << errno << ")";
176  } else {
177    FAIL() << "Unknown mode " << mode;
178  }
179  handle->socket.fd = socket_fd;
180}
181
182void IPCChannelPosixTest::SpinRunLoop(base::TimeDelta delay) {
183  base::MessageLoopForIO* loop = base::MessageLoopForIO::current();
184  // Post a quit task so that this loop eventually ends and we don't hang
185  // in the case of a bad test. Usually, the run loop will quit sooner than
186  // that because all tests use a IPCChannelPosixTestListener which quits the
187  // current run loop on any channel activity.
188  loop->PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), delay);
189  loop->Run();
190}
191
192TEST_F(IPCChannelPosixTest, BasicListen) {
193  const std::string kChannelName =
194      GetChannelDirName() + "/IPCChannelPosixTest_BasicListen";
195
196  // Test creating a socket that is listening.
197  IPC::ChannelHandle handle(kChannelName);
198  SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER);
199  unlink(handle.name.c_str());
200  IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
201  ASSERT_TRUE(channel.Connect());
202  ASSERT_TRUE(channel.AcceptsConnections());
203  ASSERT_FALSE(channel.HasAcceptedConnection());
204  channel.ResetToAcceptingConnectionState();
205  ASSERT_FALSE(channel.HasAcceptedConnection());
206}
207
208TEST_F(IPCChannelPosixTest, BasicConnected) {
209  // Test creating a socket that is connected.
210  int pipe_fds[2];
211  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds));
212  std::string socket_name("/var/tmp/IPCChannelPosixTest_BasicConnected");
213  ASSERT_GE(fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK), 0);
214
215  base::FileDescriptor fd(pipe_fds[0], false);
216  IPC::ChannelHandle handle(socket_name, fd);
217  IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL);
218  ASSERT_TRUE(channel.Connect());
219  ASSERT_FALSE(channel.AcceptsConnections());
220  channel.Close();
221  ASSERT_TRUE(HANDLE_EINTR(close(pipe_fds[1])) == 0);
222
223  // Make sure that we can use the socket that is created for us by
224  // a standard channel.
225  IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL);
226  ASSERT_TRUE(channel2.Connect());
227  ASSERT_FALSE(channel2.AcceptsConnections());
228}
229
230TEST_F(IPCChannelPosixTest, AdvancedConnected) {
231  // Test creating a connection to an external process.
232  IPCChannelPosixTestListener listener(false);
233  IPC::ChannelHandle chan_handle(GetConnectionSocketName());
234  SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
235  IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
236  ASSERT_TRUE(channel.Connect());
237  ASSERT_TRUE(channel.AcceptsConnections());
238  ASSERT_FALSE(channel.HasAcceptedConnection());
239
240  base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
241                                          false);
242  ASSERT_TRUE(handle);
243  SpinRunLoop(TestTimeouts::action_max_timeout());
244  ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
245  ASSERT_TRUE(channel.HasAcceptedConnection());
246  IPC::Message* message = new IPC::Message(0,  // routing_id
247                                           kQuitMessage,  // message type
248                                           IPC::Message::PRIORITY_NORMAL);
249  channel.Send(message);
250  SpinRunLoop(TestTimeouts::action_timeout());
251  int exit_code = 0;
252  EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
253  EXPECT_EQ(0, exit_code);
254  ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
255  ASSERT_FALSE(channel.HasAcceptedConnection());
256}
257
258TEST_F(IPCChannelPosixTest, ResetState) {
259  // Test creating a connection to an external process. Close the connection,
260  // but continue to listen and make sure another external process can connect
261  // to us.
262  IPCChannelPosixTestListener listener(false);
263  IPC::ChannelHandle chan_handle(GetConnectionSocketName());
264  SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
265  IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
266  ASSERT_TRUE(channel.Connect());
267  ASSERT_TRUE(channel.AcceptsConnections());
268  ASSERT_FALSE(channel.HasAcceptedConnection());
269
270  base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
271                                          false);
272  ASSERT_TRUE(handle);
273  SpinRunLoop(TestTimeouts::action_max_timeout());
274  ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
275  ASSERT_TRUE(channel.HasAcceptedConnection());
276  channel.ResetToAcceptingConnectionState();
277  ASSERT_FALSE(channel.HasAcceptedConnection());
278
279  base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc",
280                                          false);
281  ASSERT_TRUE(handle2);
282  SpinRunLoop(TestTimeouts::action_max_timeout());
283  ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
284  ASSERT_TRUE(channel.HasAcceptedConnection());
285  IPC::Message* message = new IPC::Message(0,  // routing_id
286                                           kQuitMessage,  // message type
287                                           IPC::Message::PRIORITY_NORMAL);
288  channel.Send(message);
289  SpinRunLoop(TestTimeouts::action_timeout());
290  EXPECT_TRUE(base::KillProcess(handle, 0, false));
291  int exit_code = 0;
292  EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
293  EXPECT_EQ(0, exit_code);
294  ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
295  ASSERT_FALSE(channel.HasAcceptedConnection());
296}
297
298TEST_F(IPCChannelPosixTest, BadChannelName) {
299  // Test empty name
300  IPC::ChannelHandle handle("");
301  IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
302  ASSERT_FALSE(channel.Connect());
303
304  // Test name that is too long.
305  const char *kTooLongName = "This_is_a_very_long_name_to_proactively_implement"
306                             "client-centered_synergy_through_top-line"
307                             "platforms_Phosfluorescently_disintermediate_"
308                             "clicks-and-mortar_best_practices_without_"
309                             "future-proof_growth_strategies_Continually"
310                             "pontificate_proactive_potentialities_before"
311                             "leading-edge_processes";
312  EXPECT_GE(strlen(kTooLongName), IPC::kMaxSocketNameLength);
313  IPC::ChannelHandle handle2(kTooLongName);
314  IPC::Channel channel2(handle2, IPC::Channel::MODE_NAMED_SERVER, NULL);
315  EXPECT_FALSE(channel2.Connect());
316}
317
318TEST_F(IPCChannelPosixTest, MultiConnection) {
319  // Test setting up a connection to an external process, and then have
320  // another external process attempt to connect to us.
321  IPCChannelPosixTestListener listener(false);
322  IPC::ChannelHandle chan_handle(GetConnectionSocketName());
323  SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
324  IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
325  ASSERT_TRUE(channel.Connect());
326  ASSERT_TRUE(channel.AcceptsConnections());
327  ASSERT_FALSE(channel.HasAcceptedConnection());
328
329  base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
330                                          false);
331  ASSERT_TRUE(handle);
332  SpinRunLoop(TestTimeouts::action_max_timeout());
333  ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
334  ASSERT_TRUE(channel.HasAcceptedConnection());
335  base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc",
336                                           false);
337  ASSERT_TRUE(handle2);
338  SpinRunLoop(TestTimeouts::action_max_timeout());
339  int exit_code = 0;
340  EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
341  EXPECT_EQ(exit_code, 0);
342  ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status());
343  ASSERT_TRUE(channel.HasAcceptedConnection());
344  IPC::Message* message = new IPC::Message(0,  // routing_id
345                                           kQuitMessage,  // message type
346                                           IPC::Message::PRIORITY_NORMAL);
347  channel.Send(message);
348  SpinRunLoop(TestTimeouts::action_timeout());
349  EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
350  EXPECT_EQ(exit_code, 0);
351  ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
352  ASSERT_FALSE(channel.HasAcceptedConnection());
353}
354
355TEST_F(IPCChannelPosixTest, DoubleServer) {
356  // Test setting up two servers with the same name.
357  IPCChannelPosixTestListener listener(false);
358  IPCChannelPosixTestListener listener2(false);
359  IPC::ChannelHandle chan_handle(GetConnectionSocketName());
360  IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener);
361  IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2);
362  ASSERT_TRUE(channel.Connect());
363  ASSERT_FALSE(channel2.Connect());
364}
365
366TEST_F(IPCChannelPosixTest, BadMode) {
367  // Test setting up two servers with a bad mode.
368  IPCChannelPosixTestListener listener(false);
369  IPC::ChannelHandle chan_handle(GetConnectionSocketName());
370  IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener);
371  ASSERT_FALSE(channel.Connect());
372}
373
374TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) {
375  const std::string& connection_socket_name = GetConnectionSocketName();
376  IPCChannelPosixTestListener listener(false);
377  IPC::ChannelHandle chan_handle(connection_socket_name);
378  ASSERT_TRUE(base::DeleteFile(base::FilePath(connection_socket_name), false));
379  ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
380      connection_socket_name));
381  IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
382  ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized(
383      connection_socket_name));
384  channel.Close();
385  ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
386      connection_socket_name));
387}
388
389// A long running process that connects to us
390MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) {
391  base::MessageLoopForIO message_loop;
392  IPCChannelPosixTestListener listener(true);
393  IPC::ChannelHandle handle(IPCChannelPosixTest::GetConnectionSocketName());
394  IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
395  IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
396  EXPECT_TRUE(channel.Connect());
397  IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout());
398  EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status());
399  return 0;
400}
401
402// Simple external process that shouldn't be able to connect to us.
403MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) {
404  base::MessageLoopForIO message_loop;
405  IPCChannelPosixTestListener listener(false);
406  IPC::ChannelHandle handle(IPCChannelPosixTest::GetConnectionSocketName());
407  IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
408  IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
409
410  // In this case connect may succeed or fail depending on if the packet
411  // actually gets sent at sendmsg. Since we never delay on send, we may not
412  // see the error. However even if connect succeeds, eventually we will get an
413  // error back since the channel will be closed when we attempt to read from
414  // it.
415  bool connected = channel.Connect();
416  if (connected) {
417    IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout());
418    EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
419  } else {
420    EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status());
421  }
422  return 0;
423}
424
425}  // namespace
426