1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/*
* 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 <timo@tiwe.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <type_traits>
#include <cstdint>
#include <array>
#include <vector>
#include <string>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <gmpxx.h>
#include <boost/operators.hpp>
namespace rfc4251 {
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;
}
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");
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;
}
template<typename T>
struct fixed_length {
static_assert(std::is_unsigned_v<T>, "support for signed types might need more work");
std::array<char, sizeof(T)> 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<T>(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<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"};
}
};
}
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>;
struct string : internal::variable_length<uint32_t> {
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()} {}
string (std::initializer_list<char> init) {
check_length_against_limit(init.size());
buf = init;
}
operator std::string () const { return {buf.cbegin(), buf.cend()}; }
};
struct name_list : internal::variable_length<uint32_t> {
using variable_length::variable_length;
explicit name_list (std::vector<std::string> const &);
operator std::vector<std::string> () const;
};
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;
};
}
|