TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / network / fd_socket_transmission.cpp
1 /*
2  * Copyright © 2014 Canonical Ltd.
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License version 3,
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17  */
18
19 #include "anbox/network/fd_socket_transmission.h"
20 #include "anbox/common/variable_length_array.h"
21
22 #include <string.h>
23 #include <sys/socket.h>
24 #include <sys/types.h>
25 #include <boost/exception/errinfo_errno.hpp>
26 #include <boost/throw_exception.hpp>
27 #include <stdexcept>
28
29 namespace anbox {
30 socket_error::socket_error(std::string const& message)
31     : std::system_error(errno, std::system_category(), message) {}
32
33 socket_disconnected_error::socket_disconnected_error(std::string const& message)
34     : std::system_error(errno, std::system_category(), message) {}
35
36 fd_reception_error::fd_reception_error(std::string const& message)
37     : std::runtime_error(message) {}
38
39 void send_fds(Fd const& socket, std::vector<Fd> const& fds) {
40   if (fds.size() > 0) {
41     // We send dummy data
42     struct iovec iov;
43     char dummy_iov_data = 'M';
44     iov.iov_base = &dummy_iov_data;
45     iov.iov_len = 1;
46
47     // Allocate space for control message
48     static auto const builtin_n_fds = 5;
49     static auto const builtin_cmsg_space =
50         CMSG_SPACE(builtin_n_fds * sizeof(int));
51     auto const fds_bytes = fds.size() * sizeof(int);
52     VariableLengthArray<builtin_cmsg_space> control{CMSG_SPACE(fds_bytes)};
53     // Silence valgrind uninitialized memory complaint
54     memset(control.data(), 0, control.size());
55
56     // Message to send
57     struct msghdr header;
58     header.msg_name = NULL;
59     header.msg_namelen = 0;
60     header.msg_iov = &iov;
61     header.msg_iovlen = 1;
62     header.msg_controllen = control.size();
63     header.msg_control = control.data();
64     header.msg_flags = 0;
65
66     // Control message contains file descriptors
67     struct cmsghdr* message = CMSG_FIRSTHDR(&header);
68     message->cmsg_len = CMSG_LEN(fds_bytes);
69     message->cmsg_level = SOL_SOCKET;
70     message->cmsg_type = SCM_RIGHTS;
71
72     int* const data = reinterpret_cast<int*>(CMSG_DATA(message));
73     int i = 0;
74     for (auto& fd : fds) data[i++] = fd;
75
76     auto const sent = sendmsg(socket, &header, MSG_NOSIGNAL);
77     if (sent < 0)
78       BOOST_THROW_EXCEPTION(std::runtime_error("Failed to send fds: " +
79                                                std::string(strerror(errno))));
80   }
81 }
82
83 bool socket_error_is_transient(int error_code) { return (error_code == EINTR); }
84
85 void receive_data(Fd const& socket, void* buffer, size_t bytes_requested,
86                   std::vector<Fd>& fds) {
87   if (bytes_requested == 0)
88     BOOST_THROW_EXCEPTION(std::logic_error("Attempted to receive 0 bytes"));
89
90   size_t bytes_read{0};
91   unsigned fds_read{0};
92   while (bytes_read < bytes_requested) {
93     // Store the data in the buffer requested
94     struct iovec iov;
95     iov.iov_base = static_cast<uint8_t*>(buffer) + bytes_read;
96     iov.iov_len = bytes_requested - bytes_read;
97
98     // Allocate space for control message
99     static auto const builtin_n_fds = 5;
100     static auto const builtin_cmsg_space =
101         CMSG_SPACE(builtin_n_fds * sizeof(int));
102     auto const fds_bytes = (fds.size() - fds_read) * sizeof(int);
103     VariableLengthArray<builtin_cmsg_space> control{CMSG_SPACE(fds_bytes)};
104
105     // Message to read
106     struct msghdr header;
107     header.msg_name = NULL;
108     header.msg_namelen = 0;
109     header.msg_iov = &iov;
110     header.msg_iovlen = 1;
111     header.msg_controllen = control.size();
112     header.msg_control = control.data();
113     header.msg_flags = 0;
114
115     ssize_t const result = recvmsg(socket, &header, MSG_NOSIGNAL | MSG_WAITALL);
116     if (result == 0)
117       BOOST_THROW_EXCEPTION(socket_disconnected_error(
118           "Failed to read message from server: server has shutdown"));
119     if (result < 0) {
120       if (socket_error_is_transient(errno)) continue;
121       if (errno == EAGAIN) continue;
122       if (errno == EPIPE)
123         BOOST_THROW_EXCEPTION(
124             boost::enable_error_info(
125                 socket_disconnected_error("Failed to read message from server"))
126             << boost::errinfo_errno(errno));
127
128       BOOST_THROW_EXCEPTION(boost::enable_error_info(socket_error(
129                                 "Failed to read message from server"))
130                             << boost::errinfo_errno(errno));
131     }
132
133     bytes_read += result;
134
135     // If we get a proper control message, copy the received
136     // file descriptors back to the caller
137     struct cmsghdr const* const cmsg = CMSG_FIRSTHDR(&header);
138     if (cmsg) {
139       if ((cmsg->cmsg_level == SOL_SOCKET) &&
140           (cmsg->cmsg_type == SCM_CREDENTIALS))
141         BOOST_THROW_EXCEPTION(
142             fd_reception_error("received SCM_CREDENTIALS when expecting fd"));
143       if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
144         BOOST_THROW_EXCEPTION(fd_reception_error(
145             "Invalid control message for receiving file descriptors"));
146
147       int const* const data = reinterpret_cast<int const*> CMSG_DATA(cmsg);
148       ptrdiff_t const header_size = reinterpret_cast<char const*>(data) -
149                                     reinterpret_cast<char const*>(cmsg);
150       int const nfds = (cmsg->cmsg_len - header_size) / sizeof(int);
151
152       // NOTE: This relies on the file descriptor cmsg being read
153       // (and written) atomically.
154       if (cmsg->cmsg_len > CMSG_LEN(fds_bytes) ||
155           (header.msg_flags & MSG_CTRUNC)) {
156         for (int i = 0; i < nfds; i++) ::close(data[i]);
157         BOOST_THROW_EXCEPTION(
158             std::runtime_error("Received more fds than expected"));
159       }
160
161       // We can't properly pass Fds through google::protobuf::Message,
162       // which is where these get shoved.
163       //
164       // When we have our own RPC generator plugin and aren't using deprecated
165       // Protobuf features this can go away.
166       for (int i = 0; i < nfds; i++)
167         fds[fds_read + i] = Fd{IntOwnedFd{data[i]}};
168
169       fds_read += nfds;
170     }
171   }
172
173   if (fds_read < fds.size()) {
174     for (auto fd : fds)
175       if (fd >= 0) ::close(fd);
176     fds.clear();
177     BOOST_THROW_EXCEPTION(
178         std::runtime_error("Received fewer fds than expected"));
179   }
180 }
181 }  // namespace anbox