OpenVPN
argv.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/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) 2002-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 * A printf-like function (that only recognizes a subset of standard printf
24 * format operators) that prints arguments to an argv list instead
25 * of a standard string. This is used to build up argv arrays for passing
26 * to execve.
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "syshead.h"
34
35#include "argv.h"
36#include "integer.h"
37#include "env_set.h"
38#include "options.h"
39
47static void
48argv_extend(struct argv *a, const size_t newcap)
49{
50 if (newcap > a->capacity)
51 {
52 char **newargv;
53 size_t i;
54 ALLOC_ARRAY_CLEAR_GC(newargv, char *, newcap, &a->gc);
55 for (i = 0; i < a->argc; ++i)
56 {
57 newargv[i] = a->argv[i];
58 }
59 a->argv = newargv;
60 a->capacity = newcap;
61 }
62}
63
70static void
71argv_init(struct argv *a)
72{
73 a->capacity = 0;
74 a->argc = 0;
75 a->argv = NULL;
76 a->gc = gc_new();
77 argv_extend(a, 8);
78}
79
86struct argv
88{
89 struct argv ret;
90 argv_init(&ret);
91 return ret;
92}
93
100void
101argv_free(struct argv *a)
102{
103 gc_free(&a->gc);
104}
105
112static void
113argv_reset(struct argv *a)
114{
115 if (a->argc)
116 {
117 size_t i;
118 for (i = 0; i < a->argc; ++i)
119 {
120 a->argv[i] = NULL;
121 }
122 a->argc = 0;
123 }
124}
125
139static void
140argv_grow(struct argv *a, const size_t add)
141{
142 const size_t newargc = a->argc + add + 1;
143 ASSERT(newargc > a->argc);
144 argv_extend(a, adjust_power_of_2(newargc));
145}
146
156static void
157argv_append(struct argv *a, char *str)
158{
159 argv_grow(a, 1);
160 a->argv[a->argc++] = str;
161}
162
178static struct argv
179argv_clone(const struct argv *source, const size_t headroom)
180{
181 struct argv r;
182 argv_init(&r);
183
184 for (size_t i = 0; i < headroom; ++i)
185 {
186 argv_append(&r, NULL);
187 }
188 if (source)
189 {
190 for (size_t i = 0; i < source->argc; ++i)
191 {
192 argv_append(&r, string_alloc(source->argv[i], &r.gc));
193 }
194 }
195 return r;
196}
197
206struct argv
207argv_insert_head(const struct argv *a, const char *head)
208{
209 struct argv r;
210 r = argv_clone(a, 1);
211 r.argv[0] = string_alloc(head, &r.gc);
212 return r;
213}
214
229const char *
230argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags)
231{
232 return print_argv((const char **)a->argv, gc, flags);
233}
234
241void
242argv_msg(const msglvl_t msglevel, const struct argv *a)
243{
244 struct gc_arena gc = gc_new();
245 msg(msglevel, "%s", argv_str(a, &gc, 0));
246 gc_free(&gc);
247}
248
258void
259argv_msg_prefix(const msglvl_t msglevel, const struct argv *a, const char *prefix)
260{
261 struct gc_arena gc = gc_new();
262 msg(msglevel, "%s: %s", prefix, argv_str(a, &gc, 0));
263 gc_free(&gc);
264}
265
286static char *
287argv_prep_format(const char *format, const char delim, size_t *count, struct gc_arena *gc)
288{
289 if (format == NULL)
290 {
291 return NULL;
292 }
293
294 bool in_token = false;
295 char *f = gc_malloc(strlen(format) + 1, true, gc);
296 for (size_t i = 0, j = 0; i < strlen(format); i++)
297 {
298 if (format[i] == ' ')
299 {
300 in_token = false;
301 continue;
302 }
303
304 if (!in_token)
305 {
306 (*count)++;
307
308 /*
309 * We don't add any delimiter to the output string if
310 * the string is empty; the resulting format string
311 * will never start with a delimiter.
312 */
313 if (j > 0) /* Has anything been written to the output string? */
314 {
315 f[j++] = delim;
316 }
317 }
318
319 f[j++] = format[i];
320 in_token = true;
321 }
322
323 return f;
324}
325
326#if defined(__GNUC__) || defined(__clang__)
327#pragma GCC diagnostic push
328#pragma GCC diagnostic ignored "-Wsign-conversion"
329#endif
330
351static bool
352argv_printf_arglist(struct argv *argres, const char *format, va_list arglist)
353{
354 const char delim = 0x1D; /* ASCII Group Separator (GS) */
355 bool res = false;
356
357 /*
358 * Prepare a format string which will be used by vsnprintf() later on.
359 *
360 * This means all space separators in the input format string will be
361 * replaced by the GS (0x1D), so we can split this up again after the
362 * the vsnprintf() call into individual arguments again which will be
363 * saved in the struct argv.
364 *
365 */
366 size_t argc = argres->argc;
367 const char *f = argv_prep_format(format, delim, &argc, &argres->gc);
368 if (f == NULL)
369 {
370 goto out;
371 }
372
373 /*
374 * Determine minimum buffer size.
375 *
376 * With C99, vsnprintf(NULL, 0, ...) will return the number of bytes
377 * it would have written, had the buffer been large enough.
378 */
379 va_list tmplist;
380 va_copy(tmplist, arglist);
381 int len = vsnprintf(NULL, 0, f, tmplist);
382 va_end(tmplist);
383 if (len < 0)
384 {
385 goto out;
386 }
387
388 /*
389 * Do the actual vsnprintf() operation, which expands the format
390 * string with the provided arguments.
391 */
392 int size = len + 1;
393 char *buf = gc_malloc(size, false, &argres->gc);
394 len = vsnprintf(buf, size, f, arglist);
395 if (len < 0 || len >= size)
396 {
397 goto out;
398 }
399
400 /*
401 * Split the string at the GS (0x1D) delimiters and put each elemen
402 * into the struct argv being returned to the caller.
403 */
404 char *end = strchr(buf, delim);
405 while (end)
406 {
407 *end = '\0';
408 argv_append(argres, buf);
409 buf = end + 1;
410 end = strchr(buf, delim);
411 }
412 argv_append(argres, buf);
413
414 if (argres->argc != argc)
415 {
416 /* Someone snuck in a GS (0x1D), fail gracefully */
417 argv_reset(argres);
418 goto out;
419 }
420 res = true;
421
422out:
423 return res;
424}
425
426#if defined(__GNUC__) || defined(__clang__)
427#pragma GCC diagnostic pop
428#endif
429
446bool
447argv_printf(struct argv *argres, const char *format, ...)
448{
449 va_list arglist;
450 va_start(arglist, format);
451
452 argv_reset(argres);
453 bool res = argv_printf_arglist(argres, format, arglist);
454 va_end(arglist);
455 return res;
456}
457
470bool
471argv_printf_cat(struct argv *argres, const char *format, ...)
472{
473 va_list arglist;
474 va_start(arglist, format);
475 bool res = argv_printf_arglist(argres, format, arglist);
476 va_end(arglist);
477 return res;
478}
479
489void
490argv_parse_cmd(struct argv *argres, const char *cmdstr)
491{
492 argv_reset(argres);
493
494 char *parms[MAX_PARMS + 1] = { 0 };
495 int nparms =
496 parse_line(cmdstr, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &argres->gc);
497 if (nparms)
498 {
499 int i;
500 for (i = 0; i < nparms; ++i)
501 {
502 argv_append(argres, parms[i]);
503 }
504 }
505 else
506 {
507 argv_append(argres, string_alloc(cmdstr, &argres->gc));
508 }
509}
void argv_msg(const msglvl_t msglevel, const struct argv *a)
Write the arguments stored in a struct argv via the msg() command.
Definition argv.c:242
const char * argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags)
Generate a single string with all the arguments in a struct argv concatenated.
Definition argv.c:230
static struct argv argv_clone(const struct argv *source, const size_t headroom)
Clones a struct argv with all the contents to a new allocated struct argv.
Definition argv.c:179
static char * argv_prep_format(const char *format, const char delim, size_t *count, struct gc_arena *gc)
Prepares argv format string for further processing.
Definition argv.c:287
static void argv_extend(struct argv *a, const size_t newcap)
Resizes the list of arguments struct argv can carry.
Definition argv.c:48
static void argv_reset(struct argv *a)
Resets the struct argv to an initial state.
Definition argv.c:113
static void argv_init(struct argv *a)
Initialise an already allocated struct argv.
Definition argv.c:71
void argv_parse_cmd(struct argv *argres, const char *cmdstr)
Parses a command string, tokenizes it and puts each element into a separate struct argv argument slot...
Definition argv.c:490
void argv_free(struct argv *a)
Frees all memory allocations allocated by the struct argv related functions.
Definition argv.c:101
static bool argv_printf_arglist(struct argv *argres, const char *format, va_list arglist)
Create a struct argv based on a format string.
Definition argv.c:352
static void argv_grow(struct argv *a, const size_t add)
Extends an existing struct argv to carry minimum 'add' number of new arguments.
Definition argv.c:140
void argv_msg_prefix(const msglvl_t msglevel, const struct argv *a, const char *prefix)
Similar to argv_msg() but prefixes the messages being written with a given string.
Definition argv.c:259
static void argv_append(struct argv *a, char *str)
Appends a string to to the list of arguments stored in a struct argv This will ensure the list size i...
Definition argv.c:157
bool argv_printf(struct argv *argres, const char *format,...)
printf() variant which populates a struct argv.
Definition argv.c:447
bool argv_printf_cat(struct argv *argres, const char *format,...)
printf() inspired argv concatenation.
Definition argv.c:471
struct argv argv_new(void)
Allocates a new struct argv and ensures it is initialised.
Definition argv.c:87
struct argv argv_insert_head(const struct argv *a, const char *head)
Inserts an argument string in front of all other argument slots.
Definition argv.c:207
char * print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
Format a NULL-terminated argument vector as a single string.
Definition buffer.c:678
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Allocate memory and, optionally, zero it.
Definition buffer.c:322
char * string_alloc(const char *str, struct gc_arena *gc)
Duplicate a string, allocating memory under garbage collection.
Definition buffer.c:619
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc)
Allocate and zero-initialise a garbage-collected array of n elements.
Definition buffer.h:2091
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1974
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1958
#define D_ARGV_PARSE_CMD
Definition errlevel.h:143
static size_t adjust_power_of_2(size_t u)
Definition integer.h:205
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:221
int parse_line(const char *line, char *p[], const int n, const char *file, const int line_num, msglvl_t msglevel, struct gc_arena *gc)
#define MAX_PARMS
Definition options.h:51
Definition argv.h:35
char ** argv
Definition argv.h:39
size_t argc
Definition argv.h:38
size_t capacity
Definition argv.h:37
struct gc_arena gc
Definition argv.h:36
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct gc_arena gc
Definition test_ssl.c:138