// Copyright 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "frontend/frontend_server.h" #include <google/protobuf/util/json_util.h> #include <iostream> #include <memory> #include <string> #include <utility> #include "google/protobuf/empty.pb.h" #include "grpcpp/server_context.h" #include "grpcpp/support/status.h" #include "netsim-daemon/src/ffi.rs.h" #include "netsim/frontend.grpc.pb.h" #include "netsim/frontend.pb.h" namespace netsim { namespace { /// The C++ implementation of the CxxServerResponseWriter interface. This is /// used by the gRPC server to invoke the Rust pcap handler and process a /// responses. class CxxServerResponseWritable : public frontend::CxxServerResponseWriter { public: CxxServerResponseWritable() : grpc_writer_(nullptr), err(""), is_ok(false), body(""), length(0) {}; CxxServerResponseWritable( grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer) : grpc_writer_(grpc_writer), err(""), is_ok(false), body(""), length(0) { }; void put_error(unsigned int error_code, const std::string &response) const override { err = std::to_string(error_code) + ": " + response; is_ok = false; } void put_ok_with_length(const std::string &mime_type, std::size_t length) const override { this->length = length; is_ok = true; } void put_chunk(rust::Slice<const uint8_t> chunk) const override { netsim::frontend::GetCaptureResponse response; response.set_capture_stream(std::string(chunk.begin(), chunk.end())); is_ok = grpc_writer_->Write(response); } void put_ok(const std::string &mime_type, const std::string &body) const override { this->body = body; is_ok = true; } mutable grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer_; mutable std::string err; mutable bool is_ok; mutable std::string body; mutable std::size_t length; }; class FrontendServer final : public frontend::FrontendService::Service { public: grpc::Status GetVersion(grpc::ServerContext *context, const google::protobuf::Empty *empty, frontend::VersionResponse *reply) { reply->set_version(std::string(netsim::GetVersion())); return grpc::Status::OK; } grpc::Status ListDevice(grpc::ServerContext *context, const google::protobuf::Empty *empty, frontend::ListDeviceResponse *reply) { CxxServerResponseWritable writer; HandleDeviceCxx(writer, "GET", "", ""); if (writer.is_ok) { google::protobuf::util::JsonStringToMessage(writer.body, reply); return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status CreateDevice(grpc::ServerContext *context, const frontend::CreateDeviceRequest *request, frontend::CreateDeviceResponse *response) { CxxServerResponseWritable writer; std::string request_json; google::protobuf::util::MessageToJsonString(*request, &request_json); HandleDeviceCxx(writer, "POST", "", request_json); if (writer.is_ok) { google::protobuf::util::JsonStringToMessage(writer.body, response); return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status DeleteChip(grpc::ServerContext *context, const frontend::DeleteChipRequest *request, google::protobuf::Empty *response) { CxxServerResponseWritable writer; std::string request_json; google::protobuf::util::MessageToJsonString(*request, &request_json); HandleDeviceCxx(writer, "DELETE", "", request_json); if (writer.is_ok) { google::protobuf::util::JsonStringToMessage(writer.body, response); return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status PatchDevice(grpc::ServerContext *context, const frontend::PatchDeviceRequest *request, google::protobuf::Empty *response) { CxxServerResponseWritable writer; std::string request_json; google::protobuf::util::MessageToJsonString(*request, &request_json); auto device = request->device(); // device.id() starts from 1. // If you don't populate the id, you must fill the name field. if (device.id() == 0) { HandleDeviceCxx(writer, "PATCH", "", request_json); } else { HandleDeviceCxx(writer, "PATCH", std::to_string(device.id()), request_json); } if (writer.is_ok) { return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status Reset(grpc::ServerContext *context, const google::protobuf::Empty *request, google::protobuf::Empty *empty) { CxxServerResponseWritable writer; HandleDeviceCxx(writer, "PUT", "", ""); if (writer.is_ok) { return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status ListCapture(grpc::ServerContext *context, const google::protobuf::Empty *empty, frontend::ListCaptureResponse *reply) { CxxServerResponseWritable writer; HandleCaptureCxx(writer, "GET", "", ""); if (writer.is_ok) { google::protobuf::util::JsonStringToMessage(writer.body, reply); return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status PatchCapture(grpc::ServerContext *context, const frontend::PatchCaptureRequest *request, google::protobuf::Empty *response) { CxxServerResponseWritable writer; HandleCaptureCxx(writer, "PATCH", std::to_string(request->id()), std::to_string(request->patch().state())); if (writer.is_ok) { return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } grpc::Status GetCapture( grpc::ServerContext *context, const netsim::frontend::GetCaptureRequest *request, grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer) { CxxServerResponseWritable writer(grpc_writer); HandleCaptureCxx(writer, "GET", std::to_string(request->id()), ""); if (writer.is_ok) { return grpc::Status::OK; } return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err); } }; } // namespace std::unique_ptr<frontend::FrontendService::Service> GetFrontendService() { return std::make_unique<FrontendServer>(); } } // namespace netsim