Added a simple CGI endcap.
[ashd.git] / src / htparser.c
1 /*
2     ashd - A Sane HTTP Daemon
3     Copyright (C) 2008  Fredrik Tolf <fredrik@dolda2000.com>
4
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.
9
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.
14
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/>.
17 */
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <errno.h>
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <utils.h>
32 #include <mt.h>
33 #include <mtio.h>
34 #include <log.h>
35 #include <req.h>
36 #include <proc.h>
37
38 static int plex;
39
40 static int listensock4(int port)
41 {
42     struct sockaddr_in name;
43     int fd;
44     int valbuf;
45     
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)
50         return(-1);
51     valbuf = 1;
52     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
53     if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
54         close(fd);
55         return(-1);
56     }
57     if(listen(fd, 16) < 0) {
58         close(fd);
59         return(-1);
60     }
61     return(fd);
62 }
63
64 static int listensock6(int port)
65 {
66     struct sockaddr_in6 name;
67     int fd;
68     int valbuf;
69     
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)
74         return(-1);
75     valbuf = 1;
76     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
77     if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
78         close(fd);
79         return(-1);
80     }
81     if(listen(fd, 16) < 0) {
82         close(fd);
83         return(-1);
84     }
85     return(fd);
86 }
87
88 static size_t readhead(int fd, struct charbuf *buf)
89 {
90     int nl;
91     size_t off;
92     
93     int get1(void)
94     {
95         int ret;
96         
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);
100             if(ret <= 0) {
101                 if((ret < 0) && (errno == EAGAIN)) {
102                     if(block(fd, EV_READ, 60) <= 0)
103                         return(-1);
104                     continue;
105                 }
106                 return(-1);
107             }
108             buf->d += ret;
109         }
110         return(buf->b[off++]);
111     }
112
113     nl = 0;
114     off = 0;
115     while(1) {
116         switch(get1()) {
117         case '\n':
118             if(nl)
119                 return(off);
120             nl = 1;
121             break;
122         case '\r':
123             break;
124         case -1:
125             return(-1);
126         default:
127             nl = 0;
128             break;
129         }
130     }
131 }
132
133 #define SKIPNL(ptr) ({                          \
134             int __buf__;                        \
135             if(*(ptr) == '\r')                  \
136                 *((ptr)++) = 0;                 \
137             if(*(ptr) != '\n') {                \
138                 __buf__ = 0;                    \
139             } else {                            \
140                 *((ptr)++) = 0;                 \
141                 __buf__ = 1;                    \
142             }                                   \
143             __buf__;})
144 static struct hthead *parserawreq(char *buf)
145 {
146     char *p, *p2, *nl;
147     char *method, *url, *ver;
148     struct hthead *req;
149     
150     if((nl = strchr(buf, '\n')) == NULL)
151         return(NULL);
152     if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
153         return(NULL);
154     method = buf;
155     *(p++) = 0;
156     if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
157         return(NULL);
158     url = p;
159     p = p2;
160     *(p++) = 0;
161     if(strncmp(p, "HTTP/", 5))
162         return(NULL);
163     ver = (p += 5);
164     for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
165     if(!SKIPNL(p))
166         return(NULL);
167
168     req = mkreq(method, url, ver);
169     while(1) {
170         if(SKIPNL(p)) {
171             if(*p)
172                 goto fail;
173             break;
174         }
175         if((nl = strchr(p, '\n')) == NULL)
176             goto fail;
177         if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
178             goto fail;
179         *(p2++) = 0;
180         for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
181         for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
182         if(!SKIPNL(nl))
183             goto fail;
184         if(strncasecmp(p, "x-ash-", 6))
185             headappheader(req, p, p2);
186         p = nl;
187     }
188     return(req);
189     
190 fail:
191     freehthead(req);
192     return(NULL);
193 }
194
195 static struct hthead *parserawresp(char *buf)
196 {
197     char *p, *p2, *nl;
198     char *msg, *ver;
199     int code;
200     struct hthead *resp;
201     
202     if((nl = strchr(buf, '\n')) == NULL)
203         return(NULL);
204     p = strchr(buf, '\r');
205     if((p != NULL) && (p < nl))
206         nl = p;
207     if(strncmp(buf, "HTTP/", 5))
208         return(NULL);
209     ver = p = buf + 5;
210     for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
211     if(*p != ' ')
212         return(NULL);
213     *(p++) = 0;
214     if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
215         return(NULL);
216     *(p2++) = 0;
217     code = atoi(p);
218     if((code < 100) || (code >= 600))
219         return(NULL);
220     if(p2 >= nl)
221         return(NULL);
222     msg = p2;
223     p = nl;
224     if(!SKIPNL(p))
225         return(NULL);
226
227     resp = mkresp(code, msg, ver);
228     while(1) {
229         if(SKIPNL(p)) {
230             if(*p)
231                 goto fail;
232             break;
233         }
234         if((nl = strchr(p, '\n')) == NULL)
235             goto fail;
236         if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
237             goto fail;
238         *(p2++) = 0;
239         for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
240         for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
241         if(!SKIPNL(nl))
242             goto fail;
243         headappheader(resp, p, p2);
244         p = nl;
245     }
246     return(resp);
247     
248 fail:
249     freehthead(resp);
250     return(NULL);
251 }
252
253 static off_t passdata(int src, int dst, struct charbuf *buf, off_t max)
254 {
255     size_t dataoff, smax;
256     off_t sent;
257     int eof, ret;
258
259     sent = 0;
260     eof = 0;
261     while((!eof || (buf->d > 0)) && ((max < 0) || (sent < max))) {
262         if(!eof && (buf->d < buf->s) && ((max < 0) || (sent + buf->d < max))) {
263             while(1) {
264                 ret = recv(src, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
265                 if((ret < 0) && (errno == EAGAIN)) {
266                 } else if(ret < 0) {
267                     return(-1);
268                 } else if(ret == 0) {
269                     eof = 1;
270                     break;
271                 } else {
272                     buf->d += ret;
273                     break;
274                 }
275                 if(buf->d > 0)
276                     break;
277                 if(block(src, EV_READ, 0) <= 0)
278                     return(-1);
279             }
280         }
281         for(dataoff = 0; (dataoff < buf->d) && ((max < 0) || (sent < max));) {
282             if(block(dst, EV_WRITE, 120) <= 0)
283                 return(-1);
284             smax = buf->d - dataoff;
285             if(sent + smax > max)
286                 smax = max - sent;
287             ret = send(dst, buf->b + dataoff, smax, MSG_NOSIGNAL | MSG_DONTWAIT);
288             if(ret < 0)
289                 return(-1);
290             dataoff += ret;
291             sent += ret;
292         }
293         bufeat(*buf, dataoff);
294     }
295     return(sent);
296 }
297
298 static void serve(struct muth *muth, va_list args)
299 {
300     vavar(int, fd);
301     vavar(struct sockaddr_storage, name);
302     int cfd;
303     int pfds[2];
304     char old;
305     char *hd, *p;
306     struct charbuf inbuf, outbuf;
307     struct hthead *req, *resp;
308     off_t dlen, sent;
309     ssize_t headoff;
310     char nmbuf[256];
311     
312     bufinit(inbuf);
313     bufinit(outbuf);
314     cfd = -1;
315     req = resp = NULL;
316     while(1) {
317         /*
318          * First, find and decode the header:
319          */
320         if((headoff = readhead(fd, &inbuf)) < 0)
321             goto out;
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. */
327             goto out;
328         }
329         old = inbuf.b[headoff];
330         inbuf.b[headoff] = 0;
331         if((req = parserawreq(inbuf.b)) == NULL)
332             goto out;
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
337          * coherently. */
338         if(req->rest[0] == '/')
339             replrest(req, req->rest + 1);
340         if((p = strchr(req->rest, '?')) != NULL)
341             *p = 0;
342         
343         /*
344          * Add metainformation and then send the request to the root
345          * multiplexer:
346          */
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)));
353         }
354         if(block(plex, EV_WRITE, 60) <= 0)
355             goto out;
356         if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfds))
357             goto out;
358         if(sendreq(plex, req, pfds[0]))
359             goto out;
360         close(pfds[0]);
361         cfd = pfds[1];
362
363         /*
364          * If there is message data, pass it:
365          */
366         if((hd = getheader(req, "content-length")) != NULL) {
367             dlen = atoo(hd);
368             if(dlen > 0)
369                 passdata(fd, cfd, &inbuf, dlen);
370         }
371         /* Make sure to send EOF */
372         shutdown(cfd, SHUT_WR);
373         
374         /*
375          * Find and decode the response header:
376          */
377         outbuf.d = 0;
378         if((headoff = readhead(cfd, &outbuf)) < 0)
379             goto out;
380         hd = memcpy(smalloc(headoff + 1), outbuf.b, headoff);
381         hd[headoff] = 0;
382         if((resp = parserawresp(hd)) == NULL)
383             goto out;
384         
385         /*
386          * Pass the actual output:
387          */
388         sizebuf(outbuf, 65536);
389         sent = passdata(cfd, fd, &outbuf, -1);
390         sent -= headoff;
391         
392         /*
393          * Check for connection expiry
394          */
395         if(strcasecmp(req->method, "head")) {
396             if((hd = getheader(resp, "content-length")) != NULL) {
397                 if(sent != atoo(hd)) {
398                     /* Exit because of error */
399                     goto out;
400                 }
401             } else {
402                 if(((hd = getheader(resp, "transfer-encoding")) == NULL) || !strcasecmp(hd, "identity"))
403                     break;
404             }
405             if(((hd = getheader(req, "connection")) != NULL) && !strcasecmp(hd, "close"))
406                 break;
407             if(((hd = getheader(resp, "connection")) != NULL) && !strcasecmp(hd, "close"))
408                 break;
409         }
410         
411         close(cfd);
412         cfd = -1;
413         freehthead(req);
414         req = NULL;
415         freehthead(resp);
416         resp = NULL;
417     }
418     
419 out:
420     if(cfd >= 0)
421         close(cfd);
422     if(req != NULL)
423         freehthead(req);
424     if(resp != NULL)
425         freehthead(resp);
426     buffree(inbuf);
427     buffree(outbuf);
428     close(fd);
429 }
430
431 static void listenloop(struct muth *muth, va_list args)
432 {
433     vavar(int, ss);
434     int ns;
435     struct sockaddr_storage name;
436     socklen_t namelen;
437     
438     while(1) {
439         namelen = sizeof(name);
440         block(ss, EV_READ, 0);
441         ns = accept(ss, (struct sockaddr *)&name, &namelen);
442         if(ns < 0) {
443             flog(LOG_ERR, "accept: %s", strerror(errno));
444             goto out;
445         }
446         mustart(serve, ns, name);
447     }
448     
449 out:
450     close(ss);
451 }
452
453 int main(int argc, char **argv)
454 {
455     int fd;
456     
457     if(argc < 2) {
458         fprintf(stderr, "usage: htparser ROOT [ARGS...]\n");
459         exit(1);
460     }
461     if((plex = stdmkchild(argv + 1)) < 0) {
462         flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno));
463         return(1);
464     }
465     if((fd = listensock6(8080)) < 0) {
466         flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
467         return(1);
468     }
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));
473             return(1);
474         }
475     } else {
476         mustart(listenloop, fd);
477     }
478     ioloop();
479     return(0);
480 }