1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "frontend/frontend_server.h"
16
17 #include <google/protobuf/util/json_util.h>
18
19 #include <iostream>
20 #include <memory>
21 #include <string>
22 #include <utility>
23
24 #include "google/protobuf/empty.pb.h"
25 #include "grpcpp/server_context.h"
26 #include "grpcpp/support/status.h"
27 #include "netsim-daemon/src/ffi.rs.h"
28 #include "netsim/frontend.grpc.pb.h"
29 #include "netsim/frontend.pb.h"
30
31 namespace netsim {
32 namespace {
33
34 /// The C++ implementation of the CxxServerResponseWriter interface. This is
35 /// used by the gRPC server to invoke the Rust pcap handler and process a
36 /// responses.
37 class CxxServerResponseWritable : public frontend::CxxServerResponseWriter {
38 public:
CxxServerResponseWritable()39 CxxServerResponseWritable()
40 : grpc_writer_(nullptr), err(""), is_ok(false), body(""), length(0) {};
CxxServerResponseWritable(grpc::ServerWriter<netsim::frontend::GetCaptureResponse> * grpc_writer)41 CxxServerResponseWritable(
42 grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer)
43 : grpc_writer_(grpc_writer), err(""), is_ok(false), body(""), length(0) {
44 };
45
put_error(unsigned int error_code,const std::string & response) const46 void put_error(unsigned int error_code,
47 const std::string &response) const override {
48 err = std::to_string(error_code) + ": " + response;
49 is_ok = false;
50 }
51
put_ok_with_length(const std::string & mime_type,std::size_t length) const52 void put_ok_with_length(const std::string &mime_type,
53 std::size_t length) const override {
54 this->length = length;
55 is_ok = true;
56 }
57
put_chunk(rust::Slice<const uint8_t> chunk) const58 void put_chunk(rust::Slice<const uint8_t> chunk) const override {
59 netsim::frontend::GetCaptureResponse response;
60 response.set_capture_stream(std::string(chunk.begin(), chunk.end()));
61 is_ok = grpc_writer_->Write(response);
62 }
63
put_ok(const std::string & mime_type,const std::string & body) const64 void put_ok(const std::string &mime_type,
65 const std::string &body) const override {
66 this->body = body;
67 is_ok = true;
68 }
69
70 mutable grpc::ServerWriter<netsim::frontend::GetCaptureResponse>
71 *grpc_writer_;
72 mutable std::string err;
73 mutable bool is_ok;
74 mutable std::string body;
75 mutable std::size_t length;
76 };
77
78 class FrontendServer final : public frontend::FrontendService::Service {
79 public:
GetVersion(grpc::ServerContext * context,const google::protobuf::Empty * empty,frontend::VersionResponse * reply)80 grpc::Status GetVersion(grpc::ServerContext *context,
81 const google::protobuf::Empty *empty,
82 frontend::VersionResponse *reply) {
83 reply->set_version(std::string(netsim::GetVersion()));
84 return grpc::Status::OK;
85 }
86
ListDevice(grpc::ServerContext * context,const google::protobuf::Empty * empty,frontend::ListDeviceResponse * reply)87 grpc::Status ListDevice(grpc::ServerContext *context,
88 const google::protobuf::Empty *empty,
89 frontend::ListDeviceResponse *reply) {
90 CxxServerResponseWritable writer;
91 HandleDeviceCxx(writer, "GET", "", "");
92 if (writer.is_ok) {
93 google::protobuf::util::JsonStringToMessage(writer.body, reply);
94 return grpc::Status::OK;
95 }
96 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
97 }
98
CreateDevice(grpc::ServerContext * context,const frontend::CreateDeviceRequest * request,frontend::CreateDeviceResponse * response)99 grpc::Status CreateDevice(grpc::ServerContext *context,
100 const frontend::CreateDeviceRequest *request,
101 frontend::CreateDeviceResponse *response) {
102 CxxServerResponseWritable writer;
103 std::string request_json;
104 google::protobuf::util::MessageToJsonString(*request, &request_json);
105 HandleDeviceCxx(writer, "POST", "", request_json);
106 if (writer.is_ok) {
107 google::protobuf::util::JsonStringToMessage(writer.body, response);
108 return grpc::Status::OK;
109 }
110 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
111 }
112
DeleteChip(grpc::ServerContext * context,const frontend::DeleteChipRequest * request,google::protobuf::Empty * response)113 grpc::Status DeleteChip(grpc::ServerContext *context,
114 const frontend::DeleteChipRequest *request,
115 google::protobuf::Empty *response) {
116 CxxServerResponseWritable writer;
117 std::string request_json;
118 google::protobuf::util::MessageToJsonString(*request, &request_json);
119 HandleDeviceCxx(writer, "DELETE", "", request_json);
120 if (writer.is_ok) {
121 google::protobuf::util::JsonStringToMessage(writer.body, response);
122 return grpc::Status::OK;
123 }
124 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
125 }
126
PatchDevice(grpc::ServerContext * context,const frontend::PatchDeviceRequest * request,google::protobuf::Empty * response)127 grpc::Status PatchDevice(grpc::ServerContext *context,
128 const frontend::PatchDeviceRequest *request,
129 google::protobuf::Empty *response) {
130 CxxServerResponseWritable writer;
131 std::string request_json;
132 google::protobuf::util::MessageToJsonString(*request, &request_json);
133 auto device = request->device();
134 // device.id() starts from 1.
135 // If you don't populate the id, you must fill the name field.
136 if (device.id() == 0) {
137 HandleDeviceCxx(writer, "PATCH", "", request_json);
138 } else {
139 HandleDeviceCxx(writer, "PATCH", std::to_string(device.id()),
140 request_json);
141 }
142 if (writer.is_ok) {
143 return grpc::Status::OK;
144 }
145 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
146 }
147
Reset(grpc::ServerContext * context,const google::protobuf::Empty * request,google::protobuf::Empty * empty)148 grpc::Status Reset(grpc::ServerContext *context,
149 const google::protobuf::Empty *request,
150 google::protobuf::Empty *empty) {
151 CxxServerResponseWritable writer;
152 HandleDeviceCxx(writer, "PUT", "", "");
153 if (writer.is_ok) {
154 return grpc::Status::OK;
155 }
156 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
157 }
158
ListCapture(grpc::ServerContext * context,const google::protobuf::Empty * empty,frontend::ListCaptureResponse * reply)159 grpc::Status ListCapture(grpc::ServerContext *context,
160 const google::protobuf::Empty *empty,
161 frontend::ListCaptureResponse *reply) {
162 CxxServerResponseWritable writer;
163 HandleCaptureCxx(writer, "GET", "", "");
164 if (writer.is_ok) {
165 google::protobuf::util::JsonStringToMessage(writer.body, reply);
166 return grpc::Status::OK;
167 }
168 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
169 }
170
PatchCapture(grpc::ServerContext * context,const frontend::PatchCaptureRequest * request,google::protobuf::Empty * response)171 grpc::Status PatchCapture(grpc::ServerContext *context,
172 const frontend::PatchCaptureRequest *request,
173 google::protobuf::Empty *response) {
174 CxxServerResponseWritable writer;
175 HandleCaptureCxx(writer, "PATCH", std::to_string(request->id()),
176 std::to_string(request->patch().state()));
177 if (writer.is_ok) {
178 return grpc::Status::OK;
179 }
180 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
181 }
GetCapture(grpc::ServerContext * context,const netsim::frontend::GetCaptureRequest * request,grpc::ServerWriter<netsim::frontend::GetCaptureResponse> * grpc_writer)182 grpc::Status GetCapture(
183 grpc::ServerContext *context,
184 const netsim::frontend::GetCaptureRequest *request,
185 grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer) {
186 CxxServerResponseWritable writer(grpc_writer);
187 HandleCaptureCxx(writer, "GET", std::to_string(request->id()), "");
188 if (writer.is_ok) {
189 return grpc::Status::OK;
190 }
191 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
192 }
193 };
194 } // namespace
195
GetFrontendService()196 std::unique_ptr<frontend::FrontendService::Service> GetFrontendService() {
197 return std::make_unique<FrontendServer>();
198 }
199
200 } // namespace netsim
201