1
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 #include "net/posix/posix_async_socket_connector.h"
16
17 #include <arpa/inet.h> // for inet_addr, inet_ntoa
18 #include <errno.h> // for errno, EAGAIN, EINPROGRESS
19 #include <netdb.h> // for gethostbyname, addrinfo
20 #include <netinet/in.h> // for sockaddr_in, in_addr
21 #include <poll.h> // for poll, POLLHUP, POLLIN, POL...
22 #include <string.h> // for strerror, NULL
23 #include <sys/socket.h> // for connect, getpeername, gets...
24
25 #include <type_traits> // for remove_extent_t
26
27 #include "log.h"
28 #include "net/posix/posix_async_socket.h" // for PosixAsyncSocket
29
30 namespace android {
31 namespace net {
32
PosixAsyncSocketConnector(AsyncManager * am)33 PosixAsyncSocketConnector::PosixAsyncSocketConnector(AsyncManager* am) : am_(am) {}
34
ConnectToRemoteServer(const std::string & server,int port,const std::chrono::milliseconds timeout)35 std::shared_ptr<AsyncDataChannel> PosixAsyncSocketConnector::ConnectToRemoteServer(
36 const std::string& server, int port, const std::chrono::milliseconds timeout) {
37 INFO("Connecting to {}:{} in {} ms", server, port, timeout.count());
38 int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
39 std::shared_ptr<PosixAsyncSocket> pas = std::make_shared<PosixAsyncSocket>(socket_fd, am_);
40
41 if (socket_fd < 1) {
42 INFO("socket() call failed: {}", strerror(errno));
43 return pas;
44 }
45
46 struct hostent* host;
47 host = gethostbyname(server.c_str());
48 if (host == NULL) {
49 INFO("gethostbyname() failed for {}: {}", server, strerror(errno));
50 pas->Close();
51 return pas;
52 }
53
54 struct in_addr** addr_list = (struct in_addr**)host->h_addr_list;
55 struct sockaddr_in serv_addr {};
56 serv_addr.sin_family = AF_INET;
57 serv_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
58 serv_addr.sin_port = htons(port);
59
60 int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
61
62 if (result != 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINPROGRESS) {
63 INFO("Failed to connect to {}:{}, error: {}", server, port, strerror(errno));
64 pas->Close();
65 return pas;
66 }
67
68 // wait for the connection.
69 struct pollfd fds[] = {
70 {
71 .fd = socket_fd,
72 .events = POLLIN | POLLOUT | POLLHUP,
73 .revents = 0,
74 },
75 };
76
77 int numFdsReady = 0;
78 REPEAT_UNTIL_NO_INTR(numFdsReady = ::poll(fds, 1, timeout.count()));
79
80 if (numFdsReady <= 0) {
81 INFO("Failed to connect to {}:{}, error: {}", server, port, strerror(errno));
82 pas->Close();
83 return pas;
84 }
85
86 // As per https://cr.yp.to/docs/connect.html, we should get the peername
87 // for validating if a connection was established.
88 struct sockaddr_storage ss;
89 socklen_t sslen = sizeof(ss);
90
91 if (getpeername(socket_fd, (struct sockaddr*)&ss, &sslen) < 0) {
92 INFO("Failed to connect to {}:{}, error: {}", server, port, strerror(errno));
93 pas->Close();
94 return pas;
95 }
96
97 int err = 0;
98 socklen_t optLen = sizeof(err);
99 if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &optLen) || err) {
100 // Either getsockopt failed or there was an error associated
101 // with the socket. The connection did not succeed.
102 INFO("Failed to connect to {}:{}, error: {}", server, port, strerror(err));
103 pas->Close();
104 return pas;
105 }
106
107 INFO("Connected to {}:{} ({})", server, port, socket_fd);
108 return pas;
109 }
110
111 } // namespace net
112 } // namespace android
113