Blob


1 /*
2 * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/queue.h>
18 #include <sys/tree.h>
19 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
24 #include <errno.h>
25 #include <event.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <string.h>
32 #include <unistd.h>
34 #include "log.h"
35 #include "tmpl.h"
37 #include "galileo.h"
39 #define MIN(a, b) ((a) < (b) ? (a) : (b))
41 #define FCGI_MAX_CONTENT_SIZE 65535
43 struct fcgi_header {
44 unsigned char version;
45 unsigned char type;
46 unsigned char req_id1;
47 unsigned char req_id0;
48 unsigned char content_len1;
49 unsigned char content_len0;
50 unsigned char padding;
51 unsigned char reserved;
52 } __attribute__((packed));
54 /*
55 * number of bytes in a FCGI_HEADER. Future version of the protocol
56 * will not reduce this number.
57 */
58 #define FCGI_HEADER_LEN 8
60 /*
61 * values for the version component
62 */
63 #define FCGI_VERSION_1 1
65 /*
66 * values for the type component
67 */
68 #define FCGI_BEGIN_REQUEST 1
69 #define FCGI_ABORT_REQUEST 2
70 #define FCGI_END_REQUEST 3
71 #define FCGI_PARAMS 4
72 #define FCGI_STDIN 5
73 #define FCGI_STDOUT 6
74 #define FCGI_STDERR 7
75 #define FCGI_DATA 8
76 #define FCGI_GET_VALUES 9
77 #define FCGI_GET_VALUES_RESULT 10
78 #define FCGI_UNKNOWN_TYPE 11
79 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
81 struct fcgi_begin_req {
82 unsigned char role1;
83 unsigned char role0;
84 unsigned char flags;
85 unsigned char reserved[5];
86 };
88 struct fcgi_begin_req_record {
89 struct fcgi_header header;
90 struct fcgi_begin_req body;
91 };
93 /*
94 * mask for flags;
95 */
96 #define FCGI_KEEP_CONN 1
98 /*
99 * values for the role
100 */
101 #define FCGI_RESPONDER 1
102 #define FCGI_AUTHORIZER 2
103 #define FCGI_FILTER 3
105 struct fcgi_end_req_body {
106 unsigned char app_status3;
107 unsigned char app_status2;
108 unsigned char app_status1;
109 unsigned char app_status0;
110 unsigned char proto_status;
111 unsigned char reserved[3];
112 };
114 /*
115 * values for proto_status
116 */
117 #define FCGI_REQUEST_COMPLETE 0
118 #define FCGI_CANT_MPX_CONN 1
119 #define FCGI_OVERLOADED 2
120 #define FCGI_UNKNOWN_ROLE 3
122 /*
123 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
124 * records.
125 */
126 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
127 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
128 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
130 #define CAT(f0, f1) ((f0) + ((f1) << 8))
132 enum {
133 FCGI_RECORD_HEADER,
134 FCGI_RECORD_BODY,
135 };
137 volatile int fcgi_inflight;
139 static int
140 fcgi_send_end_req(struct fcgi *fcgi, int id, int as, int ps)
142 struct bufferevent *bev = fcgi->fcg_bev;
143 struct fcgi_header hdr;
144 struct fcgi_end_req_body end;
146 memset(&hdr, 0, sizeof(hdr));
147 memset(&end, 0, sizeof(end));
149 hdr.version = FCGI_VERSION_1;
150 hdr.type = FCGI_END_REQUEST;
151 hdr.req_id0 = (id & 0xFF);
152 hdr.req_id1 = (id >> 8);
153 hdr.content_len0 = sizeof(end);
155 end.app_status0 = (unsigned char)as;
156 end.proto_status = (unsigned char)ps;
158 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1)
159 return (-1);
160 if (bufferevent_write(bev, &end, sizeof(end)) == -1)
161 return (-1);
162 return (0);
165 static int
166 end_request(struct client *clt, int status, int proto_status)
168 struct fcgi *fcgi = clt->clt_fcgi;
169 int r;
171 if (clt_flush(clt) == -1)
172 return (-1);
174 r = fcgi_send_end_req(fcgi, clt->clt_id, status,
175 proto_status);
176 if (r == -1) {
177 fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi);
178 return (-1);
181 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
182 proxy_client_free(clt);
184 if (!fcgi->fcg_keep_conn)
185 fcgi->fcg_done = 1;
187 return (0);
190 int
191 fcgi_end_request(struct client *clt, int status)
193 return (end_request(clt, status, FCGI_REQUEST_COMPLETE));
196 int
197 fcgi_abort_request(struct client *clt)
199 return (end_request(clt, 1, FCGI_OVERLOADED));
202 static void
203 fcgi_inflight_dec(const char *why)
205 fcgi_inflight--;
206 log_debug("%s: fcgi inflight decremented, now %d, %s",
207 __func__, fcgi_inflight, why);
210 void
211 fcgi_accept(int fd, short event, void *arg)
213 struct galileo *env = arg;
214 struct fcgi *fcgi = NULL;
215 socklen_t slen;
216 struct sockaddr_storage ss;
217 int s = -1;
219 event_add(&env->sc_evpause, NULL);
220 if ((event & EV_TIMEOUT))
221 return;
223 slen = sizeof(ss);
224 if ((s = accept_reserve(env->sc_sock_fd, (struct sockaddr *)&ss,
225 &slen, FD_RESERVE, &fcgi_inflight)) == -1) {
226 /*
227 * Pause accept if we are out of file descriptors, or
228 * libevent will haunt us here too.
229 */
230 if (errno == ENFILE || errno == EMFILE) {
231 struct timeval evtpause = { 1, 0 };
233 event_del(&env->sc_evsock);
234 evtimer_add(&env->sc_evpause, &evtpause);
235 log_debug("%s: deferring connections", __func__);
237 return;
240 if ((fcgi = calloc(1, sizeof(*fcgi))) == NULL)
241 goto err;
243 fcgi->fcg_id = ++proxy_fcg_id;
244 fcgi->fcg_s = s;
245 fcgi->fcg_env = env;
246 fcgi->fcg_want = FCGI_RECORD_HEADER;
247 fcgi->fcg_toread = sizeof(struct fcgi_header);
248 SPLAY_INIT(&fcgi->fcg_clients);
250 /* assume it's enabled until we get a FCGI_BEGIN_REQUEST */
251 fcgi->fcg_keep_conn = 1;
253 fcgi->fcg_bev = bufferevent_new(fcgi->fcg_s, fcgi_read, fcgi_write,
254 fcgi_error, fcgi);
255 if (fcgi->fcg_bev == NULL)
256 goto err;
258 SPLAY_INSERT(fcgi_tree, &env->sc_fcgi_socks, fcgi);
259 bufferevent_enable(fcgi->fcg_bev, EV_READ | EV_WRITE);
260 return;
262 err:
263 if (s != -1) {
264 close(s);
265 free(fcgi);
266 fcgi_inflight_dec(__func__);
270 static int
271 parse_len(struct fcgi *fcgi, struct evbuffer *src)
273 unsigned char c, x[3];
275 fcgi->fcg_toread--;
276 evbuffer_remove(src, &c, 1);
277 if (c >> 7 == 0)
278 return (c);
280 if (fcgi->fcg_toread < 3)
281 return (-1);
283 fcgi->fcg_toread -= 3;
284 evbuffer_remove(src, x, sizeof(x));
285 return (((c & 0x7F) << 24) | (x[0] << 16) | (x[1] << 8) | x[2]);
288 static int
289 fcgi_parse_params(struct fcgi *fcgi, struct evbuffer *src, struct client *clt)
291 char pname[32];
292 char server[HOST_NAME_MAX + 1];
293 char path[PATH_MAX];
294 char query[GEMINI_MAXLEN];
295 char method[8];
296 int nlen, vlen;
298 while (fcgi->fcg_toread > 0) {
299 if ((nlen = parse_len(fcgi, src)) < 0 ||
300 (vlen = parse_len(fcgi, src)) < 0)
301 return (-1);
303 if (fcgi->fcg_toread < nlen + vlen)
304 return (-1);
306 if ((size_t)nlen > sizeof(pname) - 1) {
307 /* ignore this parameter */
308 fcgi->fcg_toread -= nlen - vlen;
309 evbuffer_drain(src, nlen + vlen);
310 continue;
313 fcgi->fcg_toread -= nlen;
314 evbuffer_remove(src, &pname, nlen);
315 pname[nlen] = '\0';
317 if (!strcmp(pname, "SERVER_NAME") &&
318 (size_t)vlen < sizeof(server)) {
319 fcgi->fcg_toread -= vlen;
320 evbuffer_remove(src, &server, vlen);
321 server[vlen] = '\0';
323 free(clt->clt_server_name);
324 if ((clt->clt_server_name = strdup(server)) == NULL)
325 return (-1);
326 DPRINTF("clt %d: server_name: %s", clt->clt_id,
327 clt->clt_server_name);
328 continue;
331 if (!strcmp(pname, "SCRIPT_NAME") &&
332 (size_t)vlen < sizeof(path)) {
333 fcgi->fcg_toread -= vlen;
334 evbuffer_remove(src, &path, vlen);
335 path[vlen] = '\0';
337 free(clt->clt_script_name);
338 clt->clt_script_name = NULL;
340 if (vlen == 0 || path[vlen - 1] != '/')
341 asprintf(&clt->clt_script_name, "%s/", path);
342 else
343 clt->clt_script_name = strdup(path);
345 if (clt->clt_script_name == NULL)
346 return (-1);
348 DPRINTF("clt %d: script_name: %s", clt->clt_id,
349 clt->clt_script_name);
350 continue;
353 if (!strcmp(pname, "PATH_INFO") &&
354 (size_t)vlen < sizeof(path)) {
355 fcgi->fcg_toread -= vlen;
356 evbuffer_remove(src, &path, vlen);
357 path[vlen] = '\0';
359 free(clt->clt_path_info);
360 clt->clt_path_info = NULL;
362 if (*path != '/')
363 asprintf(&clt->clt_path_info, "/%s", path);
364 else
365 clt->clt_path_info = strdup(path);
367 if (clt->clt_path_info == NULL)
368 return (-1);
370 DPRINTF("clt %d: path_info: %s", clt->clt_id,
371 clt->clt_path_info);
372 continue;
375 if (!strcmp(pname, "QUERY_STRING") &&
376 (size_t)vlen < sizeof(query) &&
377 vlen > 0) {
378 fcgi->fcg_toread -= vlen;
379 evbuffer_remove(src, &query, vlen);
380 query[vlen] = '\0';
382 free(clt->clt_query);
383 if ((clt->clt_query = strdup(query)) == NULL)
384 return (-1);
386 DPRINTF("clt %d: query: %s", clt->clt_id,
387 clt->clt_query);
388 continue;
391 if (!strcmp(pname, "REQUEST_METHOD") &&
392 (size_t)vlen < sizeof(method)) {
393 fcgi->fcg_toread -= vlen;
394 evbuffer_remove(src, &method, vlen);
395 method[vlen] = '\0';
397 if (!strcasecmp(method, "GET"))
398 clt->clt_method = METHOD_GET;
399 if (!strcasecmp(method, "POST"))
400 clt->clt_method = METHOD_POST;
402 continue;
405 fcgi->fcg_toread -= vlen;
406 evbuffer_drain(src, vlen);
409 return (0);
412 static int
413 fcgi_parse_form(struct fcgi *fcgi, struct client *clt, struct evbuffer *src)
415 char *a, *s;
416 char tmp[2048];
417 size_t len;
419 if (fcgi->fcg_toread == 0) {
420 clt->clt_bodydone = 1;
421 return (proxy_start_request(fcgi->fcg_env, clt));
424 len = sizeof(tmp) - 1;
425 if (len > (size_t)fcgi->fcg_toread)
426 len = fcgi->fcg_toread;
428 fcgi->fcg_toread -= len;
429 evbuffer_remove(src, &tmp, len);
430 tmp[len] = '\0';
432 if (clt->clt_bodydone)
433 return (0);
435 if (clt->clt_bodylen > GEMINI_MAXLEN) {
436 free(clt->clt_body);
437 clt->clt_body = NULL;
438 return (0);
441 if (clt->clt_body != NULL) {
442 if ((a = strchr(tmp, '&')) != NULL ||
443 (a = strchr(tmp, '\r')) != NULL) {
444 *a = '\0';
445 clt->clt_bodydone = 1;
448 a = clt->clt_body;
449 if (asprintf(&clt->clt_body, "%s%s", a, tmp) == -1) {
450 free(a);
451 clt->clt_body = NULL;
452 return (0);
454 free(a);
455 clt->clt_bodylen += strlen(tmp);
456 return (0);
459 if (clt->clt_bodylen != 0)
460 return (0); /* EOM situation */
462 if ((s = strstr(tmp, "q=")) == NULL)
463 return (0);
464 s += 2;
466 if ((a = strchr(s, '&')) != NULL ||
467 (a = strchr(s, '\r')) != NULL) {
468 *a = '\0';
469 clt->clt_bodydone = 1;
472 clt->clt_bodylen = strlen(s);
473 if ((clt->clt_body = strdup(s)) == NULL)
474 return (0);
476 return (0);
479 void
480 fcgi_read(struct bufferevent *bev, void *d)
482 struct fcgi *fcgi = d;
483 struct galileo *env = fcgi->fcg_env;
484 struct evbuffer *src = EVBUFFER_INPUT(bev);
485 struct fcgi_header hdr;
486 struct fcgi_begin_req breq;
487 struct client *clt, q;
488 int role;
490 memset(&q, 0, sizeof(q));
492 for (;;) {
493 if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread)
494 return;
496 if (fcgi->fcg_want == FCGI_RECORD_HEADER) {
497 fcgi->fcg_want = FCGI_RECORD_BODY;
498 bufferevent_read(bev, &hdr, sizeof(hdr));
500 DPRINTF("header: v=%d t=%d id=%d len=%d p=%d",
501 hdr.version, hdr.type,
502 CAT(hdr.req_id0, hdr.req_id1),
503 CAT(hdr.content_len0, hdr.content_len1),
504 hdr.padding);
506 if (hdr.version != FCGI_VERSION_1) {
507 log_warnx("unknown fastcgi version: %d",
508 hdr.version);
509 fcgi_error(bev, EV_READ, d);
510 return;
513 fcgi->fcg_toread = CAT(hdr.content_len0,
514 hdr.content_len1);
515 if (fcgi->fcg_toread < 0) {
516 log_warnx("invalid record length: %d",
517 fcgi->fcg_toread);
518 fcgi_error(bev, EV_READ, d);
519 return;
522 fcgi->fcg_padding = hdr.padding;
523 if (fcgi->fcg_padding < 0) {
524 log_warnx("invalid padding: %d",
525 fcgi->fcg_padding);
526 fcgi_error(bev, EV_READ, d);
527 return;
530 fcgi->fcg_type = hdr.type;
531 fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1);
532 continue;
535 q.clt_id = fcgi->fcg_rec_id;
536 clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q);
538 switch (fcgi->fcg_type) {
539 case FCGI_BEGIN_REQUEST:
540 if (sizeof(breq) != fcgi->fcg_toread) {
541 log_warnx("unexpected size for "
542 "FCGI_BEGIN_REQUEST");
543 fcgi_error(bev, EV_READ, d);
544 return;
547 evbuffer_remove(src, &breq, sizeof(breq));
549 role = CAT(breq.role0, breq.role1);
550 if (role != FCGI_RESPONDER) {
551 log_warnx("unknown fastcgi role: %d",
552 role);
553 if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id,
554 1, FCGI_UNKNOWN_ROLE) == -1) {
555 fcgi_error(bev, EV_READ, d);
556 return;
558 break;
561 if (!fcgi->fcg_keep_conn) {
562 log_warnx("trying to reuse the fastcgi "
563 "socket without marking it as so.");
564 fcgi_error(bev, EV_READ, d);
565 return;
567 fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN;
569 if (clt != NULL) {
570 log_warnx("ignoring attemp to re-use an "
571 "active request id (%d)",
572 fcgi->fcg_rec_id);
573 break;
576 if ((clt = calloc(1, sizeof(*clt))) == NULL) {
577 log_warnx("calloc");
578 break;
581 clt->clt_tp = template(clt, clt_write, clt->clt_buf,
582 sizeof(clt->clt_buf));
583 if (clt->clt_tp == NULL) {
584 free(clt);
585 log_warn("template");
586 break;
589 clt->clt_id = fcgi->fcg_rec_id;
590 clt->clt_fd = -1;
591 clt->clt_fcgi = fcgi;
592 SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt);
593 break;
594 case FCGI_PARAMS:
595 if (clt == NULL) {
596 log_warnx("got FCGI_PARAMS for inactive id "
597 "(%d)", fcgi->fcg_rec_id);
598 evbuffer_drain(src, fcgi->fcg_toread);
599 break;
601 if (fcgi->fcg_toread == 0) {
602 evbuffer_drain(src, fcgi->fcg_toread);
603 if (clt->clt_method == METHOD_GET) {
604 if (proxy_start_request(env, clt)
605 == -1)
606 return;
608 break;
610 if (fcgi_parse_params(fcgi, src, clt) == -1) {
611 log_warnx("fcgi_parse_params failed");
612 fcgi_error(bev, EV_READ, d);
613 return;
615 break;
616 case FCGI_STDIN:
617 if (clt == NULL || clt->clt_method != METHOD_POST) {
618 evbuffer_drain(src, fcgi->fcg_toread);
619 break;
621 if (fcgi_parse_form(fcgi, clt, src) == -1)
622 return;
623 break;
624 case FCGI_ABORT_REQUEST:
625 if (clt == NULL) {
626 log_warnx("got FCGI_ABORT_REQUEST for inactive"
627 " id (%d)", fcgi->fcg_rec_id);
628 evbuffer_drain(src, fcgi->fcg_toread);
629 break;
631 if (fcgi_end_request(clt, 1) == -1) {
632 /* calls fcgi_error on failure */
633 return;
635 break;
636 default:
637 log_warnx("unknown fastcgi record type %d",
638 fcgi->fcg_type);
639 evbuffer_drain(src, fcgi->fcg_toread);
640 break;
643 /* Prepare for the next record. */
644 evbuffer_drain(src, fcgi->fcg_padding);
645 fcgi->fcg_want = FCGI_RECORD_HEADER;
646 fcgi->fcg_toread = sizeof(struct fcgi_header);
650 void
651 fcgi_write(struct bufferevent *bev, void *d)
653 struct fcgi *fcgi = d;
654 struct evbuffer *out = EVBUFFER_OUTPUT(bev);
656 if (fcgi->fcg_done && EVBUFFER_LENGTH(out) == 0)
657 fcgi_error(bev, EVBUFFER_EOF, fcgi);
660 void
661 fcgi_error(struct bufferevent *bev, short event, void *d)
663 struct fcgi *fcgi = d;
664 struct galileo *env = fcgi->fcg_env;
665 struct client *clt;
667 log_debug("fcgi failure, shutting down connection (ev: %x)",
668 event);
669 fcgi_inflight_dec(__func__);
671 while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) {
672 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
673 proxy_client_free(clt);
676 SPLAY_REMOVE(fcgi_tree, &env->sc_fcgi_socks, fcgi);
677 fcgi_free(fcgi);
679 return;
682 void
683 fcgi_free(struct fcgi *fcgi)
685 close(fcgi->fcg_s);
686 bufferevent_free(fcgi->fcg_bev);
687 free(fcgi);
690 int
691 clt_flush(struct client *clt)
693 return (template_flush(clt->clt_tp));
696 static inline int
697 dowrite(struct client *clt, const void *data, size_t len)
699 struct fcgi *fcgi = clt->clt_fcgi;
700 struct bufferevent *bev = fcgi->fcg_bev;
701 struct fcgi_header hdr;
703 memset(&hdr, 0, sizeof(hdr));
704 hdr.version = FCGI_VERSION_1;
705 hdr.type = FCGI_STDOUT;
706 hdr.req_id0 = (clt->clt_id & 0xFF);
707 hdr.req_id1 = (clt->clt_id >> 8);
708 hdr.content_len0 = (len & 0xFF);
709 hdr.content_len1 = (len >> 8);
711 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 ||
712 bufferevent_write(bev, clt->clt_buf, len) == -1) {
713 fcgi_error(bev, EV_WRITE, fcgi);
714 return (-1);
717 return (0);
720 int
721 clt_write(void *arg, const void *d, size_t len)
723 struct client *clt = arg;
724 const char *data = d;
725 size_t avail;
727 while (len > 0) {
728 avail = MIN(len, FCGI_MAX_CONTENT_SIZE);
729 if (dowrite(clt, data, avail) == -1)
730 return (-1);
731 data += avail;
732 len -= avail;
735 return (0);
738 int
739 clt_write_bufferevent(struct client *clt, struct bufferevent *bev)
741 struct evbuffer *src = EVBUFFER_INPUT(bev);
742 size_t len;
743 int ret;
745 len = EVBUFFER_LENGTH(src);
746 ret = clt_write(clt, EVBUFFER_DATA(src), len);
747 evbuffer_drain(src, len);
748 return (ret);
751 int
752 fcgi_cmp(struct fcgi *a, struct fcgi *b)
754 return ((int)a->fcg_id - b->fcg_id);
757 int
758 fcgi_client_cmp(struct client *a, struct client *b)
760 return ((int)a->clt_id - b->clt_id);
763 SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp);
764 SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp);