Blob


1 /*
2 * Copyright (c) 2021, 2022, 2023 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 "gmid.h"
19 #include <assert.h>
20 #include <errno.h>
21 #include <string.h>
23 #include "log.h"
25 struct fcgi_header {
26 unsigned char version;
27 unsigned char type;
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;
34 };
36 /*
37 * number of bytes in a FCGI_HEADER. Future version of the protocol
38 * will not reduce this number.
39 */
40 #define FCGI_HEADER_LEN 8
42 /*
43 * values for the version component
44 */
45 #define FCGI_VERSION_1 1
47 /*
48 * values for the type component
49 */
50 #define FCGI_BEGIN_REQUEST 1
51 #define FCGI_ABORT_REQUEST 2
52 #define FCGI_END_REQUEST 3
53 #define FCGI_PARAMS 4
54 #define FCGI_STDIN 5
55 #define FCGI_STDOUT 6
56 #define FCGI_STDERR 7
57 #define FCGI_DATA 8
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 {
64 unsigned char role1;
65 unsigned char role0;
66 unsigned char flags;
67 unsigned char reserved[5];
68 };
70 struct fcgi_begin_req_record {
71 struct fcgi_header header;
72 struct fcgi_begin_req body;
73 };
75 /*
76 * mask for flags;
77 */
78 #define FCGI_KEEP_CONN 1
80 /*
81 * values for the role
82 */
83 #define FCGI_RESPONDER 1
84 #define FCGI_AUTHORIZER 2
85 #define FCGI_FILTER 3
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];
94 };
96 /*
97 * values for proto_status
98 */
99 #define FCGI_REQUEST_COMPLETE 0
100 #define FCGI_CANT_MPX_CONN 1
101 #define FCGI_OVERLOADED 2
102 #define FCGI_UNKNOWN_ROLE 3
104 /*
105 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
106 * records.
107 */
108 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
109 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
110 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
112 static int
113 prepare_header(struct fcgi_header *h, int type, size_t size,
114 size_t padding)
116 int id = 1;
118 memset(h, 0, sizeof(*h));
120 h->version = FCGI_VERSION_1;
121 h->type = type;
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;
128 return 0;
131 static int
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);
140 r.body.role1 = 0;
141 r.body.role0 = FCGI_RESPONDER;
142 r.body.flags = FCGI_KEEP_CONN;
144 if (bufferevent_write(bev, &r, sizeof(r)) == -1)
145 return -1;
146 return 0;
149 static int
150 fcgi_send_param(struct bufferevent *bev, const char *name,
151 const char *value)
153 struct fcgi_header h;
154 uint32_t namlen, vallen, padlen;
155 uint8_t s[8];
156 size_t size;
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)
181 return -1;
183 return 0;
186 static int
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)
193 return -1;
195 prepare_header(&h, FCGI_STDIN, 0, 0);
196 if (bufferevent_write(bev, &h, sizeof(h)) == -1)
197 return -1;
199 return 0;
202 static inline int
203 recid(struct fcgi_header *h)
205 return h->req_id0 + (h->req_id1 << 8);
208 static inline int
209 reclen(struct fcgi_header *h)
211 return h->content_len0 + (h->content_len1 << 8);
214 void
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;
221 size_t len;
223 for (;;) {
224 if (EVBUFFER_LENGTH(src) < sizeof(hdr))
225 return;
227 memcpy(&hdr, EVBUFFER_DATA(src), sizeof(hdr));
229 if (recid(&hdr) != 1) {
230 log_warnx("got invalid client id %d from fcgi backend",
231 recid(&hdr));
232 goto err;
235 len = reclen(&hdr);
237 if (EVBUFFER_LENGTH(src) < sizeof(hdr) + len + hdr.padding)
238 return;
240 evbuffer_drain(src, sizeof(hdr));
242 switch (hdr.type) {
243 case FCGI_END_REQUEST:
244 if (len != sizeof(end)) {
245 log_warnx("got invalid end request"
246 " record size");
247 goto err;
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);
254 return;
256 case FCGI_STDERR:
257 /* discard stderr (for now) */
258 evbuffer_drain(src, len);
259 break;
261 case FCGI_STDOUT:
262 bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
263 evbuffer_drain(src, len);
264 break;
266 default:
267 log_warnx("got invalid fcgi record (type=%d)",
268 hdr.type);
269 goto err;
272 evbuffer_drain(src, hdr.padding);
275 err:
276 fcgi_error(bev, EVBUFFER_ERROR, c);
279 void
280 fcgi_write(struct bufferevent *bev, void *d)
282 /*
283 * There's no much use for the write callback.
284 */
285 return;
288 void
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;
297 if (c->code != 0)
298 client_close(c);
299 else
300 start_reply(c, CGI_ERROR, "CGI error");
303 void
304 fcgi_req(struct client *c)
306 char buf[22];
307 char *qs;
308 time_t tim;
309 struct tm tminfo;
310 struct envlist *p;
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) {
326 pct_decode_str(qs);
327 fcgi_send_param(c->cgibev, "GEMINI_SEARCH_STRING", qs);
328 free(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);
362 } else
363 fcgi_send_param(c->cgibev, "AUTH_TYPE", "");
365 if (fcgi_end_param(c->cgibev) == -1)
366 fcgi_error(c->cgibev, EVBUFFER_ERROR, c);