Moved htparser's ioloop to the library.
[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     char old;
304     char *hd, *p;
305     struct charbuf inbuf, outbuf;
306     struct hthead *req, *resp;
307     off_t dlen, sent;
308     ssize_t headoff;
309     char nmbuf[256];
310     
311     bufinit(inbuf);
312     bufinit(outbuf);
313     cfd = -1;
314     req = resp = NULL;
315     while(1) {
316         /*
317          * First, find and decode the header:
318          */
319         if((headoff = readhead(fd, &inbuf)) < 0)
320             goto out;
321         if(headoff > 65536) {
322             /* We cannot handle arbitrarily large headers, as they
323              * need to fit within a single Unix datagram. This is
324              * probably a safe limit, and larger packets than this are
325              * most likely erroneous (or malicious) anyway. */
326             goto out;
327         }
328         old = inbuf.b[headoff];
329         inbuf.b[headoff] = 0;
330         if((req = parserawreq(inbuf.b)) == NULL)
331             goto out;
332         inbuf.b[headoff] = old;
333         bufeat(inbuf, headoff);
334         /* We strip off the leading slash and any param string from
335          * the rest string, so that multiplexers can parse
336          * coherently. */
337         if(req->rest[0] == '/')
338             replrest(req, req->rest + 1);
339         if((p = strchr(req->rest, '?')) != NULL)
340             *p = 0;
341         
342         /*
343          * Add metainformation and then send the request to the root
344          * multiplexer:
345          */
346         if(name.ss_family == AF_INET) {
347             headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&name)->sin_addr, nmbuf, sizeof(nmbuf)));
348             headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&name)->sin_port)));
349         } else if(name.ss_family == AF_INET6) {
350             headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&name)->sin6_addr, nmbuf, sizeof(nmbuf)));
351             headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&name)->sin6_port)));
352         }
353         if((cfd = sendreq(plex, req)) < 0)
354             goto out;
355
356         /*
357          * If there is message data, pass it:
358          */
359         if((hd = getheader(req, "content-length")) != NULL) {
360             dlen = atoo(hd);
361             if(dlen > 0)
362                 passdata(fd, cfd, &inbuf, dlen);
363         }
364         /* Make sure to send EOF */
365         shutdown(cfd, SHUT_WR);
366         
367         /*
368          * Find and decode the response header:
369          */
370         outbuf.d = 0;
371         if((headoff = readhead(cfd, &outbuf)) < 0)
372             goto out;
373         hd = memcpy(smalloc(headoff + 1), outbuf.b, headoff);
374         hd[headoff] = 0;
375         if((resp = parserawresp(hd)) == NULL)
376             goto out;
377         
378         /*
379          * Pass the actual output:
380          */
381         sizebuf(outbuf, 65536);
382         sent = passdata(cfd, fd, &outbuf, -1);
383         sent -= headoff;
384         
385         /*
386          * Check for connection expiry
387          */
388         if(strcasecmp(req->method, "head")) {
389             if((hd = getheader(resp, "content-length")) != NULL) {
390                 if(sent != atoo(hd)) {
391                     /* Exit because of error */
392                     goto out;
393                 }
394             } else {
395                 if(((hd = getheader(resp, "transfer-encoding")) == NULL) || !strcasecmp(hd, "identity"))
396                     break;
397             }
398             if(((hd = getheader(req, "connection")) != NULL) && !strcasecmp(hd, "close"))
399                 break;
400             if(((hd = getheader(resp, "connection")) != NULL) && !strcasecmp(hd, "close"))
401                 break;
402         }
403         
404         close(cfd);
405         cfd = -1;
406         freehthead(req);
407         req = NULL;
408         freehthead(resp);
409         resp = NULL;
410     }
411     
412 out:
413     if(cfd >= 0)
414         close(cfd);
415     if(req != NULL)
416         freehthead(req);
417     if(resp != NULL)
418         freehthead(resp);
419     buffree(inbuf);
420     buffree(outbuf);
421     close(fd);
422 }
423
424 static void listenloop(struct muth *muth, va_list args)
425 {
426     vavar(int, ss);
427     int ns;
428     struct sockaddr_storage name;
429     socklen_t namelen;
430     
431     while(1) {
432         namelen = sizeof(name);
433         block(ss, EV_READ, 0);
434         ns = accept(ss, (struct sockaddr *)&name, &namelen);
435         if(ns < 0) {
436             flog(LOG_ERR, "accept: %s", strerror(errno));
437             goto out;
438         }
439         mustart(serve, ns, name);
440     }
441     
442 out:
443     close(ss);
444 }
445
446 int main(int argc, char **argv)
447 {
448     int fd;
449     
450     if(argc < 2) {
451         fprintf(stderr, "usage: htparser ROOT [ARGS...]\n");
452         exit(1);
453     }
454     if((plex = stdmkchild(argv + 1)) < 0) {
455         flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno));
456         return(1);
457     }
458     if((fd = listensock6(8080)) < 0) {
459         flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
460         return(1);
461     }
462     mustart(listenloop, fd);
463     if((fd = listensock4(8080)) < 0) {
464         if(errno != EADDRINUSE) {
465             flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
466             return(1);
467         }
468     } else {
469         mustart(listenloop, fd);
470     }
471     ioloop();
472     return(0);
473 }