104e438cbSEd Tanous #pragma once 204e438cbSEd Tanous 3eb1c47d3SEd Tanous #include <bmcweb_config.h> 404e438cbSEd Tanous #include <openssl/crypto.h> 504e438cbSEd Tanous 6c867a83eSEd Tanous #include <boost/callable_traits.hpp> 7eae855c7SEd Tanous #include <boost/url/url.hpp> 871f2db75SEd Tanous #include <nlohmann/json.hpp> 91d8782e7SNan Zhou 109ea15c35SEd Tanous #include <array> 1174849befSEd Tanous #include <chrono> 12c715ec29SEd Tanous #include <cstddef> 1304e438cbSEd Tanous #include <cstdint> 149ea15c35SEd Tanous #include <ctime> 1504e438cbSEd Tanous #include <functional> 169896eaedSEd Tanous #include <iomanip> 179ea15c35SEd Tanous #include <limits> 1804e438cbSEd Tanous #include <stdexcept> 1904e438cbSEd Tanous #include <string> 209ea15c35SEd Tanous #include <string_view> 2104e438cbSEd Tanous #include <tuple> 229ea15c35SEd Tanous #include <type_traits> 239ea15c35SEd Tanous #include <utility> 24ca1600c1SSzymon Dompke #include <variant> 2504e438cbSEd Tanous 2604e438cbSEd Tanous namespace crow 2704e438cbSEd Tanous { 2804e438cbSEd Tanous namespace black_magic 2904e438cbSEd Tanous { 3004e438cbSEd Tanous 31c715ec29SEd Tanous enum class TypeCode : uint8_t 32c715ec29SEd Tanous { 33c715ec29SEd Tanous Unspecified = 0, 34c715ec29SEd Tanous Integer = 1, 35c715ec29SEd Tanous UnsignedInteger = 2, 36c715ec29SEd Tanous Float = 3, 37c715ec29SEd Tanous String = 4, 38c715ec29SEd Tanous Path = 5, 39c715ec29SEd Tanous Max = 6, 40c715ec29SEd Tanous }; 41c715ec29SEd Tanous 42c715ec29SEd Tanous // Remove when we have c++23 43c715ec29SEd Tanous template <typename E> 44c715ec29SEd Tanous constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept 45c715ec29SEd Tanous { 46c715ec29SEd Tanous return static_cast<typename std::underlying_type<E>::type>(e); 47c715ec29SEd Tanous } 48c715ec29SEd Tanous 4904e438cbSEd Tanous template <typename T> 50c715ec29SEd Tanous constexpr TypeCode getParameterTag() 5104e438cbSEd Tanous { 5204e438cbSEd Tanous if constexpr (std::is_same_v<int, T>) 5304e438cbSEd Tanous { 54c715ec29SEd Tanous return TypeCode::Integer; 5504e438cbSEd Tanous } 5604e438cbSEd Tanous if constexpr (std::is_same_v<char, T>) 5704e438cbSEd Tanous { 58c715ec29SEd Tanous return TypeCode::Integer; 5904e438cbSEd Tanous } 6004e438cbSEd Tanous if constexpr (std::is_same_v<short, T>) 6104e438cbSEd Tanous { 62c715ec29SEd Tanous return TypeCode::Integer; 6304e438cbSEd Tanous } 6404e438cbSEd Tanous if constexpr (std::is_same_v<long, T>) 6504e438cbSEd Tanous { 66c715ec29SEd Tanous return TypeCode::Integer; 6704e438cbSEd Tanous } 6804e438cbSEd Tanous if constexpr (std::is_same_v<long long, T>) 6904e438cbSEd Tanous { 70c715ec29SEd Tanous return TypeCode::Integer; 7104e438cbSEd Tanous } 7204e438cbSEd Tanous if constexpr (std::is_same_v<unsigned int, T>) 7304e438cbSEd Tanous { 74c715ec29SEd Tanous return TypeCode::UnsignedInteger; 7504e438cbSEd Tanous } 7604e438cbSEd Tanous if constexpr (std::is_same_v<unsigned char, T>) 7704e438cbSEd Tanous { 78c715ec29SEd Tanous return TypeCode::UnsignedInteger; 7904e438cbSEd Tanous } 8004e438cbSEd Tanous if constexpr (std::is_same_v<unsigned short, T>) 8104e438cbSEd Tanous { 82c715ec29SEd Tanous return TypeCode::UnsignedInteger; 8304e438cbSEd Tanous } 8404e438cbSEd Tanous if constexpr (std::is_same_v<unsigned long, T>) 8504e438cbSEd Tanous { 86c715ec29SEd Tanous return TypeCode::UnsignedInteger; 8704e438cbSEd Tanous } 8804e438cbSEd Tanous if constexpr (std::is_same_v<unsigned long long, T>) 8904e438cbSEd Tanous { 90c715ec29SEd Tanous return TypeCode::UnsignedInteger; 9104e438cbSEd Tanous } 9204e438cbSEd Tanous if constexpr (std::is_same_v<double, T>) 9304e438cbSEd Tanous { 94c715ec29SEd Tanous return TypeCode::Float; 9504e438cbSEd Tanous } 9604e438cbSEd Tanous if constexpr (std::is_same_v<std::string, T>) 9704e438cbSEd Tanous { 98c715ec29SEd Tanous return TypeCode::String; 9904e438cbSEd Tanous } 100c715ec29SEd Tanous return TypeCode::Unspecified; 10104e438cbSEd Tanous } 10204e438cbSEd Tanous 10304e438cbSEd Tanous template <typename... Args> 10404e438cbSEd Tanous struct computeParameterTagFromArgsList; 10504e438cbSEd Tanous 10604e438cbSEd Tanous template <> 10704e438cbSEd Tanous struct computeParameterTagFromArgsList<> 10804e438cbSEd Tanous { 10904e438cbSEd Tanous static constexpr int value = 0; 11004e438cbSEd Tanous }; 11104e438cbSEd Tanous 11204e438cbSEd Tanous template <typename Arg, typename... Args> 11304e438cbSEd Tanous struct computeParameterTagFromArgsList<Arg, Args...> 11404e438cbSEd Tanous { 11504e438cbSEd Tanous static constexpr int subValue = 11604e438cbSEd Tanous computeParameterTagFromArgsList<Args...>::value; 11704e438cbSEd Tanous static constexpr int value = 118c715ec29SEd Tanous getParameterTag<typename std::decay<Arg>::type>() != 119c715ec29SEd Tanous TypeCode::Unspecified 120c715ec29SEd Tanous ? static_cast<unsigned long>(subValue * 121c715ec29SEd Tanous toUnderlying(TypeCode::Max)) + 122c715ec29SEd Tanous static_cast<uint64_t>( 123c715ec29SEd Tanous getParameterTag<typename std::decay<Arg>::type>()) 12404e438cbSEd Tanous : subValue; 12504e438cbSEd Tanous }; 12604e438cbSEd Tanous 12704e438cbSEd Tanous inline bool isParameterTagCompatible(uint64_t a, uint64_t b) 12804e438cbSEd Tanous { 1291c30e500SEd Tanous while (true) 1301c30e500SEd Tanous { 131ef641b65SEd Tanous if (a == 0 && b == 0) 13204e438cbSEd Tanous { 133ef641b65SEd Tanous // Both tags were equivalent, parameters are compatible 134ef641b65SEd Tanous return true; 13504e438cbSEd Tanous } 136ef641b65SEd Tanous if (a == 0 || b == 0) 13704e438cbSEd Tanous { 138ef641b65SEd Tanous // one of the tags had more parameters than the other 139ef641b65SEd Tanous return false; 14004e438cbSEd Tanous } 141c715ec29SEd Tanous TypeCode sa = static_cast<TypeCode>(a % toUnderlying(TypeCode::Max)); 142c715ec29SEd Tanous TypeCode sb = static_cast<TypeCode>(b % toUnderlying(TypeCode::Max)); 143c715ec29SEd Tanous 144c715ec29SEd Tanous if (sa == TypeCode::Path) 14504e438cbSEd Tanous { 146c715ec29SEd Tanous sa = TypeCode::String; 14704e438cbSEd Tanous } 148c715ec29SEd Tanous if (sb == TypeCode::Path) 14904e438cbSEd Tanous { 150c715ec29SEd Tanous sb = TypeCode::String; 15104e438cbSEd Tanous } 15204e438cbSEd Tanous if (sa != sb) 15304e438cbSEd Tanous { 15404e438cbSEd Tanous return false; 15504e438cbSEd Tanous } 156c715ec29SEd Tanous a /= toUnderlying(TypeCode::Max); 157c715ec29SEd Tanous b /= toUnderlying(TypeCode::Max); 1581c30e500SEd Tanous } 1591c30e500SEd Tanous return false; 16004e438cbSEd Tanous } 16104e438cbSEd Tanous 1621c30e500SEd Tanous constexpr inline uint64_t getParameterTag(std::string_view url) 16304e438cbSEd Tanous { 1641c30e500SEd Tanous uint64_t tagValue = 0; 1651c30e500SEd Tanous size_t urlSegmentIndex = std::string_view::npos; 166b00dcc27SEd Tanous 1671c30e500SEd Tanous size_t paramIndex = 0; 1681c30e500SEd Tanous 1691c30e500SEd Tanous for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++) 1701c30e500SEd Tanous { 1711c30e500SEd Tanous char character = url[urlIndex]; 1721c30e500SEd Tanous if (character == '<') 1731c30e500SEd Tanous { 1741c30e500SEd Tanous if (urlSegmentIndex != std::string_view::npos) 17504e438cbSEd Tanous { 17604e438cbSEd Tanous return 0; 17704e438cbSEd Tanous } 1781c30e500SEd Tanous urlSegmentIndex = urlIndex; 1791c30e500SEd Tanous } 1801c30e500SEd Tanous if (character == '>') 18104e438cbSEd Tanous { 1821c30e500SEd Tanous if (urlSegmentIndex == std::string_view::npos) 1831c30e500SEd Tanous { 1841c30e500SEd Tanous return 0; 1851c30e500SEd Tanous } 1861c30e500SEd Tanous std::string_view tag = 1871c30e500SEd Tanous url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex); 1881c30e500SEd Tanous 1891c30e500SEd Tanous // Note, this is a really lame way to do std::pow(6, paramIndex) 1901c30e500SEd Tanous // std::pow doesn't work in constexpr in clang. 1911c30e500SEd Tanous // Ideally in the future we'd move this to use a power of 2 packing 1921c30e500SEd Tanous // (probably 8 instead of 6) so that these just become bit shifts 1931c30e500SEd Tanous uint64_t insertIndex = 1; 1941c30e500SEd Tanous for (size_t unused = 0; unused < paramIndex; unused++) 1951c30e500SEd Tanous { 1961c30e500SEd Tanous insertIndex *= 6; 19704e438cbSEd Tanous } 19804e438cbSEd Tanous 1991c30e500SEd Tanous if (tag == "<int>") 20004e438cbSEd Tanous { 201c715ec29SEd Tanous tagValue += insertIndex * toUnderlying(TypeCode::Integer); 20204e438cbSEd Tanous } 2031c30e500SEd Tanous if (tag == "<uint>") 20404e438cbSEd Tanous { 205c715ec29SEd Tanous tagValue += 206c715ec29SEd Tanous insertIndex * toUnderlying(TypeCode::UnsignedInteger); 20704e438cbSEd Tanous } 2081c30e500SEd Tanous if (tag == "<float>" || tag == "<double>") 20904e438cbSEd Tanous { 210c715ec29SEd Tanous tagValue += insertIndex * toUnderlying(TypeCode::Float); 21104e438cbSEd Tanous } 2121c30e500SEd Tanous if (tag == "<str>" || tag == "<string>") 21304e438cbSEd Tanous { 214c715ec29SEd Tanous tagValue += insertIndex * toUnderlying(TypeCode::String); 21504e438cbSEd Tanous } 2161c30e500SEd Tanous if (tag == "<path>") 21704e438cbSEd Tanous { 218c715ec29SEd Tanous tagValue += insertIndex * toUnderlying(TypeCode::Path); 21904e438cbSEd Tanous } 2201c30e500SEd Tanous paramIndex++; 2211c30e500SEd Tanous urlSegmentIndex = std::string_view::npos; 2221c30e500SEd Tanous } 2231c30e500SEd Tanous } 2241c30e500SEd Tanous if (urlSegmentIndex != std::string_view::npos) 2251c30e500SEd Tanous { 2261c30e500SEd Tanous return 0; 2271c30e500SEd Tanous } 2281c30e500SEd Tanous return tagValue; 22904e438cbSEd Tanous } 23004e438cbSEd Tanous 23104e438cbSEd Tanous template <typename... T> 23204e438cbSEd Tanous struct S 23304e438cbSEd Tanous { 23404e438cbSEd Tanous template <typename U> 23504e438cbSEd Tanous using push = S<U, T...>; 23604e438cbSEd Tanous template <typename U> 23704e438cbSEd Tanous using push_back = S<T..., U>; 23804e438cbSEd Tanous template <template <typename... Args> class U> 23904e438cbSEd Tanous using rebind = U<T...>; 24004e438cbSEd Tanous }; 24104e438cbSEd Tanous 24204e438cbSEd Tanous template <typename F, typename Set> 24304e438cbSEd Tanous struct CallHelper; 24404e438cbSEd Tanous 24504e438cbSEd Tanous template <typename F, typename... Args> 24604e438cbSEd Tanous struct CallHelper<F, S<Args...>> 24704e438cbSEd Tanous { 24804e438cbSEd Tanous template <typename F1, typename... Args1, 24904e438cbSEd Tanous typename = decltype(std::declval<F1>()(std::declval<Args1>()...))> 25004e438cbSEd Tanous static char test(int); 25104e438cbSEd Tanous 25204e438cbSEd Tanous template <typename...> 25304e438cbSEd Tanous static int test(...); 25404e438cbSEd Tanous 25504e438cbSEd Tanous static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char); 25604e438cbSEd Tanous }; 25704e438cbSEd Tanous 25804e438cbSEd Tanous template <uint64_t N> 25904e438cbSEd Tanous struct SingleTagToType 26004e438cbSEd Tanous {}; 26104e438cbSEd Tanous 26204e438cbSEd Tanous template <> 26304e438cbSEd Tanous struct SingleTagToType<1> 26404e438cbSEd Tanous { 26504e438cbSEd Tanous using type = int64_t; 26604e438cbSEd Tanous }; 26704e438cbSEd Tanous 26804e438cbSEd Tanous template <> 26904e438cbSEd Tanous struct SingleTagToType<2> 27004e438cbSEd Tanous { 27104e438cbSEd Tanous using type = uint64_t; 27204e438cbSEd Tanous }; 27304e438cbSEd Tanous 27404e438cbSEd Tanous template <> 27504e438cbSEd Tanous struct SingleTagToType<3> 27604e438cbSEd Tanous { 27704e438cbSEd Tanous using type = double; 27804e438cbSEd Tanous }; 27904e438cbSEd Tanous 28004e438cbSEd Tanous template <> 28104e438cbSEd Tanous struct SingleTagToType<4> 28204e438cbSEd Tanous { 28304e438cbSEd Tanous using type = std::string; 28404e438cbSEd Tanous }; 28504e438cbSEd Tanous 28604e438cbSEd Tanous template <> 28704e438cbSEd Tanous struct SingleTagToType<5> 28804e438cbSEd Tanous { 28904e438cbSEd Tanous using type = std::string; 29004e438cbSEd Tanous }; 29104e438cbSEd Tanous 29204e438cbSEd Tanous template <uint64_t Tag> 29304e438cbSEd Tanous struct Arguments 29404e438cbSEd Tanous { 29504e438cbSEd Tanous using subarguments = typename Arguments<Tag / 6>::type; 29604e438cbSEd Tanous using type = typename subarguments::template push< 29704e438cbSEd Tanous typename SingleTagToType<Tag % 6>::type>; 29804e438cbSEd Tanous }; 29904e438cbSEd Tanous 30004e438cbSEd Tanous template <> 30104e438cbSEd Tanous struct Arguments<0> 30204e438cbSEd Tanous { 30304e438cbSEd Tanous using type = S<>; 30404e438cbSEd Tanous }; 30504e438cbSEd Tanous 30604e438cbSEd Tanous template <typename T> 30704e438cbSEd Tanous struct Promote 30804e438cbSEd Tanous { 30904e438cbSEd Tanous using type = T; 31004e438cbSEd Tanous }; 31104e438cbSEd Tanous 31204e438cbSEd Tanous template <typename T> 31304e438cbSEd Tanous using PromoteT = typename Promote<T>::type; 31404e438cbSEd Tanous 31504e438cbSEd Tanous template <> 31604e438cbSEd Tanous struct Promote<char> 31704e438cbSEd Tanous { 31804e438cbSEd Tanous using type = int64_t; 31904e438cbSEd Tanous }; 32004e438cbSEd Tanous template <> 32104e438cbSEd Tanous struct Promote<short> 32204e438cbSEd Tanous { 32304e438cbSEd Tanous using type = int64_t; 32404e438cbSEd Tanous }; 32504e438cbSEd Tanous template <> 32604e438cbSEd Tanous struct Promote<int> 32704e438cbSEd Tanous { 32804e438cbSEd Tanous using type = int64_t; 32904e438cbSEd Tanous }; 33004e438cbSEd Tanous template <> 33104e438cbSEd Tanous struct Promote<long> 33204e438cbSEd Tanous { 33304e438cbSEd Tanous using type = int64_t; 33404e438cbSEd Tanous }; 33504e438cbSEd Tanous template <> 33604e438cbSEd Tanous struct Promote<long long> 33704e438cbSEd Tanous { 33804e438cbSEd Tanous using type = int64_t; 33904e438cbSEd Tanous }; 34004e438cbSEd Tanous template <> 34104e438cbSEd Tanous struct Promote<unsigned char> 34204e438cbSEd Tanous { 34304e438cbSEd Tanous using type = uint64_t; 34404e438cbSEd Tanous }; 34504e438cbSEd Tanous template <> 34604e438cbSEd Tanous struct Promote<unsigned short> 34704e438cbSEd Tanous { 34804e438cbSEd Tanous using type = uint64_t; 34904e438cbSEd Tanous }; 35004e438cbSEd Tanous template <> 35104e438cbSEd Tanous struct Promote<unsigned int> 35204e438cbSEd Tanous { 35304e438cbSEd Tanous using type = uint64_t; 35404e438cbSEd Tanous }; 35504e438cbSEd Tanous template <> 35604e438cbSEd Tanous struct Promote<unsigned long> 35704e438cbSEd Tanous { 35804e438cbSEd Tanous using type = uint64_t; 35904e438cbSEd Tanous }; 36004e438cbSEd Tanous template <> 36104e438cbSEd Tanous struct Promote<unsigned long long> 36204e438cbSEd Tanous { 36304e438cbSEd Tanous using type = uint64_t; 36404e438cbSEd Tanous }; 36504e438cbSEd Tanous 36604e438cbSEd Tanous } // namespace black_magic 36704e438cbSEd Tanous 36804e438cbSEd Tanous namespace utility 36904e438cbSEd Tanous { 37004e438cbSEd Tanous 37104e438cbSEd Tanous template <typename T> 372c867a83eSEd Tanous struct FunctionTraits 37304e438cbSEd Tanous { 37404e438cbSEd Tanous template <size_t i> 375c867a83eSEd Tanous using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>; 37604e438cbSEd Tanous }; 37704e438cbSEd Tanous 378d830ff5aSAdriana Kobylak inline std::string base64encode(const std::string_view data) 379d830ff5aSAdriana Kobylak { 380d830ff5aSAdriana Kobylak const std::array<char, 64> key = { 381d830ff5aSAdriana Kobylak 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 382d830ff5aSAdriana Kobylak 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 383d830ff5aSAdriana Kobylak 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 384d830ff5aSAdriana Kobylak 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 385d830ff5aSAdriana Kobylak '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; 386d830ff5aSAdriana Kobylak 387d830ff5aSAdriana Kobylak size_t size = data.size(); 388d830ff5aSAdriana Kobylak std::string ret; 389d830ff5aSAdriana Kobylak ret.resize((size + 2) / 3 * 4); 390d830ff5aSAdriana Kobylak auto it = ret.begin(); 391d830ff5aSAdriana Kobylak 392d830ff5aSAdriana Kobylak size_t i = 0; 393d830ff5aSAdriana Kobylak while (i < size) 394d830ff5aSAdriana Kobylak { 395543f4400SEd Tanous size_t keyIndex = 0; 396d830ff5aSAdriana Kobylak 397d830ff5aSAdriana Kobylak keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2; 398d830ff5aSAdriana Kobylak *it++ = key[keyIndex]; 399d830ff5aSAdriana Kobylak 400d830ff5aSAdriana Kobylak if (i + 1 < size) 401d830ff5aSAdriana Kobylak { 402d830ff5aSAdriana Kobylak keyIndex = static_cast<size_t>(data[i] & 0x03) << 4; 403d830ff5aSAdriana Kobylak keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4; 404d830ff5aSAdriana Kobylak *it++ = key[keyIndex]; 405d830ff5aSAdriana Kobylak 406d830ff5aSAdriana Kobylak if (i + 2 < size) 407d830ff5aSAdriana Kobylak { 408d830ff5aSAdriana Kobylak keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2; 409d830ff5aSAdriana Kobylak keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6; 410d830ff5aSAdriana Kobylak *it++ = key[keyIndex]; 411d830ff5aSAdriana Kobylak 412d830ff5aSAdriana Kobylak keyIndex = static_cast<size_t>(data[i + 2] & 0x3F); 413d830ff5aSAdriana Kobylak *it++ = key[keyIndex]; 414d830ff5aSAdriana Kobylak } 415d830ff5aSAdriana Kobylak else 416d830ff5aSAdriana Kobylak { 417d830ff5aSAdriana Kobylak keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2; 418d830ff5aSAdriana Kobylak *it++ = key[keyIndex]; 419d830ff5aSAdriana Kobylak *it++ = '='; 420d830ff5aSAdriana Kobylak } 421d830ff5aSAdriana Kobylak } 422d830ff5aSAdriana Kobylak else 423d830ff5aSAdriana Kobylak { 424d830ff5aSAdriana Kobylak keyIndex = static_cast<size_t>(data[i] & 0x03) << 4; 425d830ff5aSAdriana Kobylak *it++ = key[keyIndex]; 426d830ff5aSAdriana Kobylak *it++ = '='; 427d830ff5aSAdriana Kobylak *it++ = '='; 428d830ff5aSAdriana Kobylak } 429d830ff5aSAdriana Kobylak 430d830ff5aSAdriana Kobylak i += 3; 431d830ff5aSAdriana Kobylak } 432d830ff5aSAdriana Kobylak 433d830ff5aSAdriana Kobylak return ret; 434d830ff5aSAdriana Kobylak } 435d830ff5aSAdriana Kobylak 43604e438cbSEd Tanous // TODO this is temporary and should be deleted once base64 is refactored out of 43704e438cbSEd Tanous // crow 43804e438cbSEd Tanous inline bool base64Decode(const std::string_view input, std::string& output) 43904e438cbSEd Tanous { 44004e438cbSEd Tanous static const char nop = static_cast<char>(-1); 44104e438cbSEd Tanous // See note on encoding_data[] in above function 44204e438cbSEd Tanous static const std::array<char, 256> decodingData = { 44304e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 44404e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 44504e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 44604e438cbSEd Tanous nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, 44704e438cbSEd Tanous 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, 44804e438cbSEd Tanous 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 44904e438cbSEd Tanous 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26, 45004e438cbSEd Tanous 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 45104e438cbSEd Tanous 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop, 45204e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45304e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45404e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45504e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45604e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45704e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45804e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 45904e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 46004e438cbSEd Tanous nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 46104e438cbSEd Tanous nop, nop, nop, nop}; 46204e438cbSEd Tanous 46304e438cbSEd Tanous size_t inputLength = input.size(); 46404e438cbSEd Tanous 46504e438cbSEd Tanous // allocate space for output string 46604e438cbSEd Tanous output.clear(); 46704e438cbSEd Tanous output.reserve(((inputLength + 2) / 3) * 4); 46804e438cbSEd Tanous 46904e438cbSEd Tanous auto getCodeValue = [](char c) { 47004e438cbSEd Tanous auto code = static_cast<unsigned char>(c); 47104e438cbSEd Tanous // Ensure we cannot index outside the bounds of the decoding array 47204e438cbSEd Tanous static_assert(std::numeric_limits<decltype(code)>::max() < 47304e438cbSEd Tanous decodingData.size()); 47404e438cbSEd Tanous return decodingData[code]; 47504e438cbSEd Tanous }; 47604e438cbSEd Tanous 47704e438cbSEd Tanous // for each 4-bytes sequence from the input, extract 4 6-bits sequences by 47804e438cbSEd Tanous // dropping first two bits 47904e438cbSEd Tanous // and regenerate into 3 8-bits sequences 48004e438cbSEd Tanous 48104e438cbSEd Tanous for (size_t i = 0; i < inputLength; i++) 48204e438cbSEd Tanous { 483543f4400SEd Tanous char base64code0 = 0; 484543f4400SEd Tanous char base64code1 = 0; 48504e438cbSEd Tanous char base64code2 = 0; // initialized to 0 to suppress warnings 486543f4400SEd Tanous char base64code3 = 0; 48704e438cbSEd Tanous 48804e438cbSEd Tanous base64code0 = getCodeValue(input[i]); 48904e438cbSEd Tanous if (base64code0 == nop) 49004e438cbSEd Tanous { // non base64 character 49104e438cbSEd Tanous return false; 49204e438cbSEd Tanous } 49304e438cbSEd Tanous if (!(++i < inputLength)) 49404e438cbSEd Tanous { // we need at least two input bytes for first 49504e438cbSEd Tanous // byte output 49604e438cbSEd Tanous return false; 49704e438cbSEd Tanous } 49804e438cbSEd Tanous base64code1 = getCodeValue(input[i]); 49904e438cbSEd Tanous if (base64code1 == nop) 50004e438cbSEd Tanous { // non base64 character 50104e438cbSEd Tanous return false; 50204e438cbSEd Tanous } 50304e438cbSEd Tanous output += 50404e438cbSEd Tanous static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); 50504e438cbSEd Tanous 50604e438cbSEd Tanous if (++i < inputLength) 50704e438cbSEd Tanous { 50804e438cbSEd Tanous char c = input[i]; 50904e438cbSEd Tanous if (c == '=') 51004e438cbSEd Tanous { // padding , end of input 51104e438cbSEd Tanous return (base64code1 & 0x0f) == 0; 51204e438cbSEd Tanous } 51304e438cbSEd Tanous base64code2 = getCodeValue(input[i]); 51404e438cbSEd Tanous if (base64code2 == nop) 51504e438cbSEd Tanous { // non base64 character 51604e438cbSEd Tanous return false; 51704e438cbSEd Tanous } 51804e438cbSEd Tanous output += static_cast<char>(((base64code1 << 4) & 0xf0) | 51904e438cbSEd Tanous ((base64code2 >> 2) & 0x0f)); 52004e438cbSEd Tanous } 52104e438cbSEd Tanous 52204e438cbSEd Tanous if (++i < inputLength) 52304e438cbSEd Tanous { 52404e438cbSEd Tanous char c = input[i]; 52504e438cbSEd Tanous if (c == '=') 52604e438cbSEd Tanous { // padding , end of input 52704e438cbSEd Tanous return (base64code2 & 0x03) == 0; 52804e438cbSEd Tanous } 52904e438cbSEd Tanous base64code3 = getCodeValue(input[i]); 53004e438cbSEd Tanous if (base64code3 == nop) 53104e438cbSEd Tanous { // non base64 character 53204e438cbSEd Tanous return false; 53304e438cbSEd Tanous } 53404e438cbSEd Tanous output += 53504e438cbSEd Tanous static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); 53604e438cbSEd Tanous } 53704e438cbSEd Tanous } 53804e438cbSEd Tanous 53904e438cbSEd Tanous return true; 54004e438cbSEd Tanous } 54104e438cbSEd Tanous 54204e438cbSEd Tanous inline bool constantTimeStringCompare(const std::string_view a, 54304e438cbSEd Tanous const std::string_view b) 54404e438cbSEd Tanous { 54504e438cbSEd Tanous // Important note, this function is ONLY constant time if the two input 54604e438cbSEd Tanous // sizes are the same 54704e438cbSEd Tanous if (a.size() != b.size()) 54804e438cbSEd Tanous { 54904e438cbSEd Tanous return false; 55004e438cbSEd Tanous } 55104e438cbSEd Tanous return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0; 55204e438cbSEd Tanous } 55304e438cbSEd Tanous 55404e438cbSEd Tanous struct ConstantTimeCompare 55504e438cbSEd Tanous { 55604e438cbSEd Tanous bool operator()(const std::string_view a, const std::string_view b) const 55704e438cbSEd Tanous { 55804e438cbSEd Tanous return constantTimeStringCompare(a, b); 55904e438cbSEd Tanous } 56004e438cbSEd Tanous }; 56104e438cbSEd Tanous 562eae855c7SEd Tanous namespace details 563eae855c7SEd Tanous { 564eae855c7SEd Tanous inline boost::urls::url 565*c6bcedc6SWilly Tu appendUrlPieces(boost::urls::url& url, 566*c6bcedc6SWilly Tu const std::initializer_list<std::string_view> args) 567eae855c7SEd Tanous { 568eae855c7SEd Tanous for (const std::string_view& arg : args) 569eae855c7SEd Tanous { 570eae855c7SEd Tanous url.segments().push_back(arg); 571eae855c7SEd Tanous } 572eae855c7SEd Tanous return url; 573eae855c7SEd Tanous } 574*c6bcedc6SWilly Tu 575*c6bcedc6SWilly Tu inline boost::urls::url 576*c6bcedc6SWilly Tu urlFromPiecesDetail(const std::initializer_list<std::string_view> args) 577*c6bcedc6SWilly Tu { 578*c6bcedc6SWilly Tu boost::urls::url url("/"); 579*c6bcedc6SWilly Tu appendUrlPieces(url, args); 580*c6bcedc6SWilly Tu return url; 581*c6bcedc6SWilly Tu } 582eae855c7SEd Tanous } // namespace details 583eae855c7SEd Tanous 5847f8d8fa9SEd Tanous class OrMorePaths 5857f8d8fa9SEd Tanous {}; 5867f8d8fa9SEd Tanous 587eae855c7SEd Tanous template <typename... AV> 588eae855c7SEd Tanous inline boost::urls::url urlFromPieces(const AV... args) 589eae855c7SEd Tanous { 590eae855c7SEd Tanous return details::urlFromPiecesDetail({args...}); 591eae855c7SEd Tanous } 592eae855c7SEd Tanous 593*c6bcedc6SWilly Tu template <typename... AV> 594*c6bcedc6SWilly Tu inline void appendUrlPieces(boost::urls::url& url, const AV... args) 595*c6bcedc6SWilly Tu { 596*c6bcedc6SWilly Tu details::appendUrlPieces(url, {args...}); 597*c6bcedc6SWilly Tu } 598*c6bcedc6SWilly Tu 599ca1600c1SSzymon Dompke namespace details 600ca1600c1SSzymon Dompke { 601ca1600c1SSzymon Dompke 602ca1600c1SSzymon Dompke // std::reference_wrapper<std::string> - extracts segment to variable 603ca1600c1SSzymon Dompke // std::string_view - checks if segment is equal to variable 6047f8d8fa9SEd Tanous using UrlSegment = std::variant<std::reference_wrapper<std::string>, 6057f8d8fa9SEd Tanous std::string_view, OrMorePaths>; 6067f8d8fa9SEd Tanous 6077f8d8fa9SEd Tanous enum class UrlParseResult 6087f8d8fa9SEd Tanous { 6097f8d8fa9SEd Tanous Continue, 6107f8d8fa9SEd Tanous Fail, 6117f8d8fa9SEd Tanous Done, 6127f8d8fa9SEd Tanous }; 613ca1600c1SSzymon Dompke 614ca1600c1SSzymon Dompke class UrlSegmentMatcherVisitor 615ca1600c1SSzymon Dompke { 616ca1600c1SSzymon Dompke public: 6177f8d8fa9SEd Tanous UrlParseResult operator()(std::string& output) 618ca1600c1SSzymon Dompke { 619ca1600c1SSzymon Dompke output = std::string_view(segment.data(), segment.size()); 6207f8d8fa9SEd Tanous return UrlParseResult::Continue; 621ca1600c1SSzymon Dompke } 622ca1600c1SSzymon Dompke 6237f8d8fa9SEd Tanous UrlParseResult operator()(std::string_view expected) 624ca1600c1SSzymon Dompke { 6257f8d8fa9SEd Tanous if (std::string_view(segment.data(), segment.size()) == expected) 6267f8d8fa9SEd Tanous { 6277f8d8fa9SEd Tanous return UrlParseResult::Continue; 6287f8d8fa9SEd Tanous } 6297f8d8fa9SEd Tanous return UrlParseResult::Fail; 6307f8d8fa9SEd Tanous } 6317f8d8fa9SEd Tanous 6327f8d8fa9SEd Tanous UrlParseResult operator()(OrMorePaths /*unused*/) 6337f8d8fa9SEd Tanous { 6347f8d8fa9SEd Tanous return UrlParseResult::Done; 635ca1600c1SSzymon Dompke } 636ca1600c1SSzymon Dompke 6374e23a444SEd Tanous explicit UrlSegmentMatcherVisitor( 6384e23a444SEd Tanous const boost::urls::string_value& segmentIn) : 639ca1600c1SSzymon Dompke segment(segmentIn) 640ca1600c1SSzymon Dompke {} 641ca1600c1SSzymon Dompke 642ca1600c1SSzymon Dompke private: 643ca1600c1SSzymon Dompke const boost::urls::string_value& segment; 644ca1600c1SSzymon Dompke }; 645ca1600c1SSzymon Dompke 646ca1600c1SSzymon Dompke inline bool readUrlSegments(const boost::urls::url_view& urlView, 647ca1600c1SSzymon Dompke std::initializer_list<UrlSegment>&& segments) 648ca1600c1SSzymon Dompke { 649ca1600c1SSzymon Dompke const boost::urls::segments_view& urlSegments = urlView.segments(); 650ca1600c1SSzymon Dompke 6517f8d8fa9SEd Tanous if (!urlSegments.is_absolute()) 652ca1600c1SSzymon Dompke { 653ca1600c1SSzymon Dompke return false; 654ca1600c1SSzymon Dompke } 655ca1600c1SSzymon Dompke 656ca1600c1SSzymon Dompke boost::urls::segments_view::iterator it = urlSegments.begin(); 657ca1600c1SSzymon Dompke boost::urls::segments_view::iterator end = urlSegments.end(); 658ca1600c1SSzymon Dompke 659ca1600c1SSzymon Dompke for (const auto& segment : segments) 660ca1600c1SSzymon Dompke { 6617f8d8fa9SEd Tanous if (it == end) 6627f8d8fa9SEd Tanous { 6637f8d8fa9SEd Tanous // If the request ends with an "any" path, this was successful 6647f8d8fa9SEd Tanous return std::holds_alternative<OrMorePaths>(segment); 6657f8d8fa9SEd Tanous } 6667f8d8fa9SEd Tanous UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment); 6677f8d8fa9SEd Tanous if (res == UrlParseResult::Done) 6687f8d8fa9SEd Tanous { 6697f8d8fa9SEd Tanous return true; 6707f8d8fa9SEd Tanous } 6717f8d8fa9SEd Tanous if (res == UrlParseResult::Fail) 672ca1600c1SSzymon Dompke { 673ca1600c1SSzymon Dompke return false; 674ca1600c1SSzymon Dompke } 675ca1600c1SSzymon Dompke it++; 676ca1600c1SSzymon Dompke } 6774c30e226SCarson Labrado 6784c30e226SCarson Labrado // There will be an empty segment at the end if the URI ends with a "/" 6794c30e226SCarson Labrado // e.g. /redfish/v1/Chassis/ 6804c30e226SCarson Labrado if ((it != end) && urlSegments.back().empty()) 6814c30e226SCarson Labrado { 6824c30e226SCarson Labrado it++; 6834c30e226SCarson Labrado } 6847f8d8fa9SEd Tanous return it == end; 685ca1600c1SSzymon Dompke } 686ca1600c1SSzymon Dompke 687ca1600c1SSzymon Dompke } // namespace details 688ca1600c1SSzymon Dompke 689ca1600c1SSzymon Dompke template <typename... Args> 690ca1600c1SSzymon Dompke inline bool readUrlSegments(const boost::urls::url_view& urlView, 691ca1600c1SSzymon Dompke Args&&... args) 692ca1600c1SSzymon Dompke { 693ca1600c1SSzymon Dompke return details::readUrlSegments(urlView, {std::forward<Args>(args)...}); 694ca1600c1SSzymon Dompke } 695ca1600c1SSzymon Dompke 6961c0bb5c6SCarson Labrado inline boost::urls::url replaceUrlSegment(const boost::urls::url_view& urlView, 6971c0bb5c6SCarson Labrado const uint replaceLoc, 6981c0bb5c6SCarson Labrado const std::string_view newSegment) 6991c0bb5c6SCarson Labrado { 7001c0bb5c6SCarson Labrado const boost::urls::segments_view& urlSegments = urlView.segments(); 7011c0bb5c6SCarson Labrado boost::urls::url url("/"); 7021c0bb5c6SCarson Labrado 7031c0bb5c6SCarson Labrado if (!urlSegments.is_absolute()) 7041c0bb5c6SCarson Labrado { 7051c0bb5c6SCarson Labrado return url; 7061c0bb5c6SCarson Labrado } 7071c0bb5c6SCarson Labrado 7081c0bb5c6SCarson Labrado boost::urls::segments_view::iterator it = urlSegments.begin(); 7091c0bb5c6SCarson Labrado boost::urls::segments_view::iterator end = urlSegments.end(); 7101c0bb5c6SCarson Labrado 7111c0bb5c6SCarson Labrado for (uint idx = 0; it != end; it++, idx++) 7121c0bb5c6SCarson Labrado { 7131c0bb5c6SCarson Labrado if (idx == replaceLoc) 7141c0bb5c6SCarson Labrado { 7151c0bb5c6SCarson Labrado url.segments().push_back(newSegment); 7161c0bb5c6SCarson Labrado } 7171c0bb5c6SCarson Labrado else 7181c0bb5c6SCarson Labrado { 7191c0bb5c6SCarson Labrado url.segments().push_back(*it); 7201c0bb5c6SCarson Labrado } 7211c0bb5c6SCarson Labrado } 7221c0bb5c6SCarson Labrado 7231c0bb5c6SCarson Labrado return url; 7241c0bb5c6SCarson Labrado } 7251c0bb5c6SCarson Labrado 726eb1c47d3SEd Tanous inline std::string setProtocolDefaults(const boost::urls::url_view& url) 727eb1c47d3SEd Tanous { 728eb1c47d3SEd Tanous if (url.scheme() == "https") 729eb1c47d3SEd Tanous { 730eb1c47d3SEd Tanous return "https"; 731eb1c47d3SEd Tanous } 732eb1c47d3SEd Tanous if (url.scheme() == "http") 733eb1c47d3SEd Tanous { 734eb1c47d3SEd Tanous if (bmcwebInsecureEnableHttpPushStyleEventing) 735eb1c47d3SEd Tanous { 736eb1c47d3SEd Tanous return "http"; 737eb1c47d3SEd Tanous } 738eb1c47d3SEd Tanous return ""; 739eb1c47d3SEd Tanous } 740eb1c47d3SEd Tanous return ""; 741eb1c47d3SEd Tanous } 742eb1c47d3SEd Tanous 743eb1c47d3SEd Tanous inline uint16_t setPortDefaults(const boost::urls::url_view& url) 744eb1c47d3SEd Tanous { 745eb1c47d3SEd Tanous uint16_t port = url.port_number(); 746eb1c47d3SEd Tanous if (port != 0) 747eb1c47d3SEd Tanous { 748eb1c47d3SEd Tanous // user picked a port already. 749eb1c47d3SEd Tanous return port; 750eb1c47d3SEd Tanous } 751eb1c47d3SEd Tanous 752eb1c47d3SEd Tanous // If the user hasn't explicitly stated a port, pick one explicitly for them 753eb1c47d3SEd Tanous // based on the protocol defaults 754eb1c47d3SEd Tanous if (url.scheme() == "http") 755eb1c47d3SEd Tanous { 756eb1c47d3SEd Tanous return 80; 757eb1c47d3SEd Tanous } 758eb1c47d3SEd Tanous if (url.scheme() == "https") 759eb1c47d3SEd Tanous { 760eb1c47d3SEd Tanous return 443; 761eb1c47d3SEd Tanous } 762eb1c47d3SEd Tanous return 0; 763eb1c47d3SEd Tanous } 764eb1c47d3SEd Tanous 76511baefe4SEd Tanous inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto, 766eb1c47d3SEd Tanous std::string& host, uint16_t& port, 76711baefe4SEd Tanous std::string& path) 76811baefe4SEd Tanous { 769eb1c47d3SEd Tanous boost::string_view urlBoost(destUrl.data(), destUrl.size()); 770eb1c47d3SEd Tanous boost::urls::result<boost::urls::url_view> url = 771eb1c47d3SEd Tanous boost::urls::parse_uri(urlBoost); 772eb1c47d3SEd Tanous if (!url) 773eb1c47d3SEd Tanous { 774eb1c47d3SEd Tanous return false; 775eb1c47d3SEd Tanous } 776eb1c47d3SEd Tanous urlProto = setProtocolDefaults(url.value()); 777eb1c47d3SEd Tanous if (urlProto.empty()) 77811baefe4SEd Tanous { 77911baefe4SEd Tanous return false; 78011baefe4SEd Tanous } 78111baefe4SEd Tanous 782eb1c47d3SEd Tanous port = setPortDefaults(url.value()); 78311baefe4SEd Tanous 784eb1c47d3SEd Tanous host = std::string_view(url->encoded_host().data(), 785eb1c47d3SEd Tanous url->encoded_host().size()); 786eb1c47d3SEd Tanous 787eb1c47d3SEd Tanous path = std::string_view(url->encoded_path().data(), 788eb1c47d3SEd Tanous url->encoded_path().size()); 78911baefe4SEd Tanous if (path.empty()) 79011baefe4SEd Tanous { 79111baefe4SEd Tanous path = "/"; 79211baefe4SEd Tanous } 793eb1c47d3SEd Tanous if (url->has_fragment()) 794eb1c47d3SEd Tanous { 795eb1c47d3SEd Tanous path += '#'; 796eb1c47d3SEd Tanous path += std::string_view(url->encoded_fragment().data(), 797eb1c47d3SEd Tanous url->encoded_fragment().size()); 798eb1c47d3SEd Tanous } 799eb1c47d3SEd Tanous 800eb1c47d3SEd Tanous if (url->has_query()) 801eb1c47d3SEd Tanous { 802eb1c47d3SEd Tanous path += '?'; 803eb1c47d3SEd Tanous path += std::string_view(url->encoded_query().data(), 804eb1c47d3SEd Tanous url->encoded_query().size()); 805eb1c47d3SEd Tanous } 806eb1c47d3SEd Tanous 80711baefe4SEd Tanous return true; 80811baefe4SEd Tanous } 80911baefe4SEd Tanous 81004e438cbSEd Tanous } // namespace utility 81104e438cbSEd Tanous } // namespace crow 81271f2db75SEd Tanous 81371f2db75SEd Tanous namespace nlohmann 81471f2db75SEd Tanous { 81571f2db75SEd Tanous template <> 81671f2db75SEd Tanous struct adl_serializer<boost::urls::url> 81771f2db75SEd Tanous { 81871f2db75SEd Tanous // nlohmann requires a specific casing to look these up in adl 81971f2db75SEd Tanous // NOLINTNEXTLINE(readability-identifier-naming) 82071f2db75SEd Tanous static void to_json(json& j, const boost::urls::url& url) 82171f2db75SEd Tanous { 82271f2db75SEd Tanous j = url.string(); 82371f2db75SEd Tanous } 82471f2db75SEd Tanous }; 82571f2db75SEd Tanous 82671f2db75SEd Tanous template <> 82771f2db75SEd Tanous struct adl_serializer<boost::urls::url_view> 82871f2db75SEd Tanous { 82971f2db75SEd Tanous // NOLINTNEXTLINE(readability-identifier-naming) 83071f2db75SEd Tanous static void to_json(json& j, const boost::urls::url_view& url) 83171f2db75SEd Tanous { 83271f2db75SEd Tanous j = url.string(); 83371f2db75SEd Tanous } 83471f2db75SEd Tanous }; 83571f2db75SEd Tanous } // namespace nlohmann 836