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))
131 const size_t s =
data.size();
133 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
err_ref() <<
" must have at least " << (n - 1) <<
" arguments");
138 const size_t s =
data.size();
145 if (max_len > 0 && index <
data.size())
155 if (
data.size() == 2)
157 const std::string &str =
data[1];
158 const size_t pos = str.find_first_of(
"\r\n");
159 return pos != std::string::npos;
165 static void validate_string(
const std::string &name,
const std::string &str,
const size_t max_len)
181 catch (
const std::exception &)
183 return "[DIRECTIVE]";
187 const std::string &
get(
const size_t index,
const size_t max_len)
const
194 std::string
get_optional(
const size_t index,
const size_t max_len)
const
197 if (index <
data.size())
203 std::string
get_default(
const size_t index,
const size_t max_len,
const std::string &default_value)
const
206 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())
221 template <
typename T>
224 typedef typename std::remove_const<T>::type T_nonconst;
226 const std::string &numstr =
get(idx, 64);
227 if (numstr.length() >= 2 && numstr[0] ==
'0' && numstr[1] ==
'x')
232 else if (!parse_number<T_nonconst>(numstr, n))
237 template <
typename T>
238 T
get_num(
const size_t idx,
const T default_value)
const
241 return get_num<T>(idx);
243 return default_value;
246 template <
typename T>
247 T
get_num(
const size_t idx,
const T default_value,
const T min_value,
const T max_value)
const
249 const T
ret = get_num<T>(idx, default_value);
250 if (
ret != default_value && (ret < min_value || ret > max_value))
255 template <
typename T>
256 T
get_num(
const size_t idx,
const T min_value,
const T max_value)
const
258 const T
ret = get_num<T>(idx);
259 if (ret < min_value || ret > max_value)
266 std::ostringstream
out;
271 for (std::vector<std::string>::const_iterator i =
data.begin(); i !=
data.end(); ++i)
285 static void escape_string(std::ostream &
out,
const std::string &term,
const bool must_quote)
289 for (std::string::const_iterator j = term.begin(); j != term.end(); ++j)
292 if (c ==
'\"' || c ==
'\\')
304 std::ostringstream
out;
306 for (std::vector<std::string>::const_iterator i =
data.begin(); i !=
data.end(); ++i)
308 const std::string &term = *i;
337 data.push_back(item);
341 data.push_back(std::move(item));
353 const std::string &
ref(
const size_t i)
const
357 std::string &
ref(
const size_t i)
375 const size_t n = std::min(
data.size(), n_elements);
385 void touch(
bool lightly =
false)
const
427 std::string
ret =
"option";
469 data.insert(
data.end(), arg.begin(), arg.end());
472 template <
typename T,
typename... Args>
479 template <
typename T>
480 void range_error(
const size_t idx,
const T min_value,
const T max_value)
const
482 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
err_ref() <<
'[' << idx <<
"] must be in the range [" << min_value <<
',' << max_value <<
']');
487 for (
const auto c : str)
521 typedef std::unordered_map<std::string, IndexList>
IndexMap;
526 return c ==
'#' || c ==
';';
592 const std::uint64_t max_bytes_arg,
593 const size_t extra_bytes_per_opt_arg,
594 const size_t extra_bytes_per_term_arg,
595 const size_t max_line_len_arg,
596 const size_t max_directive_len_arg)
615 bytes += str.length();
655 throw option_error(ERR_INVALID_CONFIG,
err);
683 KeyValue(
const std::string &key_arg,
const std::string &value_arg,
const int key_priority_arg = 0)
690 return key.length() +
value.length();
695 bool newline_present =
false;
697 const std::string unesc_value =
unescape(
value, newline_present);
711 else if (unesc_value !=
"NOARGS")
712 Split::by_space_void<Option, Lex, SpaceMatch, Limits>(opt, unesc_value, lim);
719 const size_t dp =
key.find_last_of(
".");
720 if (dp != std::string::npos)
722 const size_t tp = dp + 1;
723 if (tp <
key.length())
725 const char *tail =
key.c_str() + tp;
728 key_priority = parse_number_throw<int>(tail,
"option priority");
731 catch (
const number_parse_exception &)
741 const int cmp = a->key.compare(b->key);
747 return a->key_priority < b->key_priority;
755 static std::string
unescape(
const std::string &
value,
bool &newline_present)
761 for (
size_t i = 0; i <
value.length(); ++i)
763 const char c =
value[i];
769 newline_present =
true;
797 for (
size_t i = 0; i <
key.length(); ++i)
799 const char c =
key[i];
800 if (c >=
'a' && c <=
'z')
802 else if (c >=
'A' && c <=
'Z')
805 return upper && !lower;
819 for (iterator i = begin(); i != end(); ++i)
836 template <
typename T,
typename... Args>
839 reserve(1 +
sizeof...(args));
840 from_list(std::move(first), std::forward<Args>(args)...);
870 ret->parse_from_config(str, lim);
885 std::vector<Option>::clear();
894 std::vector<std::string> list = Split::by_char<std::vector<std::string>,
Lex,
Limits>(str,
',', 0, ~0, lim);
895 for (std::vector<std::string>::const_iterator i = list.begin(); i != list.end(); ++i)
897 const Option opt = Split::by_space<Option, Lex, SpaceMatch, Limits>(*i, lim);
905 push_back(std::move(opt));
914 for (
auto &arg : argv)
921 push_back(std::move(opt));
930 push_back(std::move(opt));
941 const std::string &line = in.
line_ref();
944 Split::by_char_void<Option, NullLex, Limits>(opt, line,
'=', 0, 1, lim);
952 push_back(std::move(opt));
961 const std::string meta_prefix = meta_tag +
"_";
963 for (KeyValueList::const_iterator i = list.begin(); i != list.end(); ++i)
975 push_back(std::move(opt));
981 return Split::by_space<Option, LexComment, SpaceMatch, Limits>(line, lim);
992 bool in_multiline =
false;
997 if (in.line_overflow())
999 const std::string &line = in.line_ref();
1010 push_back(std::move(multiline));
1012 in_multiline =
false;
1016 std::string &mref = multiline.
ref(1);
1032 multiline = std::move(opt);
1033 in_multiline =
true;
1042 push_back(std::move(opt));
1056 bool in_multiline =
false;
1058 const std::string prefix = tag +
"_";
1062 if (in.line_overflow())
1064 std::string &line = in.line_ref();
1067 line = std::string(line, 2);
1078 push_back(std::move(multiline));
1080 in_multiline =
false;
1084 std::string &mref = multiline.
ref(1);
1091 Option opt = Split::by_char<Option, NullLex, Limits>(std::string(line, prefix.length()),
'=', 0, 1, lim);
1100 multiline = std::move(opt);
1101 in_multiline =
true;
1111 push_back(std::move(opt));
1125 reserve(size() + other.size());
1126 for (
const auto &opt : other)
1128 if (!filt || filt->filter(opt))
1141 reserve(size() + other.size());
1142 for (
auto &opt : other)
1144 if (!filt || filt->filter(opt))
1145 push_back(std::move(opt));
1154 IndexMap::const_iterator oi = other.
map().find(name);
1155 unsigned int count = 0;
1156 if (oi != other.
map().end())
1157 for (IndexList::const_iterator i = oi->second.begin(); i != oi->second.end(); ++i)
1159 const Option &opt = other[*i];
1173 for (std::vector<Option>::const_iterator i = other.begin(); i != other.end(); ++i)
1188 IndexMap::const_iterator e =
map_.find(name);
1189 if (e !=
map_.end())
1191 const size_t size = e->second.size();
1194 for (
const auto &optidx : e->second)
1196 (*this)[optidx].touch(
true);
1198 const Option *
ret = &((*this)[e->second[size - 1]]);
1210 IndexMap::const_iterator e =
map_.find(name);
1211 if (e !=
map_.end() && !e->second.empty())
1213 if (e->second.size() == 1)
1215 const Option *
ret = &((*this)[e->second[0]]);
1220 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"more than one instance of option '" << name <<
'\'');
1230 IndexMap::const_iterator e =
map_.find(name);
1231 if (e !=
map_.end() && !e->second.empty())
1233 const Option *first = &((*this)[e->second[0]]);
1235 if (e->second.size() >= 2)
1237 for (
size_t i = 1; i < e->second.size(); ++i)
1239 const Option *other = &(*this)[e->second[i]];
1241 if (*other != *first)
1242 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"more than one instance of option '" << name <<
"' with inconsistent argument(s)");
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())
1271 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG,
"option '" << name <<
"' not found");
1278 IndexMap::const_iterator e =
map_.find(name);
1279 if (e !=
map_.end() && !e->second.empty())
1286 std::string
cat(
const std::string &name)
const
1293 OptionList::IndexList::const_iterator i;
1294 for (i = il->begin(); i != il->end(); ++i)
1296 const Option &o = (*this)[*i];
1298 size += o.
ref(1).length() + 1;
1300 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"option '" << name <<
"' (" << o.
size() <<
") must have exactly one parameter");
1303 for (i = il->begin(); i != il->end(); ++i)
1305 const Option &o = (*this)[*i];
1327 return get_ptr(name) !=
nullptr;
1333 const std::string &
get(
const std::string &name,
size_t index,
const size_t max_len)
const
1336 return o.
get(index, max_len);
1342 std::string
get_optional(
const std::string &name,
size_t index,
const size_t max_len)
const
1346 return o->
get(index, max_len);
1368 catch (
const std::exception &)
1375 const char *
get_c_str(
const std::string &name,
size_t index,
const size_t max_len)
const
1379 return o->
get(index, max_len).c_str();
1389 const size_t max_len,
1390 const std::string &default_value)
const
1394 return o->
get(index, max_len);
1396 return default_value;
1402 const size_t max_len,
1403 const std::string &default_value)
const
1408 const std::string *s = o->
get_ptr(index, max_len);
1412 return default_value;
1415 template <
typename T>
1416 T
get_num(
const std::string &name,
const size_t idx,
const T default_value)
const
1418 typedef typename std::remove_const<T>::type T_nonconst;
1419 T_nonconst n = default_value;
1422 n = o->
get_num<T>(idx, default_value);
1426 template <
typename T>
1429 const T default_value,
1431 const T max_value)
const
1433 typedef typename std::remove_const<T>::type T_nonconst;
1434 T_nonconst n = default_value;
1437 n = o->
get_num<T>(idx, default_value, min_value, max_value);
1441 template <
typename T>
1442 T
get_num(
const std::string &name,
const size_t idx,
const T min_value,
const T max_value)
const
1445 return o.
get_num<T>(idx, min_value, max_value);
1448 template <
typename T>
1449 T
get_num(
const std::string &name,
const size_t idx)
const
1456 void touch(
const std::string &name)
const
1467 std::ostringstream
out;
1468 for (
size_t i = 0; i < size(); ++i)
1470 const Option &o = (*this)[i];
1485 for (
auto &e : *
this)
1489 ret += e.escape(
true);
1499 std::ostringstream
out;
1500 for (IndexMap::const_iterator i =
map_.begin(); i !=
map_.end(); ++i)
1502 out << i->first <<
" [";
1503 for (IndexList::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
1505 out <<
" ]" << std::endl;
1515 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1518 if (!opt.
touched() && !(opt.
meta() && ignore_meta))
1529 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1544 title =
"NOTE: Unused Options";
1555 const size_t i = size();
1557 map_[opt.
ref(0)].push_back((
unsigned int)i);
1572 for (
size_t i = 0; i < size(); ++i)
1574 const Option &opt = (*this)[i];
1583 for (std::string::const_iterator i = line.begin(); i != line.end(); ++i)
1597 const size_t n = str.length();
1598 return n >= 3 && str[0] ==
'<' && str[1] !=
'/' && str[n - 1] ==
'>';
1604 const size_t n = str.length();
1605 return n >= 4 && str[0] ==
'<' && str[1] ==
'/' && str.substr(2, n - 3) == tag && str[n - 1] ==
'>';
1611 const size_t n = str.length();
1613 str = str.substr(1, n - 2);
1622 if (c ==
'\n' || c ==
'\r')
1629 if (line.length() >= 2
1645 throw option_error(ERR_INVALID_CONFIG,
"multiline breakout detected");
1658 static bool is_close_meta_tag(
const std::string &str,
const std::string &prefix,
const std::string &tag)
1660 return prefix + tag +
"_STOP" == str;
1666 const size_t n = str.length();
1668 str = std::string(str, 0, n - 6);
1683 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL,
"line " << line_num <<
" is too long");
1688 push_back(std::move(opt));
1691 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
std::unordered_map< std::string, IndexList > IndexMap
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)
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)
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
std::vector< unsigned int > IndexList
unsigned int extend(const OptionList &other, const std::string &name)
std::pair< std::string, IndexList > IndexPair
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)
bool starts_with(const STRING &str, const std::string &prefix)
void add_trailing(std::string &str, const char c)
bool is_space(const char c)
bool ends_with(const STRING &str, const std::string &suffix)
Support deferred server-side state creation when client connects.
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