First rework of path handling. - geomyidae - a small C-based gopherd (mirror) | |
git clone git://git.codemadness.org/geomyidae | |
Log | |
Files | |
Refs | |
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… |