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-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 * 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 int msglev, const struct argv *a)
243{
244 struct gc_arena gc = gc_new();
245 msg(msglev, "%s", argv_str(a, &gc, 0));
246 gc_free(&gc);
247}
248
258void
259argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix)
260{
261 struct gc_arena gc = gc_new();
262 msg(msglev, "%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 (int 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
346static bool
347argv_printf_arglist(struct argv *argres, const char *format, va_list arglist)
348{
349 const char delim = 0x1D; /* ASCII Group Separator (GS) */
350 bool res = false;
351
352 /*
353 * Prepare a format string which will be used by vsnprintf() later on.
354 *
355 * This means all space separators in the input format string will be
356 * replaced by the GS (0x1D), so we can split this up again after the
357 * the vsnprintf() call into individual arguments again which will be
358 * saved in the struct argv.
359 *
360 */
361 size_t argc = argres->argc;
362 char *f = argv_prep_format(format, delim, &argc, &argres->gc);
363 if (f == NULL)
364 {
365 goto out;
366 }
367
368 /*
369 * Determine minimum buffer size.
370 *
371 * With C99, vsnprintf(NULL, 0, ...) will return the number of bytes
372 * it would have written, had the buffer been large enough.
373 */
374 va_list tmplist;
375 va_copy(tmplist, arglist);
376 int len = vsnprintf(NULL, 0, f, tmplist);
377 va_end(tmplist);
378 if (len < 0)
379 {
380 goto out;
381 }
382
383 /*
384 * Do the actual vsnprintf() operation, which expands the format
385 * string with the provided arguments.
386 */
387 size_t size = len + 1;
388 char *buf = gc_malloc(size, false, &argres->gc);
389 len = vsnprintf(buf, size, f, arglist);
390 if (len < 0 || len >= size)
391 {
392 goto out;
393 }
394
395 /*
396 * Split the string at the GS (0x1D) delimiters and put each elemen
397 * into the struct argv being returned to the caller.
398 */
399 char *end = strchr(buf, delim);
400 while (end)
401 {
402 *end = '\0';
403 argv_append(argres, buf);
404 buf = end + 1;
405 end = strchr(buf, delim);
406 }
407 argv_append(argres, buf);
408
409 if (argres->argc != argc)
410 {
411 /* Someone snuck in a GS (0x1D), fail gracefully */
412 argv_reset(argres);
413 goto out;
414 }
415 res = true;
416
417out:
418 return res;
419}
420
437bool
438argv_printf(struct argv *argres, const char *format, ...)
439{
440 va_list arglist;
441 va_start(arglist, format);
442
443 argv_reset(argres);
444 bool res = argv_printf_arglist(argres, format, arglist);
445 va_end(arglist);
446 return res;
447}
448
461bool
462argv_printf_cat(struct argv *argres, const char *format, ...)
463{
464 va_list arglist;
465 va_start(arglist, format);
466 bool res = argv_printf_arglist(argres, format, arglist);
467 va_end(arglist);
468 return res;
469}
470
480void
481argv_parse_cmd(struct argv *argres, const char *cmdstr)
482{
483 argv_reset(argres);
484
485 char *parms[MAX_PARMS + 1] = { 0 };
486 int nparms =
487 parse_line(cmdstr, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &argres->gc);
488 if (nparms)
489 {
490 int i;
491 for (i = 0; i < nparms; ++i)
492 {
493 argv_append(argres, parms[i]);
494 }
495 }
496 else
497 {
498 argv_append(argres, string_alloc(cmdstr, &argres->gc));
499 }
500}
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
void argv_msg(const int msglev, const struct argv *a)
Write the arguments stored in a struct argv via the msg() command.
Definition argv.c:242
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
void argv_msg_prefix(const int msglev, 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_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:481
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:347
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
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:438
bool argv_printf_cat(struct argv *argres, const char *format,...)
printf() inspired argv concatenation.
Definition argv.c:462
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)
Definition buffer.c:717
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition buffer.c:336
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc)
Definition buffer.h:1064
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
#define D_ARGV_PARSE_CMD
Definition errlevel.h:146
static size_t adjust_power_of_2(size_t u)
Definition integer.h:184
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
int parse_line(const char *line, char *p[], const int n, const char *file, const int line_num, int msglevel, struct gc_arena *gc)
Definition options.c:4968
#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:116
char ** res
struct gc_arena gc
Definition test_ssl.c:154