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 
48 static void
49 argv_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 
71 static void
72 argv_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 
87 struct argv
88 argv_new(void)
89 {
90  struct argv ret;
91  argv_init(&ret);
92  return ret;
93 }
94 
101 void
102 argv_free(struct argv *a)
103 {
104  gc_free(&a->gc);
105 }
106 
113 static void
114 argv_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 
140 static void
141 argv_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 
157 static void
158 argv_append(struct argv *a, char *str)
159 {
160  argv_grow(a, 1);
161  a->argv[a->argc++] = str;
162 }
163 
179 static struct argv
180 argv_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 
207 struct argv
208 argv_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 
230 const char *
231 argv_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 
242 void
243 argv_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 
259 void
260 argv_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 
287 static char *
288 argv_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 
348 static bool
349 argv_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 
419 out:
420  return res;
421 }
422 
439 bool
440 argv_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 
463 bool
464 argv_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 
482 void
483 argv_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 }
argv_init
static void argv_init(struct argv *a)
Initialise an already allocated struct argv.
Definition: argv.c:72
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1030
argv_clone
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
argv
Definition: argv.h:35
argv_printf_cat
bool argv_printf_cat(struct argv *argres, const char *format,...)
printf() inspired argv concatenation.
Definition: argv.c:464
argv_free
void argv_free(struct argv *a)
Frees all memory allocations allocated by the struct argv related functions.
Definition: argv.c:102
parse_line
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:4966
MAX_PARMS
#define MAX_PARMS
Definition: options.h:52
argv_msg_prefix
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
argv_parse_cmd
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
options.h
argv_grow
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
string_alloc
char * string_alloc(const char *str, struct gc_arena *gc)
Definition: buffer.c:667
ASSERT
#define ASSERT(x)
Definition: error.h:195
argv.h
argv::argv
char ** argv
Definition: argv.h:39
argv_prep_format
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
env_set.h
argv_printf_arglist
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
ALLOC_ARRAY_CLEAR_GC
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc)
Definition: buffer.h:1087
argv::capacity
size_t capacity
Definition: argv.h:37
adjust_power_of_2
static size_t adjust_power_of_2(size_t u)
Definition: integer.h:168
argv_insert_head
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
argv::gc
struct gc_arena gc
Definition: argv.h:36
D_ARGV_PARSE_CMD
#define D_ARGV_PARSE_CMD
Definition: errlevel.h:147
argv::argc
size_t argc
Definition: argv.h:38
argv_extend
static void argv_extend(struct argv *a, const size_t newcap)
Resizes the list of arguments struct argv can carry.
Definition: argv.c:49
syshead.h
argv_str
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
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
argv_new
struct argv argv_new(void)
Allocates a new struct argv and ensures it is initialised.
Definition: argv.c:88
argv_printf
bool argv_printf(struct argv *argres, const char *format,...)
printf() variant which populates a struct argv.
Definition: argv.c:440
gc_malloc
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition: buffer.c:354
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1038
argv_reset
static void argv_reset(struct argv *a)
Resets the struct argv to an initial state.
Definition: argv.c:114
argv_msg
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
config.h
argv_append
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
print_argv
char * print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
Definition: buffer.c:735
msg
#define msg(flags,...)
Definition: error.h:144
integer.h
http-client.f
string f
Definition: http-client.py:6