xref: /openbsd-src/usr.sbin/httpd/server_http.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: server_http.c,v 1.110 2016/08/26 10:46:39 rzalamena Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@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/queue.h>
21 #include <sys/socket.h>
22 #include <sys/tree.h>
23 
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <limits.h>
32 #include <fnmatch.h>
33 #include <stdio.h>
34 #include <time.h>
35 #include <resolv.h>
36 #include <event.h>
37 #include <ctype.h>
38 #include <vis.h>
39 
40 #include "httpd.h"
41 #include "http.h"
42 #include "patterns.h"
43 
44 static int	 server_httpmethod_cmp(const void *, const void *);
45 static int	 server_httperror_cmp(const void *, const void *);
46 void		 server_httpdesc_free(struct http_descriptor *);
47 int		 server_http_authenticate(struct server_config *,
48 		    struct client *);
49 char		*server_expand_http(struct client *, const char *,
50 		    char *, size_t);
51 
52 static struct http_method	 http_methods[] = HTTP_METHODS;
53 static struct http_error	 http_errors[] = HTTP_ERRORS;
54 
55 void
56 server_http(void)
57 {
58 	DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid());
59 
60 	/* Sort the HTTP lookup arrays */
61 	qsort(http_methods, sizeof(http_methods) /
62 	    sizeof(http_methods[0]) - 1,
63 	    sizeof(http_methods[0]), server_httpmethod_cmp);
64 	qsort(http_errors, sizeof(http_errors) /
65 	    sizeof(http_errors[0]) - 1,
66 	    sizeof(http_errors[0]), server_httperror_cmp);
67 }
68 
69 void
70 server_http_init(struct server *srv)
71 {
72 	/* nothing */
73 }
74 
75 int
76 server_httpdesc_init(struct client *clt)
77 {
78 	struct http_descriptor	*desc;
79 
80 	if ((desc = calloc(1, sizeof(*desc))) == NULL)
81 		return (-1);
82 	RB_INIT(&desc->http_headers);
83 	clt->clt_descreq = desc;
84 
85 	if ((desc = calloc(1, sizeof(*desc))) == NULL) {
86 		/* req will be cleaned up later */
87 		return (-1);
88 	}
89 	RB_INIT(&desc->http_headers);
90 	clt->clt_descresp = desc;
91 
92 	return (0);
93 }
94 
95 void
96 server_httpdesc_free(struct http_descriptor *desc)
97 {
98 	if (desc == NULL)
99 		return;
100 
101 	free(desc->http_path);
102 	desc->http_path = NULL;
103 	free(desc->http_path_alias);
104 	desc->http_path_alias = NULL;
105 	free(desc->http_query);
106 	desc->http_query = NULL;
107 	free(desc->http_version);
108 	desc->http_version = NULL;
109 	free(desc->http_host);
110 	desc->http_host = NULL;
111 
112 	kv_purge(&desc->http_headers);
113 	desc->http_lastheader = NULL;
114 	desc->http_method = 0;
115 	desc->http_chunked = 0;
116 }
117 
118 int
119 server_http_authenticate(struct server_config *srv_conf, struct client *clt)
120 {
121 	char			 decoded[1024];
122 	FILE			*fp = NULL;
123 	struct http_descriptor	*desc = clt->clt_descreq;
124 	const struct auth	*auth = srv_conf->auth;
125 	struct kv		*ba, key;
126 	size_t			 linesize = 0;
127 	ssize_t			 linelen;
128 	int			 ret = -1;
129 	char			*line = NULL, *user = NULL, *pass = NULL;
130 	char			*clt_user = NULL, *clt_pass = NULL;
131 
132 	memset(decoded, 0, sizeof(decoded));
133 	key.kv_key = "Authorization";
134 
135 	if ((ba = kv_find(&desc->http_headers, &key)) == NULL ||
136 	    ba->kv_value == NULL)
137 		goto done;
138 
139 	if (strncmp(ba->kv_value, "Basic ", strlen("Basic ")) != 0)
140 		goto done;
141 
142 	if (b64_pton(strchr(ba->kv_value, ' ') + 1, (uint8_t *)decoded,
143 	    sizeof(decoded)) <= 0)
144 		goto done;
145 
146 	if ((clt_pass = strchr(decoded, ':')) == NULL)
147 		goto done;
148 
149 	clt_user = decoded;
150 	*clt_pass++ = '\0';
151 	if ((clt->clt_remote_user = strdup(clt_user)) == NULL)
152 		goto done;
153 
154 	if (clt_pass == NULL)
155 		goto done;
156 
157 	if ((fp = fopen(auth->auth_htpasswd, "r")) == NULL)
158 		goto done;
159 
160 	while ((linelen = getline(&line, &linesize, fp)) != -1) {
161 		if (line[linelen - 1] == '\n')
162 			line[linelen - 1] = '\0';
163 		user = line;
164 		pass = strchr(line, ':');
165 
166 		if (pass == NULL) {
167 			explicit_bzero(line, linelen);
168 			continue;
169 		}
170 
171 		*pass++ = '\0';
172 
173 		if (strcmp(clt_user, user) != 0) {
174 			explicit_bzero(line, linelen);
175 			continue;
176 		}
177 
178 		if (crypt_checkpass(clt_pass, pass) == 0) {
179 			explicit_bzero(line, linelen);
180 			ret = 0;
181 			break;
182 		}
183 	}
184 done:
185 	free(line);
186 	if (fp != NULL)
187 		fclose(fp);
188 
189 	if (ba != NULL && ba->kv_value != NULL) {
190 		explicit_bzero(ba->kv_value, strlen(ba->kv_value));
191 		explicit_bzero(decoded, sizeof(decoded));
192 	}
193 
194 	return (ret);
195 }
196 
197 void
198 server_read_http(struct bufferevent *bev, void *arg)
199 {
200 	struct client		*clt = arg;
201 	struct server_config	*srv_conf = clt->clt_srv_conf;
202 	struct http_descriptor	*desc = clt->clt_descreq;
203 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
204 	char			*line = NULL, *key, *value;
205 	const char		*errstr;
206 	size_t			 size, linelen;
207 	struct kv		*hdr = NULL;
208 
209 	getmonotime(&clt->clt_tv_last);
210 
211 	size = EVBUFFER_LENGTH(src);
212 	DPRINTF("%s: session %d: size %lu, to read %lld",
213 	    __func__, clt->clt_id, size, clt->clt_toread);
214 	if (!size) {
215 		clt->clt_toread = TOREAD_HTTP_HEADER;
216 		goto done;
217 	}
218 
219 	while (!clt->clt_done && (line =
220 	    evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT)) != NULL) {
221 		linelen = strlen(line);
222 
223 		/*
224 		 * An empty line indicates the end of the request.
225 		 * libevent already stripped the \r\n for us.
226 		 */
227 		if (!linelen) {
228 			clt->clt_done = 1;
229 			free(line);
230 			break;
231 		}
232 		key = line;
233 
234 		/* Limit the total header length minus \r\n */
235 		clt->clt_headerlen += linelen;
236 		if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) {
237 			server_abort_http(clt, 413, "request too large");
238 			goto abort;
239 		}
240 
241 		/*
242 		 * The first line is the GET/POST/PUT/... request,
243 		 * subsequent lines are HTTP headers.
244 		 */
245 		if (++clt->clt_line == 1)
246 			value = strchr(key, ' ');
247 		else if (*key == ' ' || *key == '\t')
248 			/* Multiline headers wrap with a space or tab */
249 			value = NULL;
250 		else
251 			value = strchr(key, ':');
252 		if (value == NULL) {
253 			if (clt->clt_line == 1) {
254 				server_abort_http(clt, 400, "malformed");
255 				goto abort;
256 			}
257 
258 			/* Append line to the last header, if present */
259 			if (kv_extend(&desc->http_headers,
260 			    desc->http_lastheader, line) == NULL)
261 				goto fail;
262 
263 			free(line);
264 			continue;
265 		}
266 		if (*value == ':') {
267 			*value++ = '\0';
268 			value += strspn(value, " \t\r\n");
269 		} else {
270 			*value++ = '\0';
271 		}
272 
273 		DPRINTF("%s: session %d: header '%s: %s'", __func__,
274 		    clt->clt_id, key, value);
275 
276 		/*
277 		 * Identify and handle specific HTTP request methods
278 		 */
279 		if (clt->clt_line == 1) {
280 			if ((desc->http_method = server_httpmethod_byname(key))
281 			    == HTTP_METHOD_NONE) {
282 				server_abort_http(clt, 400, "malformed");
283 				goto abort;
284 			}
285 
286 			/*
287 			 * Decode request path and query
288 			 */
289 			desc->http_path = strdup(value);
290 			if (desc->http_path == NULL)
291 				goto fail;
292 
293 			desc->http_version = strchr(desc->http_path, ' ');
294 			if (desc->http_version == NULL) {
295 				server_abort_http(clt, 400, "malformed");
296 				goto abort;
297 			}
298 
299 			*desc->http_version++ = '\0';
300 			desc->http_query = strchr(desc->http_path, '?');
301 			if (desc->http_query != NULL)
302 				*desc->http_query++ = '\0';
303 
304 			/*
305 			 * Have to allocate the strings because they could
306 			 * be changed independently by the filters later.
307 			 */
308 			if ((desc->http_version =
309 			    strdup(desc->http_version)) == NULL)
310 				goto fail;
311 
312 			if (desc->http_query != NULL &&
313 			    (desc->http_query =
314 			    strdup(desc->http_query)) == NULL)
315 				goto fail;
316 
317 		} else if (desc->http_method != HTTP_METHOD_NONE &&
318 		    strcasecmp("Content-Length", key) == 0) {
319 			if (desc->http_method == HTTP_METHOD_TRACE ||
320 			    desc->http_method == HTTP_METHOD_CONNECT) {
321 				/*
322 				 * These method should not have a body
323 				 * and thus no Content-Length header.
324 				 */
325 				server_abort_http(clt, 400, "malformed");
326 				goto abort;
327 			}
328 
329 			/*
330 			 * Need to read data from the client after the
331 			 * HTTP header.
332 			 * XXX What about non-standard clients not using
333 			 * the carriage return? And some browsers seem to
334 			 * include the line length in the content-length.
335 			 */
336 			clt->clt_toread = strtonum(value, 0, LLONG_MAX,
337 			    &errstr);
338 			if (errstr) {
339 				server_abort_http(clt, 500, errstr);
340 				goto abort;
341 			}
342 			if ((size_t)clt->clt_toread >
343 			    srv_conf->maxrequestbody) {
344 				server_abort_http(clt, 413, NULL);
345 				goto abort;
346 			}
347 		}
348 
349 		if (strcasecmp("Transfer-Encoding", key) == 0 &&
350 		    strcasecmp("chunked", value) == 0)
351 			desc->http_chunked = 1;
352 
353 		if (clt->clt_line != 1) {
354 			if ((hdr = kv_add(&desc->http_headers, key,
355 			    value)) == NULL)
356 				goto fail;
357 
358 			desc->http_lastheader = hdr;
359 		}
360 
361 		free(line);
362 	}
363 	if (clt->clt_done) {
364 		if (desc->http_method == HTTP_METHOD_NONE) {
365 			server_abort_http(clt, 406, "no method");
366 			return;
367 		}
368 
369 		switch (desc->http_method) {
370 		case HTTP_METHOD_CONNECT:
371 			/* Data stream */
372 			clt->clt_toread = TOREAD_UNLIMITED;
373 			bev->readcb = server_read;
374 			break;
375 		case HTTP_METHOD_DELETE:
376 		case HTTP_METHOD_GET:
377 		case HTTP_METHOD_HEAD:
378 		/* WebDAV methods */
379 		case HTTP_METHOD_COPY:
380 		case HTTP_METHOD_MOVE:
381 			clt->clt_toread = 0;
382 			break;
383 		case HTTP_METHOD_OPTIONS:
384 		case HTTP_METHOD_POST:
385 		case HTTP_METHOD_PUT:
386 		case HTTP_METHOD_RESPONSE:
387 		/* WebDAV methods */
388 		case HTTP_METHOD_PROPFIND:
389 		case HTTP_METHOD_PROPPATCH:
390 		case HTTP_METHOD_MKCOL:
391 		case HTTP_METHOD_LOCK:
392 		case HTTP_METHOD_UNLOCK:
393 		case HTTP_METHOD_VERSION_CONTROL:
394 		case HTTP_METHOD_REPORT:
395 		case HTTP_METHOD_CHECKOUT:
396 		case HTTP_METHOD_CHECKIN:
397 		case HTTP_METHOD_UNCHECKOUT:
398 		case HTTP_METHOD_MKWORKSPACE:
399 		case HTTP_METHOD_UPDATE:
400 		case HTTP_METHOD_LABEL:
401 		case HTTP_METHOD_MERGE:
402 		case HTTP_METHOD_BASELINE_CONTROL:
403 		case HTTP_METHOD_MKACTIVITY:
404 		case HTTP_METHOD_ORDERPATCH:
405 		case HTTP_METHOD_ACL:
406 		case HTTP_METHOD_MKREDIRECTREF:
407 		case HTTP_METHOD_UPDATEREDIRECTREF:
408 		case HTTP_METHOD_SEARCH:
409 		case HTTP_METHOD_PATCH:
410 			/* HTTP request payload */
411 			if (clt->clt_toread > 0)
412 				bev->readcb = server_read_httpcontent;
413 
414 			/* Single-pass HTTP body */
415 			if (clt->clt_toread < 0) {
416 				clt->clt_toread = TOREAD_UNLIMITED;
417 				bev->readcb = server_read;
418 			}
419 			break;
420 		default:
421 			server_abort_http(clt, 405, "method not allowed");
422 			return;
423 		}
424 		if (desc->http_chunked) {
425 			/* Chunked transfer encoding */
426 			clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH;
427 			bev->readcb = server_read_httpchunks;
428 		}
429 
430  done:
431 		if (clt->clt_toread != 0)
432 			bufferevent_disable(bev, EV_READ);
433 		server_response(httpd_env, clt);
434 		return;
435 	}
436 	if (clt->clt_done) {
437 		server_close(clt, "done");
438 		return;
439 	}
440 	if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http)
441 		bev->readcb(bev, arg);
442 	bufferevent_enable(bev, EV_READ);
443 	return;
444  fail:
445 	server_abort_http(clt, 500, strerror(errno));
446  abort:
447 	free(line);
448 }
449 
450 void
451 server_read_httpcontent(struct bufferevent *bev, void *arg)
452 {
453 	struct client		*clt = arg;
454 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
455 	size_t			 size;
456 
457 	getmonotime(&clt->clt_tv_last);
458 
459 	size = EVBUFFER_LENGTH(src);
460 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
461 	    clt->clt_id, size, clt->clt_toread);
462 	if (!size)
463 		return;
464 
465 	if (clt->clt_toread > 0) {
466 		/* Read content data */
467 		if ((off_t)size > clt->clt_toread) {
468 			size = clt->clt_toread;
469 			if (fcgi_add_stdin(clt, src) == -1)
470 				goto fail;
471 			clt->clt_toread = 0;
472 		} else {
473 			if (fcgi_add_stdin(clt, src) == -1)
474 				goto fail;
475 			clt->clt_toread -= size;
476 		}
477 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
478 		    size, clt->clt_toread);
479 	}
480 	if (clt->clt_toread == 0) {
481 		fcgi_add_stdin(clt, NULL);
482 		clt->clt_toread = TOREAD_HTTP_HEADER;
483 		bufferevent_disable(bev, EV_READ);
484 		bev->readcb = server_read_http;
485 		return;
486 	}
487 	if (clt->clt_done)
488 		goto done;
489 	if (bev->readcb != server_read_httpcontent)
490 		bev->readcb(bev, arg);
491 
492 	return;
493  done:
494 	return;
495  fail:
496 	server_close(clt, strerror(errno));
497 }
498 
499 void
500 server_read_httpchunks(struct bufferevent *bev, void *arg)
501 {
502 	struct client		*clt = arg;
503 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
504 	char			*line;
505 	long long		 llval;
506 	size_t			 size;
507 
508 	getmonotime(&clt->clt_tv_last);
509 
510 	size = EVBUFFER_LENGTH(src);
511 	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
512 	    clt->clt_id, size, clt->clt_toread);
513 	if (!size)
514 		return;
515 
516 	if (clt->clt_toread > 0) {
517 		/* Read chunk data */
518 		if ((off_t)size > clt->clt_toread) {
519 			size = clt->clt_toread;
520 			if (server_bufferevent_write_chunk(clt, src, size)
521 			    == -1)
522 				goto fail;
523 			clt->clt_toread = 0;
524 		} else {
525 			if (server_bufferevent_write_buffer(clt, src) == -1)
526 				goto fail;
527 			clt->clt_toread -= size;
528 		}
529 		DPRINTF("%s: done, size %lu, to read %lld", __func__,
530 		    size, clt->clt_toread);
531 	}
532 	switch (clt->clt_toread) {
533 	case TOREAD_HTTP_CHUNK_LENGTH:
534 		line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT);
535 		if (line == NULL) {
536 			/* Ignore empty line, continue */
537 			bufferevent_enable(bev, EV_READ);
538 			return;
539 		}
540 		if (strlen(line) == 0) {
541 			free(line);
542 			goto next;
543 		}
544 
545 		/*
546 		 * Read prepended chunk size in hex, ignore the trailer.
547 		 * The returned signed value must not be negative.
548 		 */
549 		if (sscanf(line, "%llx", &llval) != 1 || llval < 0) {
550 			free(line);
551 			server_close(clt, "invalid chunk size");
552 			return;
553 		}
554 
555 		if (server_bufferevent_print(clt, line) == -1 ||
556 		    server_bufferevent_print(clt, "\r\n") == -1) {
557 			free(line);
558 			goto fail;
559 		}
560 		free(line);
561 
562 		if ((clt->clt_toread = llval) == 0) {
563 			DPRINTF("%s: last chunk", __func__);
564 			clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER;
565 		}
566 		break;
567 	case TOREAD_HTTP_CHUNK_TRAILER:
568 		/* Last chunk is 0 bytes followed by trailer and empty line */
569 		line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT);
570 		if (line == NULL) {
571 			/* Ignore empty line, continue */
572 			bufferevent_enable(bev, EV_READ);
573 			return;
574 		}
575 		if (server_bufferevent_print(clt, line) == -1 ||
576 		    server_bufferevent_print(clt, "\r\n") == -1) {
577 			free(line);
578 			goto fail;
579 		}
580 		if (strlen(line) == 0) {
581 			/* Switch to HTTP header mode */
582 			clt->clt_toread = TOREAD_HTTP_HEADER;
583 			bev->readcb = server_read_http;
584 		}
585 		free(line);
586 		break;
587 	case 0:
588 		/* Chunk is terminated by an empty newline */
589 		line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT);
590 		free(line);
591 		if (server_bufferevent_print(clt, "\r\n") == -1)
592 			goto fail;
593 		clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH;
594 		break;
595 	}
596 
597  next:
598 	if (clt->clt_done)
599 		goto done;
600 	if (EVBUFFER_LENGTH(src))
601 		bev->readcb(bev, arg);
602 	bufferevent_enable(bev, EV_READ);
603 	return;
604 
605  done:
606 	server_close(clt, "last http chunk read (done)");
607 	return;
608  fail:
609 	server_close(clt, strerror(errno));
610 }
611 
612 void
613 server_reset_http(struct client *clt)
614 {
615 	struct server		*srv = clt->clt_srv;
616 
617 	server_log(clt, NULL);
618 
619 	server_httpdesc_free(clt->clt_descreq);
620 	server_httpdesc_free(clt->clt_descresp);
621 	clt->clt_headerlen = 0;
622 	clt->clt_line = 0;
623 	clt->clt_done = 0;
624 	clt->clt_chunk = 0;
625 	free(clt->clt_remote_user);
626 	clt->clt_remote_user = NULL;
627 	clt->clt_bev->readcb = server_read_http;
628 	clt->clt_srv_conf = &srv->srv_conf;
629 	str_match_free(&clt->clt_srv_match);
630 }
631 
632 ssize_t
633 server_http_time(time_t t, char *tmbuf, size_t len)
634 {
635 	struct tm		 tm;
636 
637 	/* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
638 	if (t == -1 || gmtime_r(&t, &tm) == NULL)
639 		return (-1);
640 	else
641 		return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
642 }
643 
644 const char *
645 server_http_host(struct sockaddr_storage *ss, char *buf, size_t len)
646 {
647 	char		hbuf[HOST_NAME_MAX+1];
648 	in_port_t	port;
649 
650 	if (print_host(ss, buf, len) == NULL)
651 		return (NULL);
652 
653 	port = ntohs(server_socket_getport(ss));
654 	if (port == HTTP_PORT)
655 		return (buf);
656 
657 	switch (ss->ss_family) {
658 	case AF_INET:
659 		if ((size_t)snprintf(hbuf, sizeof(hbuf),
660 		    "%s:%u", buf, port) >= sizeof(hbuf))
661 			return (NULL);
662 		break;
663 	case AF_INET6:
664 		if ((size_t)snprintf(hbuf, sizeof(hbuf),
665 		    "[%s]:%u", buf, port) >= sizeof(hbuf))
666 			return (NULL);
667 		break;
668 	}
669 
670 	if (strlcpy(buf, hbuf, len) >= len)
671 		return (NULL);
672 
673 	return (buf);
674 }
675 
676 char *
677 server_http_parsehost(char *host, char *buf, size_t len, int *portval)
678 {
679 	char		*start, *end, *port;
680 	const char	*errstr = NULL;
681 
682 	if (strlcpy(buf, host, len) >= len) {
683 		log_debug("%s: host name too long", __func__);
684 		return (NULL);
685 	}
686 
687 	start = buf;
688 	end = port = NULL;
689 
690 	if (*start == '[' && (end = strchr(start, ']')) != NULL) {
691 		/* Address enclosed in [] with port, eg. [2001:db8::1]:80 */
692 		start++;
693 		*end++ = '\0';
694 		if ((port = strchr(end, ':')) == NULL || *port == '\0')
695 			port = NULL;
696 		else
697 			port++;
698 		memmove(buf, start, strlen(start) + 1);
699 	} else if ((end = strchr(start, ':')) != NULL) {
700 		/* Name or address with port, eg. www.example.com:80 */
701 		*end++ = '\0';
702 		port = end;
703 	} else {
704 		/* Name or address with default port, eg. www.example.com */
705 		port = NULL;
706 	}
707 
708 	if (port != NULL) {
709 		/* Save the requested port */
710 		*portval = strtonum(port, 0, 0xffff, &errstr);
711 		if (errstr != NULL) {
712 			log_debug("%s: invalid port: %s", __func__,
713 			    strerror(errno));
714 			return (NULL);
715 		}
716 		*portval = htons(*portval);
717 	} else {
718 		/* Port not given, indicate the default port */
719 		*portval = -1;
720 	}
721 
722 	return (start);
723 }
724 
725 void
726 server_abort_http(struct client *clt, unsigned int code, const char *msg)
727 {
728 	struct server_config	*srv_conf = clt->clt_srv_conf;
729 	struct bufferevent	*bev = clt->clt_bev;
730 	struct http_descriptor	*desc = clt->clt_descreq;
731 	const char		*httperr = NULL, *style;
732 	char			*httpmsg, *body = NULL, *extraheader = NULL;
733 	char			 tmbuf[32], hbuf[128], *hstsheader = NULL;
734 	char			 buf[IBUF_READ_SIZE];
735 	char			*escapedmsg = NULL;
736 	int			 bodylen;
737 
738 	if (code == 0) {
739 		server_close(clt, "dropped");
740 		return;
741 	}
742 
743 	if ((httperr = server_httperror_byid(code)) == NULL)
744 		httperr = "Unknown Error";
745 
746 	if (bev == NULL)
747 		goto done;
748 
749 	if (server_log_http(clt, code, 0) == -1)
750 		goto done;
751 
752 	/* Some system information */
753 	if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL)
754 		goto done;
755 
756 	if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0)
757 		goto done;
758 
759 	/* Do not send details of the Internal Server Error */
760 	switch (code) {
761 	case 301:
762 	case 302:
763 	case 303:
764 		if (msg == NULL)
765 			break;
766 		memset(buf, 0, sizeof(buf));
767 		if (server_expand_http(clt, msg, buf, sizeof(buf)) == NULL)
768 			goto done;
769 		if (asprintf(&extraheader, "Location: %s\r\n", buf) == -1) {
770 			code = 500;
771 			extraheader = NULL;
772 		}
773 		msg = buf;
774 		break;
775 	case 401:
776 		if (stravis(&escapedmsg, msg, VIS_DQ) == -1) {
777 			code = 500;
778 			extraheader = NULL;
779 		} else if (asprintf(&extraheader,
780 		    "WWW-Authenticate: Basic realm=\"%s\"\r\n", escapedmsg)
781 		    == -1) {
782 			code = 500;
783 			extraheader = NULL;
784 		}
785 		break;
786 	case 416:
787 		if (asprintf(&extraheader,
788 		    "Content-Range: %s\r\n", msg) == -1) {
789 			code = 500;
790 			extraheader = NULL;
791 		}
792 		break;
793 	default:
794 		/*
795 		 * Do not send details of the error.  Traditionally,
796 		 * web servers responsed with the request path on 40x
797 		 * errors which could be abused to inject JavaScript etc.
798 		 * Instead of sanitizing the path here, we just don't
799 		 * reprint it.
800 		 */
801 		break;
802 	}
803 
804 	free(escapedmsg);
805 
806 	/* A CSS stylesheet allows minimal customization by the user */
807 	style = "body { background-color: white; color: black; font-family: "
808 	    "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n"
809 	    "hr { border: 0; border-bottom: 1px dashed; }\n";
810 
811 	/* Generate simple HTML error document */
812 	if ((bodylen = asprintf(&body,
813 	    "<!DOCTYPE html>\n"
814 	    "<html>\n"
815 	    "<head>\n"
816 	    "<meta http-equiv=\"Content-Type\" content=\"text/html; "
817 	    "charset=utf-8\"/>\n"
818 	    "<title>%03d %s</title>\n"
819 	    "<style type=\"text/css\"><!--\n%s\n--></style>\n"
820 	    "</head>\n"
821 	    "<body>\n"
822 	    "<h1>%03d %s</h1>\n"
823 	    "<hr>\n<address>%s</address>\n"
824 	    "</body>\n"
825 	    "</html>\n",
826 	    code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) {
827 		body = NULL;
828 		goto done;
829 	}
830 
831 	if (srv_conf->flags & SRVFLAG_SERVER_HSTS) {
832 		if (asprintf(&hstsheader, "Strict-Transport-Security: "
833 		    "max-age=%d%s%s\r\n", srv_conf->hsts_max_age,
834 		    srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ?
835 		    "; includeSubDomains" : "",
836 		    srv_conf->hsts_flags & HSTSFLAG_PRELOAD ?
837 		    "; preload" : "") == -1) {
838 			hstsheader = NULL;
839 			goto done;
840 		}
841 	}
842 
843 	/* Add basic HTTP headers */
844 	if (asprintf(&httpmsg,
845 	    "HTTP/1.0 %03d %s\r\n"
846 	    "Date: %s\r\n"
847 	    "Server: %s\r\n"
848 	    "Connection: close\r\n"
849 	    "Content-Type: text/html\r\n"
850 	    "Content-Length: %d\r\n"
851 	    "%s"
852 	    "%s"
853 	    "\r\n"
854 	    "%s",
855 	    code, httperr, tmbuf, HTTPD_SERVERNAME, bodylen,
856 	    extraheader == NULL ? "" : extraheader,
857 	    hstsheader == NULL ? "" : hstsheader,
858 	    desc->http_method == HTTP_METHOD_HEAD ? "" : body) == -1)
859 		goto done;
860 
861 	/* Dump the message without checking for success */
862 	server_dump(clt, httpmsg, strlen(httpmsg));
863 	free(httpmsg);
864 
865  done:
866 	free(body);
867 	free(extraheader);
868 	free(hstsheader);
869 	if (msg == NULL)
870 		msg = "\"\"";
871 	if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) {
872 		server_close(clt, msg);
873 	} else {
874 		server_close(clt, httpmsg);
875 		free(httpmsg);
876 	}
877 }
878 
879 void
880 server_close_http(struct client *clt)
881 {
882 	struct http_descriptor *desc;
883 
884 	desc = clt->clt_descreq;
885 	server_httpdesc_free(desc);
886 	free(desc);
887 	clt->clt_descreq = NULL;
888 
889 	desc = clt->clt_descresp;
890 	server_httpdesc_free(desc);
891 	free(desc);
892 	clt->clt_descresp = NULL;
893 	free(clt->clt_remote_user);
894 	clt->clt_remote_user = NULL;
895 
896 	str_match_free(&clt->clt_srv_match);
897 }
898 
899 char *
900 server_expand_http(struct client *clt, const char *val, char *buf,
901     size_t len)
902 {
903 	struct http_descriptor	*desc = clt->clt_descreq;
904 	struct server_config	*srv_conf = clt->clt_srv_conf;
905 	char			 ibuf[128], *str, *path, *query;
906 	const char		*errstr = NULL, *p;
907 	size_t			 size;
908 	int			 n, ret;
909 
910 	if (strlcpy(buf, val, len) >= len)
911 		return (NULL);
912 
913 	/* Find previously matched substrings by index */
914 	for (p = val; clt->clt_srv_match.sm_nmatch &&
915 	    (p = strstr(p, "%")) != NULL; p++) {
916 		if (!isdigit((unsigned char)*(p + 1)))
917 			continue;
918 
919 		/* Copy number, leading '%' char and add trailing \0 */
920 		size = strspn(p + 1, "0123456789") + 2;
921 		if (size  >= sizeof(ibuf))
922 			return (NULL);
923 		(void)strlcpy(ibuf, p, size);
924 		n = strtonum(ibuf + 1, 0,
925 		    clt->clt_srv_match.sm_nmatch - 1, &errstr);
926 		if (errstr != NULL)
927 			return (NULL);
928 
929 		/* Expand variable with matched value */
930 		if ((str = url_encode(clt->clt_srv_match.sm_match[n])) == NULL)
931 			return (NULL);
932 		ret = expand_string(buf, len, ibuf, str);
933 		free(str);
934 		if (ret != 0)
935 			return (NULL);
936 	}
937 	if (strstr(val, "$DOCUMENT_URI") != NULL) {
938 		if ((path = url_encode(desc->http_path)) == NULL)
939 			return (NULL);
940 		ret = expand_string(buf, len, "$DOCUMENT_URI", path);
941 		free(path);
942 		if (ret != 0)
943 			return (NULL);
944 	}
945 	if (strstr(val, "$QUERY_STRING") != NULL) {
946 		if (desc->http_query == NULL) {
947 			ret = expand_string(buf, len, "$QUERY_STRING", "");
948 		} else {
949 			if ((query = url_encode(desc->http_query)) == NULL)
950 				return (NULL);
951 			ret = expand_string(buf, len, "$QUERY_STRING", query);
952 			free(query);
953 		}
954 		if (ret != 0)
955 			return (NULL);
956 	}
957 	if (strstr(val, "$REMOTE_") != NULL) {
958 		if (strstr(val, "$REMOTE_ADDR") != NULL) {
959 			if (print_host(&clt->clt_ss,
960 			    ibuf, sizeof(ibuf)) == NULL)
961 				return (NULL);
962 			if (expand_string(buf, len,
963 			    "$REMOTE_ADDR", ibuf) != 0)
964 				return (NULL);
965 		}
966 		if (strstr(val, "$REMOTE_PORT") != NULL) {
967 			snprintf(ibuf, sizeof(ibuf),
968 			    "%u", ntohs(clt->clt_port));
969 			if (expand_string(buf, len,
970 			    "$REMOTE_PORT", ibuf) != 0)
971 				return (NULL);
972 		}
973 		if (strstr(val, "$REMOTE_USER") != NULL) {
974 			if ((srv_conf->flags & SRVFLAG_AUTH) &&
975 			    clt->clt_remote_user != NULL) {
976 				if ((str = url_encode(clt->clt_remote_user))
977 				    == NULL)
978 					return (NULL);
979 			} else
980 				str = strdup("");
981 			ret = expand_string(buf, len, "$REMOTE_USER", str);
982 			free(str);
983 			if (ret != 0)
984 				return (NULL);
985 		}
986 	}
987 	if (strstr(val, "$REQUEST_URI") != NULL) {
988 		if ((path = url_encode(desc->http_path)) == NULL)
989 			return (NULL);
990 		if (desc->http_query == NULL) {
991 			str = path;
992 		} else {
993 			if ((query = url_encode(desc->http_query)) == NULL) {
994 				free(path);
995 				return (NULL);
996 			}
997 			ret = asprintf(&str, "%s?%s", path, query);
998 			free(path);
999 			free(query);
1000 			if (ret == -1)
1001 				return (NULL);
1002 		}
1003 
1004 		ret = expand_string(buf, len, "$REQUEST_URI", str);
1005 		free(str);
1006 		if (ret != 0)
1007 			return (NULL);
1008 	}
1009 	if (strstr(val, "$SERVER_") != NULL) {
1010 		if (strstr(val, "$SERVER_ADDR") != NULL) {
1011 			if (print_host(&srv_conf->ss,
1012 			    ibuf, sizeof(ibuf)) == NULL)
1013 				return (NULL);
1014 			if (expand_string(buf, len,
1015 			    "$SERVER_ADDR", ibuf) != 0)
1016 				return (NULL);
1017 		}
1018 		if (strstr(val, "$SERVER_PORT") != NULL) {
1019 			snprintf(ibuf, sizeof(ibuf), "%u",
1020 			    ntohs(srv_conf->port));
1021 			if (expand_string(buf, len,
1022 			    "$SERVER_PORT", ibuf) != 0)
1023 				return (NULL);
1024 		}
1025 		if (strstr(val, "$SERVER_NAME") != NULL) {
1026 			if ((str = url_encode(srv_conf->name))
1027 			     == NULL)
1028 				return (NULL);
1029 			ret = expand_string(buf, len, "$SERVER_NAME", str);
1030 			free(str);
1031 			if (ret != 0)
1032 				return (NULL);
1033 		}
1034 	}
1035 
1036 	return (buf);
1037 }
1038 
1039 int
1040 server_response(struct httpd *httpd, struct client *clt)
1041 {
1042 	char			 path[PATH_MAX];
1043 	char			 hostname[HOST_NAME_MAX+1];
1044 	struct http_descriptor	*desc = clt->clt_descreq;
1045 	struct http_descriptor	*resp = clt->clt_descresp;
1046 	struct server		*srv = clt->clt_srv;
1047 	struct server_config	*srv_conf = &srv->srv_conf;
1048 	struct kv		*kv, key, *host;
1049 	struct str_find		 sm;
1050 	int			 portval = -1, ret;
1051 	char			*hostval;
1052 	const char		*errstr = NULL;
1053 
1054 	/* Canonicalize the request path */
1055 	if (desc->http_path == NULL ||
1056 	    url_decode(desc->http_path) == NULL ||
1057 	    canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
1058 		goto fail;
1059 	free(desc->http_path);
1060 	if ((desc->http_path = strdup(path)) == NULL)
1061 		goto fail;
1062 
1063 	key.kv_key = "Host";
1064 	if ((host = kv_find(&desc->http_headers, &key)) != NULL &&
1065 	    host->kv_value == NULL)
1066 		host = NULL;
1067 
1068 	if (strcmp(desc->http_version, "HTTP/1.1") == 0) {
1069 		/* Host header is mandatory */
1070 		if (host == NULL)
1071 			goto fail;
1072 
1073 		/* Is the connection persistent? */
1074 		key.kv_key = "Connection";
1075 		if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
1076 		    strcasecmp("close", kv->kv_value) == 0)
1077 			clt->clt_persist = 0;
1078 		else
1079 			clt->clt_persist++;
1080 	} else {
1081 		/* Is the connection persistent? */
1082 		key.kv_key = "Connection";
1083 		if ((kv = kv_find(&desc->http_headers, &key)) != NULL &&
1084 		    strcasecmp("keep-alive", kv->kv_value) == 0)
1085 			clt->clt_persist++;
1086 		else
1087 			clt->clt_persist = 0;
1088 	}
1089 
1090 	if (clt->clt_persist >= srv_conf->maxrequests)
1091 		clt->clt_persist = 0;
1092 
1093 	/*
1094 	 * Do we have a Host header and matching configuration?
1095 	 * XXX the Host can also appear in the URL path.
1096 	 */
1097 	if (host != NULL) {
1098 		if ((hostval = server_http_parsehost(host->kv_value,
1099 		    hostname, sizeof(hostname), &portval)) == NULL)
1100 			goto fail;
1101 
1102 		TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
1103 #ifdef DEBUG
1104 			if ((srv_conf->flags & SRVFLAG_LOCATION) == 0) {
1105 				DPRINTF("%s: virtual host \"%s:%u\""
1106 				    " host \"%s\" (\"%s\")",
1107 				    __func__, srv_conf->name,
1108 				    ntohs(srv_conf->port), host->kv_value,
1109 				    hostname);
1110 			}
1111 #endif
1112 			if (srv_conf->flags & SRVFLAG_LOCATION)
1113 				continue;
1114 			else if (srv_conf->flags & SRVFLAG_SERVER_MATCH) {
1115 				str_find(hostname, srv_conf->name,
1116 				    &sm, 1, &errstr);
1117 				ret = errstr == NULL ? 0 : -1;
1118 			} else {
1119 				ret = fnmatch(srv_conf->name,
1120 				    hostname, FNM_CASEFOLD);
1121 			}
1122 			if (ret == 0 &&
1123 			    (portval == -1 ||
1124 			    (portval != -1 && portval == srv_conf->port))) {
1125 				/* Replace host configuration */
1126 				clt->clt_srv_conf = srv_conf;
1127 				srv_conf = NULL;
1128 				break;
1129 			}
1130 		}
1131 	}
1132 
1133 	if (srv_conf != NULL) {
1134 		/* Use the actual server IP address */
1135 		if (server_http_host(&clt->clt_srv_ss, hostname,
1136 		    sizeof(hostname)) == NULL)
1137 			goto fail;
1138 	} else {
1139 		/* Host header was valid and found */
1140 		if (strlcpy(hostname, host->kv_value, sizeof(hostname)) >=
1141 		    sizeof(hostname))
1142 			goto fail;
1143 		srv_conf = clt->clt_srv_conf;
1144 	}
1145 
1146 	if ((desc->http_host = strdup(hostname)) == NULL)
1147 		goto fail;
1148 
1149 	/* Now fill in the mandatory parts of the response descriptor */
1150 	resp->http_method = desc->http_method;
1151 	if ((resp->http_version = strdup(desc->http_version)) == NULL)
1152 		goto fail;
1153 
1154 	/* Now search for the location */
1155 	srv_conf = server_getlocation(clt, desc->http_path);
1156 
1157 	if (srv_conf->flags & SRVFLAG_BLOCK) {
1158 		server_abort_http(clt, srv_conf->return_code,
1159 		    srv_conf->return_uri);
1160 		return (-1);
1161 	} else if (srv_conf->flags & SRVFLAG_AUTH &&
1162 	    server_http_authenticate(srv_conf, clt) == -1) {
1163 		server_abort_http(clt, 401, srv_conf->auth_realm);
1164 		return (-1);
1165 	} else
1166 		return (server_file(httpd, clt));
1167  fail:
1168 	server_abort_http(clt, 400, "bad request");
1169 	return (-1);
1170 }
1171 
1172 const char *
1173 server_root_strip(const char *path, int n)
1174 {
1175 	const char *p;
1176 
1177 	/* Strip strip leading directories. Leading '/' is ignored. */
1178 	for (; n > 0 && *path != '\0'; n--)
1179 		if ((p = strchr(++path, '/')) == NULL)
1180 			path = strchr(path, '\0');
1181 		else
1182 			path = p;
1183 
1184 	return (path);
1185 }
1186 
1187 struct server_config *
1188 server_getlocation(struct client *clt, const char *path)
1189 {
1190 	struct server		*srv = clt->clt_srv;
1191 	struct server_config	*srv_conf = clt->clt_srv_conf, *location;
1192 	const char		*errstr = NULL;
1193 	int			 ret;
1194 
1195 	/* Now search for the location */
1196 	TAILQ_FOREACH(location, &srv->srv_hosts, entry) {
1197 #ifdef DEBUG
1198 		if (location->flags & SRVFLAG_LOCATION) {
1199 			DPRINTF("%s: location \"%s\" path \"%s\"",
1200 			    __func__, location->location, path);
1201 		}
1202 #endif
1203 		if ((location->flags & SRVFLAG_LOCATION) &&
1204 		    location->parent_id == srv_conf->parent_id) {
1205 			errstr = NULL;
1206 			if (location->flags & SRVFLAG_LOCATION_MATCH) {
1207 				ret = str_match(path, location->location,
1208 				    &clt->clt_srv_match, &errstr);
1209 			} else {
1210 				ret = fnmatch(location->location,
1211 				    path, FNM_CASEFOLD);
1212 			}
1213 			if (ret == 0 && errstr == NULL) {
1214 				/* Replace host configuration */
1215 				clt->clt_srv_conf = srv_conf = location;
1216 				break;
1217 			}
1218 		}
1219 	}
1220 
1221 	return (srv_conf);
1222 }
1223 
1224 int
1225 server_response_http(struct client *clt, unsigned int code,
1226     struct media_type *media, off_t size, time_t mtime)
1227 {
1228 	struct server_config	*srv_conf = clt->clt_srv_conf;
1229 	struct http_descriptor	*desc = clt->clt_descreq;
1230 	struct http_descriptor	*resp = clt->clt_descresp;
1231 	const char		*error;
1232 	struct kv		*ct, *cl;
1233 	char			 tmbuf[32];
1234 
1235 	if (desc == NULL || media == NULL ||
1236 	    (error = server_httperror_byid(code)) == NULL)
1237 		return (-1);
1238 
1239 	if (server_log_http(clt, code, size) == -1)
1240 		return (-1);
1241 
1242 	/* Add error codes */
1243 	if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 ||
1244 	    kv_set(&resp->http_pathquery, "%s", error) == -1)
1245 		return (-1);
1246 
1247 	/* Add headers */
1248 	if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL)
1249 		return (-1);
1250 
1251 	/* Is it a persistent connection? */
1252 	if (clt->clt_persist) {
1253 		if (kv_add(&resp->http_headers,
1254 		    "Connection", "keep-alive") == NULL)
1255 			return (-1);
1256 	} else if (kv_add(&resp->http_headers, "Connection", "close") == NULL)
1257 		return (-1);
1258 
1259 	/* Set media type */
1260 	if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL ||
1261 	    kv_set(ct, "%s/%s", media->media_type, media->media_subtype) == -1)
1262 		return (-1);
1263 
1264 	/* Set content length, if specified */
1265 	if ((cl =
1266 	    kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL ||
1267 	    kv_set(cl, "%lld", (long long)size) == -1)
1268 		return (-1);
1269 
1270 	/* Set last modification time */
1271 	if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 ||
1272 	    kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL)
1273 		return (-1);
1274 
1275 	/* HSTS header */
1276 	if (srv_conf->flags & SRVFLAG_SERVER_HSTS) {
1277 		if ((cl =
1278 		    kv_add(&resp->http_headers, "Strict-Transport-Security",
1279 		    NULL)) == NULL ||
1280 		    kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age,
1281 		    srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ?
1282 		    "; includeSubDomains" : "",
1283 		    srv_conf->hsts_flags & HSTSFLAG_PRELOAD ?
1284 		    "; preload" : "") == -1)
1285 			return (-1);
1286 	}
1287 
1288 	/* Date header is mandatory and should be added as late as possible */
1289 	if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 ||
1290 	    kv_add(&resp->http_headers, "Date", tmbuf) == NULL)
1291 		return (-1);
1292 
1293 	/* Write completed header */
1294 	if (server_writeresponse_http(clt) == -1 ||
1295 	    server_bufferevent_print(clt, "\r\n") == -1 ||
1296 	    server_headers(clt, resp, server_writeheader_http, NULL) == -1 ||
1297 	    server_bufferevent_print(clt, "\r\n") == -1)
1298 		return (-1);
1299 
1300 	if (size == 0 || resp->http_method == HTTP_METHOD_HEAD) {
1301 		bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
1302 		if (clt->clt_persist)
1303 			clt->clt_toread = TOREAD_HTTP_HEADER;
1304 		else
1305 			clt->clt_toread = TOREAD_HTTP_NONE;
1306 		clt->clt_done = 0;
1307 		return (0);
1308 	}
1309 
1310 	return (1);
1311 }
1312 
1313 int
1314 server_writeresponse_http(struct client *clt)
1315 {
1316 	struct http_descriptor	*desc = clt->clt_descresp;
1317 
1318 	DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
1319 	    desc->http_rescode, desc->http_resmesg);
1320 
1321 	if (server_bufferevent_print(clt, desc->http_version) == -1 ||
1322 	    server_bufferevent_print(clt, " ") == -1 ||
1323 	    server_bufferevent_print(clt, desc->http_rescode) == -1 ||
1324 	    server_bufferevent_print(clt, " ") == -1 ||
1325 	    server_bufferevent_print(clt, desc->http_resmesg) == -1)
1326 		return (-1);
1327 
1328 	return (0);
1329 }
1330 
1331 int
1332 server_writeheader_http(struct client *clt, struct kv *hdr, void *arg)
1333 {
1334 	char			*ptr;
1335 	const char		*key;
1336 
1337 	if (hdr->kv_flags & KV_FLAG_INVALID)
1338 		return (0);
1339 
1340 	/* The key might have been updated in the parent */
1341 	if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
1342 		key = hdr->kv_parent->kv_key;
1343 	else
1344 		key = hdr->kv_key;
1345 
1346 	ptr = hdr->kv_value;
1347 	if (server_bufferevent_print(clt, key) == -1 ||
1348 	    (ptr != NULL &&
1349 	    (server_bufferevent_print(clt, ": ") == -1 ||
1350 	    server_bufferevent_print(clt, ptr) == -1 ||
1351 	    server_bufferevent_print(clt, "\r\n") == -1)))
1352 		return (-1);
1353 	DPRINTF("%s: %s: %s", __func__, key,
1354 	    hdr->kv_value == NULL ? "" : hdr->kv_value);
1355 
1356 	return (0);
1357 }
1358 
1359 int
1360 server_headers(struct client *clt, void *descp,
1361     int (*hdr_cb)(struct client *, struct kv *, void *), void *arg)
1362 {
1363 	struct kv		*hdr, *kv;
1364 	struct http_descriptor	*desc = descp;
1365 
1366 	RB_FOREACH(hdr, kvtree, &desc->http_headers) {
1367 		if ((hdr_cb)(clt, hdr, arg) == -1)
1368 			return (-1);
1369 		TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) {
1370 			if ((hdr_cb)(clt, kv, arg) == -1)
1371 				return (-1);
1372 		}
1373 	}
1374 
1375 	return (0);
1376 }
1377 
1378 enum httpmethod
1379 server_httpmethod_byname(const char *name)
1380 {
1381 	enum httpmethod		 id = HTTP_METHOD_NONE;
1382 	struct http_method	 method, *res = NULL;
1383 
1384 	/* Set up key */
1385 	method.method_name = name;
1386 
1387 	if ((res = bsearch(&method, http_methods,
1388 	    sizeof(http_methods) / sizeof(http_methods[0]) - 1,
1389 	    sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL)
1390 		id = res->method_id;
1391 
1392 	return (id);
1393 }
1394 
1395 const char *
1396 server_httpmethod_byid(unsigned int id)
1397 {
1398 	const char	*name = "<UNKNOWN>";
1399 	int		 i;
1400 
1401 	for (i = 0; http_methods[i].method_name != NULL; i++) {
1402 		if (http_methods[i].method_id == id) {
1403 			name = http_methods[i].method_name;
1404 			break;
1405 		}
1406 	}
1407 
1408 	return (name);
1409 }
1410 
1411 static int
1412 server_httpmethod_cmp(const void *a, const void *b)
1413 {
1414 	const struct http_method *ma = a;
1415 	const struct http_method *mb = b;
1416 
1417 	/*
1418 	 * RFC 2616 section 5.1.1 says that the method is case
1419 	 * sensitive so we don't do a strcasecmp here.
1420 	 */
1421 	return (strcmp(ma->method_name, mb->method_name));
1422 }
1423 
1424 const char *
1425 server_httperror_byid(unsigned int id)
1426 {
1427 	struct http_error	 error, *res;
1428 
1429 	/* Set up key */
1430 	error.error_code = (int)id;
1431 
1432 	if ((res = bsearch(&error, http_errors,
1433 	    sizeof(http_errors) / sizeof(http_errors[0]) - 1,
1434 	    sizeof(http_errors[0]), server_httperror_cmp)) != NULL)
1435 		return (res->error_name);
1436 
1437 	return (NULL);
1438 }
1439 
1440 static int
1441 server_httperror_cmp(const void *a, const void *b)
1442 {
1443 	const struct http_error *ea = a;
1444 	const struct http_error *eb = b;
1445 	return (ea->error_code - eb->error_code);
1446 }
1447 
1448 int
1449 server_log_http(struct client *clt, unsigned int code, size_t len)
1450 {
1451 	static char		 tstamp[64];
1452 	static char		 ip[INET6_ADDRSTRLEN];
1453 	time_t			 t;
1454 	struct kv		 key, *agent, *referrer;
1455 	struct tm		*tm;
1456 	struct server_config	*srv_conf;
1457 	struct http_descriptor	*desc;
1458 	int			 ret = -1;
1459 	char			*user = NULL;
1460 	char			*path = NULL;
1461 	char			*query = NULL;
1462 	char			*version = NULL;
1463 	char			*referrer_v = NULL;
1464 	char			*agent_v = NULL;
1465 
1466 	if ((srv_conf = clt->clt_srv_conf) == NULL)
1467 		return (-1);
1468 	if ((srv_conf->flags & SRVFLAG_LOG) == 0)
1469 		return (0);
1470 	if ((desc = clt->clt_descreq) == NULL)
1471 		return (-1);
1472 
1473 	if ((t = time(NULL)) == -1)
1474 		return (-1);
1475 	if ((tm = localtime(&t)) == NULL)
1476 		return (-1);
1477 	if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0)
1478 		return (-1);
1479 
1480 	if (print_host(&clt->clt_ss, ip, sizeof(ip)) == NULL)
1481 		return (-1);
1482 
1483 	/*
1484 	 * For details on common log format, see:
1485 	 * https://httpd.apache.org/docs/current/mod/mod_log_config.html
1486 	 *
1487 	 * httpd's format is similar to these Apache LogFormats:
1488 	 * "%v %h %l %u %t \"%r\" %>s %B"
1489 	 * "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-agent}i\""
1490 	 */
1491 	switch (srv_conf->logformat) {
1492 	case LOG_FORMAT_COMMON:
1493 		/* Use vis to encode input values from the header */
1494 		if (clt->clt_remote_user &&
1495 		    stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1)
1496 			goto done;
1497 		if (desc->http_version &&
1498 		    stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1)
1499 			goto done;
1500 
1501 		/* The following should be URL-encoded */
1502 		if (desc->http_path &&
1503 		    (path = url_encode(desc->http_path)) == NULL)
1504 			goto done;
1505 		if (desc->http_query &&
1506 		    (query = url_encode(desc->http_query)) == NULL)
1507 			goto done;
1508 
1509 		ret = evbuffer_add_printf(clt->clt_log,
1510 		    "%s %s - %s [%s] \"%s %s%s%s%s%s\" %03d %zu\n",
1511 		    srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" :
1512 		    user, tstamp,
1513 		    server_httpmethod_byid(desc->http_method),
1514 		    desc->http_path == NULL ? "" : path,
1515 		    desc->http_query == NULL ? "" : "?",
1516 		    desc->http_query == NULL ? "" : query,
1517 		    desc->http_version == NULL ? "" : " ",
1518 		    desc->http_version == NULL ? "" : version,
1519 		    code, len);
1520 
1521 		break;
1522 
1523 	case LOG_FORMAT_COMBINED:
1524 		key.kv_key = "Referer"; /* sic */
1525 		if ((referrer = kv_find(&desc->http_headers, &key)) != NULL &&
1526 		    referrer->kv_value == NULL)
1527 			referrer = NULL;
1528 
1529 		key.kv_key = "User-Agent";
1530 		if ((agent = kv_find(&desc->http_headers, &key)) != NULL &&
1531 		    agent->kv_value == NULL)
1532 			agent = NULL;
1533 
1534 		/* Use vis to encode input values from the header */
1535 		if (clt->clt_remote_user &&
1536 		    stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1)
1537 			goto done;
1538 		if (desc->http_version &&
1539 		    stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1)
1540 			goto done;
1541 		if (agent &&
1542 		    stravis(&agent_v, agent->kv_value, HTTPD_LOGVIS) == -1)
1543 			goto done;
1544 
1545 		/* The following should be URL-encoded */
1546 		if (desc->http_path &&
1547 		    (path = url_encode(desc->http_path)) == NULL)
1548 			goto done;
1549 		if (desc->http_query &&
1550 		    (query = url_encode(desc->http_query)) == NULL)
1551 			goto done;
1552 		if (referrer &&
1553 		    (referrer_v = url_encode(referrer->kv_value)) == NULL)
1554 			goto done;
1555 
1556 		ret = evbuffer_add_printf(clt->clt_log,
1557 		    "%s %s - %s [%s] \"%s %s%s%s%s%s\""
1558 		    " %03d %zu \"%s\" \"%s\"\n",
1559 		    srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" :
1560 		    user, tstamp,
1561 		    server_httpmethod_byid(desc->http_method),
1562 		    desc->http_path == NULL ? "" : path,
1563 		    desc->http_query == NULL ? "" : "?",
1564 		    desc->http_query == NULL ? "" : query,
1565 		    desc->http_version == NULL ? "" : " ",
1566 		    desc->http_version == NULL ? "" : version,
1567 		    code, len,
1568 		    referrer == NULL ? "" : referrer_v,
1569 		    agent == NULL ? "" : agent_v);
1570 
1571 		break;
1572 
1573 	case LOG_FORMAT_CONNECTION:
1574 		/* URL-encode the path */
1575 		if (desc->http_path &&
1576 		    (path = url_encode(desc->http_path)) == NULL)
1577 			goto done;
1578 
1579 		ret = evbuffer_add_printf(clt->clt_log, " [%s]",
1580 		    desc->http_path == NULL ? "" : path);
1581 
1582 		break;
1583 	}
1584 
1585 done:
1586 	free(user);
1587 	free(path);
1588 	free(query);
1589 	free(version);
1590 	free(referrer_v);
1591 	free(agent_v);
1592 
1593 	return (ret);
1594 }
1595