Blob


1 /*
2 * This file is in the public domain.
3 */
5 #include <sys/queue.h>
6 #include <sys/tree.h>
7 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
12 #include <errno.h>
13 #include <event.h>
14 #include <limits.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdint.h>
19 #include <string.h>
20 #include <unistd.h>
22 #include "msearchd.h"
24 #define MIN(a, b) ((a) < (b) ? (a) : (b))
26 struct fcgi_header {
27 unsigned char version;
28 unsigned char type;
29 unsigned char req_id1;
30 unsigned char req_id0;
31 unsigned char content_len1;
32 unsigned char content_len0;
33 unsigned char padding;
34 unsigned char reserved;
35 } __attribute__((packed));
37 /*
38 * number of bytes in a FCGI_HEADER. Future version of the protocol
39 * will not reduce this number.
40 */
41 #define FCGI_HEADER_LEN 8
43 /*
44 * values for the version component
45 */
46 #define FCGI_VERSION_1 1
48 /*
49 * values for the type component
50 */
51 #define FCGI_BEGIN_REQUEST 1
52 #define FCGI_ABORT_REQUEST 2
53 #define FCGI_END_REQUEST 3
54 #define FCGI_PARAMS 4
55 #define FCGI_STDIN 5
56 #define FCGI_STDOUT 6
57 #define FCGI_STDERR 7
58 #define FCGI_DATA 8
59 #define FCGI_GET_VALUES 9
60 #define FCGI_GET_VALUES_RESULT 10
61 #define FCGI_UNKNOWN_TYPE 11
62 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
64 struct fcgi_begin_req {
65 unsigned char role1;
66 unsigned char role0;
67 unsigned char flags;
68 unsigned char reserved[5];
69 };
71 struct fcgi_begin_req_record {
72 struct fcgi_header header;
73 struct fcgi_begin_req body;
74 };
76 /*
77 * mask for flags;
78 */
79 #define FCGI_KEEP_CONN 1
81 /*
82 * values for the role
83 */
84 #define FCGI_RESPONDER 1
85 #define FCGI_AUTHORIZER 2
86 #define FCGI_FILTER 3
88 struct fcgi_end_req_body {
89 unsigned char app_status3;
90 unsigned char app_status2;
91 unsigned char app_status1;
92 unsigned char app_status0;
93 unsigned char proto_status;
94 unsigned char reserved[3];
95 };
97 /*
98 * values for proto_status
99 */
100 #define FCGI_REQUEST_COMPLETE 0
101 #define FCGI_CANT_MPX_CONN 1
102 #define FCGI_OVERLOADED 2
103 #define FCGI_UNKNOWN_ROLE 3
105 /*
106 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
107 * records.
108 */
109 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
110 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
111 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
113 #define CAT(f0, f1) ((f0) + ((f1) << 8))
115 enum {
116 FCGI_RECORD_HEADER,
117 FCGI_RECORD_BODY,
118 };
120 volatile int fcgi_inflight;
121 int32_t fcgi_id;
123 int accept_reserve(int, struct sockaddr *, socklen_t *, int,
124 volatile int *);
126 static int
127 fcgi_send_end_req(struct fcgi *fcgi, int id, int as, int ps)
129 struct bufferevent *bev = fcgi->fcg_bev;
130 struct fcgi_header hdr;
131 struct fcgi_end_req_body end;
133 memset(&hdr, 0, sizeof(hdr));
134 memset(&end, 0, sizeof(end));
136 hdr.version = FCGI_VERSION_1;
137 hdr.type = FCGI_END_REQUEST;
138 hdr.req_id0 = (id & 0xFF);
139 hdr.req_id1 = (id >> 8);
140 hdr.content_len0 = sizeof(end);
142 end.app_status0 = (unsigned char)as;
143 end.proto_status = (unsigned char)ps;
145 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1)
146 return (-1);
147 if (bufferevent_write(bev, &end, sizeof(end)) == -1)
148 return (-1);
149 return (0);
152 static int
153 end_request(struct client *clt, int status, int proto_status)
155 struct fcgi *fcgi = clt->clt_fcgi;
156 int r;
158 if (clt_flush(clt) == -1)
159 return (-1);
161 r = fcgi_send_end_req(fcgi, clt->clt_id, status,
162 proto_status);
163 if (r == -1) {
164 fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi);
165 return (-1);
168 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
169 server_client_free(clt);
171 if (!fcgi->fcg_keep_conn)
172 fcgi->fcg_done = 1;
174 return (0);
177 int
178 fcgi_end_request(struct client *clt, int status)
180 return (end_request(clt, status, FCGI_REQUEST_COMPLETE));
183 int
184 fcgi_abort_request(struct client *clt)
186 return (end_request(clt, 1, FCGI_OVERLOADED));
189 static void
190 fcgi_inflight_dec(const char *why)
192 fcgi_inflight--;
193 log_debug("%s: fcgi inflight decremented, now %d, %s",
194 __func__, fcgi_inflight, why);
197 void
198 fcgi_accept(int fd, short event, void *arg)
200 struct env *env = arg;
201 struct fcgi *fcgi = NULL;
202 socklen_t slen;
203 struct sockaddr_storage ss;
204 int s = -1;
206 event_add(&env->env_pausev, NULL);
207 if ((event & EV_TIMEOUT))
208 return;
210 slen = sizeof(ss);
211 if ((s = accept_reserve(env->env_sockfd, (struct sockaddr *)&ss,
212 &slen, FD_RESERVE, &fcgi_inflight)) == -1) {
213 /*
214 * Pause accept if we are out of file descriptors, or
215 * libevent will haunt us here too.
216 */
217 if (errno == ENFILE || errno == EMFILE) {
218 struct timeval evtpause = { 1, 0 };
220 event_del(&env->env_sockev);
221 evtimer_add(&env->env_pausev, &evtpause);
222 log_debug("%s: deferring connections", __func__);
224 return;
227 if ((fcgi = calloc(1, sizeof(*fcgi))) == NULL)
228 goto err;
230 fcgi->fcg_id = ++fcgi_id;
231 fcgi->fcg_s = s;
232 fcgi->fcg_env = env;
233 fcgi->fcg_want = FCGI_RECORD_HEADER;
234 fcgi->fcg_toread = sizeof(struct fcgi_header);
235 SPLAY_INIT(&fcgi->fcg_clients);
237 /* assume it's enabled until we get a FCGI_BEGIN_REQUEST */
238 fcgi->fcg_keep_conn = 1;
240 fcgi->fcg_bev = bufferevent_new(fcgi->fcg_s, fcgi_read, fcgi_write,
241 fcgi_error, fcgi);
242 if (fcgi->fcg_bev == NULL)
243 goto err;
245 bufferevent_enable(fcgi->fcg_bev, EV_READ | EV_WRITE);
246 return;
248 err:
249 if (s != -1) {
250 close(s);
251 free(fcgi);
252 fcgi_inflight_dec(__func__);
256 static int
257 parse_len(struct fcgi *fcgi, struct evbuffer *src)
259 unsigned char c, x[3];
261 fcgi->fcg_toread--;
262 evbuffer_remove(src, &c, 1);
263 if (c >> 7 == 0)
264 return (c);
266 if (fcgi->fcg_toread < 3)
267 return (-1);
269 fcgi->fcg_toread -= 3;
270 evbuffer_remove(src, x, sizeof(x));
271 return (((c & 0x7F) << 24) | (x[0] << 16) | (x[1] << 8) | x[2]);
274 static int
275 fcgi_parse_params(struct fcgi *fcgi, struct evbuffer *src, struct client *clt)
277 char pname[32];
278 char server[HOST_NAME_MAX + 1];
279 char path[PATH_MAX];
280 char query[QUERY_MAXLEN];
281 char method[8];
282 int nlen, vlen;
284 while (fcgi->fcg_toread > 0) {
285 if ((nlen = parse_len(fcgi, src)) < 0 ||
286 (vlen = parse_len(fcgi, src)) < 0)
287 return (-1);
289 if (fcgi->fcg_toread < nlen + vlen)
290 return (-1);
292 if ((size_t)nlen > sizeof(pname) - 1) {
293 /* ignore this parameter */
294 fcgi->fcg_toread -= nlen - vlen;
295 evbuffer_drain(src, nlen + vlen);
296 continue;
299 fcgi->fcg_toread -= nlen;
300 evbuffer_remove(src, &pname, nlen);
301 pname[nlen] = '\0';
303 if (!strcmp(pname, "SERVER_NAME") &&
304 (size_t)vlen < sizeof(server)) {
305 fcgi->fcg_toread -= vlen;
306 evbuffer_remove(src, &server, vlen);
307 server[vlen] = '\0';
309 free(clt->clt_server_name);
310 if ((clt->clt_server_name = strdup(server)) == NULL)
311 return (-1);
312 DPRINTF("clt %d: server_name: %s", clt->clt_id,
313 clt->clt_server_name);
314 continue;
317 if (!strcmp(pname, "SCRIPT_NAME") &&
318 (size_t)vlen < sizeof(path)) {
319 fcgi->fcg_toread -= vlen;
320 evbuffer_remove(src, &path, vlen);
321 path[vlen] = '\0';
323 free(clt->clt_script_name);
324 clt->clt_script_name = NULL;
326 if (vlen == 0 || path[vlen - 1] != '/')
327 asprintf(&clt->clt_script_name, "%s/", path);
328 else
329 clt->clt_script_name = strdup(path);
331 if (clt->clt_script_name == NULL)
332 return (-1);
334 DPRINTF("clt %d: script_name: %s", clt->clt_id,
335 clt->clt_script_name);
336 continue;
339 if (!strcmp(pname, "PATH_INFO") &&
340 (size_t)vlen < sizeof(path)) {
341 fcgi->fcg_toread -= vlen;
342 evbuffer_remove(src, &path, vlen);
343 path[vlen] = '\0';
345 free(clt->clt_path_info);
346 clt->clt_path_info = NULL;
348 if (*path != '/')
349 asprintf(&clt->clt_path_info, "/%s", path);
350 else
351 clt->clt_path_info = strdup(path);
353 if (clt->clt_path_info == NULL)
354 return (-1);
356 DPRINTF("clt %d: path_info: %s", clt->clt_id,
357 clt->clt_path_info);
358 continue;
361 if (!strcmp(pname, "QUERY_STRING") &&
362 (size_t)vlen < sizeof(query) &&
363 vlen > 0) {
364 fcgi->fcg_toread -= vlen;
365 evbuffer_remove(src, &query, vlen);
366 query[vlen] = '\0';
368 free(clt->clt_query);
369 if ((clt->clt_query = strdup(query)) == NULL)
370 return (-1);
372 DPRINTF("clt %d: query: %s", clt->clt_id,
373 clt->clt_query);
374 continue;
377 if (!strcmp(pname, "REQUEST_METHOD") &&
378 (size_t)vlen < sizeof(method)) {
379 fcgi->fcg_toread -= vlen;
380 evbuffer_remove(src, &method, vlen);
381 method[vlen] = '\0';
383 if (!strcasecmp(method, "GET"))
384 clt->clt_method = METHOD_GET;
385 if (!strcasecmp(method, "POST"))
386 clt->clt_method = METHOD_POST;
388 continue;
391 fcgi->fcg_toread -= vlen;
392 evbuffer_drain(src, vlen);
395 return (0);
398 void
399 fcgi_read(struct bufferevent *bev, void *d)
401 struct fcgi *fcgi = d;
402 struct env *env = fcgi->fcg_env;
403 struct evbuffer *src = EVBUFFER_INPUT(bev);
404 struct fcgi_header hdr;
405 struct fcgi_begin_req breq;
406 struct client *clt, q;
407 int role;
409 memset(&q, 0, sizeof(q));
411 for (;;) {
412 if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread)
413 return;
415 if (fcgi->fcg_want == FCGI_RECORD_HEADER) {
416 fcgi->fcg_want = FCGI_RECORD_BODY;
417 bufferevent_read(bev, &hdr, sizeof(hdr));
419 DPRINTF("header: v=%d t=%d id=%d len=%d p=%d",
420 hdr.version, hdr.type,
421 CAT(hdr.req_id0, hdr.req_id1),
422 CAT(hdr.content_len0, hdr.content_len1),
423 hdr.padding);
425 if (hdr.version != FCGI_VERSION_1) {
426 log_warnx("unknown fastcgi version: %d",
427 hdr.version);
428 fcgi_error(bev, EV_READ, d);
429 return;
432 fcgi->fcg_toread = CAT(hdr.content_len0,
433 hdr.content_len1);
434 if (fcgi->fcg_toread < 0) {
435 log_warnx("invalid record length: %d",
436 fcgi->fcg_toread);
437 fcgi_error(bev, EV_READ, d);
438 return;
441 fcgi->fcg_padding = hdr.padding;
442 if (fcgi->fcg_padding < 0) {
443 log_warnx("invalid padding: %d",
444 fcgi->fcg_padding);
445 fcgi_error(bev, EV_READ, d);
446 return;
449 fcgi->fcg_type = hdr.type;
450 fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1);
451 continue;
454 q.clt_id = fcgi->fcg_rec_id;
455 clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q);
457 switch (fcgi->fcg_type) {
458 case FCGI_BEGIN_REQUEST:
459 if (sizeof(breq) != fcgi->fcg_toread) {
460 log_warnx("unexpected size for "
461 "FCGI_BEGIN_REQUEST");
462 fcgi_error(bev, EV_READ, d);
463 return;
466 evbuffer_remove(src, &breq, sizeof(breq));
468 role = CAT(breq.role0, breq.role1);
469 if (role != FCGI_RESPONDER) {
470 log_warnx("unknown fastcgi role: %d",
471 role);
472 if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id,
473 1, FCGI_UNKNOWN_ROLE) == -1) {
474 fcgi_error(bev, EV_READ, d);
475 return;
477 break;
480 if (!fcgi->fcg_keep_conn) {
481 log_warnx("trying to reuse the fastcgi "
482 "socket without marking it as so.");
483 fcgi_error(bev, EV_READ, d);
484 return;
486 fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN;
488 if (clt != NULL) {
489 log_warnx("ignoring attemp to re-use an "
490 "active request id (%d)",
491 fcgi->fcg_rec_id);
492 break;
495 if ((clt = calloc(1, sizeof(*clt))) == NULL) {
496 log_warnx("calloc");
497 break;
500 clt->clt_id = fcgi->fcg_rec_id;
501 clt->clt_fd = -1;
502 clt->clt_fcgi = fcgi;
503 SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt);
504 break;
505 case FCGI_PARAMS:
506 if (clt == NULL) {
507 log_warnx("got FCGI_PARAMS for inactive id "
508 "(%d)", fcgi->fcg_rec_id);
509 evbuffer_drain(src, fcgi->fcg_toread);
510 break;
512 if (fcgi->fcg_toread == 0) {
513 evbuffer_drain(src, fcgi->fcg_toread);
514 if (server_handle(env, clt) == -1)
515 return;
516 break;
518 if (fcgi_parse_params(fcgi, src, clt) == -1) {
519 log_warnx("fcgi_parse_params failed");
520 fcgi_error(bev, EV_READ, d);
521 return;
523 break;
524 case FCGI_STDIN:
525 /* not interested in reading stdin */
526 evbuffer_drain(src, fcgi->fcg_toread);
527 break;
528 case FCGI_ABORT_REQUEST:
529 if (clt == NULL) {
530 log_warnx("got FCGI_ABORT_REQUEST for inactive"
531 " id (%d)", fcgi->fcg_rec_id);
532 evbuffer_drain(src, fcgi->fcg_toread);
533 break;
535 if (fcgi_end_request(clt, 1) == -1) {
536 /* calls fcgi_error on failure */
537 return;
539 break;
540 default:
541 log_warnx("unknown fastcgi record type %d",
542 fcgi->fcg_type);
543 evbuffer_drain(src, fcgi->fcg_toread);
544 break;
547 /* Prepare for the next record. */
548 evbuffer_drain(src, fcgi->fcg_padding);
549 fcgi->fcg_want = FCGI_RECORD_HEADER;
550 fcgi->fcg_toread = sizeof(struct fcgi_header);
554 void
555 fcgi_write(struct bufferevent *bev, void *d)
557 struct fcgi *fcgi = d;
558 struct evbuffer *out = EVBUFFER_OUTPUT(bev);
560 if (fcgi->fcg_done && EVBUFFER_LENGTH(out) == 0)
561 fcgi_error(bev, EVBUFFER_EOF, fcgi);
564 void
565 fcgi_error(struct bufferevent *bev, short event, void *d)
567 struct fcgi *fcgi = d;
568 struct env *env = fcgi->fcg_env;
569 struct client *clt;
571 log_debug("fcgi failure, shutting down connection (ev: %x)",
572 event);
573 fcgi_inflight_dec(__func__);
575 while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) {
576 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
577 server_client_free(clt);
580 SPLAY_REMOVE(fcgi_tree, &env->env_fcgi_socks, fcgi);
581 fcgi_free(fcgi);
583 return;
586 void
587 fcgi_free(struct fcgi *fcgi)
589 close(fcgi->fcg_s);
590 bufferevent_free(fcgi->fcg_bev);
591 free(fcgi);
594 int
595 clt_flush(struct client *clt)
597 struct fcgi *fcgi = clt->clt_fcgi;
598 struct bufferevent *bev = fcgi->fcg_bev;
599 struct fcgi_header hdr;
601 if (clt->clt_buflen == 0)
602 return (0);
604 memset(&hdr, 0, sizeof(hdr));
605 hdr.version = FCGI_VERSION_1;
606 hdr.type = FCGI_STDOUT;
607 hdr.req_id0 = (clt->clt_id & 0xFF);
608 hdr.req_id1 = (clt->clt_id >> 8);
609 hdr.content_len0 = (clt->clt_buflen & 0xFF);
610 hdr.content_len1 = (clt->clt_buflen >> 8);
612 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 ||
613 bufferevent_write(bev, clt->clt_buf, clt->clt_buflen) == -1) {
614 fcgi_error(bev, EV_WRITE, fcgi);
615 return (-1);
618 clt->clt_buflen = 0;
620 return (0);
623 int
624 clt_write(struct client *clt, const uint8_t *buf, size_t len)
626 size_t left, copy;
628 while (len > 0) {
629 left = sizeof(clt->clt_buf) - clt->clt_buflen;
630 if (left == 0) {
631 if (clt_flush(clt) == -1)
632 return (-1);
633 left = sizeof(clt->clt_buf);
636 copy = MIN(left, len);
638 memcpy(&clt->clt_buf[clt->clt_buflen], buf, copy);
639 clt->clt_buflen += copy;
640 buf += copy;
641 len -= copy;
644 return (0);
647 int
648 clt_putc(struct client *clt, char ch)
650 return (clt_write(clt, &ch, 1));
653 int
654 clt_puts(struct client *clt, const char *str)
656 return (clt_write(clt, str, strlen(str)));
659 int
660 clt_write_bufferevent(struct client *clt, struct bufferevent *bev)
662 struct evbuffer *src = EVBUFFER_INPUT(bev);
663 size_t len, left, copy;
665 len = EVBUFFER_LENGTH(src);
666 while (len > 0) {
667 left = sizeof(clt->clt_buf) - clt->clt_buflen;
668 if (left == 0) {
669 if (clt_flush(clt) == -1)
670 return (-1);
671 left = sizeof(clt->clt_buf);
674 copy = bufferevent_read(bev, &clt->clt_buf[clt->clt_buflen],
675 MIN(left, len));
676 clt->clt_buflen += copy;
678 len = EVBUFFER_LENGTH(src);
681 return (0);
684 int
685 clt_putsan(struct client *clt, const char *s)
687 int r;
689 if (s == NULL)
690 return (0);
692 for (; *s; ++s) {
693 switch (*s) {
694 case '<':
695 r = clt_puts(clt, "&lt;");
696 break;
697 case '>':
698 r = clt_puts(clt, "&gt;");
699 break;
700 case '&':
701 r = clt_puts(clt, "&amp;");
702 break;
703 case '"':
704 r = clt_puts(clt, "&quot;");
705 break;
706 case '\'':
707 r = clt_puts(clt, "&apos;");
708 break;
709 default:
710 r = clt_putc(clt, *s);
711 break;
714 if (r == -1)
715 return (-1);
718 return (0);
721 int
722 clt_printf(struct client *clt, const char *fmt, ...)
724 struct fcgi *fcgi = clt->clt_fcgi;
725 struct bufferevent *bev = fcgi->fcg_bev;
726 char *str;
727 va_list ap;
728 int r;
730 va_start(ap, fmt);
731 r = vasprintf(&str, fmt, ap);
732 va_end(ap);
733 if (r == -1) {
734 fcgi_error(bev, EV_WRITE, fcgi);
735 return (-1);
738 r = clt_write(clt, str, r);
739 free(str);
740 return (r);
743 int
744 fcgi_cmp(struct fcgi *a, struct fcgi *b)
746 return ((int)a->fcg_id - b->fcg_id);
749 int
750 fcgi_client_cmp(struct client *a, struct client *b)
752 return ((int)a->clt_id - b->clt_id);
755 int
756 accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
757 int reserve, volatile int *counter)
759 int ret;
761 if (getdtablecount() + reserve + *counter >= getdtablesize()) {
762 errno = EMFILE;
763 return (-1);
766 if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) {
767 (*counter)++;
768 log_debug("%s: inflight incremented, now %d", __func__,
769 *counter);
772 return (ret);
775 SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp);
776 SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp);