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