OpenVPN 3 Core Library
Loading...
Searching...
No Matches
options.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// General-purpose options parser, used to parse the OpenVPN configuration
13// file as well as the server-pushed options list. Note that these classes
14// don't get into the interpretation or typing of options -- they only care
15// about parsing the options into lists of strings, and then presenting the
16// complete configuration file as a list of options.
17//
18// The parser understands the general grammar of OpenVPN configuration
19// files including:
20//
21// 1. option/argument parsing, quoting, escaping, and comments,
22// 2. inline directives such as
23// <ca>
24// ...
25// </ca>
26// 3. and meta-directives such as those used by OpenVPN Access Server such as:
27// # OVPN_ACCESS_SERVER_USERNAME=test
28//
29// The basic organization of the parser is as follows:
30//
31// Option -- a list of strings, where the first string is the
32// option/directive name, and subsequent strings are arguments.
33//
34// OptionList -- a list of Options that also contains a map for
35// optimal lookup of specific options
36
37#ifndef OPENVPN_COMMON_OPTIONS_H
38#define OPENVPN_COMMON_OPTIONS_H
39
40#include <string>
41#include <sstream>
42#include <vector>
43#include <algorithm> // for std::sort, std::min
44#include <utility> // for std::move
45#include <type_traits> // for std::is_nothrow_move_constructible
46#include <unordered_map>
47#include <cstdint> // for std::uint64_t
48
49#include <openvpn/common/rc.hpp>
58
59namespace openvpn {
60
61class Option
62{
63 public:
64 OPENVPN_UNTAGGED_EXCEPTION(RejectedException);
65 enum
66 {
67 MULTILINE = 0x8000000,
68 };
69
70 // Validate string by size and multiline status.
71 // OR max_len with MULTILINE to allow multiline string.
72 // Return values:
79
80 // Options for render methods
82 {
83 RENDER_TRUNC_64 = (1 << 0), // truncate option after 64 chars
84 RENDER_PASS_FMT = (1 << 1), // pass \r\n\t
85 RENDER_NUMBER = (1 << 2), // number lines
86 RENDER_BRACKET = (1 << 3), // quote options using []
87 RENDER_UNUSED = (1 << 4), // only show unused options
88 };
89
91 {
92 static_assert(std::is_nothrow_move_constructible<Option>::value, "class Option not noexcept move constructable");
93 }
94
95 template <typename T, typename... Args>
96 explicit Option(T first, Args... args)
97 {
98 reserve(1 + sizeof...(args));
99 from_list(std::move(first), std::forward<Args>(args)...);
100 }
101
102 static validate_status validate(const std::string &str, const size_t max_len)
103 {
104 const size_t pos = str.find_first_of("\r\n");
105 const size_t len = max_len & ((size_t)MULTILINE - 1); // NOTE -- use smallest flag value here
106 if (pos != std::string::npos && !(max_len & MULTILINE))
107 return STATUS_MULTILINE;
108 if (len > 0 && Unicode::utf8_length(str) > len)
109 return STATUS_LENGTH;
110 return STATUS_GOOD;
111 }
112
113 static const char *validate_status_description(const validate_status status)
114 {
115 switch (status)
116 {
117 case STATUS_GOOD:
118 return "good";
119 case STATUS_MULTILINE:
120 return "multiline";
121 case STATUS_LENGTH:
122 return "too long";
123 default:
124 return "unknown";
125 }
126 }
127
128 void min_args(const size_t n) const
129 {
130 const size_t s = data.size();
131 if (s < n)
132 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, err_ref() << " must have at least " << (n - 1) << " arguments");
133 }
134
135 void exact_args(const size_t n) const
136 {
137 const size_t s = data.size();
138 if (s != n)
139 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, err_ref() << " must have exactly " << n << " arguments");
140 }
141
142 void validate_arg(const size_t index, const size_t max_len) const
143 {
144 if (max_len > 0 && index < data.size())
145 {
146 const validate_status status = validate(data[index], max_len);
147 if (status != STATUS_GOOD)
148 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, err_ref() << " is " << validate_status_description(status));
149 }
150 }
151
152 bool is_multiline() const
153 {
154 if (data.size() == 2)
155 {
156 const std::string &str = data[1];
157 const size_t pos = str.find_first_of("\r\n");
158 return pos != std::string::npos;
159 }
160 return false;
161 }
162
163 static void validate_string(const std::string &name, const std::string &str, const size_t max_len)
164 {
165 const validate_status status = validate(str, max_len);
166 if (status != STATUS_GOOD)
167 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, name << " is " << validate_status_description(status));
168 }
169
170 std::string printable_directive() const
171 {
172 try
173 {
174 if (!data.empty())
175 return Unicode::utf8_printable(data[0], 32);
176 return "";
177 }
178 catch (const std::exception &)
179 {
180 return "[DIRECTIVE]";
181 }
182 }
183
184 bool parameter_exists(const std::string &parameter) const
185 {
186 return std::count(data.begin(), data.end(), parameter);
187 }
188
189 const std::string &get(const size_t index, const size_t max_len) const
190 {
191 min_args(index + 1);
192 validate_arg(index, max_len);
193 return data[index];
194 }
195
196 std::string get_optional(const size_t index, const size_t max_len) const
197 {
198 validate_arg(index, max_len);
199 if (index < data.size())
200 return data[index];
201 return "";
202 }
203
204 std::string get_default(const size_t index, const size_t max_len, const std::string &default_value) const
205 {
206 validate_arg(index, max_len);
207 if (index < data.size())
208 return data[index];
209 return default_value;
210 }
211
212 const std::string *get_ptr(const size_t index, const size_t max_len) const
213 {
214 validate_arg(index, max_len);
215 if (index < data.size())
216 return &data[index];
217 return nullptr;
218 }
219
220 template <typename T>
221 T get_num(const size_t idx) const
222 {
223 using T_nonconst = typename std::remove_const<T>::type;
224 T_nonconst n(0); // we shouldn't need to initialize here, but some compilers complain "may be used uninitialized in this function"
225 const std::string &numstr = get(idx, 64);
226 if (numstr.length() >= 2 && numstr[0] == '0' && numstr[1] == 'x')
227 {
228 if (!parse_hex_number(numstr.substr(2), n))
229 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, err_ref() << '[' << idx << "] expecting a hex number");
230 }
231 else if (!parse_number<T_nonconst>(numstr, n))
232 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, err_ref() << '[' << idx << "] must be a number");
233 return n;
234 }
235
236 template <typename T>
237 T get_num(const size_t idx, const T default_value) const
238 {
239 if (size() > idx)
240 return get_num<T>(idx);
241 return default_value;
242 }
243
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
246 {
247 const T ret = get_num<T>(idx, default_value);
248 if (ret != default_value && (ret < min_value || ret > max_value))
249 range_error(idx, min_value, max_value);
250 return ret;
251 }
252
253 template <typename T>
254 T get_num(const size_t idx, const T min_value, const T max_value) const
255 {
256 const T ret = get_num<T>(idx);
257 if (ret < min_value || ret > max_value)
258 range_error(idx, min_value, max_value);
259 return ret;
260 }
261
262 std::string render(const unsigned int flags) const
263 {
264 std::ostringstream out;
265 size_t max_len_flags = (flags & RENDER_TRUNC_64) ? 64 : 0;
266 if (flags & RENDER_PASS_FMT)
267 max_len_flags |= Unicode::UTF8_PASS_FMT;
268 bool first = true;
269 for (std::vector<std::string>::const_iterator i = data.begin(); i != data.end(); ++i)
270 {
271 if (!first)
272 out << ' ';
273 if (flags & RENDER_BRACKET)
274 out << '[';
275 out << Unicode::utf8_printable(*i, max_len_flags);
276 if (flags & RENDER_BRACKET)
277 out << ']';
278 first = false;
279 }
280 return out.str();
281 }
282
283 static void escape_string(std::ostream &out, const std::string &term, const bool must_quote)
284 {
285 if (must_quote)
286 out << '\"';
287 for (std::string::const_iterator j = term.begin(); j != term.end(); ++j)
288 {
289 const char c = *j;
290 if (c == '\"' || c == '\\')
291 out << '\\';
292 out << c;
293 }
294 if (must_quote)
295 out << '\"';
296 }
297
298 // Render the option args into a string format such that it could be parsed back to
299 // the equivalent option args.
300 std::string escape(const bool csv) const
301 {
302 std::ostringstream out;
303 bool more = false;
304 for (std::vector<std::string>::const_iterator i = data.begin(); i != data.end(); ++i)
305 {
306 const std::string &term = *i;
307 const bool must_quote = must_quote_string(term, csv);
308 if (more)
309 out << ' ';
310 escape_string(out, term, must_quote);
311 more = true;
312 }
313 return out.str();
314 }
315
316 void clear()
317 {
318 data.clear();
320 warn_only_if_unknown_ = false;
321 meta_ = false;
322 }
323
324 // delegate to data
325 size_t size() const
326 {
327 return data.size();
328 }
329 bool empty() const
330 {
331 return data.empty();
332 }
333 void push_back(const std::string &item)
334 {
335 data.push_back(item);
336 }
337 void push_back(std::string &&item)
338 {
339 data.push_back(std::move(item));
340 }
341 void reserve(const size_t n)
342 {
343 data.reserve(n);
344 }
345 void resize(const size_t n)
346 {
347 data.resize(n);
348 }
349
350 // raw references to data
351 const std::string &ref(const size_t i) const
352 {
353 return data[i];
354 }
355 std::string &ref(const size_t i)
356 {
357 return data[i];
358 }
359
360 // equality
361 bool operator==(const Option &other) const
362 {
363 return data == other.data;
364 }
365 bool operator!=(const Option &other) const
366 {
367 return data != other.data;
368 }
369
370 // remove first n elements
371 void remove_first(const size_t n_elements)
372 {
373 const size_t n = std::min(data.size(), n_elements);
374 if (n)
375 data.erase(data.begin(), data.begin() + n);
376 }
377
383 void touch(bool lightly = false) const
384 {
385 // Note that we violate constness here, which is done
386 // because the touched bit is considered to be option metadata.
387 if (lightly)
388 {
391 }
392 else
393 {
395 }
396 }
397
399 {
401 }
402
403 bool warnonlyunknown() const
404 {
406 }
407
408 // was this option processed?
409 bool touched() const
410 {
412 }
413
414 // was an option of the same name (or this option see \c touched)
415 // touched
420
421
422 // refer to the option when constructing an error message
423 std::string err_ref() const
424 {
425 std::string ret = "option";
426 if (!data.empty())
427 {
428 ret += " '";
429 ret += printable_directive();
430 ret += '\'';
431 }
432 return ret;
433 }
434
439 void set_meta(bool value = true)
440 {
441 meta_ = value;
442 }
443
449 bool meta() const
450 {
451 return meta_;
452 }
453
454 private:
455 void from_list(std::string arg)
456 {
457 push_back(std::move(arg));
458 }
459
460 void from_list(const char *arg)
461 {
462 push_back(std::string(arg));
463 }
464
465 void from_list(std::vector<std::string> arg)
466 {
467 data.insert(data.end(), arg.begin(), arg.end());
468 }
469
470 template <typename T, typename... Args>
471 void from_list(T first, Args... args)
472 {
473 from_list(std::move(first));
474 from_list(std::forward<Args>(args)...);
475 }
476
477 template <typename T>
478 void range_error(const size_t idx, const T min_value, const T max_value) const
479 {
480 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, err_ref() << '[' << idx << "] must be in the range [" << min_value << ',' << max_value << ']');
481 }
482
483 bool must_quote_string(const std::string &str, const bool csv) const
484 {
485 for (const auto c : str)
486 {
487 if (string::is_space(c))
488 return true;
489 if (csv && c == ',')
490 return true;
491 }
492 return false;
493 }
494
496 enum class touchedState
497 {
498 /* Option was never used */
505 TOUCHED
506 };
508
510 bool meta_ = false;
511 std::vector<std::string> data;
512};
513
514class OptionList : public std::vector<Option>, public RCCopyable<thread_unsafe_refcount>
515{
516 public:
518 using IndexList = std::vector<unsigned int>;
519 using IndexMap = std::unordered_map<std::string, IndexList>;
520 using IndexPair = std::pair<std::string, IndexList>;
521
522 static bool is_comment(const char c)
523 {
524 return c == '#' || c == ';';
525 }
526
527 // standard lex filter that doesn't understand end-of-line comments
529
530 // special lex filter that recognizes end-of-line comments
532 {
533 public:
534 void put(char c)
535 {
536 if (in_comment)
537 {
538 ch = -1;
539 }
540 else if (backslash)
541 {
542 ch = c;
543 backslash = false;
544 }
545 else if (c == '\\')
546 {
547 backslash = true;
548 ch = -1;
549 }
550 else if (handle_quote(c))
551 {
552 ch = -1;
553 }
554 else if (is_comment(c) && !in_quote())
555 {
556 in_comment = true;
557 ch = -1;
558 }
559 else
560 {
561 ch = c;
562 }
563 }
564
565 bool available() const
566 {
567 return ch != -1;
568 }
569 int get() const
570 {
571 return ch;
572 }
573 void reset()
574 {
575 ch = -1;
576 }
577
578 private:
579 bool in_comment = false;
580 bool backslash = false;
581 int ch = -1;
582 };
583
584 class Limits
585 {
586 public:
587 Limits(const std::string &error_message,
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)
593 : bytes(0),
594 max_bytes(max_bytes_arg),
595 extra_bytes_per_opt(extra_bytes_per_opt_arg),
596 extra_bytes_per_term(extra_bytes_per_term_arg),
597 max_line_len(max_line_len_arg),
598 max_directive_len(max_directive_len_arg),
599 err(error_message)
600 {
601 }
602
603 void add_bytes(const size_t n)
604 {
605 bytes += n;
607 }
608
609 void add_string(const std::string &str)
610 {
611 bytes += str.length();
613 }
614
615 void add_term()
616 {
619 }
620
621 void add_opt()
622 {
625 }
626
627 size_t get_max_line_len() const
628 {
629 return max_line_len;
630 }
631
632 std::uint64_t get_bytes() const
633 {
634 return bytes;
635 }
636
637 void validate_directive(const Option &opt)
638 {
640 }
641
642 private:
644 {
645 if (bytes >= max_bytes)
646 error();
647 }
648
649 void error()
650 {
651 throw option_error(ERR_INVALID_CONFIG, err);
652 }
653
654 std::uint64_t bytes;
655 const std::uint64_t max_bytes;
658 const size_t max_line_len;
659 const size_t max_directive_len;
660 const std::string err;
661 };
662
663 // Used by extend() to optionally control which options are copied.
664 struct FilterBase : public RC<thread_unsafe_refcount>
665 {
667 virtual bool filter(const Option &opt) = 0;
668 };
669
670 class KeyValue : public RC<thread_unsafe_refcount>
671 {
672 public:
674
676 : key_priority(0)
677 {
678 }
679 KeyValue(const std::string &key_arg, const std::string &value_arg, const int key_priority_arg = 0)
680 : key(key_arg), value(value_arg), key_priority(key_priority_arg)
681 {
682 }
683
684 size_t combined_length() const
685 {
686 return key.length() + value.length();
687 }
688
689 Option convert_to_option(Limits *lim, const std::string &meta_prefix) const
690 {
691 bool newline_present = false;
692 Option opt;
693 const std::string unesc_value = unescape(value, newline_present);
694
695 if (key.starts_with(meta_prefix))
696 {
697 opt.push_back(std::string(key, meta_prefix.length()));
698 opt.set_meta();
699 }
700 else
701 {
702 opt.push_back(key);
703 }
704
705 if (newline_present || singular_arg(key))
706 opt.push_back(unesc_value);
707 else if (unesc_value != "NOARGS")
708 Split::by_space_void<Option, Lex, SpaceMatch, Limits>(opt, unesc_value, lim);
709 return opt;
710 }
711
713 {
714 // look for usage such as: remote.7
715 const size_t dp = key.find_last_of(".");
716 if (dp != std::string::npos)
717 {
718 const size_t tp = dp + 1;
719 if (tp < key.length())
720 {
721 const char *tail = key.c_str() + tp;
722 try
723 {
724 key_priority = parse_number_throw<int>(tail, "option priority");
725 key = key.substr(0, dp);
726 }
727 catch (const number_parse_exception &)
728 {
729 ;
730 }
731 }
732 }
733 }
734
735 static bool compare(const Ptr &a, const Ptr &b)
736 {
737 const int cmp = a->key.compare(b->key);
738 if (cmp < 0)
739 return true;
740 if (cmp > 0)
741 return false;
742 return a->key_priority < b->key_priority;
743 }
744
745 std::string key;
746 std::string value;
748
749 private:
750 static std::string unescape(const std::string &value, bool &newline_present)
751 {
752 std::string ret;
753 ret.reserve(value.length());
754
755 bool bs = false;
756 for (size_t i = 0; i < value.length(); ++i)
757 {
758 const char c = value[i];
759 if (bs)
760 {
761 if (c == 'n')
762 {
763 ret += '\n';
764 newline_present = true;
765 }
766 else if (c == '\\')
767 ret += '\\';
768 else
769 {
770 ret += '\\';
771 ret += c;
772 }
773 bs = false;
774 }
775 else
776 {
777 if (c == '\\')
778 bs = true;
779 else
780 ret += c;
781 }
782 }
783 if (bs)
784 ret += '\\';
785 return ret;
786 }
787
788 static bool singular_arg(const std::string &key)
789 {
790 bool upper = false;
791 bool lower = false;
792 for (size_t i = 0; i < key.length(); ++i)
793 {
794 const char c = key[i];
795 if (c >= 'a' && c <= 'z')
796 lower = true;
797 else if (c >= 'A' && c <= 'Z')
798 upper = true;
799 }
800 return upper && !lower;
801 }
802 };
803
804 struct KeyValueList : public std::vector<KeyValue::Ptr>
805 {
807 {
809 sort();
810 }
811
813 {
814 for (iterator i = begin(); i != end(); ++i)
815 {
816 KeyValue &kv = **i;
817 kv.split_priority();
818 }
819 }
820
821 void sort()
822 {
823 std::sort(begin(), end(), KeyValue::compare);
824 }
825 };
826
827 OptionList() = default;
828
829 template <typename T, typename... Args>
830 explicit OptionList(T first, Args... args)
831 {
832 reserve(1 + sizeof...(args));
833 from_list(std::move(first), std::forward<Args>(args)...);
834 update_map();
835 }
836
837 static OptionList parse_from_csv_static(const std::string &str, Limits *lim)
838 {
839 OptionList ret;
840 ret.parse_from_csv(str, lim);
841 ret.update_map();
842 return ret;
843 }
844
845 static OptionList parse_from_csv_static_nomap(const std::string &str, Limits *lim)
846 {
847 OptionList ret;
848 ret.parse_from_csv(str, lim);
849 return ret;
850 }
851
852 static OptionList parse_from_config_static(const std::string &str, Limits *lim)
853 {
854 OptionList ret;
855 ret.parse_from_config(str, lim);
856 ret.update_map();
857 return ret;
858 }
859
860 static OptionList::Ptr parse_from_config_static_ptr(const std::string &str, Limits *lim)
861 {
862 OptionList::Ptr ret = new OptionList();
863 ret->parse_from_config(str, lim);
864 ret->update_map();
865 return ret;
866 }
867
868 static OptionList parse_from_argv_static(const std::vector<std::string> &argv)
869 {
870 OptionList ret;
871 ret.parse_from_argv(argv);
872 ret.update_map();
873 return ret;
874 }
875
876 void clear()
877 {
878 std::vector<Option>::clear();
879 map_.clear();
880 }
881
882 // caller should call update_map() after this function
883 void parse_from_csv(const std::string &str, Limits *lim)
884 {
885 if (lim)
886 lim->add_string(str);
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)
889 {
890 const Option opt = Split::by_space<Option, Lex, SpaceMatch, Limits>(*i, lim);
891 if (!opt.empty())
892 {
893 if (lim)
894 {
895 lim->add_opt();
896 lim->validate_directive(opt);
897 }
898 push_back(std::move(opt));
899 }
900 }
901 }
902
903 // caller should call update_map() after this function
904 void parse_from_argv(const std::vector<std::string> &argv)
905 {
906 Option opt;
907 for (auto &arg : argv)
908 {
909 std::string a = arg;
910 if (a.starts_with("--"))
911 {
912 if (!opt.empty())
913 {
914 push_back(std::move(opt));
915 opt.clear();
916 }
917 a = a.substr(2);
918 }
919 if (!a.empty())
920 opt.push_back(a);
921 }
922 if (!opt.empty())
923 push_back(std::move(opt));
924 }
925
926 // caller should call update_map() after this function
927 void parse_from_peer_info(const std::string &str, Limits *lim)
928 {
929 if (lim)
930 lim->add_string(str);
931 SplitLines in(str, 0);
932 while (in(true))
933 {
934 const std::string &line = in.line_ref();
935 Option opt;
936 opt.reserve(2);
937 Split::by_char_void<Option, NullLex, Limits>(opt, line, '=', 0, 1, lim);
938 if (!opt.empty())
939 {
940 if (lim)
941 {
942 lim->add_opt();
943 lim->validate_directive(opt);
944 }
945 push_back(std::move(opt));
946 }
947 }
948 }
949
950 // caller may want to call list.preprocess() before this function
951 // caller should call update_map() after this function
952 void parse_from_key_value_list(const KeyValueList &list, const std::string &meta_tag, Limits *lim)
953 {
954 const std::string meta_prefix = meta_tag + "_";
955
956 for (KeyValueList::const_iterator i = list.begin(); i != list.end(); ++i)
957 {
958 const KeyValue &kv = **i;
959 if (lim)
960 lim->add_bytes(kv.combined_length());
961
962 Option opt = kv.convert_to_option(lim, meta_prefix);
963 if (lim)
964 {
965 lim->add_opt();
966 lim->validate_directive(opt);
967 }
968 push_back(std::move(opt));
969 }
970 }
971
972 static Option parse_option_from_line(const std::string &line, Limits *lim)
973 {
974 return Split::by_space<Option, LexComment, SpaceMatch, Limits>(line, lim);
975 }
976
977 // caller should call update_map() after this function
978 void parse_from_config(const std::string &str, Limits *lim)
979 {
980 if (lim)
981 lim->add_string(str);
982
983 SplitLines in(str, lim ? lim->get_max_line_len() : 0);
984 int line_num = 0;
985 bool in_multiline = false;
986 Option multiline;
987 while (in(true))
988 {
989 ++line_num;
990 if (in.line_overflow())
991 line_too_long(line_num);
992 const std::string &line = in.line_ref();
993 if (in_multiline)
994 {
995 if (is_close_tag(line, multiline.ref(0)))
996 {
997 if (lim)
998 {
999 lim->add_opt();
1000 lim->validate_directive(multiline);
1001 }
1002 multiline.set_meta(true);
1003 push_back(std::move(multiline));
1004 multiline.clear();
1005 in_multiline = false;
1006 }
1007 else
1008 {
1009 std::string &mref = multiline.ref(1);
1010 mref += line;
1011 mref += '\n';
1012 }
1013 }
1014 else if (!ignore_line(line))
1015 {
1016 Option opt = parse_option_from_line(line, lim);
1017 if (!opt.empty())
1018 {
1019 if (is_open_tag(opt.ref(0)))
1020 {
1021 if (opt.size() > 1)
1022 extraneous_err(line_num, "option", opt);
1023 untag_open_tag(opt.ref(0));
1024 opt.push_back("");
1025 multiline = std::move(opt);
1026 in_multiline = true;
1027 }
1028 else
1029 {
1030 if (lim)
1031 {
1032 lim->add_opt();
1033 lim->validate_directive(opt);
1034 }
1035 push_back(std::move(opt));
1036 }
1037 }
1038 }
1039 }
1040 if (in_multiline)
1041 not_closed_out_err("option", multiline);
1042 }
1043
1044 // caller should call update_map() after this function
1045 void parse_meta_from_config(const std::string &str, const std::string &tag, Limits *lim)
1046 {
1047 SplitLines in(str, lim ? lim->get_max_line_len() : 0);
1048 int line_num = 0;
1049 bool in_multiline = false;
1050 Option multiline;
1051 const std::string prefix = tag + "_";
1052 while (in(true))
1053 {
1054 ++line_num;
1055 if (in.line_overflow())
1056 line_too_long(line_num);
1057 std::string &line = in.line_ref();
1058 if (line.starts_with("# "))
1059 {
1060 line = std::string(line, 2);
1061 if (in_multiline)
1062 {
1063 if (is_close_meta_tag(line, prefix, multiline.ref(0)))
1064 {
1065 if (lim)
1066 {
1067 lim->add_opt();
1068 lim->validate_directive(multiline);
1069 }
1070 multiline.set_meta(true);
1071 push_back(std::move(multiline));
1072 multiline.clear();
1073 in_multiline = false;
1074 }
1075 else
1076 {
1077 std::string &mref = multiline.ref(1);
1078 mref += line;
1079 mref += '\n';
1080 }
1081 }
1082 else if (line.starts_with(prefix))
1083 {
1084 Option opt = Split::by_char<Option, NullLex, Limits>(std::string(line, prefix.length()), '=', 0, 1, lim);
1085 if (!opt.empty())
1086 {
1087 if (is_open_meta_tag(opt.ref(0)))
1088 {
1089 if (opt.size() > 1)
1090 extraneous_err(line_num, "meta option", opt);
1091 untag_open_meta_tag(opt.ref(0));
1092 opt.push_back("");
1093 multiline = std::move(opt);
1094 in_multiline = true;
1095 }
1096 else
1097 {
1098 if (lim)
1099 {
1100 lim->add_opt();
1101 lim->validate_directive(opt);
1102 }
1103 opt.set_meta(true);
1104 push_back(std::move(opt));
1105 }
1106 }
1107 }
1108 }
1109 }
1110 if (in_multiline)
1111 not_closed_out_err("meta option", multiline);
1112 }
1113
1114 // Append elements in other to self,
1115 // caller should call update_map() after this function.
1116 void extend(const OptionList &other, FilterBase *filt = nullptr)
1117 {
1118 reserve(size() + other.size());
1119 for (const auto &opt : other)
1120 {
1121 if (!filt || filt->filter(opt))
1122 {
1123 push_back(opt);
1124 opt.touch();
1125 }
1126 }
1127 }
1128
1129 // Append elements in other to self,
1130 // consumes other,
1131 // caller should call update_map() after this function.
1132 void extend(OptionList &&other, FilterBase *filt = nullptr)
1133 {
1134 reserve(size() + other.size());
1135 for (auto &opt : other)
1136 {
1137 if (!filt || filt->filter(opt))
1138 push_back(std::move(opt));
1139 }
1140 }
1141
1142 // Append elements in other having given name to self,
1143 // caller should call update_map() after this function.
1144 // Return the number of elements processed.
1145 unsigned int extend(const OptionList &other, const std::string &name)
1146 {
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)
1151 {
1152 const Option &opt = other[*i];
1153 push_back(opt);
1154 opt.touch();
1155 ++count;
1156 }
1157 return count;
1158 }
1159
1160 // Append to self only those elements in other that do not exist
1161 // in self, caller should call update_map() after this function.
1162 // Caller should also consider calling update_map() before this function,
1163 // to ensure that lookups on this->map will see up-to-date data.
1165 {
1166 for (std::vector<Option>::const_iterator i = other.begin(); i != other.end(); ++i)
1167 {
1168 const Option &opt = *i;
1169 if (!opt.empty() && !map().contains(opt.ref(0)))
1170 {
1171 push_back(opt);
1172 opt.touch();
1173 }
1174 }
1175 }
1176
1177 // Get the last instance of an option, or return nullptr if option
1178 // doesn't exist.
1179 const Option *get_ptr(const std::string &name) const
1180 {
1181 IndexMap::const_iterator e = map_.find(name);
1182 if (e != map_.end())
1183 {
1184 const size_t size = e->second.size();
1185 if (size)
1186 {
1187 for (const auto &optidx : e->second)
1188 {
1189 (*this)[optidx].touch(true);
1190 }
1191 const Option *ret = &((*this)[e->second[size - 1]]);
1192 ret->touch();
1193 return ret;
1194 }
1195 }
1196 return nullptr;
1197 }
1198
1199 // Get an option, return nullptr if option doesn't exist, or
1200 // throw an error if more than one instance exists.
1201 const Option *get_unique_ptr(const std::string &name) const
1202 {
1203 IndexMap::const_iterator e = map_.find(name);
1204 if (e != map_.end() && !e->second.empty())
1205 {
1206 if (e->second.size() == 1)
1207 {
1208 const Option *ret = &((*this)[e->second[0]]);
1209 ret->touch();
1210 return ret;
1211 }
1212 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG, "more than one instance of option '" << name << '\'');
1213 }
1214 else
1215 return nullptr;
1216 }
1217
1218 // Get an option, throw an error if more than one instance exists and the instances
1219 // are not exact duplicates of one other.
1220 const Option *get_consistent(const std::string &name) const
1221 {
1222 IndexMap::const_iterator e = map_.find(name);
1223 if (e != map_.end() && !e->second.empty())
1224 {
1225 const Option *first = &((*this)[e->second[0]]);
1226 first->touch();
1227 if (e->second.size() >= 2)
1228 {
1229 for (size_t i = 1; i < e->second.size(); ++i)
1230 {
1231 const Option *other = &(*this)[e->second[i]];
1232 other->touch();
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)");
1235 }
1236 }
1237 return first;
1238 }
1239 return nullptr;
1240 }
1241
1242 // Get option, throw error if not found
1243 // If multiple options of the same name exist, return
1244 // the last one.
1245 const Option &get(const std::string &name) const
1246 {
1247 const Option *o = get_ptr(name);
1248 if (o)
1249 return *o;
1250 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG, "option '" << name << "' not found");
1251 }
1252
1253 // Get the list of options having the same name (by index),
1254 // throw an exception if option is not found.
1255 const IndexList &get_index(const std::string &name) const
1256 {
1257 IndexMap::const_iterator e = map_.find(name);
1258 if (e != map_.end() && !e->second.empty())
1259 return e->second;
1260 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_CONFIG, "option '" << name << "' not found");
1261 }
1262
1263 // Get the list of options having the same name (by index),
1264 // return nullptr is option is not found.
1265 const IndexList *get_index_ptr(const std::string &name) const
1266 {
1267 IndexMap::const_iterator e = map_.find(name);
1268 if (e != map_.end() && !e->second.empty())
1269 return &e->second;
1270 return nullptr;
1271 }
1272
1273 // Concatenate all one-arg directives of a given name, in index order.
1274 std::string cat(const std::string &name) const
1275 {
1276 std::string ret;
1277 const OptionList::IndexList *il = get_index_ptr(name);
1278 if (il)
1279 {
1280 size_t size = 0;
1281 OptionList::IndexList::const_iterator i;
1282 for (i = il->begin(); i != il->end(); ++i)
1283 {
1284 const Option &o = (*this)[*i];
1285 if (o.size() == 2)
1286 size += o.ref(1).length() + 1;
1287 else
1288 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, "option '" << name << "' (" << o.size() << ") must have exactly one parameter");
1289 }
1290 ret.reserve(size);
1291 for (i = il->begin(); i != il->end(); ++i)
1292 {
1293 const Option &o = (*this)[*i];
1294 if (o.size() >= 2)
1295 {
1296 o.touch();
1297 ret += o.ref(1);
1298 string::add_trailing(ret, '\n');
1299 }
1300 }
1301 }
1302 return ret;
1303 }
1304
1305 // Return true if option exists, but raise an exception if multiple
1306 // instances of the option exist.
1307 bool exists_unique(const std::string &name) const
1308 {
1309 return get_unique_ptr(name) != nullptr;
1310 }
1311
1312 // Return true if one or more instances of a given option exist.
1313 bool exists(const std::string &name) const
1314 {
1315 return get_ptr(name) != nullptr;
1316 }
1317
1318 // Convenience method that gets a particular argument index within an option,
1319 // while raising an exception if option doesn't exist or if argument index
1320 // is out-of-bounds.
1321 const std::string &get(const std::string &name, size_t index, const size_t max_len) const
1322 {
1323 const Option &o = get(name);
1324 return o.get(index, max_len);
1325 }
1326
1327 // Convenience method that gets a particular argument index within an option,
1328 // while returning the empty string if option doesn't exist, and raising an
1329 // exception if argument index is out-of-bounds.
1330 std::string get_optional(const std::string &name, size_t index, const size_t max_len) const
1331 {
1332 const Option *o = get_ptr(name);
1333 if (o)
1334 return o->get(index, max_len);
1335 return "";
1336 }
1337
1338 // Like get_optional(), but return "" if argument index is out-of-bounds.
1339 std::string get_optional_relaxed(const std::string &name, size_t index, const size_t max_len) const
1340 {
1341 const Option *o = get_ptr(name);
1342 if (o)
1343 return o->get_optional(index, max_len);
1344 return "";
1345 }
1346
1347 // Like get_optional(), but return "" if exception is thrown.
1348 std::string get_optional_noexcept(const std::string &name, size_t index, const size_t max_len) const
1349 {
1350 try
1351 {
1352 return get_optional(name, index, max_len);
1353 }
1354 catch (const std::exception &)
1355 {
1356 return "";
1357 }
1358 }
1359
1360 // Return raw C string to option data or nullptr if option doesn't exist.
1361 const char *get_c_str(const std::string &name, size_t index, const size_t max_len) const
1362 {
1363 const Option *o = get_ptr(name);
1364 if (o)
1365 return o->get(index, max_len).c_str();
1366 return nullptr;
1367 }
1368
1369 // Convenience method that gets a particular argument index within an option,
1370 // while returning a default string if option doesn't exist, and raising an
1371 // exception if argument index is out-of-bounds.
1372 std::string get_default(const std::string &name,
1373 size_t index,
1374 const size_t max_len,
1375 const std::string &default_value) const
1376 {
1377 const Option *o = get_ptr(name);
1378 if (o)
1379 return o->get(index, max_len);
1380 return default_value;
1381 }
1382
1383 // Like get_default(), but return default_value if argument index is out-of-bounds.
1384 std::string get_default_relaxed(const std::string &name,
1385 size_t index,
1386 const size_t max_len,
1387 const std::string &default_value) const
1388 {
1389 const Option *o = get_ptr(name);
1390 if (o)
1391 {
1392 const std::string *s = o->get_ptr(index, max_len);
1393 if (s)
1394 return *s;
1395 }
1396 return default_value;
1397 }
1398
1399 template <typename T>
1400 T get_num(const std::string &name, const size_t idx, const T default_value) const
1401 {
1402 using T_nonconst = typename std::remove_const<T>::type;
1403 T_nonconst n = default_value;
1404 const Option *o = get_ptr(name);
1405 if (o)
1406 n = o->get_num<T>(idx, default_value);
1407 return n;
1408 }
1409
1410 template <typename T>
1411 T get_num(const std::string &name,
1412 const size_t idx,
1413 const T default_value,
1414 const T min_value,
1415 const T max_value) const
1416 {
1417 using T_nonconst = typename std::remove_const<T>::type;
1418 T_nonconst n = default_value;
1419 const Option *o = get_ptr(name);
1420 if (o)
1421 n = o->get_num<T>(idx, default_value, min_value, max_value);
1422 return n;
1423 }
1424
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
1427 {
1428 const Option &o = get(name);
1429 return o.get_num<T>(idx, min_value, max_value);
1430 }
1431
1432 template <typename T>
1433 T get_num(const std::string &name, const size_t idx) const
1434 {
1435 const Option &o = get(name);
1436 return o.get_num<T>(idx);
1437 }
1438
1439 // Touch an option, if it exists.
1440 void touch(const std::string &name) const
1441 {
1442 const Option *o = get_ptr(name);
1443 if (o)
1444 o->touch();
1445 }
1446
1447 // Render object as a string.
1448 // flags should be given as Option::render_flags.
1449 std::string render(const unsigned int flags) const
1450 {
1451 std::ostringstream out;
1452 for (size_t i = 0; i < size(); ++i)
1453 {
1454 const Option &o = (*this)[i];
1455 if (!(flags & Option::RENDER_UNUSED) || !o.touched())
1456 {
1457 if (flags & Option::RENDER_NUMBER)
1458 out << i << ' ';
1459 out << o.render(flags) << '\n';
1460 }
1461 }
1462 return out.str();
1463 }
1464
1465 std::string render_csv() const
1466 {
1467 std::string ret;
1468 bool first = true;
1469 for (auto &e : *this)
1470 {
1471 if (!first)
1472 ret += ',';
1473 ret += e.escape(true);
1474 first = false;
1475 }
1476 return ret;
1477 }
1478
1479 // Render contents of hash map used to locate options after underlying option list
1480 // has been modified.
1481 std::string render_map() const
1482 {
1483 std::ostringstream out;
1484 for (IndexMap::const_iterator i = map_.begin(); i != map_.end(); ++i)
1485 {
1486 out << i->first << " [";
1487 for (IndexList::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
1488 out << ' ' << *j;
1489 out << " ]\n";
1490 }
1491 return out.str();
1492 }
1493
1494 // Return number of unused options based on the notion that
1495 // all used options have been touched.
1496 size_t n_unused(bool ignore_meta = false) const
1497 {
1498 size_t n = 0;
1499 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1500 {
1501 const Option &opt = *i;
1502 if (!opt.touched() && !(opt.meta() && ignore_meta))
1503 ++n;
1504 }
1505 return n;
1506 }
1507
1508 // Return number of unused meta options based on the notion that
1509 // all used options have been touched.
1510 size_t meta_unused() const
1511 {
1512 size_t n = 0;
1513 for (std::vector<Option>::const_iterator i = begin(); i != end(); ++i)
1514 {
1515 const Option &opt = *i;
1516 if (opt.meta() && !opt.touched())
1517 ++n;
1518 }
1519 return n;
1520 }
1521
1522 void show_unused_options(const char *title = nullptr) const
1523 {
1524 // show unused options
1525 if (n_unused())
1526 {
1527 if (!title)
1528 title = "NOTE: Unused Options";
1529 OPENVPN_LOG_NTNL(title << '\n'
1531 }
1532 }
1533
1534 // Add item to underlying option list while updating map as well.
1535 void add_item(const Option &opt)
1536 {
1537 if (!opt.empty())
1538 {
1539 const size_t i = size();
1540 push_back(opt);
1541 map_[opt.ref(0)].push_back((unsigned int)i);
1542 }
1543 }
1544
1545 // Return hash map used to locate options.
1546 const IndexMap &map() const
1547 {
1548 return map_;
1549 }
1550
1551 // Rebuild hash map used to locate options after underlying option list
1552 // has been modified.
1554 {
1555 map_.clear();
1556 for (size_t i = 0; i < size(); ++i)
1557 {
1558 const Option &opt = (*this)[i];
1559 if (!opt.empty())
1560 map_[opt.ref(0)].push_back((unsigned int)i);
1561 }
1562 }
1563
1564 // return true if line is blank or a comment
1565 static bool ignore_line(const std::string &line)
1566 {
1567 for (std::string::const_iterator i = line.begin(); i != line.end(); ++i)
1568 {
1569 const char c = *i;
1570 if (!SpaceMatch::is_space(c))
1571 return is_comment(c);
1572 }
1573 return true;
1574 }
1575
1576 // multiline tagging
1577
1578 // return true if string is a tag, e.g. "<ca>"
1579 static bool is_open_tag(const std::string &str)
1580 {
1581 const size_t n = str.length();
1582 return n >= 3 && str[0] == '<' && str[1] != '/' && str[n - 1] == '>';
1583 }
1584
1585 // return true if string is a close tag, e.g. "</ca>"
1586 static bool is_close_tag(const std::string &str, const std::string &tag)
1587 {
1588 const size_t n = str.length();
1589 return n >= 4 && str[0] == '<' && str[1] == '/' && str.substr(2, n - 3) == tag && str[n - 1] == '>';
1590 }
1591
1592 // remove <> chars from open tag
1593 static void untag_open_tag(std::string &str)
1594 {
1595 const size_t n = str.length();
1596 if (n >= 3)
1597 str = str.substr(1, n - 2);
1598 }
1599
1600 // detect multiline breakout attempt (return true)
1601 static bool detect_multiline_breakout_nothrow(const std::string &opt, const std::string &tag)
1602 {
1603 std::string line;
1604 for (auto &c : opt)
1605 {
1606 if (c == '\n' || c == '\r')
1607 line.clear();
1608 else
1609 {
1610 line += c;
1611 if (tag.empty())
1612 {
1613 if (line.length() >= 2
1614 && line[0] == '<'
1615 && line[1] == '/')
1616 return true;
1617 }
1618 else if (is_close_tag(line, tag))
1619 return true;
1620 }
1621 }
1622 return false;
1623 }
1624
1625 // detect multiline breakout attempt
1626 static void detect_multiline_breakout(const std::string &opt, const std::string &tag)
1627 {
1629 throw option_error(ERR_INVALID_CONFIG, "multiline breakout detected");
1630 }
1631
1632 private:
1633 // multiline tagging (meta)
1634
1635 // return true if string is a meta tag, e.g. WEB_CA_BUNDLE_START
1636 static bool is_open_meta_tag(const std::string &str)
1637 {
1638 return str.ends_with("_START");
1639 }
1640
1641 // return true if string is a tag, e.g. WEB_CA_BUNDLE_STOP
1642 static bool is_close_meta_tag(const std::string &str, const std::string &prefix, const std::string &tag)
1643 {
1644 return prefix + tag + "_STOP" == str;
1645 }
1646
1647 // remove trailing "_START" from open tag
1648 static void untag_open_meta_tag(std::string &str)
1649 {
1650 const size_t n = str.length();
1651 if (n >= 6)
1652 str = std::string(str, 0, n - 6);
1653 }
1654
1655 static void extraneous_err(const int line_num, const char *type, const Option &opt)
1656 {
1657 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, "line " << line_num << ": " << type << " <" << opt.printable_directive() << "> is followed by extraneous text");
1658 }
1659
1660 static void not_closed_out_err(const char *type, const Option &opt)
1661 {
1662 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, type << " <" << opt.printable_directive() << "> was not properly closed out");
1663 }
1664
1665 static void line_too_long(const int line_num)
1666 {
1667 OPENVPN_THROW_ARG1(option_error, ERR_INVALID_OPTION_VAL, "line " << line_num << " is too long");
1668 }
1669
1671 {
1672 push_back(std::move(opt));
1673 }
1674
1675 template <typename T, typename... Args>
1676 void from_list(T first, Args... args)
1677 {
1678 from_list(std::move(first));
1679 from_list(std::forward<Args>(args)...);
1680 }
1681
1683};
1684
1685} // namespace openvpn
1686
1687#endif // OPENVPN_COMMON_OPTIONS_H
Helper class to handle quote processing.
Definition lex.hpp:39
bool in_quote() const
Check if currently inside a quote.
Definition lex.hpp:45
bool handle_quote(char c)
Handle a character as a potential quote.
Definition lex.hpp:60
Option convert_to_option(Limits *lim, const std::string &meta_prefix) const
Definition options.hpp:689
KeyValue(const std::string &key_arg, const std::string &value_arg, const int key_priority_arg=0)
Definition options.hpp:679
size_t combined_length() const
Definition options.hpp:684
static std::string unescape(const std::string &value, bool &newline_present)
Definition options.hpp:750
static bool compare(const Ptr &a, const Ptr &b)
Definition options.hpp:735
static bool singular_arg(const std::string &key)
Definition options.hpp:788
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)
Definition options.hpp:587
void validate_directive(const Option &opt)
Definition options.hpp:637
void add_string(const std::string &str)
Definition options.hpp:609
void add_bytes(const size_t n)
Definition options.hpp:603
size_t get_max_line_len() const
Definition options.hpp:627
const size_t extra_bytes_per_term
Definition options.hpp:657
const size_t extra_bytes_per_opt
Definition options.hpp:656
const std::uint64_t max_bytes
Definition options.hpp:655
std::uint64_t get_bytes() const
Definition options.hpp:632
const std::string err
Definition options.hpp:660
const size_t max_directive_len
Definition options.hpp:659
static OptionList parse_from_argv_static(const std::vector< std::string > &argv)
Definition options.hpp:868
static void detect_multiline_breakout(const std::string &opt, const std::string &tag)
Definition options.hpp:1626
std::string get_optional(const std::string &name, size_t index, const size_t max_len) const
Definition options.hpp:1330
void show_unused_options(const char *title=nullptr) const
Definition options.hpp:1522
static void untag_open_tag(std::string &str)
Definition options.hpp:1593
void parse_from_peer_info(const std::string &str, Limits *lim)
Definition options.hpp:927
static void not_closed_out_err(const char *type, const Option &opt)
Definition options.hpp:1660
void from_list(Option opt)
Definition options.hpp:1670
void extend_nonexistent(const OptionList &other)
Definition options.hpp:1164
OptionList(T first, Args... args)
Definition options.hpp:830
void parse_from_config(const std::string &str, Limits *lim)
Definition options.hpp:978
const IndexMap & map() const
Definition options.hpp:1546
std::string cat(const std::string &name) const
Definition options.hpp:1274
static bool is_close_tag(const std::string &str, const std::string &tag)
Definition options.hpp:1586
const Option * get_consistent(const std::string &name) const
Definition options.hpp:1220
void extend(const OptionList &other, FilterBase *filt=nullptr)
Definition options.hpp:1116
const char * get_c_str(const std::string &name, size_t index, const size_t max_len) const
Definition options.hpp:1361
std::string get_optional_noexcept(const std::string &name, size_t index, const size_t max_len) const
Definition options.hpp:1348
void parse_from_argv(const std::vector< std::string > &argv)
Definition options.hpp:904
const IndexList & get_index(const std::string &name) const
Definition options.hpp:1255
T get_num(const std::string &name, const size_t idx, const T default_value, const T min_value, const T max_value) const
Definition options.hpp:1411
void parse_from_key_value_list(const KeyValueList &list, const std::string &meta_tag, Limits *lim)
Definition options.hpp:952
T get_num(const std::string &name, const size_t idx, const T default_value) const
Definition options.hpp:1400
T get_num(const std::string &name, const size_t idx) const
Definition options.hpp:1433
T get_num(const std::string &name, const size_t idx, const T min_value, const T max_value) const
Definition options.hpp:1426
std::string render_map() const
Definition options.hpp:1481
const IndexList * get_index_ptr(const std::string &name) const
Definition options.hpp:1265
void touch(const std::string &name) const
Definition options.hpp:1440
bool exists_unique(const std::string &name) const
Definition options.hpp:1307
static OptionList parse_from_csv_static(const std::string &str, Limits *lim)
Definition options.hpp:837
static OptionList::Ptr parse_from_config_static_ptr(const std::string &str, Limits *lim)
Definition options.hpp:860
static void line_too_long(const int line_num)
Definition options.hpp:1665
static void extraneous_err(const int line_num, const char *type, const Option &opt)
Definition options.hpp:1655
std::vector< unsigned int > IndexList
Definition options.hpp:518
std::unordered_map< std::string, IndexList > IndexMap
Definition options.hpp:519
StandardLex Lex
Definition options.hpp:528
static bool ignore_line(const std::string &line)
Definition options.hpp:1565
std::string get_optional_relaxed(const std::string &name, size_t index, const size_t max_len) const
Definition options.hpp:1339
void parse_from_csv(const std::string &str, Limits *lim)
Definition options.hpp:883
std::pair< std::string, IndexList > IndexPair
Definition options.hpp:520
const std::string & get(const std::string &name, size_t index, const size_t max_len) const
Definition options.hpp:1321
void add_item(const Option &opt)
Definition options.hpp:1535
void from_list(T first, Args... args)
Definition options.hpp:1676
static Option parse_option_from_line(const std::string &line, Limits *lim)
Definition options.hpp:972
const Option * get_unique_ptr(const std::string &name) const
Definition options.hpp:1201
std::string get_default_relaxed(const std::string &name, size_t index, const size_t max_len, const std::string &default_value) const
Definition options.hpp:1384
static OptionList parse_from_csv_static_nomap(const std::string &str, Limits *lim)
Definition options.hpp:845
const Option & get(const std::string &name) const
Definition options.hpp:1245
static OptionList parse_from_config_static(const std::string &str, Limits *lim)
Definition options.hpp:852
void extend(OptionList &&other, FilterBase *filt=nullptr)
Definition options.hpp:1132
size_t n_unused(bool ignore_meta=false) const
Definition options.hpp:1496
const Option * get_ptr(const std::string &name) const
Definition options.hpp:1179
unsigned int extend(const OptionList &other, const std::string &name)
Definition options.hpp:1145
static bool is_close_meta_tag(const std::string &str, const std::string &prefix, const std::string &tag)
Definition options.hpp:1642
std::string render_csv() const
Definition options.hpp:1465
std::string render(const unsigned int flags) const
Definition options.hpp:1449
static bool detect_multiline_breakout_nothrow(const std::string &opt, const std::string &tag)
Definition options.hpp:1601
size_t meta_unused() const
Definition options.hpp:1510
static bool is_comment(const char c)
Definition options.hpp:522
std::string get_default(const std::string &name, size_t index, const size_t max_len, const std::string &default_value) const
Definition options.hpp:1372
static bool is_open_tag(const std::string &str)
Definition options.hpp:1579
static bool is_open_meta_tag(const std::string &str)
Definition options.hpp:1636
static void untag_open_meta_tag(std::string &str)
Definition options.hpp:1648
void parse_meta_from_config(const std::string &str, const std::string &tag, Limits *lim)
Definition options.hpp:1045
bool exists(const std::string &name) const
Definition options.hpp:1313
bool touched_lightly() const
Definition options.hpp:416
bool is_multiline() const
Definition options.hpp:152
Option(T first, Args... args)
Definition options.hpp:96
std::string get_default(const size_t index, const size_t max_len, const std::string &default_value) const
Definition options.hpp:204
void exact_args(const size_t n) const
Definition options.hpp:135
std::string get_optional(const size_t index, const size_t max_len) const
Definition options.hpp:196
void push_back(const std::string &item)
Definition options.hpp:333
void touch(bool lightly=false) const
Definition options.hpp:383
void from_list(std::vector< std::string > arg)
Definition options.hpp:465
void from_list(std::string arg)
Definition options.hpp:455
std::string err_ref() const
Definition options.hpp:423
const std::string & get(const size_t index, const size_t max_len) const
Definition options.hpp:189
volatile touchedState touched_
Definition options.hpp:507
static const char * validate_status_description(const validate_status status)
Definition options.hpp:113
T get_num(const size_t idx, const T default_value) const
Definition options.hpp:237
bool must_quote_string(const std::string &str, const bool csv) const
Definition options.hpp:483
static void escape_string(std::ostream &out, const std::string &term, const bool must_quote)
Definition options.hpp:283
static void validate_string(const std::string &name, const std::string &str, const size_t max_len)
Definition options.hpp:163
void push_back(std::string &&item)
Definition options.hpp:337
OPENVPN_UNTAGGED_EXCEPTION(RejectedException)
std::vector< std::string > data
Definition options.hpp:511
T get_num(const size_t idx) const
Definition options.hpp:221
void set_meta(bool value=true)
Definition options.hpp:439
size_t size() const
Definition options.hpp:325
void min_args(const size_t n) const
Definition options.hpp:128
void resize(const size_t n)
Definition options.hpp:345
std::string & ref(const size_t i)
Definition options.hpp:355
T get_num(const size_t idx, const T min_value, const T max_value) const
Definition options.hpp:254
void validate_arg(const size_t index, const size_t max_len) const
Definition options.hpp:142
void reserve(const size_t n)
Definition options.hpp:341
std::string printable_directive() const
Definition options.hpp:170
void enableWarnOnly()
Definition options.hpp:398
bool warn_only_if_unknown_
Definition options.hpp:509
T get_num(const size_t idx, const T default_value, const T min_value, const T max_value) const
Definition options.hpp:245
void remove_first(const size_t n_elements)
Definition options.hpp:371
bool operator!=(const Option &other) const
Definition options.hpp:365
bool empty() const
Definition options.hpp:329
void from_list(T first, Args... args)
Definition options.hpp:471
void from_list(const char *arg)
Definition options.hpp:460
bool parameter_exists(const std::string &parameter) const
Definition options.hpp:184
std::string escape(const bool csv) const
Definition options.hpp:300
void range_error(const size_t idx, const T min_value, const T max_value) const
Definition options.hpp:478
const std::string & ref(const size_t i) const
Definition options.hpp:351
const std::string * get_ptr(const size_t index, const size_t max_len) const
Definition options.hpp:212
bool operator==(const Option &other) const
Definition options.hpp:361
bool warnonlyunknown() const
Definition options.hpp:403
static validate_status validate(const std::string &str, const size_t max_len)
Definition options.hpp:102
std::string render(const unsigned int flags) const
Definition options.hpp:262
bool touched() const
Definition options.hpp:409
bool meta() const
Definition options.hpp:449
Reference count base class for objects tracked by RCPtr. Allows copying and assignment.
Definition rc.hpp:975
The smart pointer class.
Definition rc.hpp:119
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:908
std::string & line_ref()
#define OPENVPN_THROW_ARG1(exc, arg, stuff)
#define OPENVPN_LOG_NTNL(args)
STRING utf8_printable(const STRING &str, size_t max_len_flags)
Definition unicode.hpp:129
size_t utf8_length(const STRING &str)
Definition unicode.hpp:179
void add_trailing(std::string &str, const char c)
Definition string.hpp:134
bool is_space(const char c)
Definition string.hpp:239
bool parse_hex_number(const char *str, T &retval)
Definition hexstr.hpp:381
virtual bool filter(const Option &opt)=0
static bool is_space(char c)
Definition lex.hpp:25
static std::stringstream out
Definition test_path.cpp:10