OpenVPN
test_options_parse.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2025 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdarg.h>
32#include <string.h>
33#include <setjmp.h>
34#include <cmocka.h>
35
36#include "options.h"
37#include "test_common.h"
38#include "mock_msg.h"
39
40void
41add_option(struct options *options, char *p[], bool is_inline, const char *file,
42 int line, const int level, const msglvl_t msglevel,
43 const unsigned int permission_mask, unsigned int *option_types_found,
44 struct env_set *es)
45{
46 function_called();
47 check_expected(p);
48 check_expected(is_inline);
49}
50
51void
52remove_option(struct context *c, struct options *options, char *p[], bool is_inline,
53 const char *file, int line, const msglvl_t msglevel,
54 const unsigned int permission_mask, unsigned int *option_types_found,
55 struct env_set *es)
56{
57}
58
59void
60update_option(struct context *c, struct options *options, char *p[], bool is_inline,
61 const char *file, int line, const int level, const msglvl_t msglevel,
62 const unsigned int permission_mask, unsigned int *option_types_found,
63 struct env_set *es, unsigned int *update_options_found)
64{
65}
66
67void
68usage(void)
69{
70}
71
72/* for building long texts */
73#define A_TIMES_256 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO"
74
75static void
76test_parse_line(void **state)
77{
78 char *p[MAX_PARMS + 1] = { 0 };
79 struct gc_arena gc = gc_new();
80 int res = 0;
81
82#define PARSE_LINE_TST(string) \
83 do \
84 { \
85 CLEAR(p); \
86 res = parse_line(string, p, SIZE(p) - 1, "test_options_parse", 1, M_INFO, &gc); \
87 } while (0);
88
89 /* basic example */
90 PARSE_LINE_TST("some-opt firstparm second-parm");
91 assert_int_equal(res, 3);
92 assert_string_equal(p[0], "some-opt");
93 assert_string_equal(p[1], "firstparm");
94 assert_string_equal(p[2], "second-parm");
95 assert_null(p[res]);
96
97 /* basic quoting, -- is not handled special */
98 PARSE_LINE_TST("--some-opt 'first parm' \"second' 'parm\"");
99 assert_int_equal(res, 3);
100 assert_string_equal(p[0], "--some-opt");
101 assert_string_equal(p[1], "first parm");
102 assert_string_equal(p[2], "second' 'parm");
103 assert_null(p[res]);
104
105 /* escaped quotes */
106 PARSE_LINE_TST("\"some opt\" 'first\" \"parm' \"second\\\" \\\"parm\"");
107 assert_int_equal(res, 3);
108 assert_string_equal(p[0], "some opt");
109 assert_string_equal(p[1], "first\" \"parm");
110 assert_string_equal(p[2], "second\" \"parm");
111 assert_null(p[res]);
112
113 /* missing closing quote */
114 PARSE_LINE_TST("--some-opt 'first parm \"second parm\"");
115 assert_int_equal(res, 0);
116
117 /* escaped backslash */
118 PARSE_LINE_TST("some\\\\opt C:\\\\directory\\\\file");
119 assert_int_equal(res, 2);
120 assert_string_equal(p[0], "some\\opt");
121 assert_string_equal(p[1], "C:\\directory\\file");
122 assert_null(p[res]);
123
124 /* comment chars are not special inside parameter */
125 PARSE_LINE_TST("some-opt firstparm; second#parm");
126 assert_int_equal(res, 3);
127 assert_string_equal(p[0], "some-opt");
128 assert_string_equal(p[1], "firstparm;");
129 assert_string_equal(p[2], "second#parm");
130 assert_null(p[res]);
131
132 /* comment */
133 PARSE_LINE_TST("some-opt firstparm # secondparm");
134 assert_int_equal(res, 2);
135 assert_string_equal(p[0], "some-opt");
136 assert_string_equal(p[1], "firstparm");
137 assert_null(p[res]);
138
139 /* parameter just long enough */
141 assert_int_equal(res, 2);
142 assert_string_equal(p[0], "opt");
143 assert_string_equal(p[1], A_TIMES_256);
144 assert_null(p[res]);
145
146 /* quoting doesn't count for parameter length */
147 PARSE_LINE_TST("opt \"" A_TIMES_256 "\"");
148 assert_int_equal(res, 2);
149 assert_string_equal(p[0], "opt");
150 assert_string_equal(p[1], A_TIMES_256);
151 assert_null(p[res]);
152
153 /* very long line */
155 assert_int_equal(res, 5);
156 assert_string_equal(p[0], "opt");
157 assert_string_equal(p[1], A_TIMES_256);
158 assert_string_equal(p[2], A_TIMES_256);
159 assert_string_equal(p[3], A_TIMES_256);
160 assert_string_equal(p[4], A_TIMES_256);
161 assert_null(p[res]);
162
163 /* parameter too long */
164 PARSE_LINE_TST("opt " A_TIMES_256 "B");
165 assert_int_equal(res, 0);
166
167 /* max parameters */
168 PARSE_LINE_TST("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15");
169 assert_int_equal(res, MAX_PARMS);
170 char num[3];
171 for (int i = 0; i < MAX_PARMS; i++)
172 {
173 assert_true(snprintf(num, 3, "%d", i) < 3);
174 assert_string_equal(p[i], num);
175 }
176 assert_null(p[res]);
177
178 /* too many parameters, overflow is ignored */
179 PARSE_LINE_TST("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16");
180 assert_int_equal(res, MAX_PARMS);
181 for (int i = 0; i < MAX_PARMS; i++)
182 {
183 assert_true(snprintf(num, 3, "%d", i) < 3);
184 assert_string_equal(p[i], num);
185 }
186 assert_null(p[res]);
187
188 gc_free(&gc);
189}
190
191static void
192read_single_config(struct options *options, const char *config)
193{
194 unsigned int option_types_found = 0;
195 struct env_set es;
196 CLEAR(es);
197 read_config_string("test_options_parse", options, config, M_INFO, OPT_P_DEFAULT,
198 &option_types_found, &es);
199}
200
201/* compat with various versions of cmocka.h
202 * Older versions have LargestIntegralType. Newer
203 * versions use uintmax_t. But LargestIntegralType
204 * is not guaranteed to be equal to uintmax_t, so
205 * we can't use that unconditionally. So we only use
206 * it if cmocka.h does not define LargestIntegralType.
207 */
208#ifndef LargestIntegralType
209#define LargestIntegralType uintmax_t
210#endif
211
217
218static int
220{
221 union tokens_parameter temp;
222 temp.as_int = value;
223 const char **p = (const char **)temp.as_pointer;
224 temp.as_int = expected;
225 const char **expected_p = (const char **)temp.as_pointer;
226 for (int i = 0; i < MAX_PARMS; i++)
227 {
228 if (!p[i] && !expected_p[i])
229 {
230 return true;
231 }
232 if ((p[i] && !expected_p[i])
233 || (!p[i] && expected_p[i]))
234 {
235 fprintf(stderr, "diff at i=%d\n", i);
236 return false;
237 }
238 if (strcmp(p[i], expected_p[i]))
239 {
240 fprintf(stderr, "diff at i=%d, p=<%s> ep=<%s>\n", i, p[i], expected_p[i]);
241 return false;
242 }
243 }
244 fprintf(stderr, "fallthrough");
245 return false;
246}
247
248static void
249test_read_config(void **state)
250{
251 struct options o;
252 CLEAR(o); /* NB: avoiding init_options to limit dependencies */
253 gc_init(&o.gc);
255 o.gc_owned = true;
256
257 char *p_expect_someopt[MAX_PARMS];
258 char *p_expect_otheropt[MAX_PARMS];
259 char *p_expect_inlineopt[MAX_PARMS];
260 CLEAR(p_expect_someopt);
261 CLEAR(p_expect_otheropt);
262 CLEAR(p_expect_inlineopt);
263 p_expect_someopt[0] = "someopt";
264 p_expect_someopt[1] = "parm1";
265 p_expect_someopt[2] = "parm2";
266 p_expect_otheropt[0] = "otheropt";
267 p_expect_otheropt[1] = "1";
268 p_expect_otheropt[2] = "2";
269 p_expect_inlineopt[0] = "inlineopt";
270 p_expect_inlineopt[1] = "some text\nother text\n";
271
272 /* basic test */
273 expect_function_call(add_option);
274 expect_check(add_option, p, check_tokens, p_expect_someopt);
275 expect_value(add_option, is_inline, 0);
276 expect_function_call(add_option);
277 expect_check(add_option, p, check_tokens, p_expect_otheropt);
278 expect_value(add_option, is_inline, 0);
279 read_single_config(&o, "someopt parm1 parm2\n otheropt 1 2");
280
281 /* -- gets stripped */
282 expect_function_call(add_option);
283 expect_check(add_option, p, check_tokens, p_expect_someopt);
284 expect_value(add_option, is_inline, 0);
285 expect_function_call(add_option);
286 expect_check(add_option, p, check_tokens, p_expect_otheropt);
287 expect_value(add_option, is_inline, 0);
288 read_single_config(&o, "someopt parm1 parm2\n\t--otheropt 1 2");
289
290 /* inline options */
291 expect_function_call(add_option);
292 expect_check(add_option, p, check_tokens, p_expect_inlineopt);
293 expect_value(add_option, is_inline, 1);
294 read_single_config(&o, "<inlineopt>\nsome text\nother text\n</inlineopt>");
295
296 p_expect_inlineopt[0] = "inlineopt";
297 p_expect_inlineopt[1] = A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 "\n";
298 expect_function_call(add_option);
299 expect_check(add_option, p, check_tokens, p_expect_inlineopt);
300 expect_value(add_option, is_inline, 1);
301 read_single_config(&o, "<inlineopt>\n" A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 "\n</inlineopt>");
302
303 gc_free(&o.gc);
305}
306
307int
308main(void)
309{
310 const struct CMUnitTest tests[] = {
311 cmocka_unit_test(test_parse_line),
312 cmocka_unit_test(test_read_config),
313 };
314
315 return cmocka_run_group_tests_name("options_parse", tests, NULL, NULL);
316}
static void gc_init(struct gc_arena *a)
Definition buffer.h:1004
static void gc_free(struct gc_arena *a)
Definition buffer.h:1025
static struct gc_arena gc_new(void)
Definition buffer.h:1017
#define M_INFO
Definition errlevel.h:54
#define CLEAR(x)
Definition basic.h:32
unsigned int msglvl_t
Definition error.h:77
#define OPT_P_DEFAULT
Definition options.h:768
#define MAX_PARMS
Definition options.h:51
void read_config_string(const char *prefix, struct options *options, const char *config, const msglvl_t msglevel, const unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es)
Contains all state information for one tunnel.
Definition openvpn.h:474
struct gc_arena gc
Definition dns.h:118
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
struct dns_options dns_options
Definition options.h:317
struct gc_arena gc
Definition options.h:256
bool gc_owned
Definition options.h:257
void remove_option(struct context *c, struct options *options, char *p[], bool is_inline, const char *file, int line, const msglvl_t msglevel, const unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es)
Resets options found in the PUSH_UPDATE message that are preceded by the - flag.
#define A_TIMES_256
#define LargestIntegralType
static void test_read_config(void **state)
int main(void)
void add_option(struct options *options, char *p[], bool is_inline, const char *file, int line, const int level, const msglvl_t msglevel, const unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es)
#define PARSE_LINE_TST(string)
static int check_tokens(const LargestIntegralType value, const LargestIntegralType expected)
static void read_single_config(struct options *options, const char *config)
void update_option(struct context *c, struct options *options, char *p[], bool is_inline, const char *file, int line, const int level, const msglvl_t msglevel, const unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es, unsigned int *update_options_found)
Processes an option to update.
static void test_parse_line(void **state)
void usage(void)
struct env_set * es
char ** res
struct gc_arena gc
Definition test_ssl.c:131
LargestIntegralType as_int