xdrpp
RFC4506 XDR compiler and message library
marshal.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 /** \file marshal.h Support for marshaling XDR types in the format
4  * specified by [RFC4506](http://tools.ietf.org/html/rfc4506).
5  */
6 
7 #ifndef _XDRPP_MARSHAL_H_HEADER_INCLUDED_
8 #define _XDRPP_MARSHAL_H_HEADER_INCLUDED_ 1
9 
10 #include <cstring>
11 #include <xdrpp/endian.h>
12 #include <xdrpp/message.h>
13 #include <xdrpp/types.h>
14 #include <iostream>
15 
16 namespace xdr {
17 
18 //! Common utility types and functions for all the marshaling classes.
19 struct marshal_base {
20  struct u64conv {
21  union {
22  std::uint64_t u64;
23  std::uint32_t u32[2];
24  };
25  u64conv() = default;
26  u64conv(std::uint64_t u) : u64(u) {}
27  };
28 
29  //! Copy \c len bytes to buf, then consume 0-3 bytes of padding to
30  //! make the total number of bytes consumed divisible by 4. \throws
31  //! xdr_should_be_zero if the padding bytes are not zero.
32  static void get_bytes(const std::uint32_t *&pr, void *buf, std::size_t len);
33  //! Copy \c len bytes from buf, then add 0-3 zero-valued padding
34  //! bytes to make the overall marshaled length a multiple of 4.
35  static void put_bytes(std::uint32_t *&pr, const void *buf, std::size_t len);
36 };
37 
38 //! Numeric marshaling mixin that does not byteswap any numeric values
39 //! (which will produce RFC4506 output on a big-endian machine).
41  static void put32(std::uint32_t *&p, std::uint32_t v) { *p++ = v; }
42  static void put64(std::uint32_t *&p, u64conv u) {
43  *p++ = u.u32[0];
44  *p++ = u.u32[1];
45  }
46 
47  static std::uint32_t get32(const std::uint32_t *&p) { return *p++; }
48  static std::uint64_t get64(const std::uint32_t *&p) {
49  u64conv u;
50  u.u32[0] = *p++;
51  u.u32[1] = *p++;
52  return u.u64;
53  }
54 };
55 
56 //! Numeric marshaling mixin that byteswaps all numeric values (thus
57 //! producing RFC4506 output on a little-endian machine).
59  static void put32(std::uint32_t *&p, std::uint32_t v) {
60  *p++ = swap32(v);
61  }
62  static void put64(std::uint32_t *&p, u64conv u) {
63  *p++ = swap32(u.u32[1]);
64  *p++ = swap32(u.u32[0]);
65  }
66  static std::uint32_t get32(const std::uint32_t *&p) { return swap32(*p++); }
67  static std::uint64_t get64(const std::uint32_t *&p) {
68  u64conv u;
69  u.u32[1] = swap32(*p++);
70  u.u32[0] = swap32(*p++);
71  return u.u64;
72  }
73 };
74 
75 //! Archive type for marshaling to a buffer. Depending on the `Base`
76 //! type, will marshal in either big- or little-endian order.
77 template<typename Base> struct xdr_generic_put : Base {
78  using Base::put32;
79  using Base::put64;
80  using Base::put_bytes;
81 
82  std::uint32_t *p_;
83  std::uint32_t *const e_;
84 
85  // Set the buffer to marshal to. Both \c start and \c end must be
86  // 4-byte aligned.
87  xdr_generic_put(void *start, void *end)
88  : p_(reinterpret_cast<std::uint32_t *>(start)),
89  e_(reinterpret_cast<std::uint32_t *>(end)) {
90  assert(!(reinterpret_cast<intptr_t>(start) & 3));
91  assert(!(reinterpret_cast<intptr_t>(end) & 3));
92  assert(p_ <= e_);
93  }
94  xdr_generic_put(msg_ptr &m)
95  : xdr_generic_put(m->data(), m->end()) {}
96 
97  void check(std::size_t n) const {
98  if (n > std::size_t(reinterpret_cast<char *>(e_)
99  - reinterpret_cast<char *>(p_)))
100  throw xdr_overflow("insufficient buffer space in xdr_generic_put");
101  }
102 
103  template<typename T> typename std::enable_if<
104  std::is_same<std::uint32_t, typename xdr_traits<T>::uint_type>::value>::type
105  operator()(T t) { check(4); put32(p_, xdr_traits<T>::to_uint(t)); }
106 
107  template<typename T> typename std::enable_if<
108  std::is_same<std::uint64_t, typename xdr_traits<T>::uint_type>::value>::type
109  operator()(T t) { check(8); put64(p_, xdr_traits<T>::to_uint(t)); }
110 
111  template<typename T> typename std::enable_if<xdr_traits<T>::is_bytes>::type
112  operator()(const T &t) {
114  check(4 + t.size());
115  put32(p_, size32(t.size()));
116  }
117  else
118  check(t.size());
119  put_bytes(p_, t.data(), t.size());
120  }
121 
122  template<typename T> typename std::enable_if<
124  operator()(const T &t) { xdr_traits<T>::save(*this, t); }
125 };
126 
127 //! Archive type for unmarshaling from a buffer. Depending on the
128 //! `Base` type, will expect input in either big- or little-endian
129 //! order.
130 template<typename Base> struct xdr_generic_get : Base {
131  using Base::get32;
132  using Base::get64;
133  using Base::get_bytes;
134 
135  const std::uint32_t *p_;
136  const std::uint32_t *const e_;
137 
138  // Set the buffer to marshal from. Both \c start and \c end must be
139  // 4-byte aligned.
140  xdr_generic_get(const void *start, const void *end)
141  : p_(reinterpret_cast<const std::uint32_t *>(start)),
142  e_(reinterpret_cast<const std::uint32_t *>(end)) {
143  assert(!(reinterpret_cast<intptr_t>(start) & 3));
144  // Message could be coming from untrusted source, so bad length is
145  // not an assertion failure.
146  if (reinterpret_cast<intptr_t>(end) & 3)
147  throw xdr_bad_message_size("xdr_generic_get: message size not"
148  " multiple of 4");
149  assert(p_ <= e_);
150  }
151  xdr_generic_get(const msg_ptr &m)
152  : xdr_generic_get(m->data(), m->end()) {}
153 
154  void check(std::size_t n) const {
155  if (n > std::size_t(reinterpret_cast<const char *>(e_)
156  - reinterpret_cast<const char *>(p_)))
157  throw xdr_overflow("insufficient buffer space in xdr_generic_get");
158  }
159 
160  template<typename T> typename std::enable_if<
161  std::is_same<std::uint32_t, typename xdr_traits<T>::uint_type>::value>::type
162  operator()(T &t) { check(4); t = xdr_traits<T>::from_uint(get32(p_)); }
163 
164  template<typename T> typename std::enable_if<
165  std::is_same<std::uint64_t, typename xdr_traits<T>::uint_type>::value>::type
166  operator()(T &t) { check(8); t = xdr_traits<T>::from_uint(get64(p_)); }
167 
168  template<typename T> typename std::enable_if<xdr_traits<T>::is_bytes>::type
169  operator()(T &t) {
171  check(4);
172  std::uint32_t size = get32(p_);
173  check(size);
174  t.resize(size);
175  }
176  else
177  check(t.size());
178  get_bytes(p_, t.data(), t.size());
179  }
180 
181  template<typename T> typename std::enable_if<
183  operator()(T &t) { xdr_traits<T>::load(*this, t); }
184 
185  void done() {
186  if (p_ != e_)
187  throw xdr_bad_message_size("unmarshaling did not consume whole message");
188  }
189 };
190 
191 #if XDRPP_WORDS_BIGENDIAN
194 #else // !XDRPP_WORDS_BIGENDIAN
195 //! Archive for marshaling in RFC4506 big-endian order.
197 //! Archive for unmarshaling in RFC4506 big-endian order.
199 #endif // !XDRPP_WORDS_BIGENDIAN
200 
201 inline std::size_t
202 xdr_argpack_size()
203 {
204  return 0;
205 }
206 //! Returns the sum of bytes required to marshal all of the argument
207 //! values.
208 template<typename T, typename...Args> inline std::size_t
209 xdr_argpack_size(const T &t, const Args &...a)
210 {
211  return xdr_size(t) + xdr_argpack_size(a...);
212 }
213 
214 template<typename Archive> inline void
215 xdr_argpack_archive(Archive &)
216 {
217 }
218 //! Applies an archive to each argument.
219 template<typename Archive, typename T, typename...Args> inline void
220 xdr_argpack_archive(Archive &ar, T &&t, Args &&...args)
221 {
222  archive(ar, std::forward<T>(t));
223  xdr_argpack_archive(ar, std::forward<Args>(args)...);
224 }
225 
226 //! Marshal one or a series of XDR types into a newly allocated buffer
227 //! referenced xdr::msg_ptr. If more than one argument is given, each
228 //! XDR value is marshaled in turn. (In particular, this allows one
229 //! to marshal a header followed by a message body.)
230 template<typename...Args> msg_ptr
231 xdr_to_msg(const Args &...args)
232 {
233  msg_ptr m (message_t::alloc(xdr_argpack_size(args...)));
234  xdr_put p (m);
235  xdr_argpack_archive(p, args...);
236  assert(p.p_ == p.e_);
237  return m;
238 }
239 
240 //! Marshal one or a series of XDR types into a newly allocated opaque
241 //! structure for embedding in other XDR types.
242 template<typename...Args> opaque_vec<>
243 xdr_to_opaque(const Args &...args)
244 {
245  opaque_vec<> m (opaque_vec<>::size_type {xdr_argpack_size(args...)});
246  xdr_put p (m.data(), m.data()+m.size());
247  xdr_argpack_archive(p, args...);
248  assert(p.p_ == p.e_);
249  return m;
250 }
251 
252 
253 //! This does the reverse of xdr::xdr_to_msg, unmarshalling one or
254 //! more types from a message. Note that it throws an exception if
255 //! the entire buffer is not consumed.
256 template<typename...Args> void
257 xdr_from_msg(const msg_ptr &m, Args &...args)
258 {
259  xdr_get g(m);
260  xdr_argpack_archive(g, args...);
261  g.done();
262 }
263 
264 namespace detail {
265 // Fake function accepting types we are willing to unmarshal from, so
266 // that xdr_from_opaque works with types such as str::string and
267 // std::vector<std::uint8_t> but not std::vector<bool>.
268 void bytes_to_void(const std::uint8_t *);
269 void bytes_to_void(const char *);
270 }
271 
272 //! The reverse of xdr::xdr_to_opaque. For convenience, accepts the
273 //! data in std::string and std::vector (of byte types) in addition to
274 //! xdr::xdr_opaque<>.
275 template<typename Bytes, typename...Args> auto
276 xdr_from_opaque(const Bytes &m, Args &...args)
277  -> decltype(detail::bytes_to_void(m.data()))
278 {
279  xdr_get g(m.data(), m.data()+m.size());
280  xdr_argpack_archive(g, args...);
281  g.done();
282 }
283 
284 }
285 
286 #endif // !_XDRPP_MARSHAL_H_HEADER_INCLUDED_
287 
static void get_bytes(const std::uint32_t *&pr, void *buf, std::size_t len)
Copy len bytes to buf, then consume 0-3 bytes of padding to make the total number of bytes consumed d...
Definition: marshal.cc:25
Constexpr std::uint32_t swap32(std::uint32_t v)
Byteswap 32-bit number.
Definition: endian.h:62
std::size_t xdr_size(const T &t)
Return the marshaled size of an XDR data type.
Definition: types.h:184
Type definitions for xdrc compiler output.
Attempt to exceed the bounds of a variable-length array or string.
Definition: types.h:46
Archive type for marshaling to a buffer.
Definition: marshal.h:77
static msg_ptr alloc(std::size_t size)
Allocate a new buffer.
Definition: marshal.cc:7
auto xdr_from_opaque(const Bytes &m, Args &...args) -> decltype(detail::bytes_to_void(m.data()))
The reverse of xdr::xdr_to_opaque.
Definition: marshal.h:276
Most of the xdrpp library is encapsulated in the xdr namespace.
Definition: arpc.cc:4
Archive type for unmarshaling from a buffer.
Definition: marshal.h:130
msg_ptr xdr_to_msg(const Args &...args)
Marshal one or a series of XDR types into a newly allocated buffer referenced xdr::msg_ptr.
Definition: marshal.h:231
static void put_bytes(std::uint32_t *&pr, const void *buf, std::size_t len)
Copy len bytes from buf, then add 0-3 zero-valued padding bytes to make the overall marshaled length ...
Definition: marshal.cc:39
Message buffer with space for marshaled length.
Numeric marshaling mixin that byteswaps all numeric values (thus producing RFC4506 output on a little...
Definition: marshal.h:58
Low-level byteswap and miscellaneous OS compatibility routines.
Metadata for all marshalable XDR types.
Definition: types.h:146
Message not multiple of 4 bytes, or cannot fully be parsed.
Definition: types.h:51
Numeric marshaling mixin that does not byteswap any numeric values (which will produce RFC4506 output...
Definition: marshal.h:40
opaque_vec xdr_to_opaque(const Args &...args)
Marshal one or a series of XDR types into a newly allocated opaque structure for embedding in other X...
Definition: marshal.h:243
Common utility types and functions for all the marshaling classes.
Definition: marshal.h:19
void xdr_from_msg(const msg_ptr &m, Args &...args)
This does the reverse of xdr::xdr_to_msg, unmarshalling one or more types from a message.
Definition: marshal.h:257