37#ifndef OPENVPN_COMMON_OPTIONS_H
38#define OPENVPN_COMMON_OPTIONS_H
46#include <unordered_map>
92 static_assert(std::is_nothrow_move_constructible<Option>::value,
"class Option not noexcept move constructable");
95 template <
typename T,
typename... Args>
96 explicit Option(T first, Args... args)
99 from_list(std::move(first), std::forward<Args>(args)...);
104 const size_t pos =
str.find_first_of(
"\r\n");
105 const size_t len = max_len & ((size_t)
MULTILINE - 1);
106 if (pos != std::string::npos && !(max_len &
MULTILINE))
130 const size_t s =
data.size();
132 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
err_ref() <<
" must have at least " << (n - 1) <<
" arguments");
137 const size_t s =
data.size();
144 if (max_len > 0 && index <
data.size())
154 if (
data.size() == 2)
156 const std::string &
str =
data[1];
157 const size_t pos =
str.find_first_of(
"\r\n");
158 return pos != std::string::npos;
178 catch (
const std::exception &)
180 return "[DIRECTIVE]";
184 const std::string &
get(
const size_t index,
const size_t max_len)
const
191 std::string
get_optional(
const size_t index,
const size_t max_len)
const
194 if (index <
data.size())
199 std::string
get_default(
const size_t index,
const size_t max_len,
const std::string &default_value)
const
202 if (index <
data.size())
204 return default_value;
207 const std::string *
get_ptr(
const size_t index,
const size_t max_len)
const
210 if (index <
data.size())
215 template <
typename T>
218 using T_nonconst =
typename std::remove_const<T>::type;
220 const std::string &numstr =
get(idx, 64);
221 if (numstr.length() >= 2 && numstr[0] ==
'0' && numstr[1] ==
'x')
226 else if (!parse_number<T_nonconst>(numstr, n))
231 template <
typename T>
232 T
get_num(
const size_t idx,
const T default_value)
const
235 return get_num<T>(idx);
236 return default_value;
239 template <
typename T>
240 T
get_num(
const size_t idx,
const T default_value,
const T min_value,
const T max_value)
const
242 const T
ret = get_num<T>(idx, default_value);
243 if (
ret != default_value && (ret < min_value || ret > max_value))
248 template <
typename T>
249 T
get_num(
const size_t idx,
const T min_value,
const T max_value)
const
251 const T
ret = get_num<T>(idx);
252 if (ret < min_value || ret > max_value)
259 std::ostringstream
out;
264 for (std::vector<std::string>::const_iterator i =
data.begin(); i !=
data.end(); ++i)
278 static void escape_string(std::ostream &
out,
const std::string &term,
const bool must_quote)
282 for (std::string::const_iterator j = term.begin(); j != term.end(); ++j)
285 if (c ==
'\"' || c ==
'\\')
297 std::ostringstream
out;
299 for (std::vector<std::string>::const_iterator i =
data.begin(); i !=
data.end(); ++i)
301 const std::string &term = *i;
330 data.push_back(item);
334 data.push_back(std::move(item));
346 const std::string &
ref(
const size_t i)
const
350 std::string &
ref(
const size_t i)
368 const size_t n = std::min(
data.size(), n_elements);
378 void touch(
bool lightly =
false)
const
420 std::string
ret =
"option";
462 data.insert(
data.end(), arg.begin(), arg.end());
465 template <
typename T,
typename... Args>
472 template <
typename T>
473 void range_error(
const size_t idx,
const T min_value,
const T max_value)
const
475 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
err_ref() <<
'[' << idx <<
"] must be in the range [" << min_value <<
',' << max_value <<
']');
480 for (
const auto c :
str)
514 using IndexMap = std::unordered_map<std::string, IndexList>;
519 return c ==
'#' || c ==
';';
583 const std::uint64_t max_bytes_arg,
584 const size_t extra_bytes_per_opt_arg,
585 const size_t extra_bytes_per_term_arg,
586 const size_t max_line_len_arg,
587 const size_t max_directive_len_arg)
646 throw option_error(ERR_INVALID_CONFIG,
err);
674 KeyValue(
const std::string &key_arg,
const std::string &value_arg,
const int key_priority_arg = 0)
681 return key.length() +
value.length();
686 bool newline_present =
false;
688 const std::string unesc_value =
unescape(
value, newline_present);
690 if (
key.starts_with(meta_prefix))
702 else if (unesc_value !=
"NOARGS")
703 Split::by_space_void<Option, Lex, SpaceMatch, Limits>(opt, unesc_value, lim);
710 const size_t dp =
key.find_last_of(
".");
711 if (dp != std::string::npos)
713 const size_t tp = dp + 1;
714 if (tp <
key.length())
716 const char *tail =
key.c_str() + tp;
719 key_priority = parse_number_throw<int>(tail,
"option priority");
722 catch (
const number_parse_exception &)
732 const int cmp = a->key.compare(b->key);
737 return a->key_priority < b->key_priority;
745 static std::string
unescape(
const std::string &
value,
bool &newline_present)
751 for (
size_t i = 0; i <
value.length(); ++i)
753 const char c =
value[i];
759 newline_present =
true;
787 for (
size_t i = 0; i <
key.length(); ++i)
789 const char c =
key[i];
790 if (c >=
'a' && c <=
'z')
792 else if (c >=
'A' && c <=
'Z')
795 return upper && !lower;
809 for (iterator i = begin(); i != end(); ++i)
824 template <
typename T,
typename... Args>
827 reserve(1 +
sizeof...(args));
828 from_list(std::move(first), std::forward<Args>(args)...);
858 ret->parse_from_config(
str, lim);
873 std::vector<Option>::clear();
882 std::vector<std::string> list = Split::by_char<std::vector<std::string>,
Lex,
Limits>(
str,
',', 0, ~0, lim);
883 for (std::vector<std::string>::const_iterator i = list.begin(); i != list.end(); ++i)
885 const Option opt = Split::by_space<Option, Lex, SpaceMatch, Limits>(*i, lim);
902 for (
auto &arg : argv)
905 if (a.starts_with(
"--"))
929 const std::string &line = in.
line_ref();
932 Split::by_char_void<Option, NullLex, Limits>(opt, line,
'=', 0, 1, lim);
949 const std::string meta_prefix = meta_tag +
"_";
951 for (KeyValueList::const_iterator i = list.begin(); i != list.end(); ++i)
969 return Split::by_space<Option, LexComment, SpaceMatch, Limits>(line, lim);
980 bool in_multiline =
false;
985 if (in.line_overflow())
987 const std::string &line = in.line_ref();
1000 in_multiline =
false;
1004 std::string &mref = multiline.
ref(1);
1020 multiline = std::move(opt);
1021 in_multiline =
true;
1044 bool in_multiline =
false;
1046 const std::string prefix = tag +
"_";
1050 if (in.line_overflow())
1052 std::string &line = in.line_ref();
1053 if (line.starts_with(
"# "))
1055 line = std::string(line, 2);
1068 in_multiline =
false;
1072 std::string &mref = multiline.
ref(1);
1077 else if (line.starts_with(prefix))
1079 Option opt = Split::by_char<Option, NullLex, Limits>(std::string(line, prefix.length()),
'=', 0, 1, lim);
1088 multiline = std::move(opt);
1089 in_multiline =
true;
1113 reserve(size() + other.size());
1114 for (
const auto &opt : other)
1116 if (!filt || filt->filter(opt))
1129 reserve(size() + other.size());
1130 for (
auto &opt : other)
1132 if (!filt || filt->filter(opt))
1142 IndexMap::const_iterator oi = other.
map().find(name);
1143 unsigned int count = 0;
1144 if (oi != other.
map().end())
1145 for (IndexList::const_iterator i = oi->second.begin(); i != oi->second.end(); ++i)
1147 const Option &opt = other[*i];
1161 for (std::vector<Option>::const_iterator i = other.begin(); i != other.end(); ++i)
1176 IndexMap::const_iterator e =
map_.find(name);
1177 if (e !=
map_.end())
1179 const size_t size = e->second.size();
1182 for (
const auto &optidx : e->second)
1184 (*this)[optidx].touch(
true);
1186 const Option *
ret = &((*this)[e->second[size - 1]]);
1198 IndexMap::const_iterator e =
map_.find(name);
1199 if (e !=
map_.end() && !e->second.empty())
1201 if (e->second.size() == 1)
1203 const Option *
ret = &((*this)[e->second[0]]);
1207 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"more than one instance of option '" << name <<
'\'');
1217 IndexMap::const_iterator e =
map_.find(name);
1218 if (e !=
map_.end() && !e->second.empty())
1220 const Option *first = &((*this)[e->second[0]]);
1222 if (e->second.size() >= 2)
1224 for (
size_t i = 1; i < e->second.size(); ++i)
1226 const Option *other = &(*this)[e->second[i]];
1228 if (*other != *first)
1229 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"more than one instance of option '" << name <<
"' with inconsistent argument(s)");
1245 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"option '" << name <<
"' not found");
1252 IndexMap::const_iterator e =
map_.find(name);
1253 if (e !=
map_.end() && !e->second.empty())
1255 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"option '" << name <<
"' not found");
1262 IndexMap::const_iterator e =
map_.find(name);
1263 if (e !=
map_.end() && !e->second.empty())
1269 std::string
cat(
const std::string &name)
const
1276 OptionList::IndexList::const_iterator i;
1277 for (i = il->begin(); i != il->end(); ++i)
1279 const Option &o = (*this)[*i];
1281 size += o.
ref(1).length() + 1;
1283 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"option '" << name <<
"' (" << o.
size() <<
") must have exactly one parameter");
1286 for (i = il->begin(); i != il->end(); ++i)
1288 const Option &o = (*this)[*i];
1310 return get_ptr(name) !=
nullptr;
1316 const std::string &
get(
const std::string &name,
size_t index,
const size_t max_len)
const
1319 return o.
get(index, max_len);
1325 std::string
get_optional(
const std::string &name,
size_t index,
const size_t max_len)
const
1329 return o->
get(index, max_len);
1349 catch (
const std::exception &)
1356 const char *
get_c_str(
const std::string &name,
size_t index,
const size_t max_len)
const
1360 return o->
get(index, max_len).c_str();
1369 const size_t max_len,
1370 const std::string &default_value)
const
1374 return o->
get(index, max_len);
1375 return default_value;
1381 const size_t max_len,
1382 const std::string &default_value)
const
1387 const std::string *s = o->
get_ptr(index, max_len);
1391 return default_value;
1394 template <
typename T>
1395 T
get_num(
const std::string &name,
const size_t idx,
const T default_value)
const
1397 using T_nonconst =
typename std::remove_const<T>::type;
1398 T_nonconst n = default_value;
1401 n = o->
get_num<T>(idx, default_value);
1405 template <
typename T>
1408 const T default_value,
1410 const T max_value)
const
1412 using T_nonconst =
typename std::remove_const<T>::type;
1413 T_nonconst n = default_value;
1416 n = o->
get_num<T>(idx, default_value, min_value, max_value);
1420 template <
typename T>
1421 T
get_num(
const std::string &name,
const size_t idx,
const T min_value,
const T max_value)
const
1424 return o.
get_num<T>(idx, min_value, max_value);
1427 template <
typename T>
1428 T
get_num(
const std::string &name,
const size_t idx)
const
1435 void touch(
const std::string &name)
const
1446 std::ostringstream
out;
1447 for (
size_t i = 0; i < size(); ++i)
1449 const Option &o = (*this)[i];
1464 for (
auto &e : *
this)
1468 ret += e.escape(
true);
1478 std::ostringstream
out;
1479 for (IndexMap::const_iterator i =
map_.begin(); i !=
map_.end(); ++i)
1481 out << i->first <<
" [";
1482 for (IndexList::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
1494 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1497 if (!opt.
touched() && !(opt.
meta() && ignore_meta))
1508 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1523 title =
"NOTE: Unused Options";
1534 const size_t i = size();
1536 map_[opt.
ref(0)].push_back((
unsigned int)i);
1551 for (
size_t i = 0; i < size(); ++i)
1553 const Option &opt = (*this)[i];
1562 for (std::string::const_iterator i = line.begin(); i != line.end(); ++i)
1576 const size_t n =
str.length();
1577 return n >= 3 &&
str[0] ==
'<' &&
str[1] !=
'/' &&
str[n - 1] ==
'>';
1583 const size_t n =
str.length();
1584 return n >= 4 &&
str[0] ==
'<' &&
str[1] ==
'/' &&
str.substr(2, n - 3) == tag &&
str[n - 1] ==
'>';
1590 const size_t n =
str.length();
1592 str =
str.substr(1, n - 2);
1601 if (c ==
'\n' || c ==
'\r')
1608 if (line.length() >= 2
1624 throw option_error(ERR_INVALID_CONFIG,
"multiline breakout detected");
1633 return str.ends_with(
"_START");
1639 return prefix + tag +
"_STOP" ==
str;
1645 const size_t n =
str.length();
1647 str = std::string(
str, 0, n - 6);
1662 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"line " << line_num <<
" is too long");
1670 template <
typename T,
typename... Args>
Helper class to handle quote processing.
bool in_quote() const
Check if currently inside a quote.
bool handle_quote(char c)
Handle a character as a potential quote.
Option convert_to_option(Limits *lim, const std::string &meta_prefix) const
KeyValue(const std::string &key_arg, const std::string &value_arg, const int key_priority_arg=0)
size_t combined_length() const
static std::string unescape(const std::string &value, bool &newline_present)
static bool compare(const Ptr &a, const Ptr &b)
static bool singular_arg(const std::string &key)
Limits(const std::string &error_message, const std::uint64_t max_bytes_arg, const size_t extra_bytes_per_opt_arg, const size_t extra_bytes_per_term_arg, const size_t max_line_len_arg, const size_t max_directive_len_arg)
void validate_directive(const Option &opt)
const size_t max_line_len
void add_string(const std::string &str)
void add_bytes(const size_t n)
size_t get_max_line_len() const
const size_t extra_bytes_per_term
const size_t extra_bytes_per_opt
const std::uint64_t max_bytes
std::uint64_t get_bytes() const
const size_t max_directive_len
static OptionList parse_from_argv_static(const std::vector< std::string > &argv)
static void detect_multiline_breakout(const std::string &opt, const std::string &tag)
std::string get_optional(const std::string &name, size_t index, const size_t max_len) const
void show_unused_options(const char *title=nullptr) const
static void untag_open_tag(std::string &str)
void parse_from_peer_info(const std::string &str, Limits *lim)
static void not_closed_out_err(const char *type, const Option &opt)
void from_list(Option opt)
void extend_nonexistent(const OptionList &other)
OptionList(T first, Args... args)
void parse_from_config(const std::string &str, Limits *lim)
const IndexMap & map() const
std::string cat(const std::string &name) const
static bool is_close_tag(const std::string &str, const std::string &tag)
const Option * get_consistent(const std::string &name) const
void extend(const OptionList &other, FilterBase *filt=nullptr)
const char * get_c_str(const std::string &name, size_t index, const size_t max_len) const
std::string get_optional_noexcept(const std::string &name, size_t index, const size_t max_len) const
void parse_from_argv(const std::vector< std::string > &argv)
const IndexList & get_index(const std::string &name) const
T get_num(const std::string &name, const size_t idx, const T default_value, const T min_value, const T max_value) const
void parse_from_key_value_list(const KeyValueList &list, const std::string &meta_tag, Limits *lim)
T get_num(const std::string &name, const size_t idx, const T default_value) const
T get_num(const std::string &name, const size_t idx) const
T get_num(const std::string &name, const size_t idx, const T min_value, const T max_value) const
std::string render_map() const
const IndexList * get_index_ptr(const std::string &name) const
void touch(const std::string &name) const
bool exists_unique(const std::string &name) const
static OptionList parse_from_csv_static(const std::string &str, Limits *lim)
static OptionList::Ptr parse_from_config_static_ptr(const std::string &str, Limits *lim)
static void line_too_long(const int line_num)
static void extraneous_err(const int line_num, const char *type, const Option &opt)
std::vector< unsigned int > IndexList
std::unordered_map< std::string, IndexList > IndexMap
static bool ignore_line(const std::string &line)
std::string get_optional_relaxed(const std::string &name, size_t index, const size_t max_len) const
void parse_from_csv(const std::string &str, Limits *lim)
std::pair< std::string, IndexList > IndexPair
const std::string & get(const std::string &name, size_t index, const size_t max_len) const
void add_item(const Option &opt)
void from_list(T first, Args... args)
static Option parse_option_from_line(const std::string &line, Limits *lim)
const Option * get_unique_ptr(const std::string &name) const
std::string get_default_relaxed(const std::string &name, size_t index, const size_t max_len, const std::string &default_value) const
static OptionList parse_from_csv_static_nomap(const std::string &str, Limits *lim)
const Option & get(const std::string &name) const
static OptionList parse_from_config_static(const std::string &str, Limits *lim)
void extend(OptionList &&other, FilterBase *filt=nullptr)
size_t n_unused(bool ignore_meta=false) const
const Option * get_ptr(const std::string &name) const
unsigned int extend(const OptionList &other, const std::string &name)
static bool is_close_meta_tag(const std::string &str, const std::string &prefix, const std::string &tag)
std::string render_csv() const
std::string render(const unsigned int flags) const
static bool detect_multiline_breakout_nothrow(const std::string &opt, const std::string &tag)
size_t meta_unused() const
static bool is_comment(const char c)
std::string get_default(const std::string &name, size_t index, const size_t max_len, const std::string &default_value) const
static bool is_open_tag(const std::string &str)
static bool is_open_meta_tag(const std::string &str)
static void untag_open_meta_tag(std::string &str)
void parse_meta_from_config(const std::string &str, const std::string &tag, Limits *lim)
bool exists(const std::string &name) const
bool touched_lightly() const
bool is_multiline() const
Option(T first, Args... args)
std::string get_default(const size_t index, const size_t max_len, const std::string &default_value) const
void exact_args(const size_t n) const
std::string get_optional(const size_t index, const size_t max_len) const
void push_back(const std::string &item)
void touch(bool lightly=false) const
void from_list(std::vector< std::string > arg)
void from_list(std::string arg)
std::string err_ref() const
const std::string & get(const size_t index, const size_t max_len) const
volatile touchedState touched_
static const char * validate_status_description(const validate_status status)
T get_num(const size_t idx, const T default_value) const
bool must_quote_string(const std::string &str, const bool csv) const
static void escape_string(std::ostream &out, const std::string &term, const bool must_quote)
static void validate_string(const std::string &name, const std::string &str, const size_t max_len)
void push_back(std::string &&item)
OPENVPN_UNTAGGED_EXCEPTION(RejectedException)
std::vector< std::string > data
T get_num(const size_t idx) const
void set_meta(bool value=true)
void min_args(const size_t n) const
void resize(const size_t n)
std::string & ref(const size_t i)
@ OPTION_OF_SAME_NAME_TOUCHED
T get_num(const size_t idx, const T min_value, const T max_value) const
void validate_arg(const size_t index, const size_t max_len) const
void reserve(const size_t n)
std::string printable_directive() const
bool warn_only_if_unknown_
T get_num(const size_t idx, const T default_value, const T min_value, const T max_value) const
void remove_first(const size_t n_elements)
bool operator!=(const Option &other) const
void from_list(T first, Args... args)
void from_list(const char *arg)
std::string escape(const bool csv) const
void range_error(const size_t idx, const T min_value, const T max_value) const
const std::string & ref(const size_t i) const
const std::string * get_ptr(const size_t index, const size_t max_len) const
bool operator==(const Option &other) const
bool warnonlyunknown() const
static validate_status validate(const std::string &str, const size_t max_len)
std::string render(const unsigned int flags) const
Reference count base class for objects tracked by RCPtr. Allows copying and assignment.
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
#define OPENVPN_THROW_ARG1(exc, arg, stuff)
#define OPENVPN_LOG_NTNL(args)
STRING utf8_printable(const STRING &str, size_t max_len_flags)
size_t utf8_length(const STRING &str)
void add_trailing(std::string &str, const char c)
bool is_space(const char c)
bool parse_hex_number(const char *str, T &retval)
virtual bool filter(const Option &opt)=0
static bool is_space(char c)
os<< "Session Name: "<< tbc-> session_name<< '\n';os<< "Layer: "<< tbc-> layer str()<< '\n'
server addresses push_back(address)
static std::stringstream out