| // 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 <errno.h> // for errno |
| #include <fcntl.h> // for fcntl, FD_CLOEXEC, F_GETFL |
| #include <string.h> // for strerror |
| #include <sys/socket.h> // for getsockopt, send, MSG_NOSIGNAL |
| #include <unistd.h> // for close, read |
| |
| #include <functional> // for __base |
| |
| #include "model/setup/async_manager.h" // for AsyncManager |
| #include "os/log.h" // for LOG_INFO |
| #include "osi/include/osi.h" // for OSI_NO_INTR |
| |
| /* set for very verbose debugging */ |
| #ifndef DEBUG |
| #define DD(...) (void)0 |
| #else |
| #define DD(...) LOG_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 | O_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) { |
| errno = 0; |
| ssize_t res = 0; |
| OSI_NO_INTR(res = read(fd_, buffer, bufferSize)); |
| if (res < 0) { |
| DD("Recv < 0: %s (%d)", strerror(errno), fd_); |
| } |
| DD("%zd bytes (%d)", 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 |
| OSI_NO_INTR(res = send(fd_, buffer, bufferSize, sendFlags)); |
| |
| DD("%zd bytes (%d)", 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: %s (%d)", strerror(errno), fd_); |
| if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| return true; |
| } |
| return false; |
| } |
| |
| // 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<void*>(&error_code), |
| &error_code_size); |
| |
| // shutdown sockets if possible, |
| OSI_NO_INTR(shutdown(fd_, SHUT_RDWR)); |
| |
| error_code = ::close(fd_); |
| if (error_code == -1) { |
| LOG_INFO("Failed to close: %s (%d)", strerror(errno), fd_); |
| } |
| LOG_INFO("(%d)", 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 |