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