1*95d80e7aSjung /* $OpenBSD: server_http.c,v 1.107 2016/05/22 19:20:03 jung Exp $ */ 2b7b6a941Sreyk 3b7b6a941Sreyk /* 405c2c945Sreyk * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> 5b7b6a941Sreyk * 6b7b6a941Sreyk * Permission to use, copy, modify, and distribute this software for any 7b7b6a941Sreyk * purpose with or without fee is hereby granted, provided that the above 8b7b6a941Sreyk * copyright notice and this permission notice appear in all copies. 9b7b6a941Sreyk * 10b7b6a941Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11b7b6a941Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12b7b6a941Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13b7b6a941Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14b7b6a941Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15b7b6a941Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16b7b6a941Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17b7b6a941Sreyk */ 18b7b6a941Sreyk 19b7b6a941Sreyk #include <sys/types.h> 20b7b6a941Sreyk #include <sys/queue.h> 21b7b6a941Sreyk #include <sys/socket.h> 22b7b6a941Sreyk #include <sys/tree.h> 23b7b6a941Sreyk 24b7b6a941Sreyk #include <netinet/in.h> 2586f952e4Sreyk #include <arpa/inet.h> 26b7b6a941Sreyk 27b7b6a941Sreyk #include <errno.h> 28b7b6a941Sreyk #include <stdlib.h> 29b7b6a941Sreyk #include <string.h> 30b7b6a941Sreyk #include <unistd.h> 31b9fc9a72Sderaadt #include <limits.h> 3259355b5aSreyk #include <fnmatch.h> 33b7b6a941Sreyk #include <stdio.h> 3486f952e4Sreyk #include <time.h> 35e286121aSflorian #include <resolv.h> 36b7b6a941Sreyk #include <event.h> 3759355b5aSreyk #include <ctype.h> 38e3c03affSreyk #include <vis.h> 39b7b6a941Sreyk 40b7b6a941Sreyk #include "httpd.h" 41b7b6a941Sreyk #include "http.h" 4259355b5aSreyk #include "patterns.h" 43b7b6a941Sreyk 44b7b6a941Sreyk static int server_httpmethod_cmp(const void *, const void *); 45b7b6a941Sreyk static int server_httperror_cmp(const void *, const void *); 46b7b6a941Sreyk void server_httpdesc_free(struct http_descriptor *); 47e286121aSflorian int server_http_authenticate(struct server_config *, 48e286121aSflorian struct client *); 49586dade4Sreyk char *server_expand_http(struct client *, const char *, 50586dade4Sreyk char *, size_t); 51b7b6a941Sreyk 52b7b6a941Sreyk static struct httpd *env = NULL; 53b7b6a941Sreyk 54b7b6a941Sreyk static struct http_method http_methods[] = HTTP_METHODS; 55b7b6a941Sreyk static struct http_error http_errors[] = HTTP_ERRORS; 56b7b6a941Sreyk 57b7b6a941Sreyk void 58b7b6a941Sreyk server_http(struct httpd *x_env) 59b7b6a941Sreyk { 60b7b6a941Sreyk if (x_env != NULL) 61b7b6a941Sreyk env = x_env; 62b7b6a941Sreyk 63b7b6a941Sreyk DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); 64b7b6a941Sreyk 65b7b6a941Sreyk /* Sort the HTTP lookup arrays */ 66b7b6a941Sreyk qsort(http_methods, sizeof(http_methods) / 67b7b6a941Sreyk sizeof(http_methods[0]) - 1, 68b7b6a941Sreyk sizeof(http_methods[0]), server_httpmethod_cmp); 69b7b6a941Sreyk qsort(http_errors, sizeof(http_errors) / 70b7b6a941Sreyk sizeof(http_errors[0]) - 1, 71b7b6a941Sreyk sizeof(http_errors[0]), server_httperror_cmp); 72b7b6a941Sreyk } 73b7b6a941Sreyk 74b7b6a941Sreyk void 75b7b6a941Sreyk server_http_init(struct server *srv) 76b7b6a941Sreyk { 77b7b6a941Sreyk /* nothing */ 78b7b6a941Sreyk } 79b7b6a941Sreyk 80b7b6a941Sreyk int 81b7b6a941Sreyk server_httpdesc_init(struct client *clt) 82b7b6a941Sreyk { 83b7b6a941Sreyk struct http_descriptor *desc; 84b7b6a941Sreyk 85b7b6a941Sreyk if ((desc = calloc(1, sizeof(*desc))) == NULL) 86b7b6a941Sreyk return (-1); 87b7b6a941Sreyk RB_INIT(&desc->http_headers); 88d08e4976Sreyk clt->clt_descreq = desc; 89d08e4976Sreyk 90d08e4976Sreyk if ((desc = calloc(1, sizeof(*desc))) == NULL) { 91d08e4976Sreyk /* req will be cleaned up later */ 92d08e4976Sreyk return (-1); 93d08e4976Sreyk } 94d08e4976Sreyk RB_INIT(&desc->http_headers); 95d08e4976Sreyk clt->clt_descresp = desc; 96b7b6a941Sreyk 97b7b6a941Sreyk return (0); 98b7b6a941Sreyk } 99b7b6a941Sreyk 100b7b6a941Sreyk void 101b7b6a941Sreyk server_httpdesc_free(struct http_descriptor *desc) 102b7b6a941Sreyk { 103d08e4976Sreyk if (desc == NULL) 104d08e4976Sreyk return; 1053fe25232Sreyk 106b7b6a941Sreyk free(desc->http_path); 107b7b6a941Sreyk desc->http_path = NULL; 108de6550b1Sreyk free(desc->http_path_alias); 109de6550b1Sreyk desc->http_path_alias = NULL; 110b7b6a941Sreyk free(desc->http_query); 111b7b6a941Sreyk desc->http_query = NULL; 112b7b6a941Sreyk free(desc->http_version); 113b7b6a941Sreyk desc->http_version = NULL; 11447dc2a9dSreyk free(desc->http_host); 11547dc2a9dSreyk desc->http_host = NULL; 1163fe25232Sreyk 117b7b6a941Sreyk kv_purge(&desc->http_headers); 11844ed0680Sreyk desc->http_lastheader = NULL; 119d08e4976Sreyk desc->http_method = 0; 120d08e4976Sreyk desc->http_chunked = 0; 121b7b6a941Sreyk } 122b7b6a941Sreyk 123e286121aSflorian int 124e286121aSflorian server_http_authenticate(struct server_config *srv_conf, struct client *clt) 125e286121aSflorian { 1267d6098fdSreyk char decoded[1024]; 127e286121aSflorian FILE *fp = NULL; 128e286121aSflorian struct http_descriptor *desc = clt->clt_descreq; 1291413cd80Sreyk const struct auth *auth = srv_conf->auth; 130e286121aSflorian struct kv *ba, key; 131e286121aSflorian size_t linesize = 0; 132e286121aSflorian ssize_t linelen; 133e286121aSflorian int ret = -1; 1347d6098fdSreyk char *line = NULL, *user = NULL, *pass = NULL; 1357d6098fdSreyk char *clt_user = NULL, *clt_pass = NULL; 136e286121aSflorian 137e286121aSflorian memset(decoded, 0, sizeof(decoded)); 138e286121aSflorian key.kv_key = "Authorization"; 139e286121aSflorian 140e286121aSflorian if ((ba = kv_find(&desc->http_headers, &key)) == NULL || 141e286121aSflorian ba->kv_value == NULL) 142e286121aSflorian goto done; 143e286121aSflorian 144e286121aSflorian if (strncmp(ba->kv_value, "Basic ", strlen("Basic ")) != 0) 145e286121aSflorian goto done; 146e286121aSflorian 1474703e0faSreyk if (b64_pton(strchr(ba->kv_value, ' ') + 1, (uint8_t *)decoded, 148e286121aSflorian sizeof(decoded)) <= 0) 149e286121aSflorian goto done; 150e286121aSflorian 151e286121aSflorian if ((clt_pass = strchr(decoded, ':')) == NULL) 152e286121aSflorian goto done; 153e286121aSflorian 154e286121aSflorian clt_user = decoded; 155e286121aSflorian *clt_pass++ = '\0'; 156d22ad799Sflorian if ((clt->clt_remote_user = strdup(clt_user)) == NULL) 157d22ad799Sflorian goto done; 158e286121aSflorian 159e286121aSflorian if (clt_pass == NULL) 160e286121aSflorian goto done; 161e286121aSflorian 162602531d9Sreyk if ((fp = fopen(auth->auth_htpasswd, "r")) == NULL) 163e286121aSflorian goto done; 164e286121aSflorian 165e286121aSflorian while ((linelen = getline(&line, &linesize, fp)) != -1) { 166e286121aSflorian if (line[linelen - 1] == '\n') 167e286121aSflorian line[linelen - 1] = '\0'; 168e286121aSflorian user = line; 169e286121aSflorian pass = strchr(line, ':'); 170e286121aSflorian 171e286121aSflorian if (pass == NULL) { 172e286121aSflorian explicit_bzero(line, linelen); 173e286121aSflorian continue; 174e286121aSflorian } 175e286121aSflorian 176e286121aSflorian *pass++ = '\0'; 177e286121aSflorian 178e286121aSflorian if (strcmp(clt_user, user) != 0) { 179e286121aSflorian explicit_bzero(line, linelen); 180e286121aSflorian continue; 181e286121aSflorian } 182e286121aSflorian 183e286121aSflorian if (crypt_checkpass(clt_pass, pass) == 0) { 184e286121aSflorian explicit_bzero(line, linelen); 185e286121aSflorian ret = 0; 186e286121aSflorian break; 187e286121aSflorian } 188e286121aSflorian } 189e286121aSflorian done: 1903ca71284Ssunil free(line); 191e286121aSflorian if (fp != NULL) 192e286121aSflorian fclose(fp); 193e286121aSflorian 194e286121aSflorian if (ba != NULL && ba->kv_value != NULL) { 195e286121aSflorian explicit_bzero(ba->kv_value, strlen(ba->kv_value)); 196e286121aSflorian explicit_bzero(decoded, sizeof(decoded)); 197e286121aSflorian } 198e286121aSflorian 199e286121aSflorian return (ret); 200e286121aSflorian } 201e286121aSflorian 202b7b6a941Sreyk void 203b7b6a941Sreyk server_read_http(struct bufferevent *bev, void *arg) 204b7b6a941Sreyk { 205b7b6a941Sreyk struct client *clt = arg; 2064dd188b5Sreyk struct server_config *srv_conf = clt->clt_srv_conf; 207d08e4976Sreyk struct http_descriptor *desc = clt->clt_descreq; 208b7b6a941Sreyk struct evbuffer *src = EVBUFFER_INPUT(bev); 209b7b6a941Sreyk char *line = NULL, *key, *value; 210b7b6a941Sreyk const char *errstr; 211b7b6a941Sreyk size_t size, linelen; 212b7b6a941Sreyk struct kv *hdr = NULL; 213b7b6a941Sreyk 214b7b6a941Sreyk getmonotime(&clt->clt_tv_last); 215b7b6a941Sreyk 216b7b6a941Sreyk size = EVBUFFER_LENGTH(src); 217b7b6a941Sreyk DPRINTF("%s: session %d: size %lu, to read %lld", 218b7b6a941Sreyk __func__, clt->clt_id, size, clt->clt_toread); 219b7b6a941Sreyk if (!size) { 220b7b6a941Sreyk clt->clt_toread = TOREAD_HTTP_HEADER; 221b7b6a941Sreyk goto done; 222b7b6a941Sreyk } 223b7b6a941Sreyk 2248757e0ccSjsg while (!clt->clt_done && (line = 2258757e0ccSjsg evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT)) != NULL) { 226b7b6a941Sreyk linelen = strlen(line); 227b7b6a941Sreyk 228b7b6a941Sreyk /* 229b7b6a941Sreyk * An empty line indicates the end of the request. 230b7b6a941Sreyk * libevent already stripped the \r\n for us. 231b7b6a941Sreyk */ 232b7b6a941Sreyk if (!linelen) { 233b7b6a941Sreyk clt->clt_done = 1; 234b7b6a941Sreyk free(line); 235b7b6a941Sreyk break; 236b7b6a941Sreyk } 237b7b6a941Sreyk key = line; 238b7b6a941Sreyk 239b7b6a941Sreyk /* Limit the total header length minus \r\n */ 240b7b6a941Sreyk clt->clt_headerlen += linelen; 241b7b6a941Sreyk if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) { 242b7b6a941Sreyk server_abort_http(clt, 413, "request too large"); 2430859b34fSreyk goto abort; 244b7b6a941Sreyk } 245b7b6a941Sreyk 246b7b6a941Sreyk /* 247b7b6a941Sreyk * The first line is the GET/POST/PUT/... request, 248b7b6a941Sreyk * subsequent lines are HTTP headers. 249b7b6a941Sreyk */ 250b7b6a941Sreyk if (++clt->clt_line == 1) 251b7b6a941Sreyk value = strchr(key, ' '); 252b7b6a941Sreyk else if (*key == ' ' || *key == '\t') 253b7b6a941Sreyk /* Multiline headers wrap with a space or tab */ 254b7b6a941Sreyk value = NULL; 255b7b6a941Sreyk else 256b7b6a941Sreyk value = strchr(key, ':'); 257b7b6a941Sreyk if (value == NULL) { 258b7b6a941Sreyk if (clt->clt_line == 1) { 259b7b6a941Sreyk server_abort_http(clt, 400, "malformed"); 26003fd4389Sreyk goto abort; 261b7b6a941Sreyk } 262b7b6a941Sreyk 263b7b6a941Sreyk /* Append line to the last header, if present */ 264b7b6a941Sreyk if (kv_extend(&desc->http_headers, 26503fd4389Sreyk desc->http_lastheader, line) == NULL) 266b7b6a941Sreyk goto fail; 267b7b6a941Sreyk 268b7b6a941Sreyk free(line); 269b7b6a941Sreyk continue; 270b7b6a941Sreyk } 271b7b6a941Sreyk if (*value == ':') { 272b7b6a941Sreyk *value++ = '\0'; 273b7b6a941Sreyk value += strspn(value, " \t\r\n"); 274b7b6a941Sreyk } else { 275b7b6a941Sreyk *value++ = '\0'; 276b7b6a941Sreyk } 277b7b6a941Sreyk 278b7b6a941Sreyk DPRINTF("%s: session %d: header '%s: %s'", __func__, 279b7b6a941Sreyk clt->clt_id, key, value); 280b7b6a941Sreyk 281b7b6a941Sreyk /* 282b7b6a941Sreyk * Identify and handle specific HTTP request methods 283b7b6a941Sreyk */ 284b7b6a941Sreyk if (clt->clt_line == 1) { 285b7b6a941Sreyk if ((desc->http_method = server_httpmethod_byname(key)) 286fc062027Sreyk == HTTP_METHOD_NONE) { 287fc062027Sreyk server_abort_http(clt, 400, "malformed"); 288fc062027Sreyk goto abort; 289fc062027Sreyk } 290b7b6a941Sreyk 291b7b6a941Sreyk /* 292b7b6a941Sreyk * Decode request path and query 293b7b6a941Sreyk */ 294b7b6a941Sreyk desc->http_path = strdup(value); 29503fd4389Sreyk if (desc->http_path == NULL) 296b7b6a941Sreyk goto fail; 29703fd4389Sreyk 298b7b6a941Sreyk desc->http_version = strchr(desc->http_path, ' '); 29903fd4389Sreyk if (desc->http_version == NULL) 3002e2bd5b6Sreyk goto fail; 30103fd4389Sreyk 302b7b6a941Sreyk *desc->http_version++ = '\0'; 303b7b6a941Sreyk desc->http_query = strchr(desc->http_path, '?'); 304b7b6a941Sreyk if (desc->http_query != NULL) 305b7b6a941Sreyk *desc->http_query++ = '\0'; 306b7b6a941Sreyk 307b7b6a941Sreyk /* 308b7b6a941Sreyk * Have to allocate the strings because they could 3092e2bd5b6Sreyk * be changed independently by the filters later. 310b7b6a941Sreyk */ 3112e2bd5b6Sreyk if ((desc->http_version = 31203fd4389Sreyk strdup(desc->http_version)) == NULL) 313b7b6a941Sreyk goto fail; 31403fd4389Sreyk 315b7b6a941Sreyk if (desc->http_query != NULL && 316b7b6a941Sreyk (desc->http_query = 31703fd4389Sreyk strdup(desc->http_query)) == NULL) 318b7b6a941Sreyk goto fail; 31903fd4389Sreyk 320b7b6a941Sreyk } else if (desc->http_method != HTTP_METHOD_NONE && 321b7b6a941Sreyk strcasecmp("Content-Length", key) == 0) { 322b7b6a941Sreyk if (desc->http_method == HTTP_METHOD_TRACE || 323b7b6a941Sreyk desc->http_method == HTTP_METHOD_CONNECT) { 324b7b6a941Sreyk /* 325b7b6a941Sreyk * These method should not have a body 326b7b6a941Sreyk * and thus no Content-Length header. 327b7b6a941Sreyk */ 328b7b6a941Sreyk server_abort_http(clt, 400, "malformed"); 329b7b6a941Sreyk goto abort; 330b7b6a941Sreyk } 331b7b6a941Sreyk 332b7b6a941Sreyk /* 333b7b6a941Sreyk * Need to read data from the client after the 334b7b6a941Sreyk * HTTP header. 335b7b6a941Sreyk * XXX What about non-standard clients not using 336b7b6a941Sreyk * the carriage return? And some browsers seem to 337b7b6a941Sreyk * include the line length in the content-length. 338b7b6a941Sreyk */ 339b7b6a941Sreyk clt->clt_toread = strtonum(value, 0, LLONG_MAX, 340b7b6a941Sreyk &errstr); 341b7b6a941Sreyk if (errstr) { 342b7b6a941Sreyk server_abort_http(clt, 500, errstr); 343b7b6a941Sreyk goto abort; 344b7b6a941Sreyk } 3454dd188b5Sreyk if ((size_t)clt->clt_toread > 3464dd188b5Sreyk srv_conf->maxrequestbody) { 3474dd188b5Sreyk server_abort_http(clt, 413, NULL); 3484dd188b5Sreyk goto abort; 3494dd188b5Sreyk } 350b7b6a941Sreyk } 351b7b6a941Sreyk 352b7b6a941Sreyk if (strcasecmp("Transfer-Encoding", key) == 0 && 353b7b6a941Sreyk strcasecmp("chunked", value) == 0) 354b7b6a941Sreyk desc->http_chunked = 1; 355b7b6a941Sreyk 356b7b6a941Sreyk if (clt->clt_line != 1) { 357b7b6a941Sreyk if ((hdr = kv_add(&desc->http_headers, key, 35803fd4389Sreyk value)) == NULL) 359b7b6a941Sreyk goto fail; 36003fd4389Sreyk 361b7b6a941Sreyk desc->http_lastheader = hdr; 362b7b6a941Sreyk } 363b7b6a941Sreyk 364b7b6a941Sreyk free(line); 365b7b6a941Sreyk } 366b7b6a941Sreyk if (clt->clt_done) { 367b7b6a941Sreyk if (desc->http_method == HTTP_METHOD_NONE) { 368b7b6a941Sreyk server_abort_http(clt, 406, "no method"); 369b7b6a941Sreyk return; 370b7b6a941Sreyk } 371b7b6a941Sreyk 372b7b6a941Sreyk switch (desc->http_method) { 373b7b6a941Sreyk case HTTP_METHOD_CONNECT: 374b7b6a941Sreyk /* Data stream */ 375b7b6a941Sreyk clt->clt_toread = TOREAD_UNLIMITED; 376b7b6a941Sreyk bev->readcb = server_read; 377b7b6a941Sreyk break; 378b7b6a941Sreyk case HTTP_METHOD_DELETE: 379b7b6a941Sreyk case HTTP_METHOD_GET: 380b7b6a941Sreyk case HTTP_METHOD_HEAD: 381b7b6a941Sreyk case HTTP_METHOD_OPTIONS: 382f0c872b4Sreyk /* WebDAV methods */ 383f0c872b4Sreyk case HTTP_METHOD_COPY: 384ac272b40Sreyk case HTTP_METHOD_MOVE: 385b7b6a941Sreyk clt->clt_toread = 0; 386b7b6a941Sreyk break; 387b7b6a941Sreyk case HTTP_METHOD_POST: 388b7b6a941Sreyk case HTTP_METHOD_PUT: 389b7b6a941Sreyk case HTTP_METHOD_RESPONSE: 390f0c872b4Sreyk /* WebDAV methods */ 391f0c872b4Sreyk case HTTP_METHOD_PROPFIND: 392f0c872b4Sreyk case HTTP_METHOD_PROPPATCH: 393f0c872b4Sreyk case HTTP_METHOD_MKCOL: 394f0c872b4Sreyk case HTTP_METHOD_LOCK: 395f0c872b4Sreyk case HTTP_METHOD_UNLOCK: 396f0c872b4Sreyk case HTTP_METHOD_VERSION_CONTROL: 397f0c872b4Sreyk case HTTP_METHOD_REPORT: 398f0c872b4Sreyk case HTTP_METHOD_CHECKOUT: 399f0c872b4Sreyk case HTTP_METHOD_CHECKIN: 400f0c872b4Sreyk case HTTP_METHOD_UNCHECKOUT: 401f0c872b4Sreyk case HTTP_METHOD_MKWORKSPACE: 402f0c872b4Sreyk case HTTP_METHOD_UPDATE: 403f0c872b4Sreyk case HTTP_METHOD_LABEL: 404f0c872b4Sreyk case HTTP_METHOD_MERGE: 405f0c872b4Sreyk case HTTP_METHOD_BASELINE_CONTROL: 406f0c872b4Sreyk case HTTP_METHOD_MKACTIVITY: 407f0c872b4Sreyk case HTTP_METHOD_ORDERPATCH: 408f0c872b4Sreyk case HTTP_METHOD_ACL: 409f0c872b4Sreyk case HTTP_METHOD_MKREDIRECTREF: 410f0c872b4Sreyk case HTTP_METHOD_UPDATEREDIRECTREF: 411f0c872b4Sreyk case HTTP_METHOD_SEARCH: 412f0c872b4Sreyk case HTTP_METHOD_PATCH: 413b7b6a941Sreyk /* HTTP request payload */ 414b7b6a941Sreyk if (clt->clt_toread > 0) 415b7b6a941Sreyk bev->readcb = server_read_httpcontent; 416b7b6a941Sreyk 417b7b6a941Sreyk /* Single-pass HTTP body */ 418b7b6a941Sreyk if (clt->clt_toread < 0) { 419b7b6a941Sreyk clt->clt_toread = TOREAD_UNLIMITED; 420b7b6a941Sreyk bev->readcb = server_read; 421b7b6a941Sreyk } 422b7b6a941Sreyk break; 423b7b6a941Sreyk default: 424f0c872b4Sreyk server_abort_http(clt, 405, "method not allowed"); 425f0c872b4Sreyk return; 426b7b6a941Sreyk } 427b7b6a941Sreyk if (desc->http_chunked) { 428b7b6a941Sreyk /* Chunked transfer encoding */ 429b7b6a941Sreyk clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; 430b7b6a941Sreyk bev->readcb = server_read_httpchunks; 431b7b6a941Sreyk } 432b7b6a941Sreyk 433b7b6a941Sreyk done: 434b6a65335Sflorian if (clt->clt_toread != 0) 435b6a65335Sflorian bufferevent_disable(bev, EV_READ); 43612312c27Sreyk server_response(env, clt); 437b7b6a941Sreyk return; 438b7b6a941Sreyk } 439b7b6a941Sreyk if (clt->clt_done) { 440b7b6a941Sreyk server_close(clt, "done"); 441b7b6a941Sreyk return; 442b7b6a941Sreyk } 443b7b6a941Sreyk if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http) 444b7b6a941Sreyk bev->readcb(bev, arg); 445b7b6a941Sreyk bufferevent_enable(bev, EV_READ); 446b7b6a941Sreyk return; 447b7b6a941Sreyk fail: 448b7b6a941Sreyk server_abort_http(clt, 500, strerror(errno)); 449b7b6a941Sreyk abort: 450b7b6a941Sreyk free(line); 451b7b6a941Sreyk } 452b7b6a941Sreyk 453b7b6a941Sreyk void 454b7b6a941Sreyk server_read_httpcontent(struct bufferevent *bev, void *arg) 455b7b6a941Sreyk { 456b7b6a941Sreyk struct client *clt = arg; 457b7b6a941Sreyk struct evbuffer *src = EVBUFFER_INPUT(bev); 458b7b6a941Sreyk size_t size; 459b7b6a941Sreyk 460b7b6a941Sreyk getmonotime(&clt->clt_tv_last); 461b7b6a941Sreyk 462b7b6a941Sreyk size = EVBUFFER_LENGTH(src); 463b7b6a941Sreyk DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 464b7b6a941Sreyk clt->clt_id, size, clt->clt_toread); 465b7b6a941Sreyk if (!size) 466b7b6a941Sreyk return; 467b7b6a941Sreyk 468b7b6a941Sreyk if (clt->clt_toread > 0) { 469b7b6a941Sreyk /* Read content data */ 470b7b6a941Sreyk if ((off_t)size > clt->clt_toread) { 471b7b6a941Sreyk size = clt->clt_toread; 472b6a65335Sflorian if (fcgi_add_stdin(clt, src) == -1) 473b7b6a941Sreyk goto fail; 474b7b6a941Sreyk clt->clt_toread = 0; 475b7b6a941Sreyk } else { 476b6a65335Sflorian if (fcgi_add_stdin(clt, src) == -1) 477b7b6a941Sreyk goto fail; 478b7b6a941Sreyk clt->clt_toread -= size; 479b7b6a941Sreyk } 480b7b6a941Sreyk DPRINTF("%s: done, size %lu, to read %lld", __func__, 481b7b6a941Sreyk size, clt->clt_toread); 482b7b6a941Sreyk } 483b7b6a941Sreyk if (clt->clt_toread == 0) { 484b6a65335Sflorian fcgi_add_stdin(clt, NULL); 485b7b6a941Sreyk clt->clt_toread = TOREAD_HTTP_HEADER; 486b6a65335Sflorian bufferevent_disable(bev, EV_READ); 487b7b6a941Sreyk bev->readcb = server_read_http; 488b6a65335Sflorian return; 489b7b6a941Sreyk } 490b7b6a941Sreyk if (clt->clt_done) 491b7b6a941Sreyk goto done; 492b7b6a941Sreyk if (bev->readcb != server_read_httpcontent) 493b7b6a941Sreyk bev->readcb(bev, arg); 494b6a65335Sflorian 495b7b6a941Sreyk return; 496b7b6a941Sreyk done: 497b7b6a941Sreyk return; 498b7b6a941Sreyk fail: 499b7b6a941Sreyk server_close(clt, strerror(errno)); 500b7b6a941Sreyk } 501b7b6a941Sreyk 502b7b6a941Sreyk void 503b7b6a941Sreyk server_read_httpchunks(struct bufferevent *bev, void *arg) 504b7b6a941Sreyk { 505b7b6a941Sreyk struct client *clt = arg; 506b7b6a941Sreyk struct evbuffer *src = EVBUFFER_INPUT(bev); 507b7b6a941Sreyk char *line; 508b7b6a941Sreyk long long llval; 509b7b6a941Sreyk size_t size; 510b7b6a941Sreyk 511b7b6a941Sreyk getmonotime(&clt->clt_tv_last); 512b7b6a941Sreyk 513b7b6a941Sreyk size = EVBUFFER_LENGTH(src); 514b7b6a941Sreyk DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 515b7b6a941Sreyk clt->clt_id, size, clt->clt_toread); 516b7b6a941Sreyk if (!size) 517b7b6a941Sreyk return; 518b7b6a941Sreyk 519b7b6a941Sreyk if (clt->clt_toread > 0) { 520b7b6a941Sreyk /* Read chunk data */ 521b7b6a941Sreyk if ((off_t)size > clt->clt_toread) { 522b7b6a941Sreyk size = clt->clt_toread; 523b7b6a941Sreyk if (server_bufferevent_write_chunk(clt, src, size) 524b7b6a941Sreyk == -1) 525b7b6a941Sreyk goto fail; 526b7b6a941Sreyk clt->clt_toread = 0; 527b7b6a941Sreyk } else { 528b7b6a941Sreyk if (server_bufferevent_write_buffer(clt, src) == -1) 529b7b6a941Sreyk goto fail; 530b7b6a941Sreyk clt->clt_toread -= size; 531b7b6a941Sreyk } 532b7b6a941Sreyk DPRINTF("%s: done, size %lu, to read %lld", __func__, 533b7b6a941Sreyk size, clt->clt_toread); 534b7b6a941Sreyk } 535b7b6a941Sreyk switch (clt->clt_toread) { 536b7b6a941Sreyk case TOREAD_HTTP_CHUNK_LENGTH: 5378757e0ccSjsg line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); 538b7b6a941Sreyk if (line == NULL) { 539b7b6a941Sreyk /* Ignore empty line, continue */ 540b7b6a941Sreyk bufferevent_enable(bev, EV_READ); 541b7b6a941Sreyk return; 542b7b6a941Sreyk } 543b7b6a941Sreyk if (strlen(line) == 0) { 544b7b6a941Sreyk free(line); 545b7b6a941Sreyk goto next; 546b7b6a941Sreyk } 547b7b6a941Sreyk 548b7b6a941Sreyk /* 549b7b6a941Sreyk * Read prepended chunk size in hex, ignore the trailer. 550b7b6a941Sreyk * The returned signed value must not be negative. 551b7b6a941Sreyk */ 552b7b6a941Sreyk if (sscanf(line, "%llx", &llval) != 1 || llval < 0) { 553b7b6a941Sreyk free(line); 554b7b6a941Sreyk server_close(clt, "invalid chunk size"); 555b7b6a941Sreyk return; 556b7b6a941Sreyk } 557b7b6a941Sreyk 558b7b6a941Sreyk if (server_bufferevent_print(clt, line) == -1 || 559b7b6a941Sreyk server_bufferevent_print(clt, "\r\n") == -1) { 560b7b6a941Sreyk free(line); 561b7b6a941Sreyk goto fail; 562b7b6a941Sreyk } 563b7b6a941Sreyk free(line); 564b7b6a941Sreyk 565b7b6a941Sreyk if ((clt->clt_toread = llval) == 0) { 566b7b6a941Sreyk DPRINTF("%s: last chunk", __func__); 567b7b6a941Sreyk clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER; 568b7b6a941Sreyk } 569b7b6a941Sreyk break; 570b7b6a941Sreyk case TOREAD_HTTP_CHUNK_TRAILER: 571b7b6a941Sreyk /* Last chunk is 0 bytes followed by trailer and empty line */ 5728757e0ccSjsg line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); 573b7b6a941Sreyk if (line == NULL) { 574b7b6a941Sreyk /* Ignore empty line, continue */ 575b7b6a941Sreyk bufferevent_enable(bev, EV_READ); 576b7b6a941Sreyk return; 577b7b6a941Sreyk } 578b7b6a941Sreyk if (server_bufferevent_print(clt, line) == -1 || 579b7b6a941Sreyk server_bufferevent_print(clt, "\r\n") == -1) { 580b7b6a941Sreyk free(line); 581b7b6a941Sreyk goto fail; 582b7b6a941Sreyk } 583b7b6a941Sreyk if (strlen(line) == 0) { 584b7b6a941Sreyk /* Switch to HTTP header mode */ 585b7b6a941Sreyk clt->clt_toread = TOREAD_HTTP_HEADER; 586b7b6a941Sreyk bev->readcb = server_read_http; 587b7b6a941Sreyk } 588b7b6a941Sreyk free(line); 589b7b6a941Sreyk break; 590b7b6a941Sreyk case 0: 591b7b6a941Sreyk /* Chunk is terminated by an empty newline */ 5928757e0ccSjsg line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); 593b7b6a941Sreyk free(line); 594b7b6a941Sreyk if (server_bufferevent_print(clt, "\r\n") == -1) 595b7b6a941Sreyk goto fail; 596b7b6a941Sreyk clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; 597b7b6a941Sreyk break; 598b7b6a941Sreyk } 599b7b6a941Sreyk 600b7b6a941Sreyk next: 601b7b6a941Sreyk if (clt->clt_done) 602b7b6a941Sreyk goto done; 603b7b6a941Sreyk if (EVBUFFER_LENGTH(src)) 604b7b6a941Sreyk bev->readcb(bev, arg); 605b7b6a941Sreyk bufferevent_enable(bev, EV_READ); 606b7b6a941Sreyk return; 607b7b6a941Sreyk 608b7b6a941Sreyk done: 609b7b6a941Sreyk server_close(clt, "last http chunk read (done)"); 610b7b6a941Sreyk return; 611b7b6a941Sreyk fail: 612b7b6a941Sreyk server_close(clt, strerror(errno)); 613b7b6a941Sreyk } 614b7b6a941Sreyk 615b7b6a941Sreyk void 616b7b6a941Sreyk server_reset_http(struct client *clt) 617b7b6a941Sreyk { 618fa361cd1Sreyk struct server *srv = clt->clt_srv; 619b7b6a941Sreyk 620c145f9a8Sreyk server_log(clt, NULL); 621c145f9a8Sreyk 622d08e4976Sreyk server_httpdesc_free(clt->clt_descreq); 623d08e4976Sreyk server_httpdesc_free(clt->clt_descresp); 624b7b6a941Sreyk clt->clt_headerlen = 0; 625b7b6a941Sreyk clt->clt_line = 0; 626b7b6a941Sreyk clt->clt_done = 0; 627ac2cdcb6Sreyk clt->clt_chunk = 0; 628daa1b608Sflorian free(clt->clt_remote_user); 629daa1b608Sflorian clt->clt_remote_user = NULL; 6305fa30660Sreyk clt->clt_bev->readcb = server_read_http; 631fa361cd1Sreyk clt->clt_srv_conf = &srv->srv_conf; 63259355b5aSreyk str_match_free(&clt->clt_srv_match); 633b7b6a941Sreyk } 634b7b6a941Sreyk 635be5ab2e6Schrisz ssize_t 636be5ab2e6Schrisz server_http_time(time_t t, char *tmbuf, size_t len) 6379f126950Sreyk { 6389f126950Sreyk struct tm tm; 6399f126950Sreyk 6409f126950Sreyk /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */ 641be5ab2e6Schrisz if (t == -1 || gmtime_r(&t, &tm) == NULL) 642be5ab2e6Schrisz return (-1); 643be5ab2e6Schrisz else 644be5ab2e6Schrisz return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm)); 6459f126950Sreyk } 6469f126950Sreyk 6476af43371Sreyk const char * 6486af43371Sreyk server_http_host(struct sockaddr_storage *ss, char *buf, size_t len) 6496af43371Sreyk { 650b9fc9a72Sderaadt char hbuf[HOST_NAME_MAX+1]; 6516af43371Sreyk in_port_t port; 6526af43371Sreyk 6536af43371Sreyk if (print_host(ss, buf, len) == NULL) 6546af43371Sreyk return (NULL); 6556af43371Sreyk 6566af43371Sreyk port = ntohs(server_socket_getport(ss)); 6576af43371Sreyk if (port == HTTP_PORT) 6586af43371Sreyk return (buf); 6596af43371Sreyk 6606af43371Sreyk switch (ss->ss_family) { 6616af43371Sreyk case AF_INET: 6626af43371Sreyk if ((size_t)snprintf(hbuf, sizeof(hbuf), 6636af43371Sreyk "%s:%u", buf, port) >= sizeof(hbuf)) 6646af43371Sreyk return (NULL); 6656af43371Sreyk break; 6666af43371Sreyk case AF_INET6: 6676af43371Sreyk if ((size_t)snprintf(hbuf, sizeof(hbuf), 6686af43371Sreyk "[%s]:%u", buf, port) >= sizeof(hbuf)) 6696af43371Sreyk return (NULL); 6706af43371Sreyk break; 6716af43371Sreyk } 6726af43371Sreyk 6736af43371Sreyk if (strlcpy(buf, hbuf, len) >= len) 6746af43371Sreyk return (NULL); 6756af43371Sreyk 6766af43371Sreyk return (buf); 6776af43371Sreyk } 6786af43371Sreyk 67977fd0032Sreyk char * 68077fd0032Sreyk server_http_parsehost(char *host, char *buf, size_t len, int *portval) 68177fd0032Sreyk { 68277fd0032Sreyk char *start, *end, *port; 68377fd0032Sreyk const char *errstr = NULL; 68477fd0032Sreyk 68577fd0032Sreyk if (strlcpy(buf, host, len) >= len) { 68677fd0032Sreyk log_debug("%s: host name too long", __func__); 68777fd0032Sreyk return (NULL); 68877fd0032Sreyk } 68977fd0032Sreyk 69077fd0032Sreyk start = buf; 69177fd0032Sreyk end = port = NULL; 69277fd0032Sreyk 69377fd0032Sreyk if (*start == '[' && (end = strchr(start, ']')) != NULL) { 69477fd0032Sreyk /* Address enclosed in [] with port, eg. [2001:db8::1]:80 */ 69577fd0032Sreyk start++; 69677fd0032Sreyk *end++ = '\0'; 69777fd0032Sreyk if ((port = strchr(end, ':')) == NULL || *port == '\0') 69877fd0032Sreyk port = NULL; 69977fd0032Sreyk else 70077fd0032Sreyk port++; 70177fd0032Sreyk memmove(buf, start, strlen(start) + 1); 70277fd0032Sreyk } else if ((end = strchr(start, ':')) != NULL) { 70377fd0032Sreyk /* Name or address with port, eg. www.example.com:80 */ 70477fd0032Sreyk *end++ = '\0'; 70577fd0032Sreyk port = end; 70677fd0032Sreyk } else { 70777fd0032Sreyk /* Name or address with default port, eg. www.example.com */ 70877fd0032Sreyk port = NULL; 70977fd0032Sreyk } 71077fd0032Sreyk 71177fd0032Sreyk if (port != NULL) { 71277fd0032Sreyk /* Save the requested port */ 71377fd0032Sreyk *portval = strtonum(port, 0, 0xffff, &errstr); 71477fd0032Sreyk if (errstr != NULL) { 71577fd0032Sreyk log_debug("%s: invalid port: %s", __func__, 71677fd0032Sreyk strerror(errno)); 71777fd0032Sreyk return (NULL); 71877fd0032Sreyk } 71977fd0032Sreyk *portval = htons(*portval); 72077fd0032Sreyk } else { 72177fd0032Sreyk /* Port not given, indicate the default port */ 72277fd0032Sreyk *portval = -1; 72377fd0032Sreyk } 72477fd0032Sreyk 72577fd0032Sreyk return (start); 72677fd0032Sreyk } 72777fd0032Sreyk 728b7b6a941Sreyk void 7294703e0faSreyk server_abort_http(struct client *clt, unsigned int code, const char *msg) 730b7b6a941Sreyk { 731f5d55328Sflorian struct server_config *srv_conf = clt->clt_srv_conf; 732b7b6a941Sreyk struct bufferevent *bev = clt->clt_bev; 7338b4c340eSflorian struct http_descriptor *desc = clt->clt_descreq; 7348b4c340eSflorian const char *httperr = NULL, *style; 7358b4c340eSflorian char *httpmsg, *body = NULL, *extraheader = NULL; 736f5d55328Sflorian char tmbuf[32], hbuf[128], *hstsheader = NULL; 737bf34a23eSreyk char buf[IBUF_READ_SIZE]; 7380674f400Ssemarie char *escapedmsg = NULL; 7398b4c340eSflorian int bodylen; 740b7b6a941Sreyk 741f8932becSreyk if (code == 0) { 742f8932becSreyk server_close(clt, "dropped"); 743f8932becSreyk return; 744f8932becSreyk } 745f8932becSreyk 746b7b6a941Sreyk if ((httperr = server_httperror_byid(code)) == NULL) 747b7b6a941Sreyk httperr = "Unknown Error"; 748b7b6a941Sreyk 749b7b6a941Sreyk if (bev == NULL) 750b7b6a941Sreyk goto done; 751b7b6a941Sreyk 752e9b02f4aSreyk if (server_log_http(clt, code, 0) == -1) 753e9b02f4aSreyk goto done; 754e9b02f4aSreyk 755b7b6a941Sreyk /* Some system information */ 7569fb8351aSreyk if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL) 757b7b6a941Sreyk goto done; 758b7b6a941Sreyk 759be5ab2e6Schrisz if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0) 760be5ab2e6Schrisz goto done; 761b7b6a941Sreyk 762b7b6a941Sreyk /* Do not send details of the Internal Server Error */ 7635d9e55e4Sreyk switch (code) { 7645d9e55e4Sreyk case 301: 7655d9e55e4Sreyk case 302: 766586dade4Sreyk case 303: 767586dade4Sreyk if (msg == NULL) 768586dade4Sreyk break; 769586dade4Sreyk memset(buf, 0, sizeof(buf)); 770bf34a23eSreyk if (server_expand_http(clt, msg, buf, sizeof(buf)) == NULL) 771586dade4Sreyk goto done; 772bf34a23eSreyk if (asprintf(&extraheader, "Location: %s\r\n", buf) == -1) { 7735d9e55e4Sreyk code = 500; 7745d9e55e4Sreyk extraheader = NULL; 7755d9e55e4Sreyk } 776bf34a23eSreyk msg = buf; 7775d9e55e4Sreyk break; 778e286121aSflorian case 401: 7790674f400Ssemarie if (stravis(&escapedmsg, msg, VIS_DQ) == -1) { 7800674f400Ssemarie code = 500; 7810674f400Ssemarie extraheader = NULL; 7820674f400Ssemarie } else if (asprintf(&extraheader, 7830674f400Ssemarie "WWW-Authenticate: Basic realm=\"%s\"\r\n", escapedmsg) 7840674f400Ssemarie == -1) { 785e286121aSflorian code = 500; 786e286121aSflorian extraheader = NULL; 787e286121aSflorian } 788e286121aSflorian break; 789b0faf28cSflorian case 416: 790b0faf28cSflorian if (asprintf(&extraheader, 791b0faf28cSflorian "Content-Range: %s\r\n", msg) == -1) { 792b0faf28cSflorian code = 500; 793b0faf28cSflorian extraheader = NULL; 794b0faf28cSflorian } 795b0faf28cSflorian break; 7965d9e55e4Sreyk default: 797cbebb5b9Sreyk /* 798cbebb5b9Sreyk * Do not send details of the error. Traditionally, 799cbebb5b9Sreyk * web servers responsed with the request path on 40x 800cbebb5b9Sreyk * errors which could be abused to inject JavaScript etc. 801cbebb5b9Sreyk * Instead of sanitizing the path here, we just don't 802cbebb5b9Sreyk * reprint it. 803cbebb5b9Sreyk */ 8045d9e55e4Sreyk break; 8055d9e55e4Sreyk } 806b7b6a941Sreyk 8070674f400Ssemarie free(escapedmsg); 8080674f400Ssemarie 809b7b6a941Sreyk /* A CSS stylesheet allows minimal customization by the user */ 810f687442bSreyk style = "body { background-color: white; color: black; font-family: " 811cbebb5b9Sreyk "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n" 812cbebb5b9Sreyk "hr { border: 0; border-bottom: 1px dashed; }\n"; 8138b4c340eSflorian 8148b4c340eSflorian /* Generate simple HTML error document */ 8158b4c340eSflorian if ((bodylen = asprintf(&body, 816d89214cdSreyk "<!DOCTYPE html>\n" 817b7b6a941Sreyk "<html>\n" 818b7b6a941Sreyk "<head>\n" 819f5ec6fb6Sflorian "<meta http-equiv=\"Content-Type\" content=\"text/html; " 820f5ec6fb6Sflorian "charset=utf-8\"/>\n" 821b7b6a941Sreyk "<title>%03d %s</title>\n" 822b7b6a941Sreyk "<style type=\"text/css\"><!--\n%s\n--></style>\n" 823b7b6a941Sreyk "</head>\n" 824b7b6a941Sreyk "<body>\n" 825cbebb5b9Sreyk "<h1>%03d %s</h1>\n" 826cbebb5b9Sreyk "<hr>\n<address>%s</address>\n" 827b7b6a941Sreyk "</body>\n" 828b7b6a941Sreyk "</html>\n", 829*95d80e7aSjung code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) { 830*95d80e7aSjung body = NULL; 8318b4c340eSflorian goto done; 832*95d80e7aSjung } 8338b4c340eSflorian 834f5d55328Sflorian if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { 835f5d55328Sflorian if (asprintf(&hstsheader, "Strict-Transport-Security: " 83652f7cd50Sreyk "max-age=%d%s%s\r\n", srv_conf->hsts_max_age, 83752f7cd50Sreyk srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? 83852f7cd50Sreyk "; includeSubDomains" : "", 83952f7cd50Sreyk srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? 840*95d80e7aSjung "; preload" : "") == -1) { 841*95d80e7aSjung hstsheader = NULL; 842f5d55328Sflorian goto done; 843f5d55328Sflorian } 844*95d80e7aSjung } 845f5d55328Sflorian 8468b4c340eSflorian /* Add basic HTTP headers */ 8478b4c340eSflorian if (asprintf(&httpmsg, 8488b4c340eSflorian "HTTP/1.0 %03d %s\r\n" 8498b4c340eSflorian "Date: %s\r\n" 8508b4c340eSflorian "Server: %s\r\n" 8518b4c340eSflorian "Connection: close\r\n" 8528b4c340eSflorian "Content-Type: text/html\r\n" 8538b4c340eSflorian "Content-Length: %d\r\n" 8548b4c340eSflorian "%s" 855f5d55328Sflorian "%s" 8568b4c340eSflorian "\r\n" 8578b4c340eSflorian "%s", 8588b4c340eSflorian code, httperr, tmbuf, HTTPD_SERVERNAME, bodylen, 8595d9e55e4Sreyk extraheader == NULL ? "" : extraheader, 860f5d55328Sflorian hstsheader == NULL ? "" : hstsheader, 8618b4c340eSflorian desc->http_method == HTTP_METHOD_HEAD ? "" : body) == -1) 862b7b6a941Sreyk goto done; 863b7b6a941Sreyk 864b7b6a941Sreyk /* Dump the message without checking for success */ 865b7b6a941Sreyk server_dump(clt, httpmsg, strlen(httpmsg)); 866b7b6a941Sreyk free(httpmsg); 867b7b6a941Sreyk 868b7b6a941Sreyk done: 8698b4c340eSflorian free(body); 8705d9e55e4Sreyk free(extraheader); 871f5d55328Sflorian free(hstsheader); 872586dade4Sreyk if (msg == NULL) 873586dade4Sreyk msg = "\"\""; 874df9b638bSreyk if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) { 875b7b6a941Sreyk server_close(clt, msg); 876df9b638bSreyk } else { 877b7b6a941Sreyk server_close(clt, httpmsg); 878b7b6a941Sreyk free(httpmsg); 879b7b6a941Sreyk } 880b7b6a941Sreyk } 881b7b6a941Sreyk 882b7b6a941Sreyk void 883b7b6a941Sreyk server_close_http(struct client *clt) 884b7b6a941Sreyk { 885d08e4976Sreyk struct http_descriptor *desc; 886b7b6a941Sreyk 887d08e4976Sreyk desc = clt->clt_descreq; 888b7b6a941Sreyk server_httpdesc_free(desc); 889b7b6a941Sreyk free(desc); 890d08e4976Sreyk clt->clt_descreq = NULL; 891d08e4976Sreyk 892d08e4976Sreyk desc = clt->clt_descresp; 893d08e4976Sreyk server_httpdesc_free(desc); 894d08e4976Sreyk free(desc); 895d08e4976Sreyk clt->clt_descresp = NULL; 896daa1b608Sflorian free(clt->clt_remote_user); 897daa1b608Sflorian clt->clt_remote_user = NULL; 89859355b5aSreyk 89959355b5aSreyk str_match_free(&clt->clt_srv_match); 900b7b6a941Sreyk } 901b7b6a941Sreyk 902586dade4Sreyk char * 903586dade4Sreyk server_expand_http(struct client *clt, const char *val, char *buf, 904586dade4Sreyk size_t len) 905586dade4Sreyk { 906586dade4Sreyk struct http_descriptor *desc = clt->clt_descreq; 907586dade4Sreyk struct server_config *srv_conf = clt->clt_srv_conf; 908bf34a23eSreyk char ibuf[128], *str, *path, *query; 90959355b5aSreyk const char *errstr = NULL, *p; 91059355b5aSreyk size_t size; 91159355b5aSreyk int n, ret; 912586dade4Sreyk 913586dade4Sreyk if (strlcpy(buf, val, len) >= len) 914586dade4Sreyk return (NULL); 915586dade4Sreyk 91659355b5aSreyk /* Find previously matched substrings by index */ 91759355b5aSreyk for (p = val; clt->clt_srv_match.sm_nmatch && 91859355b5aSreyk (p = strstr(p, "%")) != NULL; p++) { 91900f3986fSreyk if (!isdigit((unsigned char)*(p + 1))) 92059355b5aSreyk continue; 92159355b5aSreyk 92259355b5aSreyk /* Copy number, leading '%' char and add trailing \0 */ 92359355b5aSreyk size = strspn(p + 1, "0123456789") + 2; 92459355b5aSreyk if (size >= sizeof(ibuf)) 92559355b5aSreyk return (NULL); 92659355b5aSreyk (void)strlcpy(ibuf, p, size); 92759355b5aSreyk n = strtonum(ibuf + 1, 0, 92859355b5aSreyk clt->clt_srv_match.sm_nmatch - 1, &errstr); 92959355b5aSreyk if (errstr != NULL) 93059355b5aSreyk return (NULL); 93159355b5aSreyk 93259355b5aSreyk /* Expand variable with matched value */ 9330788fdf8Ssemarie if ((str = url_encode(clt->clt_srv_match.sm_match[n])) == NULL) 9340788fdf8Ssemarie return (NULL); 9350788fdf8Ssemarie ret = expand_string(buf, len, ibuf, str); 9360788fdf8Ssemarie free(str); 9370788fdf8Ssemarie if (ret != 0) 93859355b5aSreyk return (NULL); 93959355b5aSreyk } 940586dade4Sreyk if (strstr(val, "$DOCUMENT_URI") != NULL) { 941bf34a23eSreyk if ((path = url_encode(desc->http_path)) == NULL) 942bf34a23eSreyk return (NULL); 943bf34a23eSreyk ret = expand_string(buf, len, "$DOCUMENT_URI", path); 944bf34a23eSreyk free(path); 945bf34a23eSreyk if (ret != 0) 946586dade4Sreyk return (NULL); 947586dade4Sreyk } 948586dade4Sreyk if (strstr(val, "$QUERY_STRING") != NULL) { 949bf34a23eSreyk if (desc->http_query == NULL) { 950bf34a23eSreyk ret = expand_string(buf, len, "$QUERY_STRING", ""); 951bf34a23eSreyk } else { 952bf34a23eSreyk if ((query = url_encode(desc->http_query)) == NULL) 953bf34a23eSreyk return (NULL); 954bf34a23eSreyk ret = expand_string(buf, len, "$QUERY_STRING", query); 955bf34a23eSreyk free(query); 956bf34a23eSreyk } 957bf34a23eSreyk if (ret != 0) 958586dade4Sreyk return (NULL); 959586dade4Sreyk } 960586dade4Sreyk if (strstr(val, "$REMOTE_") != NULL) { 961586dade4Sreyk if (strstr(val, "$REMOTE_ADDR") != NULL) { 962586dade4Sreyk if (print_host(&clt->clt_ss, 963586dade4Sreyk ibuf, sizeof(ibuf)) == NULL) 964586dade4Sreyk return (NULL); 965586dade4Sreyk if (expand_string(buf, len, 966586dade4Sreyk "$REMOTE_ADDR", ibuf) != 0) 967586dade4Sreyk return (NULL); 968586dade4Sreyk } 969586dade4Sreyk if (strstr(val, "$REMOTE_PORT") != NULL) { 970586dade4Sreyk snprintf(ibuf, sizeof(ibuf), 971586dade4Sreyk "%u", ntohs(clt->clt_port)); 972586dade4Sreyk if (expand_string(buf, len, 973586dade4Sreyk "$REMOTE_PORT", ibuf) != 0) 974586dade4Sreyk return (NULL); 975586dade4Sreyk } 976586dade4Sreyk if (strstr(val, "$REMOTE_USER") != NULL) { 977586dade4Sreyk if ((srv_conf->flags & SRVFLAG_AUTH) && 978885c4aa1Sreyk clt->clt_remote_user != NULL) { 979885c4aa1Sreyk if ((str = url_encode(clt->clt_remote_user)) 980885c4aa1Sreyk == NULL) 981885c4aa1Sreyk return (NULL); 982885c4aa1Sreyk } else 983885c4aa1Sreyk str = strdup(""); 984885c4aa1Sreyk ret = expand_string(buf, len, "$REMOTE_USER", str); 985885c4aa1Sreyk free(str); 986885c4aa1Sreyk if (ret != 0) 987586dade4Sreyk return (NULL); 988586dade4Sreyk } 989586dade4Sreyk } 990586dade4Sreyk if (strstr(val, "$REQUEST_URI") != NULL) { 991bf34a23eSreyk if ((path = url_encode(desc->http_path)) == NULL) 992bf34a23eSreyk return (NULL); 993586dade4Sreyk if (desc->http_query == NULL) { 994bf34a23eSreyk str = path; 995bf34a23eSreyk } else { 996bf34a23eSreyk if ((query = url_encode(desc->http_query)) == NULL) { 997bf34a23eSreyk free(path); 998586dade4Sreyk return (NULL); 999586dade4Sreyk } 1000bf34a23eSreyk ret = asprintf(&str, "%s?%s", path, query); 1001bf34a23eSreyk free(path); 1002bf34a23eSreyk free(query); 1003bf34a23eSreyk if (ret == -1) 1004bf34a23eSreyk return (NULL); 1005bf34a23eSreyk } 1006bf34a23eSreyk 1007bf34a23eSreyk ret = expand_string(buf, len, "$REQUEST_URI", str); 1008586dade4Sreyk free(str); 1009bf34a23eSreyk if (ret != 0) 1010bf34a23eSreyk return (NULL); 1011586dade4Sreyk } 1012586dade4Sreyk if (strstr(val, "$SERVER_") != NULL) { 1013586dade4Sreyk if (strstr(val, "$SERVER_ADDR") != NULL) { 1014586dade4Sreyk if (print_host(&srv_conf->ss, 1015586dade4Sreyk ibuf, sizeof(ibuf)) == NULL) 1016586dade4Sreyk return (NULL); 1017586dade4Sreyk if (expand_string(buf, len, 1018586dade4Sreyk "$SERVER_ADDR", ibuf) != 0) 1019586dade4Sreyk return (NULL); 1020586dade4Sreyk } 1021586dade4Sreyk if (strstr(val, "$SERVER_PORT") != NULL) { 1022586dade4Sreyk snprintf(ibuf, sizeof(ibuf), "%u", 1023586dade4Sreyk ntohs(srv_conf->port)); 1024586dade4Sreyk if (expand_string(buf, len, 1025586dade4Sreyk "$SERVER_PORT", ibuf) != 0) 1026586dade4Sreyk return (NULL); 1027586dade4Sreyk } 1028586dade4Sreyk if (strstr(val, "$SERVER_NAME") != NULL) { 1029885c4aa1Sreyk if ((str = url_encode(srv_conf->name)) 1030885c4aa1Sreyk == NULL) 1031885c4aa1Sreyk return (NULL); 1032885c4aa1Sreyk ret = expand_string(buf, len, "$SERVER_NAME", str); 1033885c4aa1Sreyk free(str); 1034885c4aa1Sreyk if (ret != 0) 1035586dade4Sreyk return (NULL); 1036586dade4Sreyk } 1037586dade4Sreyk } 1038586dade4Sreyk 1039586dade4Sreyk return (buf); 1040586dade4Sreyk } 1041586dade4Sreyk 1042b7b6a941Sreyk int 10435fa30660Sreyk server_response(struct httpd *httpd, struct client *clt) 10445fa30660Sreyk { 1045b9fc9a72Sderaadt char path[PATH_MAX]; 1046b9fc9a72Sderaadt char hostname[HOST_NAME_MAX+1]; 1047d08e4976Sreyk struct http_descriptor *desc = clt->clt_descreq; 1048d08e4976Sreyk struct http_descriptor *resp = clt->clt_descresp; 1049d9bba0abSreyk struct server *srv = clt->clt_srv; 1050de6550b1Sreyk struct server_config *srv_conf = &srv->srv_conf; 10516af43371Sreyk struct kv *kv, key, *host; 105259355b5aSreyk struct str_find sm; 105359355b5aSreyk int portval = -1, ret; 105477fd0032Sreyk char *hostval; 105559355b5aSreyk const char *errstr = NULL; 10565fa30660Sreyk 1057c9351fd6Sreyk /* Canonicalize the request path */ 1058c9351fd6Sreyk if (desc->http_path == NULL || 1059a383dca2Sreyk url_decode(desc->http_path) == NULL || 1060c9351fd6Sreyk canonicalize_path(desc->http_path, path, sizeof(path)) == NULL) 1061c9351fd6Sreyk goto fail; 1062c9351fd6Sreyk free(desc->http_path); 1063c9351fd6Sreyk if ((desc->http_path = strdup(path)) == NULL) 10645fa30660Sreyk goto fail; 10655fa30660Sreyk 10666af43371Sreyk key.kv_key = "Host"; 10676af43371Sreyk if ((host = kv_find(&desc->http_headers, &key)) != NULL && 10686af43371Sreyk host->kv_value == NULL) 10696af43371Sreyk host = NULL; 10706af43371Sreyk 10715fa30660Sreyk if (strcmp(desc->http_version, "HTTP/1.1") == 0) { 10725fa30660Sreyk /* Host header is mandatory */ 10736af43371Sreyk if (host == NULL) 10745fa30660Sreyk goto fail; 10755fa30660Sreyk 10765fa30660Sreyk /* Is the connection persistent? */ 1077091144dbSreyk key.kv_key = "Connection"; 10785fa30660Sreyk if ((kv = kv_find(&desc->http_headers, &key)) != NULL && 10795fa30660Sreyk strcasecmp("close", kv->kv_value) == 0) 10805fa30660Sreyk clt->clt_persist = 0; 10815fa30660Sreyk else 1082091144dbSreyk clt->clt_persist++; 10835fa30660Sreyk } else { 1084091144dbSreyk /* Is the connection persistent? */ 1085091144dbSreyk key.kv_key = "Connection"; 1086091144dbSreyk if ((kv = kv_find(&desc->http_headers, &key)) != NULL && 1087091144dbSreyk strcasecmp("keep-alive", kv->kv_value) == 0) 1088091144dbSreyk clt->clt_persist++; 1089091144dbSreyk else 10905fa30660Sreyk clt->clt_persist = 0; 10915fa30660Sreyk } 10925fa30660Sreyk 10937a5a4a11Sreyk if (clt->clt_persist >= srv_conf->maxrequests) 1094e139abd5Sreyk clt->clt_persist = 0; 1095e139abd5Sreyk 1096d9bba0abSreyk /* 1097d9bba0abSreyk * Do we have a Host header and matching configuration? 1098d9bba0abSreyk * XXX the Host can also appear in the URL path. 1099d9bba0abSreyk */ 11006af43371Sreyk if (host != NULL) { 110177fd0032Sreyk if ((hostval = server_http_parsehost(host->kv_value, 110277fd0032Sreyk hostname, sizeof(hostname), &portval)) == NULL) 110377fd0032Sreyk goto fail; 110477fd0032Sreyk 1105d9bba0abSreyk TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) { 1106bd1bab2fSreyk #ifdef DEBUG 1107bd1bab2fSreyk if ((srv_conf->flags & SRVFLAG_LOCATION) == 0) { 110877fd0032Sreyk DPRINTF("%s: virtual host \"%s:%u\"" 110977fd0032Sreyk " host \"%s\" (\"%s\")", 111077fd0032Sreyk __func__, srv_conf->name, 111177fd0032Sreyk ntohs(srv_conf->port), host->kv_value, 111277fd0032Sreyk hostname); 1113bd1bab2fSreyk } 1114bd1bab2fSreyk #endif 111559355b5aSreyk if (srv_conf->flags & SRVFLAG_LOCATION) 111659355b5aSreyk continue; 111759355b5aSreyk else if (srv_conf->flags & SRVFLAG_SERVER_MATCH) { 111859355b5aSreyk str_find(hostname, srv_conf->name, 111959355b5aSreyk &sm, 1, &errstr); 112059355b5aSreyk ret = errstr == NULL ? 0 : -1; 112159355b5aSreyk } else { 112259355b5aSreyk ret = fnmatch(srv_conf->name, 112359355b5aSreyk hostname, FNM_CASEFOLD); 112459355b5aSreyk } 112559355b5aSreyk if (ret == 0 && 112677fd0032Sreyk (portval == -1 || 112777fd0032Sreyk (portval != -1 && portval == srv_conf->port))) { 1128d9bba0abSreyk /* Replace host configuration */ 1129d9bba0abSreyk clt->clt_srv_conf = srv_conf; 11306af43371Sreyk srv_conf = NULL; 1131d9bba0abSreyk break; 1132d9bba0abSreyk } 1133d9bba0abSreyk } 1134d9bba0abSreyk } 1135d9bba0abSreyk 11366af43371Sreyk if (srv_conf != NULL) { 11376af43371Sreyk /* Use the actual server IP address */ 113847dc2a9dSreyk if (server_http_host(&clt->clt_srv_ss, hostname, 113947dc2a9dSreyk sizeof(hostname)) == NULL) 11406af43371Sreyk goto fail; 11416af43371Sreyk } else { 11426af43371Sreyk /* Host header was valid and found */ 114347dc2a9dSreyk if (strlcpy(hostname, host->kv_value, sizeof(hostname)) >= 114447dc2a9dSreyk sizeof(hostname)) 11456af43371Sreyk goto fail; 11464f194ec8Sreyk srv_conf = clt->clt_srv_conf; 11474f194ec8Sreyk } 11484f194ec8Sreyk 114947dc2a9dSreyk if ((desc->http_host = strdup(hostname)) == NULL) 115047dc2a9dSreyk goto fail; 115147dc2a9dSreyk 1152d08e4976Sreyk /* Now fill in the mandatory parts of the response descriptor */ 1153d08e4976Sreyk resp->http_method = desc->http_method; 1154d08e4976Sreyk if ((resp->http_version = strdup(desc->http_version)) == NULL) 1155d08e4976Sreyk goto fail; 1156d08e4976Sreyk 11574f194ec8Sreyk /* Now search for the location */ 1158de6550b1Sreyk srv_conf = server_getlocation(clt, desc->http_path); 1159de6550b1Sreyk 1160f8932becSreyk if (srv_conf->flags & SRVFLAG_BLOCK) { 1161f8932becSreyk server_abort_http(clt, srv_conf->return_code, 1162f8932becSreyk srv_conf->return_uri); 1163f8932becSreyk return (-1); 1164f8932becSreyk } else if (srv_conf->flags & SRVFLAG_AUTH && 1165e286121aSflorian server_http_authenticate(srv_conf, clt) == -1) { 1166e286121aSflorian server_abort_http(clt, 401, srv_conf->auth_realm); 1167e286121aSflorian return (-1); 1168e286121aSflorian } else 1169de6550b1Sreyk return (server_file(httpd, clt)); 1170de6550b1Sreyk fail: 1171de6550b1Sreyk server_abort_http(clt, 400, "bad request"); 1172de6550b1Sreyk return (-1); 1173de6550b1Sreyk } 1174de6550b1Sreyk 11754624b10aSchrisz const char * 11764624b10aSchrisz server_root_strip(const char *path, int n) 11774624b10aSchrisz { 11784624b10aSchrisz const char *p; 11794624b10aSchrisz 11804624b10aSchrisz /* Strip strip leading directories. Leading '/' is ignored. */ 11814624b10aSchrisz for (; n > 0 && *path != '\0'; n--) 11824624b10aSchrisz if ((p = strchr(++path, '/')) == NULL) 11834624b10aSchrisz path = strchr(path, '\0'); 11844624b10aSchrisz else 11854624b10aSchrisz path = p; 11864624b10aSchrisz 11874624b10aSchrisz return (path); 11884624b10aSchrisz } 11894624b10aSchrisz 1190de6550b1Sreyk struct server_config * 1191de6550b1Sreyk server_getlocation(struct client *clt, const char *path) 1192de6550b1Sreyk { 1193de6550b1Sreyk struct server *srv = clt->clt_srv; 1194de6550b1Sreyk struct server_config *srv_conf = clt->clt_srv_conf, *location; 119559355b5aSreyk const char *errstr = NULL; 119659355b5aSreyk int ret; 1197de6550b1Sreyk 1198de6550b1Sreyk /* Now search for the location */ 11994f194ec8Sreyk TAILQ_FOREACH(location, &srv->srv_hosts, entry) { 1200bd1bab2fSreyk #ifdef DEBUG 1201bd1bab2fSreyk if (location->flags & SRVFLAG_LOCATION) { 1202bd1bab2fSreyk DPRINTF("%s: location \"%s\" path \"%s\"", 1203bd1bab2fSreyk __func__, location->location, path); 1204bd1bab2fSreyk } 1205bd1bab2fSreyk #endif 12064f194ec8Sreyk if ((location->flags & SRVFLAG_LOCATION) && 120759355b5aSreyk location->parent_id == srv_conf->parent_id) { 120859355b5aSreyk errstr = NULL; 120959355b5aSreyk if (location->flags & SRVFLAG_LOCATION_MATCH) { 121059355b5aSreyk ret = str_match(path, location->location, 121159355b5aSreyk &clt->clt_srv_match, &errstr); 121259355b5aSreyk } else { 121359355b5aSreyk ret = fnmatch(location->location, 121459355b5aSreyk path, FNM_CASEFOLD); 121559355b5aSreyk } 121659355b5aSreyk if (ret == 0 && errstr == NULL) { 12174f194ec8Sreyk /* Replace host configuration */ 1218435dc7cdSreyk clt->clt_srv_conf = srv_conf = location; 12194f194ec8Sreyk break; 12204f194ec8Sreyk } 12216af43371Sreyk } 122259355b5aSreyk } 12236af43371Sreyk 1224de6550b1Sreyk return (srv_conf); 12255fa30660Sreyk } 12265fa30660Sreyk 12275fa30660Sreyk int 12284703e0faSreyk server_response_http(struct client *clt, unsigned int code, 1229d1cfc522Skettenis struct media_type *media, off_t size, time_t mtime) 12305fa30660Sreyk { 1231f5d55328Sflorian struct server_config *srv_conf = clt->clt_srv_conf; 1232d08e4976Sreyk struct http_descriptor *desc = clt->clt_descreq; 1233d08e4976Sreyk struct http_descriptor *resp = clt->clt_descresp; 12345fa30660Sreyk const char *error; 12355fa30660Sreyk struct kv *ct, *cl; 12369f126950Sreyk char tmbuf[32]; 12375fa30660Sreyk 1238d24f6b1eSreyk if (desc == NULL || media == NULL || 1239d24f6b1eSreyk (error = server_httperror_byid(code)) == NULL) 12405fa30660Sreyk return (-1); 12415fa30660Sreyk 1242ea62a379Sdoug if (server_log_http(clt, code, size) == -1) 1243ea62a379Sdoug return (-1); 1244ea62a379Sdoug 12455fa30660Sreyk /* Add error codes */ 12463323ac76Sbenno if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 || 1247d08e4976Sreyk kv_set(&resp->http_pathquery, "%s", error) == -1) 12485fa30660Sreyk return (-1); 12495fa30660Sreyk 12505fa30660Sreyk /* Add headers */ 1251d08e4976Sreyk if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 12525fa30660Sreyk return (-1); 12535fa30660Sreyk 12545fa30660Sreyk /* Is it a persistent connection? */ 12555fa30660Sreyk if (clt->clt_persist) { 1256d08e4976Sreyk if (kv_add(&resp->http_headers, 12575fa30660Sreyk "Connection", "keep-alive") == NULL) 12585fa30660Sreyk return (-1); 1259d08e4976Sreyk } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL) 12605fa30660Sreyk return (-1); 12615fa30660Sreyk 12625fa30660Sreyk /* Set media type */ 1263d08e4976Sreyk if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL || 1264d24f6b1eSreyk kv_set(ct, "%s/%s", media->media_type, media->media_subtype) == -1) 12655fa30660Sreyk return (-1); 12665fa30660Sreyk 12675fa30660Sreyk /* Set content length, if specified */ 126841425654Sreyk if ((cl = 1269d08e4976Sreyk kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL || 1270d1cfc522Skettenis kv_set(cl, "%lld", (long long)size) == -1) 12715fa30660Sreyk return (-1); 12725fa30660Sreyk 1273be5ab2e6Schrisz /* Set last modification time */ 1274be5ab2e6Schrisz if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 || 1275d08e4976Sreyk kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL) 1276be5ab2e6Schrisz return (-1); 1277be5ab2e6Schrisz 1278f5d55328Sflorian /* HSTS header */ 1279f5d55328Sflorian if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { 1280f5d55328Sflorian if ((cl = 1281f5d55328Sflorian kv_add(&resp->http_headers, "Strict-Transport-Security", 1282f5d55328Sflorian NULL)) == NULL || 12833323ac76Sbenno kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age, 128452f7cd50Sreyk srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? 128552f7cd50Sreyk "; includeSubDomains" : "", 128652f7cd50Sreyk srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? 128752f7cd50Sreyk "; preload" : "") == -1) 1288f5d55328Sflorian return (-1); 1289f5d55328Sflorian } 1290f5d55328Sflorian 1291be5ab2e6Schrisz /* Date header is mandatory and should be added as late as possible */ 1292be5ab2e6Schrisz if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || 1293d08e4976Sreyk kv_add(&resp->http_headers, "Date", tmbuf) == NULL) 12949f126950Sreyk return (-1); 12959f126950Sreyk 12965fa30660Sreyk /* Write completed header */ 12975fa30660Sreyk if (server_writeresponse_http(clt) == -1 || 12985fa30660Sreyk server_bufferevent_print(clt, "\r\n") == -1 || 1299d08e4976Sreyk server_headers(clt, resp, server_writeheader_http, NULL) == -1 || 13005fa30660Sreyk server_bufferevent_print(clt, "\r\n") == -1) 13015fa30660Sreyk return (-1); 13025fa30660Sreyk 1303d08e4976Sreyk if (size == 0 || resp->http_method == HTTP_METHOD_HEAD) { 13045fa30660Sreyk bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); 13055fa30660Sreyk if (clt->clt_persist) 13065fa30660Sreyk clt->clt_toread = TOREAD_HTTP_HEADER; 13075fa30660Sreyk else 13085fa30660Sreyk clt->clt_toread = TOREAD_HTTP_NONE; 13095fa30660Sreyk clt->clt_done = 0; 13105fa30660Sreyk return (0); 13115fa30660Sreyk } 13125fa30660Sreyk 13135fa30660Sreyk return (1); 13145fa30660Sreyk } 13155fa30660Sreyk 13165fa30660Sreyk int 1317b7b6a941Sreyk server_writeresponse_http(struct client *clt) 1318b7b6a941Sreyk { 1319d08e4976Sreyk struct http_descriptor *desc = clt->clt_descresp; 1320b7b6a941Sreyk 1321b7b6a941Sreyk DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version, 1322b7b6a941Sreyk desc->http_rescode, desc->http_resmesg); 1323b7b6a941Sreyk 1324b7b6a941Sreyk if (server_bufferevent_print(clt, desc->http_version) == -1 || 1325b7b6a941Sreyk server_bufferevent_print(clt, " ") == -1 || 1326b7b6a941Sreyk server_bufferevent_print(clt, desc->http_rescode) == -1 || 1327b7b6a941Sreyk server_bufferevent_print(clt, " ") == -1 || 1328b7b6a941Sreyk server_bufferevent_print(clt, desc->http_resmesg) == -1) 1329b7b6a941Sreyk return (-1); 1330b7b6a941Sreyk 1331b7b6a941Sreyk return (0); 1332b7b6a941Sreyk } 1333b7b6a941Sreyk 1334b7b6a941Sreyk int 13354aa750c1Sreyk server_writeheader_http(struct client *clt, struct kv *hdr, void *arg) 1336b7b6a941Sreyk { 1337b7b6a941Sreyk char *ptr; 1338b7b6a941Sreyk const char *key; 1339b7b6a941Sreyk 1340b7b6a941Sreyk if (hdr->kv_flags & KV_FLAG_INVALID) 1341b7b6a941Sreyk return (0); 1342b7b6a941Sreyk 1343b7b6a941Sreyk /* The key might have been updated in the parent */ 1344b7b6a941Sreyk if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 1345b7b6a941Sreyk key = hdr->kv_parent->kv_key; 1346b7b6a941Sreyk else 1347b7b6a941Sreyk key = hdr->kv_key; 1348b7b6a941Sreyk 1349b7b6a941Sreyk ptr = hdr->kv_value; 1350b7b6a941Sreyk if (server_bufferevent_print(clt, key) == -1 || 1351b7b6a941Sreyk (ptr != NULL && 1352b7b6a941Sreyk (server_bufferevent_print(clt, ": ") == -1 || 1353b7b6a941Sreyk server_bufferevent_print(clt, ptr) == -1 || 1354b7b6a941Sreyk server_bufferevent_print(clt, "\r\n") == -1))) 1355b7b6a941Sreyk return (-1); 1356b7b6a941Sreyk DPRINTF("%s: %s: %s", __func__, key, 1357b7b6a941Sreyk hdr->kv_value == NULL ? "" : hdr->kv_value); 1358b7b6a941Sreyk 1359b7b6a941Sreyk return (0); 1360b7b6a941Sreyk } 1361b7b6a941Sreyk 1362b7b6a941Sreyk int 1363d08e4976Sreyk server_headers(struct client *clt, void *descp, 13644aa750c1Sreyk int (*hdr_cb)(struct client *, struct kv *, void *), void *arg) 1365b7b6a941Sreyk { 1366b7b6a941Sreyk struct kv *hdr, *kv; 1367d08e4976Sreyk struct http_descriptor *desc = descp; 1368b7b6a941Sreyk 1369b7b6a941Sreyk RB_FOREACH(hdr, kvtree, &desc->http_headers) { 13704aa750c1Sreyk if ((hdr_cb)(clt, hdr, arg) == -1) 1371b7b6a941Sreyk return (-1); 1372b7b6a941Sreyk TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) { 13734aa750c1Sreyk if ((hdr_cb)(clt, kv, arg) == -1) 1374b7b6a941Sreyk return (-1); 1375b7b6a941Sreyk } 1376b7b6a941Sreyk } 1377b7b6a941Sreyk 1378b7b6a941Sreyk return (0); 1379b7b6a941Sreyk } 1380b7b6a941Sreyk 1381b7b6a941Sreyk enum httpmethod 1382b7b6a941Sreyk server_httpmethod_byname(const char *name) 1383b7b6a941Sreyk { 1384b7b6a941Sreyk enum httpmethod id = HTTP_METHOD_NONE; 1385b7b6a941Sreyk struct http_method method, *res = NULL; 1386b7b6a941Sreyk 1387b7b6a941Sreyk /* Set up key */ 1388b7b6a941Sreyk method.method_name = name; 1389b7b6a941Sreyk 1390b7b6a941Sreyk if ((res = bsearch(&method, http_methods, 1391b7b6a941Sreyk sizeof(http_methods) / sizeof(http_methods[0]) - 1, 1392b7b6a941Sreyk sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL) 1393b7b6a941Sreyk id = res->method_id; 1394b7b6a941Sreyk 1395b7b6a941Sreyk return (id); 1396b7b6a941Sreyk } 1397b7b6a941Sreyk 1398b7b6a941Sreyk const char * 13994703e0faSreyk server_httpmethod_byid(unsigned int id) 1400b7b6a941Sreyk { 1401f0c872b4Sreyk const char *name = "<UNKNOWN>"; 1402b7b6a941Sreyk int i; 1403b7b6a941Sreyk 1404b7b6a941Sreyk for (i = 0; http_methods[i].method_name != NULL; i++) { 1405b7b6a941Sreyk if (http_methods[i].method_id == id) { 1406b7b6a941Sreyk name = http_methods[i].method_name; 1407b7b6a941Sreyk break; 1408b7b6a941Sreyk } 1409b7b6a941Sreyk } 1410b7b6a941Sreyk 1411b7b6a941Sreyk return (name); 1412b7b6a941Sreyk } 1413b7b6a941Sreyk 1414b7b6a941Sreyk static int 1415b7b6a941Sreyk server_httpmethod_cmp(const void *a, const void *b) 1416b7b6a941Sreyk { 1417b7b6a941Sreyk const struct http_method *ma = a; 1418b7b6a941Sreyk const struct http_method *mb = b; 14191d89351eSstsp 14201d89351eSstsp /* 14211d89351eSstsp * RFC 2616 section 5.1.1 says that the method is case 14221d89351eSstsp * sensitive so we don't do a strcasecmp here. 14231d89351eSstsp */ 1424b7b6a941Sreyk return (strcmp(ma->method_name, mb->method_name)); 1425b7b6a941Sreyk } 1426b7b6a941Sreyk 1427b7b6a941Sreyk const char * 14284703e0faSreyk server_httperror_byid(unsigned int id) 1429b7b6a941Sreyk { 14301a67c375Sreyk struct http_error error, *res; 1431b7b6a941Sreyk 1432b7b6a941Sreyk /* Set up key */ 1433b7b6a941Sreyk error.error_code = (int)id; 1434b7b6a941Sreyk 14351a67c375Sreyk if ((res = bsearch(&error, http_errors, 1436b7b6a941Sreyk sizeof(http_errors) / sizeof(http_errors[0]) - 1, 14371a67c375Sreyk sizeof(http_errors[0]), server_httperror_cmp)) != NULL) 1438b7b6a941Sreyk return (res->error_name); 14391a67c375Sreyk 14401a67c375Sreyk return (NULL); 1441b7b6a941Sreyk } 1442b7b6a941Sreyk 1443b7b6a941Sreyk static int 1444b7b6a941Sreyk server_httperror_cmp(const void *a, const void *b) 1445b7b6a941Sreyk { 1446b7b6a941Sreyk const struct http_error *ea = a; 1447b7b6a941Sreyk const struct http_error *eb = b; 1448b7b6a941Sreyk return (ea->error_code - eb->error_code); 1449b7b6a941Sreyk } 1450ea62a379Sdoug 1451ea62a379Sdoug int 14524703e0faSreyk server_log_http(struct client *clt, unsigned int code, size_t len) 1453ea62a379Sdoug { 1454ea62a379Sdoug static char tstamp[64]; 1455ea62a379Sdoug static char ip[INET6_ADDRSTRLEN]; 1456ea62a379Sdoug time_t t; 1457af3cfad1Sdoug struct kv key, *agent, *referrer; 1458ea62a379Sdoug struct tm *tm; 1459ea62a379Sdoug struct server_config *srv_conf; 1460ea62a379Sdoug struct http_descriptor *desc; 1461e22b0c74Sreyk int ret = -1; 14626ebc1f19Ssemarie char *user = NULL; 14636ebc1f19Ssemarie char *path = NULL; 14646ebc1f19Ssemarie char *query = NULL; 14656ebc1f19Ssemarie char *version = NULL; 14666ebc1f19Ssemarie char *referrer_v = NULL; 14676ebc1f19Ssemarie char *agent_v = NULL; 1468ea62a379Sdoug 1469ea62a379Sdoug if ((srv_conf = clt->clt_srv_conf) == NULL) 1470ea62a379Sdoug return (-1); 1471af3cfad1Sdoug if ((srv_conf->flags & SRVFLAG_LOG) == 0) 1472af3cfad1Sdoug return (0); 1473d08e4976Sreyk if ((desc = clt->clt_descreq) == NULL) 1474ea62a379Sdoug return (-1); 1475ea62a379Sdoug 1476ea62a379Sdoug if ((t = time(NULL)) == -1) 1477ea62a379Sdoug return (-1); 1478ea62a379Sdoug if ((tm = localtime(&t)) == NULL) 1479ea62a379Sdoug return (-1); 1480ea62a379Sdoug if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0) 1481ea62a379Sdoug return (-1); 1482ea62a379Sdoug 1483944a3fefSreyk if (print_host(&clt->clt_ss, ip, sizeof(ip)) == NULL) 1484944a3fefSreyk return (-1); 1485ea62a379Sdoug 1486ea62a379Sdoug /* 1487ea62a379Sdoug * For details on common log format, see: 1488ea62a379Sdoug * https://httpd.apache.org/docs/current/mod/mod_log_config.html 1489af3cfad1Sdoug * 1490af3cfad1Sdoug * httpd's format is similar to these Apache LogFormats: 1491af3cfad1Sdoug * "%v %h %l %u %t \"%r\" %>s %B" 1492af3cfad1Sdoug * "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-agent}i\"" 1493ea62a379Sdoug */ 1494ea62a379Sdoug switch (srv_conf->logformat) { 1495ea62a379Sdoug case LOG_FORMAT_COMMON: 1496e3c03affSreyk /* Use vis to encode input values from the header */ 14976ebc1f19Ssemarie if (clt->clt_remote_user && 1498ba87bb65Sreyk stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) 1499e3c03affSreyk goto done; 1500e3c03affSreyk if (desc->http_version && 1501ba87bb65Sreyk stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) 15026ebc1f19Ssemarie goto done; 15036ebc1f19Ssemarie 1504e3c03affSreyk /* The following should be URL-encoded */ 15056ebc1f19Ssemarie if (desc->http_path && 1506e22b0c74Sreyk (path = url_encode(desc->http_path)) == NULL) 15076ebc1f19Ssemarie goto done; 15086ebc1f19Ssemarie if (desc->http_query && 1509e22b0c74Sreyk (query = url_encode(desc->http_query)) == NULL) 15106ebc1f19Ssemarie goto done; 15116ebc1f19Ssemarie 15126ebc1f19Ssemarie ret = evbuffer_add_printf(clt->clt_log, 151341dcbd59Stim "%s %s - %s [%s] \"%s %s%s%s%s%s\" %03d %zu\n", 151441dcbd59Stim srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : 151541dcbd59Stim user, tstamp, 1516ea62a379Sdoug server_httpmethod_byid(desc->http_method), 15176ebc1f19Ssemarie desc->http_path == NULL ? "" : path, 1518ea62a379Sdoug desc->http_query == NULL ? "" : "?", 15196ebc1f19Ssemarie desc->http_query == NULL ? "" : query, 1520ea62a379Sdoug desc->http_version == NULL ? "" : " ", 15216ebc1f19Ssemarie desc->http_version == NULL ? "" : version, 15226ebc1f19Ssemarie code, len); 15236ebc1f19Ssemarie 1524ea62a379Sdoug break; 1525ea62a379Sdoug 1526ea62a379Sdoug case LOG_FORMAT_COMBINED: 1527ea62a379Sdoug key.kv_key = "Referer"; /* sic */ 1528ea62a379Sdoug if ((referrer = kv_find(&desc->http_headers, &key)) != NULL && 1529ea62a379Sdoug referrer->kv_value == NULL) 1530ea62a379Sdoug referrer = NULL; 1531ea62a379Sdoug 1532ea62a379Sdoug key.kv_key = "User-Agent"; 1533ea62a379Sdoug if ((agent = kv_find(&desc->http_headers, &key)) != NULL && 1534ea62a379Sdoug agent->kv_value == NULL) 1535ea62a379Sdoug agent = NULL; 1536ea62a379Sdoug 1537e3c03affSreyk /* Use vis to encode input values from the header */ 15386ebc1f19Ssemarie if (clt->clt_remote_user && 1539ba87bb65Sreyk stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) 1540e3c03affSreyk goto done; 1541e3c03affSreyk if (desc->http_version && 1542ba87bb65Sreyk stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) 1543e3c03affSreyk goto done; 1544e3c03affSreyk if (agent && 1545ba87bb65Sreyk stravis(&agent_v, agent->kv_value, HTTPD_LOGVIS) == -1) 15466ebc1f19Ssemarie goto done; 15476ebc1f19Ssemarie 1548e3c03affSreyk /* The following should be URL-encoded */ 15496ebc1f19Ssemarie if (desc->http_path && 1550e22b0c74Sreyk (path = url_encode(desc->http_path)) == NULL) 15516ebc1f19Ssemarie goto done; 15526ebc1f19Ssemarie if (desc->http_query && 1553e22b0c74Sreyk (query = url_encode(desc->http_query)) == NULL) 15546ebc1f19Ssemarie goto done; 1555e22b0c74Sreyk if (referrer && 1556e22b0c74Sreyk (referrer_v = url_encode(referrer->kv_value)) == NULL) 15576ebc1f19Ssemarie goto done; 15586ebc1f19Ssemarie 15596ebc1f19Ssemarie ret = evbuffer_add_printf(clt->clt_log, 156041dcbd59Stim "%s %s - %s [%s] \"%s %s%s%s%s%s\"" 15610afe0ae4Sreyk " %03d %zu \"%s\" \"%s\"\n", 156241dcbd59Stim srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : 156341dcbd59Stim user, tstamp, 1564ea62a379Sdoug server_httpmethod_byid(desc->http_method), 15656ebc1f19Ssemarie desc->http_path == NULL ? "" : path, 1566ea62a379Sdoug desc->http_query == NULL ? "" : "?", 15676ebc1f19Ssemarie desc->http_query == NULL ? "" : query, 1568ea62a379Sdoug desc->http_version == NULL ? "" : " ", 15696ebc1f19Ssemarie desc->http_version == NULL ? "" : version, 1570ea62a379Sdoug code, len, 15716ebc1f19Ssemarie referrer == NULL ? "" : referrer_v, 15726ebc1f19Ssemarie agent == NULL ? "" : agent_v); 15736ebc1f19Ssemarie 1574ea62a379Sdoug break; 1575944a3fefSreyk 1576944a3fefSreyk case LOG_FORMAT_CONNECTION: 1577e3c03affSreyk /* URL-encode the path */ 15786ebc1f19Ssemarie if (desc->http_path && 1579e22b0c74Sreyk (path = url_encode(desc->http_path)) == NULL) 15806ebc1f19Ssemarie goto done; 15816ebc1f19Ssemarie 15826ebc1f19Ssemarie ret = evbuffer_add_printf(clt->clt_log, " [%s]", 15836ebc1f19Ssemarie desc->http_path == NULL ? "" : path); 15846ebc1f19Ssemarie 1585944a3fefSreyk break; 1586ea62a379Sdoug } 1587ea62a379Sdoug 15886ebc1f19Ssemarie done: 15896ebc1f19Ssemarie free(user); 15906ebc1f19Ssemarie free(path); 15916ebc1f19Ssemarie free(query); 15926ebc1f19Ssemarie free(version); 15936ebc1f19Ssemarie free(referrer_v); 15946ebc1f19Ssemarie free(agent_v); 15956ebc1f19Ssemarie 15966ebc1f19Ssemarie return (ret); 1597ea62a379Sdoug } 1598