OpenVPN
test_misc.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) 2021-2025 Arne Schwabe <arne@rfc2549.org>
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 "ssl_util.h"
37#include "options_util.h"
38#include "test_common.h"
39#include "list.h"
40#include "mock_msg.h"
41
42static void
44{
45 struct gc_arena gc = gc_new();
46
47 const char *input =
48 "V4,dev-type tun,link-mtu 1457,tun-mtu 1400,proto UDPv4,auth SHA1,keysize 128,key-method 2,tls-server";
49
50 const char *output = options_string_compat_lzo(input, &gc);
51
52 assert_string_equal(
53 output,
54 "V4,dev-type tun,link-mtu 1458,tun-mtu 1400,proto UDPv4,auth SHA1,keysize 128,key-method 2,tls-server,comp-lzo");
55
56 /* This string is has a much too small link-mtu so we should fail on it" */
57 input =
58 "V4,dev-type tun,link-mtu 2,tun-mtu 1400,proto UDPv4,auth SHA1,keysize 128,key-method 2,tls-server";
59
60 output = options_string_compat_lzo(input, &gc);
61
62 assert_string_equal(input, output);
63
64 /* not matching at all */
65 input = "V4,dev-type tun";
66 output = options_string_compat_lzo(input, &gc);
67
68 assert_string_equal(input, output);
69
70
71 input =
72 "V4,dev-type tun,link-mtu 999,tun-mtu 1400,proto UDPv4,auth SHA1,keysize 128,key-method 2,tls-server";
73 output = options_string_compat_lzo(input, &gc);
74
75 /* 999 -> 1000, 3 to 4 chars */
76 assert_string_equal(
77 output,
78 "V4,dev-type tun,link-mtu 1000,tun-mtu 1400,proto UDPv4,auth SHA1,keysize 128,key-method 2,tls-server,comp-lzo");
79
80 gc_free(&gc);
81}
82
83static void
85{
86 struct options o;
87
88 const char *teststr = "TEMP:There are no flags here [really not]";
89
90 const char *msg = parse_auth_failed_temp(&o, teststr + strlen("TEMP"));
91 assert_string_equal(msg, "There are no flags here [really not]");
92}
93
94static void
96{
97 struct options o;
98
99 const char *teststr = "[backoff 42,advance no]";
100
101 const char *msg = parse_auth_failed_temp(&o, teststr);
102 assert_string_equal(msg, "");
103 assert_int_equal(o.server_backoff_time, 42);
104 assert_int_equal(o.no_advance, true);
105}
106
107static void
109{
110 struct options o;
111
112 const char *teststr = "[advance remote,backoff 77]:go round and round";
113
114 const char *msg = parse_auth_failed_temp(&o, teststr);
115 assert_string_equal(msg, "go round and round");
116 assert_int_equal(o.server_backoff_time, 77);
117}
118
119
120struct word
121{
122 const char *word;
123 int n;
124};
125
126
127static uint32_t
128word_hash_function(const void *key, uint32_t iv)
129{
130 const char *str = (const char *)key;
131 const int len = strlen(str);
132 return hash_func((const uint8_t *)str, len, iv);
133}
134
135static bool
136word_compare_function(const void *key1, const void *key2)
137{
138 return strcmp((const char *)key1, (const char *)key2) == 0;
139}
140
141static unsigned long
143{
144 /* rand() is not very random, but it's C99 and this is just for testing */
145 return rand();
146}
147
148static struct hash_element *
150{
151 struct hash_iterator hi;
152 struct hash_element *he;
153 struct hash_element *ret = NULL;
155
156 while ((he = hash_iterator_next(&hi)))
157 {
158 if (he->value == value)
159 {
160 ret = he;
161 }
162 }
164 return ret;
165}
166
167static void
168test_list(void **state)
169{
170 /*
171 * Test the hash code by implementing a simple
172 * word frequency algorithm.
173 */
174
175 struct gc_arena gc = gc_new();
178
179 printf("hash_init n_buckets=%d mask=0x%08x\n", hash->n_buckets, hash->mask);
180
181 char wordfile[PATH_MAX] = { 0 };
182 openvpn_test_get_srcdir_dir(wordfile, PATH_MAX, "/../../../COPYRIGHT.GPL");
183
184 FILE *words = fopen(wordfile, "r");
185 assert_non_null(words);
186
187 int wordcount = 0;
188
189 /* parse words from file */
190 while (true)
191 {
192 char buf[256];
193 char wordbuf[256];
194
195 if (!fgets(buf, sizeof(buf), words))
196 {
197 break;
198 }
199
200 char c = 0;
201 int bi = 0, wbi = 0;
202
203 do
204 {
205 c = buf[bi++];
206 if (isalnum(c) || c == '_')
207 {
208 assert_true(wbi < (int)sizeof(wordbuf));
209 wordbuf[wbi++] = c;
210 }
211 else
212 {
213 if (wbi)
214 {
215 wordcount++;
216
217 ASSERT(wbi < (int)sizeof(wordbuf));
218 wordbuf[wbi++] = '\0';
219
220 /* word is parsed from stdin */
221
222 /* does it already exist in table? */
223 struct word *w = (struct word *)hash_lookup(hash, wordbuf);
224
225 if (w)
226 {
227 assert_string_equal(w->word, wordbuf);
228 /* yes, increment count */
229 ++w->n;
230 }
231 else
232 {
233 /* no, make a new object */
234 ALLOC_OBJ_GC(w, struct word, &gc);
235 w->word = string_alloc(wordbuf, &gc);
236 w->n = 1;
237 assert_true(hash_add(hash, w->word, w, false));
238 assert_true(hash_add(nhash, w->word,
239 (void *)((ptr_type)(random() & 0x0F) + 1), false));
240 }
241 }
242 wbi = 0;
243 }
244 } while (c);
245 }
246
247 assert_int_equal(wordcount, 2971);
248
249 /* remove some words from the table */
250 {
251 assert_true(hash_remove(hash, "DEFECTIVE"));
252 assert_false(hash_remove(hash, "false"));
253 }
254
255 /* output contents of hash table */
256 {
257 ptr_type inc = 0;
258 int count = 0;
259
260 for (ptr_type base = 0; base < hash_n_buckets(hash); base += inc)
261 {
262 struct hash_iterator hi;
263 struct hash_element *he;
264 inc = (get_random() % 3) + 1;
265 hash_iterator_init_range(hash, &hi, base, base + inc);
266
267 while ((he = hash_iterator_next(&hi)))
268 {
269 struct word *w = (struct word *)he->value;
270 /*printf("%6d '%s'\n", w->n, w->word); */
271 ++count;
272 /* check a few words to match prior results */
273 if (!strcmp(w->word, "is"))
274 {
275 assert_int_equal(w->n, 49);
276 }
277 else if (!strcmp(w->word, "redistribute"))
278 {
279 assert_int_equal(w->n, 5);
280 }
281 else if (!strcmp(w->word, "circumstances"))
282 {
283 assert_int_equal(w->n, 1);
284 }
285 else if (!strcmp(w->word, "so"))
286 {
287 assert_int_equal(w->n, 8);
288 }
289 else if (!strcmp(w->word, "BECAUSE"))
290 {
291 assert_int_equal(w->n, 1);
292 }
293 }
294
296 }
297 assert_int_equal(count, hash_n_elements(hash));
298 }
299
300 /* test hash_remove_by_value function */
301 {
302 for (ptr_type i = 1; i <= 16; ++i)
303 {
304 struct hash_element *item = hash_lookup_by_value(nhash, (void *)i);
305 hash_remove_by_value(nhash, (void *)i);
306 /* check item got removed if it was present before */
307 if (item)
308 {
309 assert_null(hash_lookup_by_value(nhash, (void *)i));
310 }
311 }
312 }
313
315 hash_free(nhash);
316 gc_free(&gc);
317}
318
319static void
321{
322 assert_true(valid_integer("1234", true));
323 assert_true(valid_integer("1234", false));
324 assert_true(valid_integer("0", false));
325 assert_true(valid_integer("0", true));
326 assert_true(valid_integer("-777", false));
327 assert_false(valid_integer("-777", true));
328
329 assert_false(valid_integer("-777foo", false));
330 assert_false(valid_integer("-777foo", true));
331
332 assert_false(valid_integer("foo777", true));
333 assert_false(valid_integer("foo777", false));
334
335 /* 2**31 + 5 , just outside of signed int range */
336 assert_false(valid_integer("2147483653", true));
337 assert_false(valid_integer("2147483653", false));
338 assert_false(valid_integer("-2147483653", true));
339 assert_false(valid_integer("-2147483653", false));
340
341
342 int msglevel = D_LOW;
343 int saved_log_level = mock_get_debug_level();
345
346 /* check happy path */
347 assert_int_equal(positive_atoi("1234", msglevel), 1234);
348 assert_int_equal(positive_atoi("0", msglevel), 0);
349
350 assert_int_equal(atoi_warn("1234", msglevel), 1234);
351 assert_int_equal(atoi_warn("0", msglevel), 0);
352 assert_int_equal(atoi_warn("-1194", msglevel), -1194);
353
354 int parameter = 0;
355 assert_true(atoi_constrained("1234", &parameter, "test", 0, INT_MAX, msglevel));
356 assert_int_equal(parameter, 1234);
357 assert_true(atoi_constrained("0", &parameter, "test", -1, 0, msglevel));
358 assert_int_equal(parameter, 0);
359 assert_true(atoi_constrained("-1194", &parameter, "test", INT_MIN, INT_MAX, msglevel));
360 assert_int_equal(parameter, -1194);
361
363 assert_int_equal(positive_atoi("-1234", msglevel), 0);
364 assert_string_equal(mock_msg_buf, "Cannot parse argument '-1234' as non-negative integer");
365
366 /* 2**31 + 5 , just outside of signed int range */
368 assert_int_equal(positive_atoi("2147483653", msglevel), 0);
369 assert_string_equal(mock_msg_buf, "Cannot parse argument '2147483653' as non-negative integer");
370
372 assert_int_equal(atoi_warn("2147483653", msglevel), 0);
373 assert_string_equal(mock_msg_buf, "Cannot parse argument '2147483653' as integer");
374
376 parameter = -42;
377 assert_false(atoi_constrained("2147483653", &parameter, "test", 0, INT_MAX, msglevel));
378 assert_string_equal(mock_msg_buf, "test: Cannot parse '2147483653' as integer");
379 assert_int_equal(parameter, -42);
380
382 assert_int_equal(positive_atoi("foo77", msglevel), 0);
383 assert_string_equal(mock_msg_buf, "Cannot parse argument 'foo77' as non-negative integer");
384
386 assert_int_equal(positive_atoi("77foo", msglevel), 0);
387 assert_string_equal(mock_msg_buf, "Cannot parse argument '77foo' as non-negative integer");
388
390 parameter = -42;
391 assert_false(atoi_constrained("foo77", &parameter, "test", 0, INT_MAX, msglevel));
392 assert_string_equal(mock_msg_buf, "test: Cannot parse 'foo77' as integer");
393 assert_int_equal(parameter, -42);
394
396 parameter = -42;
397 assert_false(atoi_constrained("77foo", &parameter, "test", 0, INT_MAX, msglevel));
398 assert_string_equal(mock_msg_buf, "test: Cannot parse '77foo' as integer");
399 assert_int_equal(parameter, -42);
400
402 assert_int_equal(atoi_warn("foo77", msglevel), 0);
403 assert_string_equal(mock_msg_buf, "Cannot parse argument 'foo77' as integer");
404
406 assert_int_equal(atoi_warn("77foo", msglevel), 0);
407 assert_string_equal(mock_msg_buf, "Cannot parse argument '77foo' as integer");
408
409 /* special tests for _constrained */
411 parameter = -42;
412 assert_false(atoi_constrained("77", &parameter, "test", 0, 76, msglevel));
413 assert_string_equal(mock_msg_buf, "test: Must be an integer between 0 and 76, not 77");
414 assert_int_equal(parameter, -42);
415
417 parameter = -42;
418 assert_false(atoi_constrained("-77", &parameter, "test", -76, 76, msglevel));
419 assert_string_equal(mock_msg_buf, "test: Must be an integer between -76 and 76, not -77");
420 assert_int_equal(parameter, -42);
421
423 parameter = -42;
424 assert_false(atoi_constrained("-77", &parameter, "test", 0, INT_MAX, msglevel));
425 assert_string_equal(mock_msg_buf, "test: Must be an integer >= 0, not -77");
426 assert_int_equal(parameter, -42);
427
429 parameter = -42;
430 assert_false(atoi_constrained("0", &parameter, "test", 1, INT_MAX, msglevel));
431 assert_string_equal(mock_msg_buf, "test: Must be an integer >= 1, not 0");
432 assert_int_equal(parameter, -42);
433
434 mock_set_debug_level(saved_log_level);
435}
436
437const struct CMUnitTest misc_tests[] = { cmocka_unit_test(test_compat_lzo_string),
438 cmocka_unit_test(test_auth_fail_temp_no_flags),
439 cmocka_unit_test(test_auth_fail_temp_flags),
440 cmocka_unit_test(test_auth_fail_temp_flags_msg),
441 cmocka_unit_test(test_list),
442 cmocka_unit_test(test_atoi_variants) };
443
444int
445main(void)
446{
448 return cmocka_run_group_tests(misc_tests, NULL, NULL);
449}
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
#define ALLOC_OBJ_GC(dptr, type, gc)
Definition buffer.h:1074
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
static const char *const key1
Definition cert_data.h:55
unsigned long ptr_type
Definition common.h:57
#define D_LOW
Definition errlevel.h:96
void hash_iterator_free(struct hash_iterator *hi)
Definition list.c:272
struct hash_element * hash_iterator_next(struct hash_iterator *hi)
Definition list.c:278
void hash_iterator_init(struct hash *hash, struct hash_iterator *hi)
Definition list.c:236
struct hash * hash_init(const int n_buckets, const uint32_t iv, uint32_t(*hash_function)(const void *key, uint32_t iv), bool(*compare_function)(const void *key1, const void *key2))
Definition list.c:37
void hash_iterator_init_range(struct hash *hash, struct hash_iterator *hi, int start_bucket, int end_bucket)
Definition list.c:215
uint32_t hash_func(const uint8_t *k, uint32_t length, uint32_t initval)
Definition list.c:415
void hash_free(struct hash *hash)
Definition list.c:61
bool hash_add(struct hash *hash, const void *key, void *value, bool replace)
Definition list.c:139
void hash_remove_by_value(struct hash *hash, void *value)
Definition list.c:167
static bool hash_remove(struct hash *hash, const void *key)
Definition list.h:164
static void * hash_lookup(struct hash *hash, const void *key)
Definition list.h:131
static int hash_n_elements(const struct hash *hash)
Definition list.h:113
static int hash_n_buckets(const struct hash *hash)
Definition list.h:119
int mock_get_debug_level(void)
Definition mock_msg.c:56
char mock_msg_buf[MOCK_MSG_BUF]
Definition mock_msg.c:46
void mock_set_debug_level(int level)
Mock debug level defaults to 0, which gives clean(-ish) test reports.
Definition mock_msg.c:50
#define CLEAR(x)
Definition basic.h:32
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
bool atoi_constrained(const char *str, int *value, const char *name, int min, int max, int msglevel)
Converts a str to an integer if the string can be represented as an integer number and is between min...
int positive_atoi(const char *str, int msglevel)
Converts a str to a positive number if the string represents a postive integer number.
int atoi_warn(const char *str, int msglevel)
Converts a str to an integer if the string can be represented as an integer number.
const char * parse_auth_failed_temp(struct options *o, const char *reason)
bool valid_integer(const char *str, bool positive)
Checks if the string is a valid integer by checking if it can be converted to an integer.
const char * options_string_compat_lzo(const char *options, struct gc_arena *gc)
Takes a locally produced OCC string for TLS server mode and modifies as if the option comp-lzo was en...
Definition ssl_util.c:76
SSL utility functions.
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
void * value
Definition list.h:44
Definition list.h:56
int n_buckets
Definition list.h:57
int mask
Definition list.h:59
Container for bidirectional cipher and HMAC key material.
Definition crypto.h:240
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
int server_backoff_time
Definition options.h:306
bool no_advance
Definition options.h:295
int n
Definition test_misc.c:123
const char * word
Definition test_misc.c:122
#define random
Definition syshead.h:43
static void openvpn_unit_test_setup(void)
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition test_common.h:35
void openvpn_test_get_srcdir_dir(char *buf, size_t bufsize, const char *filename)
Helper function to get a file path from the unit test directory to open it or pass its path to anothe...
Definition test_common.h:53
static void test_atoi_variants(void **state)
Definition test_misc.c:320
static void test_auth_fail_temp_flags(void **state)
Definition test_misc.c:95
static void test_compat_lzo_string(void **state)
Definition test_misc.c:43
int main(void)
Definition test_misc.c:445
static uint32_t word_hash_function(const void *key, uint32_t iv)
Definition test_misc.c:128
static void test_list(void **state)
Definition test_misc.c:168
static void test_auth_fail_temp_no_flags(void **state)
Definition test_misc.c:84
const struct CMUnitTest misc_tests[]
Definition test_misc.c:437
static unsigned long get_random(void)
Definition test_misc.c:142
static bool word_compare_function(const void *key1, const void *key2)
Definition test_misc.c:136
static struct hash_element * hash_lookup_by_value(struct hash *hash, void *value)
Definition test_misc.c:149
static void test_auth_fail_temp_flags_msg(void **state)
Definition test_misc.c:108
struct gc_arena gc
Definition test_ssl.c:154