diff options
author | the xhr <xhr@giessen.ccc.de> | 2021-08-24 21:22:29 +0200 |
---|---|---|
committer | the xhr <xhr@giessen.ccc.de> | 2021-08-24 21:22:29 +0200 |
commit | f224a3fe688bf2ae4c108d6b4359df4d8f03db99 (patch) | |
tree | 1d9d6fdce5671be7ae5754f51c6ef5b47bf7f0c1 /request.c | |
parent | 0f9f2d04f9c340c761a9c900fac1c225b5a05419 (diff) |
Initial commit of twind - a small and simple gemini daemon
Diffstat (limited to 'request.c')
-rw-r--r-- | request.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/request.c b/request.c new file mode 100644 index 0000000..609f368 --- /dev/null +++ b/request.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021 Matthias Schmidt <xhr@giessen.ccc.de> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <ctype.h> +#include <limits.h> +#include <string.h> + +#include "log.h" +#include "twind.h" + +char hex_to_int(char); +char* uridecode(const char *); + +/* + * The following two functions are from https://geekhideout.com/urlcode.shtml + * and provided without license restrictions + */ +char hex_to_int(char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +char * +uridecode(const char *request) +{ + char *temp = xmalloc(strlen(request) + 1); + const char *p; + char *pt; + + memset(temp, 0, strlen(request)+1); + + p = request; + pt = temp; + + while (*p) { + if (*p == '%') { + if (p[1] && p[2]) { + *pt++ = hex_to_int(p[1]) << 4 | hex_to_int(p[2]); + p += 2; + } + } else if (*p == '+') { + *pt++ = ' '; + } else { + *pt++ = *p; + } + p++; + } + *pt = '\0'; + + return temp; +} + +int +get_path_from_request(char *request, char *finalpath) +{ + char hostname[MAXREQLEN]; + char localpath[MAXREQLEN]; + char temp[MAXREQLEN]; + char *p, *decoded_request; + int pos = 0, ret; + + memset(hostname, 0, sizeof(hostname)); + memset(localpath, 0, sizeof(localpath)); + memset(temp, 0, sizeof(temp)); + + p = request; + + if ((p = strchr(request, '\r')) == NULL) { + log_info("\\r missing from request, abort processing"); + return -1; + } + + *p = '\0'; /* Strip \r\n */ + p = request; + + if (strncmp(p, "gemini://", 9) != 0) { + log_info("Gemini scheme missing, abort processing"); + return -1; + } + memmove(request, p + 9, strlen(request) + 1 - 9); + + decoded_request = uridecode(request); + + /* save hostname */ + if ((p = strchr(decoded_request, '/')) != NULL) + snprintf(hostname, strlen(decoded_request) - strlen(p)+1, "%s", + decoded_request); + else + snprintf(hostname, strlen(decoded_request)+1, "%s", decoded_request); + + /* Strip possible port (e.g. :1965) from hostname */ + if ((p = strrchr(hostname, ':')) != NULL) { + pos = strlen(hostname) - strlen(p); + if (pos < 0 || pos > _POSIX_HOST_NAME_MAX) + fatalx("pos while shorten hostname out of range"); + hostname[pos] = '\0'; + } + + /* Remove ../ for security reasons */ + while ((p = strstr(decoded_request, "/..")) != NULL) { + memmove(decoded_request, p + 3, strlen(p) + 1 - 3); + } + + if ((p = strchr(decoded_request, '/')) != NULL) { + /* Save all after the first / in localpath */ + snprintf(localpath, strlen(decoded_request), "%s", p+1); + if (strlen(localpath) == 0) { + /* + * If the request is 'example.com/', localpart will be empty. In this case + * write the default to it. + */ + sprintf(localpath, "index.gmi"); + } + } else { + /* There is no slash in the request, so assume index.gmi */ + sprintf(localpath, "index.gmi"); + } + + /* + *We do not need to take the base dir aka /var/db/gemini into account + * since we already chroot() to _PATH_TWIND_CHROOT . + * + * Here, a string truncation could happen. This can be implemented + * better! XXX FIXME + */ + snprintf(finalpath, MAXREQLEN, "%s/%s", hostname, localpath); + + /* Check if the wanted path exists and if it's a directory */ + ret = check_gemini_file(finalpath); + if (ret < 0) { + log_debug("%s not found", finalpath); + free(decoded_request); + return -2; + } else if (ret == 1) { + log_debug("%s is a directory", finalpath); + /* Auto append index.gmi if destination is a directory */ + snprintf(temp, MAXREQLEN, "%s", finalpath); + snprintf(finalpath, MAXREQLEN, "%s/index.gmi", temp); + } + + log_debug("Got request for %s on server %s -> %s", + localpath, hostname, finalpath); + + /* decoded_request is no longer used, so it can be freed */ + free(decoded_request); + + return 0; +} + |