Added a tokenizer util function.
[ashd.git] / src / htparser.c
CommitLineData
f0bbedf7
FT
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>
f4cdf919
FT
22#include <string.h>
23#include <sys/select.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
9d87a119 26#include <arpa/inet.h>
f4cdf919 27#include <errno.h>
9d87a119 28#include <time.h>
f0bbedf7
FT
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33#include <utils.h>
f4cdf919
FT
34#include <mt.h>
35#include <log.h>
66987955 36#include <req.h>
9d87a119 37#include <proc.h>
f4cdf919
FT
38
39#define EV_READ 1
40#define EV_WRITE 2
41
42struct blocker {
43 struct blocker *n, *p;
44 int fd;
45 int ev;
9d87a119 46 time_t to;
f4cdf919
FT
47 struct muth *th;
48};
49
50static struct blocker *blockers;
9d87a119 51int plex;
f4cdf919 52
9d87a119 53static int block(int fd, int ev, time_t to)
f4cdf919
FT
54{
55 struct blocker *bl;
56 int rv;
57
58 omalloc(bl);
59 bl->fd = fd;
60 bl->ev = ev;
9d87a119
FT
61 if(to > 0)
62 bl->to = time(NULL) + to;
f4cdf919
FT
63 bl->th = current;
64 bl->n = blockers;
65 if(blockers)
66 blockers->p = bl;
67 blockers = bl;
68 rv = yield();
69 if(bl->n)
70 bl->n->p = bl->p;
71 if(bl->p)
72 bl->p->n = bl->n;
73 if(bl == blockers)
74 blockers = bl->n;
75 return(rv);
76}
77
78static int listensock4(int port)
79{
80 struct sockaddr_in name;
81 int fd;
82 int valbuf;
83
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)
88 return(-1);
89 valbuf = 1;
90 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
91 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
92 close(fd);
93 return(-1);
94 }
95 if(listen(fd, 16) < 0) {
96 close(fd);
97 return(-1);
98 }
99 return(fd);
100}
101
102static int listensock6(int port)
103{
104 struct sockaddr_in6 name;
105 int fd;
106 int valbuf;
107
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)
112 return(-1);
113 valbuf = 1;
114 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
115 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
116 close(fd);
117 return(-1);
118 }
119 if(listen(fd, 16) < 0) {
120 close(fd);
121 return(-1);
122 }
123 return(fd);
124}
125
c9955b14 126static size_t readhead(int fd, struct charbuf *buf)
66987955 127{
66987955 128 int nl;
c9955b14 129 size_t off;
66987955 130
c9955b14
FT
131 int get1(void)
132 {
133 int ret;
134
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);
138 if(ret <= 0) {
139 if((ret < 0) && (errno == EAGAIN)) {
9d87a119
FT
140 if(block(fd, EV_READ, 60) <= 0)
141 return(-1);
c9955b14
FT
142 continue;
143 }
144 return(-1);
66987955 145 }
c9955b14 146 buf->d += ret;
66987955 147 }
c9955b14
FT
148 return(buf->b[off++]);
149 }
150
151 nl = 0;
152 off = 0;
153 while(1) {
154 switch(get1()) {
155 case '\n':
66987955 156 if(nl)
c9955b14 157 return(off);
66987955 158 nl = 1;
c9955b14
FT
159 break;
160 case '\r':
161 break;
162 case -1:
163 return(-1);
164 default:
66987955 165 nl = 0;
c9955b14 166 break;
66987955
FT
167 }
168 }
66987955
FT
169}
170
171#define SKIPNL(ptr) ({ \
172 int __buf__; \
173 if(*(ptr) == '\r') \
174 *((ptr)++) = 0; \
175 if(*(ptr) != '\n') { \
176 __buf__ = 0; \
177 } else { \
178 *((ptr)++) = 0; \
179 __buf__ = 1; \
180 } \
181 __buf__;})
9d87a119 182static struct hthead *parserawreq(char *buf)
66987955
FT
183{
184 char *p, *p2, *nl;
185 char *method, *url, *ver;
9d87a119 186 struct hthead *req;
66987955
FT
187
188 if((nl = strchr(buf, '\n')) == NULL)
189 return(NULL);
190 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
191 return(NULL);
192 method = buf;
193 *(p++) = 0;
194 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
195 return(NULL);
196 url = p;
197 p = p2;
198 *(p++) = 0;
199 if(strncmp(p, "HTTP/", 5))
200 return(NULL);
201 ver = (p += 5);
202 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
203 if(!SKIPNL(p))
204 return(NULL);
205
206 req = mkreq(method, url, ver);
207 while(1) {
208 if(SKIPNL(p)) {
209 if(*p)
c9955b14 210 goto fail;
66987955
FT
211 break;
212 }
213 if((nl = strchr(p, '\n')) == NULL)
c9955b14 214 goto fail;
66987955 215 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
c9955b14 216 goto fail;
66987955
FT
217 *(p2++) = 0;
218 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
219 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
220 if(!SKIPNL(nl))
c9955b14 221 goto fail;
9d87a119
FT
222 if(strncasecmp(p, "x-ash-", 6))
223 headappheader(req, p, p2);
66987955
FT
224 p = nl;
225 }
226 return(req);
c9955b14
FT
227
228fail:
9d87a119 229 freehthead(req);
c9955b14 230 return(NULL);
66987955
FT
231}
232
9d87a119
FT
233static struct hthead *parserawresp(char *buf)
234{
235 char *p, *p2, *nl;
236 char *msg, *ver;
237 int code;
238 struct hthead *resp;
239
240 if((nl = strchr(buf, '\n')) == NULL)
241 return(NULL);
242 p = strchr(buf, '\r');
243 if((p != NULL) && (p < nl))
244 nl = p;
245 if(strncmp(buf, "HTTP/", 5))
246 return(NULL);
247 ver = p = buf + 5;
248 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
249 if(*p != ' ')
250 return(NULL);
251 *(p++) = 0;
252 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
253 return(NULL);
254 *(p2++) = 0;
255 code = atoi(p);
256 if((code < 100) || (code >= 600))
257 return(NULL);
258 if(p2 >= nl)
259 return(NULL);
260 msg = p2;
261 p = nl;
262 if(!SKIPNL(p))
263 return(NULL);
264
265 resp = mkresp(code, msg, ver);
266 while(1) {
267 if(SKIPNL(p)) {
268 if(*p)
269 goto fail;
270 break;
271 }
272 if((nl = strchr(p, '\n')) == NULL)
273 goto fail;
274 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
275 goto fail;
276 *(p2++) = 0;
277 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
278 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
279 if(!SKIPNL(nl))
280 goto fail;
281 headappheader(resp, p, p2);
282 p = nl;
283 }
284 return(resp);
285
286fail:
287 freehthead(resp);
288 return(NULL);
289}
290
291static off_t passdata(int src, int dst, struct charbuf *buf, off_t max)
292{
293 size_t dataoff, smax;
294 off_t sent;
295 int eof, ret;
296
297 sent = 0;
298 eof = 0;
d93d9a05 299 while((!eof || (buf->d > 0)) && ((max < 0) || (sent < max))) {
9d87a119
FT
300 if(!eof && (buf->d < buf->s) && ((max < 0) || (sent + buf->d < max))) {
301 while(1) {
302 ret = recv(src, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
303 if((ret < 0) && (errno == EAGAIN)) {
304 } else if(ret < 0) {
305 return(-1);
306 } else if(ret == 0) {
307 eof = 1;
308 break;
309 } else {
310 buf->d += ret;
311 break;
312 }
313 if(buf->d > 0)
314 break;
315 if(block(src, EV_READ, 0) <= 0)
316 return(-1);
317 }
318 }
319 for(dataoff = 0; (dataoff < buf->d) && ((max < 0) || (sent < max));) {
320 if(block(dst, EV_WRITE, 120) <= 0)
321 return(-1);
322 smax = buf->d - dataoff;
323 if(sent + smax > max)
324 smax = max - sent;
325 ret = send(dst, buf->b + dataoff, smax, MSG_NOSIGNAL | MSG_DONTWAIT);
326 if(ret < 0)
327 return(-1);
328 dataoff += ret;
329 sent += ret;
330 }
331 bufeat(*buf, dataoff);
332 }
333 return(sent);
334}
335
66987955
FT
336static void serve(struct muth *muth, va_list args)
337{
338 vavar(int, fd);
9d87a119
FT
339 vavar(struct sockaddr_storage, name);
340 int cfd;
341 char old;
edad3c6a 342 char *hd, *p;
9d87a119
FT
343 struct charbuf inbuf, outbuf;
344 struct hthead *req, *resp;
a0327573 345 off_t dlen, sent;
d93d9a05 346 ssize_t headoff;
9d87a119 347 char nmbuf[256];
66987955 348
9d87a119
FT
349 bufinit(inbuf);
350 bufinit(outbuf);
351 cfd = -1;
3c296bd4 352 req = resp = NULL;
66987955 353 while(1) {
9d87a119
FT
354 /*
355 * First, find and decode the header:
356 */
357 if((headoff = readhead(fd, &inbuf)) < 0)
358 goto out;
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. */
364 goto out;
365 }
366 old = inbuf.b[headoff];
367 inbuf.b[headoff] = 0;
368 if((req = parserawreq(inbuf.b)) == NULL)
66987955 369 goto out;
9d87a119
FT
370 inbuf.b[headoff] = old;
371 bufeat(inbuf, headoff);
edad3c6a
FT
372 /* We strip off the leading slash and any param string from
373 * the rest string, so that multiplexers can parse
374 * coherently. */
9e9eca79
FT
375 if(req->rest[0] == '/')
376 replrest(req, req->rest + 1);
edad3c6a
FT
377 if((p = strchr(req->rest, '?')) != NULL)
378 *p = 0;
9d87a119
FT
379
380 /*
381 * Add metainformation and then send the request to the root
382 * multiplexer:
383 */
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)));
390 }
3c296bd4
FT
391 if((cfd = sendreq(plex, req)) < 0)
392 goto out;
a0327573
FT
393
394 /*
395 * If there is message data, pass it:
396 */
397 if((hd = getheader(req, "content-length")) != NULL) {
398 dlen = atoo(hd);
399 if(dlen > 0)
400 passdata(fd, cfd, &inbuf, dlen);
401 }
d93d9a05
FT
402 /* Make sure to send EOF */
403 shutdown(cfd, SHUT_WR);
9d87a119
FT
404
405 /*
406 * Find and decode the response header:
407 */
408 outbuf.d = 0;
3c296bd4
FT
409 if((headoff = readhead(cfd, &outbuf)) < 0)
410 goto out;
9d87a119
FT
411 hd = memcpy(smalloc(headoff + 1), outbuf.b, headoff);
412 hd[headoff] = 0;
413 if((resp = parserawresp(hd)) == NULL)
66987955 414 goto out;
9d87a119
FT
415
416 /*
417 * Pass the actual output:
418 */
419 sizebuf(outbuf, 65536);
420 sent = passdata(cfd, fd, &outbuf, -1);
421 sent -= headoff;
422
423 /*
424 * Check for connection expiry
425 */
426 if(strcasecmp(req->method, "head")) {
427 if((hd = getheader(resp, "content-length")) != NULL) {
428 if(sent != atoo(hd)) {
429 /* Exit because of error */
430 goto out;
431 }
432 } else {
433 if(((hd = getheader(resp, "transfer-encoding")) == NULL) || !strcasecmp(hd, "identity"))
434 break;
435 }
436 if(((hd = getheader(req, "connection")) != NULL) && !strcasecmp(hd, "close"))
437 break;
438 if(((hd = getheader(resp, "connection")) != NULL) && !strcasecmp(hd, "close"))
439 break;
440 }
441
442 close(cfd);
443 cfd = -1;
444 freehthead(req);
445 req = NULL;
446 freehthead(resp);
447 resp = NULL;
66987955
FT
448 }
449
450out:
9d87a119
FT
451 if(cfd >= 0)
452 close(cfd);
453 if(req != NULL)
454 freehthead(req);
455 if(resp != NULL)
456 freehthead(resp);
457 buffree(inbuf);
458 buffree(outbuf);
66987955
FT
459 close(fd);
460}
461
f4cdf919
FT
462static void listenloop(struct muth *muth, va_list args)
463{
464 vavar(int, ss);
465 int ns;
466 struct sockaddr_storage name;
467 socklen_t namelen;
468
469 while(1) {
470 namelen = sizeof(name);
9d87a119 471 block(ss, EV_READ, 0);
f4cdf919 472 ns = accept(ss, (struct sockaddr *)&name, &namelen);
66987955
FT
473 if(ns < 0) {
474 flog(LOG_ERR, "accept: %s", strerror(errno));
475 goto out;
476 }
9d87a119 477 mustart(serve, ns, name);
f4cdf919 478 }
66987955
FT
479
480out:
481 close(ss);
f4cdf919
FT
482}
483
484static void ioloop(void)
485{
486 int ret;
487 fd_set rfds, wfds, efds;
488 struct blocker *bl, *nbl;
9d87a119
FT
489 struct timeval toval;
490 time_t now, timeout;
f4cdf919
FT
491 int maxfd;
492 int ev;
493
66987955 494 while(blockers != NULL) {
f4cdf919
FT
495 FD_ZERO(&rfds);
496 FD_ZERO(&wfds);
497 FD_ZERO(&efds);
498 maxfd = 0;
9d87a119
FT
499 now = time(NULL);
500 timeout = 0;
f4cdf919
FT
501 for(bl = blockers; bl; bl = bl->n) {
502 if(bl->ev & EV_READ)
503 FD_SET(bl->fd, &rfds);
504 if(bl->ev & EV_WRITE)
505 FD_SET(bl->fd, &wfds);
506 FD_SET(bl->fd, &efds);
507 if(bl->fd > maxfd)
508 maxfd = bl->fd;
9d87a119
FT
509 if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
510 timeout = bl->to;
f4cdf919 511 }
9d87a119
FT
512 toval.tv_sec = timeout - now;
513 toval.tv_usec = 0;
514 ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
f4cdf919
FT
515 if(ret < 0) {
516 if(errno != EINTR) {
517 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
518 /* To avoid CPU hogging in case it's bad, which it
519 * probably is. */
520 sleep(1);
521 }
522 }
9d87a119 523 now = time(NULL);
f4cdf919
FT
524 for(bl = blockers; bl; bl = nbl) {
525 nbl = bl->n;
526 ev = 0;
527 if(FD_ISSET(bl->fd, &rfds))
528 ev |= EV_READ;
529 if(FD_ISSET(bl->fd, &wfds))
530 ev |= EV_WRITE;
531 if(FD_ISSET(bl->fd, &efds))
532 ev = -1;
66987955
FT
533 if(ev != 0)
534 resume(bl->th, ev);
9d87a119
FT
535 else if((bl->to != 0) && (bl->to <= now))
536 resume(bl->th, 0);
f4cdf919
FT
537 }
538 }
539}
f0bbedf7
FT
540
541int main(int argc, char **argv)
542{
f4cdf919
FT
543 int fd;
544
9d87a119
FT
545 if(argc < 2) {
546 fprintf(stderr, "usage: htparser ROOT [ARGS...]\n");
547 exit(1);
548 }
549 if((plex = stdmkchild(argv + 1)) < 0) {
550 flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno));
551 return(1);
552 }
f4cdf919
FT
553 if((fd = listensock6(8080)) < 0) {
554 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
555 return(1);
556 }
557 mustart(listenloop, fd);
558 if((fd = listensock4(8080)) < 0) {
559 if(errno != EADDRINUSE) {
560 flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
561 return(1);
562 }
563 } else {
564 mustart(listenloop, fd);
565 }
566 ioloop();
567 return(0);
f0bbedf7 568}