Home | History | Annotate | Download | only in posix
      1 // Copyright (c) 2011 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 #include "base/posix/unix_domain_socket_linux.h"
      6 
      7 #include <errno.h>
      8 #include <sys/socket.h>
      9 #include <sys/uio.h>
     10 #include <unistd.h>
     11 
     12 #include <vector>
     13 
     14 #include "base/files/scoped_file.h"
     15 #include "base/logging.h"
     16 #include "base/memory/scoped_vector.h"
     17 #include "base/pickle.h"
     18 #include "base/posix/eintr_wrapper.h"
     19 #include "base/stl_util.h"
     20 
     21 const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
     22 
     23 // Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes
     24 // ownership of the newly allocated file descriptors to |one| and |two|.
     25 // Returns true on success.
     26 static bool CreateSocketPair(base::ScopedFD* one, base::ScopedFD* two) {
     27   int raw_socks[2];
     28   if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks) == -1)
     29     return false;
     30   one->reset(raw_socks[0]);
     31   two->reset(raw_socks[1]);
     32   return true;
     33 }
     34 
     35 // static
     36 bool UnixDomainSocket::EnableReceiveProcessId(int fd) {
     37   const int enable = 1;
     38   return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0;
     39 }
     40 
     41 // static
     42 bool UnixDomainSocket::SendMsg(int fd,
     43                                const void* buf,
     44                                size_t length,
     45                                const std::vector<int>& fds) {
     46   struct msghdr msg = {};
     47   struct iovec iov = { const_cast<void*>(buf), length };
     48   msg.msg_iov = &iov;
     49   msg.msg_iovlen = 1;
     50 
     51   char* control_buffer = NULL;
     52   if (fds.size()) {
     53     const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
     54     control_buffer = new char[control_len];
     55 
     56     struct cmsghdr* cmsg;
     57     msg.msg_control = control_buffer;
     58     msg.msg_controllen = control_len;
     59     cmsg = CMSG_FIRSTHDR(&msg);
     60     cmsg->cmsg_level = SOL_SOCKET;
     61     cmsg->cmsg_type = SCM_RIGHTS;
     62     cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
     63     memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
     64     msg.msg_controllen = cmsg->cmsg_len;
     65   }
     66 
     67   // Avoid a SIGPIPE if the other end breaks the connection.
     68   // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
     69   // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
     70   // POSIX.
     71   const int flags = MSG_NOSIGNAL;
     72   const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
     73   const bool ret = static_cast<ssize_t>(length) == r;
     74   delete[] control_buffer;
     75   return ret;
     76 }
     77 
     78 // static
     79 ssize_t UnixDomainSocket::RecvMsg(int fd,
     80                                   void* buf,
     81                                   size_t length,
     82                                   ScopedVector<base::ScopedFD>* fds) {
     83   return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, NULL);
     84 }
     85 
     86 // static
     87 ssize_t UnixDomainSocket::RecvMsgWithPid(int fd,
     88                                          void* buf,
     89                                          size_t length,
     90                                          ScopedVector<base::ScopedFD>* fds,
     91                                          base::ProcessId* pid) {
     92   return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds, pid);
     93 }
     94 
     95 // static
     96 ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
     97                                            void* buf,
     98                                            size_t length,
     99                                            int flags,
    100                                            ScopedVector<base::ScopedFD>* fds,
    101                                            base::ProcessId* out_pid) {
    102   fds->clear();
    103 
    104   struct msghdr msg = {};
    105   struct iovec iov = { buf, length };
    106   msg.msg_iov = &iov;
    107   msg.msg_iovlen = 1;
    108 
    109   char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors) +
    110                       CMSG_SPACE(sizeof(struct ucred))];
    111   msg.msg_control = control_buffer;
    112   msg.msg_controllen = sizeof(control_buffer);
    113 
    114   const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags));
    115   if (r == -1)
    116     return -1;
    117 
    118   int* wire_fds = NULL;
    119   unsigned wire_fds_len = 0;
    120   base::ProcessId pid = -1;
    121 
    122   if (msg.msg_controllen > 0) {
    123     struct cmsghdr* cmsg;
    124     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
    125       const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
    126       if (cmsg->cmsg_level == SOL_SOCKET &&
    127           cmsg->cmsg_type == SCM_RIGHTS) {
    128         DCHECK(payload_len % sizeof(int) == 0);
    129         DCHECK(wire_fds == NULL);
    130         wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
    131         wire_fds_len = payload_len / sizeof(int);
    132       }
    133       if (cmsg->cmsg_level == SOL_SOCKET &&
    134           cmsg->cmsg_type == SCM_CREDENTIALS) {
    135         DCHECK(payload_len == sizeof(struct ucred));
    136         DCHECK(pid == -1);
    137         pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid;
    138       }
    139     }
    140   }
    141 
    142   if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
    143     for (unsigned i = 0; i < wire_fds_len; ++i)
    144       close(wire_fds[i]);
    145     errno = EMSGSIZE;
    146     return -1;
    147   }
    148 
    149   if (wire_fds) {
    150     for (unsigned i = 0; i < wire_fds_len; ++i)
    151       fds->push_back(new base::ScopedFD(wire_fds[i]));
    152   }
    153 
    154   if (out_pid) {
    155     // |pid| will legitimately be -1 if we read EOF, so only DCHECK if we
    156     // actually received a message.  Unfortunately, Linux allows sending zero
    157     // length messages, which are indistinguishable from EOF, so this check
    158     // has false negatives.
    159     if (r > 0 || msg.msg_controllen > 0)
    160       DCHECK_GE(pid, 0);
    161 
    162     *out_pid = pid;
    163   }
    164 
    165   return r;
    166 }
    167 
    168 // static
    169 ssize_t UnixDomainSocket::SendRecvMsg(int fd,
    170                                       uint8_t* reply,
    171                                       unsigned max_reply_len,
    172                                       int* result_fd,
    173                                       const Pickle& request) {
    174   return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len,
    175                                                 0,  /* recvmsg_flags */
    176                                                 result_fd, request);
    177 }
    178 
    179 // static
    180 ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd,
    181                                                uint8_t* reply,
    182                                                unsigned max_reply_len,
    183                                                int recvmsg_flags,
    184                                                int* result_fd,
    185                                                const Pickle& request) {
    186   // This socketpair is only used for the IPC and is cleaned up before
    187   // returning.
    188   base::ScopedFD recv_sock, send_sock;
    189   if (!CreateSocketPair(&recv_sock, &send_sock))
    190     return -1;
    191 
    192   {
    193     std::vector<int> send_fds;
    194     send_fds.push_back(send_sock.get());
    195     if (!SendMsg(fd, request.data(), request.size(), send_fds))
    196       return -1;
    197   }
    198 
    199   // Close the sending end of the socket right away so that if our peer closes
    200   // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
    201   // return EOF instead of hanging.
    202   send_sock.reset();
    203 
    204   ScopedVector<base::ScopedFD> recv_fds;
    205   // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
    206   // sender might get a SIGPIPE.
    207   const ssize_t reply_len = RecvMsgWithFlags(
    208       recv_sock.get(), reply, max_reply_len, recvmsg_flags, &recv_fds, NULL);
    209   recv_sock.reset();
    210   if (reply_len == -1)
    211     return -1;
    212 
    213   // If we received more file descriptors than caller expected, then we treat
    214   // that as an error.
    215   if (recv_fds.size() > (result_fd != NULL ? 1 : 0)) {
    216     NOTREACHED();
    217     return -1;
    218   }
    219 
    220   if (result_fd)
    221     *result_fd = recv_fds.empty() ? -1 : recv_fds[0]->release();
    222 
    223   return reply_len;
    224 }
    225