Use SOCK_SEQPACKET between processes to ensure EOF receipt. Probably only works on...
[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>
26#include <errno.h>
f0bbedf7
FT
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <utils.h>
f4cdf919
FT
32#include <mt.h>
33#include <log.h>
66987955 34#include <req.h>
f4cdf919
FT
35
36#define EV_READ 1
37#define EV_WRITE 2
38
39struct blocker {
40 struct blocker *n, *p;
41 int fd;
42 int ev;
43 struct muth *th;
44};
45
46static struct blocker *blockers;
47
48static int block(int fd, int ev)
49{
50 struct blocker *bl;
51 int rv;
52
53 omalloc(bl);
54 bl->fd = fd;
55 bl->ev = ev;
56 bl->th = current;
57 bl->n = blockers;
58 if(blockers)
59 blockers->p = bl;
60 blockers = bl;
61 rv = yield();
62 if(bl->n)
63 bl->n->p = bl->p;
64 if(bl->p)
65 bl->p->n = bl->n;
66 if(bl == blockers)
67 blockers = bl->n;
68 return(rv);
69}
70
71static int listensock4(int port)
72{
73 struct sockaddr_in name;
74 int fd;
75 int valbuf;
76
77 memset(&name, 0, sizeof(name));
78 name.sin_family = AF_INET;
79 name.sin_port = htons(port);
80 if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
81 return(-1);
82 valbuf = 1;
83 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
84 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
85 close(fd);
86 return(-1);
87 }
88 if(listen(fd, 16) < 0) {
89 close(fd);
90 return(-1);
91 }
92 return(fd);
93}
94
95static int listensock6(int port)
96{
97 struct sockaddr_in6 name;
98 int fd;
99 int valbuf;
100
101 memset(&name, 0, sizeof(name));
102 name.sin6_family = AF_INET6;
103 name.sin6_port = htons(port);
104 if((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0)
105 return(-1);
106 valbuf = 1;
107 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &valbuf, sizeof(valbuf));
108 if(bind(fd, (struct sockaddr *)&name, sizeof(name))) {
109 close(fd);
110 return(-1);
111 }
112 if(listen(fd, 16) < 0) {
113 close(fd);
114 return(-1);
115 }
116 return(fd);
117}
118
c9955b14 119static size_t readhead(int fd, struct charbuf *buf)
66987955 120{
66987955 121 int nl;
c9955b14 122 size_t off;
66987955 123
c9955b14
FT
124 int get1(void)
125 {
126 int ret;
127
128 while(!(off < buf->d)) {
129 sizebuf(*buf, buf->d + 1024);
130 ret = recv(fd, buf->b + buf->d, buf->s - buf->d, MSG_DONTWAIT);
131 if(ret <= 0) {
132 if((ret < 0) && (errno == EAGAIN)) {
133 block(fd, EV_READ);
134 continue;
135 }
136 return(-1);
66987955 137 }
c9955b14 138 buf->d += ret;
66987955 139 }
c9955b14
FT
140 return(buf->b[off++]);
141 }
142
143 nl = 0;
144 off = 0;
145 while(1) {
146 switch(get1()) {
147 case '\n':
66987955 148 if(nl)
c9955b14 149 return(off);
66987955 150 nl = 1;
c9955b14
FT
151 break;
152 case '\r':
153 break;
154 case -1:
155 return(-1);
156 default:
66987955 157 nl = 0;
c9955b14 158 break;
66987955
FT
159 }
160 }
66987955
FT
161}
162
163#define SKIPNL(ptr) ({ \
164 int __buf__; \
165 if(*(ptr) == '\r') \
166 *((ptr)++) = 0; \
167 if(*(ptr) != '\n') { \
168 __buf__ = 0; \
169 } else { \
170 *((ptr)++) = 0; \
171 __buf__ = 1; \
172 } \
173 __buf__;})
174static struct htreq *parseraw(char *buf)
175{
176 char *p, *p2, *nl;
177 char *method, *url, *ver;
178 struct htreq *req;
179
180 if((nl = strchr(buf, '\n')) == NULL)
181 return(NULL);
182 if(((p = strchr(buf, ' ')) == NULL) || (p > nl))
183 return(NULL);
184 method = buf;
185 *(p++) = 0;
186 if(((p2 = strchr(p, ' ')) == NULL) || (p2 > nl))
187 return(NULL);
188 url = p;
189 p = p2;
190 *(p++) = 0;
191 if(strncmp(p, "HTTP/", 5))
192 return(NULL);
193 ver = (p += 5);
194 for(; ((*p >= '0') && (*p <= '9')) || (*p == '.'); p++);
195 if(!SKIPNL(p))
196 return(NULL);
197
198 req = mkreq(method, url, ver);
199 while(1) {
200 if(SKIPNL(p)) {
201 if(*p)
c9955b14 202 goto fail;
66987955
FT
203 break;
204 }
205 if((nl = strchr(p, '\n')) == NULL)
c9955b14 206 goto fail;
66987955 207 if(((p2 = strchr(p, ':')) == NULL) || (p2 > nl))
c9955b14 208 goto fail;
66987955
FT
209 *(p2++) = 0;
210 for(; (*p2 == ' ') || (*p2 == '\t'); p2++);
211 for(nl = p2; (*nl != '\r') && (*nl != '\n'); nl++);
212 if(!SKIPNL(nl))
c9955b14 213 goto fail;
66987955
FT
214 reqappheader(req, p, p2);
215 p = nl;
216 }
217 return(req);
c9955b14
FT
218
219fail:
220 freereq(req);
221 return(NULL);
66987955
FT
222}
223
224static void serve(struct muth *muth, va_list args)
225{
226 vavar(int, fd);
c9955b14 227 struct charbuf buf;
66987955 228 struct htreq *req;
c9955b14 229 size_t headoff;
66987955 230
c9955b14 231 bufinit(buf);
66987955 232 while(1) {
c9955b14
FT
233 buf.d = 0;
234 if((headoff = readhead(fd, &buf)) < 0)
66987955 235 goto out;
c9955b14 236 if((req = parseraw(buf.b)) == NULL)
66987955 237 goto out;
c9955b14 238 printf("\"%s\", \"%s\", \"%s\", \"%s\"\n", req->method, req->url, req->ver, getheader(req, "host"));
66987955
FT
239 freereq(req);
240 }
241
242out:
c9955b14 243 buffree(buf);
66987955
FT
244 close(fd);
245}
246
f4cdf919
FT
247static void listenloop(struct muth *muth, va_list args)
248{
249 vavar(int, ss);
250 int ns;
251 struct sockaddr_storage name;
252 socklen_t namelen;
253
254 while(1) {
255 namelen = sizeof(name);
256 block(ss, EV_READ);
257 ns = accept(ss, (struct sockaddr *)&name, &namelen);
66987955
FT
258 if(ns < 0) {
259 flog(LOG_ERR, "accept: %s", strerror(errno));
260 goto out;
261 }
262 mustart(serve, ns);
f4cdf919 263 }
66987955
FT
264
265out:
266 close(ss);
f4cdf919
FT
267}
268
269static void ioloop(void)
270{
271 int ret;
272 fd_set rfds, wfds, efds;
273 struct blocker *bl, *nbl;
274 int maxfd;
275 int ev;
276
66987955 277 while(blockers != NULL) {
f4cdf919
FT
278 FD_ZERO(&rfds);
279 FD_ZERO(&wfds);
280 FD_ZERO(&efds);
281 maxfd = 0;
282 for(bl = blockers; bl; bl = bl->n) {
283 if(bl->ev & EV_READ)
284 FD_SET(bl->fd, &rfds);
285 if(bl->ev & EV_WRITE)
286 FD_SET(bl->fd, &wfds);
287 FD_SET(bl->fd, &efds);
288 if(bl->fd > maxfd)
289 maxfd = bl->fd;
290 }
291 ret = select(maxfd + 1, &rfds, &wfds, &efds, NULL);
292 if(ret < 0) {
293 if(errno != EINTR) {
294 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
295 /* To avoid CPU hogging in case it's bad, which it
296 * probably is. */
297 sleep(1);
298 }
299 }
300 for(bl = blockers; bl; bl = nbl) {
301 nbl = bl->n;
302 ev = 0;
303 if(FD_ISSET(bl->fd, &rfds))
304 ev |= EV_READ;
305 if(FD_ISSET(bl->fd, &wfds))
306 ev |= EV_WRITE;
307 if(FD_ISSET(bl->fd, &efds))
308 ev = -1;
66987955
FT
309 if(ev != 0)
310 resume(bl->th, ev);
f4cdf919
FT
311 }
312 }
313}
f0bbedf7
FT
314
315int main(int argc, char **argv)
316{
f4cdf919
FT
317 int fd;
318
319 if((fd = listensock6(8080)) < 0) {
320 flog(LOG_ERR, "could not listen on IPv6: %s", strerror(errno));
321 return(1);
322 }
323 mustart(listenloop, fd);
324 if((fd = listensock4(8080)) < 0) {
325 if(errno != EADDRINUSE) {
326 flog(LOG_ERR, "could not listen on IPv4: %s", strerror(errno));
327 return(1);
328 }
329 } else {
330 mustart(listenloop, fd);
331 }
332 ioloop();
333 return(0);
f0bbedf7 334}