2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/select.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
43 struct blocker *n, *p;
50 static struct blocker *blockers;
53 static int block(int fd, int ev, time_t to)
62 bl->to = time(NULL) + to;
78 static int listensock4(int port)
80 struct sockaddr_in name;
84 memset(&name, 0, sizeof(name));
85 name.sin_family = AF_INET;
86 name.sin_port = htons(port);
87 if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
90 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
91 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
95 if(listen(fd, 16) < 0) {
102 static int listensock6(int port)
104 struct sockaddr_in6 name;
108 memset(&name, 0, sizeof(name));
109 name.sin6_family = AF_INET6;
110 name.sin6_port = htons(port);
111 if((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0)
114 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
115 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
119 if(listen(fd, 16) < 0) {
126 static size_t readhead(int fd, struct charbuf *buf)
135 while(!(off < buf->d)) {
136 sizebuf(*buf, buf->d + 1024);
137 ret = recv(fd, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
139 if((ret < 0) && (errno == EAGAIN)) {
140 if(block(fd, EV_READ, 60) <= 0)
148 return(buf->b[off++]);
171 #define SKIPNL(ptr) ({ \
175 if(*(ptr) != '\n') { \
182 static struct hthead *parserawreq(char *buf)
185 char *method, *url, *ver;
188 if((nl = strchr(buf, '\n')) == NULL)
190 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
194 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
199 if(strncmp(p, "HTTP/", 5))
202 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
206 req = mkreq(method, url, ver);
213 if((nl = strchr(p, '\n')) == NULL)
215 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
218 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
219 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
222 if(strncasecmp(p, "x-ash-", 6))
223 headappheader(req, p, p2);
233 static struct hthead *parserawresp(char *buf)
240 if((nl = strchr(buf, '\n')) == NULL)
242 p = strchr(buf, '\r');
243 if((p != NULL) && (p < nl))
245 if(strncmp(buf, "HTTP/", 5))
248 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
252 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
256 if((code < 100) || (code >= 600))
265 resp = mkresp(code, msg, ver);
272 if((nl = strchr(p, '\n')) == NULL)
274 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
277 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
278 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
281 headappheader(resp, p, p2);
291 static off_t passdata(int src, int dst, struct charbuf *buf, off_t max)
293 size_t dataoff, smax;
299 while((!eof || (buf->d > 0)) && ((max < 0) || (sent < max))) {
300 if(!eof && (buf->d < buf->s) && ((max < 0) || (sent + buf->d < max))) {
302 ret = recv(src, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
303 if((ret < 0) && (errno == EAGAIN)) {
306 } else if(ret == 0) {
315 if(block(src, EV_READ, 0) <= 0)
319 for(dataoff = 0; (dataoff < buf->d) && ((max < 0) || (sent < max));) {
320 if(block(dst, EV_WRITE, 120) <= 0)
322 smax = buf->d - dataoff;
323 if(sent + smax > max)
325 ret = send(dst, buf->b + dataoff, smax, MSG_NOSIGNAL | MSG_DONTWAIT);
331 bufeat(*buf, dataoff);
336 static void serve(struct muth *muth, va_list args)
339 vavar(struct sockaddr_storage, name);
343 struct charbuf inbuf, outbuf;
344 struct hthead *req, *resp;
355 * First, find and decode the header:
357 if((headoff = readhead(fd, &inbuf)) < 0)
359 if(headoff > 65536) {
360 /* We cannot handle arbitrarily large headers, as they
361 * need to fit within a single Unix datagram. This is
362 * probably a safe limit, and larger packets than this are
363 * most likely erroneous (or malicious) anyway. */
366 old = inbuf.b[headoff];
367 inbuf.b[headoff] = 0;
368 if((req = parserawreq(inbuf.b)) == NULL)
370 inbuf.b[headoff] = old;
371 bufeat(inbuf, headoff);
372 /* We strip off the leading slash and any param string from
373 * the rest string, so that multiplexers can parse
375 if(req->rest[0] == '/')
376 replrest(req, req->rest + 1);
377 if((p = strchr(req->rest, '?')) != NULL)
381 * Add metainformation and then send the request to the root
384 if(name.ss_family == AF_INET) {
385 headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&name)->sin_addr, nmbuf, sizeof(nmbuf)));
386 headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&name)->sin_port)));
387 } else if(name.ss_family == AF_INET6) {
388 headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&name)->sin6_addr, nmbuf, sizeof(nmbuf)));
389 headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&name)->sin6_port)));
391 cfd = sendreq(plex, req);
394 * If there is message data, pass it:
396 if((hd = getheader(req, "content-length")) != NULL) {
399 passdata(fd, cfd, &inbuf, dlen);
401 /* Make sure to send EOF */
402 shutdown(cfd, SHUT_WR);
405 * Find and decode the response header:
408 headoff = readhead(cfd, &outbuf);
409 hd = memcpy(smalloc(headoff + 1), outbuf.b, headoff);
411 if((resp = parserawresp(hd)) == NULL)
415 * Pass the actual output:
417 sizebuf(outbuf, 65536);
418 sent = passdata(cfd, fd, &outbuf, -1);
422 * Check for connection expiry
424 if(strcasecmp(req->method, "head")) {
425 if((hd = getheader(resp, "content-length")) != NULL) {
426 if(sent != atoo(hd)) {
427 /* Exit because of error */
431 if(((hd = getheader(resp, "transfer-encoding")) == NULL) || !strcasecmp(hd, "identity"))
434 if(((hd = getheader(req, "connection")) != NULL) && !strcasecmp(hd, "close"))
436 if(((hd = getheader(resp, "connection")) != NULL) && !strcasecmp(hd, "close"))
460 static void listenloop(struct muth *muth, va_list args)
464 struct sockaddr_storage name;
468 namelen = sizeof(name);
469 block(ss, EV_READ, 0);
470 ns = accept(ss, (struct sockaddr *)&name, &namelen);
472 flog(LOG_ERR, "accept: %s", strerror(errno));
475 mustart(serve, ns, name);
482 static void ioloop(void)
485 fd_set rfds, wfds, efds;
486 struct blocker *bl, *nbl;
487 struct timeval toval;
492 while(blockers != NULL) {
499 for(bl = blockers; bl; bl = bl->n) {
501 FD_SET(bl->fd, &rfds);
502 if(bl->ev & EV_WRITE)
503 FD_SET(bl->fd, &wfds);
504 FD_SET(bl->fd, &efds);
507 if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
510 toval.tv_sec = timeout - now;
512 ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
515 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
516 /* To avoid CPU hogging in case it's bad, which it
522 for(bl = blockers; bl; bl = nbl) {
525 if(FD_ISSET(bl->fd, &rfds))
527 if(FD_ISSET(bl->fd, &wfds))
529 if(FD_ISSET(bl->fd, &efds))
533 else if((bl->to != 0) && (bl->to <= now))
539 int main(int argc, char **argv)
544 fprintf(stderr, "usage: htparser ROOT [ARGS...]\n");
547 if((plex = stdmkchild(argv + 1)) < 0) {
548 flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno));
551 if((fd = listensock6(8080)) < 0) {
552 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
555 mustart(listenloop, fd);
556 if((fd = listensock4(8080)) < 0) {
557 if(errno != EADDRINUSE) {
558 flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
562 mustart(listenloop, fd);