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 "base/logging.h"
     13 #include "base/pickle.h"
     14 #include "base/posix/eintr_wrapper.h"
     15 #include "base/stl_util.h"
     16 
     17 const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
     18 
     19 // static
     20 bool UnixDomainSocket::SendMsg(int fd,
     21                                const void* buf,
     22                                size_t length,
     23                                const std::vector<int>& fds) {
     24   struct msghdr msg = {};
     25   struct iovec iov = { const_cast<void*>(buf), length };
     26   msg.msg_iov = &iov;
     27   msg.msg_iovlen = 1;
     28 
     29   char* control_buffer = NULL;
     30   if (fds.size()) {
     31     const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
     32     control_buffer = new char[control_len];
     33 
     34     struct cmsghdr* cmsg;
     35     msg.msg_control = control_buffer;
     36     msg.msg_controllen = control_len;
     37     cmsg = CMSG_FIRSTHDR(&msg);
     38     cmsg->cmsg_level = SOL_SOCKET;
     39     cmsg->cmsg_type = SCM_RIGHTS;
     40     cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
     41     memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
     42     msg.msg_controllen = cmsg->cmsg_len;
     43   }
     44 
     45   // Avoid a SIGPIPE if the other end breaks the connection.
     46   // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
     47   // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
     48   // POSIX.
     49   const int flags = MSG_NOSIGNAL;
     50   const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
     51   const bool ret = static_cast<ssize_t>(length) == r;
     52   delete[] control_buffer;
     53   return ret;
     54 }
     55 
     56 // static
     57 ssize_t UnixDomainSocket::RecvMsg(int fd,
     58                                   void* buf,
     59                                   size_t length,
     60                                   std::vector<int>* fds) {
     61   return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds);
     62 }
     63 
     64 // static
     65 ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
     66                                            void* buf,
     67                                            size_t length,
     68                                            int flags,
     69                                            std::vector<int>* fds) {
     70   fds->clear();
     71 
     72   struct msghdr msg = {};
     73   struct iovec iov = { buf, length };
     74   msg.msg_iov = &iov;
     75   msg.msg_iovlen = 1;
     76 
     77   char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)];
     78   msg.msg_control = control_buffer;
     79   msg.msg_controllen = sizeof(control_buffer);
     80 
     81   const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags));
     82   if (r == -1)
     83     return -1;
     84 
     85   int* wire_fds = NULL;
     86   unsigned wire_fds_len = 0;
     87 
     88   if (msg.msg_controllen > 0) {
     89     struct cmsghdr* cmsg;
     90     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
     91       if (cmsg->cmsg_level == SOL_SOCKET &&
     92           cmsg->cmsg_type == SCM_RIGHTS) {
     93         const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
     94         DCHECK(payload_len % sizeof(int) == 0);
     95         wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
     96         wire_fds_len = payload_len / sizeof(int);
     97         break;
     98       }
     99     }
    100   }
    101 
    102   if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
    103     for (unsigned i = 0; i < wire_fds_len; ++i)
    104       close(wire_fds[i]);
    105     errno = EMSGSIZE;
    106     return -1;
    107   }
    108 
    109   fds->resize(wire_fds_len);
    110   memcpy(vector_as_array(fds), wire_fds, sizeof(int) * wire_fds_len);
    111 
    112   return r;
    113 }
    114 
    115 // static
    116 ssize_t UnixDomainSocket::SendRecvMsg(int fd,
    117                                       uint8_t* reply,
    118                                       unsigned max_reply_len,
    119                                       int* result_fd,
    120                                       const Pickle& request) {
    121   return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len,
    122                                                 0,  /* recvmsg_flags */
    123                                                 result_fd, request);
    124 }
    125 
    126 // static
    127 ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd,
    128                                                uint8_t* reply,
    129                                                unsigned max_reply_len,
    130                                                int recvmsg_flags,
    131                                                int* result_fd,
    132                                                const Pickle& request) {
    133   int fds[2];
    134 
    135   // This socketpair is only used for the IPC and is cleaned up before
    136   // returning.
    137   if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == -1)
    138     return -1;
    139 
    140   std::vector<int> fd_vector;
    141   fd_vector.push_back(fds[1]);
    142   if (!SendMsg(fd, request.data(), request.size(), fd_vector)) {
    143     close(fds[0]);
    144     close(fds[1]);
    145     return -1;
    146   }
    147   close(fds[1]);
    148 
    149   fd_vector.clear();
    150   // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
    151   // sender might get a SIGPIPE.
    152   const ssize_t reply_len = RecvMsgWithFlags(fds[0], reply, max_reply_len,
    153                                              recvmsg_flags, &fd_vector);
    154   close(fds[0]);
    155   if (reply_len == -1)
    156     return -1;
    157 
    158   if ((!fd_vector.empty() && result_fd == NULL) || fd_vector.size() > 1) {
    159     for (std::vector<int>::const_iterator
    160          i = fd_vector.begin(); i != fd_vector.end(); ++i) {
    161       close(*i);
    162     }
    163 
    164     NOTREACHED();
    165 
    166     return -1;
    167   }
    168 
    169   if (result_fd)
    170     *result_fd = fd_vector.empty() ? -1 : fd_vector[0];
    171 
    172   return reply_len;
    173 }
    174