// Copyright (C) 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "net/posix/posix_async_socket.h" #include // for errno #include // for fcntl, FD_CLOEXEC, F_GETFL #include // for strerror #include // for getsockopt, send, MSG_NOSIGNAL #include // for close, read #include // for __base #include "log.h" #include "model/setup/async_manager.h" // for AsyncManager #ifdef _WIN32 #include "msvc-posix.h" #endif /* set for very verbose debugging */ #ifdef NDEBUG #define DD(...) (void)0 #else #define DD(...) INFO(__VA_ARGS__) #endif namespace android { namespace net { PosixAsyncSocket::PosixAsyncSocket(int fd, AsyncManager* am) : fd_(fd), am_(am), watching_(false) { int flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK); flags = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, flags | FD_CLOEXEC); #ifdef SO_NOSIGPIPE // Disable SIGPIPE generation on Darwin. // When writing to a broken pipe, send() will return -1 and // set errno to EPIPE. flags = 1; setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&flags, sizeof(flags)); #endif } PosixAsyncSocket::PosixAsyncSocket(PosixAsyncSocket&& other) { fd_ = other.fd_; watching_ = other.watching_.load(); am_ = other.am_; other.fd_ = -1; other.watching_ = false; } PosixAsyncSocket::~PosixAsyncSocket() { Close(); } ssize_t PosixAsyncSocket::Recv(uint8_t* buffer, uint64_t bufferSize) { if (fd_ == -1) { // Socket was closed locally. return 0; } errno = 0; ssize_t res = 0; REPEAT_UNTIL_NO_INTR(res = read(fd_, buffer, bufferSize)); if (res < 0) { DD("Recv < 0: {} ({})", strerror(errno), fd_); } DD("{} bytes ({})", res, fd_); return res; }; ssize_t PosixAsyncSocket::Send(const uint8_t* buffer, uint64_t bufferSize) { errno = 0; ssize_t res = 0; #ifdef MSG_NOSIGNAL // Prevent SIGPIPE generation on Linux when writing to a broken pipe. // ::send() will return -1/EPIPE instead. const int sendFlags = MSG_NOSIGNAL; #else // For Darwin, this is handled by setting SO_NOSIGPIPE when creating // the socket. const int sendFlags = 0; #endif REPEAT_UNTIL_NO_INTR(res = send(fd_, buffer, bufferSize, sendFlags)); DD("{} bytes ({})", res, fd_); return res; } bool PosixAsyncSocket::Connected() { if (fd_ == -1) { return false; } char buf; if (recv(fd_, &buf, 1, MSG_PEEK | MSG_DONTWAIT) != 1) { DD("Recv not 1, could be connected: {} ({})", strerror(errno), fd_); return errno == EAGAIN || errno == EWOULDBLOCK; } // We saw a byte in the queue, we are likely connected. return true; } void PosixAsyncSocket::Close() { if (fd_ == -1) { return; } StopWatching(); // Clear out error int error_code = 0; socklen_t error_code_size = sizeof(error_code); getsockopt(fd_, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error_code), &error_code_size); // shutdown sockets if possible, REPEAT_UNTIL_NO_INTR(shutdown(fd_, SHUT_RDWR)); error_code = ::close(fd_); if (error_code == -1) { INFO("Failed to close: {} ({})", strerror(errno), fd_); } INFO("({})", fd_); fd_ = -1; } bool PosixAsyncSocket::WatchForNonBlockingRead( const ReadCallback& on_read_ready_callback) { bool expected = false; if (watching_.compare_exchange_strong(expected, true)) { return am_->WatchFdForNonBlockingReads( fd_, [on_read_ready_callback, this](int /* fd */) { on_read_ready_callback(this); }) == 0; } return false; } void PosixAsyncSocket::StopWatching() { bool expected = true; if (watching_.compare_exchange_strong(expected, false)) { am_->StopWatchingFileDescriptor(fd_); } } } // namespace net } // namespace android