2 * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
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.
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.
17 #include <sys/queue.h>
19 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
39 #define MIN(a, b) ((a) < (b) ? (a) : (b))
42 unsigned char version;
44 unsigned char req_id1;
45 unsigned char req_id0;
46 unsigned char content_len1;
47 unsigned char content_len0;
48 unsigned char padding;
49 unsigned char reserved;
50 } __attribute__((packed));
53 * number of bytes in a FCGI_HEADER. Future version of the protocol
54 * will not reduce this number.
56 #define FCGI_HEADER_LEN 8
59 * values for the version component
61 #define FCGI_VERSION_1 1
64 * values for the type component
66 #define FCGI_BEGIN_REQUEST 1
67 #define FCGI_ABORT_REQUEST 2
68 #define FCGI_END_REQUEST 3
74 #define FCGI_GET_VALUES 9
75 #define FCGI_GET_VALUES_RESULT 10
76 #define FCGI_UNKNOWN_TYPE 11
77 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
79 struct fcgi_begin_req {
83 unsigned char reserved[5];
86 struct fcgi_begin_req_record {
87 struct fcgi_header header;
88 struct fcgi_begin_req body;
94 #define FCGI_KEEP_CONN 1
99 #define FCGI_RESPONDER 1
100 #define FCGI_AUTHORIZER 2
101 #define FCGI_FILTER 3
103 struct fcgi_end_req_body {
104 unsigned char app_status3;
105 unsigned char app_status2;
106 unsigned char app_status1;
107 unsigned char app_status0;
108 unsigned char proto_status;
109 unsigned char reserved[3];
113 * values for proto_status
115 #define FCGI_REQUEST_COMPLETE 0
116 #define FCGI_CANT_MPX_CONN 1
117 #define FCGI_OVERLOADED 2
118 #define FCGI_UNKNOWN_ROLE 3
121 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
124 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
125 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
126 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
128 #define CAT(f0, f1) ((f0) + ((f1) << 8))
135 volatile int fcgi_inflight;
138 fcgi_send_end_req(struct fcgi *fcgi, int id, int as, int ps)
140 struct bufferevent *bev = fcgi->fcg_bev;
141 struct fcgi_header hdr;
142 struct fcgi_end_req_body end;
144 memset(&hdr, 0, sizeof(hdr));
145 memset(&end, 0, sizeof(end));
147 hdr.version = FCGI_VERSION_1;
148 hdr.type = FCGI_END_REQUEST;
149 hdr.req_id0 = (id & 0xFF);
150 hdr.req_id1 = (id >> 8);
151 hdr.content_len0 = sizeof(end);
153 end.app_status0 = (unsigned char)as;
154 end.proto_status = (unsigned char)ps;
156 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1)
158 if (bufferevent_write(bev, &end, sizeof(end)) == -1)
164 end_request(struct client *clt, int status, int proto_status)
166 struct fcgi *fcgi = clt->clt_fcgi;
169 if (clt_flush(clt) == -1)
172 r = fcgi_send_end_req(fcgi, clt->clt_id, status,
175 fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi);
179 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
180 proxy_client_free(clt);
182 if (!fcgi->fcg_keep_conn)
189 fcgi_end_request(struct client *clt, int status)
191 return (end_request(clt, status, FCGI_REQUEST_COMPLETE));
195 fcgi_abort_request(struct client *clt)
197 return (end_request(clt, 1, FCGI_OVERLOADED));
201 fcgi_inflight_dec(const char *why)
204 log_debug("%s: fcgi inflight decremented, now %d, %s",
205 __func__, fcgi_inflight, why);
209 fcgi_accept(int fd, short event, void *arg)
211 struct galileo *env = arg;
212 struct fcgi *fcgi = NULL;
214 struct sockaddr_storage ss;
217 event_add(&env->sc_evpause, NULL);
218 if ((event & EV_TIMEOUT))
222 if ((s = accept_reserve(env->sc_sock_fd, (struct sockaddr *)&ss,
223 &slen, FD_RESERVE, &fcgi_inflight)) == -1) {
225 * Pause accept if we are out of file descriptors, or
226 * libevent will haunt us here too.
228 if (errno == ENFILE || errno == EMFILE) {
229 struct timeval evtpause = { 1, 0 };
231 event_del(&env->sc_evsock);
232 evtimer_add(&env->sc_evpause, &evtpause);
233 log_debug("%s: deferring connections", __func__);
238 if ((fcgi = calloc(1, sizeof(*fcgi))) == NULL)
241 fcgi->fcg_id = ++proxy_fcg_id;
244 fcgi->fcg_want = FCGI_RECORD_HEADER;
245 fcgi->fcg_toread = sizeof(struct fcgi_header);
246 SPLAY_INIT(&fcgi->fcg_clients);
248 /* assume it's enabled until we get a FCGI_BEGIN_REQUEST */
249 fcgi->fcg_keep_conn = 1;
251 fcgi->fcg_bev = bufferevent_new(fcgi->fcg_s, fcgi_read, fcgi_write,
253 if (fcgi->fcg_bev == NULL)
256 SPLAY_INSERT(fcgi_tree, &env->sc_fcgi_socks, fcgi);
257 bufferevent_enable(fcgi->fcg_bev, EV_READ | EV_WRITE);
264 fcgi_inflight_dec(__func__);
269 parse_len(struct fcgi *fcgi, struct evbuffer *src)
271 unsigned char c, x[3];
274 evbuffer_remove(src, &c, 1);
278 if (fcgi->fcg_toread < 3)
281 fcgi->fcg_toread -= 3;
282 evbuffer_remove(src, x, sizeof(x));
283 return (((c & 0x7F) << 24) | (x[0] << 16) | (x[1] << 8) | x[2]);
287 fcgi_parse_params(struct fcgi *fcgi, struct evbuffer *src, struct client *clt)
290 char server[HOST_NAME_MAX + 1];
292 char query[GEMINI_MAXLEN];
296 while (fcgi->fcg_toread > 0) {
297 if ((nlen = parse_len(fcgi, src)) < 0 ||
298 (vlen = parse_len(fcgi, src)) < 0)
301 if (fcgi->fcg_toread < nlen + vlen)
304 if ((size_t)nlen > sizeof(pname) - 1) {
305 /* ignore this parameter */
306 fcgi->fcg_toread -= nlen - vlen;
307 evbuffer_drain(src, nlen + vlen);
311 fcgi->fcg_toread -= nlen;
312 evbuffer_remove(src, &pname, nlen);
315 if (!strcmp(pname, "SERVER_NAME") &&
316 (size_t)vlen < sizeof(server)) {
317 fcgi->fcg_toread -= vlen;
318 evbuffer_remove(src, &server, vlen);
321 free(clt->clt_server_name);
322 if ((clt->clt_server_name = strdup(server)) == NULL)
324 DPRINTF("clt %d: server_name: %s", clt->clt_id,
325 clt->clt_server_name);
329 if (!strcmp(pname, "SCRIPT_NAME") &&
330 (size_t)vlen < sizeof(path)) {
331 fcgi->fcg_toread -= vlen;
332 evbuffer_remove(src, &path, vlen);
335 free(clt->clt_script_name);
336 clt->clt_script_name = NULL;
338 if (vlen == 0 || path[vlen - 1] != '/')
339 asprintf(&clt->clt_script_name, "%s/", path);
341 clt->clt_script_name = strdup(path);
343 if (clt->clt_script_name == NULL)
346 DPRINTF("clt %d: script_name: %s", clt->clt_id,
347 clt->clt_script_name);
351 if (!strcmp(pname, "PATH_INFO") &&
352 (size_t)vlen < sizeof(path)) {
353 fcgi->fcg_toread -= vlen;
354 evbuffer_remove(src, &path, vlen);
357 free(clt->clt_path_info);
358 clt->clt_path_info = NULL;
361 asprintf(&clt->clt_path_info, "/%s", path);
363 clt->clt_path_info = strdup(path);
365 if (clt->clt_path_info == NULL)
368 DPRINTF("clt %d: path_info: %s", clt->clt_id,
373 if (!strcmp(pname, "QUERY_STRING") &&
374 (size_t)vlen < sizeof(query) &&
376 fcgi->fcg_toread -= vlen;
377 evbuffer_remove(src, &query, vlen);
380 free(clt->clt_query);
381 if ((clt->clt_query = strdup(query)) == NULL)
384 DPRINTF("clt %d: query: %s", clt->clt_id,
389 if (!strcmp(pname, "REQUEST_METHOD") &&
390 (size_t)vlen < sizeof(method)) {
391 fcgi->fcg_toread -= vlen;
392 evbuffer_remove(src, &method, vlen);
395 if (!strcasecmp(method, "GET"))
396 clt->clt_method = METHOD_GET;
397 if (!strcasecmp(method, "POST"))
398 clt->clt_method = METHOD_POST;
403 fcgi->fcg_toread -= vlen;
404 evbuffer_drain(src, vlen);
411 fcgi_parse_form(struct fcgi *fcgi, struct client *clt, struct evbuffer *src)
417 if (fcgi->fcg_toread == 0) {
418 clt->clt_bodydone = 1;
419 return (proxy_start_request(fcgi->fcg_env, clt));
422 len = sizeof(tmp) - 1;
423 if (len > (size_t)fcgi->fcg_toread)
424 len = fcgi->fcg_toread;
426 fcgi->fcg_toread -= len;
427 evbuffer_remove(src, &tmp, len);
430 if (clt->clt_bodydone)
433 if (clt->clt_bodylen > GEMINI_MAXLEN) {
435 clt->clt_body = NULL;
439 if (clt->clt_body != NULL) {
440 if ((a = strchr(tmp, '&')) != NULL ||
441 (a = strchr(tmp, '\r')) != NULL) {
443 clt->clt_bodydone = 1;
447 if (asprintf(&clt->clt_body, "%s%s", a, tmp) == -1) {
449 clt->clt_body = NULL;
453 clt->clt_bodylen += strlen(tmp);
457 if (clt->clt_bodylen != 0)
458 return (0); /* EOM situation */
460 if ((s = strstr(tmp, "q=")) == NULL)
464 if ((a = strchr(s, '&')) != NULL ||
465 (a = strchr(s, '\r')) != NULL) {
467 clt->clt_bodydone = 1;
470 clt->clt_bodylen = strlen(s);
471 if ((clt->clt_body = strdup(s)) == NULL)
478 fcgi_read(struct bufferevent *bev, void *d)
480 struct fcgi *fcgi = d;
481 struct galileo *env = fcgi->fcg_env;
482 struct evbuffer *src = EVBUFFER_INPUT(bev);
483 struct fcgi_header hdr;
484 struct fcgi_begin_req breq;
485 struct client *clt, q;
488 memset(&q, 0, sizeof(q));
491 if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread)
494 if (fcgi->fcg_want == FCGI_RECORD_HEADER) {
495 fcgi->fcg_want = FCGI_RECORD_BODY;
496 bufferevent_read(bev, &hdr, sizeof(hdr));
498 DPRINTF("header: v=%d t=%d id=%d len=%d p=%d",
499 hdr.version, hdr.type,
500 CAT(hdr.req_id0, hdr.req_id1),
501 CAT(hdr.content_len0, hdr.content_len1),
504 if (hdr.version != FCGI_VERSION_1) {
505 log_warnx("unknown fastcgi version: %d",
507 fcgi_error(bev, EV_READ, d);
511 fcgi->fcg_toread = CAT(hdr.content_len0,
513 if (fcgi->fcg_toread < 0) {
514 log_warnx("invalid record length: %d",
516 fcgi_error(bev, EV_READ, d);
520 fcgi->fcg_padding = hdr.padding;
521 if (fcgi->fcg_padding < 0) {
522 log_warnx("invalid padding: %d",
524 fcgi_error(bev, EV_READ, d);
528 fcgi->fcg_type = hdr.type;
529 fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1);
533 q.clt_id = fcgi->fcg_rec_id;
534 clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q);
536 switch (fcgi->fcg_type) {
537 case FCGI_BEGIN_REQUEST:
538 if (sizeof(breq) != fcgi->fcg_toread) {
539 log_warnx("unexpected size for "
540 "FCGI_BEGIN_REQUEST");
541 fcgi_error(bev, EV_READ, d);
545 evbuffer_remove(src, &breq, sizeof(breq));
547 role = CAT(breq.role0, breq.role1);
548 if (role != FCGI_RESPONDER) {
549 log_warnx("unknown fastcgi role: %d",
551 if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id,
552 1, FCGI_UNKNOWN_ROLE) == -1) {
553 fcgi_error(bev, EV_READ, d);
559 if (!fcgi->fcg_keep_conn) {
560 log_warnx("trying to reuse the fastcgi "
561 "socket without marking it as so.");
562 fcgi_error(bev, EV_READ, d);
565 fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN;
568 log_warnx("ignoring attemp to re-use an "
569 "active request id (%d)",
574 if ((clt = calloc(1, sizeof(*clt))) == NULL) {
579 clt->clt_tp = template(clt, clt_tp_puts, clt_tp_putc);
580 if (clt->clt_tp == NULL) {
582 log_warn("template");
586 clt->clt_id = fcgi->fcg_rec_id;
588 clt->clt_fcgi = fcgi;
589 SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt);
593 log_warnx("got FCGI_PARAMS for inactive id "
594 "(%d)", fcgi->fcg_rec_id);
595 evbuffer_drain(src, fcgi->fcg_toread);
598 if (fcgi->fcg_toread == 0) {
599 evbuffer_drain(src, fcgi->fcg_toread);
600 if (clt->clt_method == METHOD_GET) {
601 if (proxy_start_request(env, clt)
607 if (fcgi_parse_params(fcgi, src, clt) == -1) {
608 log_warnx("fcgi_parse_params failed");
609 fcgi_error(bev, EV_READ, d);
614 if (clt == NULL || clt->clt_method != METHOD_POST) {
615 evbuffer_drain(src, fcgi->fcg_toread);
618 if (fcgi_parse_form(fcgi, clt, src) == -1)
621 case FCGI_ABORT_REQUEST:
623 log_warnx("got FCGI_ABORT_REQUEST for inactive"
624 " id (%d)", fcgi->fcg_rec_id);
625 evbuffer_drain(src, fcgi->fcg_toread);
628 if (fcgi_end_request(clt, 1) == -1) {
629 /* calls fcgi_error on failure */
634 log_warnx("unknown fastcgi record type %d",
636 evbuffer_drain(src, fcgi->fcg_toread);
640 /* Prepare for the next record. */
641 evbuffer_drain(src, fcgi->fcg_padding);
642 fcgi->fcg_want = FCGI_RECORD_HEADER;
643 fcgi->fcg_toread = sizeof(struct fcgi_header);
648 fcgi_write(struct bufferevent *bev, void *d)
650 struct fcgi *fcgi = d;
651 struct evbuffer *out = EVBUFFER_OUTPUT(bev);
653 if (fcgi->fcg_done && EVBUFFER_LENGTH(out) == 0)
654 fcgi_error(bev, EVBUFFER_EOF, fcgi);
658 fcgi_error(struct bufferevent *bev, short event, void *d)
660 struct fcgi *fcgi = d;
661 struct galileo *env = fcgi->fcg_env;
664 log_debug("fcgi failure, shutting down connection (ev: %x)",
666 fcgi_inflight_dec(__func__);
668 while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) {
669 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
670 proxy_client_free(clt);
673 SPLAY_REMOVE(fcgi_tree, &env->sc_fcgi_socks, fcgi);
680 fcgi_free(struct fcgi *fcgi)
683 bufferevent_free(fcgi->fcg_bev);
688 clt_flush(struct client *clt)
690 struct fcgi *fcgi = clt->clt_fcgi;
691 struct bufferevent *bev = fcgi->fcg_bev;
692 struct fcgi_header hdr;
694 if (clt->clt_buflen == 0)
697 memset(&hdr, 0, sizeof(hdr));
698 hdr.version = FCGI_VERSION_1;
699 hdr.type = FCGI_STDOUT;
700 hdr.req_id0 = (clt->clt_id & 0xFF);
701 hdr.req_id1 = (clt->clt_id >> 8);
702 hdr.content_len0 = (clt->clt_buflen & 0xFF);
703 hdr.content_len1 = (clt->clt_buflen >> 8);
705 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 ||
706 bufferevent_write(bev, clt->clt_buf, clt->clt_buflen) == -1) {
707 fcgi_error(bev, EV_WRITE, fcgi);
717 clt_write(struct client *clt, const uint8_t *buf, size_t len)
722 left = sizeof(clt->clt_buf) - clt->clt_buflen;
724 if (clt_flush(clt) == -1)
726 left = sizeof(clt->clt_buf);
729 copy = MIN(left, len);
731 memcpy(&clt->clt_buf[clt->clt_buflen], buf, copy);
732 clt->clt_buflen += copy;
741 clt_putc(struct client *clt, char ch)
743 return (clt_write(clt, &ch, 1));
747 clt_puts(struct client *clt, const char *str)
749 return (clt_write(clt, str, strlen(str)));
753 clt_write_bufferevent(struct client *clt, struct bufferevent *bev)
755 struct evbuffer *src = EVBUFFER_INPUT(bev);
756 size_t len, left, copy;
758 len = EVBUFFER_LENGTH(src);
760 left = sizeof(clt->clt_buf) - clt->clt_buflen;
762 if (clt_flush(clt) == -1)
764 left = sizeof(clt->clt_buf);
767 copy = bufferevent_read(bev, &clt->clt_buf[clt->clt_buflen],
769 clt->clt_buflen += copy;
771 len = EVBUFFER_LENGTH(src);
778 clt_printf(struct client *clt, const char *fmt, ...)
780 struct fcgi *fcgi = clt->clt_fcgi;
781 struct bufferevent *bev = fcgi->fcg_bev;
787 r = vasprintf(&str, fmt, ap);
790 fcgi_error(bev, EV_WRITE, fcgi);
794 r = clt_write(clt, str, r);
800 clt_tp_puts(struct template *tp, const char *str)
802 struct client *clt = tp->tp_arg;
804 if (clt_puts(clt, str) == -1)
811 clt_tp_putc(struct template *tp, int c)
813 struct client *clt = tp->tp_arg;
815 if (clt_putc(clt, c) == -1)
822 fcgi_cmp(struct fcgi *a, struct fcgi *b)
824 return ((int)a->fcg_id - b->fcg_id);
828 fcgi_client_cmp(struct client *a, struct client *b)
830 return ((int)a->clt_id - b->clt_id);
833 SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp);
834 SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp);