xref: /openbmc/bmcweb/http/utility.hpp (revision 141d9431)
1 #pragma once
2 
3 #include <openssl/crypto.h>
4 
5 #include <boost/date_time/posix_time/posix_time.hpp>
6 #include <boost/url/url.hpp>
7 
8 #include <array>
9 #include <chrono>
10 #include <cstdint>
11 #include <ctime>
12 #include <functional>
13 #include <limits>
14 #include <stdexcept>
15 #include <string>
16 #include <string_view>
17 #include <tuple>
18 #include <type_traits>
19 #include <utility>
20 
21 namespace crow
22 {
23 namespace black_magic
24 {
25 
26 constexpr unsigned findClosingTag(std::string_view s, unsigned p)
27 {
28     return s[p] == '>' ? p : findClosingTag(s, p + 1);
29 }
30 
31 constexpr bool isInt(std::string_view s, unsigned i)
32 {
33     return s.substr(i, 5) == "<int>";
34 }
35 
36 constexpr bool isUint(std::string_view s, unsigned i)
37 {
38     return s.substr(i, 6) == "<uint>";
39 }
40 
41 constexpr bool isFloat(std::string_view s, unsigned i)
42 {
43     return s.substr(i, 7) == "<float>" || s.substr(i, 8) == "<double>";
44 }
45 
46 constexpr bool isStr(std::string_view s, unsigned i)
47 {
48     return s.substr(i, 5) == "<str>" || s.substr(i, 8) == "<string>";
49 }
50 
51 constexpr bool isPath(std::string_view s, unsigned i)
52 {
53     return s.substr(i, 6) == "<path>";
54 }
55 
56 template <typename T>
57 constexpr int getParameterTag()
58 {
59     if constexpr (std::is_same_v<int, T>)
60     {
61         return 1;
62     }
63     if constexpr (std::is_same_v<char, T>)
64     {
65         return 1;
66     }
67     if constexpr (std::is_same_v<short, T>)
68     {
69         return 1;
70     }
71     if constexpr (std::is_same_v<long, T>)
72     {
73         return 1;
74     }
75     if constexpr (std::is_same_v<long long, T>)
76     {
77         return 1;
78     }
79     if constexpr (std::is_same_v<unsigned int, T>)
80     {
81         return 2;
82     }
83     if constexpr (std::is_same_v<unsigned char, T>)
84     {
85         return 2;
86     }
87     if constexpr (std::is_same_v<unsigned short, T>)
88     {
89         return 2;
90     }
91     if constexpr (std::is_same_v<unsigned long, T>)
92     {
93         return 2;
94     }
95     if constexpr (std::is_same_v<unsigned long long, T>)
96     {
97         return 2;
98     }
99     if constexpr (std::is_same_v<double, T>)
100     {
101         return 3;
102     }
103     if constexpr (std::is_same_v<std::string, T>)
104     {
105         return 4;
106     }
107     return 0;
108 }
109 
110 template <typename... Args>
111 struct computeParameterTagFromArgsList;
112 
113 template <>
114 struct computeParameterTagFromArgsList<>
115 {
116     static constexpr int value = 0;
117 };
118 
119 template <typename Arg, typename... Args>
120 struct computeParameterTagFromArgsList<Arg, Args...>
121 {
122     static constexpr int subValue =
123         computeParameterTagFromArgsList<Args...>::value;
124     static constexpr int value =
125         getParameterTag<typename std::decay<Arg>::type>()
126             ? subValue * 6 + getParameterTag<typename std::decay<Arg>::type>()
127             : subValue;
128 };
129 
130 inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
131 {
132 
133     if (a == 0)
134     {
135         return b == 0;
136     }
137     if (b == 0)
138     {
139         return a == 0;
140     }
141     uint64_t sa = a % 6;
142     uint64_t sb = a % 6;
143     if (sa == 5)
144     {
145         sa = 4;
146     }
147     if (sb == 5)
148     {
149         sb = 4;
150     }
151     if (sa != sb)
152     {
153         return false;
154     }
155     return isParameterTagCompatible(a / 6, b / 6);
156 }
157 
158 constexpr uint64_t getParameterTag(std::string_view s, unsigned p = 0)
159 {
160 
161     if (p == s.size())
162     {
163         return 0;
164     }
165 
166     if (s[p] != '<')
167     {
168         return getParameterTag(s, p + 1);
169     }
170 
171     if (isInt(s, p))
172     {
173         return getParameterTag(s, findClosingTag(s, p)) * 6 + 1;
174     }
175 
176     if (isUint(s, p))
177     {
178         return getParameterTag(s, findClosingTag(s, p)) * 6 + 2;
179     }
180 
181     if (isFloat(s, p))
182     {
183         return getParameterTag(s, findClosingTag(s, p)) * 6 + 3;
184     }
185 
186     if (isStr(s, p))
187     {
188         return getParameterTag(s, findClosingTag(s, p)) * 6 + 4;
189     }
190 
191     if (isPath(s, p))
192     {
193         return getParameterTag(s, findClosingTag(s, p)) * 6 + 5;
194     }
195 
196     throw std::runtime_error("invalid parameter type");
197 }
198 
199 template <typename... T>
200 struct S
201 {
202     template <typename U>
203     using push = S<U, T...>;
204     template <typename U>
205     using push_back = S<T..., U>;
206     template <template <typename... Args> class U>
207     using rebind = U<T...>;
208 };
209 
210 template <typename F, typename Set>
211 struct CallHelper;
212 
213 template <typename F, typename... Args>
214 struct CallHelper<F, S<Args...>>
215 {
216     template <typename F1, typename... Args1,
217               typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
218     static char test(int);
219 
220     template <typename...>
221     static int test(...);
222 
223     static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
224 };
225 
226 template <uint64_t N>
227 struct SingleTagToType
228 {};
229 
230 template <>
231 struct SingleTagToType<1>
232 {
233     using type = int64_t;
234 };
235 
236 template <>
237 struct SingleTagToType<2>
238 {
239     using type = uint64_t;
240 };
241 
242 template <>
243 struct SingleTagToType<3>
244 {
245     using type = double;
246 };
247 
248 template <>
249 struct SingleTagToType<4>
250 {
251     using type = std::string;
252 };
253 
254 template <>
255 struct SingleTagToType<5>
256 {
257     using type = std::string;
258 };
259 
260 template <uint64_t Tag>
261 struct Arguments
262 {
263     using subarguments = typename Arguments<Tag / 6>::type;
264     using type = typename subarguments::template push<
265         typename SingleTagToType<Tag % 6>::type>;
266 };
267 
268 template <>
269 struct Arguments<0>
270 {
271     using type = S<>;
272 };
273 
274 template <typename T>
275 struct Promote
276 {
277     using type = T;
278 };
279 
280 template <typename T>
281 using PromoteT = typename Promote<T>::type;
282 
283 template <>
284 struct Promote<char>
285 {
286     using type = int64_t;
287 };
288 template <>
289 struct Promote<short>
290 {
291     using type = int64_t;
292 };
293 template <>
294 struct Promote<int>
295 {
296     using type = int64_t;
297 };
298 template <>
299 struct Promote<long>
300 {
301     using type = int64_t;
302 };
303 template <>
304 struct Promote<long long>
305 {
306     using type = int64_t;
307 };
308 template <>
309 struct Promote<unsigned char>
310 {
311     using type = uint64_t;
312 };
313 template <>
314 struct Promote<unsigned short>
315 {
316     using type = uint64_t;
317 };
318 template <>
319 struct Promote<unsigned int>
320 {
321     using type = uint64_t;
322 };
323 template <>
324 struct Promote<unsigned long>
325 {
326     using type = uint64_t;
327 };
328 template <>
329 struct Promote<unsigned long long>
330 {
331     using type = uint64_t;
332 };
333 
334 } // namespace black_magic
335 
336 namespace detail
337 {
338 
339 template <class T, std::size_t N, class... Args>
340 struct GetIndexOfElementFromTupleByTypeImpl
341 {
342     static constexpr std::size_t value = N;
343 };
344 
345 template <class T, std::size_t N, class... Args>
346 struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...>
347 {
348     static constexpr std::size_t value = N;
349 };
350 
351 template <class T, std::size_t N, class U, class... Args>
352 struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...>
353 {
354     static constexpr std::size_t value =
355         GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value;
356 };
357 
358 } // namespace detail
359 
360 namespace utility
361 {
362 template <class T, class... Args>
363 T& getElementByType(std::tuple<Args...>& t)
364 {
365     return std::get<
366         detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t);
367 }
368 
369 template <typename T>
370 struct function_traits;
371 
372 template <typename T>
373 struct function_traits : public function_traits<decltype(&T::operator())>
374 {
375     using parent_t = function_traits<decltype(&T::operator())>;
376     static const size_t arity = parent_t::arity;
377     using result_type = typename parent_t::result_type;
378     template <size_t i>
379     using arg = typename parent_t::template arg<i>;
380 };
381 
382 template <typename ClassType, typename r, typename... Args>
383 struct function_traits<r (ClassType::*)(Args...) const>
384 {
385     static const size_t arity = sizeof...(Args);
386 
387     using result_type = r;
388 
389     template <size_t i>
390     using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
391 };
392 
393 template <typename ClassType, typename r, typename... Args>
394 struct function_traits<r (ClassType::*)(Args...)>
395 {
396     static const size_t arity = sizeof...(Args);
397 
398     using result_type = r;
399 
400     template <size_t i>
401     using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
402 };
403 
404 template <typename r, typename... Args>
405 struct function_traits<std::function<r(Args...)>>
406 {
407     static const size_t arity = sizeof...(Args);
408 
409     using result_type = r;
410 
411     template <size_t i>
412     using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
413 };
414 
415 inline std::string base64encode(const std::string_view data)
416 {
417     const std::array<char, 64> key = {
418         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
419         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
420         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
421         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
422         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
423 
424     size_t size = data.size();
425     std::string ret;
426     ret.resize((size + 2) / 3 * 4);
427     auto it = ret.begin();
428 
429     size_t i = 0;
430     while (i < size)
431     {
432         size_t keyIndex = 0;
433 
434         keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
435         *it++ = key[keyIndex];
436 
437         if (i + 1 < size)
438         {
439             keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
440             keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
441             *it++ = key[keyIndex];
442 
443             if (i + 2 < size)
444             {
445                 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
446                 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
447                 *it++ = key[keyIndex];
448 
449                 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
450                 *it++ = key[keyIndex];
451             }
452             else
453             {
454                 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
455                 *it++ = key[keyIndex];
456                 *it++ = '=';
457             }
458         }
459         else
460         {
461             keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
462             *it++ = key[keyIndex];
463             *it++ = '=';
464             *it++ = '=';
465         }
466 
467         i += 3;
468     }
469 
470     return ret;
471 }
472 
473 // TODO this is temporary and should be deleted once base64 is refactored out of
474 // crow
475 inline bool base64Decode(const std::string_view input, std::string& output)
476 {
477     static const char nop = static_cast<char>(-1);
478     // See note on encoding_data[] in above function
479     static const std::array<char, 256> decodingData = {
480         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
481         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
482         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
483         nop, 62,  nop, nop, nop, 63,  52,  53,  54,  55,  56,  57,  58,  59,
484         60,  61,  nop, nop, nop, nop, nop, nop, nop, 0,   1,   2,   3,   4,
485         5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,
486         19,  20,  21,  22,  23,  24,  25,  nop, nop, nop, nop, nop, nop, 26,
487         27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
488         41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  nop, nop, nop,
489         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
490         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
491         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
492         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
493         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
494         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
495         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
496         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
497         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
498         nop, nop, nop, nop};
499 
500     size_t inputLength = input.size();
501 
502     // allocate space for output string
503     output.clear();
504     output.reserve(((inputLength + 2) / 3) * 4);
505 
506     auto getCodeValue = [](char c) {
507         auto code = static_cast<unsigned char>(c);
508         // Ensure we cannot index outside the bounds of the decoding array
509         static_assert(std::numeric_limits<decltype(code)>::max() <
510                       decodingData.size());
511         return decodingData[code];
512     };
513 
514     // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
515     // dropping first two bits
516     // and regenerate into 3 8-bits sequences
517 
518     for (size_t i = 0; i < inputLength; i++)
519     {
520         char base64code0 = 0;
521         char base64code1 = 0;
522         char base64code2 = 0; // initialized to 0 to suppress warnings
523         char base64code3 = 0;
524 
525         base64code0 = getCodeValue(input[i]);
526         if (base64code0 == nop)
527         { // non base64 character
528             return false;
529         }
530         if (!(++i < inputLength))
531         { // we need at least two input bytes for first
532           // byte output
533             return false;
534         }
535         base64code1 = getCodeValue(input[i]);
536         if (base64code1 == nop)
537         { // non base64 character
538             return false;
539         }
540         output +=
541             static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
542 
543         if (++i < inputLength)
544         {
545             char c = input[i];
546             if (c == '=')
547             { // padding , end of input
548                 return (base64code1 & 0x0f) == 0;
549             }
550             base64code2 = getCodeValue(input[i]);
551             if (base64code2 == nop)
552             { // non base64 character
553                 return false;
554             }
555             output += static_cast<char>(((base64code1 << 4) & 0xf0) |
556                                         ((base64code2 >> 2) & 0x0f));
557         }
558 
559         if (++i < inputLength)
560         {
561             char c = input[i];
562             if (c == '=')
563             { // padding , end of input
564                 return (base64code2 & 0x03) == 0;
565             }
566             base64code3 = getCodeValue(input[i]);
567             if (base64code3 == nop)
568             { // non base64 character
569                 return false;
570             }
571             output +=
572                 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
573         }
574     }
575 
576     return true;
577 }
578 
579 namespace details
580 {
581 constexpr uint64_t maxMilliSeconds = 253402300799999;
582 constexpr uint64_t maxSeconds = 253402300799;
583 inline std::string getDateTime(boost::posix_time::milliseconds timeSinceEpoch)
584 {
585     boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
586     boost::posix_time::ptime time = epoch + timeSinceEpoch;
587     // append zero offset to the end according to the Redfish spec for Date-Time
588     return boost::posix_time::to_iso_extended_string(time) + "+00:00";
589 }
590 } // namespace details
591 
592 // Returns the formatted date time string.
593 // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
594 // the given |secondsSinceEpoch| is too large, we return the maximum supported
595 // date. This behavior is to avoid exceptions throwed by Boost.
596 inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
597 {
598     secondsSinceEpoch = std::min(secondsSinceEpoch, details::maxSeconds);
599     boost::posix_time::seconds boostSeconds(secondsSinceEpoch);
600     return details::getDateTime(
601         boost::posix_time::milliseconds(boostSeconds.total_milliseconds()));
602 }
603 
604 // Returns the formatted date time string.
605 // Note that the maximum supported date is 9999-12-31T23:59:59.999+00:00, if
606 // the given |millisSecondsSinceEpoch| is too large, we return the maximum
607 // supported date.
608 inline std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)
609 {
610     milliSecondsSinceEpoch =
611         std::min(details::maxMilliSeconds, milliSecondsSinceEpoch);
612     return details::getDateTime(
613         boost::posix_time::milliseconds(milliSecondsSinceEpoch));
614 }
615 
616 inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
617 {
618     if (secondsSinceEpoch > static_cast<int64_t>(details::maxSeconds))
619     {
620         secondsSinceEpoch = static_cast<std::time_t>(details::maxSeconds);
621     }
622     boost::posix_time::ptime time =
623         boost::posix_time::from_time_t(secondsSinceEpoch);
624     return boost::posix_time::to_iso_extended_string(time) + "+00:00";
625 }
626 
627 /**
628  * Returns the current Date, Time & the local Time Offset
629  * infromation in a pair
630  *
631  * @param[in] None
632  *
633  * @return std::pair<std::string, std::string>, which consist
634  * of current DateTime & the TimeOffset strings respectively.
635  */
636 inline std::pair<std::string, std::string> getDateTimeOffsetNow()
637 {
638     std::time_t time = std::time(nullptr);
639     std::string dateTime = getDateTimeStdtime(time);
640 
641     /* extract the local Time Offset value from the
642      * recevied dateTime string.
643      */
644     std::string timeOffset("Z00:00");
645     std::size_t lastPos = dateTime.size();
646     std::size_t len = timeOffset.size();
647     if (lastPos > len)
648     {
649         timeOffset = dateTime.substr(lastPos - len);
650     }
651 
652     return std::make_pair(dateTime, timeOffset);
653 }
654 
655 inline bool constantTimeStringCompare(const std::string_view a,
656                                       const std::string_view b)
657 {
658     // Important note, this function is ONLY constant time if the two input
659     // sizes are the same
660     if (a.size() != b.size())
661     {
662         return false;
663     }
664     return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
665 }
666 
667 struct ConstantTimeCompare
668 {
669     bool operator()(const std::string_view a, const std::string_view b) const
670     {
671         return constantTimeStringCompare(a, b);
672     }
673 };
674 
675 namespace details
676 {
677 inline boost::urls::url
678     urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
679 {
680     boost::urls::url url("/");
681     for (const std::string_view& arg : args)
682     {
683         url.segments().push_back(arg);
684     }
685     return url;
686 }
687 } // namespace details
688 
689 template <typename... AV>
690 inline boost::urls::url urlFromPieces(const AV... args)
691 {
692     return details::urlFromPiecesDetail({args...});
693 }
694 
695 } // namespace utility
696 } // namespace crow
697