quark

quark web server
git clone git://git.suckless.org/quark
Log | Files | Refs | LICENSE

commit 5e9814c84788a8c356eb16797c16715298f8e444
parent 18c6c2451d0dc67e33ddb7328c577b31dfb730fa
Author: Anselm R Garbe <anselm@garbe.us>
Date:   Sun, 28 Mar 2010 11:31:30 +0000

made simple CGI working, quark can be used to run werc
Diffstat:
Mconfig.def.h | 18+++++++++---------
Mquark.c | 148++++++++++++++++++++++++++++++++++++-------------------------------------------
2 files changed, 76 insertions(+), 90 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -1,14 +1,14 @@ /* quark configuration */ -static char *servername = "127.0.0.1"; -static char *serverport = "80"; -static char *docroot = "."; -static char *docindex = "index.html"; -static char *user = "nobody"; -static char *group = "nobody"; -static char *cgi_dir = "/var/www/werc-dev/bin"; -static char *cgi_script = "./werc.rc"; -static int cgi_mode = 0; +static const char servername[] = "127.0.0.1"; +static const char serverport[] = "80"; +static const char docroot[] = "."; +static const char docindex[] = "index.html"; +static const char user[] = "nobody"; +static const char group[] = "nobody"; +static const char cgi_dir[] = "/var/www/werc-dev/bin"; +static const char cgi_script[] = "./werc.rc"; +static const int cgi_mode = 0; static const MimeType servermimes[] = { { "html", "text/html; charset=UTF-8" }, diff --git a/quark.c b/quark.c @@ -18,7 +18,7 @@ #include <unistd.h> #define LENGTH(x) (sizeof x / sizeof x[0]) -#define MAXREQLEN 255 +#define MAXBUFLEN 1024 enum { GET = 4, @@ -39,6 +39,8 @@ static const char texthtml[] = "text/html"; static ssize_t writetext(const char *buf); static ssize_t writedata(const char *buf, size_t buflen); static void die(const char *errstr, ...); +void logmsg(const char *errstr, ...); +void logerrmsg(const char *errstr, ...); static void response(void); static void responsecgi(void); static void responsedir(void); @@ -54,9 +56,10 @@ static char *tstamp(void); static char location[256]; static int running = 1; -static char name[128]; -static char reqbuf[MAXREQLEN+1]; -static char respbuf[1024]; +static char host[NI_MAXHOST]; +static char reqbuf[MAXBUFLEN+1]; +static char resbuf[MAXBUFLEN+1]; +static char reqhost[256]; static int fd, cfd, reqtype; ssize_t @@ -65,7 +68,7 @@ writedata(const char *buf, size_t buf_len) { while(offset < buf_len) { if((r = write(cfd, buf + offset, buf_len - offset)) == -1) { - fprintf(stderr, "%s: client %s closed connection\n", tstamp(), name); + logerrmsg("%s: client %s closed connection\n", tstamp(), host); return -1; } offset += r; @@ -111,53 +114,53 @@ die(const char *errstr, ...) { int responsehdr(const char *status) { - if(snprintf(respbuf, sizeof respbuf, + if(snprintf(resbuf, MAXBUFLEN, "HTTP/1.1 %s\r\n" "Connection: close\r\n" "Date: %s\r\n" "Server: quark-"VERSION"\r\n", - status, tstamp()) >= sizeof respbuf) + status, tstamp()) >= MAXBUFLEN) { logerrmsg("snprintf failed, buffer size exceeded"); return -1; } - return writetext(respbuf); + return writetext(resbuf); } int responsecontentlen(off_t size) { - if(snprintf(respbuf, sizeof respbuf, + if(snprintf(resbuf, MAXBUFLEN, "Content-Length: %lu\r\n", - size) >= sizeof respbuf) + size) >= MAXBUFLEN) { logerrmsg("snprintf failed, buffer sizeof exceeded"); return -1; } - return writetext(respbuf); + return writetext(resbuf); } int responselocation(const char *location, const char *pathinfo) { - if(snprintf(respbuf, sizeof respbuf, + if(snprintf(resbuf, MAXBUFLEN, "Location: %s%s\r\n", - location, pathinfo) >= sizeof respbuf) + location, pathinfo) >= MAXBUFLEN) { logerrmsg("snprintf failed, buffer sizeof exceeded"); return -1; } - return writetext(respbuf); + return writetext(resbuf); } int responsecontenttype(const char *mimetype) { - if(snprintf(respbuf, sizeof respbuf, + if(snprintf(resbuf, MAXBUFLEN, "Content-Type: %s\r\n", - mimetype) >= sizeof respbuf) + mimetype) >= MAXBUFLEN) { logerrmsg("snprintf failed, buffer sizeof exceeded"); return -1; } - return writetext(respbuf); + return writetext(resbuf); } void @@ -166,7 +169,7 @@ responsefiledata(int fd, off_t size) { while(offset < size) if(sendfile(cfd, fd, &offset, size - offset) == -1) { - fprintf(stderr, "%s: sendfile failed on client %s: %s\n", tstamp(), name, strerror(errno)); + logerrmsg("sendfile failed on client %s: %s\n", host, strerror(errno)); return; } } @@ -180,7 +183,7 @@ responsefile(void) { stat(reqbuf, &st); if((ffd = open(reqbuf, O_RDONLY)) == -1) { - fprintf(stderr, "%s: %s requests unknown path %s\n", tstamp(), name, reqbuf); + logerrmsg("%s requests unknown path %s\n", host, reqbuf); if(responsehdr(HttpNotFound) != -1 && responsecontenttype(texthtml) != -1) ; @@ -225,13 +228,13 @@ responsedirdata(DIR *d) { while((e = readdir(d))) { if(e->d_name[0] == '.') /* ignore hidden files, ., .. */ continue; - if(snprintf(respbuf, sizeof respbuf, "<a href='%s%s'>%s</a><br>\r\n", - reqbuf, e->d_name, e->d_name) >= sizeof respbuf) + if(snprintf(resbuf, MAXBUFLEN, "<a href='%s%s'>%s</a><br>\r\n", + reqbuf, e->d_name, e->d_name) >= MAXBUFLEN) { logerrmsg("snprintf failed, buffer sizeof exceeded"); return; } - if(writetext(respbuf) == -1) + if(writetext(resbuf) == -1) return; } writetext("</body></html>\r\n"); @@ -243,11 +246,11 @@ responsedir(void) { ssize_t len = strlen(reqbuf); DIR *d; - if((reqbuf[len - 1] != '/') && (len + 1 < MAXREQLEN)) { + if((reqbuf[len - 1] != '/') && (len + 1 < MAXBUFLEN)) { /* add directory terminator if necessary */ reqbuf[len++] = '/'; reqbuf[len] = 0; - fprintf(stdout, "%s: redirecting %s to %s%s\n", tstamp(), name, location, reqbuf); + logmsg("redirecting %s to %s%s\n", host, location, reqbuf); if(responsehdr(HttpMoved) != -1 && responselocation(location, reqbuf) != -1 && responsecontenttype(texthtml) != -1) @@ -258,7 +261,7 @@ responsedir(void) { writetext("\r\n<html><body>301 Moved Permanently</a></body></html>\r\n"); return; } - if(len + strlen(docindex) + 1 < MAXREQLEN) + if(len + strlen(docindex) + 1 < MAXBUFLEN) memcpy(reqbuf + len, docindex, strlen(docindex) + 1); if(access(reqbuf, R_OK) == -1) { /* directory mode */ reqbuf[len] = 0; /* cut off docindex again */ @@ -282,15 +285,17 @@ responsecgi(void) { setenv("REQUEST_METHOD", "HEAD", 1); else return; - setenv("SERVER_NAME", servername, 1); + if(*reqhost) + setenv("SERVER_NAME", reqhost, 1); setenv("SCRIPT_NAME", cgi_script, 1); setenv("REQUEST_URI", reqbuf, 1); + logmsg("CGI SERVER_NAME=%s SCRIPT_NAME=%s REQUEST_URI=%s\n", reqhost, cgi_script, reqbuf); chdir(cgi_dir); if((cgi = popen(cgi_script, "r"))) { if(responsehdr(HttpOk) == -1) return; - while((r = fread(respbuf, 1, sizeof respbuf - 1, cgi)) > 0) { - if(writedata(respbuf, r) == -1) { + while((r = fread(resbuf, 1, MAXBUFLEN, cgi)) > 0) { + if(writedata(resbuf, r) == -1) { pclose(cgi); return; } @@ -298,7 +303,7 @@ responsecgi(void) { pclose(cgi); } else { - fprintf(stderr, "%s: %s requests %s, but cannot run cgi script %s\n", tstamp(), name, cgi_script, reqbuf); + logerrmsg("%s requests %s, but cannot run cgi script %s\n", host, cgi_script, reqbuf); if(responsehdr(HttpNotFound) != -1 && responsecontenttype(texthtml) != -1) ; @@ -315,8 +320,8 @@ response(void) { struct stat st; for(p = reqbuf; *p; p++) - if(*p == '\\' || *p == '?' || *p == '%' || *p == '&' || (*p == '/' && *(p + 1) == '.')) { /* don't serve bogus or hidden files */ - fprintf(stderr, "%s: %s requests bogus or hidden file %s\n", tstamp(), name, reqbuf); + if(*p == '\\' || (*p == '/' && *(p + 1) == '.')) { /* don't serve bogus or hidden files */ + logerrmsg("%s requests bogus or hidden file %s\n", host, reqbuf); if(responsehdr(HttpUnauthorized) != -1 && responsecontenttype(texthtml) != -1) ; @@ -326,7 +331,7 @@ response(void) { writetext("\r\n<html><body>401 Unauthorized</body></html>\r\n"); return; } - fprintf(stdout, "%s: %s requests: %s\n", tstamp(), name, reqbuf); + logmsg("%s requests: %s\n", host, reqbuf); if(cgi_mode) responsecgi(); else { @@ -344,24 +349,37 @@ request(void) { int r; size_t offset = 0; - do { /* MAXREQLEN byte of reqbuf is emergency 0 terminator */ - if((r = read(cfd, reqbuf + offset, MAXREQLEN - offset)) < 0) { - fprintf(stderr, "%s: read: %s\n", tstamp(), strerror(errno)); + do { /* MAXBUFLEN byte of reqbuf is emergency 0 terminator */ + if((r = read(cfd, reqbuf + offset, MAXBUFLEN - offset)) < 0) { + logerrmsg("read: %s\n", strerror(errno)); return -1; } offset += r; reqbuf[offset] = 0; } - while(offset < MAXREQLEN && !strstr(reqbuf, "\r\n\r\n") && !strstr(reqbuf, "\n\n")); + while(r > 0 && offset < MAXBUFLEN && (!strstr(reqbuf, "\r\n") || !strstr(reqbuf, "\n"))); + if((res = strstr(reqbuf, "Host:"))) { + for(res = res + 5; *res && (*res == ' ' || *res == '\t'); res++); + if(!*res) + goto invalid_request; + for(p = res; *p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\t'; p++); + if(!*p) + goto invalid_request; + *p = 0; + if(p - res > sizeof reqhost) + goto invalid_request; + memcpy(reqhost, res, p - res); + reqhost[p - res] = 0; + } for(p = reqbuf; *p && *p != '\r' && *p != '\n'; p++); - if(p >= reqbuf + MAXREQLEN) + if(!*p) goto invalid_request; if(*p == '\r' || *p == '\n') { *p = 0; /* check command */ if(!strncmp(reqbuf, "GET ", 4) && reqbuf[4] == '/') reqtype = GET; - else if(!strncmp(reqbuf, "HEAD ", 5) && reqbuf[5] != '/') + else if(!strncmp(reqbuf, "HEAD ", 5) && reqbuf[5] == '/') reqtype = HEAD; else goto invalid_request; @@ -370,16 +388,16 @@ request(void) { goto invalid_request; /* determine path */ for(res = reqbuf + reqtype; *res && *(res + 1) == '/'; res++); /* strip '/' */ - if(res >= reqbuf + MAXREQLEN) + if(!*res) goto invalid_request; for(p = res; *p && *p != ' ' && *p != '\t'; p++); - if(p >= reqbuf + MAXREQLEN) + if(!*p) goto invalid_request; *p = 0; memmove(reqbuf, res, (p - res) + 1); return 0; invalid_request: - fprintf(stderr, "%s: %s performs invalid request %s\n", tstamp(), name, reqbuf); + logerrmsg("%s performs invalid request %s\n", host, reqbuf); return -1; } @@ -393,14 +411,14 @@ serve(int fd) { while(running) { if((cfd = accept(fd, &sa, &salen)) == -1) { /* el cheapo socket release */ - fprintf(stderr, "%s: cannot accept: %s, sleep a second...\n", tstamp(), strerror(errno)); + logerrmsg("cannot accept: %s, sleep a second...\n", strerror(errno)); sleep(1); continue; } if(fork() == 0) { close(fd); - name[0] = 0; - getnameinfo(&sa, salen, name, sizeof name, NULL, 0, NI_NOFQDN); + host[0] = 0; + getnameinfo(&sa, salen, host, sizeof host, NULL, 0, NI_NOFQDN); result = request(); shutdown(cfd, SHUT_RD); if(result == 0) @@ -411,7 +429,7 @@ serve(int fd) { } close(cfd); } - fprintf(stdout, "%s: shutting down\n", tstamp()); + logmsg("shutting down\n"); } void @@ -431,7 +449,7 @@ sighandler(int sig) { case SIGQUIT: case SIGABRT: case SIGTERM: - fprintf(stderr, "%s: received signal %s, closing down\n", tstamp(), signame[sig] ? signame[sig] : ""); + logerrmsg("received signal %s, closing down\n", signame[sig] ? signame[sig] : ""); close(fd); running = 0; break; @@ -459,42 +477,10 @@ main(int argc, char *argv[]) { /* arguments */ for(i = 1; i < argc; i++) - if(!strcmp(argv[i], "-n")) { - if(++i < argc) servername = argv[i]; - } - else if(!strcmp(argv[i], "-p")) { - if(++i < argc) serverport = argv[i]; - } - else if(!strcmp(argv[i], "-r")) { - if(++i < argc) docroot = argv[i]; - } - else if(!strcmp(argv[i], "-i")) { - if(++i < argc) docindex = argv[i]; - } - else if(!strcmp(argv[i], "-u")) { - if(++i < argc) user = argv[i]; - } - else if(!strcmp(argv[i], "-g")) { - if(++i < argc) group = argv[i]; - } - else if(!strcmp(argv[i], "-c")) { - if(++i < argc) cgi_dir = argv[i]; - if(++i < argc) { - cgi_script = argv[i]; - cgi_mode = 1; - } - } - else if(!strcmp(argv[i], "-v")) + if(!strcmp(argv[i], "-v")) die("quark-"VERSION", © 2009-2010 Anselm R Garbe\n"); else - die("\nusage: quark [-n <server-name>]\n" - " [-p <port>]\n" - " [-r <doc root>]\n" - " [-i <doc index>]\n" - " [-u <user>]\n" - " [-g <group>]\n" - " [-c <cgi dir> <cgi script>]\n" - " [-v]\n"); + die("usage: quark [-v]\n"); /* sanity checks */ if(!(upwd = getpwnam(user))) @@ -556,7 +542,7 @@ main(int argc, char *argv[]) { if(getgid() == 0) die("error: won't run with root permissions, choose another group\n"); - fprintf(stdout, "%s: listening on %s:%s using %s as root directory\n", tstamp(), servername, serverport, docroot); + logmsg("listening on %s:%s using %s as root directory\n", servername, serverport, docroot); serve(fd); /* main loop */ freeaddrinfo(ai);