i recently implemented a webserver and used some code from quark in it
meanwhile i found minor issues in the code so here is a patch
(some modifications are bugfixes others are debateble,
i leave it to arg to sort it out)


offtopic:

the webserver i'm implementing is used to do secure messaging:
it accepts PUT /key and GET /key requests which store and retrieve
exactly 1K data (so it looks like a key-value store)

(server does not remember the key only the hash of the key,
if GET asks an invalid key then 1K random is generated and stored
under the key)

(the uploaded data is garbage collected: eg after 1 month it gets
deleted from the store, so this is not yet another storage solution,
it's for communication, there are no strong durability guarantees)

the next step is to implement GET /key#hash requests:
the server only answers if the (sha1) hash of the data is different
than the provided one, this can be a communication channel:

-> PUT /key HTTP/1.0\r\nContent-Length: 1024\r\n\r\ndata..
<- HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n
-> GET /key#hash-of-data HTTP/1.0\r\n\r\n
<- [waiting..]

at this point both client and server keeps the connection open
until someone modifies the data under the given key, and then
the GET request is answered with the new data

<- HTTP/1.0 200 OK\r\nContent-Type: application/octet-stream..

two client can communicate by sharing a key and updating
the data under the key
(actually many client can follow the communication and
get notification by keeping a connection alive with the
last seen hash, to solve other communication issues
higher level layers can be used ie. data format with meta info)

(security can be ensured by sending requests (keys) over a secure
channel (tls) and sharing keys using eg diffie-hellman method

actually one can encrypt the 1K data with x then use hash(x) as key
so it is enough to share x between the communicating parties
which the server does not even know, decrypting the 1K data is not
possible by just using hash(x).. of course eve can overwrite the
data using PUT /hash(x), but then alice and bob may get notified..

well the design is not complete yet, but probably a secure distributed
communication system can be built on top of this: unlike ip level
communication here the communicated data does not know its destination)

some initial code is available here:
svn co https://www.epointsystem.org/svn/epoint_wallet/trunk/messaging/prototype
(folks here at epointsystem plan to use it for some secure mobile sms thing)

(this is not yet useful in any way but i posted in case someone is interested)

diff -r a4ca37cfe934 config.def.h
--- a/config.def.h	Sat Apr 10 21:34:05 2010 +0100
+++ b/config.def.h	Fri Feb 04 15:15:33 2011 +0100
@@ -5,7 +5,7 @@
 static const char docroot[]    = ".";
 static const char docindex[]   = "index.html";
 static const char user[]       = "nobody";
-static const char group[]      = "nobody";
+static const char group[]      = "nogroup";
 static const char cgi_dir[]    = "/var/www/werc-dev/bin";
 static const char cgi_script[] = "./werc.rc";
 static const int  cgi_mode     = 0;
diff -r a4ca37cfe934 quark.c
--- a/quark.c	Sat Apr 10 21:34:05 2010 +0100
+++ b/quark.c	Fri Feb 04 15:15:33 2011 +0100
@@ -5,6 +5,7 @@
 #include <netdb.h>
 #include <netinet/in.h>
 #include <pwd.h>
+#include <grp.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -38,9 +39,10 @@
 
 static ssize_t writetext(const char *buf);
 static ssize_t writedata(const char *buf, size_t buflen);
+static void atomiclog(int fd, const char *errstr, va_list ap);
 static void die(const char *errstr, ...);
-void logmsg(const char *errstr, ...);
-void logerrmsg(const char *errstr, ...);
+static void logmsg(const char *errstr, ...);
+static void logerrmsg(const char *errstr, ...);
 static void response(void);
 static void responsecgi(void);
 static void responsedir(void);
@@ -68,7 +70,7 @@
 
 	while(offset < buf_len) {
 		if((r = write(cfd, buf + offset, buf_len - offset)) == -1) {
-			logerrmsg("%s: client %s closed connection\n", tstamp(), host);
+			logerrmsg("client %s closed connection\n", host);
 			return -1;
 		}
 		offset += r;
@@ -82,12 +84,27 @@
 }
 
 void
+atomiclog(int fd, const char *errstr, va_list ap) {
+	static char buf[512];
+	int n;
+
+	/*
+	assemble the message in buf and write it in one pass
+	to avoid interleaved concurrent writes on a shared fd.
+	*/
+	n = snprintf(buf, sizeof buf, "%s: ", tstamp());
+	n += vsnprintf(buf + n, sizeof buf - n, errstr, ap);
+	if (n >= sizeof buf)
+		n = sizeof buf - 1;
+	write(fd, buf, n);
+}
+
+void
 logmsg(const char *errstr, ...) {
 	va_list ap;
 
-	fprintf(stdout, "%s: ", tstamp());
 	va_start(ap, errstr);
-	vfprintf(stdout, errstr, ap);
+	atomiclog(STDOUT_FILENO, errstr, ap);
 	va_end(ap);
 }
 
@@ -95,9 +112,8 @@
 logerrmsg(const char *errstr, ...) {
 	va_list ap;
 
-	fprintf(stderr, "%s: ", tstamp());
 	va_start(ap, errstr);
-	vfprintf(stderr, errstr, ap);
+	atomiclog(STDERR_FILENO, errstr, ap);
 	va_end(ap);
 }
 
@@ -105,9 +121,8 @@
 die(const char *errstr, ...) {
 	va_list ap;
 
-	fprintf(stderr, "%s: ", tstamp());
 	va_start(ap, errstr);
-	vfprintf(stderr, errstr, ap);
+	atomiclog(STDERR_FILENO, errstr, ap);
 	va_end(ap);
 	exit(EXIT_FAILURE);
 }
@@ -181,8 +196,7 @@
 	int i, ffd;
 	struct stat st;
 
-	stat(reqbuf, &st);
-	if((ffd = open(reqbuf, O_RDONLY)) == -1) {
+	if(stat(reqbuf, &st) == -1 || (ffd = open(reqbuf, O_RDONLY)) == -1) {
 		logerrmsg("%s requests unknown path %s\n", host, reqbuf);
 		if(responsehdr(HttpNotFound) != -1
 		&& responsecontenttype(texthtml) != -1)
@@ -269,6 +283,8 @@
 			responsedirdata(d);
 			closedir(d);
 		}
+		else
+			logerrmsg("client %s requests %s but opendir failed: %s\n", host, reqbuf, strerror(errno));
 	}
 	else
 		responsefile(); /* docindex */
@@ -290,7 +306,8 @@
 	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(chdir(cgi_dir) == -1)
+		logerrmsg("chdir to cgi directory %s failed: %s\n", cgi_dir, strerror(errno));
 	if((cgi = popen(cgi_script, "r"))) {
 		if(responsehdr(HttpOk) == -1)
 			return;
@@ -335,8 +352,7 @@
 	if(cgi_mode)
 		responsecgi();
 	else {
-		stat(reqbuf, &st);
-		if(S_ISDIR(st.st_mode))
+		if(stat(reqbuf, &st) != -1 && S_ISDIR(st.st_mode))
 			responsedir();
 		else
 			responsefile();
@@ -350,7 +366,7 @@
 	size_t offset = 0;
 
 	do { /* MAXBUFLEN byte of reqbuf is emergency 0 terminator */
-		if((r = read(cfd, reqbuf + offset, MAXBUFLEN - offset)) < 0) {
+		if((r = read(cfd, reqbuf + offset, MAXBUFLEN - offset)) == -1) {
 			logerrmsg("read: %s\n", strerror(errno));
 			return -1;
 		}
@@ -362,7 +378,7 @@
 		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++);
+		for(p = res; *p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n'; p++);
 		if(!*p)
 			goto invalid_request;
 		*p = 0;
@@ -372,8 +388,6 @@
 		reqhost[p - res] = 0;
 	}
 	for(p = reqbuf; *p && *p != '\r' && *p != '\n'; p++);
-	if(!*p)
-		goto invalid_request;
 	if(*p == '\r' || *p == '\n') {
 		*p = 0;
 		/* check command */
@@ -407,15 +421,16 @@
 	socklen_t salen;
 	struct sockaddr sa;
 
-	salen = sizeof sa;
 	while(running) {
+		salen = sizeof sa;
 		if((cfd = accept(fd, &sa, &salen)) == -1) {
 			/* el cheapo socket release */
 			logerrmsg("cannot accept: %s, sleep a second...\n", strerror(errno));
 			sleep(1);
 			continue;
 		}
-		if(fork() == 0) {
+		result = fork();
+		if(result == 0) {
 			close(fd);
 			host[0] = 0;
 			getnameinfo(&sa, salen, host, sizeof host, NULL, 0, NI_NOFQDN);
@@ -426,7 +441,8 @@
 			shutdown(cfd, SHUT_WR);
 			close(cfd);
 			exit(EXIT_SUCCESS);
-		}
+		} else if (result == -1)
+			logerrmsg("fork failed: %s\n", strerror(errno));
 		close(cfd);
 	}
 	logmsg("shutting down\n");
@@ -472,7 +488,8 @@
 int
 main(int argc, char *argv[]) {
 	struct addrinfo hints, *ai;
-	struct passwd *upwd, *gpwd;
+	struct passwd *upwd;
+	struct group *gpwd;
 	int i;
 
 	/* arguments */
@@ -485,7 +502,7 @@
 	/* sanity checks */
 	if(!(upwd = getpwnam(user)))
 		die("error: invalid user %s\n", user);
-	if(!(gpwd = getpwnam(group)))
+	if(!(gpwd = getgrnam(group)))
 		die("error: invalid group %s\n", group);
 
 	signal(SIGCHLD, sighandler);
@@ -529,10 +546,12 @@
 		die("error: location too long\n");
 	}
 
-	if(chroot(docroot) == -1)
-		die("error: chroot %s: %s\n", docroot, strerror(errno));
+	if(chdir(docroot) == -1)
+		die("error: chdir %s: %s\n", docroot, strerror(errno));
+	if(chroot(".") == -1)
+		die("error: chroot .: %s\n", strerror(errno));
 
-	if(setgid(gpwd->pw_gid) == -1)
+	if(setgid(gpwd->gr_gid) == -1)
 		die("error: cannot set group id\n");
 	if(setuid(upwd->pw_uid) == -1)
 		die("error: cannot set user id\n");

Reply via email to