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/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
40 static int listensock4(int port)
42 struct sockaddr_in name;
46 memset(&name, 0, sizeof(name));
47 name.sin_family = AF_INET;
48 name.sin_port = htons(port);
49 if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
52 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
53 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
57 if(listen(fd, 16) < 0) {
64 static int listensock6(int port)
66 struct sockaddr_in6 name;
70 memset(&name, 0, sizeof(name));
71 name.sin6_family = AF_INET6;
72 name.sin6_port = htons(port);
73 if((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0)
76 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
77 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
81 if(listen(fd, 16) < 0) {
88 static size_t readhead(int fd, struct charbuf *buf)
97 while(!(off < buf->d)) {
98 sizebuf(*buf, buf->d + 1024);
99 ret = recv(fd, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
101 if((ret < 0) && (errno == EAGAIN)) {
102 if(block(fd, EV_READ, 60) <= 0)
110 return(buf->b[off++]);
133 #define SKIPNL(ptr) ({ \
137 if(*(ptr) != '\n') { \
144 static struct hthead *parserawreq(char *buf)
147 char *method, *url, *ver;
150 if((nl = strchr(buf, '\n')) == NULL)
152 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
156 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
161 if(strncmp(p, "HTTP/", 5))
164 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
168 req = mkreq(method, url, ver);
175 if((nl = strchr(p, '\n')) == NULL)
177 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
180 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
181 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
184 if(strncasecmp(p, "x-ash-", 6))
185 headappheader(req, p, p2);
195 static struct hthead *parserawresp(char *buf)
202 if((nl = strchr(buf, '\n')) == NULL)
204 p = strchr(buf, '\r');
205 if((p != NULL) && (p < nl))
207 if(strncmp(buf, "HTTP/", 5))
210 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
214 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
218 if((code < 100) || (code >= 600))
227 resp = mkresp(code, msg, ver);
234 if((nl = strchr(p, '\n')) == NULL)
236 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
239 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
240 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
243 headappheader(resp, p, p2);
253 static off_t passdata(int src, int dst, struct charbuf *buf, off_t max)
255 size_t dataoff, smax;
261 while((!eof || (buf->d > 0)) && ((max < 0) || (sent < max))) {
262 if(!eof && (buf->d < buf->s) && ((max < 0) || (sent + buf->d < max))) {
264 ret = recv(src, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
265 if((ret < 0) && (errno == EAGAIN)) {
268 } else if(ret == 0) {
277 if(block(src, EV_READ, 0) <= 0)
281 for(dataoff = 0; (dataoff < buf->d) && ((max < 0) || (sent < max));) {
282 if(block(dst, EV_WRITE, 120) <= 0)
284 smax = buf->d - dataoff;
285 if(sent + smax > max)
287 ret = send(dst, buf->b + dataoff, smax, MSG_NOSIGNAL | MSG_DONTWAIT);
293 bufeat(*buf, dataoff);
298 static void serve(struct muth *muth, va_list args)
301 vavar(struct sockaddr_storage, name);
306 struct charbuf inbuf, outbuf;
307 struct hthead *req, *resp;
318 * First, find and decode the header:
320 if((headoff = readhead(fd, &inbuf)) < 0)
322 if(headoff > 65536) {
323 /* We cannot handle arbitrarily large headers, as they
324 * need to fit within a single Unix datagram. This is
325 * probably a safe limit, and larger packets than this are
326 * most likely erroneous (or malicious) anyway. */
329 old = inbuf.b[headoff];
330 inbuf.b[headoff] = 0;
331 if((req = parserawreq(inbuf.b)) == NULL)
333 inbuf.b[headoff] = old;
334 bufeat(inbuf, headoff);
335 /* We strip off the leading slash and any param string from
336 * the rest string, so that multiplexers can parse
338 if(req->rest[0] == '/')
339 replrest(req, req->rest + 1);
340 if((p = strchr(req->rest, '?')) != NULL)
344 * Add metainformation and then send the request to the root
347 if(name.ss_family == AF_INET) {
348 headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&name)->sin_addr, nmbuf, sizeof(nmbuf)));
349 headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&name)->sin_port)));
350 } else if(name.ss_family == AF_INET6) {
351 headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&name)->sin6_addr, nmbuf, sizeof(nmbuf)));
352 headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&name)->sin6_port)));
354 if(block(plex, EV_WRITE, 60) <= 0)
356 if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfds))
358 if(sendreq(plex, req, pfds[0]))
364 * If there is message data, pass it:
366 if((hd = getheader(req, "content-length")) != NULL) {
369 passdata(fd, cfd, &inbuf, dlen);
371 /* Make sure to send EOF */
372 shutdown(cfd, SHUT_WR);
375 * Find and decode the response header:
378 if((headoff = readhead(cfd, &outbuf)) < 0)
380 hd = memcpy(smalloc(headoff + 1), outbuf.b, headoff);
382 if((resp = parserawresp(hd)) == NULL)
386 * Pass the actual output:
388 sizebuf(outbuf, 65536);
389 sent = passdata(cfd, fd, &outbuf, -1);
393 * Check for connection expiry
395 if(strcasecmp(req->method, "head")) {
396 if((hd = getheader(resp, "content-length")) != NULL) {
397 if(sent != atoo(hd)) {
398 /* Exit because of error */
402 if(((hd = getheader(resp, "transfer-encoding")) == NULL) || !strcasecmp(hd, "identity"))
405 if(((hd = getheader(req, "connection")) != NULL) && !strcasecmp(hd, "close"))
407 if(((hd = getheader(resp, "connection")) != NULL) && !strcasecmp(hd, "close"))
431 static void listenloop(struct muth *muth, va_list args)
435 struct sockaddr_storage name;
439 namelen = sizeof(name);
440 block(ss, EV_READ, 0);
441 ns = accept(ss, (struct sockaddr *)&name, &namelen);
443 flog(LOG_ERR, "accept: %s", strerror(errno));
446 mustart(serve, ns, name);
453 int main(int argc, char **argv)
458 fprintf(stderr, "usage: htparser ROOT [ARGS...]\n");
461 if((plex = stdmkchild(argv + 1)) < 0) {
462 flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno));
465 if((fd = listensock6(8080)) < 0) {
466 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
469 mustart(listenloop, fd);
470 if((fd = listensock4(8080)) < 0) {
471 if(errno != EADDRINUSE) {
472 flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
476 mustart(listenloop, fd);