698e268a |
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
698e268a |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
698e268a |
*
*
* A printf-like function (that only recognizes a subset of standard printf
* format operators) that prints arguments to an argv list instead
* of a standard string. This is used to build up argv arrays for passing
* to execve.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
#include "syshead.h"
#include "argv.h" |
9fc0e963 |
#include "integer.h" |
68b97b25 |
#include "env_set.h" |
698e268a |
#include "options.h"
|
aed5ef40 |
static void |
81d882d5 |
argv_init(struct argv *a) |
698e268a |
{ |
81d882d5 |
a->capacity = 0;
a->argc = 0;
a->argv = NULL; |
698e268a |
}
struct argv |
81d882d5 |
argv_new(void) |
698e268a |
{ |
81d882d5 |
struct argv ret;
argv_init(&ret);
return ret; |
698e268a |
}
void |
81d882d5 |
argv_reset(struct argv *a) |
698e268a |
{ |
81d882d5 |
size_t i;
for (i = 0; i < a->argc; ++i) |
4cd4899e |
{ |
81d882d5 |
free(a->argv[i]); |
4cd4899e |
} |
81d882d5 |
free(a->argv);
argv_init(a); |
698e268a |
}
static void |
81d882d5 |
argv_extend(struct argv *a, const size_t newcap) |
698e268a |
{ |
81d882d5 |
if (newcap > a->capacity) |
698e268a |
{ |
81d882d5 |
char **newargv;
size_t i;
ALLOC_ARRAY_CLEAR(newargv, char *, newcap);
for (i = 0; i < a->argc; ++i) |
4cd4899e |
{ |
81d882d5 |
newargv[i] = a->argv[i]; |
4cd4899e |
} |
81d882d5 |
free(a->argv);
a->argv = newargv;
a->capacity = newcap; |
698e268a |
}
}
static void |
81d882d5 |
argv_grow(struct argv *a, const size_t add) |
698e268a |
{ |
81d882d5 |
const size_t newargc = a->argc + add + 1;
ASSERT(newargc > a->argc);
argv_extend(a, adjust_power_of_2(newargc)); |
698e268a |
}
static void |
81d882d5 |
argv_append(struct argv *a, char *str) /* str must have been malloced or be NULL */ |
698e268a |
{ |
81d882d5 |
argv_grow(a, 1);
a->argv[a->argc++] = str; |
698e268a |
}
static struct argv |
81d882d5 |
argv_clone(const struct argv *a, const size_t headroom) |
698e268a |
{ |
81d882d5 |
struct argv r;
size_t i; |
698e268a |
|
81d882d5 |
argv_init(&r);
for (i = 0; i < headroom; ++i) |
4cd4899e |
{ |
81d882d5 |
argv_append(&r, NULL); |
4cd4899e |
} |
81d882d5 |
if (a) |
698e268a |
{ |
81d882d5 |
for (i = 0; i < a->argc; ++i) |
4cd4899e |
{ |
81d882d5 |
argv_append(&r, string_alloc(a->argv[i], NULL)); |
4cd4899e |
} |
698e268a |
} |
81d882d5 |
return r; |
698e268a |
}
struct argv |
81d882d5 |
argv_insert_head(const struct argv *a, const char *head) |
698e268a |
{ |
81d882d5 |
struct argv r;
r = argv_clone(a, 1);
r.argv[0] = string_alloc(head, NULL);
return r; |
698e268a |
}
|
aed5ef40 |
static char * |
81d882d5 |
argv_term(const char **f) |
698e268a |
{ |
81d882d5 |
const char *p = *f;
const char *term = NULL;
size_t termlen = 0; |
698e268a |
|
81d882d5 |
if (*p == '\0')
{
return NULL;
} |
698e268a |
|
81d882d5 |
while (true) |
698e268a |
{ |
81d882d5 |
const int c = *p;
if (c == '\0') |
698e268a |
{
break;
} |
81d882d5 |
if (term)
{
if (!isspace(c))
{
++termlen;
}
else
{
break;
}
}
else |
698e268a |
{ |
81d882d5 |
if (!isspace(c)) |
698e268a |
{ |
81d882d5 |
term = p;
termlen = 1; |
698e268a |
}
} |
81d882d5 |
++p; |
698e268a |
} |
81d882d5 |
*f = p; |
698e268a |
|
81d882d5 |
if (term)
{
char *ret;
ASSERT(termlen > 0);
ret = malloc(termlen + 1);
check_malloc_return(ret);
memcpy(ret, term, termlen);
ret[termlen] = '\0';
return ret;
}
else |
698e268a |
{ |
81d882d5 |
return NULL; |
698e268a |
}
}
const char * |
81d882d5 |
argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags) |
698e268a |
{ |
81d882d5 |
if (a->argv)
{
return print_argv((const char **)a->argv, gc, flags);
}
else
{
return "";
} |
698e268a |
}
void |
81d882d5 |
argv_msg(const int msglev, const struct argv *a) |
698e268a |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
msg(msglev, "%s", argv_str(a, &gc, 0));
gc_free(&gc); |
698e268a |
}
void |
81d882d5 |
argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix) |
698e268a |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
msg(msglev, "%s: %s", prefix, argv_str(a, &gc, 0));
gc_free(&gc); |
698e268a |
}
|
aed5ef40 |
static void |
81d882d5 |
argv_printf_arglist(struct argv *a, const char *format, va_list arglist) |
698e268a |
{ |
81d882d5 |
char *term;
const char *f = format; |
698e268a |
|
81d882d5 |
argv_extend(a, 1); /* ensure trailing NULL */ |
698e268a |
|
81d882d5 |
while ((term = argv_term(&f)) != NULL) |
698e268a |
{ |
81d882d5 |
if (term[0] == '%') |
698e268a |
{ |
81d882d5 |
if (!strcmp(term, "%s")) |
698e268a |
{ |
81d882d5 |
char *s = va_arg(arglist, char *);
if (!s)
{
s = "";
}
argv_append(a, string_alloc(s, NULL)); |
698e268a |
} |
81d882d5 |
else if (!strcmp(term, "%d")) |
698e268a |
{ |
81d882d5 |
char numstr[64];
openvpn_snprintf(numstr, sizeof(numstr), "%d", va_arg(arglist, int));
argv_append(a, string_alloc(numstr, NULL)); |
698e268a |
} |
81d882d5 |
else if (!strcmp(term, "%u")) |
698e268a |
{ |
81d882d5 |
char numstr[64];
openvpn_snprintf(numstr, sizeof(numstr), "%u", va_arg(arglist, unsigned int));
argv_append(a, string_alloc(numstr, NULL)); |
698e268a |
} |
e38d3a00 |
else if (!strcmp(term, "%lu"))
{
char numstr[64];
openvpn_snprintf(numstr, sizeof(numstr), "%lu",
va_arg(arglist, unsigned long));
argv_append(a, string_alloc(numstr, NULL));
} |
81d882d5 |
else if (!strcmp(term, "%s/%d")) |
698e268a |
{ |
81d882d5 |
char numstr[64];
char *s = va_arg(arglist, char *);
if (!s)
{
s = "";
}
openvpn_snprintf(numstr, sizeof(numstr), "%d", va_arg(arglist, int));
{
const size_t len = strlen(s) + strlen(numstr) + 2;
char *combined = (char *) malloc(len);
check_malloc_return(combined);
strcpy(combined, s);
strcat(combined, "/");
strcat(combined, numstr);
argv_append(a, combined);
} |
698e268a |
} |
81d882d5 |
else if (!strcmp(term, "%s%sc")) |
698e268a |
{ |
81d882d5 |
char *s1 = va_arg(arglist, char *);
char *s2 = va_arg(arglist, char *);
char *combined;
if (!s1)
{
s1 = "";
}
if (!s2)
{
s2 = "";
}
combined = (char *) malloc(strlen(s1) + strlen(s2) + 1);
check_malloc_return(combined);
strcpy(combined, s1);
strcat(combined, s2);
argv_append(a, combined); |
698e268a |
} |
81d882d5 |
else
{
ASSERT(0);
}
free(term); |
698e268a |
} |
81d882d5 |
else |
698e268a |
{ |
81d882d5 |
argv_append(a, term); |
698e268a |
}
}
} |
aed5ef40 |
void |
81d882d5 |
argv_printf(struct argv *a, const char *format, ...) |
aed5ef40 |
{ |
81d882d5 |
va_list arglist;
argv_reset(a);
va_start(arglist, format);
argv_printf_arglist(a, format, arglist);
va_end(arglist);
} |
aed5ef40 |
void |
81d882d5 |
argv_printf_cat(struct argv *a, const char *format, ...) |
aed5ef40 |
{ |
81d882d5 |
va_list arglist;
va_start(arglist, format);
argv_printf_arglist(a, format, arglist);
va_end(arglist); |
aed5ef40 |
} |
25360912 |
void |
81d882d5 |
argv_parse_cmd(struct argv *a, const char *s) |
25360912 |
{ |
81d882d5 |
int nparms;
char *parms[MAX_PARMS + 1];
struct gc_arena gc = gc_new(); |
25360912 |
|
81d882d5 |
argv_reset(a);
argv_extend(a, 1); /* ensure trailing NULL */ |
25360912 |
|
81d882d5 |
nparms = parse_line(s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc);
if (nparms)
{
int i;
for (i = 0; i < nparms; ++i) |
4cd4899e |
{ |
81d882d5 |
argv_append(a, string_alloc(parms[i], NULL)); |
4cd4899e |
} |
81d882d5 |
}
else |
25360912 |
{ |
81d882d5 |
argv_append(a, string_alloc(s, NULL)); |
25360912 |
}
|
81d882d5 |
gc_free(&gc); |
25360912 |
} |