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-2024 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, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 *
24 * A printf-like function (that only recognizes a subset of standard printf
25 * format operators) that prints arguments to an argv list instead
26 * of a standard string. This is used to build up argv arrays for passing
27 * to execve.
28 */
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include "syshead.h"
35
36#include "argv.h"
37#include "integer.h"
38#include "env_set.h"
39#include "options.h"
40
48static void
49argv_extend(struct argv *a, const size_t newcap)
50{
51 if (newcap > a->capacity)
52 {
53 char **newargv;
54 size_t i;
55 ALLOC_ARRAY_CLEAR_GC(newargv, char *, newcap, &a->gc);
56 for (i = 0; i < a->argc; ++i)
57 {
58 newargv[i] = a->argv[i];
59 }
60 a->argv = newargv;
61 a->capacity = newcap;
62 }
63}
64
71static void
72argv_init(struct argv *a)
73{
74 a->capacity = 0;
75 a->argc = 0;
76 a->argv = NULL;
77 a->gc = gc_new();
78 argv_extend(a, 8);
79}
80
87struct argv
89{
90 struct argv ret;
91 argv_init(&ret);
92 return ret;
93}
94
101void
102argv_free(struct argv *a)
103{
104 gc_free(&a->gc);
105}
106
113static void
114argv_reset(struct argv *a)
115{
116 if (a->argc)
117 {
118 size_t i;
119 for (i = 0; i < a->argc; ++i)
120 {
121 a->argv[i] = NULL;
122 }
123 a->argc = 0;
124 }
125}
126
140static void
141argv_grow(struct argv *a, const size_t add)
142{
143 const size_t newargc = a->argc + add + 1;
144 ASSERT(newargc > a->argc);
145 argv_extend(a, adjust_power_of_2(newargc));
146}
147
157static void
158argv_append(struct argv *a, char *str)
159{
160 argv_grow(a, 1);
161 a->argv[a->argc++] = str;
162}
163
179static struct argv
180argv_clone(const struct argv *source, const size_t headroom)
181{
182 struct argv r;
183 argv_init(&r);
184
185 for (size_t i = 0; i < headroom; ++i)
186 {
187 argv_append(&r, NULL);
188 }
189 if (source)
190 {
191 for (size_t i = 0; i < source->argc; ++i)
192 {
193 argv_append(&r, string_alloc(source->argv[i], &r.gc));
194 }
195 }
196 return r;
197}
198
207struct argv
208argv_insert_head(const struct argv *a, const char *head)
209{
210 struct argv r;
211 r = argv_clone(a, 1);
212 r.argv[0] = string_alloc(head, &r.gc);
213 return r;
214}
215
230const char *
231argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags)
232{
233 return print_argv((const char **)a->argv, gc, flags);
234}
235
242void
243argv_msg(const int msglev, const struct argv *a)
244{
245 struct gc_arena gc = gc_new();
246 msg(msglev, "%s", argv_str(a, &gc, 0));
247 gc_free(&gc);
248}
249
259void
260argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix)
261{
262 struct gc_arena gc = gc_new();
263 msg(msglev, "%s: %s", prefix, argv_str(a, &gc, 0));
264 gc_free(&gc);
265}
266
287static char *
288argv_prep_format(const char *format, const char delim, size_t *count,
289 struct gc_arena *gc)
290{
291 if (format == NULL)
292 {
293 return NULL;
294 }
295
296 bool in_token = false;
297 char *f = gc_malloc(strlen(format) + 1, true, gc);
298 for (int i = 0, j = 0; i < strlen(format); i++)
299 {
300 if (format[i] == ' ')
301 {
302 in_token = false;
303 continue;
304 }
305
306 if (!in_token)
307 {
308 (*count)++;
309
310 /*
311 * We don't add any delimiter to the output string if
312 * the string is empty; the resulting format string
313 * will never start with a delimiter.
314 */
315 if (j > 0) /* Has anything been written to the output string? */
316 {
317 f[j++] = delim;
318 }
319 }
320
321 f[j++] = format[i];
322 in_token = true;
323 }
324
325 return f;
326}
327
348static bool
349argv_printf_arglist(struct argv *argres, const char *format, va_list arglist)
350{
351 const char delim = 0x1D; /* ASCII Group Separator (GS) */
352 bool res = false;
353
354 /*
355 * Prepare a format string which will be used by vsnprintf() later on.
356 *
357 * This means all space separators in the input format string will be
358 * replaced by the GS (0x1D), so we can split this up again after the
359 * the vsnprintf() call into individual arguments again which will be
360 * saved in the struct argv.
361 *
362 */
363 size_t argc = argres->argc;
364 char *f = argv_prep_format(format, delim, &argc, &argres->gc);
365 if (f == NULL)
366 {
367 goto out;
368 }
369
370 /*
371 * Determine minimum buffer size.
372 *
373 * With C99, vsnprintf(NULL, 0, ...) will return the number of bytes
374 * it would have written, had the buffer been large enough.
375 */
376 va_list tmplist;
377 va_copy(tmplist, arglist);
378 int len = vsnprintf(NULL, 0, f, tmplist);
379 va_end(tmplist);
380 if (len < 0)
381 {
382 goto out;
383 }
384
385 /*
386 * Do the actual vsnprintf() operation, which expands the format
387 * string with the provided arguments.
388 */
389 size_t size = len + 1;
390 char *buf = gc_malloc(size, false, &argres->gc);
391 len = vsnprintf(buf, size, f, arglist);
392 if (len < 0 || len >= size)
393 {
394 goto out;
395 }
396
397 /*
398 * Split the string at the GS (0x1D) delimiters and put each elemen
399 * into the struct argv being returned to the caller.
400 */
401 char *end = strchr(buf, delim);
402 while (end)
403 {
404 *end = '\0';
405 argv_append(argres, buf);
406 buf = end + 1;
407 end = strchr(buf, delim);
408 }
409 argv_append(argres, buf);
410
411 if (argres->argc != argc)
412 {
413 /* Someone snuck in a GS (0x1D), fail gracefully */
414 argv_reset(argres);
415 goto out;
416 }
417 res = true;
418
419out:
420 return res;
421}
422
439bool
440argv_printf(struct argv *argres, const char *format, ...)
441{
442 va_list arglist;
443 va_start(arglist, format);
444
445 argv_reset(argres);
446 bool res = argv_printf_arglist(argres, format, arglist);
447 va_end(arglist);
448 return res;
449}
450
463bool
464argv_printf_cat(struct argv *argres, const char *format, ...)
465{
466 va_list arglist;
467 va_start(arglist, format);
468 bool res = argv_printf_arglist(argres, format, arglist);
469 va_end(arglist);
470 return res;
471}
472
482void
483argv_parse_cmd(struct argv *argres, const char *cmdstr)
484{
485 argv_reset(argres);
486
487 char *parms[MAX_PARMS + 1] = { 0 };
488 int nparms = parse_line(cmdstr, parms, MAX_PARMS, "SCRIPT-ARGV", 0,
489 D_ARGV_PARSE_CMD, &argres->gc);
490 if (nparms)
491 {
492 int i;
493 for (i = 0; i < nparms; ++i)
494 {
495 argv_append(argres, parms[i]);
496 }
497 }
498 else
499 {
500 argv_append(argres, string_alloc(cmdstr, &argres->gc));
501 }
502}
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:231
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:180
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:243
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:288
static void argv_extend(struct argv *a, const size_t newcap)
Resizes the list of arguments struct argv can carry.
Definition argv.c:49
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:260
static void argv_reset(struct argv *a)
Resets the struct argv to an initial state.
Definition argv.c:114
static void argv_init(struct argv *a)
Initialise an already allocated struct argv.
Definition argv.c:72
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:483
void argv_free(struct argv *a)
Frees all memory allocations allocated by the struct argv related functions.
Definition argv.c:102
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:349
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:141
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:158
bool argv_printf(struct argv *argres, const char *format,...)
printf() variant which populates a struct argv.
Definition argv.c:440
bool argv_printf_cat(struct argv *argres, const char *format,...)
printf() inspired argv concatenation.
Definition argv.c:464
struct argv argv_new(void)
Allocates a new struct argv and ensures it is initialised.
Definition argv.c:88
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:208
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:1082
static void gc_free(struct gc_arena *a)
Definition buffer.h:1033
static struct gc_arena gc_new(void)
Definition buffer.h:1025
#define D_ARGV_PARSE_CMD
Definition errlevel.h:147
static size_t adjust_power_of_2(size_t u)
Definition integer.h:181
#define msg(flags,...)
Definition error.h:144
#define ASSERT(x)
Definition error.h:195
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:5076
#define MAX_PARMS
Definition options.h:52
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:117
struct gc_arena gc
Definition test_ssl.c:155