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-2026 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_ptr(p);
48 check_expected_uint(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)
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#if HAVE_OLD_CMOCKA_API
207#endif
208
209static int
210check_tokens(const CMockaValueData value, const CMockaValueData expected)
211{
212#if HAVE_OLD_CMOCKA_API
213 union token_parameter temp;
214 temp.int_val = value;
215 const char **p = (const char **)temp.ptr;
216 temp.int_val = expected;
217 const char **expected_p = (const char **)temp.ptr;
218#else
219 const char **p = (const char **)value.ptr;
220 const char **expected_p = (const char **)expected.ptr;
221#endif
222 for (int i = 0; i < MAX_PARMS; i++)
223 {
224 if (!p[i] && !expected_p[i])
225 {
226 return true;
227 }
228 if ((p[i] && !expected_p[i])
229 || (!p[i] && expected_p[i]))
230 {
231 fprintf(stderr, "diff at i=%d\n", i);
232 return false;
233 }
234 if (strcmp(p[i], expected_p[i]))
235 {
236 fprintf(stderr, "diff at i=%d, p=<%s> ep=<%s>\n", i, p[i], expected_p[i]);
237 return false;
238 }
239 }
240 fprintf(stderr, "fallthrough");
241 return false;
242}
243
244static void
245test_read_config(void **state)
246{
247 struct options o;
248 CLEAR(o); /* NB: avoiding init_options to limit dependencies */
249 gc_init(&o.gc);
251 o.gc_owned = true;
252
253 char *p_expect_someopt[MAX_PARMS];
254 char *p_expect_otheropt[MAX_PARMS];
255 char *p_expect_inlineopt[MAX_PARMS];
256 CLEAR(p_expect_someopt);
257 CLEAR(p_expect_otheropt);
258 CLEAR(p_expect_inlineopt);
259 p_expect_someopt[0] = "someopt";
260 p_expect_someopt[1] = "parm1";
261 p_expect_someopt[2] = "parm2";
262 p_expect_otheropt[0] = "otheropt";
263 p_expect_otheropt[1] = "1";
264 p_expect_otheropt[2] = "2";
265 p_expect_inlineopt[0] = "inlineopt";
266 p_expect_inlineopt[1] = "some text\nother text\n";
267
268 /* basic test */
269 expect_function_call(add_option);
271 expect_uint_value(add_option, is_inline, 0);
272 expect_function_call(add_option);
274 expect_uint_value(add_option, is_inline, 0);
275 read_single_config(&o, "someopt parm1 parm2\n otheropt 1 2");
276
277 /* -- gets stripped */
278 expect_function_call(add_option);
280 expect_uint_value(add_option, is_inline, 0);
281 expect_function_call(add_option);
283 expect_uint_value(add_option, is_inline, 0);
284 read_single_config(&o, "someopt parm1 parm2\n\t--otheropt 1 2");
285
286 /* inline options */
287 expect_function_call(add_option);
289 expect_uint_value(add_option, is_inline, 1);
290 read_single_config(&o, "<inlineopt>\nsome text\nother text\n</inlineopt>");
291
292 p_expect_inlineopt[0] = "inlineopt";
293 p_expect_inlineopt[1] = A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 "\n";
294 expect_function_call(add_option);
296 expect_uint_value(add_option, is_inline, 1);
297 read_single_config(&o, "<inlineopt>\n" A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 A_TIMES_256 "\n</inlineopt>");
298
299 gc_free(&o.gc);
301}
302
303int
304main(void)
305{
306 const struct CMUnitTest tests[] = {
307 cmocka_unit_test(test_parse_line),
308 cmocka_unit_test(test_read_config),
309 };
310
311 return cmocka_run_group_tests_name("options_parse", tests, NULL, NULL);
312}
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:764
#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:471
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
#define expect_check_data
Definition test_common.h:44
#define cast_ptr_to_cmocka_value(x)
Definition test_common.h:45
#define LargestIntegralType
Definition test_common.h:38
#define CMockaValueData
Definition test_common.h:41
#define expect_uint_value
Definition test_common.h:43
#define check_expected_uint
Definition test_common.h:42
static int check_tokens(const CMockaValueData value, const CMockaValueData expected)
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
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)
Processes an option to update.
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 void read_single_config(struct options *options, const char *config)
static void test_parse_line(void **state)
void usage(void)
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:131
LargestIntegralType int_val