| First rework of path handling. - geomyidae - A small C-based gopherd. | |
| git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfri… | |
| Log | |
| Files | |
| Refs | |
| Tags | |
| README | |
| LICENSE | |
| --- | |
| commit b12a77acd24fc170b1ad047986ffaf13592fb326 | |
| parent f66a8a67b9471909016d6f24ce93f39584130a67 | |
| Author: Christoph Lohmann <[email protected]> | |
| Date: Thu, 20 Jul 2023 06:30:24 +0200 | |
| First rework of path handling. | |
| * Renaming the gph functions. | |
| * Beware, still full of debug functions. | |
| Diffstat: | |
| M handlr.c | 112 +++++++++++++++++------------… | |
| M handlr.h | 25 ++++++++++++++++++++----- | |
| M ind.c | 133 ++++++++++++++++-------------… | |
| M ind.h | 41 ++++++++++++++++-------------… | |
| M index.gph | 4 +++- | |
| M main.c | 144 ++++++++++++++++++++++-------… | |
| 6 files changed, 277 insertions(+), 182 deletions(-) | |
| --- | |
| diff --git a/handlr.c b/handlr.c | |
| @@ -17,6 +17,7 @@ | |
| #include <dirent.h> | |
| #include <sys/wait.h> | |
| #include <errno.h> | |
| +#include <libgen.h> | |
| #include "ind.h" | |
| #include "arg.h" | |
| @@ -25,7 +26,7 @@ void | |
| handledir(int sock, char *path, char *port, char *base, char *args, | |
| char *sear, char *ohost, char *chost, char *bhost, int istls) | |
| { | |
| - char *pa, *file, *e, *par, *b; | |
| + char *pa, *file, *e, *par; | |
| struct dirent **dirent; | |
| int ndir, i, ret = 0; | |
| struct stat st; | |
| @@ -35,22 +36,25 @@ handledir(int sock, char *path, char *port, char *base, cha… | |
| USED(sear); | |
| USED(bhost); | |
| - pa = xstrdup(path); | |
| - e = pa + strlen(pa) - 1; | |
| - if (e > pa && e[0] == '/') | |
| - *e = '\0'; | |
| + printf("handledir:\n"); | |
| + printf("sock = %d; path = %s; port = %s; base = %s; args = %s;\n", | |
| + sock, path, port, base, args); | |
| + printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n", | |
| + sear, ohost, chost, bhost, istls); | |
| - par = xstrdup(pa); | |
| + pa = xstrdup(path); | |
| - b = strrchr(makebasepath(par, base), '/'); | |
| - if (b != NULL) { | |
| - *b = '\0'; | |
| + /* Is there any directory below the request? */ | |
| + if (strlen(pa+strlen(base)) > 1) { | |
| + par = xstrdup(pa+strlen(base)); | |
| + e = strrchr(par, '/'); | |
| + *e = '\0'; | |
| dprintf(sock, "1..\t%s\t%s\t%s\r\n", | |
| - makebasepath(par, base), ohost, port); | |
| + par, ohost, port); | |
| + free(par); | |
| } | |
| - free(par); | |
| - ndir = scandir(pa[0] ? pa : ".", &dirent, 0, alphasort); | |
| + ndir = scandir(pa, &dirent, 0, alphasort); | |
| if (ndir < 0) { | |
| perror("scandir"); | |
| free(pa); | |
| @@ -61,19 +65,21 @@ handledir(int sock, char *path, char *port, char *base, cha… | |
| continue; | |
| type = gettype(dirent[i]->d_name); | |
| - file = smprintf("%s%s%s", pa, | |
| - pa[0] == '/' && pa[1] == '\0' ? "" : "… | |
| + | |
| + file = smprintf("%s%s%s", | |
| + pa, | |
| + pa[strlen(pa)-1] == '/'? "" : "/", | |
| dirent[i]->d_name); | |
| + printf("handledir: smprintf file = %s\n", file); | |
| if (stat(file, &st) >= 0 && S_ISDIR(st.st_mode)) | |
| type = gettype("index.gph"); | |
| - e = makebasepath(file, base); | |
| ret = dprintf(sock, | |
| "%c%-50.50s %10s %16s\t%s\t%s\t%s\r\n", | |
| *type->type, | |
| dirent[i]->d_name, | |
| humansize(st.st_size), | |
| humantime(&(st.st_mtime)), | |
| - e, ohost, port); | |
| + file + strlen(base), ohost, port); | |
| free(file); | |
| } | |
| for (i = 0; i < ndir; i++) | |
| @@ -89,24 +95,30 @@ void | |
| handlegph(int sock, char *file, char *port, char *base, char *args, | |
| char *sear, char *ohost, char *chost, char *bhost, int istls) | |
| { | |
| - Indexs *act; | |
| + gphindex *act; | |
| int i, ret = 0; | |
| USED(args); | |
| USED(sear); | |
| USED(bhost); | |
| - act = scanfile(file); | |
| + printf("handlegph:\n"); | |
| + printf("sock = %d; file = %s; port = %s; base = %s; args = %s;\n", | |
| + sock, file, port, base, args); | |
| + printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n", | |
| + sear, ohost, chost, bhost, istls); | |
| + | |
| + act = gph_scanfile(file); | |
| if (act != NULL) { | |
| for (i = 0; i < act->num && ret >= 0; i++) | |
| - ret = printelem(sock, act->n[i], file, base, ohost, po… | |
| + ret = gph_printelem(sock, act->n[i], file, base, ohost… | |
| dprintf(sock, ".\r\n"); | |
| for (i = 0; i < act->num; i++) { | |
| - freeelem(act->n[i]); | |
| + gph_freeelem(act->n[i]); | |
| act->n[i] = NULL; | |
| } | |
| - freeindex(act); | |
| + gph_freeindex(act); | |
| } | |
| } | |
| @@ -135,21 +147,22 @@ void | |
| handlecgi(int sock, char *file, char *port, char *base, char *args, | |
| char *sear, char *ohost, char *chost, char *bhost, int istls) | |
| { | |
| - char *p, *path; | |
| + char *script, *path; | |
| USED(base); | |
| USED(port); | |
| - path = xstrdup(file); | |
| - p = strrchr(path, '/'); | |
| - if (p != NULL) | |
| - p[1] = '\0'; | |
| - else { | |
| - free(path); | |
| - path = NULL; | |
| - } | |
| + printf("handlecgi:\n"); | |
| + printf("sock = %d; file = %s; port = %s; base = %s; args = %s;\n", | |
| + sock, file, port, base, args); | |
| + printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n", | |
| + sear, ohost, chost, bhost, istls); | |
| - p = makebasepath(file, base); | |
| + path = xstrdup(file); | |
| + path = dirname(path); | |
| + script = path + strlen(path) + 1; | |
| + printf("path = %s\n", path); | |
| + printf("script = %s\n", script); | |
| if (sear == NULL) | |
| sear = ""; | |
| @@ -166,10 +179,10 @@ handlecgi(int sock, char *file, char *port, char *base, c… | |
| break; | |
| } | |
| - setcgienviron(p, file, port, base, args, sear, ohost, chost, | |
| + setcgienviron(script, file, port, base, args, sear, ohost, cho… | |
| bhost, istls); | |
| - if (execl(file, p, sear, args, ohost, port, | |
| + if (execl(file, script, sear, args, ohost, port, | |
| (char *)NULL) == -1) { | |
| perror("execl"); | |
| _exit(1); | |
| @@ -189,25 +202,24 @@ handledcgi(int sock, char *file, char *port, char *base, … | |
| char *sear, char *ohost, char *chost, char *bhost, int istls) | |
| { | |
| FILE *fp; | |
| - char *p, *path, *ln = NULL; | |
| + char *script, *path, *ln = NULL; | |
| size_t linesiz = 0; | |
| ssize_t n; | |
| int outsocks[2], ret = 0; | |
| - Elems *el; | |
| + gphelem *el; | |
| + | |
| + printf("handledcgi:\n"); | |
| + printf("sock = %d; file = %s; port = %s; base = %s; args = %s;\n", | |
| + sock, file, port, base, args); | |
| + printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n", | |
| + sear, ohost, chost, bhost, istls); | |
| if (socketpair(AF_LOCAL, SOCK_STREAM, 0, outsocks) < 0) | |
| return; | |
| path = xstrdup(file); | |
| - p = strrchr(path, '/'); | |
| - if (p != NULL) | |
| - p[1] = '\0'; | |
| - else { | |
| - free(path); | |
| - path = NULL; | |
| - } | |
| - | |
| - p = makebasepath(file, base); | |
| + path = dirname(path); | |
| + script = path + strlen(path) + 1; | |
| if (sear == NULL) | |
| sear = ""; | |
| @@ -225,10 +237,10 @@ handledcgi(int sock, char *file, char *port, char *base, … | |
| break; | |
| } | |
| - setcgienviron(p, file, port, base, args, sear, ohost, chost, | |
| + setcgienviron(script, file, port, base, args, sear, ohost, cho… | |
| bhost, istls); | |
| - if (execl(file, p, sear, args, ohost, port, | |
| + if (execl(file, script, sear, args, ohost, port, | |
| (char *)NULL) == -1) { | |
| perror("execl"); | |
| _exit(1); | |
| @@ -251,21 +263,21 @@ handledcgi(int sock, char *file, char *port, char *base, … | |
| if (ln[n - 1] == '\n') | |
| ln[--n] = '\0'; | |
| - el = getadv(ln); | |
| + el = gph_getadv(ln); | |
| if (el == NULL) | |
| continue; | |
| - ret = printelem(sock, el, file, base, ohost, port); | |
| - freeelem(el); | |
| + ret = gph_printelem(sock, el, file, base, ohost, port); | |
| + gph_freeelem(el); | |
| } | |
| if (ferror(fp)) | |
| perror("getline"); | |
| dprintf(sock, ".\r\n"); | |
| free(ln); | |
| - free(path); | |
| fclose(fp); | |
| wait(NULL); | |
| + free(path); | |
| break; | |
| } | |
| } | |
| diff --git a/handlr.h b/handlr.h | |
| @@ -9,16 +9,31 @@ | |
| /* | |
| * Handler API definition | |
| * | |
| - * path .... absolute path to the script | |
| + * Sample: /get/some/script/with/dirs////?key=value\tsearch what?\r\n | |
| + * * in /get/some/script is a file "index.dcgi" | |
| + * * request to bitreich.org on port 70 using TLS | |
| + * * base is in /var/gopher | |
| + * * client from 85.65.4.2 | |
| + * | |
| + * path/file absolute path to the script/directory, always starts with '/' | |
| + * Sample: /var/gopher/get/some/script/index.dcgi | |
| * port .... port which the script should use when defining menu items | |
| * (See -o and -p in geomyidae(8)) | |
| - * base .... base path of geomyidae ("" in case of chroot) | |
| - * args .... query string parsed after »script?query« | |
| - * sear .... search part of request (»selector\tsearch\r\n«) | |
| - * ohost ... host of geomiydae (See -h in geomyidae(8)) | |
| + * Sample: 70 | |
| + * base .... base path of geomyidae, never ends in '/', so chroot is '' | |
| + * Sample: /var/gopher | |
| + * args .... Gives all variable input from the selector in some way. | |
| + * Sample: /with/dirs////?key=value | |
| + * sear .... search part of request | |
| + * Sample: search what? | |
| + * ohost ... host of geomyidae (See -h in geomyidae(8)) | |
| + * Sample: bitreich.org | |
| * chost ... IP of the client sending a request | |
| + * Sample: 85.65.4.2 | |
| * bhost ... server IP the server received the connection to | |
| + * Sample: 78.46.175.99 | |
| * istls ... set to 1, if TLS was used for thr request | |
| + * Sample: 1 | |
| */ | |
| void handledir(int sock, char *path, char *port, char *base, char *args, | |
| diff --git a/ind.c b/ind.c | |
| @@ -24,6 +24,7 @@ | |
| #include <arpa/inet.h> | |
| #include <sys/ioctl.h> | |
| #include <limits.h> | |
| +#include <errno.h> | |
| #define PAGE_SHIFT 12 | |
| #define PAGE_SIZE (1UL << PAGE_SHIFT) | |
| @@ -258,7 +259,7 @@ gettype(char *filename) | |
| } | |
| void | |
| -freeelem(Elems *e) | |
| +gph_freeelem(gphelem *e) | |
| { | |
| if (e != NULL) { | |
| if (e->e != NULL) { | |
| @@ -273,12 +274,12 @@ freeelem(Elems *e) | |
| } | |
| void | |
| -freeindex(Indexs *i) | |
| +gph_freeindex(gphindex *i) | |
| { | |
| if (i != NULL) { | |
| if (i->n != NULL) { | |
| for (;i->num > 0; i->num--) | |
| - freeelem(i->n[i->num - 1]); | |
| + gph_freeelem(i->n[i->num - 1]); | |
| free(i->n); | |
| } | |
| free(i); | |
| @@ -288,7 +289,7 @@ freeindex(Indexs *i) | |
| } | |
| void | |
| -addelem(Elems *e, char *s) | |
| +gph_addelem(gphelem *e, char *s) | |
| { | |
| e->num++; | |
| e->e = xrealloc(e->e, sizeof(char *) * e->num); | |
| @@ -297,23 +298,23 @@ addelem(Elems *e, char *s) | |
| return; | |
| } | |
| -Elems * | |
| -getadv(char *str) | |
| +gphelem * | |
| +gph_getadv(char *str) | |
| { | |
| char *b, *e, *o, *bo; | |
| - Elems *ret; | |
| + gphelem *ret; | |
| - ret = xcalloc(1, sizeof(Elems)); | |
| + ret = xcalloc(1, sizeof(gphelem)); | |
| if (strchr(str, '\t')) { | |
| - addelem(ret, "i"); | |
| - addelem(ret, "Happy helping ☃ here: You tried to " | |
| + gph_addelem(ret, "i"); | |
| + gph_addelem(ret, "Happy helping ☃ here: You tried to " | |
| "output a spurious TAB character. This will " | |
| "break gopher. Please review your scripts. " | |
| "Have a nice day!"); | |
| - addelem(ret, "Err"); | |
| - addelem(ret, "server"); | |
| - addelem(ret, "port"); | |
| + gph_addelem(ret, "Err"); | |
| + gph_addelem(ret, "server"); | |
| + gph_addelem(ret, "port"); | |
| return ret; | |
| } | |
| @@ -331,7 +332,7 @@ getadv(char *str) | |
| } | |
| *e = '\0'; | |
| e++; | |
| - addelem(ret, b); | |
| + gph_addelem(ret, b); | |
| b = e; | |
| bo = b; | |
| } | |
| @@ -339,7 +340,7 @@ getadv(char *str) | |
| e = strchr(b, ']'); | |
| if (e != NULL) { | |
| *e = '\0'; | |
| - addelem(ret, b); | |
| + gph_addelem(ret, b); | |
| } | |
| free(o); | |
| @@ -349,55 +350,55 @@ getadv(char *str) | |
| } | |
| /* Invalid entry: Give back the whole line. */ | |
| - freeelem(ret); | |
| - ret = xcalloc(1, sizeof(Elems)); | |
| + gph_freeelem(ret); | |
| + ret = xcalloc(1, sizeof(gphelem)); | |
| } | |
| - addelem(ret, "i"); | |
| + gph_addelem(ret, "i"); | |
| /* Jump over escape sequence. */ | |
| if (str[0] == '[' && str[1] == '|') | |
| str += 2; | |
| - addelem(ret, str); | |
| - addelem(ret, "Err"); | |
| - addelem(ret, "server"); | |
| - addelem(ret, "port"); | |
| + gph_addelem(ret, str); | |
| + gph_addelem(ret, "Err"); | |
| + gph_addelem(ret, "server"); | |
| + gph_addelem(ret, "port"); | |
| return ret; | |
| } | |
| void | |
| -addindexs(Indexs *idx, Elems *el) | |
| +gph_addindex(gphindex *idx, gphelem *el) | |
| { | |
| idx->num++; | |
| - idx->n = xrealloc(idx->n, sizeof(Elems) * idx->num); | |
| + idx->n = xrealloc(idx->n, sizeof(gphelem *) * idx->num); | |
| idx->n[idx->num - 1] = el; | |
| return; | |
| } | |
| -Indexs * | |
| -scanfile(char *fname) | |
| +gphindex * | |
| +gph_scanfile(char *fname) | |
| { | |
| char *ln = NULL; | |
| size_t linesiz = 0; | |
| ssize_t n; | |
| FILE *fp; | |
| - Indexs *ret; | |
| - Elems *el; | |
| + gphindex *ret; | |
| + gphelem *el; | |
| if (!(fp = fopen(fname, "r"))) | |
| return NULL; | |
| - ret = xcalloc(1, sizeof(Indexs)); | |
| + ret = xcalloc(1, sizeof(gphindex)); | |
| while ((n = getline(&ln, &linesiz, fp)) > 0) { | |
| if (ln[n - 1] == '\n') | |
| ln[--n] = '\0'; | |
| - el = getadv(ln); | |
| + el = gph_getadv(ln); | |
| if (el == NULL) | |
| continue; | |
| - addindexs(ret, el); | |
| + gph_addindex(ret, el); | |
| } | |
| if (ferror(fp)) | |
| perror("getline"); | |
| @@ -413,9 +414,9 @@ scanfile(char *fname) | |
| } | |
| int | |
| -printelem(int fd, Elems *el, char *file, char *base, char *addr, char *port) | |
| +gph_printelem(int fd, gphelem *el, char *file, char *base, char *addr, char *p… | |
| { | |
| - char *path, *p, *argbase, buf[PATH_MAX+1], *argp, *realbase; | |
| + char *path, *p, *argbase, buf[PATH_MAX+1], *argp, *realbase, *rpath; | |
| int len, blen; | |
| if (!strcmp(el->e[3], "server")) { | |
| @@ -432,10 +433,6 @@ printelem(int fd, Elems *el, char *file, char *base, char … | |
| * some URL and ignore various types that have different semantics, | |
| * do not point to some file or directory. | |
| */ | |
| - /* | |
| - * FUTURE: If ever special requests with no beginning '/' are used in | |
| - * geomyidae, this is the place to control this. | |
| - */ | |
| if ((el->e[2][0] != '\0' | |
| && el->e[2][0] != '/' /* Absolute Request. */ | |
| && el->e[0][0] != 'i' /* Informational item. */ | |
| @@ -446,45 +443,50 @@ printelem(int fd, Elems *el, char *file, char *base, char… | |
| && el->e[0][0] != 'T') && /* tn3270 */ | |
| !(el->e[0][0] == 'h' && !strncmp(el->e[2], "URL:", 4))) { | |
| path = file + strlen(base); | |
| - if ((p = strrchr(path, '/'))) | |
| - len = p - path; | |
| - else | |
| + | |
| + /* Strip off original gph file name. */ | |
| + if ((p = strrchr(path, '/'))) { | |
| + len = strlen(path) - strlen(basename(path)); | |
| + } else { | |
| len = strlen(path); | |
| + } | |
| /* Strip off arguments for realpath. */ | |
| argbase = strchr(el->e[2], '?'); | |
| - if (argbase != NULL) | |
| + if (argbase != NULL) { | |
| blen = argbase - el->e[2]; | |
| - else | |
| + } else { | |
| blen = strlen(el->e[2]); | |
| + } | |
| - snprintf(buf, sizeof(buf), "%s%.*s/%.*s", base, len, | |
| + /* | |
| + * Print everything together. Realpath will resolve it. | |
| + */ | |
| + snprintf(buf, sizeof(buf), "%s%.*s%.*s", base, len, | |
| path, blen, el->e[2]); | |
| - if ((path = realpath(buf, NULL)) && | |
| - (realbase = realpath(base, NULL)) && | |
| - !strncmp(realbase, path, strlen(realbase))) { | |
| - p = path + strlen(realbase); | |
| + if ((rpath = realpath(buf, NULL)) && | |
| + (realbase = realpath(*base? base : "/", NULL))… | |
| + !strncmp(realbase, rpath, strlen(realbase))) { | |
| + p = rpath + (*base? strlen(realbase) : 0); | |
| /* | |
| - * Do not forget to readd arguments which were | |
| + * Do not forget to re-add arguments which were | |
| * stripped off. | |
| */ | |
| - if (argbase != NULL) | |
| - argp = smprintf("%s%s", p[0]? p : "/", argbase… | |
| - else | |
| - argp = xstrdup(p[0]? p : "/"); | |
| + argp = smprintf("%s%s", *p? p : "/", argbase? argbase … | |
| free(el->e[2]); | |
| el->e[2] = argp; | |
| free(realbase); | |
| } | |
| - free(path); | |
| + if (rpath != NULL) | |
| + free(rpath); | |
| } | |
| if (dprintf(fd, "%.1s%s\t%s\t%s\t%s\r\n", el->e[0], el->e[1], el->e[2], | |
| el->e[3], el->e[4]) < 0) { | |
| - perror("printelem: dprintf"); | |
| + perror("printgphelem: dprintf"); | |
| return -1; | |
| } | |
| return 0; | |
| @@ -545,19 +547,26 @@ setcgienviron(char *file, char *path, char *port, char *b… | |
| setenv("GATEWAY_INTERFACE", "CGI/1.1", 1); | |
| /* TODO: Separate, if run like rest.dcgi. */ | |
| setenv("PATH_INFO", file, 1); | |
| + printf("PATH_INFO = %s\n", file); | |
| setenv("PATH_TRANSLATED", path, 1); | |
| + printf("PATH_TRANSLATED = %s\n", path); | |
| setenv("QUERY_STRING", args, 1); | |
| + printf("QUERY_STRING = %s\n", args); | |
| /* legacy compatibility */ | |
| setenv("SELECTOR", args, 1); | |
| + printf("SELECTOR = %s\n", args); | |
| setenv("REQUEST", args, 1); | |
| + printf("REQUEST = %s\n", args); | |
| setenv("REMOTE_ADDR", chost, 1); | |
| + printf("REMOTE_ADDR = %s\n", chost); | |
| /* | |
| * Don't do a reverse lookup on every call. Only do when needed, in | |
| * the script. The RFC allows us to set the IP to the value. | |
| */ | |
| setenv("REMOTE_HOST", chost, 1); | |
| + printf("REMOTE_HOST = %s\n", chost); | |
| /* Please do not implement identd here. */ | |
| unsetenv("REMOTE_IDENT"); | |
| unsetenv("REMOTE_USER"); | |
| @@ -569,9 +578,12 @@ setcgienviron(char *file, char *path, char *port, char *ba… | |
| */ | |
| setenv("REQUEST_METHOD", "GET", 1); | |
| setenv("SCRIPT_NAME", file, 1); | |
| + printf("SCRIPT_NAME = %s\n", file); | |
| setenv("SERVER_NAME", ohost, 1); | |
| + printf("SERVER_PORT = %s\n", port); | |
| setenv("SERVER_PORT", port, 1); | |
| setenv("SERVER_LISTEN_NAME", bhost, 1); | |
| + printf("SERVER_LISTEN_NAME = %s\n", bhost); | |
| if (istls) { | |
| setenv("SERVER_PROTOCOL", "gophers/1.0", 1); | |
| } else { | |
| @@ -580,8 +592,10 @@ setcgienviron(char *file, char *path, char *port, char *ba… | |
| setenv("SERVER_SOFTWARE", "geomyidae", 1); | |
| setenv("X_GOPHER_SEARCH", sear, 1); | |
| + printf("X_GOPHER_SEARCH = %s\n", sear); | |
| /* legacy compatibility */ | |
| setenv("SEARCHREQUEST", sear, 1); | |
| + printf("SEARCHREQUEST = %s\n", sear); | |
| if (istls) { | |
| setenv("GOPHERS", "on", 1); | |
| @@ -626,14 +640,3 @@ humantime(const time_t *clock) | |
| return buf; | |
| } | |
| -char * | |
| -makebasepath(char *path, char *base) | |
| -{ | |
| - if (!(base[0] == '/' && base[1] == '\0') && | |
| - strlen(path) > strlen(base)) { | |
| - return path + strlen(base); | |
| - } else { | |
| - return path; | |
| - } | |
| -} | |
| - | |
| diff --git a/ind.h b/ind.h | |
| @@ -10,18 +10,6 @@ | |
| extern int glfd; | |
| -typedef struct Elems Elems; | |
| -struct Elems { | |
| - char **e; | |
| - int num; | |
| -}; | |
| - | |
| -typedef struct Indexs Indexs; | |
| -struct Indexs { | |
| - Elems **n; | |
| - int num; | |
| -}; | |
| - | |
| typedef struct filetype filetype; | |
| struct filetype { | |
| char *end; | |
| @@ -31,20 +19,34 @@ struct filetype { | |
| }; | |
| filetype *gettype(char *filename); | |
| + | |
| +typedef struct gphelem gphelem; | |
| +struct gphelem { | |
| + char **e; | |
| + int num; | |
| +}; | |
| + | |
| +typedef struct gphindex gphindex; | |
| +struct gphindex { | |
| + gphelem **n; | |
| + int num; | |
| +}; | |
| + | |
| +gphindex *gph_scanfile(char *fname); | |
| +gphelem *gph_getadv(char *str); | |
| +int gph_printelem(int fd, gphelem *el, char *file, char *base, char *addr, cha… | |
| +void gph_addindex(gphindex *idx, gphelem *el); | |
| +void gph_addelem(gphelem *e, char *s); | |
| +void gph_freeindex(gphindex *i); | |
| +void gph_freeelem(gphelem *e); | |
| + | |
| void *xcalloc(size_t, size_t); | |
| void *xmalloc(size_t); | |
| void *xrealloc(void *, size_t); | |
| char *xstrdup(const char *str); | |
| int xsendfile(int, int); | |
| -Indexs *scanfile(char *fname); | |
| -Elems *getadv(char *str); | |
| int pendingbytes(int sock); | |
| void waitforpendingbytes(int sock); | |
| -int printelem(int fd, Elems *el, char *file, char *base, char *addr, char *por… | |
| -void addindexs(Indexs *idx, Elems *el); | |
| -void addelem(Elems *e, char *s); | |
| -void freeindex(Indexs *i); | |
| -void freeelem(Elems *e); | |
| char *smprintf(char *fmt, ...); | |
| char *reverselookup(char *host); | |
| void setcgienviron(char *file, char *path, char *port, char *base, | |
| @@ -52,7 +54,6 @@ void setcgienviron(char *file, char *path, char *port, char *… | |
| char *bhost, int istls); | |
| char *humansize(off_t n); | |
| char *humantime(const time_t *clock); | |
| -char *makebasepath(char *path, char *base); | |
| #endif | |
| diff --git a/index.gph b/index.gph | |
| @@ -3,9 +3,11 @@ tcomment (old style comment) | |
| [1|R-36|/|server|port] | |
| [0|file - comment|/file.txt|server|port] | |
| [h|http://www.heise.de|URL:http://www.heise.de|server|port] | |
| -[0|some \| escape and [ special characters ] test|error|server|port] | |
| +[0|some \| escape and [ special characters ] test|Err|server|port] | |
| [9|binary data file|/file.dat|server|port] | |
| [9|unclosed entry|/file.dat|server|port | |
| +[9|some relative path with args|./legacy/caps.txt?key=value|server|port] | |
| +[9|some other relative path|legacy/caps.txt|server|port] | |
| [|empty type||server|port] | |
| [|Escape something, [| is skipped. | |
| some invalid line | |
| diff --git a/main.c b/main.c | |
| @@ -59,7 +59,7 @@ int nlistfds = 0; | |
| char *argv0; | |
| char stdbase[] = "/var/gopher"; | |
| char *stdport = "70"; | |
| -char *indexf[] = {"/index.gph", "/index.cgi", "/index.dcgi", "/index.bob", "/i… | |
| +char *indexf[] = {"index.gph", "index.cgi", "index.dcgi", "index.bob", "index.… | |
| char *nocgierr = "3Sorry, execution of the token '%s' was requested, but this " | |
| "is disabled in the server configuration.\tErr" | |
| "\tlocalhost\t70\r\n"; | |
| @@ -83,7 +83,7 @@ char *htredir = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| " </body>\n" | |
| "</html>\n"; | |
| char *selinval ="3Happy helping ☃ here: " | |
| - "Sorry, your selector does not start with / or contains '..'. " | |
| + "Sorry, your selector does contains '..'. " | |
| "That's illegal here.\tErr\tlocalhost\t70\r\n.\r\n\r\n"; | |
| int | |
| @@ -142,12 +142,20 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| int len = 0, fd, i, maxrecv, pathfallthrough = 0; | |
| filetype *type; | |
| + printf("handlerequest:\n"); | |
| + printf("sock = %d; req = '%s';\n", sock, req); | |
| + printf("rlen = %d; base = '%s'; ohost = '%s'; port = %s;\n", rlen, | |
| + base, ohost, port); | |
| + printf("clienth = %s; clientp = %s; serverh = %s; serverp = %s;\n", | |
| + clienth, clientp, serverh, serverp); | |
| + printf("nocgi = %d; istls = %d;\n", nocgi, istls); | |
| + | |
| if (!istls) { | |
| /* | |
| * If sticky bit is set on base dir and encryption is not | |
| * used, do not serve. | |
| */ | |
| - if (stat(base, &dir) == -1) | |
| + if (stat(*base? base : "/", &dir) == -1) | |
| return; | |
| if (dir.st_mode & S_ISVTX) { | |
| dprintf(sock, tlserr, recvc); | |
| @@ -176,6 +184,7 @@ handlerequest(int sock, char *req, int rlen, char *base, ch… | |
| c = strchr(recvb, '\n'); | |
| if (c) | |
| c[0] = '\0'; | |
| + | |
| sear = strchr(recvb, '\t'); | |
| if (sear != NULL) { | |
| *sear++ = '\0'; | |
| @@ -228,29 +237,32 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| * selectors. | |
| */ | |
| + /* Strip off the arguments of req?args style. */ | |
| c = strchr(recvb, '?'); | |
| if (c != NULL) { | |
| *c++ = '\0'; | |
| - snprintf(args, sizeof(args), "%s", c); | |
| + snprintf(args, sizeof(args), "?%s", c); | |
| } | |
| + printf("args = %s\n", args); | |
| + printf("recvb = %s\n", recvb); | |
| - if (recvb[0] == '\0') { | |
| - recvb[0] = '/'; | |
| - recvb[1] = '\0'; | |
| + /* Strip '/' at the end of the request. */ | |
| + for (c = recvb + strlen(recvb) - 1; c >= recvb && c[0] == '/'; c--) { | |
| + /* Prepend to args. */ | |
| + snprintf(args, sizeof(args), "/%s", args); | |
| + c[0] = '\0'; | |
| } | |
| + printf("args = %s\n", args); | |
| - /* | |
| - * Do not allow requests not beginning with '/' or which contain | |
| - * "..". | |
| - */ | |
| - if (recvb[0] != '/' || strstr(recvb, "..")){ | |
| + /* Do not allow requests including "..". */ | |
| + if (strstr(recvb, "..")) { | |
| dprintf(sock, "%s", selinval); | |
| return; | |
| } | |
| - /* append base to request path (always starting with /), if base is a … | |
| - if (snprintf(path, sizeof(path), "%s%s", | |
| - base[0] == '/' && base[1] == '\0' ? "" : base, | |
| + printf("recvb = %s\n", recvb); | |
| + if (snprintf(path, sizeof(path), "%s%s%s", base, | |
| + (*recvb != '/')? "/" : "", | |
| recvb) > sizeof(path)) { | |
| if (loglvl & ERRORS) { | |
| logentry(clienth, clientp, recvc, | |
| @@ -259,6 +271,8 @@ handlerequest(int sock, char *req, int rlen, char *base, ch… | |
| dprintf(sock, toolongerr, recvc); | |
| return; | |
| } | |
| + /* path is now always at least '/' */ | |
| + printf("path = %s\n", path); | |
| fd = -1; | |
| /* | |
| @@ -270,33 +284,54 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| * $args = $rest_of_path + "?" + $args | |
| */ | |
| if (stat(path, &dir) == -1) { | |
| + printf("Not found. Try backtraversal.\n"); | |
| memmove(argsc, args, strlen(args)); | |
| snprintf(path, sizeof(path), "%s", base); | |
| - recvbp = recvb + 1; | |
| + recvbp = recvb; | |
| + | |
| + /* | |
| + * Walk into the selector until some directory or file | |
| + * does not exist. Then reconstruct the args, selector | |
| + * etc. | |
| + */ | |
| while (recvbp != NULL) { | |
| - sep = strsep(&recvbp, "/"); | |
| + /* Traverse multiple / in selector. */ | |
| + for (sep = recvbp; sep != recvbp && sep != recvbp+1; | |
| + sep = strsep(&recvbp, "/")); | |
| + printf("traversal directory = %s\n", sep); | |
| + | |
| + /* Append found directory to path. */ | |
| snprintf(path+strlen(path), sizeof(path)-strlen(path), | |
| "/%s", sep); | |
| + /* path is now always at least '/' */ | |
| + printf("full traversal path = %s\n", path); | |
| + | |
| if (stat(path, &dir) == -1) { | |
| + /* | |
| + * Current try was not found. Go back one | |
| + * step and finish. | |
| + */ | |
| c = strrchr(path, '/'); | |
| if (c != NULL) { | |
| *c++ = '\0'; | |
| snprintf(args, sizeof(args), | |
| - "/%s%s%s%s%s", | |
| + "/%s%s%s%s", | |
| c, | |
| (recvbp != NULL)? "/" : "", | |
| (recvbp != NULL)? recvbp : "", | |
| - (argsc[0] != '\0')? "?" : "", | |
| (argsc[0] != '\0')? argsc : "" | |
| ); | |
| + printf("args = %s\n", args); | |
| } | |
| /* path fallthrough */ | |
| pathfallthrough = 1; | |
| + printf("pathfallthrough = 1\n"); | |
| break; | |
| } | |
| } | |
| } | |
| + printf("path = %s\n", path); | |
| if (stat(path, &dir) != -1) { | |
| /* | |
| * If sticky bit is set, only serve if this is encrypted. | |
| @@ -311,9 +346,11 @@ handlerequest(int sock, char *req, int rlen, char *base, c… | |
| } | |
| if (S_ISDIR(dir.st_mode)) { | |
| + printf("S_ISDIR\n"); | |
| for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]); | |
| i++) { | |
| - if (strlen(path) + strlen(indexf[i]) | |
| + len = strlen(path); | |
| + if (len + strlen(indexf[i]) + (path[len-1] == … | |
| >= sizeof(path)) { | |
| if (loglvl & ERRORS) { | |
| logentry(clienth, clientp, | |
| @@ -322,12 +359,18 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| } | |
| return; | |
| } | |
| - strncat(path, indexf[i], | |
| - sizeof(path)-strlen(path)-1); | |
| + sprintf(path, "%s%s%s", | |
| + path, | |
| + (path[len-1] == '/')? "" : "/", | |
| + indexf[i]); | |
| + printf("path index = %s\n", path); | |
| fd = open(path, O_RDONLY); | |
| if (fd >= 0) | |
| break; | |
| - path[strlen(path)-strlen(indexf[i])] = '\0'; | |
| + | |
| + /* Not found. Clear path from indexf. */ | |
| + printf("len = %d\n", len); | |
| + path[len] = '\0'; | |
| } | |
| } else { | |
| fd = open(path, O_RDONLY); | |
| @@ -342,10 +385,9 @@ handlerequest(int sock, char *req, int rlen, char *base, c… | |
| } | |
| } | |
| + /* Some file was opened. Serve it. */ | |
| if (fd >= 0) { | |
| close(fd); | |
| - if (loglvl & FILES) | |
| - logentry(clienth, clientp, recvc, "serving"); | |
| c = strrchr(path, '/'); | |
| if (c == NULL) | |
| @@ -359,8 +401,10 @@ handlerequest(int sock, char *req, int rlen, char *base, c… | |
| if (pathfallthrough && | |
| !(type->f == handledcgi || type->f == handlecg… | |
| dprintf(sock, notfounderr, recvc); | |
| - if (loglvl & ERRORS) | |
| - logentry(clienth, clientp, recvc, "not found"); | |
| + if (loglvl & ERRORS) { | |
| + logentry(clienth, clientp, recvc, | |
| + "handler in path fallthrough not allow… | |
| + } | |
| return; | |
| } | |
| @@ -369,14 +413,21 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| if (loglvl & ERRORS) | |
| logentry(clienth, clientp, recvc, "nocgi error… | |
| } else { | |
| + if (loglvl & FILES) | |
| + logentry(clienth, clientp, recvc, "serving"); | |
| + | |
| type->f(sock, path, port, base, args, sear, ohost, | |
| clienth, serverh, istls); | |
| } | |
| } else { | |
| - /* | |
| - * If we had to traverse the path, do not allow directory | |
| - * listings, only dynamic content. | |
| - */ | |
| + if (pathfallthrough && S_ISDIR(dir.st_mode)) { | |
| + if (loglvl & ERRORS) { | |
| + logentry(clienth, clientp, recvc, | |
| + "directory listing in traversal not al… | |
| + } | |
| + return; | |
| + } | |
| + | |
| if (!pathfallthrough && S_ISDIR(dir.st_mode)) { | |
| handledir(sock, path, port, base, args, sear, ohost, | |
| clienth, serverh, istls); | |
| @@ -791,7 +842,7 @@ main(int argc, char *argv[]) | |
| perror("chdir"); | |
| return 1; | |
| } | |
| - base = "/"; | |
| + base = ""; | |
| if (chroot(".") < 0) { | |
| perror("chroot"); | |
| return 1; | |
| @@ -801,9 +852,9 @@ main(int argc, char *argv[]) | |
| return 1; | |
| } | |
| - /* strip / at the end, except if it is "/" */ | |
| - for (p = base + strlen(base); p > base + 1 && p[-1] == '/'; --p) | |
| - p[-1] = '\0'; | |
| + /* strip / at the end of base */ | |
| + for (p = base + strlen(base) - 1; p >= base && p[0] == '/'; --p) | |
| + p[0] = '\0'; | |
| if (dropprivileges(gr, us) < 0) { | |
| perror("dropprivileges"); | |
| @@ -1063,21 +1114,32 @@ read_selector_again: | |
| close(tlssocks[tlsclientreader? 1 : 0]… | |
| do { | |
| if (tlsclientreader) { | |
| - shuflen = read(tlssock… | |
| + shuflen = read(tlssock… | |
| + shufbuf, | |
| + sizeof(shufbuf… | |
| } else { | |
| - shuflen = tls_read(tls… | |
| + shuflen = tls_read(tls… | |
| + shufbuf, | |
| + sizeof(shufbuf… | |
| } | |
| if (shuflen == -1 && errno == … | |
| continue; | |
| - for (shufpos = 0; shufpos < sh… | |
| + for (shufpos = 0; shufpos < sh… | |
| + shufpos += wle… | |
| if (tlsclientreader) { | |
| - wlen = tls_wri… | |
| + wlen = tls_wri… | |
| + shufbu… | |
| + shufle… | |
| if (wlen < 0) { | |
| - fprint… | |
| + fprint… | |
| + … | |
| + … | |
| return… | |
| } | |
| } else { | |
| - wlen = write(t… | |
| + wlen = write(t… | |
| + shufbu… | |
| + shufle… | |
| if (wlen < 0) { | |
| perror… | |
| return… |