http.c (23865B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <arpa/inet.h> 3 #include <ctype.h> 4 #include <errno.h> 5 #include <limits.h> 6 #include <netinet/in.h> 7 #include <regex.h> 8 #include <stddef.h> 9 #include <stdint.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <strings.h> 13 #include <sys/socket.h> 14 #include <sys/stat.h> 15 #include <sys/types.h> 16 #include <time.h> 17 #include <unistd.h> 18 19 #include "config.h" 20 #include "http.h" 21 #include "util.h" 22 23 const char *req_field_str[] = { 24 [REQ_HOST] = "Host", 25 [REQ_RANGE] = "Range", 26 [REQ_IF_MODIFIED_SINCE] = "If-Modified-Since", 27 }; 28 29 const char *req_method_str[] = { 30 [M_GET] = "GET", 31 [M_HEAD] = "HEAD", 32 }; 33 34 const char *status_str[] = { 35 [S_OK] = "OK", 36 [S_PARTIAL_CONTENT] = "Partial Content", 37 [S_MOVED_PERMANENTLY] = "Moved Permanently", 38 [S_NOT_MODIFIED] = "Not Modified", 39 [S_BAD_REQUEST] = "Bad Request", 40 [S_FORBIDDEN] = "Forbidden", 41 [S_NOT_FOUND] = "Not Found", 42 [S_METHOD_NOT_ALLOWED] = "Method Not Allowed", 43 [S_REQUEST_TIMEOUT] = "Request Time-out", 44 [S_RANGE_NOT_SATISFIABLE] = "Range Not Satisfiable", 45 [S_REQUEST_TOO_LARGE] = "Request Header Fields Too Large", 46 [S_INTERNAL_SERVER_ERROR] = "Internal Server Error", 47 [S_VERSION_NOT_SUPPORTED] = "HTTP Version not supported", 48 }; 49 50 const char *res_field_str[] = { 51 [RES_ACCEPT_RANGES] = "Accept-Ranges", 52 [RES_ALLOW] = "Allow", 53 [RES_LOCATION] = "Location", 54 [RES_LAST_MODIFIED] = "Last-Modified", 55 [RES_CONTENT_LENGTH] = "Content-Length", 56 [RES_CONTENT_RANGE] = "Content-Range", 57 [RES_CONTENT_TYPE] = "Content-Type", 58 }; 59 60 enum status 61 http_prepare_header_buf(const struct response *res, struct buffer *buf) 62 { 63 char tstmp[FIELD_MAX]; 64 size_t i; 65 66 /* reset buffer */ 67 memset(buf, 0, sizeof(*buf)); 68 69 /* generate timestamp */ 70 if (timestamp(tstmp, sizeof(tstmp), time(NULL))) { 71 goto err; 72 } 73 74 /* write data */ 75 if (buffer_appendf(buf, 76 "HTTP/1.1 %d %s\r\n" 77 "Date: %s\r\n" 78 "Connection: close\r\n", 79 res->status, status_str[res->status], tstmp)) { 80 goto err; 81 } 82 83 for (i = 0; i < NUM_RES_FIELDS; i++) { 84 if (res->field[i][0] != '\0' && 85 buffer_appendf(buf, "%s: %s\r\n", res_field_str[i], 86 res->field[i])) { 87 goto err; 88 } 89 } 90 91 if (buffer_appendf(buf, "\r\n")) { 92 goto err; 93 } 94 95 return 0; 96 err: 97 memset(buf, 0, sizeof(*buf)); 98 return S_INTERNAL_SERVER_ERROR; 99 } 100 101 enum status 102 http_send_buf(int fd, struct buffer *buf) 103 { 104 ssize_t r; 105 106 if (buf == NULL) { 107 return S_INTERNAL_SERVER_ERROR; 108 } 109 110 while (buf->len > 0) { 111 if ((r = write(fd, buf->data, buf->len)) <= 0) { 112 if (errno == EAGAIN || errno == EWOULDBLOCK) { 113 /* 114 * socket is blocking, return normally. 115 * given the buffer still contains data, 116 * this indicates to the caller that we 117 * have been interrupted. 118 */ 119 return 0; 120 } else { 121 return S_REQUEST_TIMEOUT; 122 } 123 } 124 memmove(buf->data, buf->data + r, buf->len - r); 125 buf->len -= r; 126 } 127 128 return 0; 129 } 130 131 static void 132 decode(const char src[PATH_MAX], char dest[PATH_MAX]) 133 { 134 size_t i; 135 uint8_t n; 136 const char *s; 137 138 for (s = src, i = 0; *s; i++) { 139 if (*s == '%' && isxdigit((unsigned char)s[1]) && 140 isxdigit((unsigned char)s[2])) { 141 sscanf(s + 1, "%2hhx", &n); 142 dest[i] = n; 143 s += 3; 144 } else { 145 dest[i] = *s++; 146 } 147 } 148 dest[i] = '\0'; 149 } 150 151 enum status 152 http_recv_header(int fd, struct buffer *buf, int *done) 153 { 154 enum status s; 155 ssize_t r; 156 157 while (1) { 158 if ((r = read(fd, buf->data + buf->len, 159 sizeof(buf->data) - buf->len)) < 0) { 160 if (errno == EAGAIN || errno == EWOULDBLOCK) { 161 /* 162 * socket is drained, return normally, 163 * but set done to zero 164 */ 165 *done = 0; 166 return 0; 167 } else { 168 s = S_REQUEST_TIMEOUT; 169 goto err; 170 } 171 } else if (r == 0) { 172 /* 173 * unexpected EOF because the client probably 174 * hung up. This is technically a bad request, 175 * because it's incomplete 176 */ 177 s = S_BAD_REQUEST; 178 goto err; 179 } 180 buf->len += r; 181 182 /* check if we are done (header terminated) */ 183 if (buf->len >= 4 && !memcmp(buf->data + buf->len - 4, 184 "\r\n\r\n", 4)) { 185 break; 186 } 187 188 /* buffer is full or read over, but header is not terminated */ 189 if (r == 0 || buf->len == sizeof(buf->data)) { 190 s = S_REQUEST_TOO_LARGE; 191 goto err; 192 } 193 } 194 195 /* header is complete, remove last \r\n and set done */ 196 buf->len -= 2; 197 *done = 1; 198 199 return 0; 200 err: 201 memset(buf, 0, sizeof(*buf)); 202 return s; 203 } 204 205 enum status 206 http_parse_header(const char *h, struct request *req) 207 { 208 struct in6_addr addr; 209 size_t i, mlen; 210 const char *p, *q, *r, *s, *t; 211 char *m, *n; 212 213 /* empty the request struct */ 214 memset(req, 0, sizeof(*req)); 215 216 /* 217 * parse request line 218 */ 219 220 /* METHOD */ 221 for (i = 0; i < NUM_REQ_METHODS; i++) { 222 mlen = strlen(req_method_str[i]); 223 if (!strncmp(req_method_str[i], h, mlen)) { 224 req->method = i; 225 break; 226 } 227 } 228 if (i == NUM_REQ_METHODS) { 229 return S_METHOD_NOT_ALLOWED; 230 } 231 232 /* a single space must follow the method */ 233 if (h[mlen] != ' ') { 234 return S_BAD_REQUEST; 235 } 236 237 /* basis for next step */ 238 p = h + mlen + 1; 239 240 /* RESOURCE */ 241 242 /* 243 * path?query#fragment 244 * ^ ^ ^ ^ 245 * | | | | 246 * p r s q 247 * 248 */ 249 if (!(q = strchr(p, ' '))) { 250 return S_BAD_REQUEST; 251 } 252 253 /* search for first '?' */ 254 for (r = p; r < q; r++) { 255 if (!isprint(*r)) { 256 return S_BAD_REQUEST; 257 } 258 if (*r == '?') { 259 break; 260 } 261 } 262 if (r == q) { 263 /* not found */ 264 r = NULL; 265 } 266 267 /* search for first '#' */ 268 for (s = p; s < q; s++) { 269 if (!isprint(*s)) { 270 return S_BAD_REQUEST; 271 } 272 if (*s == '#') { 273 break; 274 } 275 } 276 if (s == q) { 277 /* not found */ 278 s = NULL; 279 } 280 281 if (r != NULL && s != NULL && s < r) { 282 /* 283 * '#' comes before '?' and thus the '?' is literal, 284 * because the query must come before the fragment 285 */ 286 r = NULL; 287 } 288 289 /* write path using temporary endpointer t */ 290 if (r != NULL) { 291 /* resource contains a query, path ends at r */ 292 t = r; 293 } else if (s != NULL) { 294 /* resource contains only a fragment, path ends at s */ 295 t = s; 296 } else { 297 /* resource contains no queries, path ends at q */ 298 t = q; 299 } 300 if ((size_t)(t - p + 1) > LEN(req->path)) { 301 return S_REQUEST_TOO_LARGE; 302 } 303 memcpy(req->path, p, t - p); 304 req->path[t - p] = '\0'; 305 decode(req->path, req->path); 306 307 /* write query if present */ 308 if (r != NULL) { 309 /* query ends either at s (if fragment present) or q */ 310 t = (s != NULL) ? s : q; 311 312 if ((size_t)(t - (r + 1) + 1) > LEN(req->query)) { 313 return S_REQUEST_TOO_LARGE; 314 } 315 memcpy(req->query, r + 1, t - (r + 1)); 316 req->query[t - (r + 1)] = '\0'; 317 } 318 319 /* write fragment if present */ 320 if (s != NULL) { 321 /* the fragment always starts at s + 1 and ends at q */ 322 if ((size_t)(q - (s + 1) + 1) > LEN(req->fragment)) { 323 return S_REQUEST_TOO_LARGE; 324 } 325 memcpy(req->fragment, s + 1, q - (s + 1)); 326 req->fragment[q - (s + 1)] = '\0'; 327 } 328 329 /* basis for next step */ 330 p = q + 1; 331 332 /* HTTP-VERSION */ 333 if (strncmp(p, "HTTP/", sizeof("HTTP/") - 1)) { 334 return S_BAD_REQUEST; 335 } 336 p += sizeof("HTTP/") - 1; 337 if (strncmp(p, "1.0", sizeof("1.0") - 1) && 338 strncmp(p, "1.1", sizeof("1.1") - 1)) { 339 return S_VERSION_NOT_SUPPORTED; 340 } 341 p += sizeof("1.*") - 1; 342 343 /* check terminator */ 344 if (strncmp(p, "\r\n", sizeof("\r\n") - 1)) { 345 return S_BAD_REQUEST; 346 } 347 348 /* basis for next step */ 349 p += sizeof("\r\n") - 1; 350 351 /* 352 * parse request-fields 353 */ 354 355 /* match field type */ 356 for (; *p != '\0';) { 357 for (i = 0; i < NUM_REQ_FIELDS; i++) { 358 if (!strncasecmp(p, req_field_str[i], 359 strlen(req_field_str[i]))) { 360 break; 361 } 362 } 363 if (i == NUM_REQ_FIELDS) { 364 /* unmatched field, skip this line */ 365 if (!(q = strstr(p, "\r\n"))) { 366 return S_BAD_REQUEST; 367 } 368 p = q + (sizeof("\r\n") - 1); 369 continue; 370 } 371 372 p += strlen(req_field_str[i]); 373 374 /* a single colon must follow the field name */ 375 if (*p != ':') { 376 return S_BAD_REQUEST; 377 } 378 379 /* skip whitespace */ 380 for (++p; *p == ' ' || *p == '\t'; p++) 381 ; 382 383 /* extract field content */ 384 if (!(q = strstr(p, "\r\n"))) { 385 return S_BAD_REQUEST; 386 } 387 if ((size_t)(q - p + 1) > LEN(req->field[i])) { 388 return S_REQUEST_TOO_LARGE; 389 } 390 memcpy(req->field[i], p, q - p); 391 req->field[i][q - p] = '\0'; 392 393 /* go to next line */ 394 p = q + (sizeof("\r\n") - 1); 395 } 396 397 /* 398 * clean up host 399 */ 400 401 m = strrchr(req->field[REQ_HOST], ':'); 402 n = strrchr(req->field[REQ_HOST], ']'); 403 404 /* strip port suffix but don't interfere with IPv6 bracket notation 405 * as per RFC 2732 */ 406 if (m && (!n || m > n)) { 407 /* port suffix must not be empty */ 408 if (*(m + 1) == '\0') { 409 return S_BAD_REQUEST; 410 } 411 *m = '\0'; 412 } 413 414 /* strip the brackets from the IPv6 notation and validate the address */ 415 if (n) { 416 /* brackets must be on the outside */ 417 if (req->field[REQ_HOST][0] != '[' || *(n + 1) != '\0') { 418 return S_BAD_REQUEST; 419 } 420 421 /* remove the right bracket */ 422 *n = '\0'; 423 m = req->field[REQ_HOST] + 1; 424 425 /* validate the contained IPv6 address */ 426 if (inet_pton(AF_INET6, m, &addr) != 1) { 427 return S_BAD_REQUEST; 428 } 429 430 /* copy it into the host field */ 431 memmove(req->field[REQ_HOST], m, n - m + 1); 432 } 433 434 return 0; 435 } 436 437 static void 438 encode(const char src[PATH_MAX], char dest[PATH_MAX]) 439 { 440 size_t i; 441 const char *s; 442 443 for (s = src, i = 0; *s && i < (PATH_MAX - 4); s++) { 444 if (iscntrl(*s) || (unsigned char)*s > 127) { 445 i += snprintf(dest + i, PATH_MAX - i, "%%%02X", 446 (unsigned char)*s); 447 } else { 448 dest[i] = *s; 449 i++; 450 } 451 } 452 dest[i] = '\0'; 453 } 454 455 static enum status 456 path_normalize(char *uri, int *redirect) 457 { 458 size_t len; 459 int last = 0; 460 char *p, *q; 461 462 /* require and skip first slash */ 463 if (uri[0] != '/') { 464 return S_BAD_REQUEST; 465 } 466 p = uri + 1; 467 468 /* get length of URI */ 469 len = strlen(p); 470 471 for (; !last; ) { 472 /* bound uri component within (p,q) */ 473 if (!(q = strchr(p, '/'))) { 474 q = strchr(p, '\0'); 475 last = 1; 476 } 477 478 if (*p == '\0') { 479 break; 480 } else if (p == q || (q - p == 1 && p[0] == '.')) { 481 /* "/" or "./" */ 482 goto squash; 483 } else if (q - p == 2 && p[0] == '.' && p[1] == '.') { 484 /* "../" */ 485 if (p != uri + 1) { 486 /* place p right after the previous / */ 487 for (p -= 2; p > uri && *p != '/'; p--); 488 p++; 489 } 490 goto squash; 491 } else { 492 /* move on */ 493 p = q + 1; 494 continue; 495 } 496 squash: 497 /* squash (p,q) into void */ 498 if (last) { 499 *p = '\0'; 500 len = p - uri; 501 } else { 502 memmove(p, q + 1, len - ((q + 1) - uri) + 2); 503 len -= (q + 1) - p; 504 } 505 if (redirect != NULL) { 506 *redirect = 1; 507 } 508 } 509 510 return 0; 511 } 512 513 static enum status 514 path_add_vhost_prefix(char uri[PATH_MAX], int *redirect, 515 const struct server *srv, const struct response *res) 516 { 517 if (srv->vhost && res->vhost && res->vhost->prefix) { 518 if (prepend(uri, PATH_MAX, res->vhost->prefix)) { 519 return S_REQUEST_TOO_LARGE; 520 } 521 if (redirect != NULL) { 522 *redirect = 1; 523 } 524 } 525 526 return 0; 527 } 528 529 static enum status 530 path_apply_prefix_mapping(char uri[PATH_MAX], int *redirect, 531 const struct server *srv, const struct response *res) 532 { 533 size_t i, len; 534 535 for (i = 0; i < srv->map_len; i++) { 536 len = strlen(srv->map[i].from); 537 if (!strncmp(uri, srv->map[i].from, len)) { 538 /* 539 * if vhosts are enabled only apply mappings 540 * defined for the current canonical host 541 */ 542 if (srv->vhost && res->vhost && srv->map[i].chost && 543 strcmp(srv->map[i].chost, res->vhost->chost)) { 544 continue; 545 } 546 547 /* swap out URI prefix */ 548 memmove(uri, uri + len, strlen(uri) + 1); 549 if (prepend(uri, PATH_MAX, srv->map[i].to)) { 550 return S_REQUEST_TOO_LARGE; 551 } 552 553 if (redirect != NULL) { 554 *redirect = 1; 555 } 556 557 /* break so we don't possibly hit an infinite loop */ 558 break; 559 } 560 } 561 562 return 0; 563 } 564 565 static enum status 566 path_ensure_dirslash(char uri[PATH_MAX], int *redirect) 567 { 568 size_t len; 569 570 /* append '/' to URI if not present */ 571 len = strlen(uri); 572 if (len + 1 + 1 > PATH_MAX) { 573 return S_REQUEST_TOO_LARGE; 574 } 575 if (len > 0 && uri[len - 1] != '/') { 576 uri[len] = '/'; 577 uri[len + 1] = '\0'; 578 if (redirect != NULL) { 579 *redirect = 1; 580 } 581 } 582 583 return 0; 584 } 585 586 static enum status 587 parse_range(const char *str, size_t size, size_t *lower, size_t *upper) 588 { 589 char first[FIELD_MAX], last[FIELD_MAX]; 590 const char *p, *q, *r, *err; 591 592 /* default to the complete range */ 593 *lower = 0; 594 *upper = size - 1; 595 596 /* done if no range-string is given */ 597 if (str == NULL || *str == '\0') { 598 return 0; 599 } 600 601 /* skip opening statement */ 602 if (strncmp(str, "bytes=", sizeof("bytes=") - 1)) { 603 return S_BAD_REQUEST; 604 } 605 p = str + (sizeof("bytes=") - 1); 606 607 /* check string (should only contain numbers and a hyphen) */ 608 for (r = p, q = NULL; *r != '\0'; r++) { 609 if (*r < '0' || *r > '9') { 610 if (*r == '-') { 611 if (q != NULL) { 612 /* we have already seen a hyphen */ 613 return S_BAD_REQUEST; 614 } else { 615 /* place q after the hyphen */ 616 q = r + 1; 617 } 618 } else if (*r == ',' && r > p) { 619 /* 620 * we refuse to accept range-lists out 621 * of spite towards this horrible part 622 * of the spec 623 */ 624 return S_RANGE_NOT_SATISFIABLE; 625 } else { 626 return S_BAD_REQUEST; 627 } 628 } 629 } 630 if (q == NULL) { 631 /* the input string must contain a hyphen */ 632 return S_BAD_REQUEST; 633 } 634 r = q + strlen(q); 635 636 /* 637 * byte-range=first-last\0 638 * ^ ^ ^ 639 * | | | 640 * p q r 641 */ 642 643 /* copy 'first' and 'last' to their respective arrays */ 644 if ((size_t)((q - 1) - p + 1) > sizeof(first) || 645 (size_t)(r - q + 1) > sizeof(last)) { 646 return S_REQUEST_TOO_LARGE; 647 } 648 memcpy(first, p, (q - 1) - p); 649 first[(q - 1) - p] = '\0'; 650 memcpy(last, q, r - q); 651 last[r - q] = '\0'; 652 653 if (first[0] != '\0') { 654 /* 655 * range has format "first-last" or "first-", 656 * i.e. return bytes 'first' to 'last' (or the 657 * last byte if 'last' is not given), 658 * inclusively, and byte-numbering beginning at 0 659 */ 660 *lower = strtonum(first, 0, MIN(SIZE_MAX, LLONG_MAX), 661 &err); 662 if (!err) { 663 if (last[0] != '\0') { 664 *upper = strtonum(last, 0, 665 MIN(SIZE_MAX, LLONG_MAX), 666 &err); 667 } else { 668 *upper = size - 1; 669 } 670 } 671 if (err) { 672 /* one of the strtonum()'s failed */ 673 return S_BAD_REQUEST; 674 } 675 676 /* check ranges */ 677 if (*lower > *upper || *lower >= size) { 678 return S_RANGE_NOT_SATISFIABLE; 679 } 680 681 /* adjust upper limit to be at most the last byte */ 682 *upper = MIN(*upper, size - 1); 683 } else { 684 /* last must not also be empty */ 685 if (last[0] == '\0') { 686 return S_BAD_REQUEST; 687 } 688 689 /* 690 * Range has format "-num", i.e. return the 'num' 691 * last bytes 692 */ 693 694 /* 695 * use upper as a temporary storage for 'num', 696 * as we know 'upper' is size - 1 697 */ 698 *upper = strtonum(last, 0, MIN(SIZE_MAX, LLONG_MAX), &err); 699 if (err) { 700 return S_BAD_REQUEST; 701 } 702 703 /* determine lower */ 704 if (*upper > size) { 705 /* more bytes requested than we have */ 706 *lower = 0; 707 } else { 708 *lower = size - *upper; 709 } 710 711 /* set upper to the correct value */ 712 *upper = size - 1; 713 } 714 715 return 0; 716 } 717 718 void 719 http_prepare_response(const struct request *req, struct response *res, 720 const struct server *srv) 721 { 722 enum status s, tmps; 723 struct in6_addr addr; 724 struct stat st; 725 struct tm tm = { 0 }; 726 size_t i; 727 int redirect, hasport, ipv6host; 728 static char tmppath[PATH_MAX]; 729 char *p, *mime; 730 731 /* empty all response fields */ 732 memset(res, 0, sizeof(*res)); 733 734 /* determine virtual host */ 735 if (srv->vhost) { 736 for (i = 0; i < srv->vhost_len; i++) { 737 if (!regexec(&(srv->vhost[i].re), 738 req->field[REQ_HOST], 0, NULL, 0)) { 739 /* we have a matching vhost */ 740 res->vhost = &(srv->vhost[i]); 741 break; 742 } 743 } 744 if (i == srv->vhost_len) { 745 s = S_NOT_FOUND; 746 goto err; 747 } 748 } 749 750 /* copy request-path to response-path and clean it up */ 751 redirect = 0; 752 memcpy(res->path, req->path, MIN(sizeof(res->path), sizeof(req->path))); 753 if ((tmps = path_normalize(res->path, &redirect)) || 754 (tmps = path_add_vhost_prefix(res->path, &redirect, srv, res)) || 755 (tmps = path_apply_prefix_mapping(res->path, &redirect, srv, res)) || 756 (tmps = path_normalize(res->path, &redirect))) { 757 s = tmps; 758 goto err; 759 } 760 761 /* redirect all non-canonical hosts to their canonical forms */ 762 if (srv->vhost && res->vhost && 763 strcmp(req->field[REQ_HOST], res->vhost->chost)) { 764 redirect = 1; 765 } 766 767 /* reject all non-well-known hidden targets (see RFC 8615) */ 768 if (strstr(res->path, "/.") && strncmp(res->path, "/.well-known/", 769 sizeof("/.well-known/") - 1)) { 770 s = S_FORBIDDEN; 771 goto err; 772 } 773 774 /* 775 * generate and stat internal path based on the cleaned up request 776 * path and the virtual host while ignoring query and fragment 777 * (valid according to RFC 3986) 778 */ 779 if (esnprintf(res->internal_path, sizeof(res->internal_path), "/%s/%s", 780 (srv->vhost && res->vhost) ? res->vhost->dir : "", 781 res->path)) { 782 s = S_REQUEST_TOO_LARGE; 783 goto err; 784 } 785 if ((tmps = path_normalize(res->internal_path, NULL))) { 786 s = tmps; 787 goto err; 788 } 789 if (stat(res->internal_path, &st) < 0) { 790 s = (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND; 791 goto err; 792 } 793 794 /* 795 * if the path points at a directory, make sure both the path 796 * and internal path have a trailing slash 797 */ 798 if (S_ISDIR(st.st_mode)) { 799 if ((tmps = path_ensure_dirslash(res->path, &redirect)) || 800 (tmps = path_ensure_dirslash(res->internal_path, NULL))) { 801 s = tmps; 802 goto err; 803 } 804 } 805 806 /* redirect if the path-cleanup necessitated it earlier */ 807 if (redirect) { 808 res->status = S_MOVED_PERMANENTLY; 809 810 /* encode path */ 811 encode(res->path, tmppath); 812 813 /* determine target location */ 814 if (srv->vhost && res->vhost) { 815 /* absolute redirection URL */ 816 817 /* do we need to add a port to the Location? */ 818 hasport = srv->port && strcmp(srv->port, "80"); 819 820 /* RFC 2732 specifies to use brackets for IPv6-addresses 821 * in URLs, so we need to check if our host is one and 822 * honor that later when we fill the "Location"-field */ 823 if ((ipv6host = inet_pton(AF_INET6, res->vhost->chost, 824 &addr)) < 0) { 825 s = S_INTERNAL_SERVER_ERROR; 826 goto err; 827 } 828 829 /* 830 * write location to response struct (re-including 831 * the query and fragment, if present) 832 */ 833 if (esnprintf(res->field[RES_LOCATION], 834 sizeof(res->field[RES_LOCATION]), 835 "//%s%s%s%s%s%s%s%s%s%s", 836 ipv6host ? "[" : "", 837 res->vhost->chost, 838 ipv6host ? "]" : "", 839 hasport ? ":" : "", 840 hasport ? srv->port : "", 841 tmppath, 842 req->query[0] ? "?" : "", 843 req->query, 844 req->fragment[0] ? "#" : "", 845 req->fragment)) { 846 s = S_REQUEST_TOO_LARGE; 847 goto err; 848 } 849 } else { 850 /* 851 * write relative redirection URI to response struct 852 * (re-including the query and fragment, if present) 853 */ 854 if (esnprintf(res->field[RES_LOCATION], 855 sizeof(res->field[RES_LOCATION]), 856 "%s%s%s%s%s", 857 tmppath, 858 req->query[0] ? "?" : "", 859 req->query, 860 req->fragment[0] ? "#" : "", 861 req->fragment)) { 862 s = S_REQUEST_TOO_LARGE; 863 goto err; 864 } 865 } 866 867 return; 868 } 869 870 if (S_ISDIR(st.st_mode)) { 871 /* 872 * when we serve a directory, we first check if there 873 * exists a directory index. If not, we either make 874 * a directory listing (if enabled) or send an error 875 */ 876 877 /* 878 * append docindex to internal_path temporarily 879 * (internal_path is guaranteed to end with '/') 880 */ 881 if (esnprintf(tmppath, sizeof(tmppath), "%s%s", 882 res->internal_path, srv->docindex)) { 883 s = S_REQUEST_TOO_LARGE; 884 goto err; 885 } 886 887 /* stat the temporary path, which must be a regular file */ 888 if (stat(tmppath, &st) < 0 || !S_ISREG(st.st_mode)) { 889 if (srv->listdirs) { 890 /* serve directory listing */ 891 892 /* check if directory is accessible */ 893 if (access(res->internal_path, R_OK) != 0) { 894 s = S_FORBIDDEN; 895 goto err; 896 } else { 897 res->status = S_OK; 898 } 899 res->type = RESTYPE_DIRLISTING; 900 901 if (esnprintf(res->field[RES_CONTENT_TYPE], 902 sizeof(res->field[RES_CONTENT_TYPE]), 903 "%s", "text/html; charset=utf-8")) { 904 s = S_INTERNAL_SERVER_ERROR; 905 goto err; 906 } 907 908 return; 909 } else { 910 /* reject */ 911 s = (!S_ISREG(st.st_mode) || errno == EACCES) ? 912 S_FORBIDDEN : S_NOT_FOUND; 913 goto err; 914 } 915 } else { 916 /* the docindex exists; copy tmppath to internal path */ 917 if (esnprintf(res->internal_path, 918 sizeof(res->internal_path), "%s", 919 tmppath)) { 920 s = S_REQUEST_TOO_LARGE; 921 goto err; 922 } 923 } 924 } 925 926 /* modified since */ 927 if (req->field[REQ_IF_MODIFIED_SINCE][0]) { 928 /* parse field */ 929 if (!strptime(req->field[REQ_IF_MODIFIED_SINCE], 930 "%a, %d %b %Y %T GMT", &tm)) { 931 s = S_BAD_REQUEST; 932 goto err; 933 } 934 935 /* compare with last modification date of the file */ 936 if (difftime(st.st_mtim.tv_sec, timegm(&tm)) <= 0) { 937 res->status = S_NOT_MODIFIED; 938 return; 939 } 940 } 941 942 /* range */ 943 if ((s = parse_range(req->field[REQ_RANGE], st.st_size, 944 &(res->file.lower), &(res->file.upper)))) { 945 if (s == S_RANGE_NOT_SATISFIABLE) { 946 res->status = S_RANGE_NOT_SATISFIABLE; 947 948 if (esnprintf(res->field[RES_CONTENT_RANGE], 949 sizeof(res->field[RES_CONTENT_RANGE]), 950 "bytes */%zu", st.st_size)) { 951 s = S_INTERNAL_SERVER_ERROR; 952 goto err; 953 } 954 955 return; 956 } else { 957 goto err; 958 } 959 } 960 961 /* mime */ 962 mime = "application/octet-stream"; 963 if ((p = strrchr(res->internal_path, '.'))) { 964 for (i = 0; i < LEN(mimes); i++) { 965 if (!strcmp(mimes[i].ext, p + 1)) { 966 mime = mimes[i].type; 967 break; 968 } 969 } 970 } 971 972 /* fill response struct */ 973 res->type = RESTYPE_FILE; 974 975 /* check if file is readable */ 976 res->status = (access(res->internal_path, R_OK)) ? S_FORBIDDEN : 977 (req->field[REQ_RANGE][0] != '\0') ? 978 S_PARTIAL_CONTENT : S_OK; 979 980 if (esnprintf(res->field[RES_ACCEPT_RANGES], 981 sizeof(res->field[RES_ACCEPT_RANGES]), 982 "%s", "bytes")) { 983 s = S_INTERNAL_SERVER_ERROR; 984 goto err; 985 } 986 987 if (esnprintf(res->field[RES_CONTENT_LENGTH], 988 sizeof(res->field[RES_CONTENT_LENGTH]), 989 "%zu", res->file.upper - res->file.lower + 1)) { 990 s = S_INTERNAL_SERVER_ERROR; 991 goto err; 992 } 993 if (req->field[REQ_RANGE][0] != '\0') { 994 if (esnprintf(res->field[RES_CONTENT_RANGE], 995 sizeof(res->field[RES_CONTENT_RANGE]), 996 "bytes %zd-%zd/%zu", res->file.lower, 997 res->file.upper, st.st_size)) { 998 s = S_INTERNAL_SERVER_ERROR; 999 goto err; 1000 } 1001 } 1002 if (esnprintf(res->field[RES_CONTENT_TYPE], 1003 sizeof(res->field[RES_CONTENT_TYPE]), 1004 "%s", mime)) { 1005 s = S_INTERNAL_SERVER_ERROR; 1006 goto err; 1007 } 1008 if (timestamp(res->field[RES_LAST_MODIFIED], 1009 sizeof(res->field[RES_LAST_MODIFIED]), 1010 st.st_mtim.tv_sec)) { 1011 s = S_INTERNAL_SERVER_ERROR; 1012 goto err; 1013 } 1014 1015 return; 1016 err: 1017 http_prepare_error_response(req, res, s); 1018 } 1019 1020 void 1021 http_prepare_error_response(const struct request *req, 1022 struct response *res, enum status s) 1023 { 1024 /* used later */ 1025 (void)req; 1026 1027 /* empty all response fields */ 1028 memset(res, 0, sizeof(*res)); 1029 1030 res->type = RESTYPE_ERROR; 1031 res->status = s; 1032 1033 if (esnprintf(res->field[RES_CONTENT_TYPE], 1034 sizeof(res->field[RES_CONTENT_TYPE]), 1035 "text/html; charset=utf-8")) { 1036 res->status = S_INTERNAL_SERVER_ERROR; 1037 } 1038 1039 if (res->status == S_METHOD_NOT_ALLOWED) { 1040 if (esnprintf(res->field[RES_ALLOW], 1041 sizeof(res->field[RES_ALLOW]), 1042 "Allow: GET, HEAD")) { 1043 res->status = S_INTERNAL_SERVER_ERROR; 1044 } 1045 } 1046 }