From patchwork Fri Oct 19 04:56:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Sommerseth X-Patchwork-Id: 562 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director10.mail.ord1d.rsapps.net ([172.27.255.51]) by backend30.mail.ord1d.rsapps.net with LMTP id 6OSJF//+yVsmOQAAIUCqbw for ; Fri, 19 Oct 2018 11:57:51 -0400 Received: from proxy21.mail.iad3a.rsapps.net ([172.27.255.51]) by director10.mail.ord1d.rsapps.net with LMTP id yN96FP/+yVtTYwAApN4f7A ; Fri, 19 Oct 2018 11:57:51 -0400 Received: from smtp34.gate.iad3a ([172.27.255.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy21.mail.iad3a.rsapps.net with LMTP id +LOqDf/+yVuOAQAASBQwCQ ; Fri, 19 Oct 2018 11:57:51 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp34.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=openvpn.net X-Suspicious-Flag: YES X-Classification-ID: bae27a54-d3b7-11e8-8207-525400865cc7-1-1 Received: from [216.105.38.7] ([216.105.38.7:33807] helo=lists.sourceforge.net) by smtp34.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 94/72-21168-EFEF9CB5; Fri, 19 Oct 2018 11:57:51 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1gDX9C-0006IX-UN; Fri, 19 Oct 2018 15:57:10 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1gDX9B-0006IO-PT for openvpn-devel@lists.sourceforge.net; Fri, 19 Oct 2018 15:57:09 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To:Message-Id:Date:Subject:To: From:Sender:Reply-To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=TSAKTXjq2/T/lR+rBW4INKUEeM/IR20YNRn7CsO/iWY=; b=QVhaKKqHB66HB9PAdUiOr3iV3+ 6OGaWqeXYy4T0AJjS05T/IriVy/8w6Pu/oWrvxz+HkryH7j1hZVysQE3XlQAjRPRa+L0oREY50jJf PAgBxywIwdIYIb83PziStygew7vSIXrdVL5l6oolceGCrRi236IqAHr7/p4cK/8Rma0M=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc :MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=TSAKTXjq2/T/lR+rBW4INKUEeM/IR20YNRn7CsO/iWY=; b=KNnNqeMtkzj+c2E3fbF584SuTS IeDjegJ2cEQEukxVux4KtXOzAQGLMPmzttKc341emt+JEO6G4BS6I4wowaZqHQXUHgGNovk/jeEhf iSkmr29OSVFbkE/y0Lp3a9Ct/yijkNwTu8+kFWqgs3wB8jLcxQ+9KjT9q3ADagrfCsyg=; Received: from mx0.basenordic.cloud ([185.212.44.139]) by sfi-mx-3.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) id 1gDX99-0057Bv-QR for openvpn-devel@lists.sourceforge.net; Fri, 19 Oct 2018 15:57:09 +0000 Received: from localhost (unknown [IPv6:::1]) by mx0.basenordic.cloud (Postfix) with ESMTP id 956E981E901 for ; Fri, 19 Oct 2018 15:56:58 +0000 (UTC) Received: from mx0.basenordic.cloud ([127.0.0.1]) by localhost (winterfell.topphemmelig.net [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id TBEcLhkFBEFM for ; Fri, 19 Oct 2018 17:56:57 +0200 (CEST) Received: from zimbra.sommerseth.email (zimbra.sommerseth.email [172.16.33.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx0.basenordic.cloud (Postfix) with ESMTPS id 9B43F8251CB for ; Fri, 19 Oct 2018 17:56:54 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by zimbra.sommerseth.email (Postfix) with ESMTP id 2AB6240B371E for ; Fri, 19 Oct 2018 17:56:54 +0200 (CEST) Received: from zimbra.sommerseth.email ([127.0.0.1]) by localhost (zimbra.sommerseth.email [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id P4ZjdrT-WiWA for ; Fri, 19 Oct 2018 17:56:54 +0200 (CEST) Received: from optimus.homebase.sommerseths.net (unknown [10.35.7.3]) by zimbra.sommerseth.email (Postfix) with ESMTPS id EAF8051E21D8 for ; Fri, 19 Oct 2018 17:56:53 +0200 (CEST) From: David Sommerseth To: openvpn-devel@lists.sourceforge.net Date: Fri, 19 Oct 2018 17:56:38 +0200 Message-Id: <20181019155638.9498-5-davids@openvpn.net> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181019155638.9498-1-davids@openvpn.net> References: <20181019155638.9498-1-davids@openvpn.net> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 SPF_HELO_PASS SPF: HELO matches SPF record 0.2 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail domains are different -0.0 SPF_PASS SPF: sender matches SPF record 0.0 AWL AWL: Adjusted score from AWL reputation of From: address X-Headers-End: 1gDX99-0057Bv-QR Subject: [Openvpn-devel] [PATCH 4/4] Documented all the argv related code with minor refactoring X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox Added doxygen comments for all the functions in argv.c. There are some slight refactoring, renaming a few variables to make their use case more obvious and ensure lines do not break our 80-chars per line coding style limit. Signed-off-by: David Sommerseth --- src/openvpn/argv.c | 246 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 209 insertions(+), 37 deletions(-) diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c index 39c6fa9e..d3b47d97 100644 --- a/src/openvpn/argv.c +++ b/src/openvpn/argv.c @@ -40,6 +40,13 @@ #include "env_set.h" #include "options.h" +/** + * Resizes the list of arguments struct argv can carry. This resize + * operation will only increase the size, never decrease the size. + * + * @param *a Valid pointer to a struct argv to resize + * @param newcap size_t with the new size of the argument list. + */ static void argv_extend(struct argv *a, const size_t newcap) { @@ -57,6 +64,12 @@ argv_extend(struct argv *a, const size_t newcap) } } +/** + * Initialise an already allocated struct argv. + * It is expected that the input argument is a valid pointer. + * + * @param *a Pointer to a struct argv to initialise + */ static void argv_init(struct argv *a) { @@ -67,6 +80,12 @@ argv_init(struct argv *a) argv_extend(a, 8); } +/** + * Allocates a new struct argv and ensures it is initialised. + * Note that it does not return a pointer, but a struct argv directly. + * + * @returns Returns an initialised and empty struct argv. + */ struct argv argv_new(void) { @@ -75,12 +94,24 @@ argv_new(void) return ret; } +/** + * Frees all memory allocations allocated by the struct argv + * related functions. + * + * @param *a Valid pointer to a struct argv to release memory from + */ void argv_free(struct argv *a) { gc_free(&a->gc); } +/** + * Resets the struct argv to an initial state. No memory buffers + * will be released by this call. + * + * @param *a Valid pointer to a struct argv to resize + */ static void argv_reset(struct argv *a) { @@ -95,6 +126,19 @@ argv_reset(struct argv *a) } } +/** + * Extends an existing struct argv to carry minimum 'add' number + * of new arguments. This builds on argv_extend(), which ensures the + * new size will only be higher than the current capacity. + * + * The new size is also calculated based on the result of adjust_power_of_2(). + * This approach ensures that the list does grow bulks and only when the + * current limit is reached. + * + * @param *a Valid pointer to the struct argv to extend + * @param add size_t with the number of elements to add. + * + */ static void argv_grow(struct argv *a, const size_t add) { @@ -103,15 +147,39 @@ argv_grow(struct argv *a, const size_t add) argv_extend(a, adjust_power_of_2(newargc)); } +/** + * Appends a string to to the list of arguments stored in a struct argv + * This will ensure the list size in struct argv has the needed capacity to + * store the value. + * + * @param *a struct argv where to append the new string value + * @param *str Pointer to string to append. The provided string *MUST* have + * been malloc()ed or NULL. + */ static void -argv_append(struct argv *a, char *str) /* str must have been gc_malloced or be NULL */ +argv_append(struct argv *a, char *str) { argv_grow(a, 1); a->argv[a->argc++] = str; } +/** + * Clones a struct argv with all the contents to a new allocated struct argv. + * If 'headroom' is larger than 0, it will create a head-room in front of the + * values being copied from the source input. + * + * + * @param *source Valid pointer to the source struct argv to clone. It may + * be NULL. + * @param headroom Number of slots to leave empty in front of the slots + * copied from the source. + * + * @returns Returns a new struct argv containing a copy of the source + * struct argv, with the given headroom in front of the copy. + * + */ static struct argv -argv_clone(const struct argv *a, const size_t headroom) +argv_clone(const struct argv *source, const size_t headroom) { struct argv r; size_t i; @@ -121,16 +189,24 @@ argv_clone(const struct argv *a, const size_t headroom) { argv_append(&r, NULL); } - if (a) + if (source) { - for (i = 0; i < a->argc; ++i) + for (i = 0; i < source->argc; ++i) { - argv_append(&r, string_alloc(a->argv[i], &r.gc)); + argv_append(&r, string_alloc(source->argv[i], &r.gc)); } } return r; } +/** + * Inserts an argument string in front of all other argument slots. + * + * @param *a Valid pointer to the struct argv to insert the argument into + * @param *head Pointer to the char * string with the argument to insert + * + * @returns Returns a new struct argv with the inserted argument in front + */ struct argv argv_insert_head(const struct argv *a, const char *head) { @@ -140,12 +216,32 @@ argv_insert_head(const struct argv *a, const char *head) return r; } +/** + * Generate a single string with all the arguments in a struct argv + * concatenated. + * + * @param *a Valid pointer to the struct argv with the arguments to list + * @param *gc Pointer to a struct gc_arena managed buffer + * @param flags Flags passed to the print_argv() function. + * + * @returns Returns a string generated by print_argv() with all the arguments + * concatenated. If the argument count is 0, it will return an empty + * string. The return string is allocated in the gc_arena managed + * buffer. If the gc_arena pointer is NULL, the returned string + * must be free()d explicitly to avoid memory leaks. + */ const char * argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags) { return print_argv((const char **)a->argv, gc, flags); } +/** + * Write the arguments stored in a struct argv via the msg() command. + * + * @param msglev Integer with the message level used by msg(). + * @param *a Valid pointer to the struct argv with the arguments to write. + */ void argv_msg(const int msglev, const struct argv *a) { @@ -154,6 +250,15 @@ argv_msg(const int msglev, const struct argv *a) gc_free(&gc); } +/** + * Similar to argv_msg() but prefixes the messages being written with a + * given string. + * + * @param msglev Integer with the message level used by msg(). + * @param *a Valid pointer to the struct argv with the arguments to write + * @param *prefix Valid char * pointer to the prefix string + * + */ void argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix) { @@ -162,16 +267,29 @@ argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix) gc_free(&gc); } - -/* - * argv_prep_format - prepare argv format string for further processing +/** + * Prepares argv format string for further processing * - * Individual argument must be separated by space. Ignores leading and trailing spaces. - * Consecutive spaces count as one. Returns prepared format string, with space replaced - * by delim and adds the number of arguments to the count parameter. + * Individual argument must be separated by space. Ignores leading and + * trailing spaces. Consecutive spaces count as one. Returns prepared + * format string, with space replaced by delim and adds the number of + * arguments to the count parameter. + * + * @param *format Pointer to a the format string to process + * @param delim Char with the delimiter to use + * @param *count size_t pointer used to return the number of + * tokens (argument slots) found in the format string. + * @param *gc Pointer to a gc_arena managed buffer. + * + * @returns Returns a parsed format string (char *), together with the + * number of tokens parts found (via *count). The result string + * is allocated within the gc_arena managed buffer. If the + * gc_arena pointer is NULL, the returned string must be explicitly + * free()d to avoid memory leaks. */ static char * -argv_prep_format(const char *format, const char delim, size_t *count, struct gc_arena *gc) +argv_prep_format(const char *format, const char delim, size_t *count, + struct gc_arena *gc) { int i, j; char *f; @@ -213,15 +331,28 @@ argv_prep_format(const char *format, const char delim, size_t *count, struct gc_ return f; } -/* - * argv_printf_arglist - create a struct argv from a format string +/** + * Create a struct argv based on a format string * - * Instead of parsing the format string ourselves place delimiters via argv_prep_format() - * before we let libc's printf() do the parsing. Then split the resulting string at the - * injected delimiters. + * Instead of parsing the format string ourselves place delimiters via + * argv_prep_format() before we let libc's printf() do the parsing. + * Then split the resulting string at the injected delimiters. + * + * @param *argres Valid pointer to a struct argv where the resulting parsed + * arguments, based on the format string. + * @param *format Char* string with a printf() compliant format string + * @param arglist A va_list with the arguments to be consumed by the format + * string + * + * @returns Returns true if the parsing and processing was successfully. If + * the resulting number of arguments does not match the expected + * number of arguments (based on the format string), it is + * considered a failure, which returns false. This can happen if + * the ASCII Group Separator (GS - 0x1D) is put into the arguments + * list or format string. */ static bool -argv_printf_arglist(struct argv *a, const char *format, va_list arglist) +argv_printf_arglist(struct argv *argres, const char *format, va_list arglist) { struct gc_arena gc = gc_new(); const char delim = 0x1D; /* ASCII Group Separator (GS) */ @@ -231,14 +362,19 @@ argv_printf_arglist(struct argv *a, const char *format, va_list arglist) va_list tmplist; int len; - argc = a->argc; + argc = argres->argc; f = argv_prep_format(format, delim, &argc, &gc); if (f == NULL) { goto out; } - /* determine minimum buffer size */ + /* + * Determine minimum buffer size. + * + * With C99, vsnprintf(NULL, 0, ...) will return the number of bytes + * it would have written, had the buffer been large enough. + */ va_copy(tmplist, arglist); len = vsnprintf(NULL, 0, f, tmplist); va_end(tmplist); @@ -248,7 +384,7 @@ argv_printf_arglist(struct argv *a, const char *format, va_list arglist) } size = adjust_power_of_2(len + 1); - buf = gc_malloc(size, false, &a->gc); + buf = gc_malloc(size, false, &argres->gc); len = vsnprintf(buf, size, f, arglist); if (len < 0 || len >= size) { @@ -260,16 +396,16 @@ argv_printf_arglist(struct argv *a, const char *format, va_list arglist) while (end) { *end = '\0'; - argv_append(a, buf); + argv_append(argres, buf); buf = end + 1; end = strchr(buf, delim); } - argv_append(a, buf); + argv_append(argres, buf); - if (a->argc != argc) + if (argres->argc != argc) { /* Someone snuck in a GS (0x1D), fail gracefully */ - argv_reset(a); + argv_reset(argres); goto out; } @@ -280,51 +416,87 @@ out: return res; } - - +/** + * printf() variant which populates a struct argv. It processes the + * format string with the provided arguments. For each space separator found + * in the format string, a new argument will be added to the resulting + * struct argv. + * + * This will always reset and ensure the result is based on a pristine + * struct argv. + * + * @param *argres Valid pointer to a struct argv where the result will be put. + * @param *format printf() compliant (char *) format string. + * + * @returns Returns true if the parsing was successful. See + * argv_printf_arglist() for more details. The parsed result will + * be put into argres. + */ bool -argv_printf(struct argv *a, const char *format, ...) +argv_printf(struct argv *argres, const char *format, ...) { bool res; va_list arglist; va_start(arglist, format); - argv_reset(a); - res = argv_printf_arglist(a, format, arglist); + argv_reset(argres); + res = argv_printf_arglist(argres, format, arglist); va_end(arglist); return res; } +/** + * printf() inspired argv concatenation. Adds arguments to an existing + * struct argv and populets the argument slots based on the printf() based + * format string. + * + * @param *argres Valid pointer to a struct argv where the result will be put. + * @param *format printf() compliant (char *) format string. + * + * @returns Returns true if the parsing was successful. See + * argv_printf_arglist() for more details. The parsed result will + * be put into argres. + */ bool -argv_printf_cat(struct argv *a, const char *format, ...) +argv_printf_cat(struct argv *argres, const char *format, ...) { bool res; va_list arglist; va_start(arglist, format); - res = argv_printf_arglist(a, format, arglist); + res = argv_printf_arglist(argres, format, arglist); va_end(arglist); return res; } +/** + * Parses a command string, tokenizes it and puts each element into a separate + * struct argv argument slot. + * + * @params *argres Valid pointer to a struct argv where the parsed result + * will be found. + * @params *cmdstr Char * based string to parse + * + */ void -argv_parse_cmd(struct argv *a, const char *s) +argv_parse_cmd(struct argv *argres, const char *cmdstr) { int nparms; char *parms[MAX_PARMS + 1]; - argv_reset(a); + argv_reset(argres); - nparms = parse_line(s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &a->gc); + nparms = parse_line(cmdstr, parms, MAX_PARMS, "SCRIPT-ARGV", 0, + D_ARGV_PARSE_CMD, &argres->gc); if (nparms) { int i; for (i = 0; i < nparms; ++i) { - argv_append(a, parms[i]); + argv_append(argres, parms[i]); } } else { - argv_append(a, string_alloc(s, &a->gc)); + argv_append(argres, string_alloc(cmdstr, &argres->gc)); } }