GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/httpd/server_http.c Lines: 0 837 0.0 %
Date: 2017-11-13 Branches: 0 648 0.0 %

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