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 "log.h"
23 #include "msearchd.h"
25 #define MIN(a, b) ((a) < (b) ? (a) : (b))
27 struct fcgi_header {
28 unsigned char version;
29 unsigned char type;
30 unsigned char req_id1;
31 unsigned char req_id0;
32 unsigned char content_len1;
33 unsigned char content_len0;
34 unsigned char padding;
35 unsigned char reserved;
36 } __attribute__((packed));
38 /*
39 * number of bytes in a FCGI_HEADER. Future version of the protocol
40 * will not reduce this number.
41 */
42 #define FCGI_HEADER_LEN 8
44 /*
45 * values for the version component
46 */
47 #define FCGI_VERSION_1 1
49 /*
50 * values for the type component
51 */
52 #define FCGI_BEGIN_REQUEST 1
53 #define FCGI_ABORT_REQUEST 2
54 #define FCGI_END_REQUEST 3
55 #define FCGI_PARAMS 4
56 #define FCGI_STDIN 5
57 #define FCGI_STDOUT 6
58 #define FCGI_STDERR 7
59 #define FCGI_DATA 8
60 #define FCGI_GET_VALUES 9
61 #define FCGI_GET_VALUES_RESULT 10
62 #define FCGI_UNKNOWN_TYPE 11
63 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
65 struct fcgi_begin_req {
66 unsigned char role1;
67 unsigned char role0;
68 unsigned char flags;
69 unsigned char reserved[5];
70 };
72 struct fcgi_begin_req_record {
73 struct fcgi_header header;
74 struct fcgi_begin_req body;
75 };
77 /*
78 * mask for flags;
79 */
80 #define FCGI_KEEP_CONN 1
82 /*
83 * values for the role
84 */
85 #define FCGI_RESPONDER 1
86 #define FCGI_AUTHORIZER 2
87 #define FCGI_FILTER 3
89 struct fcgi_end_req_body {
90 unsigned char app_status3;
91 unsigned char app_status2;
92 unsigned char app_status1;
93 unsigned char app_status0;
94 unsigned char proto_status;
95 unsigned char reserved[3];
96 };
98 /*
99 * values for proto_status
100 */
101 #define FCGI_REQUEST_COMPLETE 0
102 #define FCGI_CANT_MPX_CONN 1
103 #define FCGI_OVERLOADED 2
104 #define FCGI_UNKNOWN_ROLE 3
106 /*
107 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
108 * records.
109 */
110 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
111 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
112 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
114 #define CAT(f0, f1) ((f0) + ((f1) << 8))
116 enum {
117 FCGI_RECORD_HEADER,
118 FCGI_RECORD_BODY,
119 };
121 volatile int fcgi_inflight;
122 int32_t fcgi_id;
124 int accept_reserve(int, struct sockaddr *, socklen_t *, int,
125 volatile int *);
127 static int
128 fcgi_send_end_req(struct fcgi *fcgi, int id, int as, int ps)
130 struct bufferevent *bev = fcgi->fcg_bev;
131 struct fcgi_header hdr;
132 struct fcgi_end_req_body end;
134 memset(&hdr, 0, sizeof(hdr));
135 memset(&end, 0, sizeof(end));
137 hdr.version = FCGI_VERSION_1;
138 hdr.type = FCGI_END_REQUEST;
139 hdr.req_id0 = (id & 0xFF);
140 hdr.req_id1 = (id >> 8);
141 hdr.content_len0 = sizeof(end);
143 end.app_status0 = (unsigned char)as;
144 end.proto_status = (unsigned char)ps;
146 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1)
147 return (-1);
148 if (bufferevent_write(bev, &end, sizeof(end)) == -1)
149 return (-1);
150 return (0);
153 static int
154 end_request(struct client *clt, int status, int proto_status)
156 struct fcgi *fcgi = clt->clt_fcgi;
157 int r;
159 if (clt_flush(clt) == -1)
160 return (-1);
162 r = fcgi_send_end_req(fcgi, clt->clt_id, status,
163 proto_status);
164 if (r == -1) {
165 fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi);
166 return (-1);
169 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
170 server_client_free(clt);
172 if (!fcgi->fcg_keep_conn)
173 fcgi->fcg_done = 1;
175 return (0);
178 int
179 fcgi_end_request(struct client *clt, int status)
181 return (end_request(clt, status, FCGI_REQUEST_COMPLETE));
184 int
185 fcgi_abort_request(struct client *clt)
187 return (end_request(clt, 1, FCGI_OVERLOADED));
190 static void
191 fcgi_inflight_dec(const char *why)
193 fcgi_inflight--;
194 log_debug("%s: fcgi inflight decremented, now %d, %s",
195 __func__, fcgi_inflight, why);
198 void
199 fcgi_accept(int fd, short event, void *arg)
201 struct env *env = arg;
202 struct fcgi *fcgi = NULL;
203 socklen_t slen;
204 struct sockaddr_storage ss;
205 int s = -1;
207 event_add(&env->env_pausev, NULL);
208 if ((event & EV_TIMEOUT))
209 return;
211 slen = sizeof(ss);
212 if ((s = accept_reserve(env->env_sockfd, (struct sockaddr *)&ss,
213 &slen, FD_RESERVE, &fcgi_inflight)) == -1) {
214 /*
215 * Pause accept if we are out of file descriptors, or
216 * libevent will haunt us here too.
217 */
218 if (errno == ENFILE || errno == EMFILE) {
219 struct timeval evtpause = { 1, 0 };
221 event_del(&env->env_sockev);
222 evtimer_add(&env->env_pausev, &evtpause);
223 log_debug("%s: deferring connections", __func__);
225 return;
228 if ((fcgi = calloc(1, sizeof(*fcgi))) == NULL)
229 goto err;
231 fcgi->fcg_id = ++fcgi_id;
232 fcgi->fcg_s = s;
233 fcgi->fcg_env = env;
234 fcgi->fcg_want = FCGI_RECORD_HEADER;
235 fcgi->fcg_toread = sizeof(struct fcgi_header);
236 SPLAY_INIT(&fcgi->fcg_clients);
238 /* assume it's enabled until we get a FCGI_BEGIN_REQUEST */
239 fcgi->fcg_keep_conn = 1;
241 fcgi->fcg_bev = bufferevent_new(fcgi->fcg_s, fcgi_read, fcgi_write,
242 fcgi_error, fcgi);
243 if (fcgi->fcg_bev == NULL)
244 goto err;
246 bufferevent_enable(fcgi->fcg_bev, EV_READ | EV_WRITE);
247 return;
249 err:
250 if (s != -1) {
251 close(s);
252 free(fcgi);
253 fcgi_inflight_dec(__func__);
257 static int
258 parse_len(struct fcgi *fcgi, struct evbuffer *src)
260 unsigned char c, x[3];
262 fcgi->fcg_toread--;
263 evbuffer_remove(src, &c, 1);
264 if (c >> 7 == 0)
265 return (c);
267 if (fcgi->fcg_toread < 3)
268 return (-1);
270 fcgi->fcg_toread -= 3;
271 evbuffer_remove(src, x, sizeof(x));
272 return (((c & 0x7F) << 24) | (x[0] << 16) | (x[1] << 8) | x[2]);
275 static int
276 fcgi_parse_params(struct fcgi *fcgi, struct evbuffer *src, struct client *clt)
278 char pname[32];
279 char server[HOST_NAME_MAX + 1];
280 char path[PATH_MAX];
281 char query[QUERY_MAXLEN];
282 char method[8];
283 int nlen, vlen;
285 while (fcgi->fcg_toread > 0) {
286 if ((nlen = parse_len(fcgi, src)) < 0 ||
287 (vlen = parse_len(fcgi, src)) < 0)
288 return (-1);
290 if (fcgi->fcg_toread < nlen + vlen)
291 return (-1);
293 if ((size_t)nlen > sizeof(pname) - 1) {
294 /* ignore this parameter */
295 fcgi->fcg_toread -= nlen - vlen;
296 evbuffer_drain(src, nlen + vlen);
297 continue;
300 fcgi->fcg_toread -= nlen;
301 evbuffer_remove(src, &pname, nlen);
302 pname[nlen] = '\0';
304 if (!strcmp(pname, "SERVER_NAME") &&
305 (size_t)vlen < sizeof(server)) {
306 fcgi->fcg_toread -= vlen;
307 evbuffer_remove(src, &server, vlen);
308 server[vlen] = '\0';
310 free(clt->clt_server_name);
311 if ((clt->clt_server_name = strdup(server)) == NULL)
312 return (-1);
313 DPRINTF("clt %d: server_name: %s", clt->clt_id,
314 clt->clt_server_name);
315 continue;
318 if (!strcmp(pname, "SCRIPT_NAME") &&
319 (size_t)vlen < sizeof(path)) {
320 fcgi->fcg_toread -= vlen;
321 evbuffer_remove(src, &path, vlen);
322 path[vlen] = '\0';
324 free(clt->clt_script_name);
325 clt->clt_script_name = NULL;
327 if (vlen == 0 || path[vlen - 1] != '/')
328 asprintf(&clt->clt_script_name, "%s/", path);
329 else
330 clt->clt_script_name = strdup(path);
332 if (clt->clt_script_name == NULL)
333 return (-1);
335 DPRINTF("clt %d: script_name: %s", clt->clt_id,
336 clt->clt_script_name);
337 continue;
340 if (!strcmp(pname, "PATH_INFO") &&
341 (size_t)vlen < sizeof(path)) {
342 fcgi->fcg_toread -= vlen;
343 evbuffer_remove(src, &path, vlen);
344 path[vlen] = '\0';
346 free(clt->clt_path_info);
347 clt->clt_path_info = NULL;
349 if (*path != '/')
350 asprintf(&clt->clt_path_info, "/%s", path);
351 else
352 clt->clt_path_info = strdup(path);
354 if (clt->clt_path_info == NULL)
355 return (-1);
357 DPRINTF("clt %d: path_info: %s", clt->clt_id,
358 clt->clt_path_info);
359 continue;
362 if (!strcmp(pname, "QUERY_STRING") &&
363 (size_t)vlen < sizeof(query) &&
364 vlen > 0) {
365 fcgi->fcg_toread -= vlen;
366 evbuffer_remove(src, &query, vlen);
367 query[vlen] = '\0';
369 free(clt->clt_query);
370 if ((clt->clt_query = strdup(query)) == NULL)
371 return (-1);
373 DPRINTF("clt %d: query: %s", clt->clt_id,
374 clt->clt_query);
375 continue;
378 if (!strcmp(pname, "REQUEST_METHOD") &&
379 (size_t)vlen < sizeof(method)) {
380 fcgi->fcg_toread -= vlen;
381 evbuffer_remove(src, &method, vlen);
382 method[vlen] = '\0';
384 if (!strcasecmp(method, "GET"))
385 clt->clt_method = METHOD_GET;
386 if (!strcasecmp(method, "POST"))
387 clt->clt_method = METHOD_POST;
389 continue;
392 fcgi->fcg_toread -= vlen;
393 evbuffer_drain(src, vlen);
396 return (0);
399 void
400 fcgi_read(struct bufferevent *bev, void *d)
402 struct fcgi *fcgi = d;
403 struct env *env = fcgi->fcg_env;
404 struct evbuffer *src = EVBUFFER_INPUT(bev);
405 struct fcgi_header hdr;
406 struct fcgi_begin_req breq;
407 struct client *clt, q;
408 int role;
410 memset(&q, 0, sizeof(q));
412 for (;;) {
413 if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread)
414 return;
416 if (fcgi->fcg_want == FCGI_RECORD_HEADER) {
417 fcgi->fcg_want = FCGI_RECORD_BODY;
418 bufferevent_read(bev, &hdr, sizeof(hdr));
420 DPRINTF("header: v=%d t=%d id=%d len=%d p=%d",
421 hdr.version, hdr.type,
422 CAT(hdr.req_id0, hdr.req_id1),
423 CAT(hdr.content_len0, hdr.content_len1),
424 hdr.padding);
426 if (hdr.version != FCGI_VERSION_1) {
427 log_warnx("unknown fastcgi version: %d",
428 hdr.version);
429 fcgi_error(bev, EV_READ, d);
430 return;
433 fcgi->fcg_toread = CAT(hdr.content_len0,
434 hdr.content_len1);
435 if (fcgi->fcg_toread < 0) {
436 log_warnx("invalid record length: %d",
437 fcgi->fcg_toread);
438 fcgi_error(bev, EV_READ, d);
439 return;
442 fcgi->fcg_padding = hdr.padding;
443 if (fcgi->fcg_padding < 0) {
444 log_warnx("invalid padding: %d",
445 fcgi->fcg_padding);
446 fcgi_error(bev, EV_READ, d);
447 return;
450 fcgi->fcg_type = hdr.type;
451 fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1);
452 continue;
455 q.clt_id = fcgi->fcg_rec_id;
456 clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q);
458 switch (fcgi->fcg_type) {
459 case FCGI_BEGIN_REQUEST:
460 if (sizeof(breq) != fcgi->fcg_toread) {
461 log_warnx("unexpected size for "
462 "FCGI_BEGIN_REQUEST");
463 fcgi_error(bev, EV_READ, d);
464 return;
467 evbuffer_remove(src, &breq, sizeof(breq));
469 role = CAT(breq.role0, breq.role1);
470 if (role != FCGI_RESPONDER) {
471 log_warnx("unknown fastcgi role: %d",
472 role);
473 if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id,
474 1, FCGI_UNKNOWN_ROLE) == -1) {
475 fcgi_error(bev, EV_READ, d);
476 return;
478 break;
481 if (!fcgi->fcg_keep_conn) {
482 log_warnx("trying to reuse the fastcgi "
483 "socket without marking it as so.");
484 fcgi_error(bev, EV_READ, d);
485 return;
487 fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN;
489 if (clt != NULL) {
490 log_warnx("ignoring attemp to re-use an "
491 "active request id (%d)",
492 fcgi->fcg_rec_id);
493 break;
496 if ((clt = calloc(1, sizeof(*clt))) == NULL) {
497 log_warnx("calloc");
498 break;
501 clt->clt_id = fcgi->fcg_rec_id;
502 clt->clt_fd = -1;
503 clt->clt_fcgi = fcgi;
504 SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt);
505 break;
506 case FCGI_PARAMS:
507 if (clt == NULL) {
508 log_warnx("got FCGI_PARAMS for inactive id "
509 "(%d)", fcgi->fcg_rec_id);
510 evbuffer_drain(src, fcgi->fcg_toread);
511 break;
513 if (fcgi->fcg_toread == 0) {
514 evbuffer_drain(src, fcgi->fcg_toread);
515 if (server_handle(env, clt) == -1)
516 return;
517 break;
519 if (fcgi_parse_params(fcgi, src, clt) == -1) {
520 log_warnx("fcgi_parse_params failed");
521 fcgi_error(bev, EV_READ, d);
522 return;
524 break;
525 case FCGI_STDIN:
526 /* not interested in reading stdin */
527 evbuffer_drain(src, fcgi->fcg_toread);
528 break;
529 case FCGI_ABORT_REQUEST:
530 if (clt == NULL) {
531 log_warnx("got FCGI_ABORT_REQUEST for inactive"
532 " id (%d)", fcgi->fcg_rec_id);
533 evbuffer_drain(src, fcgi->fcg_toread);
534 break;
536 if (fcgi_end_request(clt, 1) == -1) {
537 /* calls fcgi_error on failure */
538 return;
540 break;
541 default:
542 log_warnx("unknown fastcgi record type %d",
543 fcgi->fcg_type);
544 evbuffer_drain(src, fcgi->fcg_toread);
545 break;
548 /* Prepare for the next record. */
549 evbuffer_drain(src, fcgi->fcg_padding);
550 fcgi->fcg_want = FCGI_RECORD_HEADER;
551 fcgi->fcg_toread = sizeof(struct fcgi_header);
555 void
556 fcgi_write(struct bufferevent *bev, void *d)
558 struct fcgi *fcgi = d;
559 struct evbuffer *out = EVBUFFER_OUTPUT(bev);
561 if (fcgi->fcg_done && EVBUFFER_LENGTH(out) == 0)
562 fcgi_error(bev, EVBUFFER_EOF, fcgi);
565 void
566 fcgi_error(struct bufferevent *bev, short event, void *d)
568 struct fcgi *fcgi = d;
569 struct env *env = fcgi->fcg_env;
570 struct client *clt;
572 log_debug("fcgi failure, shutting down connection (ev: %x)",
573 event);
574 fcgi_inflight_dec(__func__);
576 while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) {
577 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
578 server_client_free(clt);
581 SPLAY_REMOVE(fcgi_tree, &env->env_fcgi_socks, fcgi);
582 fcgi_free(fcgi);
584 return;
587 void
588 fcgi_free(struct fcgi *fcgi)
590 close(fcgi->fcg_s);
591 bufferevent_free(fcgi->fcg_bev);
592 free(fcgi);
595 int
596 clt_flush(struct client *clt)
598 struct fcgi *fcgi = clt->clt_fcgi;
599 struct bufferevent *bev = fcgi->fcg_bev;
600 struct fcgi_header hdr;
602 if (clt->clt_buflen == 0)
603 return (0);
605 memset(&hdr, 0, sizeof(hdr));
606 hdr.version = FCGI_VERSION_1;
607 hdr.type = FCGI_STDOUT;
608 hdr.req_id0 = (clt->clt_id & 0xFF);
609 hdr.req_id1 = (clt->clt_id >> 8);
610 hdr.content_len0 = (clt->clt_buflen & 0xFF);
611 hdr.content_len1 = (clt->clt_buflen >> 8);
613 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 ||
614 bufferevent_write(bev, clt->clt_buf, clt->clt_buflen) == -1) {
615 fcgi_error(bev, EV_WRITE, fcgi);
616 return (-1);
619 clt->clt_buflen = 0;
621 return (0);
624 int
625 clt_write(struct client *clt, const uint8_t *buf, size_t len)
627 size_t left, copy;
629 while (len > 0) {
630 left = sizeof(clt->clt_buf) - clt->clt_buflen;
631 if (left == 0) {
632 if (clt_flush(clt) == -1)
633 return (-1);
634 left = sizeof(clt->clt_buf);
637 copy = MIN(left, len);
639 memcpy(&clt->clt_buf[clt->clt_buflen], buf, copy);
640 clt->clt_buflen += copy;
641 buf += copy;
642 len -= copy;
645 return (0);
648 int
649 clt_putc(struct client *clt, char ch)
651 return (clt_write(clt, &ch, 1));
654 int
655 clt_puts(struct client *clt, const char *str)
657 return (clt_write(clt, str, strlen(str)));
660 int
661 clt_write_bufferevent(struct client *clt, struct bufferevent *bev)
663 struct evbuffer *src = EVBUFFER_INPUT(bev);
664 size_t len, left, copy;
666 len = EVBUFFER_LENGTH(src);
667 while (len > 0) {
668 left = sizeof(clt->clt_buf) - clt->clt_buflen;
669 if (left == 0) {
670 if (clt_flush(clt) == -1)
671 return (-1);
672 left = sizeof(clt->clt_buf);
675 copy = bufferevent_read(bev, &clt->clt_buf[clt->clt_buflen],
676 MIN(left, len));
677 clt->clt_buflen += copy;
679 len = EVBUFFER_LENGTH(src);
682 return (0);
685 int
686 clt_putsan(struct client *clt, const char *s)
688 int r;
690 if (s == NULL)
691 return (0);
693 for (; *s; ++s) {
694 switch (*s) {
695 case '<':
696 r = clt_puts(clt, "&lt;");
697 break;
698 case '>':
699 r = clt_puts(clt, "&gt;");
700 break;
701 case '&':
702 r = clt_puts(clt, "&amp;");
703 break;
704 case '"':
705 r = clt_puts(clt, "&quot;");
706 break;
707 case '\'':
708 r = clt_puts(clt, "&apos;");
709 break;
710 default:
711 r = clt_putc(clt, *s);
712 break;
715 if (r == -1)
716 return (-1);
719 return (0);
722 int
723 clt_putmatch(struct client *clt, const char *s)
725 int r, instrong = 0, intag = 0, lastnl = 0;
727 if (s == NULL)
728 return (0);
730 for (; *s; ++s) {
731 switch (*s) {
732 case '<':
733 if (!instrong && !strncmp(s, "<strong>", 8)) {
734 instrong = intag = 1;
735 r = clt_putc(clt, *s);
736 } else if (instrong && !strncmp(s, "</strong>", 9)) {
737 instrong = 0;
738 intag = 1;
739 r = clt_putc(clt, *s);
740 } else
741 r = clt_puts(clt, "&lt;");
742 break;
743 case '>':
744 if (intag) {
745 intag = 0;
746 r = clt_putc(clt, *s);
747 } else
748 r = clt_puts(clt, "&gt;");
749 break;
750 case '&':
751 r = clt_puts(clt, "&amp;");
752 break;
753 case '"':
754 r = clt_puts(clt, "&quot;");
755 break;
756 case '\'':
757 r = clt_puts(clt, "&apos;");
758 break;
759 case '\n':
760 if (lastnl)
761 break;
762 r = clt_puts(clt, "<br />");
763 lastnl = 1;
764 break;
765 default:
766 lastnl = 0;
767 r = clt_putc(clt, *s);
768 break;
771 if (r == -1)
772 return (-1);
775 if (instrong) /* something went wrong... */
776 return clt_puts(clt, "</strong>");
777 return (0);
780 int
781 clt_printf(struct client *clt, const char *fmt, ...)
783 struct fcgi *fcgi = clt->clt_fcgi;
784 struct bufferevent *bev = fcgi->fcg_bev;
785 char *str;
786 va_list ap;
787 int r;
789 va_start(ap, fmt);
790 r = vasprintf(&str, fmt, ap);
791 va_end(ap);
792 if (r == -1) {
793 fcgi_error(bev, EV_WRITE, fcgi);
794 return (-1);
797 r = clt_write(clt, str, r);
798 free(str);
799 return (r);
802 int
803 fcgi_cmp(struct fcgi *a, struct fcgi *b)
805 return ((int)a->fcg_id - b->fcg_id);
808 int
809 fcgi_client_cmp(struct client *a, struct client *b)
811 return ((int)a->clt_id - b->clt_id);
814 int
815 accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
816 int reserve, volatile int *counter)
818 int ret;
820 if (getdtablecount() + reserve + *counter >= getdtablesize()) {
821 errno = EMFILE;
822 return (-1);
825 if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) {
826 (*counter)++;
827 log_debug("%s: inflight incremented, now %d", __func__,
828 *counter);
831 return (ret);
834 SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp);
835 SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp);