2 * Copyright (c) 2021, 2022, 2023 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.
26 unsigned char version;
28 unsigned char req_id1;
29 unsigned char req_id0;
30 unsigned char content_len1;
31 unsigned char content_len0;
32 unsigned char padding;
33 unsigned char reserved;
37 * number of bytes in a FCGI_HEADER. Future version of the protocol
38 * will not reduce this number.
40 #define FCGI_HEADER_LEN 8
43 * values for the version component
45 #define FCGI_VERSION_1 1
48 * values for the type component
50 #define FCGI_BEGIN_REQUEST 1
51 #define FCGI_ABORT_REQUEST 2
52 #define FCGI_END_REQUEST 3
58 #define FCGI_GET_VALUES 9
59 #define FCGI_GET_VALUES_RESULT 10
60 #define FCGI_UNKNOWN_TYPE 11
61 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
63 struct fcgi_begin_req {
67 unsigned char reserved[5];
70 struct fcgi_begin_req_record {
71 struct fcgi_header header;
72 struct fcgi_begin_req body;
78 #define FCGI_KEEP_CONN 1
83 #define FCGI_RESPONDER 1
84 #define FCGI_AUTHORIZER 2
87 struct fcgi_end_req_body {
88 unsigned char app_status3;
89 unsigned char app_status2;
90 unsigned char app_status1;
91 unsigned char app_status0;
92 unsigned char proto_status;
93 unsigned char reserved[3];
97 * values for proto_status
99 #define FCGI_REQUEST_COMPLETE 0
100 #define FCGI_CANT_MPX_CONN 1
101 #define FCGI_OVERLOADED 2
102 #define FCGI_UNKNOWN_ROLE 3
105 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
108 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
109 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
110 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
113 prepare_header(struct fcgi_header *h, int type, size_t size,
118 memset(h, 0, sizeof(*h));
120 h->version = FCGI_VERSION_1;
122 h->req_id1 = (id >> 8);
123 h->req_id0 = (id & 0xFF);
124 h->content_len1 = (size >> 8);
125 h->content_len0 = (size & 0xFF);
126 h->padding = padding;
132 fcgi_begin_request(struct bufferevent *bev)
134 struct fcgi_begin_req_record r;
136 memset(&r, 0, sizeof(r));
137 prepare_header(&r.header, FCGI_BEGIN_REQUEST, sizeof(r.body), 0);
138 assert(sizeof(r.body) == FCGI_HEADER_LEN);
141 r.body.role0 = FCGI_RESPONDER;
142 r.body.flags = FCGI_KEEP_CONN;
144 if (bufferevent_write(bev, &r, sizeof(r)) == -1)
150 fcgi_send_param(struct bufferevent *bev, const char *name,
153 struct fcgi_header h;
154 uint32_t namlen, vallen, padlen;
157 const char padding[8] = { 0 };
159 namlen = strlen(name);
160 vallen = strlen(value);
161 size = namlen + vallen + 8; /* 4 for the sizes */
162 padlen = (8 - (size & 0x7)) & 0x7;
164 s[0] = ( namlen >> 24) | 0x80;
165 s[1] = ((namlen >> 16) & 0xFF);
166 s[2] = ((namlen >> 8) & 0xFF);
167 s[3] = ( namlen & 0xFF);
169 s[4] = ( vallen >> 24) | 0x80;
170 s[5] = ((vallen >> 16) & 0xFF);
171 s[6] = ((vallen >> 8) & 0xFF);
172 s[7] = ( vallen & 0xFF);
174 prepare_header(&h, FCGI_PARAMS, size, padlen);
176 if (bufferevent_write(bev, &h, sizeof(h)) == -1 ||
177 bufferevent_write(bev, s, sizeof(s)) == -1 ||
178 bufferevent_write(bev, name, namlen) == -1 ||
179 bufferevent_write(bev, value, vallen) == -1 ||
180 bufferevent_write(bev, padding, padlen) == -1)
187 fcgi_end_param(struct bufferevent *bev)
189 struct fcgi_header h;
191 prepare_header(&h, FCGI_PARAMS, 0, 0);
192 if (bufferevent_write(bev, &h, sizeof(h)) == -1)
195 prepare_header(&h, FCGI_STDIN, 0, 0);
196 if (bufferevent_write(bev, &h, sizeof(h)) == -1)
203 recid(struct fcgi_header *h)
205 return h->req_id0 + (h->req_id1 << 8);
209 reclen(struct fcgi_header *h)
211 return h->content_len0 + (h->content_len1 << 8);
215 fcgi_read(struct bufferevent *bev, void *d)
217 struct client *c = d;
218 struct evbuffer *src = EVBUFFER_INPUT(bev);
219 struct fcgi_header hdr;
220 struct fcgi_end_req_body end;
224 if (EVBUFFER_LENGTH(src) < sizeof(hdr))
227 memcpy(&hdr, EVBUFFER_DATA(src), sizeof(hdr));
229 if (recid(&hdr) != 1) {
230 log_warnx("got invalid client id %d from fcgi backend",
237 if (EVBUFFER_LENGTH(src) < sizeof(hdr) + len + hdr.padding)
240 evbuffer_drain(src, sizeof(hdr));
243 case FCGI_END_REQUEST:
244 if (len != sizeof(end)) {
245 log_warnx("got invalid end request"
249 bufferevent_read(bev, &end, sizeof(end));
251 /* TODO: do something with the status? */
252 c->type = REQUEST_DONE;
253 client_write(c->bev, c);
257 /* discard stderr (for now) */
258 evbuffer_drain(src, len);
262 bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
263 evbuffer_drain(src, len);
267 log_warnx("got invalid fcgi record (type=%d)",
272 evbuffer_drain(src, hdr.padding);
276 fcgi_error(bev, EVBUFFER_ERROR, c);
280 fcgi_write(struct bufferevent *bev, void *d)
283 * There's no much use for the write callback.
289 fcgi_error(struct bufferevent *bev, short err, void *d)
291 struct client *c = d;
293 if (!(err & (EVBUFFER_ERROR|EVBUFFER_EOF)))
294 log_warn("unknown event error (%x)", err);
296 c->type = REQUEST_DONE;
300 start_reply(c, CGI_ERROR, "CGI error");
304 fcgi_req(struct client *c)
312 fcgi_begin_request(c->cgibev);
313 fcgi_send_param(c->cgibev, "GATEWAY_INTERFACE", "CGI/1.1");
314 fcgi_send_param(c->cgibev, "GEMINI_URL_PATH", c->iri.path);
315 fcgi_send_param(c->cgibev, "QUERY_STRING", c->iri.query);
316 fcgi_send_param(c->cgibev, "REMOTE_ADDR", c->rhost);
317 fcgi_send_param(c->cgibev, "REMOTE_HOST", c->rhost);
318 fcgi_send_param(c->cgibev, "REQUEST_METHOD", "");
319 fcgi_send_param(c->cgibev, "SERVER_NAME", c->iri.host);
320 fcgi_send_param(c->cgibev, "SERVER_PROTOCOL", "GEMINI");
321 fcgi_send_param(c->cgibev, "SERVER_SOFTWARE", GMID_VERSION);
323 if (*c->iri.query != '\0' &&
324 strchr(c->iri.query, '=') == NULL &&
325 (qs = strdup(c->iri.query)) != NULL) {
327 fcgi_send_param(c->cgibev, "GEMINI_SEARCH_STRING", qs);
331 TAILQ_FOREACH(p, &c->host->params, envs) {
332 fcgi_send_param(c->cgibev, p->name, p->value);
335 if (tls_peer_cert_provided(c->ctx)) {
336 fcgi_send_param(c->cgibev, "AUTH_TYPE", "CERTIFICATE");
337 fcgi_send_param(c->cgibev, "REMOTE_USER",
338 tls_peer_cert_subject(c->ctx));
339 fcgi_send_param(c->cgibev, "TLS_CLIENT_ISSUER",
340 tls_peer_cert_issuer(c->ctx));
341 fcgi_send_param(c->cgibev, "TLS_CLIENT_HASH",
342 tls_peer_cert_hash(c->ctx));
343 fcgi_send_param(c->cgibev, "TLS_VERSION",
344 tls_conn_version(c->ctx));
345 fcgi_send_param(c->cgibev, "TLS_CIPHER",
346 tls_conn_cipher(c->ctx));
348 snprintf(buf, sizeof(buf), "%d",
349 tls_conn_cipher_strength(c->ctx));
350 fcgi_send_param(c->cgibev, "TLS_CIPHER_STRENGTH", buf);
352 tim = tls_peer_cert_notbefore(c->ctx);
353 strftime(buf, sizeof(buf), "%FT%TZ",
354 gmtime_r(&tim, &tminfo));
355 fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_BEFORE", buf);
357 tim = tls_peer_cert_notafter(c->ctx);
358 strftime(buf, sizeof(buf), "%FT%TZ",
359 gmtime_r(&tim, &tminfo));
360 fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_AFTER", buf);
363 fcgi_send_param(c->cgibev, "AUTH_TYPE", "");
365 if (fcgi_end_param(c->cgibev) == -1)
366 fcgi_error(c->cgibev, EVBUFFER_ERROR, c);