xref: /openbsd-src/usr.sbin/httpd/server_fcgi.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: server_fcgi.c,v 1.88 2021/05/20 15:12:10 florian Exp $	*/
2 
3 /*
4  * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 
27 #include <limits.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <time.h>
33 #include <ctype.h>
34 #include <event.h>
35 #include <unistd.h>
36 
37 #include "httpd.h"
38 #include "http.h"
39 
40 #define FCGI_PADDING_SIZE	 255
41 #define FCGI_RECORD_SIZE	 \
42     (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE)
43 
44 #define FCGI_BEGIN_REQUEST	 1
45 #define FCGI_ABORT_REQUEST	 2
46 #define FCGI_END_REQUEST	 3
47 #define FCGI_PARAMS		 4
48 #define FCGI_STDIN		 5
49 #define FCGI_STDOUT		 6
50 #define FCGI_STDERR		 7
51 #define FCGI_DATA		 8
52 #define FCGI_GET_VALUES		 9
53 #define FCGI_GET_VALUES_RESULT	10
54 #define FCGI_UNKNOWN_TYPE	11
55 #define FCGI_MAXTYPE		(FCGI_UNKNOWN_TYPE)
56 
57 #define FCGI_RESPONDER		 1
58 
59 struct fcgi_record_header {
60 	uint8_t		version;
61 	uint8_t		type;
62 	uint16_t	id;
63 	uint16_t	content_len;
64 	uint8_t		padding_len;
65 	uint8_t		reserved;
66 } __packed;
67 
68 struct fcgi_begin_request_body {
69 	uint16_t	role;
70 	uint8_t		flags;
71 	uint8_t		reserved[5];
72 } __packed;
73 
74 struct server_fcgi_param {
75 	int		total_len;
76 	uint8_t		buf[FCGI_RECORD_SIZE];
77 };
78 
79 int	server_fcgi_header(struct client *, unsigned int);
80 void	server_fcgi_read(struct bufferevent *, void *);
81 int	server_fcgi_writeheader(struct client *, struct kv *, void *);
82 int	server_fcgi_writechunk(struct client *);
83 int	server_fcgi_getheaders(struct client *);
84 int	fcgi_add_param(struct server_fcgi_param *, const char *, const char *,
85 	    struct client *);
86 
87 int
88 server_fcgi(struct httpd *env, struct client *clt)
89 {
90 	struct server_fcgi_param	 param;
91 	struct server_config		*srv_conf = clt->clt_srv_conf;
92 	struct http_descriptor		*desc = clt->clt_descreq;
93 	struct fcgi_record_header	*h;
94 	struct fcgi_begin_request_body	*begin;
95 	struct fastcgi_param		*fcgiparam;
96 	char				 hbuf[HOST_NAME_MAX+1];
97 	size_t				 scriptlen;
98 	int				 pathlen;
99 	int				 fd = -1, ret;
100 	const char			*stripped, *alias, *errstr = NULL;
101 	char				*query_alias, *str, *script = NULL;
102 
103 	if ((fd = socket(srv_conf->fastcgi_ss.ss_family,
104 	    SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
105 		goto fail;
106 	if ((connect(fd, (struct sockaddr *) &srv_conf->fastcgi_ss,
107 	    srv_conf->fastcgi_ss.ss_len)) == -1) {
108 		if (errno != EINPROGRESS)
109 			goto fail;
110 	}
111 
112 	memset(hbuf, 0, sizeof(hbuf));
113 	clt->clt_fcgi.state = FCGI_READ_HEADER;
114 	clt->clt_fcgi.toread = sizeof(struct fcgi_record_header);
115 	clt->clt_fcgi.status = 200;
116 	clt->clt_fcgi.headersdone = 0;
117 	clt->clt_fcgi.headerssent = 0;
118 
119 	if (clt->clt_srvevb != NULL)
120 		evbuffer_free(clt->clt_srvevb);
121 
122 	clt->clt_srvevb = evbuffer_new();
123 	if (clt->clt_srvevb == NULL) {
124 		errstr = "failed to allocate evbuffer";
125 		goto fail;
126 	}
127 
128 	close(clt->clt_fd);
129 	clt->clt_fd = fd;
130 
131 	if (clt->clt_srvbev != NULL)
132 		bufferevent_free(clt->clt_srvbev);
133 
134 	clt->clt_srvbev_throttled = 0;
135 	clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read,
136 	    NULL, server_file_error, clt);
137 	if (clt->clt_srvbev == NULL) {
138 		errstr = "failed to allocate fcgi buffer event";
139 		goto fail;
140 	}
141 
142 	memset(&param, 0, sizeof(param));
143 
144 	h = (struct fcgi_record_header *)&param.buf;
145 	h->version = 1;
146 	h->type = FCGI_BEGIN_REQUEST;
147 	h->id = htons(1);
148 	h->content_len = htons(sizeof(struct fcgi_begin_request_body));
149 	h->padding_len = 0;
150 
151 	begin = (struct fcgi_begin_request_body *)&param.buf[sizeof(struct
152 	    fcgi_record_header)];
153 	begin->role = htons(FCGI_RESPONDER);
154 
155 	if (bufferevent_write(clt->clt_srvbev, &param.buf,
156 	    sizeof(struct fcgi_record_header) +
157 	    sizeof(struct fcgi_begin_request_body)) == -1) {
158 		errstr = "failed to write to evbuffer";
159 		goto fail;
160 	}
161 
162 	h->type = FCGI_PARAMS;
163 	h->content_len = param.total_len = 0;
164 
165 	alias = desc->http_path_alias != NULL
166 	    ? desc->http_path_alias
167 	    : desc->http_path;
168 
169 	query_alias = desc->http_query_alias != NULL
170 	    ? desc->http_query_alias
171 	    : desc->http_query;
172 
173 	stripped = server_root_strip(alias, srv_conf->strip);
174 	if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped))
175 	    == -1) {
176 		errstr = "failed to get script name";
177 		goto fail;
178 	}
179 
180 	scriptlen = path_info(script);
181 	/*
182 	 * no part of root should show up in PATH_INFO.
183 	 * therefore scriptlen should be >= strlen(root)
184 	 */
185 	if (scriptlen < strlen(srv_conf->root))
186 		scriptlen = strlen(srv_conf->root);
187 	if ((int)scriptlen < pathlen) {
188 		if (fcgi_add_param(&param, "PATH_INFO",
189 		    script + scriptlen, clt) == -1) {
190 			errstr = "failed to encode param";
191 			goto fail;
192 		}
193 		script[scriptlen] = '\0';
194 	} else {
195 		/* RFC 3875 mandates that PATH_INFO is empty if not set */
196 		if (fcgi_add_param(&param, "PATH_INFO", "", clt) == -1) {
197 			errstr = "failed to encode param";
198 			goto fail;
199 		}
200 	}
201 
202 	/*
203 	 * calculate length of http SCRIPT_NAME:
204 	 * add length of stripped prefix,
205 	 * subtract length of prepended local root
206 	 */
207 	scriptlen += (stripped - alias) - strlen(srv_conf->root);
208 	if ((str = strndup(alias, scriptlen)) == NULL)
209 		goto fail;
210 	ret = fcgi_add_param(&param, "SCRIPT_NAME", str, clt);
211 	free(str);
212 	if (ret == -1) {
213 		errstr = "failed to encode param";
214 		goto fail;
215 	}
216 	if (fcgi_add_param(&param, "SCRIPT_FILENAME", server_root_strip(script,
217 	    srv_conf->fcgistrip), clt) == -1) {
218 		errstr = "failed to encode param";
219 		goto fail;
220 	}
221 
222 	if (query_alias) {
223 		if (fcgi_add_param(&param, "QUERY_STRING", query_alias,
224 		    clt) == -1) {
225 			errstr = "failed to encode param";
226 			goto fail;
227 		}
228 	} else if (fcgi_add_param(&param, "QUERY_STRING", "", clt) == -1) {
229 		errstr = "failed to encode param";
230 		goto fail;
231 	}
232 
233 	if (fcgi_add_param(&param, "DOCUMENT_ROOT", server_root_strip(
234 	    srv_conf->root, srv_conf->fcgistrip), clt) == -1) {
235 		errstr = "failed to encode param";
236 		goto fail;
237 	}
238 	if (fcgi_add_param(&param, "DOCUMENT_URI", alias,
239 	    clt) == -1) {
240 		errstr = "failed to encode param";
241 		goto fail;
242 	}
243 	if (fcgi_add_param(&param, "GATEWAY_INTERFACE", "CGI/1.1",
244 	    clt) == -1) {
245 		errstr = "failed to encode param";
246 		goto fail;
247 	}
248 
249 	if (srv_conf->flags & SRVFLAG_AUTH) {
250 		if (fcgi_add_param(&param, "REMOTE_USER",
251 		    clt->clt_remote_user, clt) == -1) {
252 			errstr = "failed to encode param";
253 			goto fail;
254 		}
255 	}
256 
257 	/* Add HTTP_* headers */
258 	if (server_headers(clt, desc, server_fcgi_writeheader, &param) == -1) {
259 		errstr = "failed to encode param";
260 		goto fail;
261 	}
262 
263 	if (srv_conf->flags & SRVFLAG_TLS) {
264 		if (fcgi_add_param(&param, "HTTPS", "on", clt) == -1) {
265 			errstr = "failed to encode param";
266 			goto fail;
267 		}
268 		if (srv_conf->tls_flags != 0 && fcgi_add_param(&param,
269 		    "TLS_PEER_VERIFY", printb_flags(srv_conf->tls_flags,
270 		    TLSFLAG_BITS), clt) == -1) {
271 			errstr = "failed to encode param";
272 			goto fail;
273 		}
274 	}
275 
276 	TAILQ_FOREACH(fcgiparam, &srv_conf->fcgiparams, entry) {
277 		if (fcgi_add_param(&param, fcgiparam->name, fcgiparam->value,
278 		    clt) == -1) {
279 			errstr = "failed to encode param";
280 			goto fail;
281 		}
282 	}
283 
284 	(void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf));
285 	if (fcgi_add_param(&param, "REMOTE_ADDR", hbuf, clt) == -1) {
286 		errstr = "failed to encode param";
287 		goto fail;
288 	}
289 
290 	(void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port));
291 	if (fcgi_add_param(&param, "REMOTE_PORT", hbuf, clt) == -1) {
292 		errstr = "failed to encode param";
293 		goto fail;
294 	}
295 
296 	if (fcgi_add_param(&param, "REQUEST_METHOD",
297 	    server_httpmethod_byid(desc->http_method), clt) == -1) {
298 		errstr = "failed to encode param";
299 		goto fail;
300 	}
301 
302 	if (!desc->http_query) {
303 		if (fcgi_add_param(&param, "REQUEST_URI", desc->http_path_orig,
304 		    clt) == -1) {
305 			errstr = "failed to encode param";
306 			goto fail;
307 		}
308 	} else {
309 		if (asprintf(&str, "%s?%s", desc->http_path_orig,
310 		    desc->http_query) == -1) {
311 			errstr = "failed to encode param";
312 			goto fail;
313 		}
314 		ret = fcgi_add_param(&param, "REQUEST_URI", str, clt);
315 		free(str);
316 		if (ret == -1) {
317 			errstr = "failed to encode param";
318 			goto fail;
319 		}
320 	}
321 
322 	(void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf));
323 	if (fcgi_add_param(&param, "SERVER_ADDR", hbuf, clt) == -1) {
324 		errstr = "failed to encode param";
325 		goto fail;
326 	}
327 
328 	(void)snprintf(hbuf, sizeof(hbuf), "%d",
329 	    ntohs(server_socket_getport(&clt->clt_srv_ss)));
330 	if (fcgi_add_param(&param, "SERVER_PORT", hbuf, clt) == -1) {
331 		errstr = "failed to encode param";
332 		goto fail;
333 	}
334 
335 	if (fcgi_add_param(&param, "SERVER_NAME", srv_conf->name,
336 	    clt) == -1) {
337 		errstr = "failed to encode param";
338 		goto fail;
339 	}
340 
341 	if (fcgi_add_param(&param, "SERVER_PROTOCOL", desc->http_version,
342 	    clt) == -1) {
343 		errstr = "failed to encode param";
344 		goto fail;
345 	}
346 
347 	if (fcgi_add_param(&param, "SERVER_SOFTWARE", HTTPD_SERVERNAME,
348 	    clt) == -1) {
349 		errstr = "failed to encode param";
350 		goto fail;
351 	}
352 
353 	if (param.total_len != 0) {	/* send last params record */
354 		if (bufferevent_write(clt->clt_srvbev, &param.buf,
355 		    sizeof(struct fcgi_record_header) +
356 		    ntohs(h->content_len)) == -1) {
357 			errstr = "failed to write to client evbuffer";
358 			goto fail;
359 		}
360 	}
361 
362 	/* send "no more params" message */
363 	h->content_len = 0;
364 	if (bufferevent_write(clt->clt_srvbev, &param.buf,
365 	    sizeof(struct fcgi_record_header)) == -1) {
366 		errstr = "failed to write to client evbuffer";
367 		goto fail;
368 	}
369 
370 	bufferevent_settimeout(clt->clt_srvbev,
371 	    srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
372 	bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE);
373 	if (clt->clt_toread != 0) {
374 		server_read_httpcontent(clt->clt_bev, clt);
375 		bufferevent_enable(clt->clt_bev, EV_READ);
376 	} else {
377 		bufferevent_disable(clt->clt_bev, EV_READ);
378 		fcgi_add_stdin(clt, NULL);
379 	}
380 
381 	if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
382 		clt->clt_fcgi.chunked = 1;
383 	} else {
384 		/* HTTP/1.0 does not support chunked encoding */
385 		clt->clt_fcgi.chunked = 0;
386 		clt->clt_persist = 0;
387 	}
388 	clt->clt_fcgi.end = 0;
389 	clt->clt_done = 0;
390 
391 	free(script);
392 	return (0);
393  fail:
394 	free(script);
395 	if (errstr == NULL)
396 		errstr = strerror(errno);
397 	if (fd != -1 && clt->clt_fd != fd)
398 		close(fd);
399 	server_abort_http(clt, 500, errstr);
400 	return (-1);
401 }
402 
403 int
404 fcgi_add_stdin(struct client *clt, struct evbuffer *evbuf)
405 {
406 	struct fcgi_record_header	h;
407 
408 	memset(&h, 0, sizeof(h));
409 	h.version = 1;
410 	h.type = FCGI_STDIN;
411 	h.id = htons(1);
412 	h.padding_len = 0;
413 
414 	if (evbuf == NULL) {
415 		h.content_len = 0;
416 		return bufferevent_write(clt->clt_srvbev, &h,
417 		    sizeof(struct fcgi_record_header));
418 	} else {
419 		h.content_len = htons(EVBUFFER_LENGTH(evbuf));
420 		if (bufferevent_write(clt->clt_srvbev, &h,
421 		    sizeof(struct fcgi_record_header)) == -1)
422 			return -1;
423 		return bufferevent_write_buffer(clt->clt_srvbev, evbuf);
424 	}
425 	return (0);
426 }
427 
428 int
429 fcgi_add_param(struct server_fcgi_param *p, const char *key,
430     const char *val, struct client *clt)
431 {
432 	struct fcgi_record_header	*h;
433 	int				 len = 0;
434 	int				 key_len = strlen(key);
435 	int				 val_len = strlen(val);
436 	uint8_t				*param;
437 
438 	len += key_len + val_len;
439 	len += key_len > 127 ? 4 : 1;
440 	len += val_len > 127 ? 4 : 1;
441 
442 	DPRINTF("%s: %s[%d] => %s[%d], total_len: %d", __func__, key, key_len,
443 	    val, val_len, p->total_len);
444 
445 	if (len > FCGI_CONTENT_SIZE)
446 		return (-1);
447 
448 	if (p->total_len + len > FCGI_CONTENT_SIZE) {
449 		if (bufferevent_write(clt->clt_srvbev, p->buf,
450 		    sizeof(struct fcgi_record_header) + p->total_len) == -1)
451 			return (-1);
452 		p->total_len = 0;
453 	}
454 
455 	h = (struct fcgi_record_header *)p->buf;
456 	param = p->buf + sizeof(*h) + p->total_len;
457 
458 	if (key_len > 127) {
459 		*param++ = ((key_len >> 24) & 0xff) | 0x80;
460 		*param++ = ((key_len >> 16) & 0xff);
461 		*param++ = ((key_len >> 8) & 0xff);
462 		*param++ = (key_len & 0xff);
463 	} else
464 		*param++ = key_len;
465 
466 	if (val_len > 127) {
467 		*param++ = ((val_len >> 24) & 0xff) | 0x80;
468 		*param++ = ((val_len >> 16) & 0xff);
469 		*param++ = ((val_len >> 8) & 0xff);
470 		*param++ = (val_len & 0xff);
471 	} else
472 		*param++ = val_len;
473 
474 	memcpy(param, key, key_len);
475 	param += key_len;
476 	memcpy(param, val, val_len);
477 
478 	p->total_len += len;
479 
480 	h->content_len = htons(p->total_len);
481 	return (0);
482 }
483 
484 void
485 server_fcgi_read(struct bufferevent *bev, void *arg)
486 {
487 	uint8_t				 buf[FCGI_RECORD_SIZE];
488 	struct client			*clt = (struct client *) arg;
489 	struct fcgi_record_header	*h;
490 	size_t				 len;
491 	char				*ptr;
492 
493 	do {
494 		len = bufferevent_read(bev, buf, clt->clt_fcgi.toread);
495 		if (evbuffer_add(clt->clt_srvevb, buf, len) == -1) {
496 			server_abort_http(clt, 500, "short write");
497 			return;
498 		}
499 		clt->clt_fcgi.toread -= len;
500 		DPRINTF("%s: len: %lu toread: %d state: %d type: %d",
501 		    __func__, len, clt->clt_fcgi.toread,
502 		    clt->clt_fcgi.state, clt->clt_fcgi.type);
503 
504 		if (clt->clt_fcgi.toread != 0)
505 			return;
506 
507 		switch (clt->clt_fcgi.state) {
508 		case FCGI_READ_HEADER:
509 			clt->clt_fcgi.state = FCGI_READ_CONTENT;
510 			h = (struct fcgi_record_header *)
511 			    EVBUFFER_DATA(clt->clt_srvevb);
512 			DPRINTF("%s: record header: version %d type %d id %d "
513 			    "content len %d padding %d", __func__,
514 			    h->version, h->type, ntohs(h->id),
515 			    ntohs(h->content_len), h->padding_len);
516 			clt->clt_fcgi.type = h->type;
517 			clt->clt_fcgi.toread = ntohs(h->content_len);
518 			clt->clt_fcgi.padding_len = h->padding_len;
519 			evbuffer_drain(clt->clt_srvevb,
520 			    EVBUFFER_LENGTH(clt->clt_srvevb));
521 			if (clt->clt_fcgi.toread != 0)
522 				break;
523 			else if (clt->clt_fcgi.type == FCGI_STDOUT &&
524 			    !clt->clt_chunk) {
525 				server_abort_http(clt, 500, "empty stdout");
526 				return;
527 			}
528 
529 			/* fallthrough if content_len == 0 */
530 		case FCGI_READ_CONTENT:
531 			switch (clt->clt_fcgi.type) {
532 			case FCGI_STDERR:
533 				if (EVBUFFER_LENGTH(clt->clt_srvevb) > 0 &&
534 				    (ptr = get_string(
535 				    EVBUFFER_DATA(clt->clt_srvevb),
536 				    EVBUFFER_LENGTH(clt->clt_srvevb)))
537 				    != NULL) {
538 					server_sendlog(clt->clt_srv_conf,
539 					    IMSG_LOG_ERROR, "%s", ptr);
540 					free(ptr);
541 				}
542 				break;
543 			case FCGI_STDOUT:
544 				++clt->clt_chunk;
545 				if (!clt->clt_fcgi.headersdone) {
546 					clt->clt_fcgi.headersdone =
547 					    server_fcgi_getheaders(clt);
548 					if (!EVBUFFER_LENGTH(clt->clt_srvevb))
549 						break;
550 				}
551 				/* FALLTHROUGH */
552 			case FCGI_END_REQUEST:
553 				if (clt->clt_fcgi.headersdone &&
554 				    !clt->clt_fcgi.headerssent) {
555 					if (server_fcgi_header(clt,
556 					    clt->clt_fcgi.status) == -1) {
557 						server_abort_http(clt, 500,
558 						    "malformed fcgi headers");
559 						return;
560 					}
561 				}
562 				if (server_fcgi_writechunk(clt) == -1) {
563 					server_abort_http(clt, 500,
564 					    "encoding error");
565 					return;
566 				}
567 				break;
568 			}
569 			evbuffer_drain(clt->clt_srvevb,
570 			    EVBUFFER_LENGTH(clt->clt_srvevb));
571 			if (!clt->clt_fcgi.padding_len) {
572 				clt->clt_fcgi.state = FCGI_READ_HEADER;
573 				clt->clt_fcgi.toread =
574 				    sizeof(struct fcgi_record_header);
575 			} else {
576 				clt->clt_fcgi.state = FCGI_READ_PADDING;
577 				clt->clt_fcgi.toread =
578 				    clt->clt_fcgi.padding_len;
579 			}
580 			break;
581 		case FCGI_READ_PADDING:
582 			evbuffer_drain(clt->clt_srvevb,
583 			    EVBUFFER_LENGTH(clt->clt_srvevb));
584 			clt->clt_fcgi.state = FCGI_READ_HEADER;
585 			clt->clt_fcgi.toread =
586 			    sizeof(struct fcgi_record_header);
587 			break;
588 		}
589 	} while (len > 0);
590 }
591 
592 int
593 server_fcgi_header(struct client *clt, unsigned int code)
594 {
595 	struct server_config	*srv_conf = clt->clt_srv_conf;
596 	struct http_descriptor	*desc = clt->clt_descreq;
597 	struct http_descriptor	*resp = clt->clt_descresp;
598 	const char		*error;
599 	char			 tmbuf[32];
600 	struct kv		*kv, *cl, key;
601 
602 	clt->clt_fcgi.headerssent = 1;
603 
604 	if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
605 		return (-1);
606 
607 	if (server_log_http(clt, code, 0) == -1)
608 		return (-1);
609 
610 	/* Add error codes */
611 	if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 ||
612 	    kv_set(&resp->http_pathquery, "%s", error) == -1)
613 		return (-1);
614 
615 	/* Add headers */
616 	if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
617 		return (-1);
618 
619 	if (clt->clt_fcgi.type == FCGI_END_REQUEST ||
620 	    EVBUFFER_LENGTH(clt->clt_srvevb) == 0) {
621 		/* Can't chunk encode an empty body. */
622 		clt->clt_fcgi.chunked = 0;
623 
624 		/* But then we need a Content-Length... */
625 		key.kv_key = "Content-Length";
626 		if ((kv = kv_find(&resp->http_headers, &key)) == NULL) {
627 			if (kv_add(&resp->http_headers,
628 			    "Content-Length", "0") == NULL)
629 				return (-1);
630 		}
631 	}
632 
633 	/* Set chunked encoding */
634 	if (clt->clt_fcgi.chunked) {
635 		/* XXX Should we keep and handle Content-Length instead? */
636 		key.kv_key = "Content-Length";
637 		if ((kv = kv_find(&resp->http_headers, &key)) != NULL)
638 			kv_delete(&resp->http_headers, kv);
639 
640 		/*
641 		 * XXX What if the FastCGI added some kind of Transfer-Encoding?
642 		 * XXX like gzip, deflate or even "chunked"?
643 		 */
644 		if (kv_add(&resp->http_headers,
645 		    "Transfer-Encoding", "chunked") == NULL)
646 			return (-1);
647 	}
648 
649 	/* Is it a persistent connection? */
650 	if (clt->clt_persist) {
651 		if (kv_add(&resp->http_headers,
652 		    "Connection", "keep-alive") == NULL)
653 			return (-1);
654 	} else if (kv_add(&resp->http_headers, "Connection", "close") == NULL)
655 		return (-1);
656 
657 	/* HSTS header */
658 	if (srv_conf->flags & SRVFLAG_SERVER_HSTS &&
659 	    srv_conf->flags & SRVFLAG_TLS) {
660 		if ((cl =
661 		    kv_add(&resp->http_headers, "Strict-Transport-Security",
662 		    NULL)) == NULL ||
663 		    kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age,
664 		    srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ?
665 		    "; includeSubDomains" : "",
666 		    srv_conf->hsts_flags & HSTSFLAG_PRELOAD ?
667 		    "; preload" : "") == -1)
668 			return (-1);
669 	}
670 
671 	/* Date header is mandatory and should be added as late as possible */
672 	key.kv_key = "Date";
673 	if (kv_find(&resp->http_headers, &key) == NULL &&
674 	    (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
675 	    kv_add(&resp->http_headers, "Date", tmbuf) == NULL))
676 		return (-1);
677 
678 	if (server_writeresponse_http(clt) == -1 ||
679 	    server_bufferevent_print(clt, "\r\n") == -1 ||
680 	    server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
681 	    server_bufferevent_print(clt, "\r\n") == -1)
682 		return (-1);
683 
684 	return (0);
685 }
686 
687 int
688 server_fcgi_writeheader(struct client *clt, struct kv *hdr, void *arg)
689 {
690 	struct server_fcgi_param	*param = arg;
691 	char				*val, *name, *p;
692 	const char			*key;
693 	int				 ret;
694 
695 	if (hdr->kv_flags & KV_FLAG_INVALID)
696 		return (0);
697 
698 	/* The key might have been updated in the parent */
699 	if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
700 		key = hdr->kv_parent->kv_key;
701 	else
702 		key = hdr->kv_key;
703 
704 	val = hdr->kv_value;
705 
706 	if (strcasecmp(key, "Content-Length") == 0 ||
707 	    strcasecmp(key, "Content-Type") == 0) {
708 		if ((name = strdup(key)) == NULL)
709 			return (-1);
710 	} else {
711 		if (asprintf(&name, "HTTP_%s", key) == -1)
712 			return (-1);
713 	}
714 
715 	/*
716 	 * RFC 7230 defines a header field-name as a "token" and a "token"
717 	 * is defined as one or more characters for which isalpha or
718 	 * isdigit is true plus a list of additional characters.
719 	 * According to RFC 3875 a CGI environment variable is created
720 	 * by converting all letters to upper case and replacing '-'
721 	 * with '_'.
722 	 */
723 	for (p = name; *p != '\0'; p++) {
724 		if (isalpha((unsigned char)*p))
725 			*p = toupper((unsigned char)*p);
726 		else if (!(*p == '!' || *p == '#' || *p == '$' || *p == '%' ||
727 		    *p == '&' || *p == '\'' || *p == '*' || *p == '+' ||
728 		    *p == '.' || *p == '^' || *p == '_' || *p == '`' ||
729 		    *p == '|' || *p == '~' || isdigit((unsigned char)*p)))
730 			*p = '_';
731 	}
732 
733 	ret = fcgi_add_param(param, name, val, clt);
734 	free(name);
735 
736 	return (ret);
737 }
738 
739 int
740 server_fcgi_writechunk(struct client *clt)
741 {
742 	struct evbuffer *evb = clt->clt_srvevb;
743 	size_t		 len;
744 
745 	if (clt->clt_fcgi.type == FCGI_END_REQUEST) {
746 		len = 0;
747 	} else
748 		len = EVBUFFER_LENGTH(evb);
749 
750 	if (clt->clt_fcgi.chunked) {
751 		/* If len is 0, make sure to write the end marker only once */
752 		if (len == 0 && clt->clt_fcgi.end++)
753 			return (0);
754 		if (server_bufferevent_printf(clt, "%zx\r\n", len) == -1 ||
755 		    server_bufferevent_write_chunk(clt, evb, len) == -1 ||
756 		    server_bufferevent_print(clt, "\r\n") == -1)
757 			return (-1);
758 	} else if (len)
759 		return (server_bufferevent_write_buffer(clt, evb));
760 
761 	return (0);
762 }
763 
764 int
765 server_fcgi_getheaders(struct client *clt)
766 {
767 	struct http_descriptor	*resp = clt->clt_descresp;
768 	struct evbuffer		*evb = clt->clt_srvevb;
769 	int			 code, ret;
770 	char			*line, *key, *value;
771 	const char		*errstr;
772 
773 	while ((line = evbuffer_getline(evb)) != NULL && *line != '\0') {
774 		key = line;
775 
776 		if ((value = strchr(key, ':')) == NULL)
777 			break;
778 
779 		*value++ = '\0';
780 		value += strspn(value, " \t");
781 
782 		DPRINTF("%s: %s: %s", __func__, key, value);
783 
784 		if (strcasecmp("Status", key) == 0) {
785 			value[strcspn(value, " \t")] = '\0';
786 			code = (int)strtonum(value, 100, 600, &errstr);
787 			if (errstr != NULL || server_httperror_byid(
788 			    code) == NULL)
789 				code = 200;
790 			clt->clt_fcgi.status = code;
791 		} else {
792 			(void)kv_add(&resp->http_headers, key, value);
793 		}
794 		free(line);
795 	}
796 
797 	ret = (line != NULL && *line == '\0');
798 
799 	free(line);
800 	return ret;
801 }
802