| // Copyright 2016 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 "mojo/edk/embedder/named_platform_handle_utils.h" |
| |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "mojo/edk/embedder/named_platform_handle.h" |
| |
| namespace mojo { |
| namespace edk { |
| namespace { |
| |
| // This function fills in |unix_addr| with the appropriate data for the socket, |
| // and sets |unix_addr_len| to the length of the data therein. |
| // Returns true on success, or false on failure (typically because |handle.name| |
| // violated the naming rules). |
| bool MakeUnixAddr(const NamedPlatformHandle& handle, |
| struct sockaddr_un* unix_addr, |
| size_t* unix_addr_len) { |
| DCHECK(unix_addr); |
| DCHECK(unix_addr_len); |
| DCHECK(handle.is_valid()); |
| |
| // We reject handle.name.length() == kMaxSocketNameLength to make room for the |
| // NUL terminator at the end of the string. |
| if (handle.name.length() >= kMaxSocketNameLength) { |
| LOG(ERROR) << "Socket name too long: " << handle.name; |
| return false; |
| } |
| |
| // Create unix_addr structure. |
| memset(unix_addr, 0, sizeof(struct sockaddr_un)); |
| unix_addr->sun_family = AF_UNIX; |
| strncpy(unix_addr->sun_path, handle.name.c_str(), kMaxSocketNameLength); |
| *unix_addr_len = |
| offsetof(struct sockaddr_un, sun_path) + handle.name.length(); |
| return true; |
| } |
| |
| // This function creates a unix domain socket, and set it as non-blocking. |
| // If successful, this returns a ScopedPlatformHandle containing the socket. |
| // Otherwise, this returns an invalid ScopedPlatformHandle. |
| ScopedPlatformHandle CreateUnixDomainSocket(bool needs_connection) { |
| // Create the unix domain socket. |
| PlatformHandle socket_handle(socket(AF_UNIX, SOCK_STREAM, 0)); |
| socket_handle.needs_connection = needs_connection; |
| ScopedPlatformHandle handle(socket_handle); |
| if (!handle.is_valid()) { |
| PLOG(ERROR) << "Failed to create AF_UNIX socket."; |
| return ScopedPlatformHandle(); |
| } |
| |
| // Now set it as non-blocking. |
| if (!base::SetNonBlocking(handle.get().handle)) { |
| PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.get().handle; |
| return ScopedPlatformHandle(); |
| } |
| return handle; |
| } |
| |
| } // namespace |
| |
| ScopedPlatformHandle CreateClientHandle( |
| const NamedPlatformHandle& named_handle) { |
| if (!named_handle.is_valid()) |
| return ScopedPlatformHandle(); |
| |
| struct sockaddr_un unix_addr; |
| size_t unix_addr_len; |
| if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) |
| return ScopedPlatformHandle(); |
| |
| ScopedPlatformHandle handle = CreateUnixDomainSocket(false); |
| if (!handle.is_valid()) |
| return ScopedPlatformHandle(); |
| |
| if (HANDLE_EINTR(connect(handle.get().handle, |
| reinterpret_cast<sockaddr*>(&unix_addr), |
| unix_addr_len)) < 0) { |
| PLOG(ERROR) << "connect " << named_handle.name; |
| return ScopedPlatformHandle(); |
| } |
| |
| return handle; |
| } |
| |
| ScopedPlatformHandle CreateServerHandle( |
| const NamedPlatformHandle& named_handle, |
| const CreateServerHandleOptions& options) { |
| if (!named_handle.is_valid()) |
| return ScopedPlatformHandle(); |
| |
| // Make sure the path we need exists. |
| base::FilePath socket_dir = base::FilePath(named_handle.name).DirName(); |
| if (!base::CreateDirectory(socket_dir)) { |
| LOG(ERROR) << "Couldn't create directory: " << socket_dir.value(); |
| return ScopedPlatformHandle(); |
| } |
| |
| // Delete any old FS instances. |
| if (unlink(named_handle.name.c_str()) < 0 && errno != ENOENT) { |
| PLOG(ERROR) << "unlink " << named_handle.name; |
| return ScopedPlatformHandle(); |
| } |
| |
| struct sockaddr_un unix_addr; |
| size_t unix_addr_len; |
| if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) |
| return ScopedPlatformHandle(); |
| |
| ScopedPlatformHandle handle = CreateUnixDomainSocket(true); |
| if (!handle.is_valid()) |
| return ScopedPlatformHandle(); |
| |
| // Bind the socket. |
| if (bind(handle.get().handle, reinterpret_cast<const sockaddr*>(&unix_addr), |
| unix_addr_len) < 0) { |
| PLOG(ERROR) << "bind " << named_handle.name; |
| return ScopedPlatformHandle(); |
| } |
| |
| // Start listening on the socket. |
| if (listen(handle.get().handle, SOMAXCONN) < 0) { |
| PLOG(ERROR) << "listen " << named_handle.name; |
| unlink(named_handle.name.c_str()); |
| return ScopedPlatformHandle(); |
| } |
| return handle; |
| } |
| |
| } // namespace edk |
| } // namespace mojo |