1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "common/libs/confui/packet.h"
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <optional>
21 #include <string>
22 #include <vector>
23 
24 namespace cuttlefish {
25 namespace confui {
26 namespace packet {
ReadRawData(SharedFD s)27 static std::optional<std::vector<std::uint8_t>> ReadRawData(SharedFD s) {
28   if (!s->IsOpen()) {
29     ConfUiLog(ERROR) << "file, socket, etc, is not open to read";
30     return std::nullopt;
31   }
32   packet::PayloadHeader p;
33   auto nread = ReadExactBinary(s, &p);
34 
35   if (nread != sizeof(p)) {
36     ConfUiLog(ERROR) << nread << " and sizeof(p) = " << sizeof(p)
37                      << " not matching";
38     return std::nullopt;
39   }
40   if (p.payload_length_ == 0) {
41     return {{}};
42   }
43 
44   if (p.payload_length_ >= packet::kMaxPayloadLength) {
45     ConfUiLog(ERROR) << "Payload length " << p.payload_length_
46                      << " must be less than " << packet::kMaxPayloadLength;
47     return std::nullopt;
48   }
49 
50   std::unique_ptr<char[]> buf{new char[p.payload_length_ + 1]};
51   nread = ReadExact(s, buf.get(), p.payload_length_);
52   buf[p.payload_length_] = 0;
53   if (nread != p.payload_length_) {
54     ConfUiLog(ERROR) << "The length ReadRawData read does not match.";
55     return std::nullopt;
56   }
57   std::vector<std::uint8_t> result{buf.get(), buf.get() + nread};
58 
59   return {result};
60 }
61 
ParseRawData(const std::vector<std::uint8_t> & data_to_parse)62 static std::optional<ParsedPacket> ParseRawData(
63     const std::vector<std::uint8_t>& data_to_parse) {
64   /*
65    * data_to_parse has 0 in it, so it is not exactly "your (text) std::string."
66    * If we type-cast data_to_parse to std::string and use 3rd party std::string-
67    * processing libraries, the outcome might be incorrect. However, the header
68    * part has no '\0' in it, and is actually a sequence of letters, or a text.
69    * So, we use android::base::Split() to take the header
70    *
71    */
72   std::string as_string{data_to_parse.begin(), data_to_parse.end()};
73   auto tokens = android::base::Split(as_string, ":");
74   CHECK(tokens.size() >= 3)
75       << "Raw packet for confirmation UI must have at least"
76       << " three components.";
77   /**
78    * Here is how the raw data, i.e. tokens[2:] looks like
79    *
80    * n:l[0]:l[1]:l[2]:...:l[n-1]:data[0]data[1]data[2]...data[n]
81    *
82    * Thus it basically has the number of items, the lengths of each item,
83    * and the byte representation of each item. n and l[i] are separated by ':'
84    * Note that the byte representation may have ':' in it. This could mess
85    * up the parsing if we totally depending on ':' separation.
86    *
87    * However, it is safe to assume that there's no ':' inside n or
88    * the string for l[i]. So, we do anyway split the data_to_parse by ':',
89    * and take n and from l[0] through l[n-1] only.
90    */
91   std::string session_id = tokens[0];
92   std::string cmd_type = tokens[1];
93   if (!IsOnlyDigits(tokens[2])) {
94     ConfUiLog(ERROR) << "Token[2] of the ConfUi packet should be a number";
95     return std::nullopt;
96   }
97   const int n = std::stoi(tokens[2]);
98 
99   if (n + 2 > tokens.size()) {
100     ConfUiLog(ERROR) << "The ConfUi packet is ill-formatted.";
101     return std::nullopt;
102   }
103   ConfUiPacketInfo data_to_return;
104   std::vector<int> lengths;
105   lengths.reserve(n);
106   for (int i = 1; i <= n; i++) {
107     if (!IsOnlyDigits(tokens[2 + i])) {
108       ConfUiLog(ERROR) << tokens[2 + i] << " should be a number but is not.";
109       return std::nullopt;
110     }
111     lengths.emplace_back(std::stoi(tokens[2 + i]));
112   }
113   // to find the first position of the non-header part
114   int pos = 0;
115   // 3 for three ":"s
116   pos += tokens[0].size() + tokens[1].size() + tokens[2].size() + 3;
117   for (int i = 1; i <= n; i++) {
118     pos += tokens[2 + i].size() + 1;
119   }
120   int expected_total_length = pos;
121   for (auto const len : lengths) {
122     expected_total_length += len;
123   }
124   if (expected_total_length != data_to_parse.size()) {
125     ConfUiLog(ERROR) << "expected length in ParseRawData is "
126                      << expected_total_length << " while the actual length is "
127                      << data_to_parse.size();
128     return std::nullopt;
129   }
130   for (const auto len : lengths) {
131     if (len == 0) {
132       // push null vector or whatever empty, appropriately-typed
133       // container
134       data_to_return.emplace_back(std::vector<std::uint8_t>{});
135       continue;
136     }
137     data_to_return.emplace_back(data_to_parse.begin() + pos,
138                                 data_to_parse.begin() + pos + len);
139     pos = pos + len;
140   }
141   ParsedPacket result{session_id, cmd_type, data_to_return};
142   return {result};
143 }
144 
ReadPayload(SharedFD s)145 std::optional<ParsedPacket> ReadPayload(SharedFD s) {
146   auto raw_data = ReadRawData(s);
147   if (!raw_data) {
148     ConfUiLog(ERROR) << "raw data returned std::nullopt";
149     return std::nullopt;
150   }
151   auto parsed_result = ParseRawData(raw_data.value());
152   if (!parsed_result) {
153     ConfUiLog(ERROR) << "parsed result returns nullopt";
154     return std::nullopt;
155   }
156   return parsed_result;
157 }
158 }  // end of namespace packet
159 }  // end of namespace confui
160 }  // end of namespace cuttlefish
161