2 * Copyright (c) 2021 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.
24 * Sometimes it can be useful to inspect the fastcgi traffic as
27 * This will make gmid connect to a `debug.sock' socket (that must
28 * exists) in the current directory and send there a copy of what gets
29 * read. The socket can be created and monitored e.g. with
31 * rm -f debug.sock ; nc -Ulk ./debug.sock | hexdump -C
33 * NB: the sandbox must be disabled for this to work.
39 static int debug_socket = -1;
43 unsigned char version;
45 unsigned char req_id1;
46 unsigned char req_id0;
47 unsigned char content_len1;
48 unsigned char content_len0;
49 unsigned char padding;
50 unsigned char reserved;
54 * number of bytes in a FCGI_HEADER. Future version of the protocol
55 * will not reduce this number.
57 #define FCGI_HEADER_LEN 8
60 * values for the version component
62 #define FCGI_VERSION_1 1
65 * values for the type component
67 #define FCGI_BEGIN_REQUEST 1
68 #define FCGI_ABORT_REQUEST 2
69 #define FCGI_END_REQUEST 3
75 #define FCGI_GET_VALUES 9
76 #define FCGI_GET_VALUES_RESULT 10
77 #define FCGI_UNKNOWN_TYPE 11
78 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
80 struct fcgi_begin_req {
84 unsigned char reserved[5];
87 struct fcgi_begin_req_record {
88 struct fcgi_header header;
89 struct fcgi_begin_req body;
95 #define FCGI_KEEP_CONN 1
100 #define FCGI_RESPONDER 1
101 #define FCGI_AUTHORIZER 2
102 #define FCGI_FILTER 3
104 struct fcgi_end_req_body {
105 unsigned char app_status3;
106 unsigned char app_status2;
107 unsigned char app_status1;
108 unsigned char app_status0;
109 unsigned char proto_status;
110 unsigned char reserved[3];
114 * values for proto_status
116 #define FCGI_REQUEST_COMPLETE 0
117 #define FCGI_CANT_MPX_CONN 1
118 #define FCGI_OVERLOADED 2
119 #define FCGI_UNKNOWN_ROLE 3
122 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
125 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
126 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
127 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
130 prepare_header(struct fcgi_header *h, int type, int id, size_t size,
133 memset(h, 0, sizeof(*h));
136 * id=0 is reserved for status messages.
140 h->version = FCGI_VERSION_1;
142 h->req_id1 = (id >> 8);
143 h->req_id0 = (id & 0xFF);
144 h->content_len1 = (size >> 8);
145 h->content_len0 = (size & 0xFF);
146 h->padding = padding;
152 fcgi_begin_request(int sock, int id)
154 struct fcgi_begin_req_record r;
159 memset(&r, 0, sizeof(r));
160 prepare_header(&r.header, FCGI_BEGIN_REQUEST, id,
162 assert(sizeof(r.body) == FCGI_HEADER_LEN);
165 r.body.role0 = FCGI_RESPONDER;
166 r.body.flags = FCGI_KEEP_CONN;
168 if (write(sock, &r, sizeof(r)) != sizeof(r))
174 fcgi_send_param(int sock, int id, const char *name, const char *value)
176 struct fcgi_header h;
177 uint32_t namlen, vallen, padlen;
180 char padding[8] = { 0 };
182 namlen = strlen(name);
183 vallen = strlen(value);
184 size = namlen + vallen + 8; /* 4 for the sizes */
185 padlen = (8 - (size & 0x7)) & 0x7;
187 s[0] = ( namlen >> 24) | 0x80;
188 s[1] = ((namlen >> 16) & 0xFF);
189 s[2] = ((namlen >> 8) & 0xFF);
190 s[3] = ( namlen & 0xFF);
192 s[4] = ( vallen >> 24) | 0x80;
193 s[5] = ((vallen >> 16) & 0xFF);
194 s[6] = ((vallen >> 8) & 0xFF);
195 s[7] = ( vallen & 0xFF);
197 prepare_header(&h, FCGI_PARAMS, id, size, padlen);
199 if (write(sock, &h, sizeof(h)) != sizeof(h) ||
200 write(sock, s, sizeof(s)) != sizeof(s) ||
201 write(sock, name, namlen) != namlen ||
202 write(sock, value, vallen) != vallen ||
203 write(sock, padding, padlen) != padlen)
210 fcgi_end_param(int sock, int id)
212 struct fcgi_header h;
214 prepare_header(&h, FCGI_PARAMS, id, 0, 0);
215 if (write(sock, &h, sizeof(h)) != sizeof(h))
218 prepare_header(&h, FCGI_STDIN, id, 0, 0);
219 if (write(sock, &h, sizeof(h)) != sizeof(h))
226 fcgi_abort_request(int sock, int id)
228 struct fcgi_header h;
230 prepare_header(&h, FCGI_ABORT_REQUEST, id, 0, 0);
231 if (write(sock, &h, sizeof(h)) != sizeof(h))
238 must_read(int sock, char *d, size_t len)
243 if (debug_socket == -1) {
244 struct sockaddr_un addr;
246 if ((debug_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
249 memset(&addr, 0, sizeof(addr));
250 addr.sun_family = AF_UNIX;
251 strlcpy(addr.sun_path, "./debug.sock", sizeof(addr.sun_path));
252 if (connect(debug_socket, (struct sockaddr*)&addr, sizeof(addr))
259 switch (r = read(sock, d, len)) {
265 write(debug_socket, d, r);
268 if (r == (ssize_t)len)
277 fcgi_read_header(int sock, struct fcgi_header *h)
279 if (must_read(sock, (char*)h, sizeof(*h)) == -1)
281 if (h->version != FCGI_VERSION_1) {
289 recid(struct fcgi_header *h)
291 return h->req_id0 + (h->req_id1 << 8) - 1;
295 reclen(struct fcgi_header *h)
297 return h->content_len0 + (h->content_len1 << 8);
301 copy_mbuf(int fd, short ev, void *d)
303 struct client *c = d;
310 mbuf = TAILQ_FIRST(&c->mbufhead);
314 len = mbuf->len - mbuf->off;
315 data = mbuf->data + mbuf->off;
316 switch (r = tls_write(c->ctx, data, len)) {
319 * Can't close_conn here. The application
320 * needs to be informed first, otherwise it
321 * can interfere with future connections.
322 * Check also that we're not doing recursion
323 * (copy_mbuf -> handle_fcgi -> copy_mbuf ...)
327 fcgi_abort_request(0, c->id);
329 case TLS_WANT_POLLIN:
330 event_once(c->fd, EV_READ, ©_mbuf, c, NULL);
332 case TLS_WANT_POLLOUT:
333 event_once(c->fd, EV_WRITE, ©_mbuf, c, NULL);
338 if (mbuf->off == mbuf->len) {
339 TAILQ_REMOVE(&c->mbufhead, mbuf, mbufs);
350 consume(int fd, size_t len)
356 if ((l = len) > sizeof(buf))
358 if (must_read(fd, buf, l) == -1)
367 close_all(struct fcgi *f)
372 for (i = 0; i < MAX_USERS; i++) {
375 if (c->fcgi != f->id)
381 start_reply(c, CGI_ERROR, "CGI error");
384 fcgi_close_backend(f);
388 fcgi_close_backend(struct fcgi *f)
398 handle_fcgi(int sock, short event, void *d)
401 struct fcgi_header h;
402 struct fcgi_end_req_body end;
407 if (fcgi_read_header(sock, &h) == -1)
410 c = try_client_by_id(recid(&h));
411 if (c == NULL || c->fcgi != f->id)
417 case FCGI_END_REQUEST:
418 if (len != sizeof(end))
420 if (must_read(sock, (char*)&end, sizeof(end)) == -1)
422 /* TODO: do something with the status? */
426 c->next = close_conn;
427 event_once(c->fd, EV_WRITE, ©_mbuf, c, NULL);
431 /* discard stderr (for now) */
432 if (!consume(sock, len))
437 if ((mbuf = calloc(1, sizeof(*mbuf) + len)) == NULL)
440 if (must_read(sock, mbuf->data, len) == -1) {
445 if (TAILQ_EMPTY(&c->mbufhead)) {
446 TAILQ_INSERT_HEAD(&c->mbufhead, mbuf, mbufs);
447 event_once(c->fd, EV_WRITE, ©_mbuf, c, NULL);
449 TAILQ_INSERT_TAIL(&c->mbufhead, mbuf, mbufs);
453 log_err(NULL, "got invalid fcgi record (type=%d)", h.type);
457 if (!consume(sock, h.padding))
460 if (f->pending == 0 && shutting_down)
461 fcgi_close_backend(f);
470 send_fcgi_req(struct fcgi *f, struct client *c)
472 char addr[NI_MAXHOST], buf[22];
480 e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
485 fatal("getnameinfo failed");
489 fcgi_begin_request(f->fd, c->id);
490 fcgi_send_param(f->fd, c->id, "GATEWAY_INTERFACE", "CGI/1.1");
491 fcgi_send_param(f->fd, c->id, "GEMINI_URL_PATH", c->iri.path);
492 fcgi_send_param(f->fd, c->id, "QUERY_STRING", c->iri.query);
493 fcgi_send_param(f->fd, c->id, "REMOTE_ADDR", addr);
494 fcgi_send_param(f->fd, c->id, "REMOTE_HOST", addr);
495 fcgi_send_param(f->fd, c->id, "REQUEST_METHOD", "");
496 fcgi_send_param(f->fd, c->id, "SERVER_NAME", c->iri.host);
497 fcgi_send_param(f->fd, c->id, "SERVER_PROTOCOL", "GEMINI");
498 fcgi_send_param(f->fd, c->id, "SERVER_SOFTWARE", GMID_VERSION);
500 if (tls_peer_cert_provided(c->ctx)) {
501 fcgi_send_param(f->fd, c->id, "AUTH_TYPE", "CERTIFICATE");
502 fcgi_send_param(f->fd, c->id, "REMOTE_USER",
503 tls_peer_cert_subject(c->ctx));
504 fcgi_send_param(f->fd, c->id, "TLS_CLIENT_ISSUER",
505 tls_peer_cert_issuer(c->ctx));
506 fcgi_send_param(f->fd, c->id, "TLS_CLIENT_HASH",
507 tls_peer_cert_hash(c->ctx));
508 fcgi_send_param(f->fd, c->id, "TLS_VERSION",
509 tls_conn_version(c->ctx));
510 fcgi_send_param(f->fd, c->id, "TLS_CIPHER",
511 tls_conn_cipher(c->ctx));
513 snprintf(buf, sizeof(buf), "%d",
514 tls_conn_cipher_strength(c->ctx));
515 fcgi_send_param(f->fd, c->id, "TLS_CIPHER_STRENGTH", buf);
517 tim = tls_peer_cert_notbefore(c->ctx);
518 strftime(buf, sizeof(buf), "%FT%TZ",
519 gmtime_r(&tim, &tminfo));
520 fcgi_send_param(f->fd, c->id, "TLS_CLIENT_NOT_BEFORE", buf);
522 tim = tls_peer_cert_notafter(c->ctx);
523 strftime(buf, sizeof(buf), "%FT%TZ",
524 gmtime_r(&tim, &tminfo));
525 fcgi_send_param(f->fd, c->id, "TLS_CLIENT_NOT_AFTER", buf);
527 TAILQ_FOREACH(p, &c->host->params, envs) {
528 fcgi_send_param(f->fd, c->id, p->name, p->value);
531 fcgi_send_param(f->fd, c->id, "AUTH_TYPE", "");
533 if (fcgi_end_param(f->fd, c->id) == -1)