xref: /openbsd/usr.sbin/httpd/server_http.c (revision b0c10c99)
1 /*	$OpenBSD: server_http.c,v 1.155 2024/12/22 13:51:42 florian Exp $	*/
2 
3 /*
4  * Copyright (c) 2020 Matthias Pressfreund <mpfr@fn.de>
5  * Copyright (c) 2006 - 2018 Reyk Floeter <reyk@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/tree.h>
24 #include <sys/stat.h>
25 
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <fnmatch.h>
35 #include <stdio.h>
36 #include <time.h>
37 #include <resolv.h>
38 #include <event.h>
39 #include <ctype.h>
40 #include <vis.h>
41 #include <fcntl.h>
42 
43 #include "httpd.h"
44 #include "http.h"
45 #include "patterns.h"
46 
47 static int	 server_httpmethod_cmp(const void *, const void *);
48 static int	 server_httperror_cmp(const void *, const void *);
49 void		 server_httpdesc_free(struct http_descriptor *);
50 int		 server_http_authenticate(struct server_config *,
51 		    struct client *);
52 static int	 http_version_num(char *);
53 char		*server_expand_http(struct client *, const char *,
54 		    char *, size_t);
55 char		*replace_var(char *, const char *, const char *);
56 char		*read_errdoc(const char *, const char *);
57 
58 static struct http_method	 http_methods[] = HTTP_METHODS;
59 static struct http_error	 http_errors[] = HTTP_ERRORS;
60 
61 void
server_http(void)62 server_http(void)
63 {
64 	DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid());
65 
66 	/* Sort the HTTP lookup arrays */
67 	qsort(http_methods, sizeof(http_methods) /
68 	    sizeof(http_methods[0]) - 1,
69 	    sizeof(http_methods[0]), server_httpmethod_cmp);
70 	qsort(http_errors, sizeof(http_errors) /
71 	    sizeof(http_errors[0]) - 1,
72 	    sizeof(http_errors[0]), server_httperror_cmp);
73 }
74 
75 void
server_http_init(struct server * srv)76 server_http_init(struct server *srv)
77 {
78 	/* nothing */
79 }
80 
81 int
server_httpdesc_init(struct client * clt)82 server_httpdesc_init(struct client *clt)
83 {
84 	struct http_descriptor	*desc;
85 
86 	if ((desc = calloc(1, sizeof(*desc))) == NULL)
87 		return (-1);
88 	RB_INIT(&desc->http_headers);
89 	clt->clt_descreq = desc;
90 
91 	if ((desc = calloc(1, sizeof(*desc))) == NULL) {
92 		/* req will be cleaned up later */
93 		return (-1);
94 	}
95 	RB_INIT(&desc->http_headers);
96 	clt->clt_descresp = desc;
97 
98 	return (0);
99 }
100 
101 void
server_httpdesc_free(struct http_descriptor * desc)102 server_httpdesc_free(struct http_descriptor *desc)
103 {
104 	if (desc == NULL)
105 		return;
106 
107 	free(desc->http_path);
108 	desc->http_path = NULL;
109 	free(desc->http_path_orig);
110 	desc->http_path_orig = NULL;
111 	free(desc->http_path_alias);
112 	desc->http_path_alias = NULL;
113 	free(desc->http_query);
114 	desc->http_query = NULL;
115 	free(desc->http_query_alias);
116 	desc->http_query_alias = NULL;
117 	free(desc->http_version);
118 	desc->http_version = NULL;
119 	free(desc->http_host);
120 	desc->http_host = NULL;
121 
122 	kv_purge(&desc->http_headers);
123 	desc->http_lastheader = NULL;
124 	desc->http_method = 0;
125 	desc->http_chunked = 0;
126 }
127 
128 int
server_http_authenticate(struct server_config * srv_conf,struct client * clt)129 server_http_authenticate(struct server_config *srv_conf, struct client *clt)
130 {
131 	char			 decoded[1024];
132 	FILE			*fp = NULL;
133 	struct http_descriptor	*desc = clt->clt_descreq;
134 	const struct auth	*auth = srv_conf->auth;
135 	struct kv		*ba, key;
136 	size_t			 linesize = 0;
137 	ssize_t			 linelen;
138 	int			 ret = -1;
139 	char			*line = NULL, *user = NULL, *pass = NULL;
140 	char			*clt_user = NULL, *clt_pass = NULL;
141 
142 	memset(decoded, 0, sizeof(decoded));
143 	key.kv_key = "Authorization";
144 
145 	if ((ba = kv_find(&desc->http_headers, &key)) == NULL ||
146 	    ba->kv_value == NULL)
147 		goto done;
148 
149 	if (strncmp(ba->kv_value, "Basic ", strlen("Basic ")) != 0)
150 		goto done;
151 
152 	if (b64_pton(strchr(ba->kv_value, ' ') + 1, (uint8_t *)decoded,
153 	    sizeof(decoded)) <= 0)
154 		goto done;
155 
156 	if ((clt_pass = strchr(decoded, ':')) == NULL)
157 		goto done;
158 
159 	clt_user = decoded;
160 	*clt_pass++ = '\0';
161 	if ((clt->clt_remote_user = strdup(clt_user)) == NULL)
162 		goto done;
163 
164 	if ((fp = fopen(auth->auth_htpasswd, "r")) == NULL)
165 		goto done;
166 
167 	while ((linelen = getline(&line, &linesize, fp)) != -1) {
168 		if (line[linelen - 1] == '\n')
169 			line[linelen - 1] = '\0';
170 		user = line;
171 		pass = strchr(line, ':');
172 
173 		if (pass == NULL) {
174 			explicit_bzero(line, linelen);
175 			continue;
176 		}
177 
178 		*pass++ = '\0';
179 
180 		if (strcmp(clt_user, user) != 0) {
181 			explicit_bzero(line, linelen);
182 			continue;
183 		}
184 
185 		if (crypt_checkpass(clt_pass, pass) == 0) {
186 			explicit_bzero(line, linelen);
187 			ret = 0;
188 			break;
189 		}
190 	}
191 done:
192 	free(line);
193 	if (fp != NULL)
194 		fclose(fp);
195 
196 	if (ba != NULL && ba->kv_value != NULL) {
197 		explicit_bzero(ba->kv_value, strlen(ba->kv_value));
198 		explicit_bzero(decoded, sizeof(decoded));
199 	}
200 
201 	return (ret);
202 }
203 
204 static int
http_version_num(char * version)205 http_version_num(char *version)
206 {
207 	if (strlen(version) != 8 || strncmp(version, "HTTP/", 5) != 0
208 	    || !isdigit((unsigned char)version[5]) || version[6] != '.'
209 	    || !isdigit((unsigned char)version[7]))
210 		return (-1);
211 	if (version[5] == '0' && version[7] == '9')
212 		return (9);
213 	if (version[5] == '1') {
214 		if (version[7] == '0')
215 			return (10);
216 		else
217 			/* any other version 1.x gets downgraded to 1.1 */
218 			return (11);
219 	}
220 	return (0);
221 }
222 
223 void
server_read_http(struct bufferevent * bev,void * arg)224 server_read_http(struct bufferevent *bev, void *arg)
225 {
226 	struct client		*clt = arg;
227 	struct http_descriptor	*desc = clt->clt_descreq;
228 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
229 	char			*line = NULL, *key, *value;
230 	const char		*errstr;
231 	char			*http_version, *query;
232 	size_t			 size, linelen;
233 	int			 version;
234 	struct kv		*hdr = NULL;
235 
236 	getmonotime(&clt->clt_tv_last);
237 
238 	size = EVBUFFER_LENGTH(src);
239 	DPRINTF("%s: session %d: size %lu, to read %lld",
240 	    __func__, clt->clt_id, size, clt->clt_toread);
241 	if (!size) {
242 		clt->clt_toread = TOREAD_HTTP_HEADER;
243 		goto done;
244 	}
245 
246 	while (!clt->clt_headersdone) {
247 		if (!clt->clt_line) {
248 			/* Peek into the buffer to see if it looks like HTTP */
249 			key = EVBUFFER_DATA(src);
250 			if (!isalpha((unsigned char)*key)) {
251 				server_abort_http(clt, 400,
252 				    "invalid request line");
253 				goto abort;
254 			}
255 		}
256 
257 		if ((line = evbuffer_readln(src,
258 		    &linelen, EVBUFFER_EOL_CRLF_STRICT)) == NULL) {
259 			/* No newline found after too many bytes */
260 			if (size > SERVER_MAXHEADERLENGTH) {
261 				server_abort_http(clt, 413,
262 				    "request line too long");
263 				goto abort;
264 			}
265 			break;
266 		}
267 
268 		/*
269 		 * An empty line indicates the end of the request.
270 		 * libevent already stripped the \r\n for us.
271 		 */
272 		if (!linelen) {
273 			clt->clt_headersdone = 1;
274 			free(line);
275 			break;
276 		}
277 		key = line;
278 
279 		/* Limit the total header length minus \r\n */
280 		clt->clt_headerlen += linelen;
281 		if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) {
282 			server_abort_http(clt, 413, "request too large");
283 			goto abort;
284 		}
285 
286 		/*
287 		 * The first line is the GET/POST/PUT/... request,
288 		 * subsequent lines are HTTP headers.
289 		 */
290 		if (++clt->clt_line == 1)
291 			value = strchr(key, ' ');
292 		else if (*key == ' ' || *key == '\t')
293 			/* Multiline headers wrap with a space or tab */
294 			value = NULL;
295 		else {
296 			/* Not a multiline header, should have a : */
297 			value = strchr(key, ':');
298 			if (value == NULL) {
299 				server_abort_http(clt, 400, "malformed");
300 				goto abort;
301 			}
302 		}
303 		if (value == NULL) {
304 			if (clt->clt_line == 1) {
305 				server_abort_http(clt, 400, "malformed");
306 				goto abort;
307 			}
308 
309 			/* Append line to the last header, if present */
310 			if (kv_extend(&desc->http_headers,
311 			    desc->http_lastheader, line) == NULL)
312 				goto fail;
313 
314 			free(line);
315 			continue;
316 		}
317 		if (*value == ':') {
318 			*value++ = '\0';
319 			value += strspn(value, " \t\r\n");
320 		} else {
321 			*value++ = '\0';
322 		}
323 
324 		DPRINTF("%s: session %d: header '%s: %s'", __func__,
325 		    clt->clt_id, key, value);
326 
327 		/*
328 		 * Identify and handle specific HTTP request methods
329 		 */
330 		if (clt->clt_line == 1) {
331 			if ((desc->http_method = server_httpmethod_byname(key))
332 			    == HTTP_METHOD_NONE) {
333 				server_abort_http(clt, 400, "malformed");
334 				goto abort;
335 			}
336 
337 			/*
338 			 * Decode request path and query
339 			 */
340 			desc->http_path = strdup(value);
341 			if (desc->http_path == NULL)
342 				goto fail;
343 
344 			http_version = strchr(desc->http_path, ' ');
345 			if (http_version == NULL) {
346 				server_abort_http(clt, 400, "malformed");
347 				goto abort;
348 			}
349 
350 			*http_version++ = '\0';
351 
352 			/*
353 			 * We have to allocate the strings because they could
354 			 * be changed independently by the filters later.
355 			 * Allow HTTP version 0.9 to 1.1.
356 			 * Downgrade http version > 1.1 <= 1.9 to version 1.1.
357 			 * Return HTTP Version Not Supported for anything else.
358 			 */
359 
360 			version = http_version_num(http_version);
361 
362 			if (version == -1) {
363 				server_abort_http(clt, 400, "malformed");
364 				goto abort;
365 			} else if (version == 0) {
366 				server_abort_http(clt, 505, "bad http version");
367 				goto abort;
368 			} else if (version == 11) {
369 				if ((desc->http_version =
370 				    strdup("HTTP/1.1")) == NULL)
371 					goto fail;
372 			} else {
373 				if ((desc->http_version =
374 				    strdup(http_version)) == NULL)
375 					goto fail;
376 			}
377 
378 			query = strchr(desc->http_path, '?');
379 			if (query != NULL) {
380 				*query++ = '\0';
381 
382 				if ((desc->http_query = strdup(query)) == NULL)
383 					goto fail;
384 			}
385 
386 		} else if (desc->http_method != HTTP_METHOD_NONE &&
387 		    strcasecmp("Content-Length", key) == 0) {
388 			if (desc->http_method == HTTP_METHOD_TRACE ||
389 			    desc->http_method == HTTP_METHOD_CONNECT) {
390 				/*
391 				 * These method should not have a body
392 				 * and thus no Content-Length header.
393 				 */
394 				server_abort_http(clt, 400, "malformed");
395 				goto abort;
396 			}
397 
398 			/*
399 			 * Need to read data from the client after the
400 			 * HTTP header.
401 			 * XXX What about non-standard clients not using
402 			 * the carriage return? And some browsers seem to
403 			 * include the line length in the content-length.
404 			 */
405 			clt->clt_toread = strtonum(value, 0, LLONG_MAX,
406 			    &errstr);
407 			if (errstr) {
408 				server_abort_http(clt, 500, errstr);
409 				goto abort;
410 			}
411 		}
412 
413 		if (strcasecmp("Transfer-Encoding", key) == 0 &&
414 		    strcasecmp("chunked", value) == 0)
415 			desc->http_chunked = 1;
416 
417 		if (clt->clt_line != 1) {
418 			if ((hdr = kv_add(&desc->http_headers, key,
419 			    value)) == NULL)
420 				goto fail;
421 
422 			desc->http_lastheader = hdr;
423 		}
424 
425 		free(line);
426 	}
427 	if (clt->clt_headersdone) {
428 		if (desc->http_method == HTTP_METHOD_NONE) {
429 			server_abort_http(clt, 406, "no method");
430 			return;
431 		}
432 
433 		switch (desc->http_method) {
434 		case HTTP_METHOD_CONNECT:
435 			/* Data stream */
436 			clt->clt_toread = TOREAD_UNLIMITED;
437 			bev->readcb = server_read;
438 			break;
439 		case HTTP_METHOD_GET:
440 		case HTTP_METHOD_HEAD:
441 		/* WebDAV methods */
442 		case HTTP_METHOD_COPY:
443 		case HTTP_METHOD_MOVE:
444 			clt->clt_toread = 0;
445 			break;
446 		case HTTP_METHOD_DELETE:
447 		case HTTP_METHOD_OPTIONS:
448 		case HTTP_METHOD_POST:
449 		case HTTP_METHOD_PUT:
450 		case HTTP_METHOD_RESPONSE:
451 		/* WebDAV methods */
452 		case HTTP_METHOD_PROPFIND:
453 		case HTTP_METHOD_PROPPATCH:
454 		case HTTP_METHOD_MKCOL:
455 		case HTTP_METHOD_LOCK:
456 		case HTTP_METHOD_UNLOCK:
457 		case HTTP_METHOD_VERSION_CONTROL:
458 		case HTTP_METHOD_REPORT:
459 		case HTTP_METHOD_CHECKOUT:
460 		case HTTP_METHOD_CHECKIN:
461 		case HTTP_METHOD_UNCHECKOUT:
462 		case HTTP_METHOD_MKWORKSPACE:
463 		case HTTP_METHOD_UPDATE:
464 		case HTTP_METHOD_LABEL:
465 		case HTTP_METHOD_MERGE:
466 		case HTTP_METHOD_BASELINE_CONTROL:
467 		case HTTP_METHOD_MKACTIVITY:
468 		case HTTP_METHOD_ORDERPATCH:
469 		case HTTP_METHOD_ACL:
470 		case HTTP_METHOD_MKREDIRECTREF:
471 		case HTTP_METHOD_UPDATEREDIRECTREF:
472 		case HTTP_METHOD_SEARCH:
473 		case HTTP_METHOD_PATCH:
474 			/* HTTP request payload */
475 			if (clt->clt_toread > 0)
476 				bev->readcb = server_read_httpcontent;
477 			if (clt->clt_toread < 0 && !desc->http_chunked)
478 				/* 7. of RFC 9112 Section 6.3 */
479 				clt->clt_toread = 0;
480 			break;
481 		default:
482 			server_abort_http(clt, 405, "method not allowed");
483 			return;
484 		}
485 		if (desc->http_chunked) {
486 			/* Chunked transfer encoding */
487 			clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH;
488 			bev->readcb = server_read_httpchunks;
489 		}
490 
491  done:
492 		if (clt->clt_toread != 0)
493 			bufferevent_disable(bev, EV_READ);
494 		server_response(httpd_env, clt);
495 		return;
496 	}
497 	if (clt->clt_done) {
498 		server_close(clt, "done");
499 		return;
500 	}
501 	if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http)
502 		bev->readcb(bev, arg);
503 	bufferevent_enable(bev, EV_READ);
504 	return;
505  fail:
506 	server_abort_http(clt, 500, strerror(errno));
507  abort:
508 	free(line);
509 }
510 
511 void
server_read_httpcontent(struct bufferevent * bev,void * arg)512 server_read_httpcontent(struct bufferevent *bev, void *arg)
513 {
514 	struct client		*clt = arg;
515 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
516 	size_t			 size;
517 
518 	getmonotime(&clt->clt_tv_last);
519 
520 	size = EVBUFFER_LENGTH(src);
521 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
522 	    clt->clt_id, size, clt->clt_toread);
523 	if (!size)
524 		return;
525 
526 	if (clt->clt_toread > 0) {
527 		/* Read content data */
528 		if ((off_t)size > clt->clt_toread) {
529 			size = clt->clt_toread;
530 			if (fcgi_add_stdin(clt, src) == -1)
531 				goto fail;
532 			clt->clt_toread = 0;
533 		} else {
534 			if (fcgi_add_stdin(clt, src) == -1)
535 				goto fail;
536 			clt->clt_toread -= size;
537 		}
538 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
539 		    size, clt->clt_toread);
540 	}
541 	if (clt->clt_toread == 0) {
542 		fcgi_add_stdin(clt, NULL);
543 		clt->clt_toread = TOREAD_HTTP_HEADER;
544 		bufferevent_disable(bev, EV_READ);
545 		bev->readcb = server_read_http;
546 		return;
547 	}
548 	if (clt->clt_done)
549 		goto done;
550 	if (bev->readcb != server_read_httpcontent)
551 		bev->readcb(bev, arg);
552 
553 	return;
554  done:
555 	return;
556  fail:
557 	server_close(clt, strerror(errno));
558 }
559 
560 void
server_read_httpchunks(struct bufferevent * bev,void * arg)561 server_read_httpchunks(struct bufferevent *bev, void *arg)
562 {
563 	struct client		*clt = arg;
564 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
565 	char			*line;
566 	long long		 llval;
567 	size_t			 size;
568 
569 	getmonotime(&clt->clt_tv_last);
570 
571 	size = EVBUFFER_LENGTH(src);
572 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
573 	    clt->clt_id, size, clt->clt_toread);
574 	if (!size)
575 		return;
576 
577 	if (clt->clt_toread > 0) {
578 		/* Read chunk data */
579 		if ((off_t)size > clt->clt_toread) {
580 			size = clt->clt_toread;
581 			if (server_bufferevent_write_chunk(clt, src, size)
582 			    == -1)
583 				goto fail;
584 			clt->clt_toread = 0;
585 		} else {
586 			if (server_bufferevent_write_buffer(clt, src) == -1)
587 				goto fail;
588 			clt->clt_toread -= size;
589 		}
590 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
591 		    size, clt->clt_toread);
592 	}
593 	switch (clt->clt_toread) {
594 	case TOREAD_HTTP_CHUNK_LENGTH:
595 		line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT);
596 		if (line == NULL) {
597 			/* Ignore empty line, continue */
598 			bufferevent_enable(bev, EV_READ);
599 			return;
600 		}
601 		if (strlen(line) == 0) {
602 			free(line);
603 			goto next;
604 		}
605 
606 		/*
607 		 * Read prepended chunk size in hex, ignore the trailer.
608 		 * The returned signed value must not be negative.
609 		 */
610 		if (sscanf(line, "%llx", &llval) != 1 || llval < 0) {
611 			free(line);
612 			server_close(clt, "invalid chunk size");
613 			return;
614 		}
615 
616 		if (server_bufferevent_print(clt, line) == -1 ||
617 		    server_bufferevent_print(clt, "\r\n") == -1) {
618 			free(line);
619 			goto fail;
620 		}
621 		free(line);
622 
623 		if ((clt->clt_toread = llval) == 0) {
624 			DPRINTF("%s: last chunk", __func__);
625 			clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER;
626 		}
627 		break;
628 	case TOREAD_HTTP_CHUNK_TRAILER:
629 		/* Last chunk is 0 bytes followed by trailer and empty line */
630 		line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT);
631 		if (line == NULL) {
632 			/* Ignore empty line, continue */
633 			bufferevent_enable(bev, EV_READ);
634 			return;
635 		}
636 		if (server_bufferevent_print(clt, line) == -1 ||
637 		    server_bufferevent_print(clt, "\r\n") == -1) {
638 			free(line);
639 			goto fail;
640 		}
641 		if (strlen(line) == 0) {
642 			/* Switch to HTTP header mode */
643 			clt->clt_toread = TOREAD_HTTP_HEADER;
644 			bev->readcb = server_read_http;
645 		}
646 		free(line);
647 		break;
648 	case 0:
649 		/* Chunk is terminated by an empty newline */
650 		line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT);
651 		free(line);
652 		if (server_bufferevent_print(clt, "\r\n") == -1)
653 			goto fail;
654 		clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH;
655 		break;
656 	}
657 
658  next:
659 	if (clt->clt_done)
660 		goto done;
661 	if (EVBUFFER_LENGTH(src))
662 		bev->readcb(bev, arg);
663 	bufferevent_enable(bev, EV_READ);
664 	return;
665 
666  done:
667 	server_close(clt, "last http chunk read (done)");
668 	return;
669  fail:
670 	server_close(clt, strerror(errno));
671 }
672 
673 void
server_read_httprange(struct bufferevent * bev,void * arg)674 server_read_httprange(struct bufferevent *bev, void *arg)
675 {
676 	struct client		*clt = arg;
677 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
678 	size_t			 size;
679 	struct media_type	*media;
680 	struct range_data	*r = &clt->clt_ranges;
681 	struct range		*range;
682 
683 	getmonotime(&clt->clt_tv_last);
684 
685 	if (r->range_toread > 0) {
686 		size = EVBUFFER_LENGTH(src);
687 		if (!size)
688 			return;
689 
690 		/* Read chunk data */
691 		if ((off_t)size > r->range_toread) {
692 			size = r->range_toread;
693 			if (server_bufferevent_write_chunk(clt, src, size)
694 			    == -1)
695 				goto fail;
696 			r->range_toread = 0;
697 		} else {
698 			if (server_bufferevent_write_buffer(clt, src) == -1)
699 				goto fail;
700 			r->range_toread -= size;
701 		}
702 		if (r->range_toread < 1)
703 			r->range_toread = TOREAD_HTTP_RANGE;
704 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
705 		    size, r->range_toread);
706 	}
707 
708 	switch (r->range_toread) {
709 	case TOREAD_HTTP_RANGE:
710 		if (r->range_index >= r->range_count) {
711 			if (r->range_count > 1) {
712 				/* Add end marker */
713 				if (server_bufferevent_printf(clt,
714 				    "\r\n--%llu--\r\n",
715 				    clt->clt_boundary) == -1)
716 					goto fail;
717 			}
718 			r->range_toread = TOREAD_HTTP_NONE;
719 			break;
720 		}
721 
722 		range = &r->range[r->range_index];
723 
724 		if (r->range_count > 1) {
725 			media = r->range_media;
726 			if (server_bufferevent_printf(clt,
727 			    "\r\n--%llu\r\n"
728 			    "Content-Type: %s/%s\r\n"
729 			    "Content-Range: bytes %lld-%lld/%zu\r\n\r\n",
730 			    clt->clt_boundary,
731 			    media->media_type, media->media_subtype,
732 			    range->start, range->end, r->range_total) == -1)
733 				goto fail;
734 		}
735 		r->range_toread = range->end - range->start + 1;
736 
737 		if (lseek(clt->clt_fd, range->start, SEEK_SET) == -1)
738 			goto fail;
739 
740 		/* Throw away bytes that are already in the input buffer */
741 		evbuffer_drain(src, EVBUFFER_LENGTH(src));
742 
743 		/* Increment for the next part */
744 		r->range_index++;
745 		break;
746 	case TOREAD_HTTP_NONE:
747 		goto done;
748 	case 0:
749 		break;
750 	}
751 
752 	if (clt->clt_done)
753 		goto done;
754 
755 	if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(clt->clt_bev)) > (size_t)
756 	    SERVER_MAX_PREFETCH * clt->clt_sndbufsiz) {
757 		bufferevent_disable(clt->clt_srvbev, EV_READ);
758 		clt->clt_srvbev_throttled = 1;
759 	}
760 
761 	return;
762  done:
763 	(*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg);
764 	return;
765  fail:
766 	server_close(clt, strerror(errno));
767 }
768 
769 void
server_reset_http(struct client * clt)770 server_reset_http(struct client *clt)
771 {
772 	struct server		*srv = clt->clt_srv;
773 
774 	server_log(clt, NULL);
775 
776 	server_httpdesc_free(clt->clt_descreq);
777 	server_httpdesc_free(clt->clt_descresp);
778 	clt->clt_headerlen = 0;
779 	clt->clt_headersdone = 0;
780 	clt->clt_done = 0;
781 	clt->clt_line = 0;
782 	clt->clt_chunk = 0;
783 	free(clt->clt_remote_user);
784 	clt->clt_remote_user = NULL;
785 	clt->clt_bev->readcb = server_read_http;
786 	clt->clt_srv_conf = &srv->srv_conf;
787 	str_match_free(&clt->clt_srv_match);
788 }
789 
790 ssize_t
server_http_time(time_t t,char * tmbuf,size_t len)791 server_http_time(time_t t, char *tmbuf, size_t len)
792 {
793 	struct tm		 tm;
794 
795 	/* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
796 	if (t == -1 || gmtime_r(&t, &tm) == NULL)
797 		return (-1);
798 	else
799 		return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
800 }
801 
802 const char *
server_http_host(struct sockaddr_storage * ss,char * buf,size_t len)803 server_http_host(struct sockaddr_storage *ss, char *buf, size_t len)
804 {
805 	char		hbuf[HOST_NAME_MAX+1];
806 	in_port_t	port;
807 
808 	if (print_host(ss, buf, len) == NULL)
809 		return (NULL);
810 
811 	port = ntohs(server_socket_getport(ss));
812 	if (port == HTTP_PORT)
813 		return (buf);
814 
815 	switch (ss->ss_family) {
816 	case AF_INET:
817 		if ((size_t)snprintf(hbuf, sizeof(hbuf),
818 		    "%s:%u", buf, port) >= sizeof(hbuf))
819 			return (NULL);
820 		break;
821 	case AF_INET6:
822 		if ((size_t)snprintf(hbuf, sizeof(hbuf),
823 		    "[%s]:%u", buf, port) >= sizeof(hbuf))
824 			return (NULL);
825 		break;
826 	}
827 
828 	if (strlcpy(buf, hbuf, len) >= len)
829 		return (NULL);
830 
831 	return (buf);
832 }
833 
834 char *
server_http_parsehost(char * host,char * buf,size_t len,int * portval)835 server_http_parsehost(char *host, char *buf, size_t len, int *portval)
836 {
837 	char		*start, *end, *port;
838 	const char	*errstr = NULL;
839 
840 	if (strlcpy(buf, host, len) >= len) {
841 		log_debug("%s: host name too long", __func__);
842 		return (NULL);
843 	}
844 
845 	start = buf;
846 	end = port = NULL;
847 
848 	if (*start == '[' && (end = strchr(start, ']')) != NULL) {
849 		/* Address enclosed in [] with port, eg. [2001:db8::1]:80 */
850 		start++;
851 		*end++ = '\0';
852 		if ((port = strchr(end, ':')) == NULL || *port == '\0')
853 			port = NULL;
854 		else
855 			port++;
856 		memmove(buf, start, strlen(start) + 1);
857 	} else if ((end = strchr(start, ':')) != NULL) {
858 		/* Name or address with port, eg. www.example.com:80 */
859 		*end++ = '\0';
860 		port = end;
861 	} else {
862 		/* Name or address with default port, eg. www.example.com */
863 		port = NULL;
864 	}
865 
866 	if (port != NULL) {
867 		/* Save the requested port */
868 		*portval = strtonum(port, 0, 0xffff, &errstr);
869 		if (errstr != NULL) {
870 			log_debug("%s: invalid port: %s", __func__,
871 			    strerror(errno));
872 			return (NULL);
873 		}
874 		*portval = htons(*portval);
875 	} else {
876 		/* Port not given, indicate the default port */
877 		*portval = -1;
878 	}
879 
880 	return (start);
881 }
882 
883 void
server_abort_http(struct client * clt,unsigned int code,const char * msg)884 server_abort_http(struct client *clt, unsigned int code, const char *msg)
885 {
886 	struct server_config	*srv_conf = clt->clt_srv_conf;
887 	struct bufferevent	*bev = clt->clt_bev;
888 	struct http_descriptor	*desc = clt->clt_descreq;
889 	const char		*httperr = NULL, *style;
890 	char			*httpmsg, *body = NULL, *extraheader = NULL;
891 	char			 tmbuf[32], hbuf[128], *hstsheader = NULL;
892 	char			*clenheader = NULL;
893 	char			 buf[IBUF_READ_SIZE];
894 	char			*escapedmsg = NULL;
895 	char			 cstr[5];
896 	ssize_t			 bodylen;
897 
898 	if (code == 0) {
899 		server_close(clt, "dropped");
900 		return;
901 	}
902 
903 	if ((httperr = server_httperror_byid(code)) == NULL)
904 		httperr = "Unknown Error";
905 
906 	if (bev == NULL)
907 		goto done;
908 
909 	if (server_log_http(clt, code, 0) == -1)
910 		goto done;
911 
912 	/* Some system information */
913 	if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL)
914 		goto done;
915 
916 	if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0)
917 		goto done;
918 
919 	/* Do not send details of the Internal Server Error */
920 	switch (code) {
921 	case 301:
922 	case 302:
923 	case 303:
924 	case 307:
925 	case 308:
926 		if (msg == NULL)
927 			break;
928 		memset(buf, 0, sizeof(buf));
929 		if (server_expand_http(clt, msg, buf, sizeof(buf)) == NULL)
930 			goto done;
931 		if (asprintf(&extraheader, "Location: %s\r\n", buf) == -1) {
932 			code = 500;
933 			extraheader = NULL;
934 		}
935 		msg = buf;
936 		break;
937 	case 401:
938 		if (msg == NULL)
939 			break;
940 		if (stravis(&escapedmsg, msg, VIS_DQ) == -1) {
941 			code = 500;
942 			extraheader = NULL;
943 		} else if (asprintf(&extraheader,
944 		    "WWW-Authenticate: Basic realm=\"%s\"\r\n", escapedmsg)
945 		    == -1) {
946 			code = 500;
947 			extraheader = NULL;
948 		}
949 		break;
950 	case 416:
951 		if (msg == NULL)
952 			break;
953 		if (asprintf(&extraheader,
954 		    "Content-Range: %s\r\n", msg) == -1) {
955 			code = 500;
956 			extraheader = NULL;
957 		}
958 		break;
959 	default:
960 		/*
961 		 * Do not send details of the error.  Traditionally,
962 		 * web servers responsed with the request path on 40x
963 		 * errors which could be abused to inject JavaScript etc.
964 		 * Instead of sanitizing the path here, we just don't
965 		 * reprint it.
966 		 */
967 		break;
968 	}
969 
970 	free(escapedmsg);
971 
972 	if ((srv_conf->flags & SRVFLAG_ERRDOCS) == 0)
973 		goto builtin; /* errdocs not enabled */
974 	if ((size_t)snprintf(cstr, sizeof(cstr), "%03u", code) >= sizeof(cstr))
975 		goto builtin;
976 
977 	if ((body = read_errdoc(srv_conf->errdocroot, cstr)) == NULL &&
978 	    (body = read_errdoc(srv_conf->errdocroot, HTTPD_ERRDOCTEMPLATE))
979 	    == NULL)
980 		goto builtin;
981 
982 	body = replace_var(body, "$HTTP_ERROR", httperr);
983 	body = replace_var(body, "$RESPONSE_CODE", cstr);
984 	body = replace_var(body, "$SERVER_SOFTWARE", HTTPD_SERVERNAME);
985 	bodylen = strlen(body);
986 	goto send;
987 
988  builtin:
989 	/* A CSS stylesheet allows minimal customization by the user */
990 	style = "body { background-color: white; color: black; font-family: "
991 	    "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n"
992 	    "hr { border: 0; border-bottom: 1px dashed; }\n"
993 	    "@media (prefers-color-scheme: dark) {\n"
994 	    "body { background-color: #1E1F21; color: #EEEFF1; }\n"
995 	    "a { color: #BAD7FF; }\n}";
996 
997 	/* Generate simple HTML error document */
998 	if ((bodylen = asprintf(&body,
999 	    "<!DOCTYPE html>\n"
1000 	    "<html>\n"
1001 	    "<head>\n"
1002 	    "<meta charset=\"utf-8\">\n"
1003 	    "<title>%03d %s</title>\n"
1004 	    "<style type=\"text/css\"><!--\n%s\n--></style>\n"
1005 	    "</head>\n"
1006 	    "<body>\n"
1007 	    "<h1>%03d %s</h1>\n"
1008 	    "<hr>\n<address>%s</address>\n"
1009 	    "</body>\n"
1010 	    "</html>\n",
1011 	    code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) {
1012 		body = NULL;
1013 		goto done;
1014 	}
1015 
1016  send:
1017 	if (srv_conf->flags & SRVFLAG_SERVER_HSTS &&
1018 	    srv_conf->flags & SRVFLAG_TLS) {
1019 		if (asprintf(&hstsheader, "Strict-Transport-Security: "
1020 		    "max-age=%d%s%s\r\n", srv_conf->hsts_max_age,
1021 		    srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ?
1022 		    "; includeSubDomains" : "",
1023 		    srv_conf->hsts_flags & HSTSFLAG_PRELOAD ?
1024 		    "; preload" : "") == -1) {
1025 			hstsheader = NULL;
1026 			goto done;
1027 		}
1028 	}
1029 
1030 	if ((code >= 100 && code < 200) || code == 204)
1031 		clenheader = NULL;
1032 	else {
1033 		if (asprintf(&clenheader,
1034 		    "Content-Length: %zd\r\n", bodylen) == -1) {
1035 			clenheader = NULL;
1036 			goto done;
1037 		}
1038 	}
1039 
1040 	/* Add basic HTTP headers */
1041 	if (asprintf(&httpmsg,
1042 	    "HTTP/1.0 %03d %s\r\n"
1043 	    "Date: %s\r\n"
1044 	    "Server: %s\r\n"
1045 	    "Connection: close\r\n"
1046 	    "Content-Type: text/html\r\n"
1047 	    "%s"
1048 	    "%s"
1049 	    "%s"
1050 	    "\r\n"
1051 	    "%s",
1052 	    code, httperr, tmbuf, HTTPD_SERVERNAME,
1053 	    clenheader == NULL ? "" : clenheader,
1054 	    extraheader == NULL ? "" : extraheader,
1055 	    hstsheader == NULL ? "" : hstsheader,
1056 	    desc->http_method == HTTP_METHOD_HEAD || clenheader == NULL ?
1057 	    "" : body) == -1)
1058 		goto done;
1059 
1060 	/* Dump the message without checking for success */
1061 	server_dump(clt, httpmsg, strlen(httpmsg));
1062 	free(httpmsg);
1063 
1064  done:
1065 	free(body);
1066 	free(extraheader);
1067 	free(hstsheader);
1068 	free(clenheader);
1069 	if (msg == NULL)
1070 		msg = "\"\"";
1071 	if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) {
1072 		server_close(clt, msg);
1073 	} else {
1074 		server_close(clt, httpmsg);
1075 		free(httpmsg);
1076 	}
1077 }
1078 
1079 void
server_close_http(struct client * clt)1080 server_close_http(struct client *clt)
1081 {
1082 	struct http_descriptor *desc;
1083 
1084 	desc = clt->clt_descreq;
1085 	server_httpdesc_free(desc);
1086 	free(desc);
1087 	clt->clt_descreq = NULL;
1088 
1089 	desc = clt->clt_descresp;
1090 	server_httpdesc_free(desc);
1091 	free(desc);
1092 	clt->clt_descresp = NULL;
1093 	free(clt->clt_remote_user);
1094 	clt->clt_remote_user = NULL;
1095 
1096 	str_match_free(&clt->clt_srv_match);
1097 }
1098 
1099 char *
server_expand_http(struct client * clt,const char * val,char * buf,size_t len)1100 server_expand_http(struct client *clt, const char *val, char *buf,
1101     size_t len)
1102 {
1103 	struct http_descriptor	*desc = clt->clt_descreq;
1104 	struct server_config	*srv_conf = clt->clt_srv_conf;
1105 	char			 ibuf[128], *str, *path, *query;
1106 	const char		*errstr = NULL, *p;
1107 	size_t			 size;
1108 	int			 n, ret;
1109 
1110 	if (strlcpy(buf, val, len) >= len)
1111 		return (NULL);
1112 
1113 	/* Find previously matched substrings by index */
1114 	for (p = val; clt->clt_srv_match.sm_nmatch &&
1115 	    (p = strstr(p, "%")) != NULL; p++) {
1116 		if (!isdigit((unsigned char)*(p + 1)))
1117 			continue;
1118 
1119 		/* Copy number, leading '%' char and add trailing \0 */
1120 		size = strspn(p + 1, "0123456789") + 2;
1121 		if (size  >= sizeof(ibuf))
1122 			return (NULL);
1123 		(void)strlcpy(ibuf, p, size);
1124 		n = strtonum(ibuf + 1, 0,
1125 		    clt->clt_srv_match.sm_nmatch - 1, &errstr);
1126 		if (errstr != NULL)
1127 			return (NULL);
1128 
1129 		/* Expand variable with matched value */
1130 		if ((str = url_encode(clt->clt_srv_match.sm_match[n])) == NULL)
1131 			return (NULL);
1132 		ret = expand_string(buf, len, ibuf, str);
1133 		free(str);
1134 		if (ret != 0)
1135 			return (NULL);
1136 	}
1137 	if (strstr(val, "$DOCUMENT_URI") != NULL) {
1138 		if ((path = url_encode(desc->http_path)) == NULL)
1139 			return (NULL);
1140 		ret = expand_string(buf, len, "$DOCUMENT_URI", path);
1141 		free(path);
1142 		if (ret != 0)
1143 			return (NULL);
1144 	}
1145 	if (strstr(val, "$QUERY_STRING_ENC") != NULL) {
1146 		if (desc->http_query == NULL) {
1147 			ret = expand_string(buf, len, "$QUERY_STRING_ENC", "");
1148 		} else {
1149 			if ((query = url_encode(desc->http_query)) == NULL)
1150 				return (NULL);
1151 			ret = expand_string(buf, len, "$QUERY_STRING_ENC", query);
1152 			free(query);
1153 		}
1154 		if (ret != 0)
1155 			return (NULL);
1156 	}
1157 	if (strstr(val, "$QUERY_STRING") != NULL) {
1158 		if (desc->http_query == NULL) {
1159 			ret = expand_string(buf, len, "$QUERY_STRING", "");
1160 		} else {
1161 			ret = expand_string(buf, len, "$QUERY_STRING",
1162 			    desc->http_query);
1163 		}
1164 		if (ret != 0)
1165 			return (NULL);
1166 	}
1167 	if (strstr(val, "$HTTP_HOST") != NULL) {
1168 		if (desc->http_host == NULL)
1169 			return (NULL);
1170 		if ((str = url_encode(desc->http_host)) == NULL)
1171 			return (NULL);
1172 		expand_string(buf, len, "$HTTP_HOST", str);
1173 		free(str);
1174 	}
1175 	if (strstr(val, "$REMOTE_") != NULL) {
1176 		if (strstr(val, "$REMOTE_ADDR") != NULL) {
1177 			if (print_host(&clt->clt_ss,
1178 			    ibuf, sizeof(ibuf)) == NULL)
1179 				return (NULL);
1180 			if (expand_string(buf, len,
1181 			    "$REMOTE_ADDR", ibuf) != 0)
1182 				return (NULL);
1183 		}
1184 		if (strstr(val, "$REMOTE_PORT") != NULL) {
1185 			snprintf(ibuf, sizeof(ibuf),
1186 			    "%u", ntohs(clt->clt_port));
1187 			if (expand_string(buf, len,
1188 			    "$REMOTE_PORT", ibuf) != 0)
1189 				return (NULL);
1190 		}
1191 		if (strstr(val, "$REMOTE_USER") != NULL) {
1192 			if ((srv_conf->flags & SRVFLAG_AUTH) &&
1193 			    clt->clt_remote_user != NULL) {
1194 				if ((str = url_encode(clt->clt_remote_user))
1195 				    == NULL)
1196 					return (NULL);
1197 			} else
1198 				str = strdup("");
1199 			ret = expand_string(buf, len, "$REMOTE_USER", str);
1200 			free(str);
1201 			if (ret != 0)
1202 				return (NULL);
1203 		}
1204 	}
1205 	if (strstr(val, "$REQUEST_URI") != NULL) {
1206 		if ((path = url_encode(desc->http_path)) == NULL)
1207 			return (NULL);
1208 		if (desc->http_query == NULL) {
1209 			str = path;
1210 		} else {
1211 			ret = asprintf(&str, "%s?%s", path, desc->http_query);
1212 			free(path);
1213 			if (ret == -1)
1214 				return (NULL);
1215 		}
1216 
1217 		ret = expand_string(buf, len, "$REQUEST_URI", str);
1218 		free(str);
1219 		if (ret != 0)
1220 			return (NULL);
1221 	}
1222 	if (strstr(val, "$REQUEST_SCHEME") != NULL) {
1223 		if (srv_conf->flags & SRVFLAG_TLS) {
1224 			ret = expand_string(buf, len, "$REQUEST_SCHEME", "https");
1225 		} else {
1226 			ret = expand_string(buf, len, "$REQUEST_SCHEME", "http");
1227 		}
1228 		if (ret != 0)
1229 			return (NULL);
1230 	}
1231 	if (strstr(val, "$SERVER_") != NULL) {
1232 		if (strstr(val, "$SERVER_ADDR") != NULL) {
1233 			if (print_host(&srv_conf->ss,
1234 			    ibuf, sizeof(ibuf)) == NULL)
1235 				return (NULL);
1236 			if (expand_string(buf, len,
1237 			    "$SERVER_ADDR", ibuf) != 0)
1238 				return (NULL);
1239 		}
1240 		if (strstr(val, "$SERVER_PORT") != NULL) {
1241 			snprintf(ibuf, sizeof(ibuf), "%u",
1242 			    ntohs(srv_conf->port));
1243 			if (expand_string(buf, len,
1244 			    "$SERVER_PORT", ibuf) != 0)
1245 				return (NULL);
1246 		}
1247 		if (strstr(val, "$SERVER_NAME") != NULL) {
1248 			if ((str = url_encode(srv_conf->name))
1249 			     == NULL)
1250 				return (NULL);
1251 			ret = expand_string(buf, len, "$SERVER_NAME", str);
1252 			free(str);
1253 			if (ret != 0)
1254 				return (NULL);
1255 		}
1256 	}
1257 
1258 	return (buf);
1259 }
1260 
1261 int
server_response(struct httpd * httpd,struct client * clt)1262 server_response(struct httpd *httpd, struct client *clt)
1263 {
1264 	char			 path[PATH_MAX];
1265 	char			 hostname[HOST_NAME_MAX+1];
1266 	struct http_descriptor	*desc = clt->clt_descreq;
1267 	struct http_descriptor	*resp = clt->clt_descresp;
1268 	struct server		*srv = clt->clt_srv;
1269 	struct server_config	*srv_conf = &srv->srv_conf;
1270 	struct kv		*kv, key, *host;
1271 	struct str_find		 sm;
1272 	int			 portval = -1, ret;
1273 	char			*hostval, *query;
1274 	const char		*errstr = NULL;
1275 
1276 	/* Preserve original path */
1277 	if (desc->http_path == NULL ||
1278 	    (desc->http_path_orig = strdup(desc->http_path)) == NULL)
1279 		goto fail;
1280 
1281 	/* Decode the URL */
1282 	if (url_decode(desc->http_path) == NULL)
1283 		goto fail;
1284 
1285 	/* Canonicalize the request path */
1286 	if (canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
1287 		goto fail;
1288 	free(desc->http_path);
1289 	if ((desc->http_path = strdup(path)) == NULL)
1290 		goto fail;
1291 
1292 	key.kv_key = "Host";
1293 	if ((host = kv_find(&desc->http_headers, &key)) != NULL &&
1294 	    host->kv_value == NULL)
1295 		host = NULL;
1296 
1297 	if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
1298 		/* Host header is mandatory */
1299 		if (host == NULL)
1300 			goto fail;
1301 
1302 		/* Is the connection persistent? */
1303 		key.kv_key = "Connection";
1304 		if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
1305 		    strcasecmp("close", kv->kv_value) == 0)
1306 			clt->clt_persist = 0;
1307 		else
1308 			clt->clt_persist++;
1309 	} else {
1310 		/* Is the connection persistent? */
1311 		key.kv_key = "Connection";
1312 		if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
1313 		    strcasecmp("keep-alive", kv->kv_value) == 0)
1314 			clt->clt_persist++;
1315 		else
1316 			clt->clt_persist = 0;
1317 	}
1318 
1319 	/*
1320 	 * Do we have a Host header and matching configuration?
1321 	 * XXX the Host can also appear in the URL path.
1322 	 */
1323 	if (host != NULL) {
1324 		if ((hostval = server_http_parsehost(host->kv_value,
1325 		    hostname, sizeof(hostname), &portval)) == NULL)
1326 			goto fail;
1327 
1328 		TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
1329 #ifdef DEBUG
1330 			if ((srv_conf->flags & SRVFLAG_LOCATION) == 0) {
1331 				DPRINTF("%s: virtual host \"%s:%u\""
1332 				    " host \"%s\" (\"%s\")",
1333 				    __func__, srv_conf->name,
1334 				    ntohs(srv_conf->port), host->kv_value,
1335 				    hostname);
1336 			}
1337 #endif
1338 			if (srv_conf->flags & SRVFLAG_LOCATION)
1339 				continue;
1340 			else if (srv_conf->flags & SRVFLAG_SERVER_MATCH) {
1341 				str_find(hostname, srv_conf->name,
1342 				    &sm, 1, &errstr);
1343 				ret = errstr == NULL ? 0 : -1;
1344 			} else {
1345 				ret = fnmatch(srv_conf->name,
1346 				    hostname, FNM_CASEFOLD);
1347 			}
1348 			if (ret == 0 &&
1349 			    (portval == -1 || portval == srv_conf->port)) {
1350 				/* Replace host configuration */
1351 				clt->clt_srv_conf = srv_conf;
1352 				srv_conf = NULL;
1353 				break;
1354 			}
1355 		}
1356 	}
1357 
1358 	if (srv_conf != NULL) {
1359 		/* Use the actual server IP address */
1360 		if (server_http_host(&clt->clt_srv_ss, hostname,
1361 		    sizeof(hostname)) == NULL)
1362 			goto fail;
1363 	} else {
1364 		/* Host header was valid and found */
1365 		if (strlcpy(hostname, host->kv_value, sizeof(hostname)) >=
1366 		    sizeof(hostname))
1367 			goto fail;
1368 		srv_conf = clt->clt_srv_conf;
1369 	}
1370 
1371 
1372 	/* Set request timeout from matching host configuration. */
1373 	bufferevent_settimeout(clt->clt_bev,
1374 	    srv_conf->requesttimeout.tv_sec, srv_conf->requesttimeout.tv_sec);
1375 
1376 	if (clt->clt_persist >= srv_conf->maxrequests)
1377 		clt->clt_persist = 0;
1378 
1379 	/* pipelining should end after the first "idempotent" method */
1380 	if (clt->clt_pipelining && clt->clt_toread > 0)
1381 		clt->clt_persist = 0;
1382 
1383 	if ((desc->http_host = strdup(hostname)) == NULL)
1384 		goto fail;
1385 
1386 	/* Now fill in the mandatory parts of the response descriptor */
1387 	resp->http_method = desc->http_method;
1388 	if ((resp->http_version = strdup(desc->http_version)) == NULL)
1389 		goto fail;
1390 
1391 	/* Now search for the location */
1392 	if ((srv_conf = server_getlocation(clt, desc->http_path)) == NULL) {
1393 		server_abort_http(clt, 500, desc->http_path);
1394 		return (-1);
1395 	}
1396 
1397 	/* Optional rewrite */
1398 	if (srv_conf->flags & SRVFLAG_PATH_REWRITE) {
1399 		/* Expand macros */
1400 		if (server_expand_http(clt, srv_conf->path,
1401 		    path, sizeof(path)) == NULL)
1402 			goto fail;
1403 
1404 		/*
1405 		 * Reset and update the query.  The updated query must already
1406 		 * be URL encoded - either specified by the user or by using the
1407 		 * original $QUERY_STRING.
1408 		 */
1409 		free(desc->http_query_alias);
1410 		desc->http_query_alias = NULL;
1411 		if ((query = strchr(path, '?')) != NULL) {
1412 			*query++ = '\0';
1413 			if ((desc->http_query_alias = strdup(query)) == NULL)
1414 				goto fail;
1415 		}
1416 
1417 		/* Canonicalize the updated request path */
1418 		if (canonicalize_path(path,
1419 		    path, sizeof(path)) == NULL)
1420 			goto fail;
1421 
1422 		log_debug("%s: rewrote %s?%s -> %s?%s", __func__,
1423 		    desc->http_path, desc->http_query ? desc->http_query : "",
1424 		    path, query ? query : "");
1425 
1426 		free(desc->http_path_alias);
1427 		if ((desc->http_path_alias = strdup(path)) == NULL)
1428 			goto fail;
1429 
1430 		/* Now search for the updated location */
1431 		if ((srv_conf = server_getlocation(clt,
1432 		    desc->http_path_alias)) == NULL) {
1433 			server_abort_http(clt, 500, desc->http_path_alias);
1434 			return (-1);
1435 		}
1436 	}
1437 
1438 	if (clt->clt_toread > 0 && (size_t)clt->clt_toread >
1439 	    srv_conf->maxrequestbody) {
1440 		server_abort_http(clt, 413, "request body too large");
1441 		return (-1);
1442 	}
1443 
1444 	if (srv_conf->flags & SRVFLAG_BLOCK) {
1445 		server_abort_http(clt, srv_conf->return_code,
1446 		    srv_conf->return_uri);
1447 		return (-1);
1448 	} else if (srv_conf->flags & SRVFLAG_AUTH &&
1449 	    server_http_authenticate(srv_conf, clt) == -1) {
1450 		server_abort_http(clt, 401, srv_conf->auth_realm);
1451 		return (-1);
1452 	} else
1453 		return (server_file(httpd, clt));
1454  fail:
1455 	server_abort_http(clt, 400, "bad request");
1456 	return (-1);
1457 }
1458 
1459 const char *
server_root_strip(const char * path,int n)1460 server_root_strip(const char *path, int n)
1461 {
1462 	const char *p;
1463 
1464 	/* Strip strip leading directories. Leading '/' is ignored. */
1465 	for (; n > 0 && *path != '\0'; n--)
1466 		if ((p = strchr(++path, '/')) == NULL)
1467 			path = strchr(path, '\0');
1468 		else
1469 			path = p;
1470 
1471 	return (path);
1472 }
1473 
1474 struct server_config *
server_getlocation(struct client * clt,const char * path)1475 server_getlocation(struct client *clt, const char *path)
1476 {
1477 	struct server		*srv = clt->clt_srv;
1478 	struct server_config	*srv_conf = clt->clt_srv_conf, *location;
1479 	const char		*errstr = NULL;
1480 	int			 ret;
1481 
1482 	/* Now search for the location */
1483 	TAILQ_FOREACH(location, &srv->srv_hosts, entry) {
1484 #ifdef DEBUG
1485 		if (location->flags & SRVFLAG_LOCATION) {
1486 			DPRINTF("%s: location \"%s\" path \"%s\"",
1487 			    __func__, location->location, path);
1488 		}
1489 #endif
1490 		if ((location->flags & SRVFLAG_LOCATION) &&
1491 		    location->parent_id == srv_conf->parent_id) {
1492 			errstr = NULL;
1493 			if (location->flags & SRVFLAG_LOCATION_MATCH) {
1494 				ret = str_match(path, location->location,
1495 				    &clt->clt_srv_match, &errstr);
1496 			} else {
1497 				ret = fnmatch(location->location,
1498 				    path, FNM_CASEFOLD);
1499 			}
1500 			if (ret == 0 && errstr == NULL) {
1501 				if ((ret = server_locationaccesstest(location,
1502 				    path)) == -1)
1503 					return (NULL);
1504 
1505 				if (ret)
1506 					continue;
1507 				/* Replace host configuration */
1508 				clt->clt_srv_conf = srv_conf = location;
1509 				break;
1510 			}
1511 		}
1512 	}
1513 
1514 	return (srv_conf);
1515 }
1516 
1517 int
server_locationaccesstest(struct server_config * srv_conf,const char * path)1518 server_locationaccesstest(struct server_config *srv_conf, const char *path)
1519 {
1520 	int		 rootfd, ret;
1521 	struct stat	 sb;
1522 
1523 	if (((SRVFLAG_LOCATION_FOUND | SRVFLAG_LOCATION_NOT_FOUND) &
1524 	    srv_conf->flags) == 0)
1525 		return (0);
1526 
1527 	if ((rootfd = open(srv_conf->root, O_RDONLY)) == -1)
1528 		return (-1);
1529 
1530 	path = server_root_strip(path, srv_conf->strip) + 1;
1531 	if ((ret = faccessat(rootfd, path, R_OK, 0)) != -1)
1532 		ret = fstatat(rootfd, path, &sb, 0);
1533 	close(rootfd);
1534 	return ((ret == -1 && SRVFLAG_LOCATION_FOUND & srv_conf->flags) ||
1535 	    (ret == 0 && SRVFLAG_LOCATION_NOT_FOUND & srv_conf->flags));
1536 }
1537 
1538 int
server_response_http(struct client * clt,unsigned int code,struct media_type * media,off_t size,time_t mtime)1539 server_response_http(struct client *clt, unsigned int code,
1540     struct media_type *media, off_t size, time_t mtime)
1541 {
1542 	struct server_config	*srv_conf = clt->clt_srv_conf;
1543 	struct http_descriptor	*desc = clt->clt_descreq;
1544 	struct http_descriptor	*resp = clt->clt_descresp;
1545 	const char		*error;
1546 	struct kv		*ct, *cl;
1547 	char			 tmbuf[32];
1548 
1549 	if (desc == NULL || media == NULL ||
1550 	    (error = server_httperror_byid(code)) == NULL)
1551 		return (-1);
1552 
1553 	if (server_log_http(clt, code, size >= 0 ? size : 0) == -1)
1554 		return (-1);
1555 
1556 	/* Add error codes */
1557 	if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 ||
1558 	    kv_set(&resp->http_pathquery, "%s", error) == -1)
1559 		return (-1);
1560 
1561 	/* Add headers */
1562 	if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
1563 		return (-1);
1564 
1565 	/* Is it a persistent connection? */
1566 	if (clt->clt_persist) {
1567 		if (kv_add(&resp->http_headers,
1568 		    "Connection", "keep-alive") == NULL)
1569 			return (-1);
1570 	} else if (kv_add(&resp->http_headers, "Connection", "close") == NULL)
1571 		return (-1);
1572 
1573 	/* Set media type */
1574 	if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL ||
1575 	    kv_set(ct, "%s/%s", media->media_type, media->media_subtype) == -1)
1576 		return (-1);
1577 
1578 	/* Set content length, if specified */
1579 	if (size >= 0 && ((cl =
1580 	    kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL ||
1581 	    kv_set(cl, "%lld", (long long)size) == -1))
1582 		return (-1);
1583 
1584 	/* Set last modification time */
1585 	if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 ||
1586 	    kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL)
1587 		return (-1);
1588 
1589 	/* HSTS header */
1590 	if (srv_conf->flags & SRVFLAG_SERVER_HSTS &&
1591 	    srv_conf->flags & SRVFLAG_TLS) {
1592 		if ((cl =
1593 		    kv_add(&resp->http_headers, "Strict-Transport-Security",
1594 		    NULL)) == NULL ||
1595 		    kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age,
1596 		    srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ?
1597 		    "; includeSubDomains" : "",
1598 		    srv_conf->hsts_flags & HSTSFLAG_PRELOAD ?
1599 		    "; preload" : "") == -1)
1600 			return (-1);
1601 	}
1602 
1603 	/* Date header is mandatory and should be added as late as possible */
1604 	if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
1605 	    kv_add(&resp->http_headers, "Date", tmbuf) == NULL)
1606 		return (-1);
1607 
1608 	/* Write completed header */
1609 	if (server_writeresponse_http(clt) == -1 ||
1610 	    server_bufferevent_print(clt, "\r\n") == -1 ||
1611 	    server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
1612 	    server_bufferevent_print(clt, "\r\n") == -1)
1613 		return (-1);
1614 
1615 	if (size <= 0 || resp->http_method == HTTP_METHOD_HEAD) {
1616 		bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
1617 		if (clt->clt_persist)
1618 			clt->clt_toread = TOREAD_HTTP_HEADER;
1619 		else
1620 			clt->clt_toread = TOREAD_HTTP_NONE;
1621 		clt->clt_done = 0;
1622 		return (0);
1623 	}
1624 
1625 	return (1);
1626 }
1627 
1628 int
server_writeresponse_http(struct client * clt)1629 server_writeresponse_http(struct client *clt)
1630 {
1631 	struct http_descriptor	*desc = clt->clt_descresp;
1632 
1633 	DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
1634 	    desc->http_rescode, desc->http_resmesg);
1635 
1636 	if (server_bufferevent_print(clt, desc->http_version) == -1 ||
1637 	    server_bufferevent_print(clt, " ") == -1 ||
1638 	    server_bufferevent_print(clt, desc->http_rescode) == -1 ||
1639 	    server_bufferevent_print(clt, " ") == -1 ||
1640 	    server_bufferevent_print(clt, desc->http_resmesg) == -1)
1641 		return (-1);
1642 
1643 	return (0);
1644 }
1645 
1646 int
server_writeheader_http(struct client * clt,struct kv * hdr,void * arg)1647 server_writeheader_http(struct client *clt, struct kv *hdr, void *arg)
1648 {
1649 	char			*ptr;
1650 	const char		*key;
1651 
1652 	/* The key might have been updated in the parent */
1653 	if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
1654 		key = hdr->kv_parent->kv_key;
1655 	else
1656 		key = hdr->kv_key;
1657 
1658 	ptr = hdr->kv_value;
1659 	if (server_bufferevent_print(clt, key) == -1 ||
1660 	    (ptr != NULL &&
1661 	    (server_bufferevent_print(clt, ": ") == -1 ||
1662 	    server_bufferevent_print(clt, ptr) == -1 ||
1663 	    server_bufferevent_print(clt, "\r\n") == -1)))
1664 		return (-1);
1665 	DPRINTF("%s: %s: %s", __func__, key,
1666 	    hdr->kv_value == NULL ? "" : hdr->kv_value);
1667 
1668 	return (0);
1669 }
1670 
1671 int
server_headers(struct client * clt,void * descp,int (* hdr_cb)(struct client *,struct kv *,void *),void * arg)1672 server_headers(struct client *clt, void *descp,
1673     int (*hdr_cb)(struct client *, struct kv *, void *), void *arg)
1674 {
1675 	struct kv		*hdr, *kv;
1676 	struct http_descriptor	*desc = descp;
1677 
1678 	RB_FOREACH(hdr, kvtree, &desc->http_headers) {
1679 		if ((hdr_cb)(clt, hdr, arg) == -1)
1680 			return (-1);
1681 		TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) {
1682 			if ((hdr_cb)(clt, kv, arg) == -1)
1683 				return (-1);
1684 		}
1685 	}
1686 
1687 	return (0);
1688 }
1689 
1690 enum httpmethod
server_httpmethod_byname(const char * name)1691 server_httpmethod_byname(const char *name)
1692 {
1693 	enum httpmethod		 id = HTTP_METHOD_NONE;
1694 	struct http_method	 method, *res = NULL;
1695 
1696 	/* Set up key */
1697 	method.method_name = name;
1698 
1699 	if ((res = bsearch(&method, http_methods,
1700 	    sizeof(http_methods) / sizeof(http_methods[0]) - 1,
1701 	    sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL)
1702 		id = res->method_id;
1703 
1704 	return (id);
1705 }
1706 
1707 const char *
server_httpmethod_byid(unsigned int id)1708 server_httpmethod_byid(unsigned int id)
1709 {
1710 	const char	*name = "<UNKNOWN>";
1711 	int		 i;
1712 
1713 	for (i = 0; http_methods[i].method_name != NULL; i++) {
1714 		if (http_methods[i].method_id == id) {
1715 			name = http_methods[i].method_name;
1716 			break;
1717 		}
1718 	}
1719 
1720 	return (name);
1721 }
1722 
1723 static int
server_httpmethod_cmp(const void * a,const void * b)1724 server_httpmethod_cmp(const void *a, const void *b)
1725 {
1726 	const struct http_method *ma = a;
1727 	const struct http_method *mb = b;
1728 
1729 	/*
1730 	 * RFC 2616 section 5.1.1 says that the method is case
1731 	 * sensitive so we don't do a strcasecmp here.
1732 	 */
1733 	return (strcmp(ma->method_name, mb->method_name));
1734 }
1735 
1736 const char *
server_httperror_byid(unsigned int id)1737 server_httperror_byid(unsigned int id)
1738 {
1739 	struct http_error	 error, *res;
1740 
1741 	/* Set up key */
1742 	error.error_code = (int)id;
1743 
1744 	if ((res = bsearch(&error, http_errors,
1745 	    sizeof(http_errors) / sizeof(http_errors[0]) - 1,
1746 	    sizeof(http_errors[0]), server_httperror_cmp)) != NULL)
1747 		return (res->error_name);
1748 
1749 	return (NULL);
1750 }
1751 
1752 static int
server_httperror_cmp(const void * a,const void * b)1753 server_httperror_cmp(const void *a, const void *b)
1754 {
1755 	const struct http_error *ea = a;
1756 	const struct http_error *eb = b;
1757 	return (ea->error_code - eb->error_code);
1758 }
1759 
1760 /*
1761  * return -1 on failure, strlen() of read file otherwise.
1762  * body is NULL on failure, contents of file with trailing \0 otherwise.
1763  */
1764 char *
read_errdoc(const char * root,const char * file)1765 read_errdoc(const char *root, const char *file)
1766 {
1767 	struct stat	 sb;
1768 	char		*path;
1769 	int		 fd;
1770 	char		*ret;
1771 
1772 	if (asprintf(&path, "%s/%s.html", root, file) == -1)
1773 		fatal("asprintf");
1774 	if ((fd = open(path, O_RDONLY)) == -1) {
1775 		free(path);
1776 		if (errno != ENOENT)
1777 			log_warn("%s: open", __func__);
1778 		return (NULL);
1779 	}
1780 	free(path);
1781 	if (fstat(fd, &sb) < 0) {
1782 		log_warn("%s: stat", __func__);
1783 		close(fd);
1784 		return (NULL);
1785 	}
1786 
1787 	if ((ret = calloc(1, sb.st_size + 1)) == NULL)
1788 		fatal("calloc");
1789 	if (sb.st_size == 0) {
1790 		close(fd);
1791 		return (ret);
1792 	}
1793 	if (read(fd, ret, sb.st_size) != sb.st_size) {
1794 		log_warn("%s: read", __func__);
1795 		close(fd);
1796 		free(ret);
1797 		return (NULL);
1798 	}
1799 	close(fd);
1800 
1801 	return (ret);
1802 }
1803 
1804 char *
replace_var(char * str,const char * var,const char * repl)1805 replace_var(char *str, const char *var, const char *repl)
1806 {
1807 	char	*iv, *r;
1808 	size_t	 vlen;
1809 
1810 	vlen = strlen(var);
1811 	while ((iv = strstr(str, var)) != NULL) {
1812 		*iv = '\0';
1813 		if (asprintf(&r, "%s%s%s", str, repl, &iv[vlen]) == -1)
1814 			fatal("asprintf");
1815 		free(str);
1816 		str = r;
1817 	}
1818 	return (str);
1819 }
1820 
1821 int
server_log_http(struct client * clt,unsigned int code,size_t len)1822 server_log_http(struct client *clt, unsigned int code, size_t len)
1823 {
1824 	static char		 tstamp[64];
1825 	static char		 ip[INET6_ADDRSTRLEN];
1826 	time_t			 t;
1827 	struct kv		 key, *agent, *referrer, *xff, *xfp;
1828 	struct tm		*tm;
1829 	struct server_config	*srv_conf;
1830 	struct http_descriptor	*desc;
1831 	int			 ret = -1;
1832 	char			*user = NULL;
1833 	char			*path = NULL;
1834 	char			*version = NULL;
1835 	char			*referrer_v = NULL;
1836 	char			*agent_v = NULL;
1837 	char			*xff_v = NULL;
1838 	char			*xfp_v = NULL;
1839 
1840 	if ((srv_conf = clt->clt_srv_conf) == NULL)
1841 		return (-1);
1842 	if ((srv_conf->flags & SRVFLAG_LOG) == 0)
1843 		return (0);
1844 	if ((desc = clt->clt_descreq) == NULL)
1845 		return (-1);
1846 
1847 	if ((t = time(NULL)) == -1)
1848 		return (-1);
1849 	if ((tm = localtime(&t)) == NULL)
1850 		return (-1);
1851 	if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0)
1852 		return (-1);
1853 
1854 	if (print_host(&clt->clt_ss, ip, sizeof(ip)) == NULL)
1855 		return (-1);
1856 
1857 	/*
1858 	 * For details on common log format, see:
1859 	 * https://httpd.apache.org/docs/current/mod/mod_log_config.html
1860 	 *
1861 	 * httpd's format is similar to these Apache LogFormats:
1862 	 * "%v %h %l %u %t \"%r\" %>s %B"
1863 	 * "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-agent}i\""
1864 	 */
1865 	switch (srv_conf->logformat) {
1866 	case LOG_FORMAT_COMMON:
1867 		/* Use vis to encode input values from the header */
1868 		if (clt->clt_remote_user &&
1869 		    stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1)
1870 			goto done;
1871 		if (desc->http_version &&
1872 		    stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1)
1873 			goto done;
1874 
1875 		/* The following should be URL-encoded */
1876 		if (desc->http_path &&
1877 		    (path = url_encode(desc->http_path)) == NULL)
1878 			goto done;
1879 
1880 		ret = evbuffer_add_printf(clt->clt_log,
1881 		    "%s %s - %s [%s] \"%s %s%s%s%s%s\" %03d %zu\n",
1882 		    srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" :
1883 		    user, tstamp,
1884 		    server_httpmethod_byid(desc->http_method),
1885 		    desc->http_path == NULL ? "" : path,
1886 		    desc->http_query == NULL ? "" : "?",
1887 		    desc->http_query == NULL ? "" : desc->http_query,
1888 		    desc->http_version == NULL ? "" : " ",
1889 		    desc->http_version == NULL ? "" : version,
1890 		    code, len);
1891 
1892 		break;
1893 
1894 	case LOG_FORMAT_COMBINED:
1895 	case LOG_FORMAT_FORWARDED:
1896 		key.kv_key = "Referer"; /* sic */
1897 		if ((referrer = kv_find(&desc->http_headers, &key)) != NULL &&
1898 		    referrer->kv_value == NULL)
1899 			referrer = NULL;
1900 
1901 		key.kv_key = "User-Agent";
1902 		if ((agent = kv_find(&desc->http_headers, &key)) != NULL &&
1903 		    agent->kv_value == NULL)
1904 			agent = NULL;
1905 
1906 		/* Use vis to encode input values from the header */
1907 		if (clt->clt_remote_user &&
1908 		    stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1)
1909 			goto done;
1910 		if (clt->clt_remote_user == NULL &&
1911 		    clt->clt_tls_ctx != NULL &&
1912 		    (srv_conf->tls_flags & TLSFLAG_CA) &&
1913 		    tls_peer_cert_subject(clt->clt_tls_ctx) != NULL &&
1914 		    stravis(&user, tls_peer_cert_subject(clt->clt_tls_ctx),
1915 		    HTTPD_LOGVIS) == -1)
1916 			goto done;
1917 		if (desc->http_version &&
1918 		    stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1)
1919 			goto done;
1920 		if (agent &&
1921 		    stravis(&agent_v, agent->kv_value, HTTPD_LOGVIS) == -1)
1922 			goto done;
1923 
1924 		/* The following should be URL-encoded */
1925 		if (desc->http_path &&
1926 		    (path = url_encode(desc->http_path)) == NULL)
1927 			goto done;
1928 		if (referrer &&
1929 		    (referrer_v = url_encode(referrer->kv_value)) == NULL)
1930 			goto done;
1931 
1932 		if ((ret = evbuffer_add_printf(clt->clt_log,
1933 		    "%s %s - %s [%s] \"%s %s%s%s%s%s\""
1934 		    " %03d %zu \"%s\" \"%s\"",
1935 		    srv_conf->name, ip, user == NULL ? "-" :
1936 		    user, tstamp,
1937 		    server_httpmethod_byid(desc->http_method),
1938 		    desc->http_path == NULL ? "" : path,
1939 		    desc->http_query == NULL ? "" : "?",
1940 		    desc->http_query == NULL ? "" : desc->http_query,
1941 		    desc->http_version == NULL ? "" : " ",
1942 		    desc->http_version == NULL ? "" : version,
1943 		    code, len,
1944 		    referrer == NULL ? "" : referrer_v,
1945 		    agent == NULL ? "" : agent_v)) == -1)
1946 			break;
1947 
1948 		if (srv_conf->logformat == LOG_FORMAT_COMBINED)
1949 			goto finish;
1950 
1951 		xff = xfp = NULL;
1952 
1953 		key.kv_key = "X-Forwarded-For";
1954 		if ((xff = kv_find(&desc->http_headers, &key)) != NULL
1955 		    && xff->kv_value == NULL)
1956 			xff = NULL;
1957 
1958 		if (xff &&
1959 		    stravis(&xff_v, xff->kv_value, HTTPD_LOGVIS) == -1)
1960 			goto finish;
1961 
1962 		key.kv_key = "X-Forwarded-Port";
1963 		if ((xfp = kv_find(&desc->http_headers, &key)) != NULL &&
1964 		    (xfp->kv_value == NULL))
1965 			xfp = NULL;
1966 
1967 		if (xfp &&
1968 		    stravis(&xfp_v, xfp->kv_value, HTTPD_LOGVIS) == -1)
1969 			goto finish;
1970 
1971 		if ((ret = evbuffer_add_printf(clt->clt_log, " %s %s",
1972 		    xff == NULL ? "-" : xff_v,
1973 		    xfp == NULL ? "-" : xfp_v)) == -1)
1974 			break;
1975 finish:
1976 		ret = evbuffer_add_printf(clt->clt_log, "\n");
1977 
1978 		break;
1979 
1980 	case LOG_FORMAT_CONNECTION:
1981 		/* URL-encode the path */
1982 		if (desc->http_path &&
1983 		    (path = url_encode(desc->http_path)) == NULL)
1984 			goto done;
1985 
1986 		ret = evbuffer_add_printf(clt->clt_log, " [%s]",
1987 		    desc->http_path == NULL ? "" : path);
1988 
1989 		break;
1990 	}
1991 
1992 done:
1993 	free(user);
1994 	free(path);
1995 	free(version);
1996 	free(referrer_v);
1997 	free(agent_v);
1998 	free(xff_v);
1999 	free(xfp_v);
2000 
2001 	return (ret);
2002 }
2003