1 /*
2 *
3 * Copyright (C) 2023 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <fstream>
20 #include <iostream>
21 #include <memory>
22 #include <regex>
23 #include <string>
24
25 #include <android-base/strings.h>
26 #include <gflags/gflags.h>
27 #include <grpcpp/ext/proto_server_reflection_plugin.h>
28 #include <grpcpp/grpcpp.h>
29 #include <grpcpp/health_check_service_interface.h>
30
31 #include "common/libs/utils/files.h"
32 #include "common/libs/utils/json.h"
33 #include "common/libs/utils/result.h"
34 #include "host/libs/web/http_client/http_client.h"
35 #include "openwrt_control.grpc.pb.h"
36
37 using android::base::StartsWith;
38 using google::protobuf::Empty;
39 using google::protobuf::RepeatedPtrField;
40 using grpc::Server;
41 using grpc::ServerBuilder;
42 using grpc::ServerContext;
43 using grpc::Status;
44 using grpc::StatusCode;
45 using openwrtcontrolserver::LuciRpcReply;
46 using openwrtcontrolserver::LuciRpcRequest;
47 using openwrtcontrolserver::OpenwrtControlService;
48 using openwrtcontrolserver::OpenwrtIpaddrReply;
49
50 DEFINE_string(grpc_uds_path, "", "grpc_uds_path");
51 DEFINE_bool(bridged_wifi_tap, false,
52 "True for using cvd-wtap-XX, false for using cvd-wifiap-XX");
53 DEFINE_string(webrtc_device_id, "", "The device ID in WebRTC like cvd-1");
54 DEFINE_string(launcher_log_path, "", "File path for launcher.log");
55 DEFINE_string(openwrt_log_path, "", "File path for crosvm_openwrt.log");
56
57 constexpr char kErrorMessageRpc[] = "Luci RPC request failed";
58 constexpr char kErrorMessageRpcAuth[] = "Luci authentication request failed";
59
60 namespace cuttlefish {
61
ErrorResultToStatus(const std::string_view prefix,const StackTraceError & error)62 static Status ErrorResultToStatus(const std::string_view prefix,
63 const StackTraceError& error) {
64 std::string msg = fmt::format("{}:\n\n{}", prefix, error.FormatForEnv(false));
65 return Status(StatusCode::UNAVAILABLE, msg);
66 }
67
68 class OpenwrtControlServiceImpl final : public OpenwrtControlService::Service {
69 public:
OpenwrtControlServiceImpl(HttpClient & http_client)70 OpenwrtControlServiceImpl(HttpClient& http_client)
71 : http_client_(http_client) {}
72
LuciRpc(ServerContext * context,const LuciRpcRequest * request,LuciRpcReply * response)73 Status LuciRpc(ServerContext* context, const LuciRpcRequest* request,
74 LuciRpcReply* response) override {
75 // Update authentication key when it's empty.
76 if (auth_key_.empty()) {
77 Result<void> auth_res = UpdateLuciRpcAuthKey();
78 if (!auth_res.ok()) {
79 return ErrorResultToStatus(kErrorMessageRpcAuth, auth_res.error());
80 }
81 }
82
83 auto reply = RequestLuciRpc(request->subpath(), request->method(),
84 ToVector(request->params()));
85
86 // When RPC request fails, update authentication key and retry once.
87 if (!reply.ok()) {
88 Result<void> auth_res = UpdateLuciRpcAuthKey();
89 if (!auth_res.ok()) {
90 return ErrorResultToStatus(kErrorMessageRpcAuth, auth_res.error());
91 }
92 reply = RequestLuciRpc(request->subpath(), request->method(),
93 ToVector(request->params()));
94 if (!reply.ok()) {
95 return ErrorResultToStatus(kErrorMessageRpc, reply.error());
96 }
97 }
98
99 Json::FastWriter writer;
100 response->set_id((*reply)["id"].asInt());
101 response->set_error((*reply)["error"].asString());
102 response->set_result(writer.write((*reply)["result"]));
103
104 return Status::OK;
105 }
106
OpenwrtIpaddr(ServerContext * context,const Empty * request,OpenwrtIpaddrReply * response)107 Status OpenwrtIpaddr(ServerContext* context, const Empty* request,
108 OpenwrtIpaddrReply* response) override {
109 // TODO(seungjaeyoo) : Find IP address from crosvm_openwrt.log when using
110 // cvd-wtap-XX after disabling DHCP inside OpenWRT in bridged_wifi_tap mode.
111 Result<std::string> ipaddr = FindIpaddrLauncherLog();
112 if (!ipaddr.ok()) {
113 return ErrorResultToStatus("Failed to get Openwrt IP address",
114 ipaddr.error());
115 }
116 response->set_ipaddr(*ipaddr);
117 return Status::OK;
118 }
119
120 private:
121 template <typename T>
ToVector(const RepeatedPtrField<T> & repeated_field)122 std::vector<T> ToVector(const RepeatedPtrField<T>& repeated_field) {
123 std::vector<T> vec;
124 for (const auto& value : repeated_field) {
125 vec.push_back(value);
126 }
127 return vec;
128 }
129
LuciRpcAddress(const std::string & subpath)130 Result<std::string> LuciRpcAddress(const std::string& subpath) {
131 auto ipaddr = CF_EXPECT(FindIpaddrLauncherLog());
132 return "http://" + ipaddr + "/devices/" + FLAGS_webrtc_device_id +
133 "/openwrt/cgi-bin/luci/rpc/" + subpath;
134 }
135
LuciRpcAddress(const std::string & subpath,const std::string & auth_key)136 Result<std::string> LuciRpcAddress(const std::string& subpath,
137 const std::string& auth_key) {
138 auto addr_without_auth = CF_EXPECT(LuciRpcAddress(subpath));
139 return addr_without_auth + "?auth=" + auth_key;
140 }
141
LuciRpcData(const std::string & method,const std::vector<std::string> & params)142 Json::Value LuciRpcData(const std::string& method,
143 const std::vector<std::string>& params) {
144 Json::Value data;
145 data["method"] = method;
146 Json::Value params_json_obj(Json::arrayValue);
147 for (const auto& param : params) {
148 params_json_obj.append(param);
149 }
150 data["params"] = params_json_obj;
151 return data;
152 }
153
LuciRpcData(int id,const std::string & method,const std::vector<std::string> & params)154 Json::Value LuciRpcData(int id, const std::string& method,
155 const std::vector<std::string>& params) {
156 Json::Value data = LuciRpcData(method, params);
157 data["id"] = id;
158 return data;
159 }
160
UpdateLuciRpcAuthKey()161 Result<void> UpdateLuciRpcAuthKey() {
162 auto auth_url = CF_EXPECT(LuciRpcAddress("auth"));
163 auto auth_data = LuciRpcData(1, "login", {"root", "password"});
164 auto auth_reply =
165 CF_EXPECT(http_client_.PostToJson(auth_url, auth_data, header_));
166 if (auth_reply.data["error"].isString()) {
167 CF_EXPECT(!StartsWith(auth_reply.data["error"].asString(),
168 "Failed to parse json:"),
169 kErrorMessageRpcAuth);
170 }
171 CF_EXPECT(auth_reply.data["result"].isString(),
172 "Reply doesn't contain result");
173 auth_key_ = auth_reply.data["result"].asString();
174
175 return {};
176 }
177
RequestLuciRpc(const std::string & subpath,const std::string & method,const std::vector<std::string> & params)178 Result<Json::Value> RequestLuciRpc(const std::string& subpath,
179 const std::string& method,
180 const std::vector<std::string>& params) {
181 auto url = CF_EXPECT(LuciRpcAddress(subpath, auth_key_));
182 auto data = LuciRpcData(method, params);
183 auto reply = CF_EXPECT(http_client_.PostToJson(url, data, header_));
184 if (reply.data["error"].isString()) {
185 CF_EXPECT(
186 !StartsWith(reply.data["error"].asString(), "Failed to parse json:"),
187 kErrorMessageRpc);
188 }
189 return reply.data;
190 }
191
FindIpaddrLauncherLog()192 Result<std::string> FindIpaddrLauncherLog() {
193 if (!FileExists(FLAGS_launcher_log_path)) {
194 return CF_ERR("launcher.log doesn't exist");
195 }
196
197 std::regex re("wan_ipaddr=[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+");
198 std::smatch matches;
199 std::ifstream ifs(FLAGS_launcher_log_path);
200 std::string line, last_match;
201 while (std::getline(ifs, line)) {
202 if (std::regex_search(line, matches, re)) {
203 last_match = matches[0];
204 }
205 }
206
207 if (last_match.empty()) {
208 return CF_ERR("IP address is not found from launcher.log");
209 } else {
210 return last_match.substr(last_match.find('=') + 1);
211 }
212 }
213
214 HttpClient& http_client_;
215 const std::vector<std::string> header_{"Content-Type: application/json"};
216 std::string auth_key_;
217 };
218
219 } // namespace cuttlefish
220
RunServer()221 void RunServer() {
222 std::string server_address("unix:" + FLAGS_grpc_uds_path);
223 auto http_client = cuttlefish::HttpClient::CurlClient();
224 cuttlefish::OpenwrtControlServiceImpl service(*http_client);
225
226 grpc::EnableDefaultHealthCheckService(true);
227 grpc::reflection::InitProtoReflectionServerBuilderPlugin();
228 ServerBuilder builder;
229 // Listen on the given address without any authentication mechanism.
230 builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
231 // Register "service" as the instance through which we'll communicate with
232 // clients. In this case it corresponds to an *synchronous* service.
233 builder.RegisterService(&service);
234 // Finally assemble the server.
235 std::unique_ptr<Server> server(builder.BuildAndStart());
236 std::cout << "Server listening on " << server_address << std::endl;
237
238 // Wait for the server to shutdown. Note that some other thread must be
239 // responsible for shutting down the server for this call to ever return.
240 server->Wait();
241 }
242
main(int argc,char ** argv)243 int main(int argc, char** argv) {
244 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
245 RunServer();
246
247 return 0;
248 }
249