// 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_connector.h" #include // for inet_addr, inet_ntoa #include // for errno, EAGAIN, EINPROGRESS #include // for gethostbyname, addrinfo #include // for sockaddr_in, in_addr #include // for poll, POLLHUP, POLLIN, POL... #include // for strerror, NULL #include // for connect, getpeername, gets... #include // for remove_extent_t #include "net/posix/posix_async_socket.h" // for PosixAsyncSocket #include "os/log.h" // for LOG_INFO #include "osi/include/osi.h" // for OSI_NO_INTR namespace android { namespace net { PosixAsyncSocketConnector::PosixAsyncSocketConnector(AsyncManager* am) : am_(am) {} std::shared_ptr PosixAsyncSocketConnector::ConnectToRemoteServer( const std::string& server, int port, const std::chrono::milliseconds timeout) { LOG_INFO("Connecting to %s:%d in %d ms", server.c_str(), port, (int)timeout.count()); int socket_fd = socket(AF_INET, SOCK_STREAM, 0); std::shared_ptr pas = std::make_shared(socket_fd, am_); if (socket_fd < 1) { LOG_INFO("socket() call failed: %s", strerror(errno)); return pas; } struct hostent* host; host = gethostbyname(server.c_str()); if (host == NULL) { LOG_INFO("gethostbyname() failed for %s: %s", server.c_str(), strerror(errno)); pas->Close(); return pas; } struct in_addr** addr_list = (struct in_addr**)host->h_addr_list; struct sockaddr_in serv_addr {}; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0])); serv_addr.sin_port = htons(port); int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (result != 0 && !(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINPROGRESS)) { LOG_INFO("Failed to connect to %s:%d, error: %s", server.c_str(), port, strerror(errno)); pas->Close(); return pas; } // wait for the connection. struct pollfd fds[] = { { .fd = socket_fd, .events = POLLIN | POLLOUT | POLLHUP, .revents = 0, }, }; int numFdsReady = 0; OSI_NO_INTR(numFdsReady = ::poll(fds, 1, timeout.count())); if (numFdsReady <= 0) { LOG_INFO("Failed to connect to %s:%d, error: %s", server.c_str(), port, strerror(errno)); pas->Close(); return pas; } // As per https://cr.yp.to/docs/connect.html, we should get the peername // for validating if a connection was established. struct sockaddr_storage ss; socklen_t sslen = sizeof(ss); if (getpeername(socket_fd, (struct sockaddr*)&ss, &sslen) < 0) { LOG_INFO("Failed to connect to %s:%d, error: %s", server.c_str(), port, strerror(errno)); pas->Close(); return pas; } int err = 0; socklen_t optLen = sizeof(err); if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, reinterpret_cast(&err), &optLen) || err) { // Either getsockopt failed or there was an error associated // with the socket. The connection did not succeed. LOG_INFO("Failed to connect to %s:%d, error: %s", server.c_str(), port, strerror(err)); pas->Close(); return pas; } LOG_INFO("Connected to %s:%d (%d)", server.c_str(), port, socket_fd); return pas; } } // namespace net } // namespace android