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