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.

Previously committed:
- https://src.chromium.org/viewvc/chrome?view=rev&revision=186912
- https://src.chromium.org/viewvc/chrome?view=rev&revision=187233
- https://src.chromium.org/viewvc/chrome?view=rev&revision=187554

Review URL: https://codereview.chromium.org/12386010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187772 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: bdf9bdc258df1e56f7ac7e3de705b0a70e9f93e4
diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h
index 29caec3..35ead53 100644
--- a/ipc/ipc_channel.h
+++ b/ipc/ipc_channel.h
@@ -167,8 +167,8 @@
   bool HasAcceptedConnection() const;
 
   // Returns true if the peer process' effective user id can be determined, in
-  // which case the supplied client_euid is updated with it.
-  bool GetClientEuid(uid_t* client_euid) const;
+  // which case the supplied peer_euid is updated with it.
+  bool GetPeerEuid(uid_t* peer_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
new file mode 100644
index 0000000..d355328
--- /dev/null
+++ b/ipc/ipc_channel_factory.cc
@@ -0,0 +1,88 @@
+// 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
new file mode 100644
index 0000000..a1e1d85
--- /dev/null
+++ b/ipc/ipc_channel_factory.h
@@ -0,0 +1,57 @@
+// 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 17b3641..7f38a48 100644
--- a/ipc/ipc_channel_posix.cc
+++ b/ipc/ipc_channel_posix.cc
@@ -40,6 +40,7 @@
 #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 {
 
@@ -137,143 +138,6 @@
 };
 
 //------------------------------------------------------------------------------
-// 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)
@@ -388,12 +252,14 @@
   } else if (mode_ & MODE_NAMED_FLAG) {
     // Case 2 from comment above.
     if (mode_ & MODE_SERVER_FLAG) {
-      if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) {
+      if (!CreateServerUnixDomainSocket(base::FilePath(pipe_name_),
+                                        &local_pipe)) {
         return false;
       }
       must_unlink_ = true;
     } else if (mode_ & MODE_CLIENT_FLAG) {
-      if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) {
+      if (!CreateClientUnixDomainSocket(base::FilePath(pipe_name_),
+                                        &local_pipe)) {
         return false;
       }
     } else {
@@ -674,33 +540,9 @@
   return AcceptsConnections() && pipe_ != -1;
 }
 
-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
+bool Channel::ChannelImpl::GetPeerEuid(uid_t* peer_euid) const {
+  DCHECK(!(mode_ & MODE_SERVER) || HasAcceptedConnection());
+  return IPC::GetPeerEuid(pipe_, peer_euid);
 }
 
 void Channel::ChannelImpl::ResetToAcceptingConnectionState() {
@@ -753,7 +595,8 @@
   bool send_server_hello_msg = false;
   if (fd == server_listen_pipe_) {
     int new_pipe = 0;
-    if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) {
+    if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) ||
+        new_pipe < 0) {
       Close();
       listener()->OnChannelListenError();
     }
@@ -773,7 +616,7 @@
     if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) {
       // Verify that the IPC channel peer is running as the same user.
       uid_t client_euid;
-      if (!GetClientEuid(&client_euid)) {
+      if (!GetPeerEuid(&client_euid)) {
         DLOG(ERROR) << "Unable to query client euid";
         ResetToAcceptingConnectionState();
         return;
@@ -1157,8 +1000,8 @@
   return channel_impl_->HasAcceptedConnection();
 }
 
-bool Channel::GetClientEuid(uid_t* client_euid) const {
-  return channel_impl_->GetClientEuid(client_euid);
+bool Channel::GetPeerEuid(uid_t* peer_euid) const {
+  return channel_impl_->GetPeerEuid(peer_euid);
 }
 
 void Channel::ResetToAcceptingConnectionState() {
diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h
index 38e14ef..6378c33 100644
--- a/ipc/ipc_channel_posix.h
+++ b/ipc/ipc_channel_posix.h
@@ -63,7 +63,7 @@
   void CloseClientFileDescriptor();
   bool AcceptsConnections() const;
   bool HasAcceptedConnection() const;
-  bool GetClientEuid(uid_t* client_euid) const;
+  bool GetPeerEuid(uid_t* peer_euid) const;
   void ResetToAcceptingConnectionState();
   base::ProcessId peer_pid() const { return peer_pid_; }
   static bool IsNamedServerInitialized(const std::string& channel_id);
@@ -194,12 +194,6 @@
   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 448e648..b49b096 100644
--- a/ipc/ipc_channel_posix_unittest.cc
+++ b/ipc/ipc_channel_posix_unittest.cc
@@ -21,6 +21,7 @@
 #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 {
@@ -145,7 +146,7 @@
   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::kMaxPipeNameLength,
+  int path_len = snprintf(server_address.sun_path, IPC::kMaxSocketNameLength,
                           "%s", name.c_str());
   DCHECK_EQ(static_cast<int>(name.length()), path_len);
   size_t server_address_len = offsetof(struct sockaddr_un,
@@ -311,7 +312,7 @@
                              "future-proof_growth_strategies_Continually"
                              "pontificate_proactive_potentialities_before"
                              "leading-edge_processes";
-  EXPECT_GE(strlen(kTooLongName), IPC::kMaxPipeNameLength);
+  EXPECT_GE(strlen(kTooLongName), IPC::kMaxSocketNameLength);
   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 0202e9e..1048310 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -414,13 +414,13 @@
   return channel->TakeClientFileDescriptor();
 }
 
-bool ChannelProxy::GetClientEuid(uid_t* client_euid) const {
+bool ChannelProxy::GetPeerEuid(uid_t* peer_euid) const {
   DCHECK(CalledOnValidThread());
 
   Channel* channel = context_.get()->channel_.get();
   // Channel must have been created first.
   DCHECK(channel) << context_.get()->channel_id_;
-  return channel->GetClientEuid(client_euid);
+  return channel->GetPeerEuid(peer_euid);
 }
 #endif
 
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index e4cd83a..6fbbc2a 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -181,7 +181,7 @@
   // Calls through to the underlying channel's methods.
   int GetClientFileDescriptor();
   int TakeClientFileDescriptor();
-  bool GetClientEuid(uid_t* client_euid) const;
+  bool GetPeerEuid(uid_t* peer_euid) const;
 #endif  // defined(OS_POSIX)
 
  protected:
diff --git a/ipc/unix_domain_socket_util.cc b/ipc/unix_domain_socket_util.cc
new file mode 100644
index 0000000..7f513a3
--- /dev/null
+++ b/ipc/unix_domain_socket_util.cc
@@ -0,0 +1,202 @@
+// 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
new file mode 100644
index 0000000..5752364
--- /dev/null
+++ b/ipc/unix_domain_socket_util.h
@@ -0,0 +1,64 @@
+// 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
new file mode 100644
index 0000000..a00d3aa
--- /dev/null
+++ b/ipc/unix_domain_socket_util_unittest.cc
@@ -0,0 +1,179 @@
+// 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