OpenVPN 3 Core Library
Loading...
Searching...
No Matches
customcontrolchannel.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
18#pragma once
19
20#include <vector>
21#include <string>
22#include <utility>
28
29namespace openvpn {
30
32{
34 bool encoding_base64 = false;
35
38 bool encoding_text = false;
39
41 bool encoding_binary = false;
42
44 std::vector<std::string> supported_protocols;
45
47 int max_msg_size = 0;
48
49 bool operator==(const AppControlMessageConfig &other) const
50 {
52 }
53
54 void parse_flags(const std::string &flags)
55 {
56 for (const auto &flag : string::split(flags, ':'))
57 {
58 if (flag == "A")
59 encoding_text = true;
60 else if (flag == "B")
61 encoding_binary = true;
62 else if (flag == "6")
63 encoding_base64 = true;
64 }
65 }
66
67 bool supports_protocol(const std::string &protocol)
68 {
69 return (std::find(supported_protocols.begin(), supported_protocols.end(), protocol) != std::end(supported_protocols));
70 }
83 std::vector<std::string> format_message(const std::string &protocol, const std::string &message)
84 {
85 if (!supports_protocol(protocol))
86 throw std::invalid_argument("protocol [" + protocol + "] is not supported by peer");
87
88 std::string format;
89
90 /* 2 for the encoding and potential 'F', and ','; 4 for the commas; 5 for the message size itself */
91 /* Example: ACC,muppets,41,A,{ "me": "pig", "msg": "I am Miss Piggy" }) */
92 const std::size_t header_size = std::strlen("ACC,") + 2 + protocol.size() + 4 + 5;
93 auto max_fragment_size = max_msg_size - header_size;
94
95
96 /* check if the message would be able to pass through the message
97 * sanitisation of normal control channel receive logic */
98 const std::string sanitised_msg = Unicode::utf8_printable(message, Unicode::UTF8_FILTER);
99 if (sanitised_msg == message && encoding_text)
100 format = "A";
101 else if (encoding_base64)
102 {
103 format = "6";
104 max_fragment_size = max_fragment_size * 6 / 8 - 1;
105 }
106 else
107 {
108 throw std::invalid_argument("no encoding available to encode app custom control message");
109 }
110
111
112 std::vector<std::string> control_messages;
113
114 for (std::size_t i = 0; i < message.size(); i += max_fragment_size)
115 {
116 std::string fragment = message.substr(i, max_fragment_size);
117 const bool lastfragment = (i + max_fragment_size) >= message.size();
118
119 if (format == "6")
120 {
121 fragment = base64->encode(fragment);
122 }
123
124 std::stringstream control_msg{};
125 control_msg << "ACC," << protocol << ",";
126 control_msg << std::to_string(fragment.size()) << ",";
127 control_msg << format;
128 if (!lastfragment)
129 control_msg << "F";
130 control_msg << "," << fragment;
131
132 control_messages.emplace_back(control_msg.str());
133 }
134 return control_messages;
135 }
136
137 std::string str() const
138 {
139 if (supported_protocols.empty())
140 {
141 return {"no supported protocols"};
142 }
143
144 std::stringstream out;
145 out << "protocols " << string::join(supported_protocols, " ") << ", ";
146 out << "msg_size " << max_msg_size << ", ";
147 out << "encoding";
148 if (encoding_binary)
149 out << " binary";
150 if (encoding_text)
151 out << " ascii";
152 if (encoding_base64)
153 out << " base64";
154
155 return out.str();
156 }
157};
158
159OPENVPN_EXCEPTION(parse_acc_message);
160
162{
163 private:
165 std::string recvprotocol;
166
167 public:
173 bool receive_message(const std::string &msg)
174 {
175 if (!recvbuf.defined())
177 // msg includes ACC, prefix
178 auto parts = string::split(msg, ',', 4);
179 if (parts.size() != 5 || parts[0] != "ACC")
180 {
181 throw parse_acc_message{"Discarding malformed custom app control message"};
182 }
183
184
185 auto protocol = std::move(parts[1]);
186 auto length_str = std::move(parts[2]);
187 auto flags = std::move(parts[3]);
188 auto message = std::move(parts[4]);
189
190 bool base64Encoding = false;
191 bool textEncoding = false;
192 bool fragment = false;
193
194 size_t length = 0;
195 if (!parse_number(length_str, length) || length != message.length())
196 {
197 throw parse_acc_message{"Discarding malformed custom app control message"};
198 }
199
200 for (char const &c : flags)
201 {
202 switch (c)
203 {
204 case '6':
205 base64Encoding = true;
206 break;
207 case 'A':
208 textEncoding = true;
209 break;
210 case 'F':
211 fragment = true;
212 break;
213 default:
214 throw parse_acc_message{"Discarding malformed custom app control message. "
215 "Unknown flag '"
216 + std::to_string(c) + "' in message found"};
217 }
218 }
219 // exactly just one encoding has to be present. So ensure that the sum of the bools as 0/1 is exactly 1
220 if (textEncoding + base64Encoding != 1)
221 {
222 throw parse_acc_message{"Discarding malformed custom app control message. "
223 "Unknown or no encoding flag in message found"};
224 }
225
226 if (base64Encoding)
228
229 if (!recvbuf.empty() && recvprotocol != protocol)
230 {
231 throw parse_acc_message{"custom app control framing error: message with different "
232 "protocol and previous fragmented message not finished"};
233 }
234
235 recvbuf.write(message.data(), message.size());
236 recvprotocol = protocol;
237 return !fragment;
238 }
239
240 std::pair<std::string, std::string> get_message()
241 {
242 auto ret = buf_to_string(recvbuf);
243 recvbuf.clear();
244 return {recvprotocol, ret};
245 }
246};
247
248
249
250} // namespace openvpn
bool receive_message(const std::string &msg)
std::pair< std::string, std::string > get_message()
std::string encode(const V &data) const
Definition base64.hpp:139
size_t decode(void *data, size_t len, const std::string &str) const
Definition base64.hpp:186
void clear()
Clears the contents of the buffer.
Definition buffer.hpp:1824
void reset(const size_t min_capacity, const BufferFlags flags=BufAllocFlags::NO_FLAGS)
Resets the buffer with the specified minimum capacity and flags.
Definition buffer.hpp:1773
bool defined() const
Returns true if the buffer is not empty.
Definition buffer.hpp:1224
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
#define OPENVPN_EXCEPTION(C)
constexpr BufferFlags GROW(1u<< 2)
if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
STRING utf8_printable(const STRING &str, size_t max_len_flags)
Definition unicode.hpp:129
auto join(const T &strings, const typename T::value_type &delim, const bool tail=false)
Definition string.hpp:521
std::vector< T > split(const T &str, const typename T::value_type sep, const int maxsplit=-1)
Definition string.hpp:492
const Base64 * base64
Definition base64.hpp:299
bool parse_number(const char *str, T &retval, const bool nondigit_term=false)
Definition number.hpp:34
std::string buf_to_string(const Buffer &buf)
Definition bufstr.hpp:22
bool supports_protocol(const std::string &protocol)
std::vector< std::string > supported_protocols
List of supported protocols.
int max_msg_size
Maximum size of each individual message/message fragment.
bool encoding_base64
Supports sending/receiving messages as base64 encoded binary.
std::vector< std::string > format_message(const std::string &protocol, const std::string &message)
Format a protocol string and a message into a properly packed series of message fragments.
bool operator==(const AppControlMessageConfig &other) const
bool encoding_binary
support sending binary as is as part of the ACC control channel message (not implemented yet)
void parse_flags(const std::string &flags)
reroute_gw flags
std::string ret
static std::stringstream out
Definition test_path.cpp:10
const char message[]
#define msg(flags,...)