diff options
author | dmikurube@chromium.org <dmikurube@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-11 06:16:57 +0000 |
---|---|---|
committer | dmikurube@chromium.org <dmikurube@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-11 06:16:57 +0000 |
commit | 081ace94bb3e5f46908dda4e6a15d70334666f0d (patch) | |
tree | 9d75a72b60df319f2d0c2c8885766989f3ddd0b7 | |
parent | 9fe646539615b7ecadaa94d325949567b56d9830 (diff) | |
download | chromium_src-081ace94bb3e5f46908dda4e6a15d70334666f0d.zip chromium_src-081ace94bb3e5f46908dda4e6a15d70334666f0d.tar.gz chromium_src-081ace94bb3e5f46908dda4e6a15d70334666f0d.tar.bz2 |
Revert 187233
> Implement IPC::ChannelFactory, a class that accept()s on a UNIX socket.
>
> IPC::ChannelFactory listens on a UNIX domain socket and notifies its delegate
> when a client connects. The delegate is expected to craft an IPC::Channel from
> the handle it is given.
>
> Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=186912
>
> Review URL: https://chromiumcodereview.appspot.com/12386010
TBR=jeremya@chromium.org
Review URL: https://codereview.chromium.org/12761003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187240 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ipc/ipc.gyp | 6 | ||||
-rw-r--r-- | ipc/ipc.gypi | 14 | ||||
-rw-r--r-- | ipc/ipc_channel.h | 4 | ||||
-rw-r--r-- | ipc/ipc_channel_factory.cc | 88 | ||||
-rw-r--r-- | ipc/ipc_channel_factory.h | 57 | ||||
-rw-r--r-- | ipc/ipc_channel_posix.cc | 189 | ||||
-rw-r--r-- | ipc/ipc_channel_posix.h | 8 | ||||
-rw-r--r-- | ipc/ipc_channel_posix_unittest.cc | 5 | ||||
-rw-r--r-- | ipc/ipc_channel_proxy.cc | 4 | ||||
-rw-r--r-- | ipc/ipc_channel_proxy.h | 2 | ||||
-rw-r--r-- | ipc/unix_domain_socket_util.cc | 202 | ||||
-rw-r--r-- | ipc/unix_domain_socket_util.h | 64 | ||||
-rw-r--r-- | ipc/unix_domain_socket_util_unittest.cc | 178 |
13 files changed, 185 insertions, 636 deletions
diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp index a247651..5e2f3a7 100644 --- a/ipc/ipc.gyp +++ b/ipc/ipc.gyp @@ -58,7 +58,6 @@ 'ipc_test_base.cc', 'ipc_test_base.h', 'sync_socket_unittest.cc', - 'unix_domain_socket_util_unittest.cc', ], 'conditions': [ ['toolkit_uses_gtk == 1', { @@ -66,11 +65,6 @@ '../build/linux/system.gyp:gtk', ], }], - ['OS == "win" or OS == "ios"', { - 'sources!': [ - 'unix_domain_socket_util_unittest.cc', - ], - }], ['OS == "android" and gtest_target_type == "shared_library"', { 'dependencies': [ '../testing/android/native_test.gyp:native_test_native_code', diff --git a/ipc/ipc.gypi b/ipc/ipc.gypi index ba80e42..ef986a7 100644 --- a/ipc/ipc.gypi +++ b/ipc/ipc.gypi @@ -13,10 +13,8 @@ 'sources': [ 'file_descriptor_set_posix.cc', 'file_descriptor_set_posix.h', - 'ipc_channel.cc', 'ipc_channel.h', - 'ipc_channel_factory.cc', - 'ipc_channel_factory.h', + 'ipc_channel.cc', 'ipc_channel_handle.h', 'ipc_channel_nacl.cc', 'ipc_channel_nacl.h', @@ -59,8 +57,6 @@ 'param_traits_write_macros.h', 'struct_constructor_macros.h', 'struct_destructor_macros.h', - 'unix_domain_socket_util.cc', - 'unix_domain_socket_util.h', ], 'defines': [ 'IPC_IMPLEMENTATION', @@ -72,15 +68,7 @@ ['>(nacl_untrusted_build)==1', { 'sources!': [ 'ipc_channel.cc', - 'ipc_channel_factory.cc', 'ipc_channel_posix.cc', - 'unix_domain_socket_util.cc', - ], - }], - ['OS == "win" or OS == "ios"', { - 'sources!': [ - 'ipc_channel_factory.cc', - 'unix_domain_socket_util.cc', ], }], ], diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h index 35ead53..29caec3 100644 --- a/ipc/ipc_channel.h +++ b/ipc/ipc_channel.h @@ -167,8 +167,8 @@ class IPC_EXPORT Channel : public Sender { bool HasAcceptedConnection() const; // Returns true if the peer process' effective user id can be determined, in - // which case the supplied peer_euid is updated with it. - bool GetPeerEuid(uid_t* peer_euid) const; + // which case the supplied client_euid is updated with it. + bool GetClientEuid(uid_t* client_euid) const; // Closes any currently connected socket, and returns to a listening state // for more connections. diff --git a/ipc/ipc_channel_factory.cc b/ipc/ipc_channel_factory.cc deleted file mode 100644 index d355328..0000000 --- a/ipc/ipc_channel_factory.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2013 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 "ipc/ipc_channel_factory.h" - -#include "base/file_util.h" -#include "base/logging.h" -#include "ipc/unix_domain_socket_util.h" - -namespace IPC { - -ChannelFactory::ChannelFactory(const base::FilePath& path, Delegate* delegate) - : path_(path), delegate_(delegate), listen_fd_(-1) { - DCHECK(delegate_); - CreateSocket(); -} - -ChannelFactory::~ChannelFactory() { - Close(); -} - -bool ChannelFactory::CreateSocket() { - DCHECK(listen_fd_ < 0); - - // Create the socket. - return CreateServerUnixDomainSocket(path_, &listen_fd_); -} - -bool ChannelFactory::Listen() { - if (listen_fd_ < 0) - return false; - - // Watch the fd for connections, and turn any connections into - // active sockets. - MessageLoopForIO::current()->WatchFileDescriptor( - listen_fd_, - true, - MessageLoopForIO::WATCH_READ, - &server_listen_connection_watcher_, - this); - return true; -} - -// Called by libevent when we can read from the fd without blocking. -void ChannelFactory::OnFileCanReadWithoutBlocking(int fd) { - DCHECK(fd == listen_fd_); - int new_fd = -1; - if (!ServerAcceptConnection(listen_fd_, &new_fd)) { - Close(); - delegate_->OnListenError(); - return; - } - - if (new_fd < 0) { - // The accept() failed, but not in such a way that the factory needs to be - // shut down. - return; - } - - file_util::ScopedFD scoped_fd(&new_fd); - - // Verify that the IPC channel peer is running as the same user. - if (!IsPeerAuthorized(new_fd)) - return; - - ChannelHandle handle("", base::FileDescriptor(*scoped_fd.release(), true)); - delegate_->OnClientConnected(handle); -} - -void ChannelFactory::OnFileCanWriteWithoutBlocking(int fd) { - NOTREACHED() << "Listen fd should never be writable."; -} - -void ChannelFactory::Close() { - if (listen_fd_ < 0) - return; - if (HANDLE_EINTR(close(listen_fd_)) < 0) - PLOG(ERROR) << "close"; - listen_fd_ = -1; - if (unlink(path_.value().c_str()) < 0) - PLOG(ERROR) << "unlink"; - - // Unregister libevent for the listening socket and close it. - server_listen_connection_watcher_.StopWatchingFileDescriptor(); -} - -} // namespace IPC diff --git a/ipc/ipc_channel_factory.h b/ipc/ipc_channel_factory.h deleted file mode 100644 index a1e1d85..0000000 --- a/ipc/ipc_channel_factory.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 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 IPC_IPC_CHANNEL_FACTORY_H_ -#define IPC_IPC_CHANNEL_FACTORY_H_ - -#include "base/files/file_path.h" -#include "base/message_loop.h" -#include "ipc/ipc_channel_handle.h" -#include "ipc/ipc_export.h" - -namespace IPC { - -// A ChannelFactory listens on a UNIX domain socket. When a client connects to -// the socket, it accept()s the connection and passes the new FD to the -// delegate. The delegate is then responsible for creating a new IPC::Channel -// for the FD. -class IPC_EXPORT ChannelFactory : public MessageLoopForIO::Watcher { - public: - class Delegate { - public: - // Called when a client connects to the factory. It is the delegate's - // responsibility to create an IPC::Channel for the handle, or else close - // the file descriptor contained therein. - virtual void OnClientConnected(const ChannelHandle& handle) = 0; - - // Called when an error occurs and the channel is closed. - virtual void OnListenError() = 0; - }; - - ChannelFactory(const base::FilePath& path, Delegate* delegate); - - virtual ~ChannelFactory(); - - // Call this to start listening on the socket. - bool Listen(); - - // Close and unlink the socket, and stop accepting connections. - void Close(); - - private: - bool CreateSocket(); - virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; - virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; - - MessageLoopForIO::FileDescriptorWatcher server_listen_connection_watcher_; - base::FilePath path_; - Delegate* delegate_; - int listen_fd_; - - DISALLOW_COPY_AND_ASSIGN(ChannelFactory); -}; - -} // namespace IPC - -#endif // IPC_IPC_CHANNEL_FACTORY_H_ diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc index 6aaac49..17b3641 100644 --- a/ipc/ipc_channel_posix.cc +++ b/ipc/ipc_channel_posix.cc @@ -40,7 +40,6 @@ #include "ipc/ipc_logging.h" #include "ipc/ipc_message_utils.h" #include "ipc/ipc_switches.h" -#include "ipc/unix_domain_socket_util.h" namespace IPC { @@ -138,6 +137,143 @@ class PipeMap { }; //------------------------------------------------------------------------------ +// Verify that kMaxPipeNameLength is a decent size. +COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength, + BAD_SUN_PATH_LENGTH); + +// Creates a unix domain socket bound to the specified name that is listening +// for connections. +bool CreateServerUnixDomainSocket(const std::string& pipe_name, + int* server_listen_fd) { + DCHECK(server_listen_fd); + + if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { + DLOG(ERROR) << "pipe_name.length() == " << pipe_name.length(); + return false; + } + + // Create socket. + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return false; + } + + // Make socket non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Delete any old FS instances. + unlink(pipe_name.c_str()); + + // Make sure the path we need exists. + base::FilePath path(pipe_name); + base::FilePath dir_path = path.DirName(); + if (!file_util::CreateDirectory(dir_path)) { + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Create unix_addr structure. + struct sockaddr_un unix_addr; + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + int path_len = snprintf(unix_addr.sun_path, IPC::kMaxPipeNameLength, + "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); + size_t unix_addr_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + // Bind the socket. + if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), + unix_addr_len) != 0) { + PLOG(ERROR) << "bind " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Start listening on the socket. + const int listen_queue_length = 1; + if (listen(fd, listen_queue_length) != 0) { + PLOG(ERROR) << "listen " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + *server_listen_fd = fd; + return true; +} + +// Accept a connection on a socket we are listening to. +bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { + DCHECK(server_socket); + + int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); + if (accept_fd < 0) + return false; + if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; + if (HANDLE_EINTR(close(accept_fd)) < 0) + PLOG(ERROR) << "close " << accept_fd; + return false; + } + + *server_socket = accept_fd; + return true; +} + +bool CreateClientUnixDomainSocket(const std::string& pipe_name, + int* client_socket) { + DCHECK(client_socket); + DCHECK_GT(pipe_name.length(), 0u); + DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); + + if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { + return false; + } + + // Create socket. + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + PLOG(ERROR) << "socket " << pipe_name; + return false; + } + + // Make socket non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Create server side of socket. + struct sockaddr_un server_unix_addr; + memset(&server_unix_addr, 0, sizeof(server_unix_addr)); + server_unix_addr.sun_family = AF_UNIX; + int path_len = snprintf(server_unix_addr.sun_path, IPC::kMaxPipeNameLength, + "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); + size_t server_unix_addr_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr), + server_unix_addr_len)) != 0) { + PLOG(ERROR) << "connect " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + *client_socket = fd; + return true; +} bool SocketWriteErrorIsRecoverable() { #if defined(OS_MACOSX) @@ -252,20 +388,12 @@ bool Channel::ChannelImpl::CreatePipe( } else if (mode_ & MODE_NAMED_FLAG) { // Case 2 from comment above. if (mode_ & MODE_SERVER_FLAG) { - if (!CreateServerUnixDomainSocket(base::FilePath(pipe_name_), - &local_pipe)) { + if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) { return false; } must_unlink_ = true; } else if (mode_ & MODE_CLIENT_FLAG) { - if (!CreateClientUnixDomainSocket(base::FilePath(pipe_name_), - &local_pipe)) { - return false; - } - // Verify that the server has the same euid as the client. - if (!IPC::IsPeerAuthorized(local_pipe)) { - if (HANDLE_EINTR(close(local_pipe)) < 0) - PLOG(ERROR) << "close " << pipe_name_; + if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) { return false; } } else { @@ -546,9 +674,33 @@ bool Channel::ChannelImpl::HasAcceptedConnection() const { return AcceptsConnections() && pipe_ != -1; } -bool Channel::ChannelImpl::GetPeerEuid(uid_t* peer_euid) const { - DCHECK(!(mode_ & MODE_SERVER) || HasAcceptedConnection()); - return IPC::GetPeerEuid(pipe_, peer_euid); +bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { + DCHECK(HasAcceptedConnection()); +#if defined(OS_MACOSX) || defined(OS_OPENBSD) + uid_t peer_euid; + gid_t peer_gid; + if (getpeereid(pipe_, &peer_euid, &peer_gid) != 0) { + PLOG(ERROR) << "getpeereid " << pipe_; + return false; + } + *client_euid = peer_euid; + return true; +#elif defined(OS_SOLARIS) + return false; +#else + struct ucred cred; + socklen_t cred_len = sizeof(cred); + if (getsockopt(pipe_, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) != 0) { + PLOG(ERROR) << "getsockopt " << pipe_; + return false; + } + if (static_cast<unsigned>(cred_len) < sizeof(cred)) { + NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; + return false; + } + *client_euid = cred.uid; + return true; +#endif } void Channel::ChannelImpl::ResetToAcceptingConnectionState() { @@ -601,8 +753,7 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { bool send_server_hello_msg = false; if (fd == server_listen_pipe_) { int new_pipe = 0; - if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) || - new_pipe < 0) { + if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) { Close(); listener()->OnChannelListenError(); } @@ -622,7 +773,7 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { // Verify that the IPC channel peer is running as the same user. uid_t client_euid; - if (!GetPeerEuid(&client_euid)) { + if (!GetClientEuid(&client_euid)) { DLOG(ERROR) << "Unable to query client euid"; ResetToAcceptingConnectionState(); return; @@ -1006,8 +1157,8 @@ bool Channel::HasAcceptedConnection() const { return channel_impl_->HasAcceptedConnection(); } -bool Channel::GetPeerEuid(uid_t* peer_euid) const { - return channel_impl_->GetPeerEuid(peer_euid); +bool Channel::GetClientEuid(uid_t* client_euid) const { + return channel_impl_->GetClientEuid(client_euid); } void Channel::ResetToAcceptingConnectionState() { diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h index 6378c33..38e14ef 100644 --- a/ipc/ipc_channel_posix.h +++ b/ipc/ipc_channel_posix.h @@ -63,7 +63,7 @@ class Channel::ChannelImpl : public internal::ChannelReader, void CloseClientFileDescriptor(); bool AcceptsConnections() const; bool HasAcceptedConnection() const; - bool GetPeerEuid(uid_t* peer_euid) const; + bool GetClientEuid(uid_t* client_euid) const; void ResetToAcceptingConnectionState(); base::ProcessId peer_pid() const { return peer_pid_; } static bool IsNamedServerInitialized(const std::string& channel_id); @@ -194,6 +194,12 @@ class Channel::ChannelImpl : public internal::ChannelReader, DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelImpl); }; +// The maximum length of the name of a pipe for MODE_NAMED_SERVER or +// MODE_NAMED_CLIENT if you want to pass in your own socket. +// The standard size on linux is 108, mac is 104. To maintain consistency +// across platforms we standardize on the smaller value. +static const size_t kMaxPipeNameLength = 104; + } // namespace IPC #endif // IPC_IPC_CHANNEL_POSIX_H_ diff --git a/ipc/ipc_channel_posix_unittest.cc b/ipc/ipc_channel_posix_unittest.cc index b49b096..448e648 100644 --- a/ipc/ipc_channel_posix_unittest.cc +++ b/ipc/ipc_channel_posix_unittest.cc @@ -21,7 +21,6 @@ #include "base/test/multiprocess_test.h" #include "base/test/test_timeouts.h" #include "ipc/ipc_listener.h" -#include "ipc/unix_domain_socket_util.h" #include "testing/multiprocess_func_list.h" namespace { @@ -146,7 +145,7 @@ void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle, struct sockaddr_un server_address = { 0 }; memset(&server_address, 0, sizeof(server_address)); server_address.sun_family = AF_UNIX; - int path_len = snprintf(server_address.sun_path, IPC::kMaxSocketNameLength, + int path_len = snprintf(server_address.sun_path, IPC::kMaxPipeNameLength, "%s", name.c_str()); DCHECK_EQ(static_cast<int>(name.length()), path_len); size_t server_address_len = offsetof(struct sockaddr_un, @@ -312,7 +311,7 @@ TEST_F(IPCChannelPosixTest, BadChannelName) { "future-proof_growth_strategies_Continually" "pontificate_proactive_potentialities_before" "leading-edge_processes"; - EXPECT_GE(strlen(kTooLongName), IPC::kMaxSocketNameLength); + EXPECT_GE(strlen(kTooLongName), IPC::kMaxPipeNameLength); IPC::ChannelHandle handle2(kTooLongName); IPC::Channel channel2(handle2, IPC::Channel::MODE_NAMED_SERVER, NULL); EXPECT_FALSE(channel2.Connect()); diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc index 1048310..0202e9e 100644 --- a/ipc/ipc_channel_proxy.cc +++ b/ipc/ipc_channel_proxy.cc @@ -414,13 +414,13 @@ int ChannelProxy::TakeClientFileDescriptor() { return channel->TakeClientFileDescriptor(); } -bool ChannelProxy::GetPeerEuid(uid_t* peer_euid) const { +bool ChannelProxy::GetClientEuid(uid_t* client_euid) const { DCHECK(CalledOnValidThread()); Channel* channel = context_.get()->channel_.get(); // Channel must have been created first. DCHECK(channel) << context_.get()->channel_id_; - return channel->GetPeerEuid(peer_euid); + return channel->GetClientEuid(client_euid); } #endif diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h index 6fbbc2a..e4cd83a 100644 --- a/ipc/ipc_channel_proxy.h +++ b/ipc/ipc_channel_proxy.h @@ -181,7 +181,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { // Calls through to the underlying channel's methods. int GetClientFileDescriptor(); int TakeClientFileDescriptor(); - bool GetPeerEuid(uid_t* peer_euid) const; + bool GetClientEuid(uid_t* client_euid) const; #endif // defined(OS_POSIX) protected: diff --git a/ipc/unix_domain_socket_util.cc b/ipc/unix_domain_socket_util.cc deleted file mode 100644 index 7f513a3..0000000 --- a/ipc/unix_domain_socket_util.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2013 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 "ipc/unix_domain_socket_util.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/un.h> -#include <unistd.h> - -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" - -namespace IPC { - -// Verify that kMaxSocketNameLength is a decent size. -COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxSocketNameLength, - BAD_SUN_PATH_LENGTH); - -namespace { - -// Returns fd (>= 0) on success, -1 on failure. If successful, fills in -// |unix_addr| with the appropriate data for the socket, and sets -// |unix_addr_len| to the length of the data therein. -int MakeUnixAddrForPath(const std::string& socket_name, - struct sockaddr_un* unix_addr, - size_t* unix_addr_len) { - DCHECK(unix_addr); - DCHECK(unix_addr_len); - - if (socket_name.length() == 0) { - LOG(ERROR) << "Empty socket name provided for unix socket address."; - return -1; - } - // We reject socket_name.length() == kMaxSocketNameLength to make room for - // the NUL terminator at the end of the string. - if (socket_name.length() >= kMaxSocketNameLength) { - LOG(ERROR) << "Socket name too long: " << socket_name; - return -1; - } - - // Create socket. - int fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - PLOG(ERROR) << "socket"; - return -1; - } - file_util::ScopedFD scoped_fd(&fd); - - // Make socket non-blocking - if (HANDLE_EINTR(fcntl(fd, F_SETFL, O_NONBLOCK)) < 0) { - PLOG(ERROR) << "fcntl(O_NONBLOCK)"; - return -1; - } - - // Create unix_addr structure. - memset(unix_addr, 0, sizeof(struct sockaddr_un)); - unix_addr->sun_family = AF_UNIX; - strncpy(unix_addr->sun_path, socket_name.c_str(), kMaxSocketNameLength); - *unix_addr_len = - offsetof(struct sockaddr_un, sun_path) + socket_name.length(); - return *scoped_fd.release(); -} - -} // namespace - -bool CreateServerUnixDomainSocket(const base::FilePath& socket_path, - int* server_listen_fd) { - DCHECK(server_listen_fd); - - std::string socket_name = socket_path.value(); - base::FilePath socket_dir = socket_path.DirName(); - - struct sockaddr_un unix_addr; - size_t unix_addr_len; - int fd = MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len); - if (fd < 0) - return false; - file_util::ScopedFD scoped_fd(&fd); - - // Make sure the path we need exists. - if (!file_util::CreateDirectory(socket_dir)) { - LOG(ERROR) << "Couldn't create directory: " << socket_dir.value(); - return false; - } - - // Delete any old FS instances. - if (unlink(socket_name.c_str()) < 0 && errno != ENOENT) { - PLOG(ERROR) << "unlink " << socket_name; - return false; - } - - // Bind the socket. - if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), - unix_addr_len) < 0) { - PLOG(ERROR) << "bind " << socket_path.value(); - return false; - } - - // Start listening on the socket. - if (listen(fd, SOMAXCONN) < 0) { - PLOG(ERROR) << "listen " << socket_path.value(); - unlink(socket_name.c_str()); - return false; - } - - *server_listen_fd = *scoped_fd.release(); - return true; -} - -bool CreateClientUnixDomainSocket(const base::FilePath& socket_path, - int* client_socket) { - DCHECK(client_socket); - - std::string socket_name = socket_path.value(); - base::FilePath socket_dir = socket_path.DirName(); - - struct sockaddr_un unix_addr; - size_t unix_addr_len; - int fd = MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len); - if (fd < 0) - return false; - file_util::ScopedFD scoped_fd(&fd); - - if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&unix_addr), - unix_addr_len)) < 0) { - PLOG(ERROR) << "connect " << socket_path.value(); - return false; - } - - *client_socket = *scoped_fd.release(); - return true; -} - -bool GetPeerEuid(int fd, uid_t* peer_euid) { - DCHECK(peer_euid); -#if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) - uid_t socket_euid; - gid_t socket_gid; - if (getpeereid(fd, &socket_euid, &socket_gid) < 0) { - PLOG(ERROR) << "getpeereid " << fd; - return false; - } - *peer_euid = socket_euid; - return true; -#else - struct ucred cred; - socklen_t cred_len = sizeof(cred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { - PLOG(ERROR) << "getsockopt " << fd; - return false; - } - if (static_cast<unsigned>(cred_len) < sizeof(cred)) { - NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; - return false; - } - *peer_euid = cred.uid; - return true; -#endif -} - -bool IsPeerAuthorized(int peer_fd) { - uid_t peer_euid; - if (!GetPeerEuid(peer_fd, &peer_euid)) - return false; - if (peer_euid != geteuid()) { - DLOG(ERROR) << "Client euid is not authorised"; - return false; - } - return true; -} - -bool IsRecoverableError(int err) { - return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE || - errno == ENOMEM || errno == ENOBUFS; -} - -bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { - DCHECK(server_socket); - *server_socket = -1; - - int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); - if (accept_fd < 0) - return IsRecoverableError(errno); - file_util::ScopedFD scoped_fd(&accept_fd); - if (HANDLE_EINTR(fcntl(accept_fd, F_SETFL, O_NONBLOCK)) < 0) { - PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; - // It's safe to keep listening on |server_listen_fd| even if the attempt to - // set O_NONBLOCK failed on the client fd. - return true; - } - - *server_socket = *scoped_fd.release(); - return true; -} - -} // namespace IPC diff --git a/ipc/unix_domain_socket_util.h b/ipc/unix_domain_socket_util.h deleted file mode 100644 index 5752364..0000000 --- a/ipc/unix_domain_socket_util.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 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 IPC_UNIX_DOMAIN_SOCKET_UTIL_H_ -#define IPC_UNIX_DOMAIN_SOCKET_UTIL_H_ - -#include <sys/types.h> - -#include <string> - -#include "ipc/ipc_export.h" - -namespace base { -class FilePath; -} // namespace base - -namespace IPC { - -// Creates a UNIX-domain socket at |socket_name| and bind()s it, then listen()s -// on it. If successful, |server_listen_fd| will be set to the new file -// descriptor, and the function will return true. Otherwise returns false. -// -// This function also effectively performs `mkdir -p` on the dirname of -// |socket_name| to ensure that all the directories up to |socket_name| exist. -// As a result of which this function must be run on a thread that allows -// blocking I/O, e.g. the FILE thread in Chrome's browser process. -IPC_EXPORT bool CreateServerUnixDomainSocket(const base::FilePath& socket_name, - int* server_listen_fd); - -// Opens a UNIX-domain socket at |socket_name| and connect()s to it. If -// successful, |client_socket| will be set to the new file descriptor, and the -// function will return true. Otherwise returns false. -IPC_EXPORT bool CreateClientUnixDomainSocket(const base::FilePath& socket_name, - int* client_socket); - -// Gets the effective user ID of the other end of the UNIX-domain socket -// specified by |fd|. If successful, sets |peer_euid| to the uid, and returns -// true. Otherwise returns false. -IPC_EXPORT bool GetPeerEuid(int fd, uid_t* peer_euid); - -// Checks that the process on the other end of the UNIX domain socket -// represented by |peer_fd| shares the same EUID as this process. -IPC_EXPORT bool IsPeerAuthorized(int peer_fd); - -// Accepts a client attempting to connect to |server_listen_fd|, storing the -// new file descriptor for the connection in |server_socket|. -// -// Returns false if |server_listen_fd| encounters an unrecoverable error. -// Returns true if it's valid to keep listening on |server_listen_fd|. In this -// case, it's possible that a connection wasn't successfully established; then, -// |server_socket| will be set to -1. -IPC_EXPORT bool ServerAcceptConnection(int server_listen_fd, - int* server_socket); - -// The maximum length of the name of a socket for MODE_NAMED_SERVER or -// MODE_NAMED_CLIENT if you want to pass in your own socket. -// The standard size on linux is 108, mac is 104. To maintain consistency -// across platforms we standardize on the smaller value. -static const size_t kMaxSocketNameLength = 104; - -} // namespace IPC - -#endif // IPC_UNIX_DOMAIN_SOCKET_UTIL_H_ diff --git a/ipc/unix_domain_socket_util_unittest.cc b/ipc/unix_domain_socket_util_unittest.cc deleted file mode 100644 index 58744dc..0000000 --- a/ipc/unix_domain_socket_util_unittest.cc +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2013 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 <sys/socket.h> - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" -#include "base/threading/thread_restrictions.h" -#include "ipc/unix_domain_socket_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -class SocketAcceptor : public MessageLoopForIO::Watcher { - public: - SocketAcceptor(int fd, base::MessageLoopProxy* target_thread) - : server_fd_(-1), - target_thread_(target_thread), - started_watching_event_(false, false), - accepted_event_(false, false) { - target_thread->PostTask(FROM_HERE, - base::Bind(&SocketAcceptor::StartWatching, base::Unretained(this), fd)); - } - - virtual ~SocketAcceptor() { - Close(); - } - - int server_fd() const { return server_fd_; } - - void WaitUntilReady() { - started_watching_event_.Wait(); - } - void WaitForAccept() { - accepted_event_.Wait(); - } - - void Close() { - if (watcher_.get()) { - target_thread_->PostTask(FROM_HERE, - base::Bind(&SocketAcceptor::StopWatching, base::Unretained(this), - watcher_.release())); - } - } - - private: - void StartWatching(int fd) { - watcher_.reset(new MessageLoopForIO::FileDescriptorWatcher); - MessageLoopForIO::current()->WatchFileDescriptor( - fd, - true, - MessageLoopForIO::WATCH_READ, - watcher_.get(), - this); - started_watching_event_.Signal(); - } - void StopWatching(MessageLoopForIO::FileDescriptorWatcher* watcher) { - watcher->StopWatchingFileDescriptor(); - delete watcher; - } - virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE { - ASSERT_EQ(-1, server_fd_); - IPC::ServerAcceptConnection(fd, &server_fd_); - watcher_->StopWatchingFileDescriptor(); - accepted_event_.Signal(); - } - virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} - - int server_fd_; - base::MessageLoopProxy* target_thread_; - scoped_ptr<MessageLoopForIO::FileDescriptorWatcher> watcher_; - base::WaitableEvent started_watching_event_; - base::WaitableEvent accepted_event_; - - DISALLOW_COPY_AND_ASSIGN(SocketAcceptor); -}; - -const base::FilePath GetChannelDir() { -#if defined(OS_ANDROID) - base::FilePath tmp_dir; - PathService::Get(base::DIR_CACHE, &tmp_dir); - return tmp_dir; -#else - return base::FilePath("/var/tmp"); -#endif -} - -class TestUnixSocketConnection { - public: - TestUnixSocketConnection() - : worker_("WorkerThread"), - server_listen_fd_(-1), - server_fd_(-1), - client_fd_(-1) { - socket_name_ = GetChannelDir().Append("TestSocket"); - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - worker_.StartWithOptions(options); - } - - bool CreateServerSocket() { - IPC::CreateServerUnixDomainSocket(socket_name_, &server_listen_fd_); - if (server_listen_fd_ < 0) - return false; - struct stat socket_stat; - stat(socket_name_.value().c_str(), &socket_stat); - EXPECT_TRUE(S_ISSOCK(socket_stat.st_mode)); - acceptor_.reset(new SocketAcceptor(server_listen_fd_, - worker_.message_loop_proxy())); - acceptor_->WaitUntilReady(); - return true; - } - - bool CreateClientSocket() { - DCHECK(server_listen_fd_ >= 0); - IPC::CreateClientUnixDomainSocket(socket_name_, &client_fd_); - if (client_fd_ < 0) - return false; - acceptor_->WaitForAccept(); - server_fd_ = acceptor_->server_fd(); - return server_fd_ >= 0; - } - - virtual ~TestUnixSocketConnection() { - if (client_fd_ >= 0) - close(client_fd_); - if (server_fd_ >= 0) - close(server_fd_); - if (server_listen_fd_ >= 0) { - close(server_listen_fd_); - unlink(socket_name_.value().c_str()); - } - } - - int client_fd() const { return client_fd_; } - int server_fd() const { return server_fd_; } - - private: - base::Thread worker_; - base::FilePath socket_name_; - int server_listen_fd_; - int server_fd_; - int client_fd_; - scoped_ptr<SocketAcceptor> acceptor_; -}; - -// Ensure that IPC::CreateServerUnixDomainSocket creates a socket that -// IPC::CreateClientUnixDomainSocket can successfully connect to. -TEST(UnixDomainSocketUtil, Connect) { - TestUnixSocketConnection connection; - ASSERT_TRUE(connection.CreateServerSocket()); - ASSERT_TRUE(connection.CreateClientSocket()); -} - -// Ensure that messages can be sent across the resulting socket. -TEST(UnixDomainSocketUtil, SendReceive) { - TestUnixSocketConnection connection; - ASSERT_TRUE(connection.CreateServerSocket()); - ASSERT_TRUE(connection.CreateClientSocket()); - - const char buffer[] = "Hello, server!"; - size_t buf_len = sizeof(buffer); - size_t sent_bytes = - HANDLE_EINTR(send(connection.client_fd(), buffer, buf_len, 0)); - ASSERT_EQ(buf_len, sent_bytes); - char recv_buf[sizeof(buffer)]; - size_t received_bytes = - HANDLE_EINTR(recv(connection.server_fd(), recv_buf, buf_len, 0)); - ASSERT_EQ(buf_len, received_bytes); - ASSERT_EQ(0, memcmp(recv_buf, buffer, buf_len)); -} - -} // namespace |