Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
secondary_tcp_server.cc
1 #include "secondary_tcp_server.h"
2 
3 #include "AKIpUptaneMes.h"
4 #include "asn1/asn1_message.h"
5 #include "logging/logging.h"
6 #include "uptane/secondaryinterface.h"
7 #include "utilities/dequeue_buffer.h"
8 
9 #include <netinet/tcp.h>
10 
11 SecondaryTcpServer::SecondaryTcpServer(Uptane::SecondaryInterface &secondary, const std::string &primary_ip,
12  in_port_t primary_port, in_port_t port, bool reboot_after_install)
13  : impl_(secondary), listen_socket_(port), keep_running_(true), reboot_after_install_(reboot_after_install) {
14  if (primary_ip.empty()) {
15  return;
16  }
17 
18  ConnectionSocket conn_socket(primary_ip, primary_port, listen_socket_.port());
19  if (conn_socket.connect() == 0) {
20  LOG_INFO << "Connected to Primary, sending info about this Secondary.";
21  HandleOneConnection(*conn_socket);
22  } else {
23  LOG_INFO << "Failed to connect to Primary.";
24  }
25 }
26 
28  if (listen(*listen_socket_, SOMAXCONN) < 0) {
29  throw std::system_error(errno, std::system_category(), "listen");
30  }
31  LOG_INFO << "Secondary TCP server listening on " << listen_socket_.toString();
32 
33  while (keep_running_.load()) {
34  sockaddr_storage peer_sa{};
35  socklen_t peer_sa_size = sizeof(sockaddr_storage);
36 
37  LOG_DEBUG << "Waiting for connection from Primary...";
38  int con_fd = accept(*listen_socket_, reinterpret_cast<sockaddr *>(&peer_sa), &peer_sa_size);
39  if (con_fd == -1) {
40  LOG_INFO << "Socket accept failed, aborting.";
41  break;
42  }
43  Socket con_socket(con_fd);
44  LOG_DEBUG << "Connected to Primary.";
45  bool continue_serving = HandleOneConnection(*con_socket);
46  LOG_DEBUG << "Primary disconnected.";
47  if (!continue_serving) {
48  break;
49  }
50  }
51  LOG_INFO << "Secondary TCP server exiting.";
52 }
53 
54 void SecondaryTcpServer::stop() {
55  keep_running_ = false;
56  // unblock accept
57  ConnectionSocket("localhost", listen_socket_.port()).connect();
58 }
59 
60 in_port_t SecondaryTcpServer::port() const { return listen_socket_.port(); }
61 SecondaryTcpServer::ExitReason SecondaryTcpServer::exit_reason() const { return exit_reason_; }
62 
63 bool SecondaryTcpServer::HandleOneConnection(int socket) {
64  // Outside the message loop, because one recv() may have parts of 2 messages
65  // Note that one recv() call returning 2+ messages doesn't work at the
66  // moment. This shouldn't be a problem until we have messages that aren't
67  // strictly request/response
68  DequeueBuffer buffer;
69 
70  while (true) { // Keep reading until we get an error
71  // Read an incomming message
72  AKIpUptaneMes_t *m = nullptr;
73  asn_dec_rval_t res;
74  asn_codec_ctx_s context{};
75  ssize_t received;
76  bool need_reboot = false;
77 
78  do {
79  received = recv(socket, buffer.Tail(), buffer.TailSpace(), 0);
80  buffer.HaveEnqueued(static_cast<size_t>(received));
81  res = ber_decode(&context, &asn_DEF_AKIpUptaneMes, reinterpret_cast<void **>(&m), buffer.Head(), buffer.Size());
82  buffer.Consume(res.consumed);
83  } while (res.code == RC_WMORE && received > 0);
84  // Note that ber_decode allocates *m even on failure, so this must always be done
85  Asn1Message::Ptr msg = Asn1Message::FromRaw(&m);
86 
87  if (res.code != RC_OK) {
88  return true; // Either an error or the client closed the socket
89  }
90 
91  // Figure out what to do with the message
92  Asn1Message::Ptr resp = Asn1Message::Empty();
93  switch (msg->present()) {
94  case AKIpUptaneMes_PR_getInfoReq: {
95  Uptane::EcuSerial serial = impl_.getSerial();
96  Uptane::HardwareIdentifier hw_id = impl_.getHwId();
97  PublicKey pk = impl_.getPublicKey();
98  resp->present(AKIpUptaneMes_PR_getInfoResp);
99  auto r = resp->getInfoResp();
100  SetString(&r->ecuSerial, serial.ToString());
101  SetString(&r->hwId, hw_id.ToString());
102  r->keyType = static_cast<AKIpUptaneKeyType_t>(pk.Type());
103  SetString(&r->key, pk.Value());
104  } break;
105  case AKIpUptaneMes_PR_manifestReq: {
106  std::string manifest = Utils::jsonToStr(impl_.getManifest());
107  resp->present(AKIpUptaneMes_PR_manifestResp);
108  auto r = resp->manifestResp();
109  r->manifest.present = manifest_PR_json;
110  SetString(&r->manifest.choice.json, manifest); // NOLINT
111  LOG_TRACE << "Manifest : \n" << manifest;
112  } break;
113  case AKIpUptaneMes_PR_putMetaReq: {
114  auto md = msg->putMetaReq();
115  Uptane::RawMetaPack meta_pack;
116 
117  if (md->director.present == director_PR_json) {
118  meta_pack.director_root = ToString(md->director.choice.json.root); // NOLINT
119  meta_pack.director_targets = ToString(md->director.choice.json.targets); // NOLINT
120  LOG_DEBUG << "Received Director repo Root metadata:\n" << meta_pack.director_root;
121  LOG_DEBUG << "Received Director repo Targets metadata:\n" << meta_pack.director_targets;
122  } else {
123  LOG_WARNING << "Director metadata in unknown format:" << md->director.present;
124  }
125 
126  if (md->image.present == image_PR_json) {
127  meta_pack.image_root = ToString(md->image.choice.json.root); // NOLINT
128  meta_pack.image_timestamp = ToString(md->image.choice.json.timestamp); // NOLINT
129  meta_pack.image_snapshot = ToString(md->image.choice.json.snapshot); // NOLINT
130  meta_pack.image_targets = ToString(md->image.choice.json.targets); // NOLINT
131  LOG_DEBUG << "Received Image repo Root metadata:\n" << meta_pack.image_root;
132  LOG_DEBUG << "Received Image repo Timestamp metadata:\n" << meta_pack.image_timestamp;
133  LOG_DEBUG << "Received Image repo Snapshot metadata:\n" << meta_pack.image_snapshot;
134  LOG_DEBUG << "Received Image repo Targets metadata:\n" << meta_pack.image_targets;
135  } else {
136  LOG_WARNING << "Image repo metadata in unknown format:" << md->image.present;
137  }
138 
139  bool ok;
140  try {
141  ok = impl_.putMetadata(meta_pack);
142  } catch (Uptane::SecurityException &e) {
143  LOG_WARNING << "Rejected metadata push because of security failure" << e.what();
144  ok = false;
145  }
146  resp->present(AKIpUptaneMes_PR_putMetaResp);
147  auto r = resp->putMetaResp();
148  r->result = ok ? AKInstallationResult_success : AKInstallationResult_failure;
149  } break;
150  case AKIpUptaneMes_PR_sendFirmwareReq: {
151  LOG_INFO << "Received sendFirmwareReq from Primary.";
152  auto fw = msg->sendFirmwareReq();
153  auto send_firmware_result = impl_.sendFirmware(ToString(fw->firmware));
154  resp->present(AKIpUptaneMes_PR_sendFirmwareResp);
155  auto r = resp->sendFirmwareResp();
156  r->result = send_firmware_result ? AKInstallationResult_success : AKInstallationResult_failure;
157  } break;
158  case AKIpUptaneMes_PR_installReq: {
159  LOG_INFO << "Received installReq from Primary.";
160  auto request = msg->installReq();
161 
162  auto install_result = impl_.install(ToString(request->hash));
163 
164  resp->present(AKIpUptaneMes_PR_installResp);
165  auto response_message = resp->installResp();
166  response_message->result = static_cast<AKInstallationResultCode_t>(install_result);
167 
168  if (install_result == data::ResultCode::Numeric::kNeedCompletion) {
169  need_reboot = true;
170  }
171  } break;
172  default:
173  LOG_ERROR << "Unrecognised message type:" << msg->present();
174  return true;
175  }
176 
177  // Send the response
178  if (resp->present() != AKIpUptaneMes_PR_NOTHING) {
179  int optval = 0;
180  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
181  asn_enc_rval_t encode_result =
182  der_encode(&asn_DEF_AKIpUptaneMes, &resp->msg_, Asn1SocketWriteCallback, reinterpret_cast<void *>(&socket));
183  if (encode_result.encoded == -1) {
184  return true; // write error
185  }
186  optval = 1;
187  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
188  } else {
189  LOG_DEBUG << "Not sending a response to message " << msg->present();
190  }
191 
192  if (need_reboot && reboot_after_install_) {
193  exit_reason_ = ExitReason::kRebootNeeded;
194  return false;
195  }
196 
197  } // Go back round and read another message
198 
199  return true;
200  // Parse error => Shutdown the socket
201  // write error => Shutdown the socket
202  // Timeout on write => shutdown
203 }
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
DequeueBuffer::Tail
char * Tail()
A pointer to the next place to write data to.
Definition: dequeue_buffer.cc:32
DequeueBuffer::Consume
void Consume(size_t bytes)
Called after bytes have been read from Head().
Definition: dequeue_buffer.cc:16
ConnectionSocket
Definition: utils.h:146
Uptane::HardwareIdentifier
Definition: tuf.h:143
Uptane::RawMetaPack
Definition: tuf.h:535
Uptane::EcuSerial
Definition: tuf.h:174
SecondaryTcpServer::run
void run()
Accept connections on the socket, decode requests and respond using the secondary implementation.
Definition: secondary_tcp_server.cc:27
PublicKey
Definition: crypto.h:26
DequeueBuffer::Size
size_t Size() const
The number of elements that are valid (have been written) after Head()
Definition: dequeue_buffer.cc:11
DequeueBuffer::TailSpace
size_t TailSpace()
The number of bytes beyond Tail() that are allocated and may be written to.
Definition: dequeue_buffer.cc:38
Asn1Message::FromRaw
static Asn1Message::Ptr FromRaw(AKIpUptaneMes_t **msg)
Destructively copy from a raw msg pointer created by parsing an incomming message.
Definition: asn1_message.h:53
Socket
Definition: utils.h:127
DequeueBuffer
A dequeue based on a contiguous buffer in memory.
Definition: dequeue_buffer.h:11
Uptane::SecondaryInterface
Definition: secondaryinterface.h:12
Uptane::SecurityException
Definition: exceptions.h:21
DequeueBuffer::Head
char * Head()
A pointer to the first element that has not been Consumed().
Definition: dequeue_buffer.cc:6
DequeueBuffer::HaveEnqueued
void HaveEnqueued(size_t bytes)
Call to indicate that bytes have been written in the range Tail() ...
Definition: dequeue_buffer.cc:43