2 f224a3fe 2021-08-24 xhr * Copyright (c) 2021 Matthias Schmidt <xhr@giessen.ccc.de>
4 f224a3fe 2021-08-24 xhr * Permission to use, copy, modify, and distribute this software for any
5 f224a3fe 2021-08-24 xhr * purpose with or without fee is hereby granted, provided that the above
6 f224a3fe 2021-08-24 xhr * copyright notice and this permission notice appear in all copies.
8 f224a3fe 2021-08-24 xhr * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 f224a3fe 2021-08-24 xhr * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 f224a3fe 2021-08-24 xhr * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 f224a3fe 2021-08-24 xhr * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 f224a3fe 2021-08-24 xhr * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 f224a3fe 2021-08-24 xhr * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 f224a3fe 2021-08-24 xhr * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 f224a3fe 2021-08-24 xhr #include <ctype.h>
18 f224a3fe 2021-08-24 xhr #include <limits.h>
19 f224a3fe 2021-08-24 xhr #include <string.h>
21 f224a3fe 2021-08-24 xhr #include "log.h"
22 f224a3fe 2021-08-24 xhr #include "twind.h"
24 f224a3fe 2021-08-24 xhr char hex_to_int(char);
25 f224a3fe 2021-08-24 xhr char* uridecode(const char *);
28 f224a3fe 2021-08-24 xhr * The following two functions are from https://geekhideout.com/urlcode.shtml
29 f224a3fe 2021-08-24 xhr * and provided without license restrictions
31 f224a3fe 2021-08-24 xhr char hex_to_int(char ch) {
32 f224a3fe 2021-08-24 xhr return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
36 f224a3fe 2021-08-24 xhr uridecode(const char *request)
38 f224a3fe 2021-08-24 xhr char *temp = xmalloc(strlen(request) + 1);
39 f224a3fe 2021-08-24 xhr const char *p;
46 f224a3fe 2021-08-24 xhr if (*p == '%') {
47 f224a3fe 2021-08-24 xhr if (p[1] && p[2]) {
48 f224a3fe 2021-08-24 xhr *pt++ = hex_to_int(p[1]) << 4 | hex_to_int(p[2]);
51 f224a3fe 2021-08-24 xhr } else if (*p == '+') {
64 f224a3fe 2021-08-24 xhr get_path_from_request(char *request, char *finalpath)
66 f224a3fe 2021-08-24 xhr char hostname[MAXREQLEN];
67 f224a3fe 2021-08-24 xhr char localpath[MAXREQLEN];
68 f224a3fe 2021-08-24 xhr char temp[MAXREQLEN];
69 f224a3fe 2021-08-24 xhr char *p, *decoded_request;
70 f224a3fe 2021-08-24 xhr int pos = 0, ret;
72 f224a3fe 2021-08-24 xhr memset(hostname, 0, sizeof(hostname));
73 f224a3fe 2021-08-24 xhr memset(localpath, 0, sizeof(localpath));
74 f224a3fe 2021-08-24 xhr memset(temp, 0, sizeof(temp));
78 f224a3fe 2021-08-24 xhr if ((p = strchr(request, '\r')) == NULL) {
79 f224a3fe 2021-08-24 xhr log_info("\\r missing from request, abort processing");
83 f224a3fe 2021-08-24 xhr *p = '\0'; /* Strip \r\n */
86 f224a3fe 2021-08-24 xhr if (strncmp(p, "gemini://", 9) != 0) {
87 f224a3fe 2021-08-24 xhr log_info("Gemini scheme missing, abort processing");
90 f224a3fe 2021-08-24 xhr memmove(request, p + 9, strlen(request) + 1 - 9);
92 f224a3fe 2021-08-24 xhr decoded_request = uridecode(request);
94 f224a3fe 2021-08-24 xhr /* save hostname */
95 f224a3fe 2021-08-24 xhr if ((p = strchr(decoded_request, '/')) != NULL)
96 f224a3fe 2021-08-24 xhr snprintf(hostname, strlen(decoded_request) - strlen(p)+1, "%s",
97 f224a3fe 2021-08-24 xhr decoded_request);
99 f224a3fe 2021-08-24 xhr snprintf(hostname, strlen(decoded_request)+1, "%s", decoded_request);
101 f224a3fe 2021-08-24 xhr /* Strip possible port (e.g. :1965) from hostname */
102 f224a3fe 2021-08-24 xhr if ((p = strrchr(hostname, ':')) != NULL) {
103 f224a3fe 2021-08-24 xhr pos = strlen(hostname) - strlen(p);
104 f224a3fe 2021-08-24 xhr if (pos < 0 || pos > _POSIX_HOST_NAME_MAX)
105 f224a3fe 2021-08-24 xhr fatalx("pos while shorten hostname out of range");
106 f224a3fe 2021-08-24 xhr hostname[pos] = '\0';
109 f224a3fe 2021-08-24 xhr /* Remove ../ for security reasons */
110 f224a3fe 2021-08-24 xhr while ((p = strstr(decoded_request, "/..")) != NULL) {
111 f224a3fe 2021-08-24 xhr memmove(decoded_request, p + 3, strlen(p) + 1 - 3);
114 f224a3fe 2021-08-24 xhr if ((p = strchr(decoded_request, '/')) != NULL) {
115 f224a3fe 2021-08-24 xhr /* Save all after the first / in localpath */
116 f224a3fe 2021-08-24 xhr snprintf(localpath, strlen(decoded_request), "%s", p+1);
117 f224a3fe 2021-08-24 xhr if (strlen(localpath) == 0) {
119 f224a3fe 2021-08-24 xhr * If the request is 'example.com/', localpart will be empty. In this case
120 f224a3fe 2021-08-24 xhr * write the default to it.
122 f224a3fe 2021-08-24 xhr sprintf(localpath, "index.gmi");
125 f224a3fe 2021-08-24 xhr /* There is no slash in the request, so assume index.gmi */
126 f224a3fe 2021-08-24 xhr sprintf(localpath, "index.gmi");
130 f224a3fe 2021-08-24 xhr *We do not need to take the base dir aka /var/db/gemini into account
131 f224a3fe 2021-08-24 xhr * since we already chroot() to _PATH_TWIND_CHROOT .
133 f224a3fe 2021-08-24 xhr * Here, a string truncation could happen. This can be implemented
134 f224a3fe 2021-08-24 xhr * better! XXX FIXME
136 f224a3fe 2021-08-24 xhr snprintf(finalpath, MAXREQLEN, "%s/%s", hostname, localpath);
138 f224a3fe 2021-08-24 xhr /* Check if the wanted path exists and if it's a directory */
139 f224a3fe 2021-08-24 xhr ret = check_gemini_file(finalpath);
140 f224a3fe 2021-08-24 xhr if (ret < 0) {
141 f224a3fe 2021-08-24 xhr log_debug("%s not found", finalpath);
142 f224a3fe 2021-08-24 xhr free(decoded_request);
144 f224a3fe 2021-08-24 xhr } else if (ret == 1) {
145 f224a3fe 2021-08-24 xhr log_debug("%s is a directory", finalpath);
146 f224a3fe 2021-08-24 xhr /* Auto append index.gmi if destination is a directory */
147 f224a3fe 2021-08-24 xhr snprintf(temp, MAXREQLEN, "%s", finalpath);
148 f224a3fe 2021-08-24 xhr snprintf(finalpath, MAXREQLEN, "%s/index.gmi", temp);
151 f224a3fe 2021-08-24 xhr log_debug("Got request for %s on server %s -> %s",
152 f224a3fe 2021-08-24 xhr localpath, hostname, finalpath);
154 f224a3fe 2021-08-24 xhr /* decoded_request is no longer used, so it can be freed */
155 f224a3fe 2021-08-24 xhr free(decoded_request);