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)
13  : SecondaryTcpServer(secondary, port) {
14  ConnectionSocket conn_socket(primary_ip, primary_port, listen_socket_.port());
15  if (conn_socket.connect() == 0) {
16  LOG_INFO << "Connected to Primary, sending info about this secondary...";
17  HandleOneConnection(*conn_socket);
18  } else {
19  LOG_INFO << "Failed to connect to Primary";
20  }
21 }
22 
24  if (listen(*listen_socket_, SOMAXCONN) < 0) {
25  throw std::system_error(errno, std::system_category(), "listen");
26  }
27  LOG_INFO << "Secondary TCP server listens on " << listen_socket_.toString();
28 
29  while (keep_running_.load()) {
30  int con_fd;
31  sockaddr_storage peer_sa{};
32  socklen_t peer_sa_size = sizeof(sockaddr_storage);
33 
34  LOG_DEBUG << "Waiting for connection from client...";
35  if ((con_fd = accept(*listen_socket_, reinterpret_cast<sockaddr *>(&peer_sa), &peer_sa_size)) == -1) {
36  LOG_INFO << "Socket accept failed. aborting";
37  break;
38  }
39  LOG_DEBUG << "Connected...";
40  HandleOneConnection(con_fd);
41  LOG_DEBUG << "Client disconnected";
42  }
43  LOG_INFO << "Secondary TCP server exit";
44 }
45 
46 void SecondaryTcpServer::stop() {
47  keep_running_ = false;
48  // unblock accept
49  ConnectionSocket("localhost", listen_socket_.port()).connect();
50 }
51 
52 in_port_t SecondaryTcpServer::port() const { return listen_socket_.port(); }
53 
54 void SecondaryTcpServer::HandleOneConnection(int socket) {
55  // Outside the message loop, because one recv() may have parts of 2 messages
56  // Note that one recv() call returning 2+ messages doesn't work at the
57  // moment. This shouldn't be a problem until we have messages that aren't
58  // strictly request/response
59  DequeueBuffer buffer;
60 
61  while (true) { // Keep reading until we get an error
62  // Read an incomming message
63  AKIpUptaneMes_t *m = nullptr;
64  asn_dec_rval_t res;
65  asn_codec_ctx_s context{};
66  ssize_t received;
67  do {
68  received = recv(socket, buffer.Tail(), buffer.TailSpace(), 0);
69  LOG_TRACE << "Got " << received << " bytes "
70  << Utils::toBase64(std::string(buffer.Tail(), static_cast<size_t>(received)));
71  buffer.HaveEnqueued(static_cast<size_t>(received));
72  res = ber_decode(&context, &asn_DEF_AKIpUptaneMes, reinterpret_cast<void **>(&m), buffer.Head(), buffer.Size());
73  buffer.Consume(res.consumed);
74  } while (res.code == RC_WMORE && received > 0);
75  // Note that ber_decode allocates *m even on failure, so this must always be done
76  Asn1Message::Ptr msg = Asn1Message::FromRaw(&m);
77 
78  if (res.code != RC_OK) {
79  return; // Either an error or the client closed the socket
80  }
81 
82  // Figure out what to do with the message
83  Asn1Message::Ptr resp = Asn1Message::Empty();
84  switch (msg->present()) {
85  case AKIpUptaneMes_PR_getInfoReq: {
86  Uptane::EcuSerial serial = impl_.getSerial();
87  Uptane::HardwareIdentifier hw_id = impl_.getHwId();
88  PublicKey pk = impl_.getPublicKey();
89  resp->present(AKIpUptaneMes_PR_getInfoResp);
90  auto r = resp->getInfoResp();
91  SetString(&r->ecuSerial, serial.ToString());
92  SetString(&r->hwId, hw_id.ToString());
93  r->keyType = static_cast<AKIpUptaneKeyType_t>(pk.Type());
94  SetString(&r->key, pk.Value());
95  } break;
96  case AKIpUptaneMes_PR_manifestReq: {
97  std::string manifest = Utils::jsonToStr(impl_.getManifest());
98  resp->present(AKIpUptaneMes_PR_manifestResp);
99  auto r = resp->manifestResp();
100  r->manifest.present = manifest_PR_json;
101  SetString(&r->manifest.choice.json, manifest); // NOLINT
102  } break;
103  case AKIpUptaneMes_PR_putMetaReq: {
104  auto md = msg->putMetaReq();
105  Uptane::RawMetaPack meta_pack;
106  if (md->image.present == image_PR_json) {
107  meta_pack.image_root = ToString(md->image.choice.json.root); // NOLINT
108  meta_pack.image_targets = ToString(md->image.choice.json.targets); // NOLINT
109  meta_pack.image_snapshot = ToString(md->image.choice.json.snapshot); // NOLINT
110  meta_pack.image_timestamp = ToString(md->image.choice.json.timestamp); // NOLINT
111  } else {
112  LOG_WARNING << "Images metadata in unknown format:" << md->image.present;
113  }
114 
115  if (md->director.present == director_PR_json) {
116  meta_pack.director_root = ToString(md->director.choice.json.root); // NOLINT
117  meta_pack.director_targets = ToString(md->director.choice.json.targets); // NOLINT
118  } else {
119  LOG_WARNING << "Director metadata in unknown format:" << md->director.present;
120  }
121  bool ok;
122  try {
123  ok = impl_.putMetadata(meta_pack);
124  } catch (Uptane::SecurityException &e) {
125  LOG_WARNING << "Rejected metadata push because of security failure" << e.what();
126  ok = false;
127  }
128  resp->present(AKIpUptaneMes_PR_putMetaResp);
129  auto r = resp->putMetaResp();
130  r->result = ok ? AKInstallationResult_success : AKInstallationResult_failure;
131  } break;
132  case AKIpUptaneMes_PR_sendFirmwareReq: {
133  auto fw = msg->sendFirmwareReq();
134  auto send_firmware_result = impl_.sendFirmware(ToString(fw->firmware));
135  resp->present(AKIpUptaneMes_PR_sendFirmwareResp);
136  auto r = resp->sendFirmwareResp();
137  r->result = send_firmware_result ? AKInstallationResult_success : AKInstallationResult_failure;
138  } break;
139  case AKIpUptaneMes_PR_installReq: {
140  auto request = msg->installReq();
141 
142  auto install_result = impl_.install(ToString(request->hash));
143 
144  resp->present(AKIpUptaneMes_PR_installResp);
145  auto response_message = resp->installResp();
146  response_message->result = static_cast<AKInstallationResultCode_t>(install_result);
147  } break;
148  default:
149  LOG_ERROR << "Unrecognised message type:" << msg->present();
150  return;
151  }
152 
153  // Send the response
154  if (resp->present() != AKIpUptaneMes_PR_NOTHING) {
155  int optval = 0;
156  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
157  asn_enc_rval_t encode_result =
158  der_encode(&asn_DEF_AKIpUptaneMes, &resp->msg_, Asn1SocketWriteCallback, reinterpret_cast<void *>(&socket));
159  if (encode_result.encoded == -1) {
160  return; // write error
161  }
162  optval = 1;
163  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
164  } else {
165  LOG_DEBUG << "Not sending a response to message " << msg->present();
166  }
167  } // Go back round and read another message
168 
169  // Parse error => Shutdown the socket
170  // write error => Shutdown the socket
171  // Timeout on write => shutdown
172 }
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::Size
size_t Size()
The number of elements that are valid (have been written) after Head()
Definition: dequeue_buffer.cc:11
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:145
Uptane::HardwareIdentifier
Definition: tuf.h:143
Uptane::RawMetaPack
Definition: tuf.h:532
Uptane::EcuSerial
Definition: tuf.h:174
SecondaryTcpServer
Listens on a socket, decodes calls (ASN.1) and forwards them to an Uptane Secondary implementation.
Definition: secondary_tcp_server.h:15
SecondaryTcpServer::run
void run()
Accept connections on the socket, decode requests and respond using the secondary implementation.
Definition: secondary_tcp_server.cc:23
PublicKey
Definition: crypto.h:26
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
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