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;
163 static void validate_string(
const std::string &name,
const std::string &str,
const size_t max_len)
178 catch (
const std::exception &)
180 return "[DIRECTIVE]";
186 return std::count(
data.begin(),
data.end(), parameter);
189 const std::string &
get(
const size_t index,
const size_t max_len)
const
196 std::string
get_optional(
const size_t index,
const size_t max_len)
const
199 if (index <
data.size())
204 std::string
get_default(
const size_t index,
const size_t max_len,
const std::string &default_value)
const
207 if (index <
data.size())
209 return default_value;
212 const std::string *
get_ptr(
const size_t index,
const size_t max_len)
const
215 if (index <
data.size())
220 template <
typename T>
223 using T_nonconst =
typename std::remove_const<T>::type;
225 const std::string &numstr =
get(idx, 64);
226 if (numstr.length() >= 2 && numstr[0] ==
'0' && numstr[1] ==
'x')
231 else if (!parse_number<T_nonconst>(numstr, n))
236 template <
typename T>
237 T
get_num(
const size_t idx,
const T default_value)
const
240 return get_num<T>(idx);
241 return default_value;
244 template <
typename T>
245 T
get_num(
const size_t idx,
const T default_value,
const T min_value,
const T max_value)
const
247 const T ret = get_num<T>(idx, default_value);
248 if (ret != default_value && (ret < min_value || ret > max_value))
253 template <
typename T>
254 T
get_num(
const size_t idx,
const T min_value,
const T max_value)
const
256 const T ret = get_num<T>(idx);
257 if (ret < min_value || ret > max_value)
262 std::string
render(
const unsigned int flags)
const
264 std::ostringstream
out;
269 for (std::vector<std::string>::const_iterator i =
data.begin(); i !=
data.end(); ++i)
283 static void escape_string(std::ostream &
out,
const std::string &term,
const bool must_quote)
287 for (std::string::const_iterator j = term.begin(); j != term.end(); ++j)
290 if (c ==
'\"' || c ==
'\\')
302 std::ostringstream
out;
304 for (std::vector<std::string>::const_iterator i =
data.begin(); i !=
data.end(); ++i)
306 const std::string &term = *i;
335 data.push_back(item);
339 data.push_back(std::move(item));
351 const std::string &
ref(
const size_t i)
const
355 std::string &
ref(
const size_t i)
373 const size_t n = std::min(
data.size(), n_elements);
383 void touch(
bool lightly =
false)
const
425 std::string ret =
"option";
467 data.insert(
data.end(), arg.begin(), arg.end());
470 template <
typename T,
typename... Args>
477 template <
typename T>
478 void range_error(
const size_t idx,
const T min_value,
const T max_value)
const
480 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
err_ref() <<
'[' << idx <<
"] must be in the range [" << min_value <<
',' << max_value <<
']');
485 for (
const auto c : str)
519 using IndexMap = std::unordered_map<std::string, IndexList>;
524 return c ==
'#' || c ==
';';
588 const std::uint64_t max_bytes_arg,
589 const size_t extra_bytes_per_opt_arg,
590 const size_t extra_bytes_per_term_arg,
591 const size_t max_line_len_arg,
592 const size_t max_directive_len_arg)
611 bytes += str.length();
651 throw option_error(ERR_INVALID_CONFIG,
err);
679 KeyValue(
const std::string &key_arg,
const std::string &value_arg,
const int key_priority_arg = 0)
686 return key.length() +
value.length();
691 bool newline_present =
false;
693 const std::string unesc_value =
unescape(
value, newline_present);
695 if (
key.starts_with(meta_prefix))
707 else if (unesc_value !=
"NOARGS")
708 Split::by_space_void<Option, Lex, SpaceMatch, Limits>(opt, unesc_value, lim);
715 const size_t dp =
key.find_last_of(
".");
716 if (dp != std::string::npos)
718 const size_t tp = dp + 1;
719 if (tp <
key.length())
721 const char *tail =
key.c_str() + tp;
724 key_priority = parse_number_throw<int>(tail,
"option priority");
727 catch (
const number_parse_exception &)
737 const int cmp = a->key.compare(b->key);
742 return a->key_priority < b->key_priority;
750 static std::string
unescape(
const std::string &
value,
bool &newline_present)
753 ret.reserve(
value.length());
756 for (
size_t i = 0; i <
value.length(); ++i)
758 const char c =
value[i];
764 newline_present =
true;
792 for (
size_t i = 0; i <
key.length(); ++i)
794 const char c =
key[i];
795 if (c >=
'a' && c <=
'z')
797 else if (c >=
'A' && c <=
'Z')
800 return upper && !lower;
814 for (iterator i = begin(); i != end(); ++i)
829 template <
typename T,
typename... Args>
832 reserve(1 +
sizeof...(args));
833 from_list(std::move(first), std::forward<Args>(args)...);
863 ret->parse_from_config(str, lim);
878 std::vector<Option>::clear();
887 std::vector<std::string> list = Split::by_char<std::vector<std::string>,
Lex,
Limits>(str,
',', 0, ~0, lim);
888 for (std::vector<std::string>::const_iterator i = list.begin(); i != list.end(); ++i)
890 const Option opt = Split::by_space<Option, Lex, SpaceMatch, Limits>(*i, lim);
898 push_back(std::move(opt));
907 for (
auto &arg : argv)
910 if (a.starts_with(
"--"))
914 push_back(std::move(opt));
923 push_back(std::move(opt));
934 const std::string &line = in.
line_ref();
937 Split::by_char_void<Option, NullLex, Limits>(opt, line,
'=', 0, 1, lim);
945 push_back(std::move(opt));
954 const std::string meta_prefix = meta_tag +
"_";
956 for (KeyValueList::const_iterator i = list.begin(); i != list.end(); ++i)
968 push_back(std::move(opt));
974 return Split::by_space<Option, LexComment, SpaceMatch, Limits>(line, lim);
985 bool in_multiline =
false;
990 if (in.line_overflow())
992 const std::string &line = in.line_ref();
1003 push_back(std::move(multiline));
1005 in_multiline =
false;
1009 std::string &mref = multiline.
ref(1);
1025 multiline = std::move(opt);
1026 in_multiline =
true;
1035 push_back(std::move(opt));
1049 bool in_multiline =
false;
1051 const std::string prefix = tag +
"_";
1055 if (in.line_overflow())
1057 std::string &line = in.line_ref();
1058 if (line.starts_with(
"# "))
1060 line = std::string(line, 2);
1071 push_back(std::move(multiline));
1073 in_multiline =
false;
1077 std::string &mref = multiline.
ref(1);
1082 else if (line.starts_with(prefix))
1084 Option opt = Split::by_char<Option, NullLex, Limits>(std::string(line, prefix.length()),
'=', 0, 1, lim);
1093 multiline = std::move(opt);
1094 in_multiline =
true;
1104 push_back(std::move(opt));
1118 reserve(size() + other.size());
1119 for (
const auto &opt : other)
1121 if (!filt || filt->filter(opt))
1134 reserve(size() + other.size());
1135 for (
auto &opt : other)
1137 if (!filt || filt->filter(opt))
1138 push_back(std::move(opt));
1147 IndexMap::const_iterator oi = other.
map().find(name);
1148 unsigned int count = 0;
1149 if (oi != other.
map().end())
1150 for (IndexList::const_iterator i = oi->second.begin(); i != oi->second.end(); ++i)
1152 const Option &opt = other[*i];
1166 for (std::vector<Option>::const_iterator i = other.begin(); i != other.end(); ++i)
1181 IndexMap::const_iterator e =
map_.find(name);
1182 if (e !=
map_.end())
1184 const size_t size = e->second.size();
1187 for (
const auto &optidx : e->second)
1189 (*this)[optidx].touch(
true);
1191 const Option *ret = &((*this)[e->second[size - 1]]);
1203 IndexMap::const_iterator e =
map_.find(name);
1204 if (e !=
map_.end() && !e->second.empty())
1206 if (e->second.size() == 1)
1208 const Option *ret = &((*this)[e->second[0]]);
1212 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"more than one instance of option '" << name <<
'\'');
1222 IndexMap::const_iterator e =
map_.find(name);
1223 if (e !=
map_.end() && !e->second.empty())
1225 const Option *first = &((*this)[e->second[0]]);
1227 if (e->second.size() >= 2)
1229 for (
size_t i = 1; i < e->second.size(); ++i)
1231 const Option *other = &(*this)[e->second[i]];
1233 if (*other != *first)
1234 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"more than one instance of option '" << name <<
"' with inconsistent argument(s)");
1250 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"option '" << name <<
"' not found");
1257 IndexMap::const_iterator e =
map_.find(name);
1258 if (e !=
map_.end() && !e->second.empty())
1260 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"option '" << name <<
"' not found");
1267 IndexMap::const_iterator e =
map_.find(name);
1268 if (e !=
map_.end() && !e->second.empty())
1274 std::string
cat(
const std::string &name)
const
1281 OptionList::IndexList::const_iterator i;
1282 for (i = il->begin(); i != il->end(); ++i)
1284 const Option &o = (*this)[*i];
1286 size += o.
ref(1).length() + 1;
1288 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"option '" << name <<
"' (" << o.
size() <<
") must have exactly one parameter");
1291 for (i = il->begin(); i != il->end(); ++i)
1293 const Option &o = (*this)[*i];
1315 return get_ptr(name) !=
nullptr;
1321 const std::string &
get(
const std::string &name,
size_t index,
const size_t max_len)
const
1324 return o.
get(index, max_len);
1330 std::string
get_optional(
const std::string &name,
size_t index,
const size_t max_len)
const
1334 return o->
get(index, max_len);
1354 catch (
const std::exception &)
1361 const char *
get_c_str(
const std::string &name,
size_t index,
const size_t max_len)
const
1365 return o->
get(index, max_len).c_str();
1374 const size_t max_len,
1375 const std::string &default_value)
const
1379 return o->
get(index, max_len);
1380 return default_value;
1386 const size_t max_len,
1387 const std::string &default_value)
const
1392 const std::string *s = o->
get_ptr(index, max_len);
1396 return default_value;
1399 template <
typename T>
1400 T
get_num(
const std::string &name,
const size_t idx,
const T default_value)
const
1402 using T_nonconst =
typename std::remove_const<T>::type;
1403 T_nonconst n = default_value;
1406 n = o->
get_num<T>(idx, default_value);
1410 template <
typename T>
1413 const T default_value,
1415 const T max_value)
const
1417 using T_nonconst =
typename std::remove_const<T>::type;
1418 T_nonconst n = default_value;
1421 n = o->
get_num<T>(idx, default_value, min_value, max_value);
1425 template <
typename T>
1426 T
get_num(
const std::string &name,
const size_t idx,
const T min_value,
const T max_value)
const
1429 return o.
get_num<T>(idx, min_value, max_value);
1432 template <
typename T>
1433 T
get_num(
const std::string &name,
const size_t idx)
const
1440 void touch(
const std::string &name)
const
1449 std::string
render(
const unsigned int flags)
const
1451 std::ostringstream
out;
1452 for (
size_t i = 0; i < size(); ++i)
1454 const Option &o = (*this)[i];
1469 for (
auto &e : *
this)
1473 ret += e.escape(
true);
1483 std::ostringstream
out;
1484 for (IndexMap::const_iterator i =
map_.begin(); i !=
map_.end(); ++i)
1486 out << i->first <<
" [";
1487 for (IndexList::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
1499 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1502 if (!opt.
touched() && !(opt.
meta() && ignore_meta))
1513 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1528 title =
"NOTE: Unused Options";
1539 const size_t i = size();
1541 map_[opt.
ref(0)].push_back((
unsigned int)i);
1556 for (
size_t i = 0; i < size(); ++i)
1558 const Option &opt = (*this)[i];
1567 for (std::string::const_iterator i = line.begin(); i != line.end(); ++i)
1581 const size_t n = str.length();
1582 return n >= 3 && str[0] ==
'<' && str[1] !=
'/' && str[n - 1] ==
'>';
1588 const size_t n = str.length();
1589 return n >= 4 && str[0] ==
'<' && str[1] ==
'/' && str.substr(2, n - 3) == tag && str[n - 1] ==
'>';
1595 const size_t n = str.length();
1597 str = str.substr(1, n - 2);
1606 if (c ==
'\n' || c ==
'\r')
1613 if (line.length() >= 2
1629 throw option_error(ERR_INVALID_CONFIG,
"multiline breakout detected");
1638 return str.ends_with(
"_START");
1642 static bool is_close_meta_tag(
const std::string &str,
const std::string &prefix,
const std::string &tag)
1644 return prefix + tag +
"_STOP" == str;
1650 const size_t n = str.length();
1652 str = std::string(str, 0, n - 6);
1667 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"line " << line_num <<
" is too long");
1672 push_back(std::move(opt));
1675 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)
bool parameter_exists(const std::string ¶meter) const
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)
static std::stringstream out