OpenVPN 3 Core Library
Loading...
Searching...
No Matches
continuation_fragment.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// Server-side code to fragment an oversized options buffer
13// into multiple buffers using the push-continuation option.
14
15#pragma once
16
17#include <string>
18#include <vector>
19
24
25namespace openvpn {
26
27// Fragment a long PUSH_REPLY/PUSH_UPDATE buffer into
28// multiple buffers using the push-continuation option.
29class PushContinuationFragment : public std::vector<BufferPtr>
30{
31 public:
32 // maximum allowable fragment size (excluding null termination
33 // that will be appended by push_reply() in servproto.hpp).
34 static constexpr size_t FRAGMENT_SIZE = 1023;
35
36 OPENVPN_EXCEPTION(push_continuation_fragment_error);
37
38 static bool should_fragment(const ConstBuffer &buf)
39 {
40 return buf.size() > FRAGMENT_SIZE;
41 }
42
43 // prefix should be PUSH_REPLY or PUSH_UPDATE
44 PushContinuationFragment(const ConstBuffer &buf, const std::string &prefix)
45 {
46 // size of ",push-continuation n"
47 const size_t push_continuation_len = 20;
48
49 // loop over options
50 bool did_continuation = false;
51 PushLex lex(buf, true);
52 while (lex.defined())
53 {
54 // get escaped opt
55 const std::string escaped_opt = lex.next();
56
57 // create first buffer on loop startup
58 if (empty())
59 append_new_buffer(prefix);
60
61 // ready to finalize this outbut buffer and move on to next?
62 // (the +1 is for escaped_opt comma)
63 if (back()->size() + escaped_opt.size() + push_continuation_len + 1 > FRAGMENT_SIZE)
64 {
65 did_continuation = true;
66 append_push_continuation(*back(), false);
67 append_new_buffer(prefix);
68 }
69
70 back()->push_back(',');
71 buf_append_string(*back(), escaped_opt);
72 }
73
74 // push final push-continuation
75 if (!empty() && did_continuation)
76 append_push_continuation(*back(), true);
77 }
78
79 // prefix should be PUSH_REPLY or PUSH_UPDATE
80 static BufferPtr defragment(const std::vector<BufferPtr> &bv,
81 const std::string &prefix)
82 {
83 // exit cases where no need to defrag
84 if (bv.empty())
85 return BufferPtr();
86 if (bv.size() == 1)
87 return bv[0];
88
89 // compute length
90 size_t total_size = 0;
91 for (const auto &e : bv)
92 total_size += e->size();
93
94 // allocate return buffer
95 auto ret = BufferAllocatedRc::Create(total_size);
96 buf_append_string(*ret, prefix);
97
98 // terminators
99 static const char pc1[] = ",push-continuation 1";
100 static const char pc2[] = ",push-continuation 2";
101
102 // build return buffer
103 const std::string prefix_comma = prefix + ',';
104 const size_t size = bv.size();
105 for (size_t i = 0; i < size; ++i)
106 {
107 const Buffer &buf = *bv[i];
108 const char *pc = (i == size - 1) ? pc1 : pc2;
109 if (string::starts_with(buf, prefix_comma) && string::ends_with(buf, pc))
110 {
111 Buffer b = buf;
112 b.advance(prefix.size()); // advance past prefix
113 b.set_size(b.size() - 20); // truncate ",push-continuation n"
114 ret->append(b);
115 }
116 else
117 throw push_continuation_fragment_error("badly formatted fragments");
118 }
119 return ret;
120 }
121
122 private:
123 // create a new PUSH_REPLY/PUSH_UPDATE buffer
124 void append_new_buffer(const std::string &prefix)
125 {
126 // include extra byte for null termination
128 buf_append_string(*bp, prefix);
129 push_back(std::move(bp));
130 }
131
132 // append a push-continuation directive to buffer
133 static void append_push_continuation(Buffer &buf, bool end)
134 {
135 buf_append_string(buf, ",push-continuation ");
136 buf.push_back(end ? '1' : '2');
137 }
138};
139} // namespace openvpn
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
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1242
void advance(const size_t delta)
Advances the buffer by the specified delta.
Definition buffer.hpp:1277
void set_size(const size_t size)
After an external method, operating on the array as a mutable unsigned char buffer,...
Definition buffer.hpp:1384
static void append_push_continuation(Buffer &buf, bool end)
OPENVPN_EXCEPTION(push_continuation_fragment_error)
PushContinuationFragment(const ConstBuffer &buf, const std::string &prefix)
void append_new_buffer(const std::string &prefix)
static BufferPtr defragment(const std::vector< BufferPtr > &bv, const std::string &prefix)
static bool should_fragment(const ConstBuffer &buf)
bool defined() const
Definition pushlex.hpp:61
std::string next()
Definition pushlex.hpp:71
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
bool starts_with(const STRING &str, const std::string &prefix)
Definition string.hpp:79
bool ends_with(const STRING &str, const std::string &suffix)
Definition string.hpp:111
void buf_append_string(Buffer &buf, const std::string &str)
Definition bufstr.hpp:82
RCPtr< BufferAllocatedRc > BufferPtr
Definition buffer.hpp:1899
server addresses push_back({address, port})
std::string ret