Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
secondary_rpc_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <netinet/tcp.h>
4 
5 #include "aktualizr_secondary_interface.h"
6 #include "ipuptanesecondary.h"
7 #include "logging/logging.h"
8 #include "msg_dispatcher.h"
9 #include "secondary_tcp_server.h"
10 #include "test_utils.h"
11 
13  public:
14  SecondaryMock(const Uptane::EcuSerial& serial, const Uptane::HardwareIdentifier& hdw_id, const PublicKey& pub_key,
15  const Uptane::Manifest& manifest)
16  : serial_(serial), hdw_id_(hdw_id), pub_key_(pub_key), manifest_(manifest), msg_dispatcher_{*this} {}
17 
18  public:
19  MsgDispatcher& getDispatcher() { return msg_dispatcher_; }
20  Uptane::EcuSerial getSerial() const { return serial_; }
21  Uptane::HardwareIdentifier getHwId() const { return hdw_id_; }
22  PublicKey getPublicKey() const { return pub_key_; }
23 
24  std::tuple<Uptane::EcuSerial, Uptane::HardwareIdentifier, PublicKey> getInfo() const override {
25  return {serial_, hdw_id_, pub_key_};
26  }
27 
28  Uptane::Manifest getManifest() const override { return manifest_; }
29 
30  bool putMetadata(const Uptane::RawMetaPack& meta_pack) override {
31  metapack_ = meta_pack;
32  return true;
33  }
34 
35  bool sendFirmware(const std::string& data) override {
36  data_ = data;
37  return true;
38  }
39 
40  data::ResultCode::Numeric install(const std::string& target_name) override {
41  (void)target_name;
42  return data::ResultCode::Numeric::kOk;
43  }
44 
45  public:
46  const Uptane::EcuSerial serial_;
47  const Uptane::HardwareIdentifier hdw_id_;
48  const PublicKey pub_key_;
49  const Uptane::Manifest manifest_;
50 
51  Uptane::RawMetaPack metapack_;
52  std::string data_;
53 
54  private:
55  AktualizrSecondaryMsgDispatcher msg_dispatcher_;
56 };
57 
58 bool operator==(const Uptane::RawMetaPack& lhs, const Uptane::RawMetaPack& rhs) {
59  return (lhs.director_root == rhs.director_root) && (lhs.image_root == rhs.image_root) &&
60  (lhs.director_targets == rhs.director_targets) && (lhs.image_snapshot == rhs.image_snapshot) &&
61  (lhs.image_timestamp == rhs.image_timestamp) && (lhs.image_targets == rhs.image_targets);
62 }
63 
64 // Test the serialization/deserialization and the TCP/IP communication implementation
65 // that occurs during communication between Primary and IP Secondary
66 TEST(SecondaryTcpServer, TestIpSecondaryRPC) {
67  // secondary object on Secondary ECU
68  SecondaryMock secondary(Uptane::EcuSerial("serial"), Uptane::HardwareIdentifier("hardware-id"),
69  PublicKey("pub-key", KeyType::kED25519), Uptane::Manifest());
70 
71  // create Secondary on Secondary ECU, and run it in a dedicated thread
72  SecondaryTcpServer secondary_server(secondary.getDispatcher(), "", 0);
73  std::thread secondary_server_thread{[&secondary_server]() { secondary_server.run(); }};
74 
75  secondary_server.wait_until_running();
76  // create Secondary on Primary ECU, try it a few times since the secondary thread
77  // might not be ready at the moment of the first try
78  Uptane::SecondaryInterface::Ptr ip_secondary =
79  Uptane::IpUptaneSecondary::connectAndCreate("localhost", secondary_server.port());
80 
81  ASSERT_TRUE(ip_secondary != nullptr) << "Failed to create IP Secondary";
82  EXPECT_EQ(ip_secondary->getSerial(), secondary.getSerial());
83  EXPECT_EQ(ip_secondary->getHwId(), secondary.getHwId());
84  EXPECT_EQ(ip_secondary->getPublicKey(), secondary.getPublicKey());
85  EXPECT_EQ(ip_secondary->getManifest(), secondary.getManifest());
86 
87  Uptane::RawMetaPack meta_pack{"director-root", "director-target", "image_root",
88  "image_targets", "image_timestamp", "image_snapshot"};
89 
90  EXPECT_TRUE(ip_secondary->putMetadata(meta_pack));
91  EXPECT_TRUE(meta_pack == secondary.metapack_);
92 
93  std::string firmware = "firmware";
94  EXPECT_TRUE(ip_secondary->sendFirmware(firmware));
95  EXPECT_EQ(firmware, secondary.data_);
96 
97  EXPECT_EQ(ip_secondary->install(""), data::ResultCode::Numeric::kOk);
98 
99  secondary_server.stop();
100  secondary_server_thread.join();
101 }
102 
103 TEST(SecondaryTcpServer, TestIpSecondaryIfSecondaryIsNotRunning) {
104  in_port_t secondary_port = TestUtils::getFreePortAsInt();
105  Uptane::SecondaryInterface::Ptr ip_secondary;
106 
107  // trying to connect to a non-running Secondary and create a corresponding instance on Primary
108  ip_secondary = Uptane::IpUptaneSecondary::connectAndCreate("localhost", secondary_port);
109  EXPECT_EQ(ip_secondary, nullptr);
110 
111  // create Primary's secondary without connecting to Secondary
112  ip_secondary = std::make_shared<Uptane::IpUptaneSecondary>("localhost", secondary_port, Uptane::EcuSerial("serial"),
114  PublicKey("key", KeyType::kED25519));
115 
116  Uptane::RawMetaPack meta_pack{"director-root", "director-target", "image_root",
117  "image_targets", "image_timestamp", "image_snapshot"};
118 
119  // expect failures since the secondary is not running
120  EXPECT_EQ(ip_secondary->getManifest(), Json::Value());
121  EXPECT_FALSE(ip_secondary->sendFirmware("firmware"));
122  EXPECT_FALSE(ip_secondary->putMetadata(meta_pack));
123  EXPECT_EQ(ip_secondary->install(""), data::ResultCode::Numeric::kInternalError);
124 }
125 
126 class SecondaryRpcTestNegative : public ::testing::Test {
127  protected:
129  : secondary_server_{msg_dispatcher_, "", 0}, secondary_server_thread_{[&]() { secondary_server_.run(); }} {
130  msg_dispatcher_.registerHandler(AKIpUptaneMes_PR_installReq, [](Asn1Message& in_msg, Asn1Message& out_msg) {
131  (void)in_msg;
132  out_msg.present(AKIpUptaneMes_PR_installResp).installResp()->result = AKInstallationResultCode_ok;
133  // out_msg.installResp()->result = AKInstallationResultCode_ok;
134  return MsgDispatcher::HandleStatusCode::kOk;
135  });
136  secondary_server_.wait_until_running();
137  }
138 
140  secondary_server_.stop();
141  secondary_server_thread_.join();
142  }
143 
144  AKIpUptaneMes_PR sendInstallMsg() {
145  // compose and send a valid message
146  Asn1Message::Ptr req(Asn1Message::Empty());
147  req->present(AKIpUptaneMes_PR_installReq);
148 
149  // prepare request message
150  auto req_mes = req->installReq();
151  SetString(&req_mes->hash, "target_name");
152  // send request and receive response, a request-response type of RPC
153  std::pair<std::string, uint16_t> secondary_server_addr{"127.0.0.1", secondary_server_.port()};
154  auto resp = Asn1Rpc(req, secondary_server_addr);
155 
156  return resp->present();
157  }
158 
159  protected:
160  MsgDispatcher msg_dispatcher_;
161  SecondaryTcpServer secondary_server_;
162  std::thread secondary_server_thread_;
163 };
164 
165 // The given test fails because the Secondary TCP server implementation
166 // is a single-threaded, synchronous and blocking hence it cannot accept any new connections
167 // until the current one is closed. Therefore, if a client/Primary does not close its socket for some reason then
168 // Secondary becomes "unavailable"
169 // TEST_F(SecondaryRpcTestNegative, primaryNotClosingSocket) {
170 // ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
171 // con_sock.connect();
172 // ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
173 //}
174 
175 TEST_F(SecondaryRpcTestNegative, primaryConnectAndDisconnect) {
176  ConnectionSocket{"127.0.0.1", secondary_server_.port()}.connect();
177  // do a valid request/response exchange to verify if Secondary works as expected
178  // after accepting and closing a new connection
179  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
180 }
181 
182 TEST_F(SecondaryRpcTestNegative, primaryConnectAndSendValidButNotSupportedMsg) {
183  {
184  ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
185  con_sock.connect();
186  uint8_t garbage[] = {0x30, 0x13, 0x02, 0x01, 0x05, 0x16, 0x0e, 0x41, 0x6e, 0x79, 0x62,
187  0x6f, 0x64, 0x79, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x3f};
188  send(*con_sock, garbage, sizeof(garbage), 0);
189  }
190  // do a valid request/response exchange to verify if Secondary works as expected
191  // after receiving not-supported message
192  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
193 }
194 
195 TEST_F(SecondaryRpcTestNegative, primaryConnectAndSendBrokenAsn1Msg) {
196  {
197  ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
198  con_sock.connect();
199  uint8_t garbage[] = {0x30, 0x99, 0x02, 0x01, 0x05, 0x16, 0x0e, 0x41, 0x6e, 0x79,
200  0x62, 0x6f, 0x64, 0x79, 0x20, 0x74, 0x68, 0x72, 0x65, 0x3f};
201  send(*con_sock, garbage, sizeof(garbage), 0);
202  }
203  // do a valid request/response exchange to verify if Secondary works as expected
204  // after receiving broken ASN1 message
205  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
206 }
207 
208 TEST_F(SecondaryRpcTestNegative, primaryConnectAndSendGarbage) {
209  {
210  ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
211  con_sock.connect();
212  uint8_t garbage[] = "some garbage message";
213  send(*con_sock, garbage, sizeof(garbage), 0);
214  }
215  // do a valid request/response exchange to verify if Secondary works as expected
216  // after receiving some garbage
217  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
218 }
219 
220 int main(int argc, char** argv) {
221  ::testing::InitGoogleTest(&argc, argv);
222 
223  logger_init();
224  logger_set_threshold(boost::log::trivial::debug);
225 
226  return RUN_ALL_TESTS();
227 }
Asn1Message::Empty
static Asn1Message::Ptr Empty()
Create a new Asn1Message, in order to fill it with data and send it.
Definition: asn1_message.h:46
AktualizrSecondaryMsgDispatcher
Definition: msg_dispatcher.h:35
IAktualizrSecondary
Definition: aktualizr_secondary_interface.h:6
ConnectionSocket
Definition: utils.h:146
SecondaryMock
Definition: secondary_rpc_test.cc:12
data
General data structures.
Definition: types.cc:55
Uptane::HardwareIdentifier
Definition: tuf.h:146
Uptane::RawMetaPack
Definition: tuf.h:507
Uptane::EcuSerial
Definition: tuf.h:177
SecondaryTcpServer
Listens on a socket, decodes calls (ASN.1) and forwards them to an Uptane Secondary implementation.
Definition: secondary_tcp_server.h:16
SecondaryTcpServer::run
void run()
Accept connections on the socket, decode requests and respond using the secondary implementation.
Definition: secondary_tcp_server.cc:32
SecondaryRpcTestNegative
Definition: secondary_rpc_test.cc:126
PublicKey
Definition: crypto.h:26
MsgDispatcher
Definition: msg_dispatcher.h:12
data::ResultCode::Numeric::kInternalError
SWM Internal integrity error.
Asn1Message
Reference counted holder for the top-level ASN1 message structure.
Definition: asn1_message.h:34
data::ResultCode::Numeric
Numeric
Definition: types.h:128
Uptane::Manifest
Definition: manifest.h:15