// Copyright (c) 2011 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 "base/posix/unix_domain_socket_linux.h" #include <errno.h> #include <sys/socket.h> #include <sys/uio.h> #include <unistd.h> #include "base/logging.h" #include "base/pickle.h" #include "base/posix/eintr_wrapper.h" #include "base/stl_util.h" const size_t UnixDomainSocket::kMaxFileDescriptors = 16; // static bool UnixDomainSocket::SendMsg(int fd, const void* buf, size_t length, const std::vector<int>& fds) { struct msghdr msg = {}; struct iovec iov = { const_cast<void*>(buf), length }; msg.msg_iov = &iov; msg.msg_iovlen = 1; char* control_buffer = NULL; if (fds.size()) { const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size()); control_buffer = new char[control_len]; struct cmsghdr* cmsg; msg.msg_control = control_buffer; msg.msg_controllen = control_len; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size()); memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size()); msg.msg_controllen = cmsg->cmsg_len; } // Avoid a SIGPIPE if the other end breaks the connection. // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by // POSIX. const int flags = MSG_NOSIGNAL; const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags)); const bool ret = static_cast<ssize_t>(length) == r; delete[] control_buffer; return ret; } // static ssize_t UnixDomainSocket::RecvMsg(int fd, void* buf, size_t length, std::vector<int>* fds) { return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds); } // static ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, void* buf, size_t length, int flags, std::vector<int>* fds) { fds->clear(); struct msghdr msg = {}; struct iovec iov = { buf, length }; msg.msg_iov = &iov; msg.msg_iovlen = 1; char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)]; msg.msg_control = control_buffer; msg.msg_controllen = sizeof(control_buffer); const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags)); if (r == -1) return -1; int* wire_fds = NULL; unsigned wire_fds_len = 0; if (msg.msg_controllen > 0) { struct cmsghdr* cmsg; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); DCHECK(payload_len % sizeof(int) == 0); wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); wire_fds_len = payload_len / sizeof(int); break; } } } if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) { for (unsigned i = 0; i < wire_fds_len; ++i) close(wire_fds[i]); errno = EMSGSIZE; return -1; } fds->resize(wire_fds_len); memcpy(vector_as_array(fds), wire_fds, sizeof(int) * wire_fds_len); return r; } // static ssize_t UnixDomainSocket::SendRecvMsg(int fd, uint8_t* reply, unsigned max_reply_len, int* result_fd, const Pickle& request) { return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len, 0, /* recvmsg_flags */ result_fd, request); } // static ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd, uint8_t* reply, unsigned max_reply_len, int recvmsg_flags, int* result_fd, const Pickle& request) { int fds[2]; // This socketpair is only used for the IPC and is cleaned up before // returning. if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == -1) return -1; std::vector<int> fd_vector; fd_vector.push_back(fds[1]); if (!SendMsg(fd, request.data(), request.size(), fd_vector)) { close(fds[0]); close(fds[1]); return -1; } close(fds[1]); fd_vector.clear(); // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the // sender might get a SIGPIPE. const ssize_t reply_len = RecvMsgWithFlags(fds[0], reply, max_reply_len, recvmsg_flags, &fd_vector); close(fds[0]); if (reply_len == -1) return -1; if ((!fd_vector.empty() && result_fd == NULL) || fd_vector.size() > 1) { for (std::vector<int>::const_iterator i = fd_vector.begin(); i != fd_vector.end(); ++i) { close(*i); } NOTREACHED(); return -1; } if (result_fd) *result_fd = fd_vector.empty() ? -1 : fd_vector[0]; return reply_len; }