1 /*
2 * Copyright (C) 2022 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 */
16
17 #include "common/libs/utils/socket2socket_proxy.h"
18
19 #include <poll.h>
20 #include <sys/socket.h>
21
22 #include <cstring>
23 #include <functional>
24 #include <memory>
25 #include <ostream>
26 #include <string>
27 #include <thread>
28
29 #include <android-base/logging.h>
30
31 namespace cuttlefish {
32 namespace {
33
Forward(const std::string & label,SharedFD from,SharedFD to)34 void Forward(const std::string& label, SharedFD from, SharedFD to) {
35 LOG(DEBUG) << label << ": Proxy thread started. Starting copying data";
36 auto success = to->CopyAllFrom(*from);
37 if (!success) {
38 if (from->GetErrno()) {
39 LOG(ERROR) << label << ": Error reading: " << from->StrError();
40 }
41 if (to->GetErrno()) {
42 LOG(ERROR) << label << ": Error writing: " << to->StrError();
43 }
44 }
45 to->Shutdown(SHUT_WR);
46 LOG(DEBUG) << label << ": Proxy thread completed";
47 }
48
SetupProxying(SharedFD client,SharedFD target)49 void SetupProxying(SharedFD client, SharedFD target) {
50 LOG(DEBUG) << "Launching proxy threads";
51 std::thread client2target(Forward, "c2t", client, target);
52 std::thread target2client(Forward, "t2c", target, client);
53 client2target.detach();
54 target2client.detach();
55 }
56
57 } // namespace
58
ProxyServer(SharedFD server,std::function<SharedFD ()> clients_factory)59 ProxyServer::ProxyServer(SharedFD server, std::function<SharedFD()> clients_factory)
60 : stop_fd_(SharedFD::Event()) {
61
62 if (!stop_fd_->IsOpen()) {
63 LOG(FATAL) << "Failed to open eventfd: " << stop_fd_->StrError();
64 return;
65 }
66 server_ = std::thread([&, server_fd = std::move(server),
67 clients_factory = std::move(clients_factory)]() {
68 constexpr ssize_t SERVER = 0;
69 constexpr ssize_t STOP = 1;
70
71 std::vector<PollSharedFd> server_poll = {
72 {.fd = server_fd, .events = POLLIN},
73 {.fd = stop_fd_, .events = POLLIN}
74 };
75
76 while (server_fd->IsOpen()) {
77 server_poll[SERVER].revents = 0;
78 server_poll[STOP].revents = 0;
79
80 const int poll_result = SharedFD::Poll(server_poll, -1);
81 if (poll_result < 0) {
82 LOG(ERROR) << "Failed to poll to wait for incoming connection";
83 continue;
84 }
85 if (server_poll[STOP].revents & POLLIN) {
86 // Stop fd is available to read, so we received a stop event
87 // and must stop the thread
88 break;
89 }
90 if (!(server_poll[SERVER].revents & POLLIN)) {
91 continue;
92 }
93
94 // Server fd is available to read, so we can accept the
95 // connection without blocking on that
96 auto client = SharedFD::Accept(*server_fd);
97 if (!client->IsOpen()) {
98 LOG(ERROR) << "Failed to accept incoming connection: " << client->StrError();
99 continue;
100 }
101 auto target = clients_factory();
102 if (target->IsOpen()) {
103 SetupProxying(client, target);
104 } else {
105 LOG(ERROR) << "Cannot connect to the target to setup proxying: " << target->StrError();
106 }
107 // The client will close when it goes out of scope here if the target
108 // didn't open.
109 }
110 });
111 }
112
Join()113 void ProxyServer::Join() {
114 if (server_.joinable()) {
115 server_.join();
116 }
117 }
118
~ProxyServer()119 ProxyServer::~ProxyServer() {
120 if (stop_fd_->EventfdWrite(1) != 0) {
121 LOG(ERROR) << "Failed to stop proxy thread: " << stop_fd_->StrError();
122 }
123 Join();
124 }
125
Proxy(SharedFD server,std::function<SharedFD ()> conn_factory)126 void Proxy(SharedFD server, std::function<SharedFD()> conn_factory) {
127 ProxyServer proxy(std::move(server), std::move(conn_factory));
128 proxy.Join();
129 }
130
ProxyAsync(SharedFD server,std::function<SharedFD ()> conn_factory)131 std::unique_ptr<ProxyServer> ProxyAsync(SharedFD server, std::function<SharedFD()> conn_factory) {
132 return std::unique_ptr<ProxyServer>(
133 new ProxyServer(std::move(server), std::move(conn_factory)));
134 }
135
136 } // namespace cuttlefish
137