From patchwork Mon Oct 12 12:37:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Petr_=C5=A0tetiar?= X-Patchwork-Id: 1380881 X-Patchwork-Delegate: ynezz@true.cz Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2001:8b0:10b:1231::1; helo=merlin.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=true.cz Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=merlin.20170209 header.b=TqMQy+zi; dkim-atps=neutral Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:8b0:10b:1231::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4C8ytF2Z6Cz9sTf for ; Mon, 12 Oct 2020 23:39:32 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=psHqAYqPTjfi8LcWAfoJpvFv+tE7GFNneVzlueSeYe4=; b=TqMQy+ziKBa3z/7JIIQ1qRLd4 nmBDKmjyy6yvsOqyJjwt3O1OZvsPZ6aYT2LBCuUfn3wsH7IgYNMR1LCP1a9+0MmNNrvET0DmMqxi6 tSfj1NPJk08t2bbgGqxqQstHzvJLMD8inf3l8x6w7IuiFooxSBsyqvhPS5hbiA7rEP783V4noBjN6 cGSk4cVdNXwScytT5TRKdA4Di/Ef+0uK5/Pv56MGgqCB2oZY9feLATY+BHgsI3yp97NePBcYxq2pg pEzzZ52qr/h9Hbv771sCjEF0BJDKawRq8mrYqDUQWztV10y/nvhNb8n//9Gp+kXRDVn70jqYpJ9fB 7TGClHa5A==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kRx5C-0002rY-Rw; Mon, 12 Oct 2020 12:37:42 +0000 Received: from smtp-out.xnet.cz ([178.217.244.18]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1kRx4z-0002m1-B7 for openwrt-devel@lists.openwrt.org; Mon, 12 Oct 2020 12:37:32 +0000 Received: from meh.true.cz (meh.true.cz [108.61.167.218]) (Authenticated sender: petr@true.cz) by smtp-out.xnet.cz (Postfix) with ESMTPSA id D7BE935DE; Mon, 12 Oct 2020 14:37:25 +0200 (CEST) Received: by meh.true.cz (OpenSMTPD) with ESMTP id ff1de196; Mon, 12 Oct 2020 14:37:09 +0200 (CEST) From: =?utf-8?q?Petr_=C5=A0tetiar?= To: openwrt-devel@lists.openwrt.org Subject: [PATCH cgi-io 04/12] Refactor utility functions into static library Date: Mon, 12 Oct 2020 14:37:10 +0200 Message-Id: <20201012123718.25623-5-ynezz@true.cz> In-Reply-To: <20201012123718.25623-1-ynezz@true.cz> References: <20201012123718.25623-1-ynezz@true.cz> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20201012_083729_570550_1F034A3B X-CRM114-Status: GOOD ( 30.55 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 SPF_NONE SPF: sender does not publish an SPF Record X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?utf-8?q?Petr_=C5=A0tetiar?= Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org For reusability during testing. Signed-off-by: Petr Štetiar --- CMakeLists.txt | 7 +- main.c | 270 +---------------------------------------------- util.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 11 ++ 4 files changed, 293 insertions(+), 271 deletions(-) create mode 100644 util.c create mode 100644 util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c9d40caa07..693830a85274 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,10 @@ IF(APPLE) LINK_DIRECTORIES(/opt/local/lib) ENDIF() -ADD_EXECUTABLE(cgi-io main.c multipart_parser.c) -TARGET_LINK_LIBRARIES(cgi-io ${ubox} ${ubus}) +ADD_LIBRARY(cgi-lib STATIC multipart_parser.c util.c) + +ADD_EXECUTABLE(cgi-io main.c) +TARGET_LINK_LIBRARIES(cgi-io cgi-lib ${ubox} ${ubus}) + INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin) diff --git a/main.c b/main.c index 53c672836294..ff9bb63f5cd8 100644 --- a/main.c +++ b/main.c @@ -35,6 +35,7 @@ #include #include +#include "util.h" #include "multipart_parser.h" #ifndef O_TMPFILE @@ -42,7 +43,6 @@ #endif #define READ_BLOCK 4096 -#define POST_LIMIT 131072 enum part { PART_UNKNOWN, @@ -173,201 +173,6 @@ checksum(const char *applet, size_t sumlen, const char *file) return chksum; } -static char * -datadup(const void *in, size_t len) -{ - char *out = malloc(len + 1); - - if (!out) - return NULL; - - memcpy(out, in, len); - - *(out + len) = 0; - - return out; -} - -static bool -urldecode(char *buf) -{ - char *c, *p; - - if (!buf || !*buf) - return true; - -#define hex(x) \ - (((x) <= '9') ? ((x) - '0') : \ - (((x) <= 'F') ? ((x) - 'A' + 10) : \ - ((x) - 'a' + 10))) - - for (c = p = buf; *p; c++) - { - if (*p == '%') - { - if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2))) - return false; - - *c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2))); - - p += 3; - } - else if (*p == '+') - { - *c = ' '; - p++; - } - else - { - *c = *p++; - } - } - - *c = 0; - - return true; -} - -static char * -postdecode(char **fields, int n_fields) -{ - const char *var; - char *p, *postbuf; - int i, field, found = 0; - ssize_t len = 0, rlen = 0, content_length = 0; - - var = getenv("CONTENT_TYPE"); - - if (!var || strncmp(var, "application/x-www-form-urlencoded", 33)) - return NULL; - - var = getenv("CONTENT_LENGTH"); - - if (!var) - return NULL; - - content_length = strtol(var, &p, 10); - - if (p == var || content_length <= 0 || content_length >= POST_LIMIT) - return NULL; - - postbuf = calloc(1, content_length + 1); - - if (postbuf == NULL) - return NULL; - - for (len = 0; len < content_length; ) - { - rlen = read(0, postbuf + len, content_length - len); - - if (rlen <= 0) - break; - - len += rlen; - } - - if (len < content_length) - { - free(postbuf); - return NULL; - } - - for (p = postbuf, i = 0; i <= len; i++) - { - if (postbuf[i] == '=') - { - postbuf[i] = 0; - - for (field = 0; field < (n_fields * 2); field += 2) - { - if (!strcmp(p, fields[field])) - { - fields[field + 1] = postbuf + i + 1; - found++; - } - } - } - else if (postbuf[i] == '&' || postbuf[i] == '\0') - { - postbuf[i] = 0; - - if (found >= n_fields) - break; - - p = postbuf + i + 1; - } - } - - for (field = 0; field < (n_fields * 2); field += 2) - { - if (!urldecode(fields[field + 1])) - { - free(postbuf); - return NULL; - } - } - - return postbuf; -} - -static char * -canonicalize_path(const char *path, size_t len) -{ - char *canonpath, *cp; - const char *p, *e; - - if (path == NULL || *path == '\0') - return NULL; - - canonpath = datadup(path, len); - - if (canonpath == NULL) - return NULL; - - /* normalize */ - for (cp = canonpath, p = path, e = path + len; p < e; ) { - if (*p != '/') - goto next; - - /* skip repeating / */ - if ((p + 1 < e) && (p[1] == '/')) { - p++; - continue; - } - - /* /./ or /../ */ - if ((p + 1 < e) && (p[1] == '.')) { - /* skip /./ */ - if ((p + 2 >= e) || (p[2] == '/')) { - p += 2; - continue; - } - - /* collapse /x/../ */ - if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) { - while ((cp > canonpath) && (*--cp != '/')) - ; - - p += 3; - continue; - } - } - -next: - *cp++ = *p++; - } - - /* remove trailing slash if not root / */ - if ((cp > canonpath + 1) && (cp[-1] == '/')) - cp--; - else if (cp == canonpath) - *cp++ = '/'; - - *cp = '\0'; - - return canonpath; -} - static int response(bool success, const char *message) { @@ -916,79 +721,6 @@ lookup_executable(const char *cmd) return NULL; } -static char ** -parse_command(const char *cmdline) -{ - const char *p = cmdline, *s; - char **argv = NULL, *out; - size_t arglen = 0; - int argnum = 0; - bool esc; - - while (isspace(*cmdline)) - cmdline++; - - for (p = cmdline, s = p, esc = false; p; p++) { - if (esc) { - esc = false; - } - else if (*p == '\\' && p[1] != 0) { - esc = true; - } - else if (isspace(*p) || *p == 0) { - if (p > s) { - argnum += 1; - arglen += sizeof(char *) + (p - s) + 1; - } - - s = p + 1; - } - - if (*p == 0) - break; - } - - if (arglen == 0) - return NULL; - - argv = calloc(1, arglen + sizeof(char *)); - - if (!argv) - return NULL; - - out = (char *)argv + sizeof(char *) * (argnum + 1); - argv[0] = out; - - for (p = cmdline, s = p, esc = false, argnum = 0; p; p++) { - if (esc) { - esc = false; - *out++ = *p; - } - else if (*p == '\\' && p[1] != 0) { - esc = true; - } - else if (isspace(*p) || *p == 0) { - if (p > s) { - *out++ = ' '; - argv[++argnum] = out; - } - - s = p + 1; - } - else { - *out++ = *p; - } - - if (*p == 0) - break; - } - - argv[argnum] = NULL; - out[-1] = 0; - - return argv; -} - static int main_exec(int argc, char **argv) { diff --git a/util.c b/util.c new file mode 100644 index 000000000000..9eb7b485e5fa --- /dev/null +++ b/util.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" + +char ** +parse_command(const char *cmdline) +{ + const char *p = cmdline, *s; + char **argv = NULL, *out; + size_t arglen = 0; + int argnum = 0; + bool esc; + + while (isspace(*cmdline)) + cmdline++; + + for (p = cmdline, s = p, esc = false; p; p++) { + if (esc) { + esc = false; + } + else if (*p == '\\' && p[1] != 0) { + esc = true; + } + else if (isspace(*p) || *p == 0) { + if (p > s) { + argnum += 1; + arglen += sizeof(char *) + (p - s) + 1; + } + + s = p + 1; + } + + if (*p == 0) + break; + } + + if (arglen == 0) + return NULL; + + argv = calloc(1, arglen + sizeof(char *)); + + if (!argv) + return NULL; + + out = (char *)argv + sizeof(char *) * (argnum + 1); + argv[0] = out; + + for (p = cmdline, s = p, esc = false, argnum = 0; p; p++) { + if (esc) { + esc = false; + *out++ = *p; + } + else if (*p == '\\' && p[1] != 0) { + esc = true; + } + else if (isspace(*p) || *p == 0) { + if (p > s) { + *out++ = ' '; + argv[++argnum] = out; + } + + s = p + 1; + } + else { + *out++ = *p; + } + + if (*p == 0) + break; + } + + argv[argnum] = NULL; + out[-1] = 0; + + return argv; +} + +char * +postdecode(char **fields, int n_fields) +{ + const char *var; + char *p, *postbuf; + int i, field, found = 0; + ssize_t len = 0, rlen = 0, content_length = 0; + + var = getenv("CONTENT_TYPE"); + + if (!var || strncmp(var, "application/x-www-form-urlencoded", 33)) + return NULL; + + var = getenv("CONTENT_LENGTH"); + + if (!var) + return NULL; + + content_length = strtol(var, &p, 10); + + if (p == var || content_length <= 0 || content_length >= POST_LIMIT) + return NULL; + + postbuf = calloc(1, content_length + 1); + + if (postbuf == NULL) + return NULL; + + for (len = 0; len < content_length; ) + { + rlen = read(0, postbuf + len, content_length - len); + + if (rlen <= 0) + break; + + len += rlen; + } + + if (len < content_length) + { + free(postbuf); + return NULL; + } + + for (p = postbuf, i = 0; i <= len; i++) + { + if (postbuf[i] == '=') + { + postbuf[i] = 0; + + for (field = 0; field < (n_fields * 2); field += 2) + { + if (!strcmp(p, fields[field])) + { + fields[field + 1] = postbuf + i + 1; + found++; + } + } + } + else if (postbuf[i] == '&' || postbuf[i] == '\0') + { + postbuf[i] = 0; + + if (found >= n_fields) + break; + + p = postbuf + i + 1; + } + } + + for (field = 0; field < (n_fields * 2); field += 2) + { + if (!urldecode(fields[field + 1])) + { + free(postbuf); + return NULL; + } + } + + return postbuf; +} + +char * +datadup(const void *in, size_t len) +{ + char *out = malloc(len + 1); + + if (!out) + return NULL; + + memcpy(out, in, len); + + *(out + len) = 0; + + return out; +} + +char * +canonicalize_path(const char *path, size_t len) +{ + char *canonpath, *cp; + const char *p, *e; + + if (path == NULL || *path == '\0') + return NULL; + + canonpath = datadup(path, len); + + if (canonpath == NULL) + return NULL; + + /* normalize */ + for (cp = canonpath, p = path, e = path + len; p < e; ) { + if (*p != '/') + goto next; + + /* skip repeating / */ + if ((p + 1 < e) && (p[1] == '/')) { + p++; + continue; + } + + /* /./ or /../ */ + if ((p + 1 < e) && (p[1] == '.')) { + /* skip /./ */ + if ((p + 2 >= e) || (p[2] == '/')) { + p += 2; + continue; + } + + /* collapse /x/../ */ + if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) { + while ((cp > canonpath) && (*--cp != '/')) + ; + + p += 3; + continue; + } + } + +next: + *cp++ = *p++; + } + + /* remove trailing slash if not root / */ + if ((cp > canonpath + 1) && (cp[-1] == '/')) + cp--; + else if (cp == canonpath) + *cp++ = '/'; + + *cp = '\0'; + + return canonpath; +} + +bool +urldecode(char *buf) +{ + char *c, *p; + + if (!buf || !*buf) + return true; + +#define hex(x) \ + (((x) <= '9') ? ((x) - '0') : \ + (((x) <= 'F') ? ((x) - 'A' + 10) : \ + ((x) - 'a' + 10))) + + for (c = p = buf; *p; c++) + { + if (*p == '%') + { + if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2))) + return false; + + *c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2))); + + p += 3; + } + else if (*p == '+') + { + *c = ' '; + p++; + } + else + { + *c = *p++; + } + } + + *c = 0; + + return true; +} diff --git a/util.h b/util.h new file mode 100644 index 000000000000..0001195df38a --- /dev/null +++ b/util.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#define POST_LIMIT 131072 + +char** parse_command(const char *cmdline); +char* postdecode(char **fields, int n_fields); +char* canonicalize_path(const char *path, size_t len); +bool urldecode(char *buf); +char* datadup(const void *in, size_t len);