Bug Summary

File:src/usr.sbin/httpd/server_http.c
Warning:line 529, column 4
Value stored to 'size' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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