OpenVPN 3 Core Library
Loading...
Searching...
No Matches
pktstream.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#pragma once
13
14#include <algorithm> // for std::min
15#include <cstdint> // for std::uint16_t, etc.
16#include <limits>
17
24
25namespace openvpn {
26
27// Used to encapsulate OpenVPN, DNS, or other protocols onto a
28// stream transport such as TCP, or extract them from the stream.
29// SIZE_TYPE indicates the size of the length word, and should be
30// a uint16_t for OpenVPN and DNS protocols, but may be uint32_t
31// for other procotols. In all cases, the length word is represented
32// by network-endian ordering.
33template <typename SIZE_TYPE>
35{
36 private:
37 static constexpr size_t SIZE_UNDEF = std::numeric_limits<size_t>::max();
38
39 public:
40 OPENVPN_SIMPLE_EXCEPTION(embedded_packet_size_error);
41 OPENVPN_SIMPLE_EXCEPTION(packet_not_fully_formed);
42
43 // Add stream fragment to packet that we are building up.
44 // Data will be read from buf. On return buf may still contain
45 // residual data. If function is able to use all of buf, it may
46 // grab ownership of it, replacing buf as returned to caller with
47 // an empty (but possibly pre-allocated) BufferAllocated object.
48 void put(BufferAllocated &buf, const Frame::Context &frame_context)
49 {
50 if (buf.defined())
51 {
53 {
54 if (size_defined(buf))
55 {
56 extract_size(buf, frame_context);
57 if (buf.size() == declared_size) // packet is correctly sized
58 buffer.swap(buf);
59 else if (buf.size() < declared_size) // packet is undersized
60 {
61 if (buf.offset() + declared_size + frame_context.tailroom() <= buf.capacity())
62 buffer.swap(buf);
63 else
64 {
65 buffer.swap(buf);
66 frame_context.realign(buffer);
67 }
68 }
69 else // packet is oversized
70 {
71 frame_context.prepare(buffer);
72 const unsigned char *data = buf.read_alloc(declared_size);
74 }
75 }
76 else // rare case where packet fragment is too small to contain embedded size
77 {
78 buffer.swap(buf);
79 frame_context.realign(buffer);
80 }
81 }
82 else
83 {
84 while (!declared_size_defined())
85 {
86 if (buf.empty())
87 return;
90 extract_size(buffer, frame_context);
91 }
93 {
94 const size_t needed = std::min(declared_size - buffer.size(), buf.size());
95 const unsigned char *data = buf.read_alloc(needed);
96 buffer.write(data, needed);
97 }
98 }
99 }
100 }
101
102 // returns true if get() may be called to return fully formed packet
103 bool ready() const
104 {
106 }
107
108 // return fully formed packet as ret. ret, as passed to method, will
109 // be grabbed, reset, and subsequently used internally.
111 {
113 {
114 ret.swap(buffer);
117 }
118 else
119 throw packet_not_fully_formed();
120 }
121
122 // this method is provided for prototype compatibility
123 // with PacketStreamResidual
124 void get(BufferAllocated &ret, const Frame::Context &frame_context)
125 {
126 get(ret);
127 }
128
129 // prepend SIZE_TYPE size to buffer
130 static void prepend_size(Buffer &buf)
131 {
132 SIZE_TYPE net_len;
133 host_to_network(net_len, buf.size());
134 buf.prepend((const unsigned char *)&net_len, sizeof(net_len));
135 }
136
137 // reset the object to default-initialized state
138 void reset()
139 {
141 buffer.clear();
142 }
143
144#ifndef UNIT_TEST
145 private:
146#endif
147
148 // specialized methods for ntohl, ntohs, htonl, htons
149
150 static size_t network_to_host(const std::uint16_t value)
151 {
152 return ntohs(value);
153 }
154
155 static size_t network_to_host(const std::uint32_t value)
156 {
157 return ntohl(value);
158 }
159
160 static void host_to_network(std::uint16_t &result, const size_t value)
161 {
162 result = htons(numeric_cast<std::uint16_t>(value));
163 }
164
165 static void host_to_network(std::uint32_t &result, const size_t value)
166 {
167 result = htonl(numeric_cast<std::uint32_t>(value));
168 }
169
171 {
172 return declared_size != SIZE_UNDEF;
173 }
174
175 void extract_size(Buffer &buf, const Frame::Context &frame_context)
176 {
177 const size_t size = read_size(buf);
178 validate_size(size, frame_context);
179 declared_size = size;
180 }
181
182 static bool size_defined(const Buffer &buf)
183 {
184 return buf.size() >= sizeof(SIZE_TYPE);
185 }
186
187 static size_t read_size(Buffer &buf)
188 {
189 SIZE_TYPE net_len;
190 buf.read((unsigned char *)&net_len, sizeof(net_len));
191 return network_to_host(net_len);
192 }
193
194 static void validate_size(const size_t size, const Frame::Context &frame_context)
195 {
196 // Don't validate upper bound on size if BufAllocFlags::GROW is set,
197 // allowing it to range up to larger sizes.
198 if (!size || (!(frame_context.buffer_flags() & BufAllocFlags::GROW) && size > frame_context.payload()))
199 throw embedded_packet_size_error();
200 }
201
202 size_t declared_size = SIZE_UNDEF; // declared size of packet in leading SIZE_TYPE prefix
203 BufferAllocated buffer; // accumulated packet data
204};
205
206// In this variant of PacketStreamResidual, put()
207// will absorb all residual data in buf, so that
208// buf is always returned empty.
209template <typename SIZE_TYPE>
211{
212 public:
213 void put(BufferAllocated &buf, const Frame::Context &frame_context)
214 {
215 if (residual.empty())
216 {
217 pktstream.put(buf, frame_context);
218 residual = std::move(buf);
219 }
220 else
221 {
222 residual.append(buf);
223 pktstream.put(residual, frame_context);
224 }
225 buf.reset_content();
226 }
227
228 void get(BufferAllocated &ret, const Frame::Context &frame_context)
229 {
230 pktstream.get(ret);
231 if (!residual.empty())
232 pktstream.put(residual, frame_context);
233 }
234
235 bool ready() const
236 {
237 return pktstream.ready();
238 }
239
240 static void prepend_size(Buffer &buf)
241 {
243 }
244
245 // reset the object to default-initialized state
246 void reset()
247 {
248 pktstream.reset();
249 residual.clear();
250 }
251
252 private:
255};
256
257} // namespace openvpn
void clear()
Clears the contents of the buffer.
Definition buffer.hpp:1824
void swap(BufferAllocatedType< T_ > &other)
Swaps the contents of this BufferAllocatedType object with another BufferAllocatedType object.
Definition buffer.hpp:1799
bool defined() const
Returns true if the buffer is not empty.
Definition buffer.hpp:1224
void append(const B &other)
Append data from another buffer to this buffer.
Definition buffer.hpp:1626
void push_back(const T &value)
Append a T object to the end of the array, resizing the array if necessary.
Definition buffer.hpp:1482
void prepend(const T *data, const size_t size)
Prepend data to the buffer.
Definition buffer.hpp:1575
size_t capacity() const
Returns the capacity (raw size) of the allocated buffer in T objects.
Definition buffer.hpp:1212
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1242
bool empty() const
Returns true if the buffer is empty.
Definition buffer.hpp:1236
void write(const T *data, const size_t size)
Write data to the buffer.
Definition buffer.hpp:1563
auto * read_alloc(const size_t size)
Allocate memory and read data from the buffer into the allocated memory.
Definition buffer.hpp:1343
T pop_front()
Removes and returns the first element from the buffer.
Definition buffer.hpp:1256
size_t offset() const
Returns the current offset (headroom) into the buffer.
Definition buffer.hpp:1218
void reset_content()
Resets the content of the buffer.
Definition buffer.hpp:1176
void read(NCT *data, const size_t size)
Read data from the buffer into the specified memory location.
Definition buffer.hpp:1331
size_t payload() const
Definition frame.hpp:97
size_t prepare(Buffer &buf) const
Definition frame.hpp:116
size_t tailroom() const
Definition frame.hpp:101
BufferFlags buffer_flags() const
Definition frame.hpp:109
void realign(Buffer &buf) const
Definition frame.hpp:132
static void prepend_size(Buffer &buf)
PacketStream< SIZE_TYPE > pktstream
void put(BufferAllocated &buf, const Frame::Context &frame_context)
void get(BufferAllocated &ret, const Frame::Context &frame_context)
static void prepend_size(Buffer &buf)
static size_t read_size(Buffer &buf)
static constexpr size_t SIZE_UNDEF
Definition pktstream.hpp:37
OPENVPN_SIMPLE_EXCEPTION(packet_not_fully_formed)
void get(BufferAllocated &ret)
static void validate_size(const size_t size, const Frame::Context &frame_context)
static void host_to_network(std::uint16_t &result, const size_t value)
void extract_size(Buffer &buf, const Frame::Context &frame_context)
static size_t network_to_host(const std::uint16_t value)
BufferAllocated buffer
OPENVPN_SIMPLE_EXCEPTION(embedded_packet_size_error)
bool declared_size_defined() const
void get(BufferAllocated &ret, const Frame::Context &frame_context)
static bool size_defined(const Buffer &buf)
static size_t network_to_host(const std::uint32_t value)
void put(BufferAllocated &buf, const Frame::Context &frame_context)
Definition pktstream.hpp:48
static void host_to_network(std::uint32_t &result, const size_t value)
constexpr BufferFlags GROW(1u<< 2)
if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
std::string ret