From 000553d8c5ff70e8eaedab0ba9604270c96eb2e5 Mon Sep 17 00:00:00 2001 From: Laslo Hunhold Date: Tue, 11 Jul 2017 12:34:55 +0200 Subject: [PATCH] Add vhost support As given in the config, we match a regex of hosts to a canonical host which points to an internal directory. Regexes are compiled on initialization, so we can error out early. The rest is just modifications to use relative directories rather than absolute ones, as we chdir() into the vhost directories dynamically. Given we normalize the targets beforehand, there is no danger of malformed requests escaping the vhost-context. --- config.def.h | 13 ++++++++++- quark.c | 62 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/config.def.h b/config.def.h index 863add9..563fcb9 100644 --- a/config.def.h +++ b/config.def.h @@ -2,14 +2,25 @@ static const char *host = "localhost"; static const char *port = "80"; static const char *servedir = "."; static const char *docindex = "index.html"; -static int listdirs = 1; + static const char *user = "nobody"; static const char *group = "nogroup"; + +static int listdirs = 1; +static int vhosts = 0; static const int maxnprocs = 512; #define HEADER_MAX 4096 #define FIELD_MAX 200 +static const struct { + char *name; + char *regex; + char *dir; +} vhost[] = { + { "example.org", "^(www.)example.org$", "/example.org" }, +}; + static const struct { char *ext; char *type; diff --git a/quark.c b/quark.c index 67b696a..8bf94ff 100644 --- a/quark.c +++ b/quark.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,10 @@ char *argv0; #undef MIN #define MIN(x,y) ((x) < (y) ? (x) : (y)) +#undef LEN +#define LEN(x) (sizeof (x) / sizeof *(x)) +#undef RELPATH +#define RELPATH(x) ((!*(x) || !strcmp(x, "/")) ? "." : ((x) + 1)) #define TIMESTAMP_LEN 30 @@ -97,6 +102,9 @@ static char *status_str[] = { [S_VERSION_NOT_SUPPORTED] = "HTTP Version not supported", }; +/* vhost regex compilate */ +static regex_t vhost_regex[LEN(vhost)]; + long long strtonum(const char *, long long, long long, const char **); static char * @@ -555,6 +563,23 @@ sendresponse(int fd, struct request *r) char *p, *q, *mime; const char *err; + /* match vhost */ + if (vhosts) { + for (i = 0; i < LEN(vhost); i++) { + if (!regexec(&vhost_regex[i], r->field[REQ_HOST], 0, + NULL, 0)) { + break; + } + } + if (i < LEN(vhost)) { + /* switch to vhost directory */ + if (chdir(vhost[i].dir) < 0) { + return sendstatus(fd, (errno == EACCES) ? + S_FORBIDDEN : S_NOT_FOUND); + } + } + } + /* normalize target */ memcpy(realtarget, r->target, sizeof(realtarget)); if (normabspath(realtarget)) { @@ -567,7 +592,7 @@ sendresponse(int fd, struct request *r) } /* stat the target */ - if (stat(realtarget, &st) < 0) { + if (stat(RELPATH(realtarget), &st) < 0) { return sendstatus(fd, (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND); } @@ -583,8 +608,9 @@ sendresponse(int fd, struct request *r) } } - /* redirect if targets differ */ - if (strcmp(r->target, realtarget)) { + /* redirect if targets differ or host is non-canonical */ + if (strcmp(r->target, realtarget) || (vhosts && r->field[REQ_HOST][0] && + strcmp(r->field[REQ_HOST], vhost[i].name))) { /* do we need to add a port to the Location? */ hasport = strcmp(port, "80"); @@ -609,7 +635,8 @@ sendresponse(int fd, struct request *r) S_MOVED_PERMANENTLY, status_str[S_MOVED_PERMANENTLY], timestamp(time(NULL), t), ipv6host ? "[" : "", - r->field[REQ_HOST][0] ? r->field[REQ_HOST] : host, + r->field[REQ_HOST][0] ? (vhosts && i < LEN(vhost)) ? + vhost[i].name : r->field[REQ_HOST] : host, ipv6host ? "]" : "", hasport ? ":" : "", hasport ? port : "", tmptarget) < 0) { return S_REQUEST_TIMEOUT; @@ -626,12 +653,12 @@ sendresponse(int fd, struct request *r) } /* stat the docindex, which must be a regular file */ - if (stat(realtarget, &st) < 0 || !S_ISREG(st.st_mode)) { + if (stat(RELPATH(realtarget), &st) < 0 || !S_ISREG(st.st_mode)) { if (listdirs) { /* remove index suffix and serve dir */ realtarget[strlen(realtarget) - strlen(docindex)] = '\0'; - return senddir(fd, realtarget, r); + return senddir(fd, RELPATH(realtarget), r); } else { /* reject */ if (!S_ISREG(st.st_mode) || errno == EACCES) { @@ -721,7 +748,7 @@ sendresponse(int fd, struct request *r) } } - return sendfile(fd, realtarget, r, &st, mime, lower, upper); + return sendfile(fd, RELPATH(realtarget), r, &st, mime, lower, upper); } static void @@ -782,14 +809,14 @@ serve(int insock) inet_ntop(AF_INET, &(((struct sockaddr_in *)&in_sa)->sin_addr), inip4, sizeof(inip4)); - printf("%s\t%s\t%d\t%s\n", tstmp, inip4, - status, r.target); + printf("%s\t%s\t%d\t%s\t%s\n", tstmp, inip4, + status, r.field[REQ_HOST], r.target); } else { inet_ntop(AF_INET6, &(((struct sockaddr_in6*)&in_sa)->sin6_addr), inip6, sizeof(inip6)); - printf("%s\t%s\t%d\t%s\n", tstmp, inip6, - status, r.target); + printf("%s\t%s\t%d\t%s\t%s\n", tstmp, inip6, + status, r.field[REQ_HOST], r.target); } /* clean up and finish */ @@ -902,7 +929,7 @@ main(int argc, char *argv[]) struct passwd *pwd = NULL; struct group *grp = NULL; struct rlimit rlim; - int insock; + int i, insock; char *udsname = NULL; ARGBEGIN { @@ -941,6 +968,17 @@ main(int argc, char *argv[]) usage(); } + /* compile and check the supplied vhost regexes */ + if (vhosts) { + for (i = 0; i < LEN(vhost); i++) { + if (regcomp(&vhost_regex[i], vhost[i].regex, + REG_ICASE | REG_NOSUB)) { + die("%s: regcomp '%s': invalid regex\n", argv0, + vhost[i].regex); + } + } + } + /* reap children automatically */ if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) { fprintf(stderr, "%s: signal: Failed to set SIG_IGN on"