From patchwork Thu Feb 6 02:21:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Sommerseth X-Patchwork-Id: 982 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director12.mail.ord1d.rsapps.net ([172.28.255.1]) by backend30.mail.ord1d.rsapps.net with LMTP id GLYCOVAWPF65eAAAIUCqbw for ; Thu, 06 Feb 2020 08:36:16 -0500 Received: from proxy8.mail.ord1c.rsapps.net ([172.28.255.1]) by director12.mail.ord1d.rsapps.net with LMTP id KAyTOFAWPF6pGgAAIasKDg ; Thu, 06 Feb 2020 08:36:16 -0500 Received: from smtp12.gate.ord1c ([172.28.255.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy8.mail.ord1c.rsapps.net with LMTP id kKiEOFAWPF7JZgAAHz/atg ; Thu, 06 Feb 2020 08:36:16 -0500 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: smtp12.gate.ord1c.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=fail (p=none; dis=none) header.from=openvpn.net X-Suspicious-Flag: YES X-Classification-ID: a647a4e4-48e5-11ea-a6eb-bc305bf03e5c-1-1 Received: from [216.105.38.7] ([216.105.38.7:46230] helo=lists.sourceforge.net) by smtp12.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 56/1E-19918-0561C3E5; Thu, 06 Feb 2020 08:36:16 -0500 Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1izhJK-0005sk-FH; Thu, 06 Feb 2020 13:35:14 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1izhJJ-0005sc-Gd for openvpn-devel@lists.sourceforge.net; Thu, 06 Feb 2020 13:35:13 +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=gldOkZ64kIZRaouRxZvxNIUjeHCtPmSOQ9Ruczf23Ls=; b=brKiTlSC//tfFyrEdG9GUuU441 19CEEne64GO1yUK1o0iGVCOV8AF5LWgjkLS+ye4DajmgQHjKjXh+zOEqqOhcLXJs8r18jYlih49YK rYqch8M9gkwrHEKWPy29Gv0ojUQJuq3Nltl3aqViS+KNGgML30zdl0eZjacKYWe//WpU=; 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=gldOkZ64kIZRaouRxZvxNIUjeHCtPmSOQ9Ruczf23Ls=; b=VsWuTdbS5LOIPV6jCidOdcBoau lRb0TDjNCHNfpy3koTkxWVmgRPn+lEpN7OG/Ntx2vq5dc7LNqeBw6mTku8BORtdhidDccbifW9Ipq 1E8apcXG7ZnxZfHJSK92XxIRzg74xDoTVAHr48fPgAexFFhzcUev9XYQijH/1wHbyWbY=; Received: from mx0.basenordic.cloud ([185.212.44.139]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.2) id 1izhJH-001N3E-Qh for openvpn-devel@lists.sourceforge.net; Thu, 06 Feb 2020 13:35:13 +0000 Received: from localhost (unknown [IPv6:::1]) by mx0.basenordic.cloud (Postfix) with ESMTP id EBB65838A15 for ; Thu, 6 Feb 2020 13:21:31 +0000 (UTC) Received: from mx0.basenordic.cloud ([IPv6:::1]) by localhost (winterfell.topphemmelig.net [IPv6:::1]) (amavisd-new, port 10024) with ESMTP id aT_eKwr7R4_L for ; Thu, 6 Feb 2020 14:21:29 +0100 (CET) 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 A71E78189DC for ; Thu, 6 Feb 2020 14:21:29 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by zimbra.sommerseth.email (Postfix) with ESMTP id F098C401D334 for ; Thu, 6 Feb 2020 14:21:28 +0100 (CET) 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 Zd_1BCymoxmU for ; Thu, 6 Feb 2020 14:21:26 +0100 (CET) Received: from optimus.homebase.sommerseths.net (unknown [10.35.7.3]) by zimbra.sommerseth.email (Postfix) with ESMTPS id 9BFF8401D333 for ; Thu, 6 Feb 2020 14:21:23 +0100 (CET) From: David Sommerseth To: openvpn-devel@lists.sourceforge.net Date: Thu, 6 Feb 2020 14:21:03 +0100 Message-Id: <20200206132103.15977-5-davids@openvpn.net> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200206132103.15977-1-davids@openvpn.net> References: <20200206132103.15977-1-davids@openvpn.net> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: openvpn.net] -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 X-Headers-End: 1izhJH-001N3E-Qh 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 Acked-By: Arne Schwabe --- src/openvpn/argv.c | 251 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 211 insertions(+), 40 deletions(-) diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c index 7d949d24..b799c974 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; argv_init(&r); @@ -120,16 +188,24 @@ argv_clone(const struct argv *a, const size_t headroom) { argv_append(&r, NULL); } - if (a) + if (source) { - for (size_t i = 0; i < a->argc; ++i) + for (size_t 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) { @@ -139,12 +215,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) { @@ -153,6 +249,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) { @@ -161,16 +266,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) { if (format == NULL) { @@ -209,15 +327,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) { const char delim = 0x1D; /* ASCII Group Separator (GS) */ bool res = false; @@ -231,14 +362,19 @@ argv_printf_arglist(struct argv *a, const char *format, va_list arglist) * saved in the struct argv. * */ - size_t argc = a->argc; - char *f = argv_prep_format(format, delim, &argc, &a->gc); + size_t argc = argres->argc; + char *f = argv_prep_format(format, delim, &argc, &argres->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_list tmplist; va_copy(tmplist, arglist); int len = vsnprintf(NULL, 0, f, tmplist); @@ -252,8 +388,8 @@ argv_printf_arglist(struct argv *a, const char *format, va_list arglist) * Do the actual vsnprintf() operation, which expands the format * string with the provided arguments. */ - size_t size = adjust_power_of_2(len + 1); - char *buf = gc_malloc(size, false, &a->gc); + size_t size = len + 1; + char *buf = gc_malloc(size, false, &argres->gc); len = vsnprintf(buf, size, f, arglist); if (len < 0 || len >= size) { @@ -268,16 +404,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; } res = true; @@ -286,48 +422,83 @@ 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, ...) { va_list arglist; va_start(arglist, format); - argv_reset(a); - bool res = argv_printf_arglist(a, format, arglist); + argv_reset(argres); + bool 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, ...) { va_list arglist; va_start(arglist, format); - - bool res = argv_printf_arglist(a, format, arglist); + bool 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) { - argv_reset(a); + argv_reset(argres); char *parms[MAX_PARMS + 1] = { 0 }; - int nparms = parse_line(s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &a->gc); + int 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)); } }