diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | browse.c | 71 | ||||
-rw-r--r-- | browse.h | 6 | ||||
-rw-r--r-- | cgi.h | 8 | ||||
-rw-r--r-- | config.h | 4 | ||||
-rw-r--r-- | error.h | 39 | ||||
-rw-r--r-- | http.c | 24 | ||||
-rw-r--r-- | http.h | 16 | ||||
-rw-r--r-- | main.c | 51 | ||||
-rw-r--r-- | url.h | 8 |
10 files changed, 113 insertions, 116 deletions
@@ -30,7 +30,7 @@ CC=cc CFLAGS_DEPS != pkg-config --cflags kcgi kcgi-html LDFLAGS_DEPS != pkg-config --libs kcgi kcgi-html -CFLAGS=-g -W -Wall -Wextra $(CFLAGS_DEPS) +CFLAGS=-g -W -Wall -Wextra -DLOG_INFO $(CFLAGS_DEPS) LDFLAGS=-static $(LDFLAGS_DEPS) all: main @@ -71,7 +71,7 @@ build_browse_url(struct kreq * r, char *url, size_t url_size, const char *file) return url_build(url, url_size, 4, r->pname, page_name, r->path, file); } -int +void browse(struct kreq * r) { struct dirent *dir; @@ -82,58 +82,53 @@ browse(struct kreq * r) struct khtmlreq html; /* check that the requested URL can be safely processed */ - if (strlen(r->path) >= URL_LENGTH_MAX) { - http_open(r, KHTTP_414); - return VAULT_URI_ERROR; - } - if (!check_request_path(r->path)) { - http_open(r, KHTTP_400); - return VAULT_URI_ERROR; - } + if (!check_request_path(r->path)) + http_exit(r, KHTTP_400, "browse: Invalid request path"); + if (strlen(r->path) >= URL_LENGTH_MAX) + http_exit(r, KHTTP_414, NULL); + /* list requested directory content */ url_size = url_build(current_dir, URL_LENGTH_MAX, 2, DATA_DIR, r->path); - if (url_size == 0) { - http_open(r, KHTTP_404); - return VAULT_IO_ERROR; - } else if (url_size >= URL_LENGTH_MAX) { - http_open(r, KHTTP_414); - return VAULT_URI_ERROR; - } + if (url_size == 0) + http_exit(r, KHTTP_404, "browse: Unable to build data path"); + if (url_size >= URL_LENGTH_MAX) + http_exit(r, KHTTP_414, NULL); data_dir = opendir(current_dir); - if (NULL == data_dir) { - http_open(r, KHTTP_404); - return VAULT_IO_ERROR; - } + if (NULL == data_dir) + http_exit(r, KHTTP_404, NULL); + http_open(r, KHTTP_200); - K_OK(khtml_open(&html, r, 0)); + K_OK(khtml_open(&html, r, 0), r); /* build basic html page */ - K_OK(khtml_elem(&html, KELEM_DOCTYPE)); - K_OK(khtml_elem(&html, KELEM_HEAD)); - K_OK(khtml_attr(&html, KELEM_META, KATTR_CHARSET, "utf-8", KATTR__MAX)); - K_OK(khtml_elem(&html, KELEM_HTML)); - K_OK(khtml_elem(&html, KELEM_BODY)); - K_OK(khtml_elem(&html, KELEM_P)); - K_OK(khtml_puts(&html, "/")); - K_OK(khtml_closeelem(&html, 1)); + K_OK(khtml_elem(&html, KELEM_DOCTYPE), r); + K_OK(khtml_elem(&html, KELEM_HEAD), r); + K_OK(khtml_attr(&html, KELEM_META, KATTR_CHARSET, "utf-8", KATTR__MAX), + r); + K_OK(khtml_elem(&html, KELEM_HTML), r); + K_OK(khtml_elem(&html, KELEM_BODY), r); + K_OK(khtml_elem(&html, KELEM_P), r); + K_OK(khtml_puts(&html, "/"), r); + K_OK(khtml_closeelem(&html, 1), r); - K_OK(khtml_elem(&html, KELEM_UL)); + K_OK(khtml_elem(&html, KELEM_UL), r); while ((dir = readdir(data_dir)) != NULL) { /* ignore special . and .. folders */ file_name = dir->d_name; if (strcmp(".", file_name) == 0 || strcmp("..", file_name) == 0) continue; url_size = build_browse_url(r, url, URL_LENGTH_MAX, file_name); - if (url_size == 0 || url_size >= URL_LENGTH_MAX) + if (url_size == 0 || url_size >= URL_LENGTH_MAX) { + kutil_warn(r, NULL, "browse: Detected URL overflow: %s", url); continue; - K_OK(khtml_elem(&html, KELEM_LI)); - K_OK(khtml_attr(&html, KELEM_A, KATTR_HREF, url, KATTR__MAX)); - K_OK(khtml_puts(&html, dir->d_name)); - K_OK(khtml_closeelem(&html, 2)); + } + K_OK(khtml_elem(&html, KELEM_LI), r); + K_OK(khtml_attr(&html, KELEM_A, KATTR_HREF, url, KATTR__MAX), + r); + K_OK(khtml_puts(&html, dir->d_name), r); + K_OK(khtml_closeelem(&html, 2), r); } - K_OK(khtml_close(&html)); + K_OK(khtml_close(&html), r); closedir(data_dir); - - return VAULT_SUCCESS; } @@ -36,9 +36,9 @@ #define BROWSE_URL "browse" /* - * browse page allows to browse through the folder hierarchy under DATA_DIR, - * returns 0 in case of success, an error code otherwise + * Print a browse page that allows to browse through the folder hierarchy. + * The KCGI request is required. */ -int browse (struct kreq *); +void browse(struct kreq *); #endif /* VAULT_BROWSE_H */ @@ -33,9 +33,9 @@ #include <kcgi.h> -#include "error.h" - -#define K_OK(kcgi_call) if(kcgi_call != KCGI_OK) \ - return VAULT_CGI_ERROR +#define K_OK(kcgi_call, r) \ +if(kcgi_call != KCGI_OK) { \ + kutil_warn(r, NULL, "Unexpected CGI error"); \ +} #endif /* VAULT_CGI_H */ @@ -32,6 +32,6 @@ #define VAULT_CONFIG_H #define DATA_DIR "/var/www/vault-data" +#define LOG_FILE "/var/log/vault.log" -#endif /* VAULT_CONFIG_H */ - +#endif /* VAULT_CONFIG_H */ diff --git a/error.h b/error.h deleted file mode 100644 index ce96418..0000000 --- a/error.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023, Vincent Douillet <vincent@vdouillet.fr> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef VAULT_ERROR_H -#define VAULT_ERROR_H - -#define VAULT_SUCCESS 0 -#define VAULT_CGI_ERROR 1 -#define VAULT_IO_ERROR 2 -#define VAULT_URI_ERROR 3 - -#endif /* VAULT_ERROR_H */ @@ -28,6 +28,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> #include "http.h" @@ -39,3 +41,25 @@ http_open(struct kreq * r, enum khttp code) khttp_head(r, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[r->mime]); khttp_body(r); } + +void +http_exit(struct kreq * r, enum khttp code, char *content,...) +{ + va_list args; + + printf("Status: %s\n", khttps[code]); + printf("Content-Type: text/plain\n"); + printf("\n"); + + if (content == NULL) + printf("%s", khttps[code]); + else { + va_start(args, content); + vprintf(content, args); + va_end(args); + } + + va_start(args, content); + kutil_verr(r, NULL, content, args); + va_end(args); +} @@ -34,8 +34,20 @@ #include <kcgi.h> /* - * Initialize headers and start the document body with the provided http code + * Initialize headers and start the document body with the provided http code. + * The KCGI request and khttp code are both required. */ -void http_open(struct kreq *, enum khttp); +void http_open(struct kreq *, enum khttp); + +/* + * Send a plain text error response. + * The KCGI request can be NULL. + * The khttp code is required. + * The error string can be NULL, in this case the http code is printed in plain + * text form instead. + * + * This function exits with EXIT_FAILURE. It never returns. + */ +void http_exit(struct kreq *, enum khttp, char *,...); #endif /* VAULT_HTTP_H */ @@ -28,11 +28,14 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <errno.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include "browse.h" #include "cgi.h" +#include "config.h" #include "http.h" enum page { @@ -47,46 +50,40 @@ static const char *const pages[PAGE__MAX] = { int main(void) { - int page_ret_val; + enum kcgi_err parse_err; struct kreq r; - K_OK(khttp_parse(&r, NULL, 0, pages, PAGE__MAX, PAGE_BROWSE)); + if (kutil_openlog(LOG_FILE) == 0) + http_exit(NULL, KHTTP_500, "Unable to open %s", LOG_FILE); + + parse_err = khttp_parse(&r, NULL, 0, pages, PAGE__MAX, PAGE_BROWSE); + if (parse_err != KCGI_OK) + http_exit(NULL, KHTTP_500, "Unable to parse request: %s", + kcgi_strerror(parse_err)); + + /* A bit of security cannot hurt */ + if (-1 == pledge("stdio rpath wpath cpath", NULL)) + http_exit(&r, KHTTP_500, "Pledge failed: %s", strerror(errno)); /* * Make sure basic request parameters are as expected : GET or POST, * valid page and HTML document */ - - if (r.method != KMETHOD_GET && r.method != KMETHOD_POST) { - http_open(&r, KHTTP_405); - khttp_free(&r); - return EXIT_SUCCESS; - } else if (r.mime != KMIME_TEXT_HTML) { - http_open(&r, KHTTP_406); /* Not Acceptable */ - khttp_free(&r); - return EXIT_SUCCESS; - } else if (r.page == PAGE__MAX) { - http_open(&r, KHTTP_404); - khttp_free(&r); - return EXIT_SUCCESS; - } - if (-1 == pledge("stdio rpath", NULL)) { - khttp_free(&r); - return EXIT_FAILURE; - } - /* - * TODO error happens here if(-1 == unveil(DATA_DIR, "r")) { ret_val - * = EXIT_FAILURE; goto FAIL_HTML; } - */ + if (r.method != KMETHOD_GET && r.method != KMETHOD_POST) + http_exit(&r, KHTTP_405, NULL); + if (r.mime != KMIME_TEXT_HTML) + http_exit(&r, KHTTP_406, NULL); /* Not Acceptable */ + if (r.page == PAGE__MAX) + http_exit(&r, KHTTP_404, NULL); switch (r.page) { case PAGE_BROWSE: - page_ret_val = browse(&r); + browse(&r); break; default: - abort(); + http_exit(&r, KHTTP_404, NULL); } khttp_free(&r); - return page_ret_val; + return EXIT_SUCCESS; } @@ -35,6 +35,14 @@ /* * Build an URL from the provided components. + * The first argument is the destination buffer where the URL will be written. + * It is required. + * The second argument is the size of the destination buffer. It is required. + * The third argument is the number of URL components provided in the following + * vargs. + * Returns the number of characters written to the destination buffer. If it + * is equal to the destination buffer size, it means the buffer was too small + * and the resulting URL has been truncated. */ size_t url_build(char *, size_t, int,...); |