#pragma once #include "Procedures.h" /// Gateway -> X [Agent|Route] namespace MWR::C3::Core::ProceduresG2X { /// Base for Gateway -> [Agent|Route] Queries. struct QueryG2X : BaseQuery { /// Create Query from received packet. /// @param sender device that received query. /// @param destinationRid - destination route Id from packet. /// @param procedureNo number identifying procedure. /// @param packetAfterProcedureNumber body of query. QueryG2X(std::weak_ptr sender, RouteId destinationRid, ProceduresUnderlyingType procedureNo, ByteView packetAfterProcedureNumber) : BaseQuery{ sender } , m_ReceiverRid{ destinationRid } , m_QueryPacketBody{ packetAfterProcedureNumber } , m_GatewayPrivateSignature{} // FIXME it's just bad to default construct this { // In the future - parse packet. Currently it's not used anywhere. } /// Creates Query that is empty and should be filled then sent. /// @param propagation - type of propagation for this query /// @param receiverRid - the route to propagate this message through /// @param gatewayPrivateSignature - gateway's signature, used to encrypt and sign the message /// @param responseType - requested response type [Not used] QueryG2X(Propagation propagation, RouteId receiverRid, Crypto::PrivateSignature const& gatewayPrivateSignature, ResponseType responseType = ResponseType::None) : BaseQuery{ responseType } , m_Propagation{ propagation } , m_ReceiverRid{ receiverRid } , m_GatewayPrivateSignature{ &gatewayPrivateSignature } { } /// @returns destination route Id RouteId GetRecipientRouteId() { return m_ReceiverRid; } /// @returns packet body ByteVector const& GetPacketBody() { return m_QueryPacketBody; } /// Constructs the following header: [G2X|(Signed[AID.IID|ProcedureNo|QueryBody])]. /// @return buffer containing whole packet. ByteVector ComposeQueryPacket() const override { assert(m_GatewayPrivateSignature); return CompileProtocolHeader().Concat(Crypto::SignMessage(m_ReceiverRid.ToByteVector().Concat(GetQueryHeader()).Concat(m_QueryPacketBody), *m_GatewayPrivateSignature)); } protected: const Crypto::PrivateSignature* const m_GatewayPrivateSignature; ///< Gateway signature used to sign the query. ByteVector m_QueryPacketBody; ///< Whole Query packet along with all the headers. private: /// Constructs the following header: [G2A/G2R]. /// @return buffer containing composed header. ByteVector CompileProtocolHeader() const override { return ByteVector{}.Write(static_cast(GetProtocol())); } /// @return Protocol type for propagation type. Protocols GetProtocol() const { switch (m_Propagation) { case Propagation::Agent: return Protocols::G2A; case Propagation::Route: return Protocols::G2R; default: throw std::logic_error(OBF("Invalid propagation mode")); } } /// @return Buffer with query header appropriate to propagation ByteVector GetQueryHeader() const { switch (m_Propagation) { case Propagation::Agent: return {}; case Propagation::Route: return CompileQueryHeader(); default: throw std::logic_error(OBF("Invalid propagation mode")); } } Propagation m_Propagation; ///< Propagation type for query RouteId m_ReceiverRid; ///< This Query receiver RouteId }; /// Helper to template creating Queries. /// @tparam ProcedureNumber identifier for each query. Assure that only one type inherits from Query<> with unique identifier. template struct Query : QueryG2X { /// Get underlying number of procedure. static constexpr ProceduresUnderlyingType GetProcedureNumberConstexpr() { return ProcedureNumber; } /// Get underlying number of procedure. ProceduresUnderlyingType GetProcedureNumber() const override { return GetProcedureNumberConstexpr(); } /// Forwarded constructors. using QueryG2X::QueryG2X; }; /// Helper to template creating Queries to Agent /// @tparam ProcedureNumber identifier for each query. Assure that only one type inherits from Query<> with unique identifier. template struct QueryToAgent : Query { /// Encrypts and sets query body /// @param queryPacketBody plaintext body of query /// @param agentPublicKey - recipient agent's public key /// @param gatewayPrivateKey - gateawy private key void EncrpytQueryWithBody(ByteView queryPacketBody, Crypto::PublicKey const& agentPublicKey, Crypto::PrivateKey const& gatewayPrivateKey) { m_QueryPacketBody = Crypto::EncryptAndAuthenticate(CompileQueryHeader().Concat(queryPacketBody), agentPublicKey, gatewayPrivateKey); } /// Forwarded constructors. using Query::Query; using Query::CompileQueryHeader; }; /// Helper to template creating Queries to Route /// @tparam ProcedureNumber identifier for each query. Assure that only one type inherits from Query<> with unique identifier. template struct QueryToRoute : Query { /// Forwarded constructors. using Query::Query; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Query agent to run a command struct RunCommandOnAgentQuery : QueryToAgent<0> { /// Create new instance. /// @param receiverRid - destination route id /// @param gatewayPrivateSignature - gateway's private signature /// @param agentPublicKey - destination agent's public key /// @param gatewayPrivateKey - gateway's private key /// @param commandWithArguments - plaintext command with it's arguments in binary form /// @param responseType - [Not used] /// @returns a new query instance static std::unique_ptr Create(RouteId receiverRid, Crypto::PrivateSignature const& gatewayPrivateSignature, Crypto::PublicKey const& agentPublicKey, Crypto::PrivateKey const& gatewayPrivateKey, ByteView commandWithArguments, ResponseType responseType = ResponseType::None) { auto query = std::make_unique(Propagation::Agent, receiverRid, gatewayPrivateSignature, responseType); query->EncrpytQueryWithBody(commandWithArguments, agentPublicKey, gatewayPrivateKey); return query; } private: /// Inherit Constructors. using QueryToAgent::QueryToAgent; }; /// Query to add a new route (eg. to new agent) struct AddRoute : QueryToRoute<1> { /// Create new instance. /// @param receiverRid - destination route id /// @param gatewayPrivateSignature - gateway's private signature /// @param commandWithArguments - plaintext command with it's arguments in binary form /// @param responseType - [Not used] /// @returns a new query instance static std::unique_ptr Create(RouteId receiverRid, Crypto::PrivateSignature const& gatewayPrivateSignature, ByteView commandWithArguments, ResponseType responseType = ResponseType::None) { auto query = std::make_unique(Propagation::Route, receiverRid, gatewayPrivateSignature, responseType); query->m_QueryPacketBody = commandWithArguments; return query; } private: /// Inherit Constructors. using QueryToRoute::QueryToRoute; }; /// Query agent to run command on it's device struct RunCommandOnDeviceQuery : QueryToAgent<2> { /// Create new instance. /// @param receiverRid - destination route id /// @param gatewayPrivateSignature - gateway's private signature /// @param agentPublicKey - destination agent's public key /// @param gatewayPrivateKey - gateway's private key /// @param deviceToRunOn - device which should execute command /// @param commandWithArguments - plaintext command with it's arguments in binary form /// @param responseType - [Not used] /// @returns a new query instance static std::unique_ptr Create(RouteId receiverRid, Crypto::PrivateSignature const& gatewayPrivateSignature, Crypto::PublicKey const& agentPublicKey, Crypto::PrivateKey const& gatewayPrivateKey, DeviceId deviceToRunOn, ByteView commandWithArguments, ResponseType responseType = ResponseType::None) { auto query = std::make_unique(Propagation::Agent, receiverRid, gatewayPrivateSignature, responseType); query->EncrpytQueryWithBody(deviceToRunOn.ToByteVector().Concat(commandWithArguments), agentPublicKey, gatewayPrivateKey); return query; } private: /// Inherit Constructors. using QueryToAgent::QueryToAgent; }; /// Query agent to run command on it's device struct DeliverToBinder : QueryToAgent<3> { /// Create new instance. /// @param receiverRid - destination route id /// @param gatewayPrivateSignature - gateway's private signature /// @param agentPublicKey - destination agent's public key /// @param gatewayPrivateKey - gateway's private key /// @param deliverTo - device to deliver message to /// @param commandWithArguments - message to binder /// @param responseType - [Not used] /// @returns a new query instance static std::unique_ptr Create(RouteId receiverRid, Crypto::PrivateSignature const& gatewayPrivateSignature, Crypto::PublicKey const& agentPublicKey, Crypto::PrivateKey const& gatewayPrivateKey, DeviceId deliverTo, ByteView commandWithArguments, ResponseType responseType = ResponseType::None) { auto query = std::make_unique(Propagation::Agent, receiverRid, gatewayPrivateSignature, responseType); query->EncrpytQueryWithBody(deliverTo.ToByteVector().Concat(commandWithArguments), agentPublicKey, gatewayPrivateKey); return query; } private: /// Inherit Constructors. using QueryToAgent::QueryToAgent; }; /// Base class for G2X queries request handler struct RequestHandler { /// empty RunCommandOnAgentQuery handler virtual void On(RunCommandOnAgentQuery) {}; /// empty AddRoute handler virtual void On(AddRoute) {}; /// empty RunCommandOnDeviceQuery handler virtual void On(RunCommandOnDeviceQuery) {}; /// empty DeliverToBinder handler virtual void On(DeliverToBinder) {}; /// Dispatch the query packet to suitable handler /// @param sender - device that originally received packet /// @param destinationRoute - destination route of this packet /// @param packetAtProcedureNumber - query packet starting with procedure number void ParseRequestAndHandleIt(std::weak_ptr sender, RouteId destinationRoute, ByteView packetAtProcedureNumber) { auto procedureNo = ReadProcedureNo(packetAtProcedureNumber); HandleQuery(sender, destinationRoute, procedureNo, packetAtProcedureNumber); } private: /// Dispatch the query packet to suitable handler /// @param sender - device that originally received packet /// @param destinationRoute - destination route of this packet /// @param procedureNo - query procedure number /// @param packetAfterProcedureNumber - query packet without procedure number void HandleQuery(std::weak_ptr sender, RouteId destinationRoute, ProceduresUnderlyingType procedureNo, ByteView packetAfterProcedureNumber) { switch (procedureNo) { case RunCommandOnAgentQuery::GetProcedureNumberConstexpr(): return On(RunCommandOnAgentQuery{ sender, destinationRoute, procedureNo, packetAfterProcedureNumber }); case AddRoute::GetProcedureNumberConstexpr(): return On(AddRoute{sender, destinationRoute, procedureNo, packetAfterProcedureNumber}); case RunCommandOnDeviceQuery::GetProcedureNumberConstexpr(): return On(RunCommandOnDeviceQuery{sender, destinationRoute, procedureNo, packetAfterProcedureNumber}); case DeliverToBinder::GetProcedureNumberConstexpr(): return On(DeliverToBinder{ sender, destinationRoute, procedureNo, packetAfterProcedureNumber }); } throw std::invalid_argument{ OBF("Unknown G2X Query Procedure number: ") + std::to_string(procedureNo) + '.' }; } }; }