xref: /aosp_15_r20/external/pigweed/pw_stream/socket_stream.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_stream/socket_stream.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #if defined(_WIN32) && _WIN32
18*61c4878aSAndroid Build Coastguard Worker #include <fcntl.h>
19*61c4878aSAndroid Build Coastguard Worker #include <io.h>
20*61c4878aSAndroid Build Coastguard Worker #include <winsock2.h>
21*61c4878aSAndroid Build Coastguard Worker #include <ws2tcpip.h>
22*61c4878aSAndroid Build Coastguard Worker #define SHUT_RDWR SD_BOTH
23*61c4878aSAndroid Build Coastguard Worker #else
24*61c4878aSAndroid Build Coastguard Worker #include <arpa/inet.h>
25*61c4878aSAndroid Build Coastguard Worker #include <netdb.h>
26*61c4878aSAndroid Build Coastguard Worker #include <netinet/in.h>
27*61c4878aSAndroid Build Coastguard Worker #include <poll.h>
28*61c4878aSAndroid Build Coastguard Worker #include <sys/socket.h>
29*61c4878aSAndroid Build Coastguard Worker #include <sys/types.h>
30*61c4878aSAndroid Build Coastguard Worker #include <unistd.h>
31*61c4878aSAndroid Build Coastguard Worker #endif  // defined(_WIN32) && _WIN32
32*61c4878aSAndroid Build Coastguard Worker 
33*61c4878aSAndroid Build Coastguard Worker #include <cerrno>
34*61c4878aSAndroid Build Coastguard Worker #include <cstring>
35*61c4878aSAndroid Build Coastguard Worker 
36*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
37*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
38*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
39*61c4878aSAndroid Build Coastguard Worker #include "pw_string/to_string.h"
40*61c4878aSAndroid Build Coastguard Worker 
41*61c4878aSAndroid Build Coastguard Worker namespace pw::stream {
42*61c4878aSAndroid Build Coastguard Worker namespace {
43*61c4878aSAndroid Build Coastguard Worker 
44*61c4878aSAndroid Build Coastguard Worker constexpr uint32_t kServerBacklogLength = 1;
45*61c4878aSAndroid Build Coastguard Worker constexpr const char* kLocalhostAddress = "localhost";
46*61c4878aSAndroid Build Coastguard Worker 
47*61c4878aSAndroid Build Coastguard Worker // Set necessary options on a socket file descriptor.
ConfigureSocket(int socket)48*61c4878aSAndroid Build Coastguard Worker void ConfigureSocket([[maybe_unused]] int socket) {
49*61c4878aSAndroid Build Coastguard Worker #if defined(__APPLE__)
50*61c4878aSAndroid Build Coastguard Worker   // Use SO_NOSIGPIPE to avoid getting a SIGPIPE signal when the remote peer
51*61c4878aSAndroid Build Coastguard Worker   // drops the connection. This is supported on macOS only.
52*61c4878aSAndroid Build Coastguard Worker   constexpr int value = 1;
53*61c4878aSAndroid Build Coastguard Worker   if (setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(int)) < 0) {
54*61c4878aSAndroid Build Coastguard Worker     PW_LOG_WARN("Failed to set SO_NOSIGPIPE: %s", std::strerror(errno));
55*61c4878aSAndroid Build Coastguard Worker   }
56*61c4878aSAndroid Build Coastguard Worker #endif  // defined(__APPLE__)
57*61c4878aSAndroid Build Coastguard Worker }
58*61c4878aSAndroid Build Coastguard Worker 
59*61c4878aSAndroid Build Coastguard Worker #if defined(_WIN32) && _WIN32
close(SOCKET s)60*61c4878aSAndroid Build Coastguard Worker int close(SOCKET s) { return closesocket(s); }
61*61c4878aSAndroid Build Coastguard Worker 
write(int fd,const void * buf,size_t count)62*61c4878aSAndroid Build Coastguard Worker ssize_t write(int fd, const void* buf, size_t count) {
63*61c4878aSAndroid Build Coastguard Worker   return _write(fd, buf, count);
64*61c4878aSAndroid Build Coastguard Worker }
65*61c4878aSAndroid Build Coastguard Worker 
poll(struct pollfd * fds,unsigned int nfds,int timeout)66*61c4878aSAndroid Build Coastguard Worker int poll(struct pollfd* fds, unsigned int nfds, int timeout) {
67*61c4878aSAndroid Build Coastguard Worker   return WSAPoll(fds, nfds, timeout);
68*61c4878aSAndroid Build Coastguard Worker }
69*61c4878aSAndroid Build Coastguard Worker 
pipe(int pipefd[2])70*61c4878aSAndroid Build Coastguard Worker int pipe(int pipefd[2]) { return _pipe(pipefd, 256, O_BINARY); }
71*61c4878aSAndroid Build Coastguard Worker 
setsockopt(int fd,int level,int optname,const void * optval,unsigned int optlen)72*61c4878aSAndroid Build Coastguard Worker int setsockopt(
73*61c4878aSAndroid Build Coastguard Worker     int fd, int level, int optname, const void* optval, unsigned int optlen) {
74*61c4878aSAndroid Build Coastguard Worker   return setsockopt(static_cast<SOCKET>(fd),
75*61c4878aSAndroid Build Coastguard Worker                     level,
76*61c4878aSAndroid Build Coastguard Worker                     optname,
77*61c4878aSAndroid Build Coastguard Worker                     static_cast<const char*>(optval),
78*61c4878aSAndroid Build Coastguard Worker                     static_cast<int>(optlen));
79*61c4878aSAndroid Build Coastguard Worker }
80*61c4878aSAndroid Build Coastguard Worker 
81*61c4878aSAndroid Build Coastguard Worker class WinsockInitializer {
82*61c4878aSAndroid Build Coastguard Worker  public:
WinsockInitializer()83*61c4878aSAndroid Build Coastguard Worker   WinsockInitializer() {
84*61c4878aSAndroid Build Coastguard Worker     WSADATA data = {};
85*61c4878aSAndroid Build Coastguard Worker     PW_CHECK_INT_EQ(
86*61c4878aSAndroid Build Coastguard Worker         WSAStartup(MAKEWORD(2, 2), &data), 0, "Failed to initialize winsock");
87*61c4878aSAndroid Build Coastguard Worker   }
~WinsockInitializer()88*61c4878aSAndroid Build Coastguard Worker   ~WinsockInitializer() {
89*61c4878aSAndroid Build Coastguard Worker     // TODO: b/301545011 - This currently fails, probably a cleanup race.
90*61c4878aSAndroid Build Coastguard Worker     WSACleanup();
91*61c4878aSAndroid Build Coastguard Worker   }
92*61c4878aSAndroid Build Coastguard Worker };
93*61c4878aSAndroid Build Coastguard Worker 
94*61c4878aSAndroid Build Coastguard Worker [[maybe_unused]] WinsockInitializer initializer;
95*61c4878aSAndroid Build Coastguard Worker 
96*61c4878aSAndroid Build Coastguard Worker #endif  // defined(_WIN32) && _WIN32
97*61c4878aSAndroid Build Coastguard Worker 
98*61c4878aSAndroid Build Coastguard Worker }  // namespace
99*61c4878aSAndroid Build Coastguard Worker 
Connect(const char * host,uint16_t port)100*61c4878aSAndroid Build Coastguard Worker Status SocketStream::SocketStream::Connect(const char* host, uint16_t port) {
101*61c4878aSAndroid Build Coastguard Worker   if (host == nullptr) {
102*61c4878aSAndroid Build Coastguard Worker     host = kLocalhostAddress;
103*61c4878aSAndroid Build Coastguard Worker   }
104*61c4878aSAndroid Build Coastguard Worker 
105*61c4878aSAndroid Build Coastguard Worker   struct addrinfo hints = {};
106*61c4878aSAndroid Build Coastguard Worker   struct addrinfo* res;
107*61c4878aSAndroid Build Coastguard Worker   char port_buffer[6];
108*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(ToString(port, port_buffer).ok());
109*61c4878aSAndroid Build Coastguard Worker   hints.ai_family = AF_UNSPEC;
110*61c4878aSAndroid Build Coastguard Worker   hints.ai_socktype = SOCK_STREAM;
111*61c4878aSAndroid Build Coastguard Worker   hints.ai_flags = AI_NUMERICSERV;
112*61c4878aSAndroid Build Coastguard Worker   if (getaddrinfo(host, port_buffer, &hints, &res) != 0) {
113*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to configure connection address for socket");
114*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
115*61c4878aSAndroid Build Coastguard Worker   }
116*61c4878aSAndroid Build Coastguard Worker 
117*61c4878aSAndroid Build Coastguard Worker   struct addrinfo* rp;
118*61c4878aSAndroid Build Coastguard Worker   int connection_fd;
119*61c4878aSAndroid Build Coastguard Worker   for (rp = res; rp != nullptr; rp = rp->ai_next) {
120*61c4878aSAndroid Build Coastguard Worker     connection_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
121*61c4878aSAndroid Build Coastguard Worker     if (connection_fd != kInvalidFd) {
122*61c4878aSAndroid Build Coastguard Worker       break;
123*61c4878aSAndroid Build Coastguard Worker     }
124*61c4878aSAndroid Build Coastguard Worker   }
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker   if (connection_fd == kInvalidFd) {
127*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to create a socket: %s", std::strerror(errno));
128*61c4878aSAndroid Build Coastguard Worker     freeaddrinfo(res);
129*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
130*61c4878aSAndroid Build Coastguard Worker   }
131*61c4878aSAndroid Build Coastguard Worker 
132*61c4878aSAndroid Build Coastguard Worker   ConfigureSocket(connection_fd);
133*61c4878aSAndroid Build Coastguard Worker   if (connect(connection_fd, rp->ai_addr, rp->ai_addrlen) == -1) {
134*61c4878aSAndroid Build Coastguard Worker     close(connection_fd);
135*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR(
136*61c4878aSAndroid Build Coastguard Worker         "Failed to connect to %s:%d: %s", host, port, std::strerror(errno));
137*61c4878aSAndroid Build Coastguard Worker     freeaddrinfo(res);
138*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
139*61c4878aSAndroid Build Coastguard Worker   }
140*61c4878aSAndroid Build Coastguard Worker 
141*61c4878aSAndroid Build Coastguard Worker   // Mark as ready and take ownership of the connection by this object.
142*61c4878aSAndroid Build Coastguard Worker   {
143*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(connection_mutex_);
144*61c4878aSAndroid Build Coastguard Worker     connection_fd_ = connection_fd;
145*61c4878aSAndroid Build Coastguard Worker     TakeConnectionWithLockHeld();
146*61c4878aSAndroid Build Coastguard Worker     ready_ = true;
147*61c4878aSAndroid Build Coastguard Worker   }
148*61c4878aSAndroid Build Coastguard Worker 
149*61c4878aSAndroid Build Coastguard Worker   freeaddrinfo(res);
150*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
151*61c4878aSAndroid Build Coastguard Worker }
152*61c4878aSAndroid Build Coastguard Worker 
153*61c4878aSAndroid Build Coastguard Worker // Configures socket options.
SetSockOpt(int level,int optname,const void * optval,unsigned int optlen)154*61c4878aSAndroid Build Coastguard Worker int SocketStream::SetSockOpt(int level,
155*61c4878aSAndroid Build Coastguard Worker                              int optname,
156*61c4878aSAndroid Build Coastguard Worker                              const void* optval,
157*61c4878aSAndroid Build Coastguard Worker                              unsigned int optlen) {
158*61c4878aSAndroid Build Coastguard Worker   ConnectionOwnership ownership(this);
159*61c4878aSAndroid Build Coastguard Worker   if (ownership.fd() == kInvalidFd) {
160*61c4878aSAndroid Build Coastguard Worker     return EBADF;
161*61c4878aSAndroid Build Coastguard Worker   }
162*61c4878aSAndroid Build Coastguard Worker   return setsockopt(ownership.fd(), level, optname, optval, optlen);
163*61c4878aSAndroid Build Coastguard Worker }
164*61c4878aSAndroid Build Coastguard Worker 
Close()165*61c4878aSAndroid Build Coastguard Worker void SocketStream::Close() {
166*61c4878aSAndroid Build Coastguard Worker   ConnectionOwnership ownership(this);
167*61c4878aSAndroid Build Coastguard Worker   {
168*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(connection_mutex_);
169*61c4878aSAndroid Build Coastguard Worker     if (ready_) {
170*61c4878aSAndroid Build Coastguard Worker       // Shutdown the connection and send tear down notification to unblock any
171*61c4878aSAndroid Build Coastguard Worker       // waiters.
172*61c4878aSAndroid Build Coastguard Worker       if (connection_fd_ != kInvalidFd) {
173*61c4878aSAndroid Build Coastguard Worker         shutdown(connection_fd_, SHUT_RDWR);
174*61c4878aSAndroid Build Coastguard Worker       }
175*61c4878aSAndroid Build Coastguard Worker       if (connection_pipe_w_fd_ != kInvalidFd) {
176*61c4878aSAndroid Build Coastguard Worker         write(connection_pipe_w_fd_, "T", 1);
177*61c4878aSAndroid Build Coastguard Worker       }
178*61c4878aSAndroid Build Coastguard Worker 
179*61c4878aSAndroid Build Coastguard Worker       // Release ownership of the connection by this object and mark as no
180*61c4878aSAndroid Build Coastguard Worker       // longer ready.
181*61c4878aSAndroid Build Coastguard Worker       ReleaseConnectionWithLockHeld();
182*61c4878aSAndroid Build Coastguard Worker       ready_ = false;
183*61c4878aSAndroid Build Coastguard Worker     }
184*61c4878aSAndroid Build Coastguard Worker   }
185*61c4878aSAndroid Build Coastguard Worker }
186*61c4878aSAndroid Build Coastguard Worker 
DoWrite(span<const std::byte> data)187*61c4878aSAndroid Build Coastguard Worker Status SocketStream::DoWrite(span<const std::byte> data) {
188*61c4878aSAndroid Build Coastguard Worker   int send_flags = 0;
189*61c4878aSAndroid Build Coastguard Worker #if defined(__linux__)
190*61c4878aSAndroid Build Coastguard Worker   // Use MSG_NOSIGNAL to avoid getting a SIGPIPE signal when the remote
191*61c4878aSAndroid Build Coastguard Worker   // peer drops the connection. This is supported on Linux only.
192*61c4878aSAndroid Build Coastguard Worker   send_flags |= MSG_NOSIGNAL;
193*61c4878aSAndroid Build Coastguard Worker #endif  // defined(__linux__)
194*61c4878aSAndroid Build Coastguard Worker 
195*61c4878aSAndroid Build Coastguard Worker   ssize_t bytes_sent;
196*61c4878aSAndroid Build Coastguard Worker   {
197*61c4878aSAndroid Build Coastguard Worker     ConnectionOwnership ownership(this);
198*61c4878aSAndroid Build Coastguard Worker     if (ownership.fd() == kInvalidFd) {
199*61c4878aSAndroid Build Coastguard Worker       return Status::Unknown();
200*61c4878aSAndroid Build Coastguard Worker     }
201*61c4878aSAndroid Build Coastguard Worker     bytes_sent = send(ownership.fd(),
202*61c4878aSAndroid Build Coastguard Worker                       reinterpret_cast<const char*>(data.data()),
203*61c4878aSAndroid Build Coastguard Worker                       data.size_bytes(),
204*61c4878aSAndroid Build Coastguard Worker                       send_flags);
205*61c4878aSAndroid Build Coastguard Worker   }
206*61c4878aSAndroid Build Coastguard Worker 
207*61c4878aSAndroid Build Coastguard Worker   if (bytes_sent < 0 || static_cast<size_t>(bytes_sent) != data.size()) {
208*61c4878aSAndroid Build Coastguard Worker     if (errno == EPIPE) {
209*61c4878aSAndroid Build Coastguard Worker       // An EPIPE indicates that the connection is closed.  Return an OutOfRange
210*61c4878aSAndroid Build Coastguard Worker       // error.
211*61c4878aSAndroid Build Coastguard Worker       return Status::OutOfRange();
212*61c4878aSAndroid Build Coastguard Worker     }
213*61c4878aSAndroid Build Coastguard Worker 
214*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
215*61c4878aSAndroid Build Coastguard Worker   }
216*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
217*61c4878aSAndroid Build Coastguard Worker }
218*61c4878aSAndroid Build Coastguard Worker 
DoRead(ByteSpan dest)219*61c4878aSAndroid Build Coastguard Worker StatusWithSize SocketStream::DoRead(ByteSpan dest) {
220*61c4878aSAndroid Build Coastguard Worker   ConnectionOwnership ownership(this);
221*61c4878aSAndroid Build Coastguard Worker   if (ownership.fd() == kInvalidFd) {
222*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::Unknown();
223*61c4878aSAndroid Build Coastguard Worker   }
224*61c4878aSAndroid Build Coastguard Worker 
225*61c4878aSAndroid Build Coastguard Worker   // Wait for data to read or a tear down notification.
226*61c4878aSAndroid Build Coastguard Worker   pollfd fds_to_poll[2];
227*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[0].fd = ownership.fd();
228*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[0].events = POLLIN | POLLERR | POLLHUP;
229*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[1].fd = ownership.pipe_r_fd();
230*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[1].events = POLLIN;
231*61c4878aSAndroid Build Coastguard Worker   poll(fds_to_poll, 2, -1);
232*61c4878aSAndroid Build Coastguard Worker   if (!(fds_to_poll[0].revents & POLLIN)) {
233*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::Unknown();
234*61c4878aSAndroid Build Coastguard Worker   }
235*61c4878aSAndroid Build Coastguard Worker 
236*61c4878aSAndroid Build Coastguard Worker   ssize_t bytes_rcvd = recv(ownership.fd(),
237*61c4878aSAndroid Build Coastguard Worker                             reinterpret_cast<char*>(dest.data()),
238*61c4878aSAndroid Build Coastguard Worker                             dest.size_bytes(),
239*61c4878aSAndroid Build Coastguard Worker                             0);
240*61c4878aSAndroid Build Coastguard Worker   if (bytes_rcvd == 0) {
241*61c4878aSAndroid Build Coastguard Worker     // Remote peer has closed the connection.
242*61c4878aSAndroid Build Coastguard Worker     Close();
243*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::OutOfRange();
244*61c4878aSAndroid Build Coastguard Worker   } else if (bytes_rcvd < 0) {
245*61c4878aSAndroid Build Coastguard Worker     if (errno == EAGAIN || errno == EWOULDBLOCK) {
246*61c4878aSAndroid Build Coastguard Worker       // Socket timed out when trying to read.
247*61c4878aSAndroid Build Coastguard Worker       // This should only occur if SO_RCVTIMEO was configured to be nonzero, or
248*61c4878aSAndroid Build Coastguard Worker       // if the socket was opened with the O_NONBLOCK flag to prevent any
249*61c4878aSAndroid Build Coastguard Worker       // blocking when performing reads or writes.
250*61c4878aSAndroid Build Coastguard Worker       return StatusWithSize::ResourceExhausted();
251*61c4878aSAndroid Build Coastguard Worker     }
252*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::Unknown();
253*61c4878aSAndroid Build Coastguard Worker   }
254*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize(bytes_rcvd);
255*61c4878aSAndroid Build Coastguard Worker }
256*61c4878aSAndroid Build Coastguard Worker 
TakeConnection()257*61c4878aSAndroid Build Coastguard Worker int SocketStream::TakeConnection() {
258*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(connection_mutex_);
259*61c4878aSAndroid Build Coastguard Worker   return TakeConnectionWithLockHeld();
260*61c4878aSAndroid Build Coastguard Worker }
261*61c4878aSAndroid Build Coastguard Worker 
TakeConnectionWithLockHeld()262*61c4878aSAndroid Build Coastguard Worker int SocketStream::TakeConnectionWithLockHeld() {
263*61c4878aSAndroid Build Coastguard Worker   ++connection_own_count_;
264*61c4878aSAndroid Build Coastguard Worker 
265*61c4878aSAndroid Build Coastguard Worker   if (ready_ && (connection_fd_ != kInvalidFd) &&
266*61c4878aSAndroid Build Coastguard Worker       (connection_pipe_r_fd_ == kInvalidFd)) {
267*61c4878aSAndroid Build Coastguard Worker     int fd_list[2];
268*61c4878aSAndroid Build Coastguard Worker     if (pipe(fd_list) >= 0) {
269*61c4878aSAndroid Build Coastguard Worker       connection_pipe_r_fd_ = fd_list[0];
270*61c4878aSAndroid Build Coastguard Worker       connection_pipe_w_fd_ = fd_list[1];
271*61c4878aSAndroid Build Coastguard Worker     }
272*61c4878aSAndroid Build Coastguard Worker   }
273*61c4878aSAndroid Build Coastguard Worker 
274*61c4878aSAndroid Build Coastguard Worker   if (!ready_ || (connection_pipe_r_fd_ == kInvalidFd) ||
275*61c4878aSAndroid Build Coastguard Worker       (connection_pipe_w_fd_ == kInvalidFd)) {
276*61c4878aSAndroid Build Coastguard Worker     return kInvalidFd;
277*61c4878aSAndroid Build Coastguard Worker   }
278*61c4878aSAndroid Build Coastguard Worker   return connection_fd_;
279*61c4878aSAndroid Build Coastguard Worker }
280*61c4878aSAndroid Build Coastguard Worker 
ReleaseConnection()281*61c4878aSAndroid Build Coastguard Worker void SocketStream::ReleaseConnection() {
282*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(connection_mutex_);
283*61c4878aSAndroid Build Coastguard Worker   ReleaseConnectionWithLockHeld();
284*61c4878aSAndroid Build Coastguard Worker }
285*61c4878aSAndroid Build Coastguard Worker 
ReleaseConnectionWithLockHeld()286*61c4878aSAndroid Build Coastguard Worker void SocketStream::ReleaseConnectionWithLockHeld() {
287*61c4878aSAndroid Build Coastguard Worker   --connection_own_count_;
288*61c4878aSAndroid Build Coastguard Worker 
289*61c4878aSAndroid Build Coastguard Worker   if (connection_own_count_ <= 0) {
290*61c4878aSAndroid Build Coastguard Worker     ready_ = false;
291*61c4878aSAndroid Build Coastguard Worker     if (connection_fd_ != kInvalidFd) {
292*61c4878aSAndroid Build Coastguard Worker       close(connection_fd_);
293*61c4878aSAndroid Build Coastguard Worker       connection_fd_ = kInvalidFd;
294*61c4878aSAndroid Build Coastguard Worker     }
295*61c4878aSAndroid Build Coastguard Worker     if (connection_pipe_r_fd_ != kInvalidFd) {
296*61c4878aSAndroid Build Coastguard Worker       close(connection_pipe_r_fd_);
297*61c4878aSAndroid Build Coastguard Worker       connection_pipe_r_fd_ = kInvalidFd;
298*61c4878aSAndroid Build Coastguard Worker     }
299*61c4878aSAndroid Build Coastguard Worker     if (connection_pipe_w_fd_ != kInvalidFd) {
300*61c4878aSAndroid Build Coastguard Worker       close(connection_pipe_w_fd_);
301*61c4878aSAndroid Build Coastguard Worker       connection_pipe_w_fd_ = kInvalidFd;
302*61c4878aSAndroid Build Coastguard Worker     }
303*61c4878aSAndroid Build Coastguard Worker   }
304*61c4878aSAndroid Build Coastguard Worker }
305*61c4878aSAndroid Build Coastguard Worker 
306*61c4878aSAndroid Build Coastguard Worker // Listen for connections on the given port.
307*61c4878aSAndroid Build Coastguard Worker // If port is 0, a random unused port is chosen and can be retrieved with
308*61c4878aSAndroid Build Coastguard Worker // port().
Listen(uint16_t port)309*61c4878aSAndroid Build Coastguard Worker Status ServerSocket::Listen(uint16_t port) {
310*61c4878aSAndroid Build Coastguard Worker   int socket_fd = socket(AF_INET6, SOCK_STREAM, 0);
311*61c4878aSAndroid Build Coastguard Worker   if (socket_fd == kInvalidFd) {
312*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
313*61c4878aSAndroid Build Coastguard Worker   }
314*61c4878aSAndroid Build Coastguard Worker 
315*61c4878aSAndroid Build Coastguard Worker   // Allow binding to an address that may still be in use by a closed socket.
316*61c4878aSAndroid Build Coastguard Worker   constexpr int value = 1;
317*61c4878aSAndroid Build Coastguard Worker   setsockopt(socket_fd,
318*61c4878aSAndroid Build Coastguard Worker              SOL_SOCKET,
319*61c4878aSAndroid Build Coastguard Worker              SO_REUSEADDR,
320*61c4878aSAndroid Build Coastguard Worker              reinterpret_cast<const char*>(&value),
321*61c4878aSAndroid Build Coastguard Worker              sizeof(int));
322*61c4878aSAndroid Build Coastguard Worker 
323*61c4878aSAndroid Build Coastguard Worker   if (port != 0) {
324*61c4878aSAndroid Build Coastguard Worker     struct sockaddr_in6 addr = {};
325*61c4878aSAndroid Build Coastguard Worker     socklen_t addr_len = sizeof(addr);
326*61c4878aSAndroid Build Coastguard Worker     addr.sin6_family = AF_INET6;
327*61c4878aSAndroid Build Coastguard Worker     addr.sin6_port = htons(port);
328*61c4878aSAndroid Build Coastguard Worker     addr.sin6_addr = in6addr_any;
329*61c4878aSAndroid Build Coastguard Worker     if (bind(socket_fd, reinterpret_cast<sockaddr*>(&addr), addr_len) < 0) {
330*61c4878aSAndroid Build Coastguard Worker       close(socket_fd);
331*61c4878aSAndroid Build Coastguard Worker       return Status::Unknown();
332*61c4878aSAndroid Build Coastguard Worker     }
333*61c4878aSAndroid Build Coastguard Worker   }
334*61c4878aSAndroid Build Coastguard Worker 
335*61c4878aSAndroid Build Coastguard Worker   if (listen(socket_fd, kServerBacklogLength) < 0) {
336*61c4878aSAndroid Build Coastguard Worker     close(socket_fd);
337*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
338*61c4878aSAndroid Build Coastguard Worker   }
339*61c4878aSAndroid Build Coastguard Worker 
340*61c4878aSAndroid Build Coastguard Worker   // Find out which port the socket is listening on, and fill in port_.
341*61c4878aSAndroid Build Coastguard Worker   struct sockaddr_in6 addr = {};
342*61c4878aSAndroid Build Coastguard Worker   socklen_t addr_len = sizeof(addr);
343*61c4878aSAndroid Build Coastguard Worker   if (getsockname(socket_fd, reinterpret_cast<sockaddr*>(&addr), &addr_len) <
344*61c4878aSAndroid Build Coastguard Worker           0 ||
345*61c4878aSAndroid Build Coastguard Worker       static_cast<size_t>(addr_len) > sizeof(addr)) {
346*61c4878aSAndroid Build Coastguard Worker     close(socket_fd);
347*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
348*61c4878aSAndroid Build Coastguard Worker   }
349*61c4878aSAndroid Build Coastguard Worker 
350*61c4878aSAndroid Build Coastguard Worker   port_ = ntohs(addr.sin6_port);
351*61c4878aSAndroid Build Coastguard Worker 
352*61c4878aSAndroid Build Coastguard Worker   // Mark as ready and take ownership of the socket by this object.
353*61c4878aSAndroid Build Coastguard Worker   {
354*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(socket_mutex_);
355*61c4878aSAndroid Build Coastguard Worker     socket_fd_ = socket_fd;
356*61c4878aSAndroid Build Coastguard Worker     TakeSocketWithLockHeld();
357*61c4878aSAndroid Build Coastguard Worker     ready_ = true;
358*61c4878aSAndroid Build Coastguard Worker   }
359*61c4878aSAndroid Build Coastguard Worker 
360*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
361*61c4878aSAndroid Build Coastguard Worker }
362*61c4878aSAndroid Build Coastguard Worker 
363*61c4878aSAndroid Build Coastguard Worker // Accept a connection. Blocks until after a client is connected.
364*61c4878aSAndroid Build Coastguard Worker // On success, returns a SocketStream connected to the new client.
Accept()365*61c4878aSAndroid Build Coastguard Worker Result<SocketStream> ServerSocket::Accept() {
366*61c4878aSAndroid Build Coastguard Worker   struct sockaddr_in6 sockaddr_client_ = {};
367*61c4878aSAndroid Build Coastguard Worker   socklen_t len = sizeof(sockaddr_client_);
368*61c4878aSAndroid Build Coastguard Worker 
369*61c4878aSAndroid Build Coastguard Worker   SocketOwnership ownership(this);
370*61c4878aSAndroid Build Coastguard Worker   if (ownership.fd() == kInvalidFd) {
371*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
372*61c4878aSAndroid Build Coastguard Worker   }
373*61c4878aSAndroid Build Coastguard Worker 
374*61c4878aSAndroid Build Coastguard Worker   // Wait for a connection or a tear down notification.
375*61c4878aSAndroid Build Coastguard Worker   pollfd fds_to_poll[2];
376*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[0].fd = ownership.fd();
377*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[0].events = POLLIN | POLLERR | POLLHUP;
378*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[1].fd = ownership.pipe_r_fd();
379*61c4878aSAndroid Build Coastguard Worker   fds_to_poll[1].events = POLLIN;
380*61c4878aSAndroid Build Coastguard Worker   int rv = poll(fds_to_poll, 2, -1);
381*61c4878aSAndroid Build Coastguard Worker   if ((rv <= 0) || !(fds_to_poll[0].revents & POLLIN)) {
382*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
383*61c4878aSAndroid Build Coastguard Worker   }
384*61c4878aSAndroid Build Coastguard Worker 
385*61c4878aSAndroid Build Coastguard Worker   int connection_fd = accept(
386*61c4878aSAndroid Build Coastguard Worker       ownership.fd(), reinterpret_cast<sockaddr*>(&sockaddr_client_), &len);
387*61c4878aSAndroid Build Coastguard Worker   if (connection_fd == kInvalidFd) {
388*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
389*61c4878aSAndroid Build Coastguard Worker   }
390*61c4878aSAndroid Build Coastguard Worker   ConfigureSocket(connection_fd);
391*61c4878aSAndroid Build Coastguard Worker 
392*61c4878aSAndroid Build Coastguard Worker   return SocketStream(connection_fd);
393*61c4878aSAndroid Build Coastguard Worker }
394*61c4878aSAndroid Build Coastguard Worker 
395*61c4878aSAndroid Build Coastguard Worker // Close the server socket, preventing further connections.
Close()396*61c4878aSAndroid Build Coastguard Worker void ServerSocket::Close() {
397*61c4878aSAndroid Build Coastguard Worker   SocketOwnership ownership(this);
398*61c4878aSAndroid Build Coastguard Worker   {
399*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(socket_mutex_);
400*61c4878aSAndroid Build Coastguard Worker     if (ready_) {
401*61c4878aSAndroid Build Coastguard Worker       // Shutdown the socket and send tear down notification to unblock any
402*61c4878aSAndroid Build Coastguard Worker       // waiters.
403*61c4878aSAndroid Build Coastguard Worker       if (socket_fd_ != kInvalidFd) {
404*61c4878aSAndroid Build Coastguard Worker         shutdown(socket_fd_, SHUT_RDWR);
405*61c4878aSAndroid Build Coastguard Worker       }
406*61c4878aSAndroid Build Coastguard Worker       if (socket_pipe_w_fd_ != kInvalidFd) {
407*61c4878aSAndroid Build Coastguard Worker         write(socket_pipe_w_fd_, "T", 1);
408*61c4878aSAndroid Build Coastguard Worker       }
409*61c4878aSAndroid Build Coastguard Worker 
410*61c4878aSAndroid Build Coastguard Worker       // Release ownership of the socket by this object and mark as no longer
411*61c4878aSAndroid Build Coastguard Worker       // ready.
412*61c4878aSAndroid Build Coastguard Worker       ReleaseSocketWithLockHeld();
413*61c4878aSAndroid Build Coastguard Worker       ready_ = false;
414*61c4878aSAndroid Build Coastguard Worker     }
415*61c4878aSAndroid Build Coastguard Worker   }
416*61c4878aSAndroid Build Coastguard Worker }
417*61c4878aSAndroid Build Coastguard Worker 
TakeSocket()418*61c4878aSAndroid Build Coastguard Worker int ServerSocket::TakeSocket() {
419*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(socket_mutex_);
420*61c4878aSAndroid Build Coastguard Worker   return TakeSocketWithLockHeld();
421*61c4878aSAndroid Build Coastguard Worker }
422*61c4878aSAndroid Build Coastguard Worker 
TakeSocketWithLockHeld()423*61c4878aSAndroid Build Coastguard Worker int ServerSocket::TakeSocketWithLockHeld() {
424*61c4878aSAndroid Build Coastguard Worker   ++socket_own_count_;
425*61c4878aSAndroid Build Coastguard Worker 
426*61c4878aSAndroid Build Coastguard Worker   if (ready_ && (socket_fd_ != kInvalidFd) &&
427*61c4878aSAndroid Build Coastguard Worker       (socket_pipe_r_fd_ == kInvalidFd)) {
428*61c4878aSAndroid Build Coastguard Worker     int fd_list[2];
429*61c4878aSAndroid Build Coastguard Worker     if (pipe(fd_list) >= 0) {
430*61c4878aSAndroid Build Coastguard Worker       socket_pipe_r_fd_ = fd_list[0];
431*61c4878aSAndroid Build Coastguard Worker       socket_pipe_w_fd_ = fd_list[1];
432*61c4878aSAndroid Build Coastguard Worker     }
433*61c4878aSAndroid Build Coastguard Worker   }
434*61c4878aSAndroid Build Coastguard Worker 
435*61c4878aSAndroid Build Coastguard Worker   if (!ready_ || (socket_pipe_r_fd_ == kInvalidFd) ||
436*61c4878aSAndroid Build Coastguard Worker       (socket_pipe_w_fd_ == kInvalidFd)) {
437*61c4878aSAndroid Build Coastguard Worker     return kInvalidFd;
438*61c4878aSAndroid Build Coastguard Worker   }
439*61c4878aSAndroid Build Coastguard Worker   return socket_fd_;
440*61c4878aSAndroid Build Coastguard Worker }
441*61c4878aSAndroid Build Coastguard Worker 
ReleaseSocket()442*61c4878aSAndroid Build Coastguard Worker void ServerSocket::ReleaseSocket() {
443*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(socket_mutex_);
444*61c4878aSAndroid Build Coastguard Worker   ReleaseSocketWithLockHeld();
445*61c4878aSAndroid Build Coastguard Worker }
446*61c4878aSAndroid Build Coastguard Worker 
ReleaseSocketWithLockHeld()447*61c4878aSAndroid Build Coastguard Worker void ServerSocket::ReleaseSocketWithLockHeld() {
448*61c4878aSAndroid Build Coastguard Worker   --socket_own_count_;
449*61c4878aSAndroid Build Coastguard Worker 
450*61c4878aSAndroid Build Coastguard Worker   if (socket_own_count_ <= 0) {
451*61c4878aSAndroid Build Coastguard Worker     ready_ = false;
452*61c4878aSAndroid Build Coastguard Worker     if (socket_fd_ != kInvalidFd) {
453*61c4878aSAndroid Build Coastguard Worker       close(socket_fd_);
454*61c4878aSAndroid Build Coastguard Worker       socket_fd_ = kInvalidFd;
455*61c4878aSAndroid Build Coastguard Worker     }
456*61c4878aSAndroid Build Coastguard Worker     if (socket_pipe_r_fd_ != kInvalidFd) {
457*61c4878aSAndroid Build Coastguard Worker       close(socket_pipe_r_fd_);
458*61c4878aSAndroid Build Coastguard Worker       socket_pipe_r_fd_ = kInvalidFd;
459*61c4878aSAndroid Build Coastguard Worker     }
460*61c4878aSAndroid Build Coastguard Worker     if (socket_pipe_w_fd_ != kInvalidFd) {
461*61c4878aSAndroid Build Coastguard Worker       close(socket_pipe_w_fd_);
462*61c4878aSAndroid Build Coastguard Worker       socket_pipe_w_fd_ = kInvalidFd;
463*61c4878aSAndroid Build Coastguard Worker     }
464*61c4878aSAndroid Build Coastguard Worker   }
465*61c4878aSAndroid Build Coastguard Worker }
466*61c4878aSAndroid Build Coastguard Worker 
467*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::stream
468