/* * rfc4251.h -- implements types from RFC 4251, section 5 * * rfc4251::byte byte * rfc4251::boolean boolean * rfc4251::uint32 uint32 * rfc4251::uint64 uint64 * rfc4251::string string * rfc4251::mpint mpint * rfc4251::name_list name-list * * those structs contain the objects in their RFC 4251 representation, * conversions are provided via constructors and cast operators * * Copyright (C) 2013-2015,2021 Timo Weingärtner * * This file is part of ssh-agent-filter. * * ssh-agent-filter is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ssh-agent-filter is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ssh-agent-filter. If not, see . */ #include #include #include #include #include #include #include #include #include #include namespace rfc4251 { namespace internal { template constexpr inline auto host_to_network (T value) noexcept { static_assert(std::is_unsigned_v, "shift is only supported on unsigned"); std::array ret; for (auto it{rbegin(ret)}; it != rend(ret); ++it) { *it = value & 0xff; if constexpr (sizeof(T) > 1) value >>= 8; } return ret; } template constexpr inline auto network_to_host (std::array const buf) noexcept { static_assert(std::is_unsigned_v, "shift is only supported on unsigned"); T ret{0}; for (auto it{cbegin(buf)}; it != cend(buf); ++it) { if constexpr (sizeof(T) > 1) ret <<= 8; ret |= static_cast(*it); } return ret; } template struct fixed_length { static_assert(std::is_unsigned_v, "support for signed types might need more work"); std::array buf{}; constexpr fixed_length () noexcept = default; constexpr explicit fixed_length (T const v) noexcept : buf{host_to_network(v)} {} explicit fixed_length (std::istream & is) { is >> *this; } constexpr operator T () const noexcept { return network_to_host(buf); } friend std::istream & operator>> (std::istream & is, fixed_length & x) { return is.read(x.buf.data(), x.buf.size()); } friend std::ostream & operator<< (std::ostream & os, fixed_length const & x) { return os.write(x.buf.data(), x.buf.size()); } }; template struct variable_length : boost::totally_ordered> { using length_type = Length_Type; static_assert(std::is_unsigned_v, "negative lengths are not supported"); std::vector buf; variable_length () = default; explicit variable_length (std::istream & is) { is >> *this; } size_t size () const noexcept { return buf.size(); } char const * data () const noexcept { return buf.data(); } char * data () noexcept { return buf.data(); } friend std::istream & operator>> (std::istream & is, variable_length & v) { if (length_type const len{fixed_length{is}}; is) { v.buf.clear(); v.buf.resize(len); is.read(v.buf.data(), len); } return is; } friend std::ostream & operator<< (std::ostream & os, variable_length const & v) { check_length_against_limit(v.buf.size()); if (os << fixed_length{static_cast(v.buf.size())}) os.write(v.buf.data(), v.buf.size()); return os; } friend bool operator== (variable_length const & l, variable_length const & r) { return l.buf == r.buf; } friend bool operator< (variable_length const & l, variable_length const & r) { return l.buf < r.buf; } constexpr static void check_length_against_limit(size_t const length) { if (length > std::numeric_limits::max()) throw std::length_error{"numeric limit for length field in rfc4251::variable_length type exceeded"}; } }; } using byte = internal::fixed_length; using boolean = internal::fixed_length; using uint32 = internal::fixed_length; using uint64 = internal::fixed_length; struct string : internal::variable_length { using variable_length::variable_length; inline explicit string (char const * s, size_t l) { check_length_against_limit(l); buf.insert(buf.end(), s, s + l); } explicit string (std::string const & s) : string{s.data(), s.size()} {} operator std::string () const { return {buf.cbegin(), buf.cend()}; } }; struct name_list : internal::variable_length { using variable_length::variable_length; explicit name_list (std::vector const &); operator std::vector () const; }; struct mpint : internal::variable_length { using variable_length::variable_length; explicit mpint (mpz_srcptr); explicit mpint (mpz_class const & x) : mpint{x.get_mpz_t()} {} operator mpz_class () const; }; }