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"
36 #include "galileo.h"
38 #define MIN(a, b) ((a) < (b) ? (a) : (b))
40 struct fcgi_header {
41 unsigned char version;
42 unsigned char type;
43 unsigned char req_id1;
44 unsigned char req_id0;
45 unsigned char content_len1;
46 unsigned char content_len0;
47 unsigned char padding;
48 unsigned char reserved;
49 } __attribute__((packed));
51 /*
52 * number of bytes in a FCGI_HEADER. Future version of the protocol
53 * will not reduce this number.
54 */
55 #define FCGI_HEADER_LEN 8
57 /*
58 * values for the version component
59 */
60 #define FCGI_VERSION_1 1
62 /*
63 * values for the type component
64 */
65 #define FCGI_BEGIN_REQUEST 1
66 #define FCGI_ABORT_REQUEST 2
67 #define FCGI_END_REQUEST 3
68 #define FCGI_PARAMS 4
69 #define FCGI_STDIN 5
70 #define FCGI_STDOUT 6
71 #define FCGI_STDERR 7
72 #define FCGI_DATA 8
73 #define FCGI_GET_VALUES 9
74 #define FCGI_GET_VALUES_RESULT 10
75 #define FCGI_UNKNOWN_TYPE 11
76 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
78 struct fcgi_begin_req {
79 unsigned char role1;
80 unsigned char role0;
81 unsigned char flags;
82 unsigned char reserved[5];
83 };
85 struct fcgi_begin_req_record {
86 struct fcgi_header header;
87 struct fcgi_begin_req body;
88 };
90 /*
91 * mask for flags;
92 */
93 #define FCGI_KEEP_CONN 1
95 /*
96 * values for the role
97 */
98 #define FCGI_RESPONDER 1
99 #define FCGI_AUTHORIZER 2
100 #define FCGI_FILTER 3
102 struct fcgi_end_req_body {
103 unsigned char app_status3;
104 unsigned char app_status2;
105 unsigned char app_status1;
106 unsigned char app_status0;
107 unsigned char proto_status;
108 unsigned char reserved[3];
109 };
111 /*
112 * values for proto_status
113 */
114 #define FCGI_REQUEST_COMPLETE 0
115 #define FCGI_CANT_MPX_CONN 1
116 #define FCGI_OVERLOADED 2
117 #define FCGI_UNKNOWN_ROLE 3
119 /*
120 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
121 * records.
122 */
123 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
124 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
125 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
127 #define CAT(f0, f1) ((f0) + ((f1) << 8))
129 enum {
130 FCGI_RECORD_HEADER,
131 FCGI_RECORD_BODY,
132 };
134 volatile int fcgi_inflight;
136 static int
137 fcgi_send_end_req(struct fcgi *fcgi, int id, int as, int ps)
139 struct bufferevent *bev = fcgi->fcg_bev;
140 struct fcgi_header hdr;
141 struct fcgi_end_req_body end;
143 memset(&hdr, 0, sizeof(hdr));
144 memset(&end, 0, sizeof(end));
146 hdr.version = FCGI_VERSION_1;
147 hdr.type = FCGI_END_REQUEST;
148 hdr.req_id0 = (id & 0xFF);
149 hdr.req_id1 = (id >> 8);
150 hdr.content_len0 = sizeof(end);
152 end.app_status0 = (unsigned char)as;
153 end.proto_status = (unsigned char)ps;
155 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1)
156 return (-1);
157 if (bufferevent_write(bev, &end, sizeof(end)) == -1)
158 return (-1);
159 return (0);
162 static int
163 end_request(struct client *clt, int status, int proto_status)
165 struct fcgi *fcgi = clt->clt_fcgi;
166 int r;
168 if (clt_flush(clt) == -1)
169 return (-1);
171 r = fcgi_send_end_req(fcgi, clt->clt_id, status,
172 proto_status);
173 if (r == -1) {
174 fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi);
175 return (-1);
178 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
179 proxy_client_free(clt);
180 return (0);
183 int
184 fcgi_end_request(struct client *clt, int status)
186 return (end_request(clt, status, FCGI_REQUEST_COMPLETE));
189 int
190 fcgi_abort_request(struct client *clt)
192 return (end_request(clt, 1, FCGI_OVERLOADED));
195 static void
196 fcgi_inflight_dec(const char *why)
198 fcgi_inflight--;
199 log_debug("%s: fcgi inflight decremented, now %d, %s",
200 __func__, fcgi_inflight, why);
203 void
204 fcgi_accept(struct galileo *env)
206 struct fcgi *fcgi = NULL;
207 socklen_t slen;
208 struct sockaddr_storage ss;
209 int s = -1;
211 slen = sizeof(ss);
212 if ((s = accept_reserve(env->sc_sock_fd, (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->sc_evsock);
222 evtimer_add(&env->sc_evpause, &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 = ++proxy_fcg_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 int nlen, vlen;
283 while (fcgi->fcg_toread > 0) {
284 if ((nlen = parse_len(fcgi, src)) < 0 ||
285 (vlen = parse_len(fcgi, src)) < 0)
286 return (-1);
288 if (fcgi->fcg_toread < nlen + vlen)
289 return (-1);
291 if ((size_t)nlen > sizeof(pname) - 1) {
292 /* ignore this parameter */
293 fcgi->fcg_toread -= nlen - vlen;
294 evbuffer_drain(src, nlen + vlen);
295 continue;
298 fcgi->fcg_toread -= nlen;
299 evbuffer_remove(src, &pname, nlen);
300 pname[nlen] = '\0';
302 if (!strcmp(pname, "SERVER_NAME") &&
303 (size_t)vlen < sizeof(server)) {
304 fcgi->fcg_toread -= vlen;
305 evbuffer_remove(src, &server, vlen);
306 server[vlen] = '\0';
308 if ((clt->clt_server_name = strdup(server)) == NULL)
309 return (-1);
310 log_debug("clt %d: server_name: %s", clt->clt_id,
311 clt->clt_server_name);
312 continue;
315 if (!strcmp(pname, "SCRIPT_NAME") &&
316 (size_t)vlen < sizeof(path)) {
317 fcgi->fcg_toread -= vlen;
318 evbuffer_remove(src, &path, vlen);
319 path[vlen] = '\0';
321 if ((clt->clt_script_name = strdup(path)) == NULL)
322 return (-1);
323 log_debug("clt %d: script_name: %s", clt->clt_id,
324 clt->clt_script_name);
325 continue;
328 if (!strcmp(pname, "PATH_INFO") &&
329 (size_t)vlen < sizeof(path)) {
330 fcgi->fcg_toread -= vlen;
331 evbuffer_remove(src, &path, vlen);
332 path[vlen] = '\0';
334 if ((clt->clt_path_info = strdup(path)) == NULL)
335 return (-1);
336 log_debug("clt %d: path_info: %s", clt->clt_id,
337 clt->clt_path_info);
338 continue;
341 fcgi->fcg_toread -= vlen;
342 evbuffer_drain(src, vlen);
345 return (0);
348 void
349 fcgi_read(struct bufferevent *bev, void *d)
351 struct fcgi *fcgi = d;
352 struct galileo *env = fcgi->fcg_env;
353 struct evbuffer *src = EVBUFFER_INPUT(bev);
354 struct fcgi_header hdr;
355 struct fcgi_begin_req breq;
356 struct client *clt, q;
357 int role;
359 memset(&q, 0, sizeof(q));
361 for (;;) {
362 if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread)
363 return;
365 if (fcgi->fcg_want == FCGI_RECORD_HEADER) {
366 fcgi->fcg_want = FCGI_RECORD_BODY;
367 bufferevent_read(bev, &hdr, sizeof(hdr));
369 #ifdef DEBUG
370 log_warnx("header: v=%d t=%d id=%d len=%d p=%d",
371 hdr.version, hdr.type,
372 CAT(hdr.req_id0, hdr.req_id1),
373 CAT(hdr.content_len0, hdr.content_len1),
374 hdr.padding);
375 #endif
377 if (hdr.version != FCGI_VERSION_1) {
378 log_warnx("unknown fastcgi version: %d",
379 hdr.version);
380 fcgi_error(bev, EV_READ, d);
381 return;
384 fcgi->fcg_toread = CAT(hdr.content_len0,
385 hdr.content_len1);
386 if (fcgi->fcg_toread < 0) {
387 log_warnx("invalid record length: %d",
388 fcgi->fcg_toread);
389 fcgi_error(bev, EV_READ, d);
390 return;
393 fcgi->fcg_padding = hdr.padding;
394 if (fcgi->fcg_padding < 0) {
395 log_warnx("invalid padding: %d",
396 fcgi->fcg_padding);
397 fcgi_error(bev, EV_READ, d);
398 return;
401 fcgi->fcg_type = hdr.type;
402 fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1);
403 continue;
406 q.clt_id = fcgi->fcg_rec_id;
407 clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q);
409 switch (fcgi->fcg_type) {
410 case FCGI_BEGIN_REQUEST:
411 if (sizeof(breq) != fcgi->fcg_toread) {
412 log_warnx("unexpected size for "
413 "FCGI_BEGIN_REQUEST");
414 fcgi_error(bev, EV_READ, d);
415 return;
418 evbuffer_remove(src, &breq, sizeof(breq));
420 role = CAT(breq.role0, breq.role1);
421 if (role != FCGI_RESPONDER) {
422 log_warnx("unknown fastcgi role: %d",
423 role);
424 if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id,
425 1, FCGI_UNKNOWN_ROLE) == -1) {
426 fcgi_error(bev, EV_READ, d);
427 return;
429 break;
432 if (!fcgi->fcg_keep_conn) {
433 log_warnx("trying to reuse the fastcgi "
434 "socket without marking it as so.");
435 fcgi_error(bev, EV_READ, d);
436 return;
438 fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN;
440 if (clt != NULL) {
441 log_warnx("ignoring attemp to re-use an "
442 "active request id (%d)",
443 fcgi->fcg_rec_id);
444 break;
447 if ((clt = calloc(1, sizeof(*clt))) == NULL) {
448 log_warnx("calloc");
449 break;
452 clt->clt_id = fcgi->fcg_rec_id;
453 clt->clt_fd = -1;
454 clt->clt_fcgi = fcgi;
455 SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt);
456 break;
457 case FCGI_PARAMS:
458 if (clt == NULL) {
459 log_warnx("got FCGI_PARAMS for inactive id "
460 "(%d)", fcgi->fcg_rec_id);
461 evbuffer_drain(src, fcgi->fcg_toread);
462 break;
464 if (fcgi->fcg_toread == 0) {
465 evbuffer_drain(src, fcgi->fcg_toread);
466 proxy_start_request(env, clt);
467 break;
469 if (fcgi_parse_params(fcgi, src, clt) == -1) {
470 log_warnx("fcgi_parse_params failed");
471 fcgi_error(bev, EV_READ, d);
472 return;
474 break;
475 case FCGI_STDIN:
476 /* ignore */
477 evbuffer_drain(src, fcgi->fcg_toread);
478 break;
479 case FCGI_ABORT_REQUEST:
480 if (clt == NULL) {
481 log_warnx("got FCGI_ABORT_REQUEST for inactive"
482 " id (%d)", fcgi->fcg_rec_id);
483 evbuffer_drain(src, fcgi->fcg_toread);
484 break;
486 if (fcgi_end_request(clt, 1) == -1) {
487 /* calls fcgi_error on failure */
488 return;
490 break;
491 default:
492 log_warnx("unknown fastcgi record type %d",
493 fcgi->fcg_type);
494 evbuffer_drain(src, fcgi->fcg_toread);
495 break;
498 /* Prepare for the next record. */
499 evbuffer_drain(src, fcgi->fcg_padding);
500 fcgi->fcg_want = FCGI_RECORD_HEADER;
501 fcgi->fcg_toread = sizeof(struct fcgi_header);
505 void
506 fcgi_write(struct bufferevent *bev, void *d)
508 struct fcgi *fcgi = d;
510 (void)fcgi;
513 void
514 fcgi_error(struct bufferevent *bev, short event, void *d)
516 struct fcgi *fcgi = d;
517 struct galileo *env = fcgi->fcg_env;
518 struct client *clt;
520 log_debug("fcgi failure, shutting down connection (ev: %x)",
521 event);
522 fcgi_inflight_dec(__func__);
524 while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) {
525 SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt);
526 proxy_client_free(clt);
529 close(fcgi->fcg_s);
530 bufferevent_free(fcgi->fcg_bev);
531 SPLAY_REMOVE(fcgi_tree, &env->sc_fcgi_socks, fcgi);
532 free(fcgi);
534 return;
537 int
538 clt_flush(struct client *clt)
540 struct fcgi *fcgi = clt->clt_fcgi;
541 struct bufferevent *bev = fcgi->fcg_bev;
542 struct fcgi_header hdr;
544 if (clt->clt_buflen == 0)
545 return (0);
547 memset(&hdr, 0, sizeof(hdr));
548 hdr.version = FCGI_VERSION_1;
549 hdr.type = FCGI_STDOUT;
550 hdr.req_id0 = (clt->clt_id & 0xFF);
551 hdr.req_id1 = (clt->clt_id >> 8);
552 hdr.content_len0 = (clt->clt_buflen & 0xFF);
553 hdr.content_len1 = (clt->clt_buflen >> 8);
555 if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 ||
556 bufferevent_write(bev, clt->clt_buf, clt->clt_buflen) == -1) {
557 fcgi_error(bev, EV_WRITE, fcgi);
558 return (-1);
561 clt->clt_buflen = 0;
563 return (0);
566 int
567 clt_write(struct client *clt, const uint8_t *buf, size_t len)
569 size_t left, copy;
571 while (len > 0) {
572 left = sizeof(clt->clt_buf) - clt->clt_buflen;
573 if (left == 0) {
574 if (clt_flush(clt) == -1)
575 return (-1);
576 left = sizeof(clt->clt_buf);
579 copy = MIN(left, len);
581 memcpy(&clt->clt_buf[clt->clt_buflen], buf, copy);
582 clt->clt_buflen += copy;
583 buf += copy;
584 len -= copy;
587 return (0);
590 int
591 clt_write_bufferevent(struct client *clt, struct bufferevent *bev)
593 struct evbuffer *src = EVBUFFER_INPUT(bev);
594 size_t len, left, copy;
596 len = EVBUFFER_LENGTH(src);
597 while (len > 0) {
598 left = sizeof(clt->clt_buf) - clt->clt_buflen;
599 if (left == 0) {
600 if (clt_flush(clt) == -1)
601 return (-1);
602 left = sizeof(clt->clt_buf);
605 copy = bufferevent_read(bev, &clt->clt_buf[clt->clt_buflen],
606 MIN(left, len));
607 clt->clt_buflen += copy;
609 len = EVBUFFER_LENGTH(src);
612 return (0);
615 int
616 clt_printf(struct client *clt, const char *fmt, ...)
618 struct fcgi *fcgi = clt->clt_fcgi;
619 struct bufferevent *bev = fcgi->fcg_bev;
620 char *str;
621 va_list ap;
622 int r;
624 va_start(ap, fmt);
625 r = vasprintf(&str, fmt, ap);
626 va_end(ap);
627 if (r == -1) {
628 fcgi_error(bev, EV_WRITE, fcgi);
629 return (-1);
632 r = clt_write(clt, str, r);
633 free(str);
634 return (r);
637 int
638 fcgi_cmp(struct fcgi *a, struct fcgi *b)
640 return ((int)a->fcg_id - b->fcg_id);
643 int
644 fcgi_client_cmp(struct client *a, struct client *b)
646 return ((int)a->clt_id - b->clt_id);
649 SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp);
650 SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp);