put argv_* functions into own file, add unit tests

misc.c is too crowded with different things to perform any
sane unit testing due to its dependencies. So, in order to re-write
the #ifdef'ed tests for the argv_* family of functions into unit
tests I moved them into a dedicated file.

Signed-off-by: Heiko Hund <heiko.hund@sophos.com>
Acked-by: David Sommerseth <davids@redhat.com>
Message-Id: <1477672963-5724-2-git-send-email-heiko.hund@sophos.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12811.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
Heiko Hund 2016-10-28 18:42:37 +02:00 committed by Gert Doering
parent b7e51b1379
commit 698e268afb
9 changed files with 700 additions and 507 deletions

View file

@ -1301,6 +1301,7 @@ AC_CONFIG_FILES([
tests/unit_tests/plugins/Makefile
tests/unit_tests/plugins/auth-pam/Makefile
tests/unit_tests/example_test/Makefile
tests/unit_tests/openvpn/Makefile
vendor/Makefile
sample/Makefile
doc/Makefile

View file

@ -36,6 +36,7 @@ endif
sbin_PROGRAMS = openvpn
openvpn_SOURCES = \
argv.c argv.h \
base64.c base64.h \
basic.h \
buffer.c buffer.h \

411
src/openvpn/argv.c Normal file
View file

@ -0,0 +1,411 @@
/*
* 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.
*
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* 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"
#include "options.h"
void
argv_init (struct argv *a)
{
a->capacity = 0;
a->argc = 0;
a->argv = NULL;
a->system_str = NULL;
}
struct argv
argv_new (void)
{
struct argv ret;
argv_init (&ret);
return ret;
}
void
argv_reset (struct argv *a)
{
size_t i;
for (i = 0; i < a->argc; ++i)
free (a->argv[i]);
free (a->argv);
free (a->system_str);
argv_init (a);
}
static void
argv_extend (struct argv *a, const size_t newcap)
{
if (newcap > a->capacity)
{
char **newargv;
size_t i;
ALLOC_ARRAY_CLEAR (newargv, char *, newcap);
for (i = 0; i < a->argc; ++i)
newargv[i] = a->argv[i];
free (a->argv);
a->argv = newargv;
a->capacity = newcap;
}
}
static void
argv_grow (struct argv *a, const size_t add)
{
const size_t newargc = a->argc + add + 1;
ASSERT (newargc > a->argc);
argv_extend (a, adjust_power_of_2 (newargc));
}
static void
argv_append (struct argv *a, char *str) /* str must have been malloced or be NULL */
{
argv_grow (a, 1);
a->argv[a->argc++] = str;
}
static void
argv_system_str_append (struct argv *a, const char *str, const bool enquote)
{
if (str)
{
char *newstr;
/* compute length of new system_str */
size_t l = strlen (str) + 1; /* space for new string plus trailing '\0' */
if (a->system_str)
l += strlen (a->system_str) + 1; /* space for existing string + space (" ") separator */
if (enquote)
l += 2; /* space for two quotes */
/* build new system_str */
newstr = (char *) malloc (l);
newstr[0] = '\0';
check_malloc_return (newstr);
if (a->system_str)
{
strcpy (newstr, a->system_str);
strcat (newstr, " ");
}
if (enquote)
strcat (newstr, "\"");
strcat (newstr, str);
if (enquote)
strcat (newstr, "\"");
free (a->system_str);
a->system_str = newstr;
}
}
static char *
argv_extract_cmd_name (const char *path)
{
char *ret = NULL;
if (path)
{
char *path_cp = string_alloc(path, NULL); /* POSIX basename() implementaions may modify its arguments */
const char *bn = basename (path_cp);
if (bn)
{
char *dot = NULL;
ret = string_alloc (bn, NULL);
dot = strrchr (ret, '.');
if (dot)
*dot = '\0';
free(path_cp);
if (ret[0] == '\0')
{
free(ret);
ret = NULL;
}
}
}
return ret;
}
const char *
argv_system_str (const struct argv *a)
{
return a->system_str;
}
static struct argv
argv_clone (const struct argv *a, const size_t headroom)
{
struct argv r;
size_t i;
argv_init (&r);
for (i = 0; i < headroom; ++i)
argv_append (&r, NULL);
if (a)
{
for (i = 0; i < a->argc; ++i)
argv_append (&r, string_alloc (a->argv[i], NULL));
r.system_str = string_alloc (a->system_str, NULL);
}
return r;
}
struct argv
argv_insert_head (const struct argv *a, const char *head)
{
struct argv r;
char *s;
r = argv_clone (a, 1);
r.argv[0] = string_alloc (head, NULL);
s = r.system_str;
r.system_str = string_alloc (head, NULL);
if (s)
{
argv_system_str_append (&r, s, false);
free (s);
}
return r;
}
char *
argv_term (const char **f)
{
const char *p = *f;
const char *term = NULL;
size_t termlen = 0;
if (*p == '\0')
return NULL;
while (true)
{
const int c = *p;
if (c == '\0')
break;
if (term)
{
if (!isspace (c))
++termlen;
else
break;
}
else
{
if (!isspace (c))
{
term = p;
termlen = 1;
}
}
++p;
}
*f = p;
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
return NULL;
}
const char *
argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
{
if (a->argv)
return print_argv ((const char **)a->argv, gc, flags);
else
return "";
}
void
argv_msg (const int msglev, const struct argv *a)
{
struct gc_arena gc = gc_new ();
msg (msglev, "%s", argv_str (a, &gc, 0));
gc_free (&gc);
}
void
argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
{
struct gc_arena gc = gc_new ();
msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
gc_free (&gc);
}
void
argv_printf (struct argv *a, const char *format, ...)
{
va_list arglist;
va_start (arglist, format);
argv_printf_arglist (a, format, 0, arglist);
va_end (arglist);
}
void
argv_printf_cat (struct argv *a, const char *format, ...)
{
va_list arglist;
va_start (arglist, format);
argv_printf_arglist (a, format, APA_CAT, arglist);
va_end (arglist);
}
void
argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist)
{
struct gc_arena gc = gc_new ();
char *term;
const char *f = format;
if (!(flags & APA_CAT))
argv_reset (a);
argv_extend (a, 1); /* ensure trailing NULL */
while ((term = argv_term (&f)) != NULL)
{
if (term[0] == '%')
{
if (!strcmp (term, "%s"))
{
char *s = va_arg (arglist, char *);
if (!s)
s = "";
argv_append (a, string_alloc (s, NULL));
argv_system_str_append (a, s, true);
}
else if (!strcmp (term, "%sc"))
{
char *s = va_arg (arglist, char *);
if (s)
{
int nparms;
char *parms[MAX_PARMS+1];
int i;
nparms = parse_line (s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc);
if (nparms)
{
for (i = 0; i < nparms; ++i)
argv_append (a, string_alloc (parms[i], NULL));
}
else
argv_append (a, string_alloc (s, NULL));
argv_system_str_append (a, s, false);
}
else
{
argv_append (a, string_alloc ("", NULL));
argv_system_str_append (a, "echo", false);
}
}
else if (!strcmp (term, "%d"))
{
char numstr[64];
openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
argv_append (a, string_alloc (numstr, NULL));
argv_system_str_append (a, numstr, false);
}
else if (!strcmp (term, "%u"))
{
char numstr[64];
openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
argv_append (a, string_alloc (numstr, NULL));
argv_system_str_append (a, numstr, false);
}
else if (!strcmp (term, "%s/%d"))
{
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);
argv_system_str_append (a, combined, false);
}
}
else if (!strcmp (term, "%s%sc"))
{
char *s1 = va_arg (arglist, char *);
char *s2 = va_arg (arglist, char *);
char *combined;
char *cmd_name;
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);
cmd_name = argv_extract_cmd_name (combined);
if (cmd_name)
{
argv_system_str_append (a, cmd_name, false);
free (cmd_name);
}
}
else
ASSERT (0);
free (term);
}
else
{
argv_append (a, term);
argv_system_str_append (a, term, false);
}
}
gc_free (&gc);
}

76
src/openvpn/argv.h Normal file
View file

@ -0,0 +1,76 @@
/*
* 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.
*
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* 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.
*/
#ifndef ARGV_H
#define ARGV_H
#include "buffer.h"
struct argv {
size_t capacity;
size_t argc;
char **argv;
char *system_str;
};
void argv_init (struct argv *a);
struct argv argv_new (void);
void argv_reset (struct argv *a);
char *argv_term (const char **f);
const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
struct argv argv_insert_head (const struct argv *a, const char *head);
void argv_msg (const int msglev, const struct argv *a);
void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
const char *argv_system_str (const struct argv *a);
#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
void argv_printf (struct argv *a, const char *format, ...)
#ifdef __GNUC__
#if __USE_MINGW_ANSI_STDIO
__attribute__ ((format (gnu_printf, 2, 3)))
#else
__attribute__ ((format (__printf__, 2, 3)))
#endif
#endif
;
void argv_printf_cat (struct argv *a, const char *format, ...)
#ifdef __GNUC__
#if __USE_MINGW_ANSI_STDIO
__attribute__ ((format (gnu_printf, 2, 3)))
#else
__attribute__ ((format (__printf__, 2, 3)))
#endif
#endif
;
#endif

View file

@ -1587,465 +1587,6 @@ adjust_power_of_2 (size_t u)
return ret;
}
/*
* 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.
*/
void
argv_init (struct argv *a)
{
a->capacity = 0;
a->argc = 0;
a->argv = NULL;
a->system_str = NULL;
}
struct argv
argv_new (void)
{
struct argv ret;
argv_init (&ret);
return ret;
}
void
argv_reset (struct argv *a)
{
size_t i;
for (i = 0; i < a->argc; ++i)
free (a->argv[i]);
free (a->argv);
free (a->system_str);
argv_init (a);
}
static void
argv_extend (struct argv *a, const size_t newcap)
{
if (newcap > a->capacity)
{
char **newargv;
size_t i;
ALLOC_ARRAY_CLEAR (newargv, char *, newcap);
for (i = 0; i < a->argc; ++i)
newargv[i] = a->argv[i];
free (a->argv);
a->argv = newargv;
a->capacity = newcap;
}
}
static void
argv_grow (struct argv *a, const size_t add)
{
const size_t newargc = a->argc + add + 1;
ASSERT (newargc > a->argc);
argv_extend (a, adjust_power_of_2 (newargc));
}
static void
argv_append (struct argv *a, char *str) /* str must have been malloced or be NULL */
{
argv_grow (a, 1);
a->argv[a->argc++] = str;
}
static void
argv_system_str_append (struct argv *a, const char *str, const bool enquote)
{
if (str)
{
char *newstr;
/* compute length of new system_str */
size_t l = strlen (str) + 1; /* space for new string plus trailing '\0' */
if (a->system_str)
l += strlen (a->system_str) + 1; /* space for existing string + space (" ") separator */
if (enquote)
l += 2; /* space for two quotes */
/* build new system_str */
newstr = (char *) malloc (l);
newstr[0] = '\0';
check_malloc_return (newstr);
if (a->system_str)
{
strcpy (newstr, a->system_str);
strcat (newstr, " ");
}
if (enquote)
strcat (newstr, "\"");
strcat (newstr, str);
if (enquote)
strcat (newstr, "\"");
free (a->system_str);
a->system_str = newstr;
}
}
static char *
argv_extract_cmd_name (const char *path)
{
char *ret = NULL;
if (path)
{
char *path_cp = string_alloc(path, NULL); /* POSIX basename() implementaions may modify its arguments */
const char *bn = basename (path_cp);
if (bn)
{
char *dot = NULL;
ret = string_alloc (bn, NULL);
dot = strrchr (ret, '.');
if (dot)
*dot = '\0';
free(path_cp);
if (ret[0] == '\0')
{
free(ret);
ret = NULL;
}
}
}
return ret;
}
const char *
argv_system_str (const struct argv *a)
{
return a->system_str;
}
struct argv
argv_clone (const struct argv *a, const size_t headroom)
{
struct argv r;
size_t i;
argv_init (&r);
for (i = 0; i < headroom; ++i)
argv_append (&r, NULL);
if (a)
{
for (i = 0; i < a->argc; ++i)
argv_append (&r, string_alloc (a->argv[i], NULL));
r.system_str = string_alloc (a->system_str, NULL);
}
return r;
}
struct argv
argv_insert_head (const struct argv *a, const char *head)
{
struct argv r;
char *s;
r = argv_clone (a, 1);
r.argv[0] = string_alloc (head, NULL);
s = r.system_str;
r.system_str = string_alloc (head, NULL);
if (s)
{
argv_system_str_append (&r, s, false);
free (s);
}
return r;
}
char *
argv_term (const char **f)
{
const char *p = *f;
const char *term = NULL;
size_t termlen = 0;
if (*p == '\0')
return NULL;
while (true)
{
const int c = *p;
if (c == '\0')
break;
if (term)
{
if (!isspace (c))
++termlen;
else
break;
}
else
{
if (!isspace (c))
{
term = p;
termlen = 1;
}
}
++p;
}
*f = p;
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
return NULL;
}
const char *
argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
{
if (a->argv)
return print_argv ((const char **)a->argv, gc, flags);
else
return "";
}
void
argv_msg (const int msglev, const struct argv *a)
{
struct gc_arena gc = gc_new ();
msg (msglev, "%s", argv_str (a, &gc, 0));
gc_free (&gc);
}
void
argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
{
struct gc_arena gc = gc_new ();
msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
gc_free (&gc);
}
void
argv_printf (struct argv *a, const char *format, ...)
{
va_list arglist;
va_start (arglist, format);
argv_printf_arglist (a, format, 0, arglist);
va_end (arglist);
}
void
argv_printf_cat (struct argv *a, const char *format, ...)
{
va_list arglist;
va_start (arglist, format);
argv_printf_arglist (a, format, APA_CAT, arglist);
va_end (arglist);
}
void
argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist)
{
struct gc_arena gc = gc_new ();
char *term;
const char *f = format;
if (!(flags & APA_CAT))
argv_reset (a);
argv_extend (a, 1); /* ensure trailing NULL */
while ((term = argv_term (&f)) != NULL)
{
if (term[0] == '%')
{
if (!strcmp (term, "%s"))
{
char *s = va_arg (arglist, char *);
if (!s)
s = "";
argv_append (a, string_alloc (s, NULL));
argv_system_str_append (a, s, true);
}
else if (!strcmp (term, "%sc"))
{
char *s = va_arg (arglist, char *);
if (s)
{
int nparms;
char *parms[MAX_PARMS+1];
int i;
nparms = parse_line (s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc);
if (nparms)
{
for (i = 0; i < nparms; ++i)
argv_append (a, string_alloc (parms[i], NULL));
}
else
argv_append (a, string_alloc (s, NULL));
argv_system_str_append (a, s, false);
}
else
{
argv_append (a, string_alloc ("", NULL));
argv_system_str_append (a, "echo", false);
}
}
else if (!strcmp (term, "%d"))
{
char numstr[64];
openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
argv_append (a, string_alloc (numstr, NULL));
argv_system_str_append (a, numstr, false);
}
else if (!strcmp (term, "%u"))
{
char numstr[64];
openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
argv_append (a, string_alloc (numstr, NULL));
argv_system_str_append (a, numstr, false);
}
else if (!strcmp (term, "%s/%d"))
{
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);
argv_system_str_append (a, combined, false);
}
}
else if (!strcmp (term, "%s%sc"))
{
char *s1 = va_arg (arglist, char *);
char *s2 = va_arg (arglist, char *);
char *combined;
char *cmd_name;
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);
cmd_name = argv_extract_cmd_name (combined);
if (cmd_name)
{
argv_system_str_append (a, cmd_name, false);
free (cmd_name);
}
}
else
ASSERT (0);
free (term);
}
else
{
argv_append (a, term);
argv_system_str_append (a, term, false);
}
}
gc_free (&gc);
}
#ifdef ARGV_TEST
void
argv_test (void)
{
struct gc_arena gc = gc_new ();
const char *s;
struct argv a;
argv_init (&a);
argv_printf (&a, "%sc foo bar %s", "c:\\\\src\\\\test\\\\jyargs.exe", "foo bar");
argv_msg_prefix (M_INFO, &a, "ARGV");
msg (M_INFO, "ARGV-S: %s", argv_system_str(&a));
/*openvpn_execve_check (&a, NULL, 0, "command failed");*/
argv_printf (&a, "%sc %s %s", "c:\\\\src\\\\test files\\\\batargs.bat", "foo", "bar");
argv_msg_prefix (M_INFO, &a, "ARGV");
msg (M_INFO, "ARGV-S: %s", argv_system_str(&a));
/*openvpn_execve_check (&a, NULL, 0, "command failed");*/
argv_printf (&a, "%s%sc foo bar %s %s/%d %d %u", "/foo", "/bar.exe", "one two", "1.2.3.4", 24, -69, 96);
argv_msg_prefix (M_INFO, &a, "ARGV");
msg (M_INFO, "ARGV-S: %s", argv_system_str(&a));
/*openvpn_execve_check (&a, NULL, 0, "command failed");*/
argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("PF: %s\n", s);
printf ("PF-S: %s\n", argv_system_str(&a));
{
struct argv b = argv_insert_head (&a, "MARK");
s = argv_str (&b, &gc, PA_BRACKET);
printf ("PF: %s\n", s);
printf ("PF-S: %s\n", argv_system_str(&b));
argv_reset (&b);
}
argv_printf (&a, "%sc foo bar %d", "\"multi term\" command following \\\"spaces", 99);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("PF: %s\n", s);
printf ("PF-S: %s\n", argv_system_str(&a));
argv_reset (&a);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("PF: %s\n", s);
printf ("PF-S: %s\n", argv_system_str(&a));
argv_reset (&a);
argv_printf (&a, "foo bar %d", 99);
argv_printf_cat (&a, "bar %d foo %sc", 42, "nonesuch");
argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("PF: %s\n", s);
printf ("PF-S: %s\n", argv_system_str(&a));
argv_reset (&a);
#if 0
{
char line[512];
while (fgets (line, sizeof(line), stdin) != NULL)
{
char *term;
const char *f = line;
int i = 0;
while ((term = argv_term (&f)) != NULL)
{
printf ("[%d] '%s'\n", i, term);
++i;
free (term);
}
}
}
#endif
argv_reset (&a);
gc_free (&gc);
}
#endif
/*
* Remove security-sensitive strings from control message
* so that they will not be output to log file.

View file

@ -25,6 +25,7 @@
#ifndef MISC_H
#define MISC_H
#include "argv.h"
#include "basic.h"
#include "common.h"
#include "integer.h"
@ -37,14 +38,6 @@
/* forward declarations */
struct plugin_list;
/* used by argv_x functions */
struct argv {
size_t capacity;
size_t argc;
char **argv;
char *system_str;
};
/*
* Handle environmental variable lists
*/
@ -325,45 +318,6 @@ extern int script_security; /* GLOBAL */
/* return the next largest power of 2 */
size_t adjust_power_of_2 (size_t u);
/*
* 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.
*/
void argv_init (struct argv *a);
struct argv argv_new (void);
void argv_reset (struct argv *a);
char *argv_term (const char **f);
const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
struct argv argv_insert_head (const struct argv *a, const char *head);
void argv_msg (const int msglev, const struct argv *a);
void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
const char *argv_system_str (const struct argv *a);
#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
void argv_printf (struct argv *a, const char *format, ...)
#ifdef __GNUC__
#if __USE_MINGW_ANSI_STDIO
__attribute__ ((format (gnu_printf, 2, 3)))
#else
__attribute__ ((format (__printf__, 2, 3)))
#endif
#endif
;
void argv_printf_cat (struct argv *a, const char *format, ...)
#ifdef __GNUC__
#if __USE_MINGW_ANSI_STDIO
__attribute__ ((format (gnu_printf, 2, 3)))
#else
__attribute__ ((format (__printf__, 2, 3)))
#endif
#endif
;
#define COMPAT_FLAG_QUERY 0 /** compat_flags operator: Query for a flag */
#define COMPAT_FLAG_SET (1<<0) /** compat_flags operator: Set a compat flag */
#define COMPAT_NAMES (1<<1) /** compat flag: --compat-names set */

View file

@ -1,5 +1,5 @@
AUTOMAKE_OPTIONS = foreign
if CMOCKA_INITIALIZED
SUBDIRS = example_test plugins
SUBDIRS = example_test plugins openvpn
endif

View file

@ -0,0 +1,15 @@
AUTOMAKE_OPTIONS = foreign
check_PROGRAMS = argv_testdriver
TESTS = $(check_PROGRAMS)
openvpn_srcdir = $(top_srcdir)/src/openvpn
compat_srcdir = $(top_srcdir)/src/compat
argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line
argv_testdriver_SOURCES = test_argv.c \
$(openvpn_srcdir)/platform.c \
$(openvpn_srcdir)/buffer.c \
$(openvpn_srcdir)/argv.c

View file

@ -0,0 +1,194 @@
#include "config.h"
#include "syshead.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#include <assert.h>
#include "argv.h"
#include "buffer.h"
/*
* Dummy symbols that need to be defined due to them being
* referenced in #include'd header files and their includes
*/
unsigned int x_debug_level;
bool dont_mute (unsigned int flags) { return true; }
void assert_failed (const char *filename, int line, const char *condition) { exit(0); }
void out_of_memory (void) { }
void x_msg (const unsigned int flags, const char *format, ...) { }
/*
* This is defined here to prevent #include'ing misc.h
* which makes things difficult beyond any recognition
*/
size_t
adjust_power_of_2 (size_t u)
{
size_t ret = 1;
while (ret < u)
{
ret <<= 1;
assert (ret > 0);
}
return ret;
}
/* Defines for use in the tests and the mock parse_line() */
#define PATH1 "/s p a c e"
#define PATH2 "/foo bar/baz"
#define PARAM1 "param1"
#define PARAM2 "param two"
#define SCRIPT_CMD "\"" PATH1 PATH2 "\"" PARAM1 "\"" PARAM2 "\""
int
__wrap_parse_line (const char *line, char **p, const int n, const char *file,
const int line_num, int msglevel, struct gc_arena *gc)
{
p[0] = PATH1 PATH2;
p[1] = PARAM1;
p[2] = PARAM2;
return 3;
}
static void
argv_printf__multiple_spaces_in_format__parsed_as_one (void **state)
{
struct argv a = argv_new ();
argv_printf (&a, " %s %s %d ", PATH1, PATH2, 42);
assert_int_equal (a.argc, 3);
argv_reset (&a);
}
static void
argv_printf_cat__multiple_spaces_in_format__parsed_as_one (void **state)
{
struct argv a = argv_new ();
argv_printf (&a, "%s ", PATH1);
argv_printf_cat (&a, " %s %s", PATH2, PARAM1);
assert_int_equal (a.argc, 3);
argv_reset (&a);
}
static void
argv_printf__combined_path_with_spaces__argc_correct (void **state)
{
struct argv a = argv_new ();
argv_printf (&a, "%s%sc", PATH1, PATH2);
assert_int_equal (a.argc, 1);
argv_printf (&a, "%s%sc %d", PATH1, PATH2, 42);
assert_int_equal (a.argc, 2);
argv_printf (&a, "foo %s%sc %s x y", PATH2, PATH1, "foo");
assert_int_equal (a.argc, 5);
argv_reset (&a);
}
static void
argv_printf__script_command__argc_correct (void **state)
{
struct argv a = argv_new ();
argv_printf (&a, "%sc", SCRIPT_CMD);
assert_int_equal (a.argc, 3);
argv_printf (&a, "bar baz %sc %d %s", SCRIPT_CMD, 42, PATH1);
assert_int_equal (a.argc, 7);
argv_reset (&a);
}
static void
argv_printf_cat__used_twice__argc_correct (void **state)
{
struct argv a = argv_new ();
argv_printf (&a, "%s %s %s", PATH1, PATH2, PARAM1);
argv_printf_cat (&a, "%s", PARAM2);
argv_printf_cat (&a, "foo");
assert_int_equal (a.argc, 5);
argv_reset (&a);
}
static void
argv_str__multiple_argv__correct_output (void **state)
{
struct argv a = argv_new ();
struct gc_arena gc = gc_new ();
const char *output;
argv_printf (&a, "%s%sc", PATH1, PATH2);
argv_printf_cat (&a, "%s", PARAM1);
argv_printf_cat (&a, "%s", PARAM2);
output = argv_str (&a, &gc, PA_BRACKET);
assert_string_equal (output, "[" PATH1 PATH2 "] [" PARAM1 "] [" PARAM2 "]");
argv_reset (&a);
gc_free (&gc);
}
static void
argv_insert_head__empty_argv__head_only (void **state)
{
struct argv a = argv_new ();
struct argv b;
b = argv_insert_head (&a, PATH1);
assert_int_equal (b.argc, 1);
assert_string_equal (b.argv[0], PATH1);
argv_reset (&b);
argv_reset (&a);
}
static void
argv_insert_head__non_empty_argv__head_added (void **state)
{
struct argv a = argv_new ();
struct argv b;
int i;
argv_printf (&a, "%s", PATH2);
b = argv_insert_head (&a, PATH1);
assert_int_equal (b.argc, a.argc + 1);
for (i = 0; i < b.argc; i++) {
if (i == 0)
assert_string_equal (b.argv[i], PATH1);
else
assert_string_equal (b.argv[i], a.argv[i - 1]);
}
argv_reset (&b);
argv_reset (&a);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test (argv_printf__multiple_spaces_in_format__parsed_as_one),
cmocka_unit_test (argv_printf_cat__multiple_spaces_in_format__parsed_as_one),
cmocka_unit_test (argv_printf__combined_path_with_spaces__argc_correct),
cmocka_unit_test (argv_printf__script_command__argc_correct),
cmocka_unit_test (argv_printf_cat__used_twice__argc_correct),
cmocka_unit_test (argv_str__multiple_argv__correct_output),
cmocka_unit_test (argv_insert_head__non_empty_argv__head_added),
};
return cmocka_run_group_tests_name ("argv", tests, NULL, NULL);
}