Merge branch 'master' into timeheap
[ashd.git] / lib / req.c
CommitLineData
33733396
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 <string.h>
41213112
FT
21#include <unistd.h>
22#include <sys/socket.h>
23#include <errno.h>
5fc1bf9f
FT
24#include <ctype.h>
25#include <stdio.h>
470938bd 26#include <fcntl.h>
33733396
FT
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <utils.h>
41213112 32#include <log.h>
33733396 33#include <req.h>
41213112 34#include <proc.h>
e7bdd59a 35#include <bufio.h>
33733396 36
41213112 37struct hthead *mkreq(char *method, char *url, char *ver)
33733396 38{
41213112 39 struct hthead *req;
33733396
FT
40
41 omalloc(req);
42 req->method = sstrdup(method);
43 req->url = sstrdup(url);
44 req->ver = sstrdup(ver);
41213112 45 req->rest = sstrdup(url);
33733396
FT
46 return(req);
47}
48
41213112
FT
49struct hthead *mkresp(int code, char *msg, char *ver)
50{
51 struct hthead *resp;
52
53 omalloc(resp);
54 resp->code = code;
55 resp->msg = sstrdup(msg);
56 resp->ver = sstrdup(ver);
57 return(resp);
58}
59
60void freehthead(struct hthead *head)
33733396
FT
61{
62 int i;
63
41213112
FT
64 if(head->method != NULL)
65 free(head->method);
66 if(head->url != NULL)
67 free(head->url);
68 if(head->msg != NULL)
69 free(head->msg);
70 if(head->ver != NULL)
71 free(head->ver);
72 if(head->rest != NULL)
73 free(head->rest);
74 if(head->headers) {
75 for(i = 0; i < head->noheaders; i++) {
76 free(head->headers[i][0]);
77 free(head->headers[i][1]);
78 free(head->headers[i]);
33733396 79 }
41213112 80 free(head->headers);
33733396 81 }
41213112 82 free(head);
33733396
FT
83}
84
41213112 85char *getheader(struct hthead *head, char *name)
66987955
FT
86{
87 int i;
88
41213112
FT
89 for(i = 0; i < head->noheaders; i++) {
90 if(!strcasecmp(head->headers[i][0], name))
91 return(head->headers[i][1]);
66987955
FT
92 }
93 return(NULL);
94}
95
5fc1bf9f
FT
96static void trim(struct charbuf *buf)
97{
98 char *p;
99
a39d9e16
FT
100 for(p = buf->b; (p - buf->b < buf->d) && isspace(*p); p++);
101 memmove(buf->b, p, buf->d -= (p - buf->b));
102 if(buf->d > 0)
c7b1ef35 103 for(p = buf->b + buf->d - 1; (p > buf->b) && isspace(*p); p--, buf->d--);
5fc1bf9f
FT
104}
105
106int parseheaders(struct hthead *head, FILE *in)
107{
108 int c, state;
109 struct charbuf name, val;
90b0ba0f 110 size_t tsz;
5fc1bf9f
FT
111
112 bufinit(name);
113 bufinit(val);
114 state = 0;
90b0ba0f 115 tsz = 0;
5fc1bf9f
FT
116 while(1) {
117 c = fgetc(in);
90b0ba0f
FT
118 if(++tsz >= 65536)
119 goto fail;
5fc1bf9f
FT
120 again:
121 if(state == 0) {
122 if(c == '\r') {
123 } else if(c == '\n') {
124 break;
125 } else if(c == EOF) {
126 goto fail;
127 } else {
128 state = 1;
129 goto again;
130 }
131 } else if(state == 1) {
132 if(c == ':') {
133 trim(&name);
134 bufadd(name, 0);
135 state = 2;
136 } else if(c == '\r') {
137 } else if(c == '\n') {
138 goto fail;
139 } else if(c == EOF) {
140 goto fail;
141 } else {
142 bufadd(name, c);
143 }
144 } else if(state == 2) {
145 if(c == '\r') {
146 } else if(c == '\n') {
147 trim(&val);
148 bufadd(val, 0);
149 headappheader(head, name.b, val.b);
150 buffree(name);
151 buffree(val);
152 state = 0;
153 } else if(c == EOF) {
154 goto fail;
155 } else {
156 bufadd(val, c);
157 }
158 }
159 }
160 return(0);
161
162fail:
163 buffree(name);
164 buffree(val);
165 return(-1);
166}
167
e7bdd59a
FT
168int parseheadersb(struct hthead *head, struct bufio *in)
169{
170 int c, state;
171 struct charbuf name, val;
172 size_t tsz;
173
174 bufinit(name);
175 bufinit(val);
176 state = 0;
177 tsz = 0;
178 while(1) {
179 c = biogetc(in);
180 if(++tsz >= 65536)
181 goto fail;
182 again:
183 if(state == 0) {
184 if(c == '\r') {
185 } else if(c == '\n') {
186 break;
187 } else if(c == EOF) {
188 goto fail;
189 } else {
190 state = 1;
191 goto again;
192 }
193 } else if(state == 1) {
194 if(c == ':') {
195 trim(&name);
196 bufadd(name, 0);
197 state = 2;
198 } else if(c == '\r') {
199 } else if(c == '\n') {
200 goto fail;
201 } else if(c == EOF) {
202 goto fail;
203 } else {
204 bufadd(name, c);
205 }
206 } else if(state == 2) {
207 if(c == '\r') {
208 } else if(c == '\n') {
209 trim(&val);
210 bufadd(val, 0);
211 headappheader(head, name.b, val.b);
212 buffree(name);
213 buffree(val);
214 state = 0;
215 } else if(c == EOF) {
216 goto fail;
217 } else {
218 bufadd(val, c);
219 }
220 }
221 }
222 return(0);
223
224fail:
225 buffree(name);
226 buffree(val);
227 return(-1);
228}
229
3ef78895
FT
230struct hthead *parseresponse(FILE *in)
231{
232 struct hthead *req;
233 int code;
234 struct charbuf ver, msg;
235 int c;
236
237 req = NULL;
238 bufinit(ver);
239 bufinit(msg);
240 code = 0;
241 while(1) {
242 c = getc(in);
243 if(c == ' ') {
244 break;
245 } else if((c == EOF) || (c < 32) || (c >= 128)) {
246 goto fail;
247 } else {
248 bufadd(ver, c);
249 if(ver.d >= 128)
250 goto fail;
251 }
252 }
253 while(1) {
254 c = getc(in);
255 if(c == ' ') {
256 break;
257 } else if((c == EOF) || (c < '0') || (c > '9')) {
258 goto fail;
259 } else {
260 code = (code * 10) + (c - '0');
261 if(code >= 10000)
262 goto fail;
263 }
264 }
265 while(1) {
266 c = getc(in);
267 if(c == 10) {
268 break;
269 } else if(c == 13) {
270 } else if((c == EOF) || (c < 32)) {
271 goto fail;
272 } else {
273 bufadd(msg, c);
274 if(msg.d >= 512)
275 goto fail;
276 }
277 }
278 bufadd(msg, 0);
279 bufadd(ver, 0);
280 req = mkresp(code, msg.b, ver.b);
281 if(parseheaders(req, in))
282 goto fail;
283 goto out;
284
285fail:
286 if(req != NULL) {
287 freehthead(req);
288 req = NULL;
289 }
290out:
291 buffree(msg);
292 buffree(ver);
293 return(req);
294}
295
e7bdd59a
FT
296struct hthead *parseresponseb(struct bufio *in)
297{
298 struct hthead *req;
299 int code;
300 struct charbuf ver, msg;
301 int c;
302
303 req = NULL;
304 bufinit(ver);
305 bufinit(msg);
306 code = 0;
307 while(1) {
308 c = biogetc(in);
309 if(c == ' ') {
310 break;
311 } else if((c == EOF) || (c < 32) || (c >= 128)) {
312 goto fail;
313 } else {
314 bufadd(ver, c);
315 if(ver.d >= 128)
316 goto fail;
317 }
318 }
319 while(1) {
320 c = biogetc(in);
321 if(c == ' ') {
322 break;
323 } else if((c == EOF) || (c < '0') || (c > '9')) {
324 goto fail;
325 } else {
326 code = (code * 10) + (c - '0');
327 if(code >= 10000)
328 goto fail;
329 }
330 }
331 while(1) {
332 c = biogetc(in);
333 if(c == 10) {
334 break;
335 } else if(c == 13) {
336 } else if((c == EOF) || (c < 32)) {
337 goto fail;
338 } else {
339 bufadd(msg, c);
340 if(msg.d >= 512)
341 goto fail;
342 }
343 }
344 bufadd(msg, 0);
345 bufadd(ver, 0);
346 req = mkresp(code, msg.b, ver.b);
347 if(parseheadersb(req, in))
348 goto fail;
349 goto out;
350
351fail:
352 if(req != NULL) {
353 freehthead(req);
354 req = NULL;
355 }
356out:
357 buffree(msg);
358 buffree(ver);
359 return(req);
360}
361
9e9eca79
FT
362void replrest(struct hthead *head, char *rest)
363{
364 char *tmp;
365
366 /* Do not free the current rest string yet, so that the new one
5fc1bf9f 367 * can be taken from a subpart of the old one. */
9e9eca79
FT
368 tmp = head->rest;
369 head->rest = sstrdup(rest);
370 free(tmp);
371}
372
41213112 373void headpreheader(struct hthead *head, const char *name, const char *val)
33733396 374{
41213112
FT
375 head->headers = srealloc(head->headers, sizeof(*head->headers) * (head->noheaders + 1));
376 memmove(head->headers + 1, head->headers, sizeof(*head->headers) * head->noheaders);
377 head->noheaders++;
378 head->headers[0] = smalloc(sizeof(*head->headers[0]) * 2);
379 head->headers[0][0] = sstrdup(name);
380 head->headers[0][1] = sstrdup(val);
33733396
FT
381}
382
41213112 383void headappheader(struct hthead *head, const char *name, const char *val)
33733396
FT
384{
385 int i;
386
41213112
FT
387 i = head->noheaders++;
388 head->headers = srealloc(head->headers, sizeof(*head->headers) * head->noheaders);
389 head->headers[i] = smalloc(sizeof(*head->headers[i]) * 2);
390 head->headers[i][0] = sstrdup(name);
391 head->headers[i][1] = sstrdup(val);
392}
393
f89ce57a
FT
394void headrmheader(struct hthead *head, const char *name)
395{
396 int i;
397
398 for(i = 0; i < head->noheaders; i++) {
399 if(!strcasecmp(head->headers[i][0], name)) {
400 free(head->headers[i][0]);
401 free(head->headers[i][1]);
402 free(head->headers[i]);
608f4ac7 403 memmove(head->headers + i, head->headers + i + 1, sizeof(head->headers) * (--head->noheaders - i));
f89ce57a
FT
404 return;
405 }
406 }
407}
408
5fc1bf9f
FT
409int writeresp(FILE *out, struct hthead *resp)
410{
411 int i;
412
413 if(fprintf(out, "%s %i %s\r\n", resp->ver, resp->code, resp->msg) < 0)
414 return(-1);
415 for(i = 0; i < resp->noheaders; i++) {
416 if(fprintf(out, "%s: %s\r\n", resp->headers[i][0], resp->headers[i][1]) < 0)
417 return(-1);
418 }
419 return(0);
420}
421
e7bdd59a
FT
422int writerespb(struct bufio *out, struct hthead *resp)
423{
424 int i;
425
426 if(bioprintf(out, "%s %i %s\r\n", resp->ver, resp->code, resp->msg) < 0)
427 return(-1);
428 for(i = 0; i < resp->noheaders; i++) {
429 if(bioprintf(out, "%s: %s\r\n", resp->headers[i][0], resp->headers[i][1]) < 0)
430 return(-1);
431 }
432 return(0);
433}
434
d9f67fea 435int sendreq2(int sock, struct hthead *req, int fd, int flags)
41213112
FT
436{
437 int ret, i;
41213112
FT
438 struct charbuf buf;
439
41213112
FT
440 bufinit(buf);
441 bufcatstr2(buf, req->method);
442 bufcatstr2(buf, req->url);
443 bufcatstr2(buf, req->ver);
444 bufcatstr2(buf, req->rest);
445 for(i = 0; i < req->noheaders; i++) {
446 bufcatstr2(buf, req->headers[i][0]);
447 bufcatstr2(buf, req->headers[i][1]);
448 }
449 bufcatstr2(buf, "");
d9f67fea 450 ret = sendfd2(sock, fd, buf.b, buf.d, flags);
41213112 451 buffree(buf);
af34331c 452 if(ret < 0)
41213112 453 return(-1);
af34331c
FT
454 else
455 return(0);
41213112
FT
456}
457
d9f67fea
FT
458int sendreq(int sock, struct hthead *req, int fd)
459{
096c79fd 460 return(sendreq2(sock, req, fd, MSG_NOSIGNAL));
d9f67fea
FT
461}
462
41213112
FT
463int recvreq(int sock, struct hthead **reqp)
464{
465 int fd;
466 struct charbuf buf;
467 char *p;
468 size_t l;
469 char *name, *val;
470 struct hthead *req;
471
472 if((fd = recvfd(sock, &buf.b, &buf.d)) < 0) {
473 return(-1);
474 }
470938bd 475 fcntl(fd, F_SETFD, FD_CLOEXEC);
41213112
FT
476 buf.s = buf.d;
477 p = buf.b;
478 l = buf.d;
479
480 *reqp = omalloc(req);
481 if((req->method = sstrdup(decstr(&p, &l))) == NULL)
482 goto fail;
483 if((req->url = sstrdup(decstr(&p, &l))) == NULL)
484 goto fail;
485 if((req->ver = sstrdup(decstr(&p, &l))) == NULL)
486 goto fail;
487 if((req->rest = sstrdup(decstr(&p, &l))) == NULL)
488 goto fail;
489
490 while(1) {
491 if(!*(name = decstr(&p, &l)))
492 break;
493 val = decstr(&p, &l);
494 headappheader(req, name, val);
495 }
496
497 buffree(buf);
498 return(fd);
499
500fail:
501 close(fd);
502 freehthead(req);
503 errno = EPROTO;
504 return(-1);
33733396 505}
1604c096
FT
506
507char *unquoteurl(char *in)
508{
509 struct charbuf buf;
510 char *p;
511 int c;
512
513 bufinit(buf);
514 p = in;
515 while(*p) {
516 if(*p == '%') {
517 if(!p[1] || !p[2])
518 goto fail;
519 c = 0;
520 if((p[1] >= '0') && (p[1] <= '9')) c |= (p[1] - '0') << 4;
521 else if((p[1] >= 'a') && (p[1] <= 'f')) c |= (p[1] - 'a' + 10) << 4;
522 else if((p[1] >= 'A') && (p[1] <= 'F')) c |= (p[1] - 'A' + 10) << 4;
523 else goto fail;
524 if((p[2] >= '0') && (p[2] <= '9')) c |= (p[2] - '0');
525 else if((p[2] >= 'a') && (p[2] <= 'f')) c |= (p[2] - 'a' + 10);
526 else if((p[2] >= 'A') && (p[2] <= 'F')) c |= (p[2] - 'A' + 10);
527 else goto fail;
528 bufadd(buf, c);
529 p += 3;
530 } else {
531 bufadd(buf, *(p++));
532 }
533 }
534 bufadd(buf, 0);
535 return(buf.b);
536fail:
537 buffree(buf);
538 return(NULL);
539}