Warning, file /sdk/dferry/transport/localsocket.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002    Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #include "localsocket.h"
0025 
0026 #include <errno.h>
0027 #include <fcntl.h>
0028 #include <sys/ioctl.h>
0029 #include <sys/socket.h>
0030 #include "sys/uio.h"
0031 #include <sys/un.h>
0032 #include <unistd.h>
0033 
0034 #include <cassert>
0035 #include <climits>
0036 #include <cstdlib>
0037 #include <cstring>
0038 
0039 enum {
0040     // ### This is configurable in libdbus-1 but nobody ever seems to change it from the default of 16.
0041     MaxFds = 16,
0042     MaxFdPayloadSize = MaxFds * sizeof(int)
0043 };
0044 
0045 LocalSocket::LocalSocket(const std::string &socketFilePath)
0046    : m_fd(-1)
0047 {
0048     m_supportedUnixFdsCount = MaxFds;
0049     const int fd = socket(PF_UNIX, SOCK_STREAM, 0);
0050     if (fd < 0) {
0051         return;
0052     }
0053     // don't let forks inherit the file descriptor - that can cause confusion...
0054     fcntl(fd, F_SETFD, FD_CLOEXEC);
0055 
0056     struct sockaddr_un addr;
0057     addr.sun_family = PF_UNIX;
0058     bool ok = socketFilePath.length() + 1 <= sizeof(addr.sun_path);
0059     if (ok) {
0060         memcpy(addr.sun_path, socketFilePath.c_str(), socketFilePath.length() + 1);
0061     }
0062 
0063     ok = ok && (connect(fd, (struct sockaddr *)&addr, sizeof(sa_family_t) + socketFilePath.length()) == 0);
0064 
0065     if (ok) {
0066         m_fd = fd;
0067     } else {
0068         ::close(fd);
0069     }
0070 }
0071 
0072 LocalSocket::LocalSocket(int fd)
0073    : m_fd(fd)
0074 {
0075 }
0076 
0077 LocalSocket::~LocalSocket()
0078 {
0079     close();
0080 }
0081 
0082 void LocalSocket::platformClose()
0083 {
0084     if (m_fd >= 0) {
0085         ::close(m_fd);
0086         m_fd = -1;
0087     }
0088 }
0089 
0090 IO::Result LocalSocket::write(chunk data)
0091 {
0092     IO::Result ret;
0093     if (data.length == 0) {
0094         return ret;
0095     }
0096     if (m_fd < 0) {
0097         ret.status = IO::Status::InternalError;
0098         return ret;
0099     }
0100 
0101     const uint32 initialLength = data.length;
0102 
0103     while (data.length > 0) {
0104         ssize_t nbytes = send(m_fd, data.ptr, data.length, MSG_DONTWAIT | MSG_NOSIGNAL);
0105         if (nbytes < 0) {
0106             if (errno == EINTR) {
0107                 continue;
0108             }
0109             // see EAGAIN comment in read()
0110             if (errno == EAGAIN || errno == EWOULDBLOCK) {
0111                 break;
0112             }
0113             close();
0114             ret.status = IO::Status::RemoteClosed;
0115             return ret;
0116         } else if (nbytes == 0) {
0117             break;
0118         }
0119 
0120         data.ptr += nbytes;
0121         data.length -= size_t(nbytes);
0122     }
0123 
0124     ret.length = initialLength - data.length;
0125     return ret;
0126 }
0127 
0128 // TODO: consider using iovec to avoid "copying together" message parts before sending; iovec tricks
0129 // are probably not going to help for receiving, though.
0130 IO::Result LocalSocket::writeWithFileDescriptors(chunk data, const std::vector<int> &fileDescriptors)
0131 {
0132     IO::Result ret;
0133     if (data.length == 0) {
0134         return ret;
0135     }
0136     if (m_fd < 0) {
0137         ret.status = IO::Status::InternalError;
0138         return ret;
0139     }
0140 
0141     // sendmsg  boilerplate
0142     struct msghdr send_msg;
0143     struct iovec iov;
0144 
0145     send_msg.msg_name = nullptr;
0146     send_msg.msg_namelen = 0;
0147     send_msg.msg_flags = 0;
0148     send_msg.msg_iov = &iov;
0149     send_msg.msg_iovlen = 1;
0150 
0151     iov.iov_base = data.ptr;
0152     iov.iov_len = data.length;
0153 
0154     // we can only send a fixed number of fds anyway due to the non-flexible size of the control message
0155     // receive buffer, so we set an arbitrary limit.
0156     const uint32 numFds = fileDescriptors.size();
0157     if (fileDescriptors.size() > MaxFds) {
0158         // TODO allow a proper error return
0159         close();
0160         ret.status = IO::Status::InternalError;
0161         return ret;
0162     }
0163 
0164     char cmsgBuf[CMSG_SPACE(MaxFdPayloadSize)];
0165     const uint32 fdPayloadSize = numFds * sizeof(int);
0166 
0167     if (numFds) {
0168         // fill in a control message
0169         send_msg.msg_control = cmsgBuf;
0170         send_msg.msg_controllen = CMSG_SPACE(fdPayloadSize);
0171 
0172         struct cmsghdr *c_msg = CMSG_FIRSTHDR(&send_msg);
0173         c_msg->cmsg_len = CMSG_LEN(fdPayloadSize);
0174         c_msg->cmsg_level = SOL_SOCKET;
0175         c_msg->cmsg_type = SCM_RIGHTS;
0176 
0177         // set the control data to pass - this is why we don't use the simpler write()
0178         int *const fdPayload = reinterpret_cast<int *>(CMSG_DATA(c_msg));
0179         for (uint32 i = 0; i < numFds; i++) {
0180             fdPayload[i] = fileDescriptors[i];
0181         }
0182     } else {
0183         // no file descriptor to send, no control message
0184         send_msg.msg_control = nullptr;
0185         send_msg.msg_controllen = 0;
0186     }
0187 
0188     while (iov.iov_len > 0) {
0189         ssize_t nbytes = sendmsg(m_fd, &send_msg, MSG_DONTWAIT);
0190         if (nbytes < 0) {
0191             if (errno == EINTR) {
0192                 continue;
0193             }
0194             // see EAGAIN comment in read()
0195             if (errno == EAGAIN || errno == EWOULDBLOCK) {
0196                 break;
0197             }
0198             close();
0199             ret.status = IO::Status::RemoteClosed;
0200             break;
0201         } else if (nbytes == 0) {
0202             break;
0203         } else if (nbytes > 0) {
0204             // control message already sent, don't send again
0205             send_msg.msg_control = nullptr;
0206             send_msg.msg_controllen = 0;
0207         }
0208 
0209         iov.iov_base = static_cast<char *>(iov.iov_base) + nbytes;
0210         iov.iov_len -= size_t(nbytes);
0211     }
0212 
0213     ret.length = data.length - iov.iov_len;
0214     return ret;
0215 }
0216 
0217 IO::Result LocalSocket::read(byte *buffer, uint32 maxSize)
0218 {
0219     IO::Result ret;
0220     if (maxSize == 0) {
0221         return ret;
0222     }
0223     if (m_fd < 0) {
0224         ret.status = IO::Status::InternalError;
0225         return ret;
0226     }
0227 
0228     while (ret.length < maxSize) {
0229         ssize_t nbytes = recv(m_fd, buffer + ret.length, maxSize - ret.length, MSG_DONTWAIT);
0230         if (nbytes < 0) {
0231             if (errno == EINTR) {
0232                 continue;
0233             }
0234             // If we were notified for reading directly by the event dispatcher, we must be able to read at
0235             // least one byte before getting AGAIN aka EWOULDBLOCK - *however* the event loop might notify
0236             // something that tries to read everything (like Message::notifyRead()...) by calling read()
0237             // in a loop, and in that case, we may be called in an attempt to read more when there is
0238             // currently no more data, and it's not an error.
0239             // Just return zero bytes and no error in that case.
0240             if (errno == EAGAIN || errno == EWOULDBLOCK) {
0241                 break;
0242             }
0243             close();
0244             ret.status = IO::Status::RemoteClosed;
0245             break;
0246         } else if (nbytes == 0) {
0247             // orderly shutdown
0248             close();
0249             ret.status = IO::Status::RemoteClosed;
0250             return ret;
0251         }
0252         ret.length += size_t(nbytes);
0253     }
0254 
0255     return ret;
0256 }
0257 
0258 IO::Result LocalSocket::readWithFileDescriptors(byte *buffer, uint32 maxSize,
0259                                                 std::vector<int> *fileDescriptors)
0260 {
0261     IO::Result ret;
0262     if (maxSize == 0) {
0263         return ret;
0264     }
0265     if (m_fd < 0) {
0266         ret.status = IO::Status::InternalError;
0267         return ret;
0268     }
0269 
0270     // recvmsg-with-control-message boilerplate
0271     struct msghdr recv_msg;
0272     char cmsgBuf[CMSG_SPACE(sizeof(int) * MaxFds)];
0273 
0274     recv_msg.msg_control = cmsgBuf;
0275     recv_msg.msg_controllen = CMSG_SPACE(MaxFdPayloadSize);
0276     memset(cmsgBuf, 0, recv_msg.msg_controllen);
0277     // prevent equivalent to CVE-2014-3635 in libdbus-1: We could receive and ignore an extra file
0278     // descriptor, thus eventually run out of file descriptors
0279     recv_msg.msg_controllen = CMSG_LEN(MaxFdPayloadSize);
0280     recv_msg.msg_name = nullptr;
0281     recv_msg.msg_namelen = 0;
0282     recv_msg.msg_flags = 0;
0283 
0284     struct iovec iov;
0285     recv_msg.msg_iov = &iov;
0286     recv_msg.msg_iovlen = 1;
0287 
0288     // end boilerplate
0289 
0290     iov.iov_base = buffer;
0291     iov.iov_len = maxSize;
0292     while (iov.iov_len > 0) {
0293         ssize_t nbytes = recvmsg(m_fd, &recv_msg, MSG_DONTWAIT);
0294         if (nbytes < 0) {
0295             if (errno == EINTR) {
0296                 continue;
0297             }
0298             // see comment in read()
0299             if (errno == EAGAIN || errno == EWOULDBLOCK) {
0300                 break;
0301             }
0302             close();
0303             ret.status = IO::Status::RemoteClosed;
0304             break;
0305         }  else if (nbytes == 0) {
0306             // orderly shutdown
0307             close();
0308             ret.status = IO::Status::RemoteClosed;
0309             break;
0310         } else {
0311             // read any file descriptors passed via control messages
0312 
0313             struct cmsghdr *c_msg = CMSG_FIRSTHDR(&recv_msg);
0314             if (c_msg && c_msg->cmsg_level == SOL_SOCKET && c_msg->cmsg_type == SCM_RIGHTS) {
0315                 const int count = (c_msg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
0316                 const int *const fdPayload = reinterpret_cast<int *>(CMSG_DATA(c_msg));
0317                 for (int i = 0; i < count; i++) {
0318                     fileDescriptors->push_back(fdPayload[i]);
0319                 }
0320             }
0321 
0322             // control message already received, don't receive another
0323             recv_msg.msg_control = nullptr;
0324             recv_msg.msg_controllen = 0;
0325         }
0326 
0327         ret.length += size_t(nbytes);
0328         iov.iov_base = static_cast<char *>(iov.iov_base) + nbytes;
0329         iov.iov_len -= size_t(nbytes);
0330     }
0331 
0332     return ret;
0333 }
0334 
0335 bool LocalSocket::isOpen()
0336 {
0337     return m_fd != -1;
0338 }
0339 
0340 int LocalSocket::fileDescriptor() const
0341 {
0342     return m_fd;
0343 }