1 /**
2  * Copyright © 2018 Intel Corporation
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 #define SD_JOURNAL_SUPPRESS_LOCATION
17 
18 #include <systemd/sd-journal.h>
19 
20 #include <ipmid/api.hpp>
21 #include <ipmid/message.hpp>
22 
23 #include <stdexcept>
24 
25 #include <gtest/gtest.h>
26 
27 TEST(Payload, InputSize)
28 {
29     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
30     size_t input_size = i.size();
31     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
32     ASSERT_EQ(input_size, p.size());
33 }
34 
35 TEST(Payload, OutputSize)
36 {
37     ipmi::message::Payload p;
38     ASSERT_EQ(0, p.size());
39     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
40     p.pack(i);
41     ASSERT_EQ(i.size(), p.size());
42     p.pack(i);
43     p.pack(i);
44     ASSERT_EQ(3 * i.size(), p.size());
45 }
46 
47 TEST(Payload, Resize)
48 {
49     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
50     ipmi::message::Payload p;
51     p.pack(i);
52     p.resize(16);
53     ASSERT_EQ(p.size(), 16);
54 }
55 
56 TEST(Payload, Data)
57 {
58     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
59     ipmi::message::Payload p;
60     p.pack(i);
61     ASSERT_NE(nullptr, p.data());
62 }
63 
64 TEST(PayloadResponse, Append)
65 {
66     std::string s("0123456789abcdef");
67     ipmi::message::Payload p;
68     p.append(s.data(), s.data() + s.size());
69     ASSERT_EQ(s.size(), p.size());
70 }
71 
72 TEST(PayloadResponse, AppendDrain)
73 {
74     std::string s("0123456789abcdef");
75     ipmi::message::Payload p;
76     bool b = true;
77     // first pack a lone bit
78     p.pack(b);
79     p.append(s.data(), s.data() + s.size());
80     // append will 'drain' first, padding the lone bit into a full byte
81     ASSERT_EQ(s.size() + 1, p.size());
82 }
83 
84 TEST(PayloadResponse, AppendBits)
85 {
86     ipmi::message::Payload p;
87     p.appendBits(3, 0b101);
88     ASSERT_EQ(p.bitStream, 0b101);
89     p.appendBits(4, 0b1100);
90     ASSERT_EQ(p.bitStream, 0b1100101);
91     p.appendBits(1, 0b1);
92     ASSERT_EQ(p.bitStream, 0);
93     ASSERT_EQ(p.bitCount, 0);
94     // appended 8 bits, should be one byte
95     ASSERT_EQ(p.size(), 1);
96     ipmi::SecureBuffer k1 = {0b11100101};
97     ASSERT_EQ(p.raw, k1);
98     p.appendBits(7, 0b1110111);
99     // appended 7 more bits, should still be one byte
100     ASSERT_EQ(p.size(), 1);
101     p.drain();
102     // drain forces padding; should be two bytes now
103     ASSERT_EQ(p.size(), 2);
104     ipmi::SecureBuffer k2 = {0b11100101, 0b01110111};
105     ASSERT_EQ(p.raw, k2);
106 }
107 
108 TEST(PayloadResponse, Drain16Bits)
109 {
110     ipmi::message::Payload p;
111     p.bitStream = 0b1011010011001111;
112     p.bitCount = 16;
113     p.drain();
114     ASSERT_EQ(p.size(), 2);
115     ASSERT_EQ(p.bitCount, 0);
116     ASSERT_EQ(p.bitStream, 0);
117     ipmi::SecureBuffer k1 = {0b11001111, 0b10110100};
118     ASSERT_EQ(p.raw, k1);
119 }
120 
121 TEST(PayloadResponse, Drain15Bits)
122 {
123     ipmi::message::Payload p;
124     p.bitStream = 0b101101001100111;
125     p.bitCount = 15;
126     p.drain();
127     ASSERT_EQ(p.size(), 2);
128     ASSERT_EQ(p.bitCount, 0);
129     ASSERT_EQ(p.bitStream, 0);
130     ipmi::SecureBuffer k1 = {0b1100111, 0b1011010};
131     ASSERT_EQ(p.raw, k1);
132 }
133 
134 TEST(PayloadResponse, Drain15BitsWholeBytesOnly)
135 {
136     ipmi::message::Payload p;
137     p.bitStream = 0b101101001100111;
138     p.bitCount = 15;
139     p.drain(true);
140     // only the first whole byte should have been 'drained' into p.raw
141     ASSERT_EQ(p.size(), 1);
142     ASSERT_EQ(p.bitCount, 7);
143     ASSERT_EQ(p.bitStream, 0b1011010);
144     ipmi::SecureBuffer k1 = {0b1100111};
145     ASSERT_EQ(p.raw, k1);
146 }
147 
148 TEST(PayloadRequest, Pop)
149 {
150     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
151     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
152     const auto& [vb, ve] = p.pop<uint8_t>(4);
153     std::vector<uint8_t> v(vb, ve);
154     std::vector<uint8_t> k = {0xbf, 0x04, 0x86, 0x00};
155     ASSERT_EQ(v, k);
156 }
157 
158 TEST(PayloadRequest, FillBits)
159 {
160     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
161     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
162     p.fillBits(5);
163     ASSERT_FALSE(p.unpackError);
164     ASSERT_EQ(p.bitStream, 0xbf);
165     ASSERT_EQ(p.bitCount, 8);
166     // should still have 5 bits available, no change
167     p.fillBits(5);
168     ASSERT_FALSE(p.unpackError);
169     ASSERT_EQ(p.bitStream, 0xbf);
170     ASSERT_EQ(p.bitCount, 8);
171     // discard 5 bits (low order)
172     p.popBits(5);
173     // should add another byte into the stream (high order)
174     p.fillBits(5);
175     ASSERT_FALSE(p.unpackError);
176     ASSERT_EQ(p.bitStream, 0x25);
177     ASSERT_EQ(p.bitCount, 11);
178 }
179 
180 TEST(PayloadRequest, FillBitsTooManyBits)
181 {
182     ipmi::SecureBuffer i = {1, 2, 3, 4, 5, 6, 7, 8, 9};
183     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
184     p.fillBits(72);
185     ASSERT_TRUE(p.unpackError);
186 }
187 
188 TEST(PayloadRequest, FillBitsNotEnoughBytes)
189 {
190     ipmi::SecureBuffer i = {1, 2, 3, 4};
191     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
192     p.fillBits(48);
193     ASSERT_TRUE(p.unpackError);
194 }
195 
196 TEST(PayloadRequest, PopBits)
197 {
198     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
199     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
200     p.fillBits(4);
201     uint8_t v = p.popBits(4);
202     ASSERT_FALSE(p.unpackError);
203     ASSERT_EQ(p.bitStream, 0x0b);
204     ASSERT_EQ(p.bitCount, 4);
205     ASSERT_EQ(v, 0x0f);
206 }
207 
208 TEST(PayloadRequest, PopBitsNoFillBits)
209 {
210     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
211     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
212     p.popBits(4);
213     ASSERT_TRUE(p.unpackError);
214 }
215 
216 TEST(PayloadRequest, DiscardBits)
217 {
218     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
219     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
220     p.fillBits(5);
221     ASSERT_FALSE(p.unpackError);
222     ASSERT_EQ(p.bitStream, 0xbf);
223     ASSERT_EQ(p.bitCount, 8);
224     p.discardBits();
225     ASSERT_FALSE(p.unpackError);
226     ASSERT_EQ(p.bitStream, 0);
227     ASSERT_EQ(p.bitCount, 0);
228 }
229 
230 TEST(PayloadRequest, FullyUnpacked)
231 {
232     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
233     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
234     uint32_t v1;
235     p.unpack(v1);
236     // still one remaining byte
237     ASSERT_FALSE(p.fullyUnpacked());
238     p.fillBits(3);
239     p.popBits(3);
240     // still five remaining bits
241     ASSERT_FALSE(p.fullyUnpacked());
242     p.fillBits(5);
243     p.popBits(5);
244     // fully unpacked, should be no errors
245     ASSERT_TRUE(p.fullyUnpacked());
246     p.fillBits(4);
247     // fullyUnpacked fails because an attempt to unpack too many bytes
248     ASSERT_FALSE(p.fullyUnpacked());
249 }
250 
251 TEST(PayloadRequest, ResetInternal)
252 {
253     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
254     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
255     p.fillBits(4);
256     p.unpackError = true;
257     p.reset();
258     ASSERT_EQ(p.rawIndex, 0);
259     ASSERT_EQ(p.bitStream, 0);
260     ASSERT_EQ(p.bitCount, 0);
261     ASSERT_FALSE(p.unpackError);
262 }
263 
264 TEST(PayloadRequest, ResetUsage)
265 {
266     // Payload.reset is used to rewind the unpacking to the initial
267     // state. This is needed so that OEM commands can unpack the group
268     // number or the IANA to determine which handler needs to be called
269     ipmi::SecureBuffer i = {0x04, 0x86};
270     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
271     uint8_t v1;
272     // check that the number of bytes matches
273     ASSERT_EQ(p.unpack(v1), 0);
274     // check that the payload was not fully unpacked
275     ASSERT_FALSE(p.fullyUnpacked());
276     uint8_t k1 = 0x04;
277     // check that the bytes were correctly unpacked (LSB first)
278     ASSERT_EQ(v1, k1);
279     // do a reset on the payload
280     p.reset();
281     // unpack a uint16
282     uint16_t v2;
283     // check that the number of bytes matches
284     ASSERT_EQ(p.unpack(v2), 0);
285     // check that the payload was fully unpacked
286     ASSERT_TRUE(p.fullyUnpacked());
287     uint16_t k2 = 0x8604;
288     // check that the bytes were correctly unpacked (LSB first)
289     ASSERT_EQ(v2, k2);
290 }
291 
292 TEST(PayloadRequest, PartialPayload)
293 {
294     ipmi::SecureBuffer i = {0xbf, 0x04, 0x86, 0x00, 0x02};
295     ipmi::message::Payload p(std::forward<ipmi::SecureBuffer>(i));
296     uint8_t v1;
297     ipmi::message::Payload localPayload;
298     // check that the number of bytes matches
299     ASSERT_EQ(p.unpack(v1, localPayload), 0);
300     // check that the payload was partially unpacked and not in error
301     ASSERT_FALSE(p.fullyUnpacked());
302     ASSERT_FALSE(p.unpackError);
303     // check that the 'extracted' payload is not fully unpacked
304     ASSERT_FALSE(localPayload.fullyUnpacked());
305     uint8_t k1 = 0xbf;
306     // check that the bytes were correctly unpacked (LSB first)
307     ASSERT_EQ(v1, k1);
308     uint32_t v2;
309     // unpack using the 'extracted' payload
310     ASSERT_EQ(localPayload.unpack(v2), 0);
311     ASSERT_TRUE(localPayload.fullyUnpacked());
312     uint32_t k2 = 0x02008604;
313     ASSERT_EQ(v2, k2);
314 }
315 
316 std::vector<std::string> logs;
317 
318 extern "C"
319 {
320     int sd_journal_send(const char* format, ...)
321     {
322         logs.push_back(format);
323         return 0;
324     }
325 
326     int sd_journal_send_with_location(const char* /*file*/,
327                                       const char* /*line*/,
328                                       const char* /*func*/, const char* format,
329                                       ...)
330     {
331         logs.push_back(format);
332         return 0;
333     }
334 }
335 
336 class PayloadLogging : public testing::Test
337 {
338   public:
339     void SetUp()
340     {
341         logs.clear();
342     }
343 };
344 
345 TEST_F(PayloadLogging, TrailingOk)
346 {
347     {
348         ipmi::message::Payload p({1, 2});
349     }
350     EXPECT_EQ(logs.size(), 0);
351 }
352 
353 TEST_F(PayloadLogging, EnforcingUnchecked)
354 {
355     {
356         ipmi::message::Payload p({1, 2});
357         p.trailingOk = false;
358     }
359     EXPECT_EQ(logs.size(), 1);
360 }
361 
362 TEST_F(PayloadLogging, EnforcingUncheckedUnpacked)
363 {
364     {
365         ipmi::message::Payload p({1, 2});
366         p.trailingOk = false;
367         uint8_t out;
368         p.unpack(out, out);
369     }
370     EXPECT_EQ(logs.size(), 1);
371 }
372 
373 TEST_F(PayloadLogging, EnforcingUncheckedError)
374 {
375     {
376         ipmi::message::Payload p({1, 2});
377         p.trailingOk = false;
378         uint32_t out;
379         p.unpack(out);
380     }
381     EXPECT_EQ(logs.size(), 0);
382 }
383 
384 TEST_F(PayloadLogging, EnforcingChecked)
385 {
386     {
387         ipmi::message::Payload p({1, 2});
388         p.trailingOk = false;
389         EXPECT_FALSE(p.fullyUnpacked());
390     }
391     EXPECT_EQ(logs.size(), 0);
392 }
393 
394 TEST_F(PayloadLogging, EnforcingCheckedUnpacked)
395 {
396     {
397         ipmi::message::Payload p({1, 2});
398         p.trailingOk = false;
399         uint8_t out;
400         p.unpack(out, out);
401         EXPECT_TRUE(p.fullyUnpacked());
402     }
403     EXPECT_EQ(logs.size(), 0);
404 }
405 
406 TEST_F(PayloadLogging, EnforcingUnpackPayload)
407 {
408     {
409         ipmi::message::Payload p;
410         {
411             ipmi::message::Payload q({1, 2});
412             q.trailingOk = false;
413             q.unpack(p);
414         }
415         EXPECT_EQ(logs.size(), 0);
416     }
417     EXPECT_EQ(logs.size(), 1);
418 }
419 
420 TEST_F(PayloadLogging, EnforcingMove)
421 {
422     {
423         ipmi::message::Payload p;
424         {
425             ipmi::message::Payload q({1, 2});
426             q.trailingOk = false;
427             p = std::move(q);
428         }
429         EXPECT_EQ(logs.size(), 0);
430     }
431     EXPECT_EQ(logs.size(), 1);
432 }
433 
434 TEST_F(PayloadLogging, EnforcingException)
435 {
436     try
437     {
438         ipmi::message::Payload p({1, 2});
439         p.trailingOk = false;
440         throw std::runtime_error("test");
441     }
442     catch (...)
443     {}
444     EXPECT_EQ(logs.size(), 0);
445 }
446