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 }