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 struct fcgi_header {
42 unsigned char version;
43 unsigned char type;
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));
52 /*
53 * number of bytes in a FCGI_HEADER. Future version of the protocol
54 * will not reduce this number.
55 */
56 #define FCGI_HEADER_LEN 8
58 /*
59 * values for the version component
60 */
61 #define FCGI_VERSION_1 1
63 /*
64 * values for the type component
65 */
66 #define FCGI_BEGIN_REQUEST 1
67 #define FCGI_ABORT_REQUEST 2
68 #define FCGI_END_REQUEST 3
69 #define FCGI_PARAMS 4
70 #define FCGI_STDIN 5
71 #define FCGI_STDOUT 6
72 #define FCGI_STDERR 7
73 #define FCGI_DATA 8
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 {
80 unsigned char role1;
81 unsigned char role0;
82 unsigned char flags;
83 unsigned char reserved[5];
84 };
86 struct fcgi_begin_req_record {
87 struct fcgi_header header;
88 struct fcgi_begin_req body;
89 };
91 /*
92 * mask for flags;
93 */
94 #define FCGI_KEEP_CONN 1
96 /*
97 * values for the role
98 */
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];
110 };
112 /*
113 * values for proto_status
114 */
115 #define FCGI_REQUEST_COMPLETE 0
116 #define FCGI_CANT_MPX_CONN 1
117 #define FCGI_OVERLOADED 2
118 #define FCGI_UNKNOWN_ROLE 3
120 /*
121 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
122 * records.
123 */
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))
130 enum {
131 FCGI_RECORD_HEADER,
132 FCGI_RECORD_BODY,
133 };
135 volatile int fcgi_inflight;
137 static int
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)
157 return (-1);
158 if (bufferevent_write(bev, &end, sizeof(end)) == -1)
159 return (-1);
160 return (0);
163 static int
164 end_request(struct client *clt, int status, int proto_status)
166 struct fcgi *fcgi = clt->clt_fcgi;
167 int r;
169 if (clt_flush(clt) == -1)
170 return (-1);
172 r = fcgi_send_end_req(fcgi, clt->clt_id, status,
173 proto_status);
174 if (r == -1) {
175 fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi);
176 return (-1);
179 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
180 proxy_client_free(clt);
182 if (!fcgi->fcg_keep_conn)
183 fcgi->fcg_done = 1;
185 return (0);
188 int
189 fcgi_end_request(struct client *clt, int status)
191 return (end_request(clt, status, FCGI_REQUEST_COMPLETE));
194 int
195 fcgi_abort_request(struct client *clt)
197 return (end_request(clt, 1, FCGI_OVERLOADED));
200 static void
201 fcgi_inflight_dec(const char *why)
203 fcgi_inflight--;
204 log_debug("%s: fcgi inflight decremented, now %d, %s",
205 __func__, fcgi_inflight, why);
208 void
209 fcgi_accept(int fd, short event, void *arg)
211 struct galileo *env = arg;
212 struct fcgi *fcgi = NULL;
213 socklen_t slen;
214 struct sockaddr_storage ss;
215 int s = -1;
217 event_add(&env->sc_evpause, NULL);
218 if ((event & EV_TIMEOUT))
219 return;
221 slen = sizeof(ss);
222 if ((s = accept_reserve(env->sc_sock_fd, (struct sockaddr *)&ss,
223 &slen, FD_RESERVE, &fcgi_inflight)) == -1) {
224 /*
225 * Pause accept if we are out of file descriptors, or
226 * libevent will haunt us here too.
227 */
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__);
235 return;
238 if ((fcgi = calloc(1, sizeof(*fcgi))) == NULL)
239 goto err;
241 fcgi->fcg_id = ++proxy_fcg_id;
242 fcgi->fcg_s = s;
243 fcgi->fcg_env = env;
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,
252 fcgi_error, fcgi);
253 if (fcgi->fcg_bev == NULL)
254 goto err;
256 bufferevent_enable(fcgi->fcg_bev, EV_READ | EV_WRITE);
257 return;
259 err:
260 if (s != -1) {
261 close(s);
262 free(fcgi);
263 fcgi_inflight_dec(__func__);
267 static int
268 parse_len(struct fcgi *fcgi, struct evbuffer *src)
270 unsigned char c, x[3];
272 fcgi->fcg_toread--;
273 evbuffer_remove(src, &c, 1);
274 if (c >> 7 == 0)
275 return (c);
277 if (fcgi->fcg_toread < 3)
278 return (-1);
280 fcgi->fcg_toread -= 3;
281 evbuffer_remove(src, x, sizeof(x));
282 return (((c & 0x7F) << 24) | (x[0] << 16) | (x[1] << 8) | x[2]);
285 static int
286 fcgi_parse_params(struct fcgi *fcgi, struct evbuffer *src, struct client *clt)
288 char pname[32];
289 char server[HOST_NAME_MAX + 1];
290 char path[PATH_MAX];
291 char query[GEMINI_MAXLEN];
292 int nlen, vlen;
294 while (fcgi->fcg_toread > 0) {
295 if ((nlen = parse_len(fcgi, src)) < 0 ||
296 (vlen = parse_len(fcgi, src)) < 0)
297 return (-1);
299 if (fcgi->fcg_toread < nlen + vlen)
300 return (-1);
302 if ((size_t)nlen > sizeof(pname) - 1) {
303 /* ignore this parameter */
304 fcgi->fcg_toread -= nlen - vlen;
305 evbuffer_drain(src, nlen + vlen);
306 continue;
309 fcgi->fcg_toread -= nlen;
310 evbuffer_remove(src, &pname, nlen);
311 pname[nlen] = '\0';
313 if (!strcmp(pname, "SERVER_NAME") &&
314 (size_t)vlen < sizeof(server)) {
315 fcgi->fcg_toread -= vlen;
316 evbuffer_remove(src, &server, vlen);
317 server[vlen] = '\0';
319 free(clt->clt_server_name);
320 if ((clt->clt_server_name = strdup(server)) == NULL)
321 return (-1);
322 log_debug("clt %d: server_name: %s", clt->clt_id,
323 clt->clt_server_name);
324 continue;
327 if (!strcmp(pname, "SCRIPT_NAME") &&
328 (size_t)vlen < sizeof(path)) {
329 fcgi->fcg_toread -= vlen;
330 evbuffer_remove(src, &path, vlen);
331 path[vlen] = '\0';
333 free(clt->clt_script_name);
334 clt->clt_script_name = NULL;
336 if (vlen > 0 && path[vlen - 1] != '/')
337 asprintf(&clt->clt_script_name, "%s/", path);
338 else
339 clt->clt_script_name = strdup(path);
341 if (clt->clt_script_name == NULL)
342 return (-1);
344 log_debug("clt %d: script_name: %s", clt->clt_id,
345 clt->clt_script_name);
346 continue;
349 if (!strcmp(pname, "PATH_INFO") &&
350 (size_t)vlen < sizeof(path)) {
351 fcgi->fcg_toread -= vlen;
352 evbuffer_remove(src, &path, vlen);
353 path[vlen] = '\0';
355 free(clt->clt_path_info);
356 clt->clt_path_info = NULL;
358 if (*path != '/')
359 asprintf(&clt->clt_path_info, "/%s", path);
360 else
361 clt->clt_path_info = strdup(path);
363 if (clt->clt_path_info == NULL)
364 return (-1);
366 log_debug("clt %d: path_info: %s", clt->clt_id,
367 clt->clt_path_info);
368 continue;
371 if (!strcmp(pname, "QUERY_STRING") &&
372 (size_t)vlen < sizeof(query) &&
373 vlen > 0) {
374 fcgi->fcg_toread -= vlen;
375 evbuffer_remove(src, &query, vlen);
377 free(clt->clt_query);
378 if ((clt->clt_query = strdup(query)) == NULL)
379 return (-1);
381 log_debug("clt %d: query: %s", clt->clt_id,
382 clt->clt_query);
383 continue;
386 fcgi->fcg_toread -= vlen;
387 evbuffer_drain(src, vlen);
390 return (0);
393 void
394 fcgi_read(struct bufferevent *bev, void *d)
396 struct fcgi *fcgi = d;
397 struct galileo *env = fcgi->fcg_env;
398 struct evbuffer *src = EVBUFFER_INPUT(bev);
399 struct fcgi_header hdr;
400 struct fcgi_begin_req breq;
401 struct client *clt, q;
402 int role;
404 memset(&q, 0, sizeof(q));
406 for (;;) {
407 if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread)
408 return;
410 if (fcgi->fcg_want == FCGI_RECORD_HEADER) {
411 fcgi->fcg_want = FCGI_RECORD_BODY;
412 bufferevent_read(bev, &hdr, sizeof(hdr));
414 #ifdef DEBUG
415 log_warnx("header: v=%d t=%d id=%d len=%d p=%d",
416 hdr.version, hdr.type,
417 CAT(hdr.req_id0, hdr.req_id1),
418 CAT(hdr.content_len0, hdr.content_len1),
419 hdr.padding);
420 #endif
422 if (hdr.version != FCGI_VERSION_1) {
423 log_warnx("unknown fastcgi version: %d",
424 hdr.version);
425 fcgi_error(bev, EV_READ, d);
426 return;
429 fcgi->fcg_toread = CAT(hdr.content_len0,
430 hdr.content_len1);
431 if (fcgi->fcg_toread < 0) {
432 log_warnx("invalid record length: %d",
433 fcgi->fcg_toread);
434 fcgi_error(bev, EV_READ, d);
435 return;
438 fcgi->fcg_padding = hdr.padding;
439 if (fcgi->fcg_padding < 0) {
440 log_warnx("invalid padding: %d",
441 fcgi->fcg_padding);
442 fcgi_error(bev, EV_READ, d);
443 return;
446 fcgi->fcg_type = hdr.type;
447 fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1);
448 continue;
451 q.clt_id = fcgi->fcg_rec_id;
452 clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q);
454 switch (fcgi->fcg_type) {
455 case FCGI_BEGIN_REQUEST:
456 if (sizeof(breq) != fcgi->fcg_toread) {
457 log_warnx("unexpected size for "
458 "FCGI_BEGIN_REQUEST");
459 fcgi_error(bev, EV_READ, d);
460 return;
463 evbuffer_remove(src, &breq, sizeof(breq));
465 role = CAT(breq.role0, breq.role1);
466 if (role != FCGI_RESPONDER) {
467 log_warnx("unknown fastcgi role: %d",
468 role);
469 if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id,
470 1, FCGI_UNKNOWN_ROLE) == -1) {
471 fcgi_error(bev, EV_READ, d);
472 return;
474 break;
477 if (!fcgi->fcg_keep_conn) {
478 log_warnx("trying to reuse the fastcgi "
479 "socket without marking it as so.");
480 fcgi_error(bev, EV_READ, d);
481 return;
483 fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN;
485 if (clt != NULL) {
486 log_warnx("ignoring attemp to re-use an "
487 "active request id (%d)",
488 fcgi->fcg_rec_id);
489 break;
492 if ((clt = calloc(1, sizeof(*clt))) == NULL) {
493 log_warnx("calloc");
494 break;
497 clt->clt_tp = template(clt, clt_tp_puts, clt_tp_putc);
498 if (clt->clt_tp == NULL) {
499 free(clt);
500 log_warn("template");
501 break;
504 clt->clt_id = fcgi->fcg_rec_id;
505 clt->clt_fd = -1;
506 clt->clt_fcgi = fcgi;
507 SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt);
508 break;
509 case FCGI_PARAMS:
510 if (clt == NULL) {
511 log_warnx("got FCGI_PARAMS for inactive id "
512 "(%d)", fcgi->fcg_rec_id);
513 evbuffer_drain(src, fcgi->fcg_toread);
514 break;
516 if (fcgi->fcg_toread == 0) {
517 evbuffer_drain(src, fcgi->fcg_toread);
518 proxy_start_request(env, clt);
519 break;
521 if (fcgi_parse_params(fcgi, src, clt) == -1) {
522 log_warnx("fcgi_parse_params failed");
523 fcgi_error(bev, EV_READ, d);
524 return;
526 break;
527 case FCGI_STDIN:
528 /* ignore */
529 evbuffer_drain(src, fcgi->fcg_toread);
530 break;
531 case FCGI_ABORT_REQUEST:
532 if (clt == NULL) {
533 log_warnx("got FCGI_ABORT_REQUEST for inactive"
534 " id (%d)", fcgi->fcg_rec_id);
535 evbuffer_drain(src, fcgi->fcg_toread);
536 break;
538 if (fcgi_end_request(clt, 1) == -1) {
539 /* calls fcgi_error on failure */
540 return;
542 break;
543 default:
544 log_warnx("unknown fastcgi record type %d",
545 fcgi->fcg_type);
546 evbuffer_drain(src, fcgi->fcg_toread);
547 break;
550 /* Prepare for the next record. */
551 evbuffer_drain(src, fcgi->fcg_padding);
552 fcgi->fcg_want = FCGI_RECORD_HEADER;
553 fcgi->fcg_toread = sizeof(struct fcgi_header);
557 void
558 fcgi_write(struct bufferevent *bev, void *d)
560 struct fcgi *fcgi = d;
561 struct evbuffer *out = EVBUFFER_OUTPUT(bev);
563 if (fcgi->fcg_done && EVBUFFER_LENGTH(out) == 0)
564 fcgi_error(bev, EVBUFFER_EOF, fcgi);
567 void
568 fcgi_error(struct bufferevent *bev, short event, void *d)
570 struct fcgi *fcgi = d;
571 struct galileo *env = fcgi->fcg_env;
572 struct client *clt;
574 log_debug("fcgi failure, shutting down connection (ev: %x)",
575 event);
576 fcgi_inflight_dec(__func__);
578 while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) {
579 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
580 proxy_client_free(clt);
583 close(fcgi->fcg_s);
584 bufferevent_free(fcgi->fcg_bev);
585 SPLAY_REMOVE(fcgi_tree, &env->sc_fcgi_socks, fcgi);
586 free(fcgi);
588 return;
591 int
592 clt_flush(struct client *clt)
594 struct fcgi *fcgi = clt->clt_fcgi;
595 struct bufferevent *bev = fcgi->fcg_bev;
596 struct fcgi_header hdr;
598 if (clt->clt_buflen == 0)
599 return (0);
601 memset(&hdr, 0, sizeof(hdr));
602 hdr.version = FCGI_VERSION_1;
603 hdr.type = FCGI_STDOUT;
604 hdr.req_id0 = (clt->clt_id & 0xFF);
605 hdr.req_id1 = (clt->clt_id >> 8);
606 hdr.content_len0 = (clt->clt_buflen & 0xFF);
607 hdr.content_len1 = (clt->clt_buflen >> 8);
609 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 ||
610 bufferevent_write(bev, clt->clt_buf, clt->clt_buflen) == -1) {
611 fcgi_error(bev, EV_WRITE, fcgi);
612 return (-1);
615 clt->clt_buflen = 0;
617 return (0);
620 int
621 clt_write(struct client *clt, const uint8_t *buf, size_t len)
623 size_t left, copy;
625 while (len > 0) {
626 left = sizeof(clt->clt_buf) - clt->clt_buflen;
627 if (left == 0) {
628 if (clt_flush(clt) == -1)
629 return (-1);
630 left = sizeof(clt->clt_buf);
633 copy = MIN(left, len);
635 memcpy(&clt->clt_buf[clt->clt_buflen], buf, copy);
636 clt->clt_buflen += copy;
637 buf += copy;
638 len -= copy;
641 return (0);
644 int
645 clt_putc(struct client *clt, char ch)
647 return (clt_write(clt, &ch, 1));
650 int
651 clt_puts(struct client *clt, const char *str)
653 return (clt_write(clt, str, strlen(str)));
656 int
657 clt_write_bufferevent(struct client *clt, struct bufferevent *bev)
659 struct evbuffer *src = EVBUFFER_INPUT(bev);
660 size_t len, left, copy;
662 len = EVBUFFER_LENGTH(src);
663 while (len > 0) {
664 left = sizeof(clt->clt_buf) - clt->clt_buflen;
665 if (left == 0) {
666 if (clt_flush(clt) == -1)
667 return (-1);
668 left = sizeof(clt->clt_buf);
671 copy = bufferevent_read(bev, &clt->clt_buf[clt->clt_buflen],
672 MIN(left, len));
673 clt->clt_buflen += copy;
675 len = EVBUFFER_LENGTH(src);
678 return (0);
681 int
682 clt_printf(struct client *clt, const char *fmt, ...)
684 struct fcgi *fcgi = clt->clt_fcgi;
685 struct bufferevent *bev = fcgi->fcg_bev;
686 char *str;
687 va_list ap;
688 int r;
690 va_start(ap, fmt);
691 r = vasprintf(&str, fmt, ap);
692 va_end(ap);
693 if (r == -1) {
694 fcgi_error(bev, EV_WRITE, fcgi);
695 return (-1);
698 r = clt_write(clt, str, r);
699 free(str);
700 return (r);
703 int
704 clt_tp_puts(struct template *tp, const char *str)
706 struct client *clt = tp->tp_arg;
708 if (clt_puts(clt, str) == -1) {
709 tp->tp_ret = -1;
710 return (-1);
713 return (0);
716 int
717 clt_tp_putc(struct template *tp, int c)
719 struct client *clt = tp->tp_arg;
721 if (clt_putc(clt, c) == -1) {
722 tp->tp_ret = -1;
723 return (-1);
726 return (0);
729 int
730 fcgi_cmp(struct fcgi *a, struct fcgi *b)
732 return ((int)a->fcg_id - b->fcg_id);
735 int
736 fcgi_client_cmp(struct client *a, struct client *b)
738 return ((int)a->clt_id - b->clt_id);
741 SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp);
742 SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp);