diff options
| author | Timo Weingärtner <timo@tiwe.de> | 2026-03-07 22:36:10 +0100 |
|---|---|---|
| committer | Timo Weingärtner <timo@tiwe.de> | 2026-03-07 22:36:10 +0100 |
| commit | 960aa108a9cd4ff31932653fdc847d492901a37b (patch) | |
| tree | 905386c7eb0a7d470ff90b8428f2a70f3327743e /rfc4251.H | |
| parent | f47546025f34ff4294b7f6b3e672c521c8d2d3f5 (diff) | |
| parent | 8bd8f61f2ab2978a0a1bf7aa6f19a6352b9769c9 (diff) | |
| download | ssh-agent-filter-960aa108a9cd4ff31932653fdc847d492901a37b.tar.gz | |
Merge tag '0.5.3' into debian
0.5.3
Diffstat (limited to 'rfc4251.H')
| -rw-r--r-- | rfc4251.H | 262 |
1 files changed, 110 insertions, 152 deletions
@@ -5,12 +5,14 @@ * rfc4251::boolean boolean * rfc4251::uint32 uint32 * rfc4251::uint64 uint64 - * rfc4251::string string, incl. mpint and name-list + * 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 Timo Weingärtner <timo@tiwe.de> + * Copyright (C) 2013-2015,2021 Timo Weingärtner <timo@tiwe.de> * * This file is part of ssh-agent-filter. * @@ -28,187 +30,143 @@ * along with ssh-agent-filter. If not, see <http://www.gnu.org/licenses/>. */ +#include <type_traits> +#include <cstdint> +#include <array> #include <vector> #include <string> #include <iostream> #include <limits> #include <stdexcept> -#include <arpa/inet.h> // ntohl() / htonl() #include <gmpxx.h> #include <boost/operators.hpp> namespace rfc4251 { -struct byte { - union { - uint8_t value; - char buf[1]; - }; - - byte () = default; - explicit byte (uint8_t v) : value(v) {} - inline explicit byte (std::istream &); - - operator uint8_t () const { return value; } -}; - -inline std::istream & operator>> (std::istream & is, byte & x) { - return is.read(x.buf, sizeof(x.buf)); -} - -inline std::ostream & operator<< (std::ostream & os, byte const & x) { - return os.write(x.buf, sizeof(x.buf)); -} +namespace internal { + template<typename T> + constexpr inline auto host_to_network (T value) noexcept { + static_assert(std::is_unsigned_v<T>, "shift is only supported on unsigned"); + + std::array<char, sizeof(T)> ret; + for (auto it{rbegin(ret)}; it != rend(ret); ++it) { + *it = value & 0xff; + if constexpr (sizeof(T) > 1) value >>= 8; + } + return ret; + } -inline byte::byte (std::istream & is) { - is >> *this; -} + template<typename T> + constexpr inline auto network_to_host (std::array<char, sizeof(T)> const buf) noexcept { + static_assert(std::is_unsigned_v<T>, "shift is only supported on unsigned"); -struct boolean { - union { - bool value; - char buf[1]; - }; + T ret{0}; + for (auto it{cbegin(buf)}; it != cend(buf); ++it) { + if constexpr (sizeof(T) > 1) ret <<= 8; + ret |= static_cast<uint8_t>(*it); + } + return ret; + } - boolean () = default; - explicit boolean (uint8_t v) : value(v) {} - inline explicit boolean (std::istream &); + template<typename T> + struct fixed_length { + static_assert(std::is_unsigned_v<T>, "support for signed types might need more work"); - operator uint8_t () const { return value; } -}; + std::array<char, sizeof(T)> buf{}; -inline std::istream & operator>> (std::istream & is, boolean & x) { - return is.read(x.buf, sizeof(x.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; } -inline std::ostream & operator<< (std::ostream & os, boolean const & x) { - return os.write(x.buf, sizeof(x.buf)); -} + constexpr operator T () const noexcept { return network_to_host<T>(buf); } -inline boolean::boolean (std::istream & is) { - is >> *this; -} - -struct uint32 { - union { - uint32_t value; - char buf[4]; + 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()); + } }; - uint32 () = default; - explicit uint32 (uint32_t v) { value = htonl(v); } - inline explicit uint32 (std::istream &); - - operator uint32_t () const { return ntohl(value); } -}; - -inline std::istream & operator>> (std::istream & is, uint32 & x) { - return is.read(x.buf, sizeof(x.buf)); -} - -inline std::ostream & operator<< (std::ostream & os, uint32 const & x) { - return os.write(x.buf, sizeof(x.buf)); -} + template<typename Length_Type> + struct variable_length : boost::totally_ordered<variable_length<Length_Type>> { + using length_type = Length_Type; + static_assert(std::is_unsigned_v<length_type>, "negative lengths are not supported"); + + std::vector<char> 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<length_type>{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<length_type>{static_cast<length_type>(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<length_type>::max()) + throw std::length_error{"numeric limit for length field in rfc4251::variable_length type exceeded"}; + } -inline uint32::uint32 (std::istream & is) { - is >> *this; + }; } -struct uint64 { - union { - uint64_t value; - char buf[8]; - }; +using byte = internal::fixed_length<uint8_t>; +using boolean = internal::fixed_length<bool>; +using uint32 = internal::fixed_length<uint32_t>; +using uint64 = internal::fixed_length<uint64_t>; - uint64 () = default; - inline explicit uint64 (uint64_t v); - inline explicit uint64 (std::istream &); +struct string : internal::variable_length<uint32_t> { + using variable_length::variable_length; - inline explicit operator uint64_t () const; -}; - -inline uint64::uint64 (uint64_t v) { - for (int_fast8_t i{7}; i >= 0; --i) { - buf[i] = v & 0xff; - v >>= 8; + inline explicit string (char const * s, size_t l) { + check_length_against_limit(l); + buf.insert(buf.end(), s, s + l); } -} - -inline uint64::operator uint64_t () const { - uint64_t ret{0}; - for (uint_fast8_t i{0}; i < 8; ++i) { - ret <<= 8; - ret |= static_cast<uint8_t>(buf[i]); + explicit string (std::string const & s) : string{s.data(), s.size()} {} + string (std::initializer_list<char> init) { + check_length_against_limit(init.size()); + buf = init; } - return ret; -} - -inline std::istream & operator>> (std::istream & is, uint64 & x) { - return is.read(x.buf, sizeof(x.buf)); -} -inline std::ostream & operator<< (std::ostream & os, uint64 const & x) { - return os.write(x.buf, sizeof(x.buf)); -} - -inline uint64::uint64 (std::istream & is) { - is >> *this; -} + operator std::string () const { return {buf.cbegin(), buf.cend()}; } +}; -struct string : boost::totally_ordered<string> { - std::vector<char> value; +struct name_list : internal::variable_length<uint32_t> { + using variable_length::variable_length; + + explicit name_list (std::vector<std::string> const &); - string () = default; - inline explicit string (char const *, size_t); - explicit string (std::string const & s) : string{s.data(), s.size()} {} - explicit string (std::vector<std::string> const &); - explicit string (mpz_srcptr); - explicit string (mpz_class const & x) : string{x.get_mpz_t()} {} - inline explicit string (std::istream &); - - size_t size () const { return value.size(); } - char const * data () const { return value.data(); } - char * data () { return value.data(); } - - operator std::string () const { return {value.begin(), value.end()}; } operator std::vector<std::string> () const; - operator mpz_class () const; }; -inline string::string (char const * s, size_t l) { - if (l > std::numeric_limits<uint32_t>::max()) - throw std::length_error{"32-bit limit for rfc4251::string exceeded"}; - value.insert(value.end(), s, s + l); -} - -inline std::istream & operator>> (std::istream & is, string & s) { - s.value.clear(); - uint32 len; - if (is >> len) { - s.value.resize(len); - is.read(s.value.data(), len); - } - return is; -} - -inline std::ostream & operator<< (std::ostream & os, string const & s) { - if (s.value.size() > std::numeric_limits<uint32_t>::max()) - throw std::length_error{"32-bit limit for rfc4251::string exceeded"}; - if (os << uint32{static_cast<uint32_t>(s.value.size())}) - os.write(s.value.data(), s.value.size()); - return os; -} - -inline string::string (std::istream & is) { - is >> *this; -} - -inline bool operator== (string const & l, string const & r) { - return l.value == r.value; -} - -inline bool operator< (string const & l, string const & r) { - return l.value < r.value; -} +struct mpint : internal::variable_length<uint32_t> { + using variable_length::variable_length; + + explicit mpint (mpz_srcptr); + explicit mpint (mpz_class const & x) : mpint{x.get_mpz_t()} {} + + operator mpz_class () const; +}; } |
