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*/, const char* /*line*/,
327                                   const char* /*func*/, const char* format, ...)
328 {
329     logs.push_back(format);
330     return 0;
331 }
332 }
333 
334 class PayloadLogging : public testing::Test
335 {
336   public:
337     void SetUp()
338     {
339         logs.clear();
340     }
341 };
342 
343 TEST_F(PayloadLogging, TrailingOk)
344 {
345     {
346         ipmi::message::Payload p({1, 2});
347     }
348     EXPECT_EQ(logs.size(), 0);
349 }
350 
351 TEST_F(PayloadLogging, EnforcingUnchecked)
352 {
353     {
354         ipmi::message::Payload p({1, 2});
355         p.trailingOk = false;
356     }
357     EXPECT_EQ(logs.size(), 1);
358 }
359 
360 TEST_F(PayloadLogging, EnforcingUncheckedUnpacked)
361 {
362     {
363         ipmi::message::Payload p({1, 2});
364         p.trailingOk = false;
365         uint8_t out;
366         p.unpack(out, out);
367     }
368     EXPECT_EQ(logs.size(), 1);
369 }
370 
371 TEST_F(PayloadLogging, EnforcingUncheckedError)
372 {
373     {
374         ipmi::message::Payload p({1, 2});
375         p.trailingOk = false;
376         uint32_t out;
377         p.unpack(out);
378     }
379     EXPECT_EQ(logs.size(), 0);
380 }
381 
382 TEST_F(PayloadLogging, EnforcingChecked)
383 {
384     {
385         ipmi::message::Payload p({1, 2});
386         p.trailingOk = false;
387         EXPECT_FALSE(p.fullyUnpacked());
388     }
389     EXPECT_EQ(logs.size(), 0);
390 }
391 
392 TEST_F(PayloadLogging, EnforcingCheckedUnpacked)
393 {
394     {
395         ipmi::message::Payload p({1, 2});
396         p.trailingOk = false;
397         uint8_t out;
398         p.unpack(out, out);
399         EXPECT_TRUE(p.fullyUnpacked());
400     }
401     EXPECT_EQ(logs.size(), 0);
402 }
403 
404 TEST_F(PayloadLogging, EnforcingUnpackPayload)
405 {
406     {
407         ipmi::message::Payload p;
408         {
409             ipmi::message::Payload q({1, 2});
410             q.trailingOk = false;
411             q.unpack(p);
412         }
413         EXPECT_EQ(logs.size(), 0);
414     }
415     EXPECT_EQ(logs.size(), 1);
416 }
417 
418 TEST_F(PayloadLogging, EnforcingMove)
419 {
420     {
421         ipmi::message::Payload p;
422         {
423             ipmi::message::Payload q({1, 2});
424             q.trailingOk = false;
425             p = std::move(q);
426         }
427         EXPECT_EQ(logs.size(), 0);
428     }
429     EXPECT_EQ(logs.size(), 1);
430 }
431 
432 TEST_F(PayloadLogging, EnforcingException)
433 {
434     try
435     {
436         ipmi::message::Payload p({1, 2});
437         p.trailingOk = false;
438         throw std::runtime_error("test");
439     }
440     catch (...)
441     {}
442     EXPECT_EQ(logs.size(), 0);
443 }
444