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)
34 : am_(am) {}
35
36 std::shared_ptr<AsyncDataChannel>
ConnectToRemoteServer(const std::string & server,int port,const std::chrono::milliseconds timeout)37 PosixAsyncSocketConnector::ConnectToRemoteServer(
38 const std::string& server, int port,
39 const std::chrono::milliseconds timeout) {
40 INFO("Connecting to {}:{} in {} ms", server, port, timeout.count());
41 int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
42 std::shared_ptr<PosixAsyncSocket> pas =
43 std::make_shared<PosixAsyncSocket>(socket_fd, am_);
44
45 if (socket_fd < 1) {
46 INFO("socket() call failed: {}", strerror(errno));
47 return pas;
48 }
49
50 struct hostent* host;
51 host = gethostbyname(server.c_str());
52 if (host == NULL) {
53 INFO("gethostbyname() failed for {}: {}", server, strerror(errno));
54 pas->Close();
55 return pas;
56 }
57
58 struct in_addr** addr_list = (struct in_addr**)host->h_addr_list;
59 struct sockaddr_in serv_addr {};
60 serv_addr.sin_family = AF_INET;
61 serv_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
62 serv_addr.sin_port = htons(port);
63
64 int result =
65 connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
66
67 if (result != 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
68 errno != EINPROGRESS) {
69 INFO("Failed to connect to {}:{}, error: {}", server, port,
70 strerror(errno));
71 pas->Close();
72 return pas;
73 }
74
75 // wait for the connection.
76 struct pollfd fds[] = {
77 {
78 .fd = socket_fd,
79 .events = POLLIN | POLLOUT | POLLHUP,
80 .revents = 0,
81 },
82 };
83
84 int numFdsReady = 0;
85 REPEAT_UNTIL_NO_INTR(numFdsReady = ::poll(fds, 1, timeout.count()));
86
87 if (numFdsReady <= 0) {
88 INFO("Failed to connect to {}:{}, error: {}", server, port,
89 strerror(errno));
90 pas->Close();
91 return pas;
92 }
93
94 // As per https://cr.yp.to/docs/connect.html, we should get the peername
95 // for validating if a connection was established.
96 struct sockaddr_storage ss;
97 socklen_t sslen = sizeof(ss);
98
99 if (getpeername(socket_fd, (struct sockaddr*)&ss, &sslen) < 0) {
100 INFO("Failed to connect to {}:{}, error: {}", server, port,
101 strerror(errno));
102 pas->Close();
103 return pas;
104 }
105
106 int err = 0;
107 socklen_t optLen = sizeof(err);
108 if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err),
109 &optLen) ||
110 err) {
111 // Either getsockopt failed or there was an error associated
112 // with the socket. The connection did not succeed.
113 INFO("Failed to connect to {}:{}, error: {}", server, port, strerror(err));
114 pas->Close();
115 return pas;
116 }
117
118 INFO("Connected to {}:{} ({})", server, port, socket_fd);
119 return pas;
120 }
121
122 } // namespace net
123 } // namespace android
124