Annotation of embedaddon/lighttpd/src/request.c, revision 1.1
1.1 ! misho 1: #include "request.h"
! 2: #include "keyvalue.h"
! 3: #include "log.h"
! 4:
! 5: #include <sys/stat.h>
! 6:
! 7: #include <limits.h>
! 8: #include <stdlib.h>
! 9: #include <string.h>
! 10: #include <stdio.h>
! 11: #include <ctype.h>
! 12:
! 13: static int request_check_hostname(server *srv, connection *con, buffer *host) {
! 14: enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;
! 15: size_t i;
! 16: int label_len = 0;
! 17: size_t host_len;
! 18: char *colon;
! 19: int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */
! 20: int level = 0;
! 21:
! 22: UNUSED(srv);
! 23: UNUSED(con);
! 24:
! 25: /*
! 26: * hostport = host [ ":" port ]
! 27: * host = hostname | IPv4address | IPv6address
! 28: * hostname = *( domainlabel "." ) toplabel [ "." ]
! 29: * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
! 30: * toplabel = alpha | alpha *( alphanum | "-" ) alphanum
! 31: * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
! 32: * IPv6address = "[" ... "]"
! 33: * port = *digit
! 34: */
! 35:
! 36: /* no Host: */
! 37: if (!host || host->used == 0) return 0;
! 38:
! 39: host_len = host->used - 1;
! 40:
! 41: /* IPv6 adress */
! 42: if (host->ptr[0] == '[') {
! 43: char *c = host->ptr + 1;
! 44: int colon_cnt = 0;
! 45:
! 46: /* check portnumber */
! 47: for (; *c && *c != ']'; c++) {
! 48: if (*c == ':') {
! 49: if (++colon_cnt > 7) {
! 50: return -1;
! 51: }
! 52: } else if (!light_isxdigit(*c) && '.' != *c) {
! 53: return -1;
! 54: }
! 55: }
! 56:
! 57: /* missing ] */
! 58: if (!*c) {
! 59: return -1;
! 60: }
! 61:
! 62: /* check port */
! 63: if (*(c+1) == ':') {
! 64: for (c += 2; *c; c++) {
! 65: if (!light_isdigit(*c)) {
! 66: return -1;
! 67: }
! 68: }
! 69: }
! 70: return 0;
! 71: }
! 72:
! 73: if (NULL != (colon = memchr(host->ptr, ':', host_len))) {
! 74: char *c = colon + 1;
! 75:
! 76: /* check portnumber */
! 77: for (; *c; c++) {
! 78: if (!light_isdigit(*c)) return -1;
! 79: }
! 80:
! 81: /* remove the port from the host-len */
! 82: host_len = colon - host->ptr;
! 83: }
! 84:
! 85: /* Host is empty */
! 86: if (host_len == 0) return -1;
! 87:
! 88: /* if the hostname ends in a "." strip it */
! 89: if (host->ptr[host_len-1] == '.') {
! 90: /* shift port info one left */
! 91: if (NULL != colon) memmove(colon-1, colon, host->used - host_len);
! 92: else host->ptr[host_len-1] = '\0';
! 93: host_len -= 1;
! 94: host->used -= 1;
! 95: }
! 96:
! 97: if (host_len == 0) return -1;
! 98:
! 99: /* scan from the right and skip the \0 */
! 100: for (i = host_len; i-- > 0; ) {
! 101: const char c = host->ptr[i];
! 102:
! 103: switch (stage) {
! 104: case TOPLABEL:
! 105: if (c == '.') {
! 106: /* only switch stage, if this is not the last character */
! 107: if (i != host_len - 1) {
! 108: if (label_len == 0) {
! 109: return -1;
! 110: }
! 111:
! 112: /* check the first character at right of the dot */
! 113: if (is_ip == 0) {
! 114: if (!light_isalnum(host->ptr[i+1])) {
! 115: return -1;
! 116: }
! 117: } else if (!light_isdigit(host->ptr[i+1])) {
! 118: is_ip = 0;
! 119: } else if ('-' == host->ptr[i+1]) {
! 120: return -1;
! 121: } else {
! 122: /* just digits */
! 123: is_ip = 1;
! 124: }
! 125:
! 126: stage = DOMAINLABEL;
! 127:
! 128: label_len = 0;
! 129: level++;
! 130: } else if (i == 0) {
! 131: /* just a dot and nothing else is evil */
! 132: return -1;
! 133: }
! 134: } else if (i == 0) {
! 135: /* the first character of the hostname */
! 136: if (!light_isalnum(c)) {
! 137: return -1;
! 138: }
! 139: label_len++;
! 140: } else {
! 141: if (c != '-' && !light_isalnum(c)) {
! 142: return -1;
! 143: }
! 144: if (is_ip == -1) {
! 145: if (!light_isdigit(c)) is_ip = 0;
! 146: }
! 147: label_len++;
! 148: }
! 149:
! 150: break;
! 151: case DOMAINLABEL:
! 152: if (is_ip == 1) {
! 153: if (c == '.') {
! 154: if (label_len == 0) {
! 155: return -1;
! 156: }
! 157:
! 158: label_len = 0;
! 159: level++;
! 160: } else if (!light_isdigit(c)) {
! 161: return -1;
! 162: } else {
! 163: label_len++;
! 164: }
! 165: } else {
! 166: if (c == '.') {
! 167: if (label_len == 0) {
! 168: return -1;
! 169: }
! 170:
! 171: /* c is either - or alphanum here */
! 172: if ('-' == host->ptr[i+1]) {
! 173: return -1;
! 174: }
! 175:
! 176: label_len = 0;
! 177: level++;
! 178: } else if (i == 0) {
! 179: if (!light_isalnum(c)) {
! 180: return -1;
! 181: }
! 182: label_len++;
! 183: } else {
! 184: if (c != '-' && !light_isalnum(c)) {
! 185: return -1;
! 186: }
! 187: label_len++;
! 188: }
! 189: }
! 190:
! 191: break;
! 192: }
! 193: }
! 194:
! 195: /* a IP has to consist of 4 parts */
! 196: if (is_ip == 1 && level != 3) {
! 197: return -1;
! 198: }
! 199:
! 200: if (label_len == 0) {
! 201: return -1;
! 202: }
! 203:
! 204: return 0;
! 205: }
! 206:
! 207: #if 0
! 208: #define DUMP_HEADER
! 209: #endif
! 210:
! 211: static int http_request_split_value(array *vals, buffer *b) {
! 212: size_t i;
! 213: int state = 0;
! 214:
! 215: const char *current;
! 216: const char *token_start = NULL, *token_end = NULL;
! 217: /*
! 218: * parse
! 219: *
! 220: * val1, val2, val3, val4
! 221: *
! 222: * into a array (more or less a explode() incl. striping of whitespaces
! 223: */
! 224:
! 225: if (b->used == 0) return 0;
! 226:
! 227: current = b->ptr;
! 228: for (i = 0; i < b->used; ++i, ++current) {
! 229: data_string *ds;
! 230:
! 231: switch (state) {
! 232: case 0: /* find start of a token */
! 233: switch (*current) {
! 234: case ' ':
! 235: case '\t': /* skip white space */
! 236: case ',': /* skip empty token */
! 237: break;
! 238: case '\0': /* end of string */
! 239: return 0;
! 240: default:
! 241: /* found real data, switch to state 1 to find the end of the token */
! 242: token_start = token_end = current;
! 243: state = 1;
! 244: break;
! 245: }
! 246: break;
! 247: case 1: /* find end of token and last non white space character */
! 248: switch (*current) {
! 249: case ' ':
! 250: case '\t':
! 251: /* space - don't update token_end */
! 252: break;
! 253: case ',':
! 254: case '\0': /* end of string also marks the end of a token */
! 255: if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) {
! 256: ds = data_string_init();
! 257: }
! 258:
! 259: buffer_copy_string_len(ds->value, token_start, token_end-token_start+1);
! 260: array_insert_unique(vals, (data_unset *)ds);
! 261:
! 262: state = 0;
! 263: break;
! 264: default:
! 265: /* no white space, update token_end to include current character */
! 266: token_end = current;
! 267: break;
! 268: }
! 269: break;
! 270: }
! 271: }
! 272:
! 273: return 0;
! 274: }
! 275:
! 276: static int request_uri_is_valid_char(unsigned char c) {
! 277: if (c <= 32) return 0;
! 278: if (c == 127) return 0;
! 279: if (c == 255) return 0;
! 280:
! 281: return 1;
! 282: }
! 283:
! 284: int http_request_parse(server *srv, connection *con) {
! 285: char *uri = NULL, *proto = NULL, *method = NULL, con_length_set;
! 286: int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding;
! 287: char *value = NULL, *key = NULL;
! 288: char *reqline_host = NULL;
! 289: int reqline_hostlen = 0;
! 290:
! 291: enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET;
! 292:
! 293: int line = 0;
! 294:
! 295: int request_line_stage = 0;
! 296: size_t i, first;
! 297:
! 298: int done = 0;
! 299:
! 300: /*
! 301: * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
! 302: * Option : "^([-a-zA-Z]+): (.+)$"
! 303: * End : "^$"
! 304: */
! 305:
! 306: if (con->conf.log_request_header) {
! 307: log_error_write(srv, __FILE__, __LINE__, "sdsdSb",
! 308: "fd:", con->fd,
! 309: "request-len:", con->request.request->used,
! 310: "\n", con->request.request);
! 311: }
! 312:
! 313: if (con->request_count > 1 &&
! 314: con->request.request->ptr[0] == '\r' &&
! 315: con->request.request->ptr[1] == '\n') {
! 316: /* we are in keep-alive and might get \r\n after a previous POST request.*/
! 317:
! 318: buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, con->request.request->used - 1 - 2);
! 319: } else {
! 320: /* fill the local request buffer */
! 321: buffer_copy_string_buffer(con->parse_request, con->request.request);
! 322: }
! 323:
! 324: keep_alive_set = 0;
! 325: con_length_set = 0;
! 326:
! 327: /* parse the first line of the request
! 328: *
! 329: * should be:
! 330: *
! 331: * <method> <uri> <protocol>\r\n
! 332: * */
! 333: for (i = 0, first = 0; i < con->parse_request->used && line == 0; i++) {
! 334: char *cur = con->parse_request->ptr + i;
! 335:
! 336: switch(*cur) {
! 337: case '\r':
! 338: if (con->parse_request->ptr[i+1] == '\n') {
! 339: http_method_t r;
! 340: char *nuri = NULL;
! 341: size_t j;
! 342:
! 343: /* \r\n -> \0\0 */
! 344: con->parse_request->ptr[i] = '\0';
! 345: con->parse_request->ptr[i+1] = '\0';
! 346:
! 347: buffer_copy_string_len(con->request.request_line, con->parse_request->ptr, i);
! 348:
! 349: if (request_line_stage != 2) {
! 350: con->http_status = 400;
! 351: con->response.keep_alive = 0;
! 352: con->keep_alive = 0;
! 353:
! 354: if (srv->srvconf.log_request_header_on_error) {
! 355: log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400");
! 356: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 357: "request-header:\n",
! 358: con->request.request);
! 359: }
! 360: return 0;
! 361: }
! 362:
! 363: proto = con->parse_request->ptr + first;
! 364:
! 365: *(uri - 1) = '\0';
! 366: *(proto - 1) = '\0';
! 367:
! 368: /* we got the first one :) */
! 369: if (HTTP_METHOD_UNSET == (r = get_http_method_key(method))) {
! 370: con->http_status = 501;
! 371: con->response.keep_alive = 0;
! 372: con->keep_alive = 0;
! 373:
! 374: if (srv->srvconf.log_request_header_on_error) {
! 375: log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501");
! 376: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 377: "request-header:\n",
! 378: con->request.request);
! 379: }
! 380:
! 381: return 0;
! 382: }
! 383:
! 384: con->request.http_method = r;
! 385:
! 386: /*
! 387: * RFC2616 says:
! 388: *
! 389: * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
! 390: *
! 391: * */
! 392: if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) {
! 393: char * major = proto + sizeof("HTTP/") - 1;
! 394: char * minor = strchr(major, '.');
! 395: char *err = NULL;
! 396: int major_num = 0, minor_num = 0;
! 397:
! 398: int invalid_version = 0;
! 399:
! 400: if (NULL == minor || /* no dot */
! 401: minor == major || /* no major */
! 402: *(minor + 1) == '\0' /* no minor */) {
! 403: invalid_version = 1;
! 404: } else {
! 405: *minor = '\0';
! 406: major_num = strtol(major, &err, 10);
! 407:
! 408: if (*err != '\0') invalid_version = 1;
! 409:
! 410: *minor++ = '.';
! 411: minor_num = strtol(minor, &err, 10);
! 412:
! 413: if (*err != '\0') invalid_version = 1;
! 414: }
! 415:
! 416: if (invalid_version) {
! 417: con->http_status = 400;
! 418: con->keep_alive = 0;
! 419:
! 420: if (srv->srvconf.log_request_header_on_error) {
! 421: log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
! 422: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 423: "request-header:\n",
! 424: con->request.request);
! 425: }
! 426: return 0;
! 427: }
! 428:
! 429: if (major_num == 1 && minor_num == 1) {
! 430: con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0;
! 431: } else if (major_num == 1 && minor_num == 0) {
! 432: con->request.http_version = HTTP_VERSION_1_0;
! 433: } else {
! 434: con->http_status = 505;
! 435:
! 436: if (srv->srvconf.log_request_header_on_error) {
! 437: log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505");
! 438: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 439: "request-header:\n",
! 440: con->request.request);
! 441: }
! 442: return 0;
! 443: }
! 444: } else {
! 445: con->http_status = 400;
! 446: con->keep_alive = 0;
! 447:
! 448: if (srv->srvconf.log_request_header_on_error) {
! 449: log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
! 450: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 451: "request-header:\n",
! 452: con->request.request);
! 453: }
! 454: return 0;
! 455: }
! 456:
! 457: if (0 == strncmp(uri, "http://", 7) &&
! 458: NULL != (nuri = strchr(uri + 7, '/'))) {
! 459: reqline_host = uri + 7;
! 460: reqline_hostlen = nuri - reqline_host;
! 461:
! 462: buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
! 463: } else if (0 == strncmp(uri, "https://", 8) &&
! 464: NULL != (nuri = strchr(uri + 8, '/'))) {
! 465: reqline_host = uri + 8;
! 466: reqline_hostlen = nuri - reqline_host;
! 467:
! 468: buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
! 469: } else {
! 470: /* everything looks good so far */
! 471: buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
! 472: }
! 473:
! 474: /* check uri for invalid characters */
! 475: for (j = 0; j < con->request.uri->used - 1; j++) {
! 476: if (!request_uri_is_valid_char(con->request.uri->ptr[j])) {
! 477: unsigned char buf[2];
! 478: con->http_status = 400;
! 479: con->keep_alive = 0;
! 480:
! 481: if (srv->srvconf.log_request_header_on_error) {
! 482: buf[0] = con->request.uri->ptr[j];
! 483: buf[1] = '\0';
! 484:
! 485: if (con->request.uri->ptr[j] > 32 &&
! 486: con->request.uri->ptr[j] != 127) {
! 487: /* the character is printable -> print it */
! 488: log_error_write(srv, __FILE__, __LINE__, "ss",
! 489: "invalid character in URI -> 400",
! 490: buf);
! 491: } else {
! 492: /* a control-character, print ascii-code */
! 493: log_error_write(srv, __FILE__, __LINE__, "sd",
! 494: "invalid character in URI -> 400",
! 495: con->request.uri->ptr[j]);
! 496: }
! 497:
! 498: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 499: "request-header:\n",
! 500: con->request.request);
! 501: }
! 502:
! 503: return 0;
! 504: }
! 505: }
! 506:
! 507: buffer_copy_string_buffer(con->request.orig_uri, con->request.uri);
! 508:
! 509: con->http_status = 0;
! 510:
! 511: i++;
! 512: line++;
! 513: first = i+1;
! 514: }
! 515: break;
! 516: case ' ':
! 517: switch(request_line_stage) {
! 518: case 0:
! 519: /* GET|POST|... */
! 520: method = con->parse_request->ptr + first;
! 521: first = i + 1;
! 522: break;
! 523: case 1:
! 524: /* /foobar/... */
! 525: uri = con->parse_request->ptr + first;
! 526: first = i + 1;
! 527: break;
! 528: default:
! 529: /* ERROR, one space to much */
! 530: con->http_status = 400;
! 531: con->response.keep_alive = 0;
! 532: con->keep_alive = 0;
! 533:
! 534: if (srv->srvconf.log_request_header_on_error) {
! 535: log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400");
! 536: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 537: "request-header:\n",
! 538: con->request.request);
! 539: }
! 540: return 0;
! 541: }
! 542:
! 543: request_line_stage++;
! 544: break;
! 545: }
! 546: }
! 547:
! 548: in_folding = 0;
! 549:
! 550: if (con->request.uri->used == 1) {
! 551: con->http_status = 400;
! 552: con->response.keep_alive = 0;
! 553: con->keep_alive = 0;
! 554:
! 555: if (srv->srvconf.log_request_header_on_error) {
! 556: log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400");
! 557: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 558: "request-header:\n",
! 559: con->request.request);
! 560: }
! 561: return 0;
! 562: }
! 563:
! 564: if (reqline_host) {
! 565: /* Insert as host header */
! 566: data_string *ds;
! 567:
! 568: if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
! 569: ds = data_string_init();
! 570: }
! 571:
! 572: buffer_copy_string_len(ds->key, CONST_STR_LEN("Host"));
! 573: buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen);
! 574: array_insert_unique(con->request.headers, (data_unset *)ds);
! 575: con->request.http_host = ds->value;
! 576: }
! 577:
! 578: for (; i < con->parse_request->used && !done; i++) {
! 579: char *cur = con->parse_request->ptr + i;
! 580:
! 581: if (is_key) {
! 582: size_t j;
! 583: int got_colon = 0;
! 584:
! 585: /**
! 586: * 1*<any CHAR except CTLs or separators>
! 587: * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127)
! 588: *
! 589: */
! 590: switch(*cur) {
! 591: case ':':
! 592: is_key = 0;
! 593:
! 594: value = cur + 1;
! 595:
! 596: if (is_ws_after_key == 0) {
! 597: key_len = i - first;
! 598: }
! 599: is_ws_after_key = 0;
! 600:
! 601: break;
! 602: case '(':
! 603: case ')':
! 604: case '<':
! 605: case '>':
! 606: case '@':
! 607: case ',':
! 608: case ';':
! 609: case '\\':
! 610: case '\"':
! 611: case '/':
! 612: case '[':
! 613: case ']':
! 614: case '?':
! 615: case '=':
! 616: case '{':
! 617: case '}':
! 618: con->http_status = 400;
! 619: con->keep_alive = 0;
! 620: con->response.keep_alive = 0;
! 621:
! 622: if (srv->srvconf.log_request_header_on_error) {
! 623: log_error_write(srv, __FILE__, __LINE__, "sbsds",
! 624: "invalid character in key", con->request.request, cur, *cur, "-> 400");
! 625:
! 626: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 627: "request-header:\n",
! 628: con->request.request);
! 629: }
! 630: return 0;
! 631: case ' ':
! 632: case '\t':
! 633: if (i == first) {
! 634: is_key = 0;
! 635: in_folding = 1;
! 636: value = cur;
! 637:
! 638: break;
! 639: }
! 640:
! 641:
! 642: key_len = i - first;
! 643:
! 644: /* skip every thing up to the : */
! 645: for (j = 1; !got_colon; j++) {
! 646: switch(con->parse_request->ptr[j + i]) {
! 647: case ' ':
! 648: case '\t':
! 649: /* skip WS */
! 650: continue;
! 651: case ':':
! 652: /* ok, done; handle the colon the usual way */
! 653:
! 654: i += j - 1;
! 655: got_colon = 1;
! 656: is_ws_after_key = 1; /* we already know the key length */
! 657:
! 658: break;
! 659: default:
! 660: /* error */
! 661:
! 662: if (srv->srvconf.log_request_header_on_error) {
! 663: log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400");
! 664: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 665: "request-header:\n",
! 666: con->request.request);
! 667: }
! 668:
! 669: con->http_status = 400;
! 670: con->response.keep_alive = 0;
! 671: con->keep_alive = 0;
! 672:
! 673: return 0;
! 674: }
! 675: }
! 676:
! 677: break;
! 678: case '\r':
! 679: if (con->parse_request->ptr[i+1] == '\n' && i == first) {
! 680: /* End of Header */
! 681: con->parse_request->ptr[i] = '\0';
! 682: con->parse_request->ptr[i+1] = '\0';
! 683:
! 684: i++;
! 685:
! 686: done = 1;
! 687: } else {
! 688: if (srv->srvconf.log_request_header_on_error) {
! 689: log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400");
! 690: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 691: "request-header:\n",
! 692: con->request.request);
! 693: }
! 694:
! 695: con->http_status = 400;
! 696: con->keep_alive = 0;
! 697: con->response.keep_alive = 0;
! 698: return 0;
! 699: }
! 700: break;
! 701: default:
! 702: if (*cur < 32 || ((unsigned char)*cur) >= 127) {
! 703: con->http_status = 400;
! 704: con->keep_alive = 0;
! 705: con->response.keep_alive = 0;
! 706:
! 707: if (srv->srvconf.log_request_header_on_error) {
! 708: log_error_write(srv, __FILE__, __LINE__, "sbsds",
! 709: "invalid character in key", con->request.request, cur, *cur, "-> 400");
! 710:
! 711: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 712: "request-header:\n",
! 713: con->request.request);
! 714: }
! 715:
! 716: return 0;
! 717: }
! 718: /* ok */
! 719: break;
! 720: }
! 721: } else {
! 722: switch(*cur) {
! 723: case '\r':
! 724: if (con->parse_request->ptr[i+1] == '\n') {
! 725: data_string *ds = NULL;
! 726:
! 727: /* End of Headerline */
! 728: con->parse_request->ptr[i] = '\0';
! 729: con->parse_request->ptr[i+1] = '\0';
! 730:
! 731: if (in_folding) {
! 732: buffer *key_b;
! 733: /**
! 734: * we use a evil hack to handle the line-folding
! 735: *
! 736: * As array_insert_unique() deletes 'ds' in the case of a duplicate
! 737: * ds points somewhere and we get a evil crash. As a solution we keep the old
! 738: * "key" and get the current value from the hash and append us
! 739: *
! 740: * */
! 741:
! 742: if (!key || !key_len) {
! 743: /* 400 */
! 744:
! 745: if (srv->srvconf.log_request_header_on_error) {
! 746: log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400");
! 747:
! 748: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 749: "request-header:\n",
! 750: con->request.request);
! 751: }
! 752:
! 753:
! 754: con->http_status = 400;
! 755: con->keep_alive = 0;
! 756: con->response.keep_alive = 0;
! 757: return 0;
! 758: }
! 759:
! 760: key_b = buffer_init();
! 761: buffer_copy_string_len(key_b, key, key_len);
! 762:
! 763: if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key_b->ptr))) {
! 764: buffer_append_string(ds->value, value);
! 765: }
! 766:
! 767: buffer_free(key_b);
! 768: } else {
! 769: int s_len;
! 770: key = con->parse_request->ptr + first;
! 771:
! 772: s_len = cur - value;
! 773:
! 774: /* strip trailing white-spaces */
! 775: for (; s_len > 0 &&
! 776: (value[s_len - 1] == ' ' ||
! 777: value[s_len - 1] == '\t'); s_len--);
! 778:
! 779: value[s_len] = '\0';
! 780:
! 781: if (s_len > 0) {
! 782: int cmp = 0;
! 783: if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
! 784: ds = data_string_init();
! 785: }
! 786: buffer_copy_string_len(ds->key, key, key_len);
! 787: buffer_copy_string_len(ds->value, value, s_len);
! 788:
! 789: /* retreive values
! 790: *
! 791: *
! 792: * the list of options is sorted to simplify the search
! 793: */
! 794:
! 795: if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
! 796: array *vals;
! 797: size_t vi;
! 798:
! 799: /* split on , */
! 800:
! 801: vals = srv->split_vals;
! 802:
! 803: array_reset(vals);
! 804:
! 805: http_request_split_value(vals, ds->value);
! 806:
! 807: for (vi = 0; vi < vals->used; vi++) {
! 808: data_string *dsv = (data_string *)vals->data[vi];
! 809:
! 810: if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
! 811: keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
! 812:
! 813: break;
! 814: } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
! 815: keep_alive_set = HTTP_CONNECTION_CLOSE;
! 816:
! 817: break;
! 818: }
! 819: }
! 820:
! 821: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
! 822: char *err;
! 823: unsigned long int r;
! 824: size_t j;
! 825:
! 826: if (con_length_set) {
! 827: con->http_status = 400;
! 828: con->keep_alive = 0;
! 829:
! 830: if (srv->srvconf.log_request_header_on_error) {
! 831: log_error_write(srv, __FILE__, __LINE__, "s",
! 832: "duplicate Content-Length-header -> 400");
! 833: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 834: "request-header:\n",
! 835: con->request.request);
! 836: }
! 837: array_insert_unique(con->request.headers, (data_unset *)ds);
! 838: return 0;
! 839: }
! 840:
! 841: if (ds->value->used == 0) SEGFAULT();
! 842:
! 843: for (j = 0; j < ds->value->used - 1; j++) {
! 844: char c = ds->value->ptr[j];
! 845: if (!isdigit((unsigned char)c)) {
! 846: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 847: "content-length broken:", ds->value, "-> 400");
! 848:
! 849: con->http_status = 400;
! 850: con->keep_alive = 0;
! 851:
! 852: array_insert_unique(con->request.headers, (data_unset *)ds);
! 853: return 0;
! 854: }
! 855: }
! 856:
! 857: r = strtoul(ds->value->ptr, &err, 10);
! 858:
! 859: if (*err == '\0') {
! 860: con_length_set = 1;
! 861: con->request.content_length = r;
! 862: } else {
! 863: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 864: "content-length broken:", ds->value, "-> 400");
! 865:
! 866: con->http_status = 400;
! 867: con->keep_alive = 0;
! 868:
! 869: array_insert_unique(con->request.headers, (data_unset *)ds);
! 870: return 0;
! 871: }
! 872: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
! 873: /* if dup, only the first one will survive */
! 874: if (!con->request.http_content_type) {
! 875: con->request.http_content_type = ds->value->ptr;
! 876: } else {
! 877: con->http_status = 400;
! 878: con->keep_alive = 0;
! 879:
! 880: if (srv->srvconf.log_request_header_on_error) {
! 881: log_error_write(srv, __FILE__, __LINE__, "s",
! 882: "duplicate Content-Type-header -> 400");
! 883: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 884: "request-header:\n",
! 885: con->request.request);
! 886: }
! 887: array_insert_unique(con->request.headers, (data_unset *)ds);
! 888: return 0;
! 889: }
! 890: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) {
! 891: /* HTTP 2616 8.2.3
! 892: * Expect: 100-continue
! 893: *
! 894: * -> (10.1.1) 100 (read content, process request, send final status-code)
! 895: * -> (10.4.18) 417 (close)
! 896: *
! 897: * (not handled at all yet, we always send 417 here)
! 898: *
! 899: * What has to be added ?
! 900: * 1. handling of chunked request body
! 901: * 2. out-of-order sending from the HTTP/1.1 100 Continue
! 902: * header
! 903: *
! 904: */
! 905:
! 906: if (srv->srvconf.reject_expect_100_with_417 && 0 == buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) {
! 907: con->http_status = 417;
! 908: con->keep_alive = 0;
! 909: array_insert_unique(con->request.headers, (data_unset *)ds);
! 910: return 0;
! 911: }
! 912: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
! 913: if (reqline_host) {
! 914: /* ignore all host: headers as we got the host in the request line */
! 915: ds->free((data_unset*) ds);
! 916: ds = NULL;
! 917: } else if (!con->request.http_host) {
! 918: con->request.http_host = ds->value;
! 919: } else {
! 920: con->http_status = 400;
! 921: con->keep_alive = 0;
! 922:
! 923: if (srv->srvconf.log_request_header_on_error) {
! 924: log_error_write(srv, __FILE__, __LINE__, "s",
! 925: "duplicate Host-header -> 400");
! 926: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 927: "request-header:\n",
! 928: con->request.request);
! 929: }
! 930: array_insert_unique(con->request.headers, (data_unset *)ds);
! 931: return 0;
! 932: }
! 933: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
! 934: /* Proxies sometimes send dup headers
! 935: * if they are the same we ignore the second
! 936: * if not, we raise an error */
! 937: if (!con->request.http_if_modified_since) {
! 938: con->request.http_if_modified_since = ds->value->ptr;
! 939: } else if (0 == strcasecmp(con->request.http_if_modified_since,
! 940: ds->value->ptr)) {
! 941: /* ignore it if they are the same */
! 942:
! 943: ds->free((data_unset *)ds);
! 944: ds = NULL;
! 945: } else {
! 946: con->http_status = 400;
! 947: con->keep_alive = 0;
! 948:
! 949: if (srv->srvconf.log_request_header_on_error) {
! 950: log_error_write(srv, __FILE__, __LINE__, "s",
! 951: "duplicate If-Modified-Since header -> 400");
! 952: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 953: "request-header:\n",
! 954: con->request.request);
! 955: }
! 956: array_insert_unique(con->request.headers, (data_unset *)ds);
! 957: return 0;
! 958: }
! 959: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
! 960: /* if dup, only the first one will survive */
! 961: if (!con->request.http_if_none_match) {
! 962: con->request.http_if_none_match = ds->value->ptr;
! 963: } else {
! 964: ds->free((data_unset*) ds);
! 965: ds = NULL;
! 966: }
! 967: } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) {
! 968: if (!con->request.http_range) {
! 969: /* bytes=.*-.* */
! 970:
! 971: if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) &&
! 972: NULL != strchr(ds->value->ptr+6, '-')) {
! 973:
! 974: /* if dup, only the first one will survive */
! 975: con->request.http_range = ds->value->ptr + 6;
! 976: }
! 977: } else {
! 978: con->http_status = 400;
! 979: con->keep_alive = 0;
! 980:
! 981: if (srv->srvconf.log_request_header_on_error) {
! 982: log_error_write(srv, __FILE__, __LINE__, "s",
! 983: "duplicate Range-header -> 400");
! 984: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 985: "request-header:\n",
! 986: con->request.request);
! 987: }
! 988: array_insert_unique(con->request.headers, (data_unset *)ds);
! 989: return 0;
! 990: }
! 991: }
! 992:
! 993: if (ds) array_insert_unique(con->request.headers, (data_unset *)ds);
! 994: } else {
! 995: /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
! 996: }
! 997: }
! 998:
! 999: i++;
! 1000: first = i+1;
! 1001: is_key = 1;
! 1002: value = NULL;
! 1003: #if 0
! 1004: /**
! 1005: * for Bug 1230 keep the key_len a live
! 1006: */
! 1007: key_len = 0;
! 1008: #endif
! 1009: in_folding = 0;
! 1010: } else {
! 1011: if (srv->srvconf.log_request_header_on_error) {
! 1012: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 1013: "CR without LF", con->request.request, "-> 400");
! 1014: }
! 1015:
! 1016: con->http_status = 400;
! 1017: con->keep_alive = 0;
! 1018: con->response.keep_alive = 0;
! 1019: return 0;
! 1020: }
! 1021: break;
! 1022: case ' ':
! 1023: case '\t':
! 1024: /* strip leading WS */
! 1025: if (value == cur) value = cur+1;
! 1026: default:
! 1027: if (*cur >= 0 && *cur < 32 && *cur != '\t') {
! 1028: if (srv->srvconf.log_request_header_on_error) {
! 1029: log_error_write(srv, __FILE__, __LINE__, "sds",
! 1030: "invalid char in header", (int)*cur, "-> 400");
! 1031: }
! 1032:
! 1033: con->http_status = 400;
! 1034: con->keep_alive = 0;
! 1035:
! 1036: return 0;
! 1037: }
! 1038: break;
! 1039: }
! 1040: }
! 1041: }
! 1042:
! 1043: con->header_len = i;
! 1044:
! 1045: /* do some post-processing */
! 1046:
! 1047: if (con->request.http_version == HTTP_VERSION_1_1) {
! 1048: if (keep_alive_set != HTTP_CONNECTION_CLOSE) {
! 1049: /* no Connection-Header sent */
! 1050:
! 1051: /* HTTP/1.1 -> keep-alive default TRUE */
! 1052: con->keep_alive = 1;
! 1053: } else {
! 1054: con->keep_alive = 0;
! 1055: }
! 1056:
! 1057: /* RFC 2616, 14.23 */
! 1058: if (con->request.http_host == NULL ||
! 1059: buffer_is_empty(con->request.http_host)) {
! 1060: con->http_status = 400;
! 1061: con->response.keep_alive = 0;
! 1062: con->keep_alive = 0;
! 1063:
! 1064: if (srv->srvconf.log_request_header_on_error) {
! 1065: log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400");
! 1066: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 1067: "request-header:\n",
! 1068: con->request.request);
! 1069: }
! 1070: return 0;
! 1071: }
! 1072: } else {
! 1073: if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
! 1074: /* no Connection-Header sent */
! 1075:
! 1076: /* HTTP/1.0 -> keep-alive default FALSE */
! 1077: con->keep_alive = 1;
! 1078: } else {
! 1079: con->keep_alive = 0;
! 1080: }
! 1081: }
! 1082:
! 1083: /* check hostname field if it is set */
! 1084: if (NULL != con->request.http_host &&
! 1085: 0 != request_check_hostname(srv, con, con->request.http_host)) {
! 1086:
! 1087: if (srv->srvconf.log_request_header_on_error) {
! 1088: log_error_write(srv, __FILE__, __LINE__, "s",
! 1089: "Invalid Hostname -> 400");
! 1090: log_error_write(srv, __FILE__, __LINE__, "Sb",
! 1091: "request-header:\n",
! 1092: con->request.request);
! 1093: }
! 1094:
! 1095: con->http_status = 400;
! 1096: con->response.keep_alive = 0;
! 1097: con->keep_alive = 0;
! 1098:
! 1099: return 0;
! 1100: }
! 1101:
! 1102: switch(con->request.http_method) {
! 1103: case HTTP_METHOD_GET:
! 1104: case HTTP_METHOD_HEAD:
! 1105: /* content-length is forbidden for those */
! 1106: if (con_length_set && con->request.content_length != 0) {
! 1107: /* content-length is missing */
! 1108: log_error_write(srv, __FILE__, __LINE__, "s",
! 1109: "GET/HEAD with content-length -> 400");
! 1110:
! 1111: con->keep_alive = 0;
! 1112: con->http_status = 400;
! 1113: return 0;
! 1114: }
! 1115: break;
! 1116: case HTTP_METHOD_POST:
! 1117: /* content-length is required for them */
! 1118: if (!con_length_set) {
! 1119: /* content-length is missing */
! 1120: log_error_write(srv, __FILE__, __LINE__, "s",
! 1121: "POST-request, but content-length missing -> 411");
! 1122:
! 1123: con->keep_alive = 0;
! 1124: con->http_status = 411;
! 1125: return 0;
! 1126:
! 1127: }
! 1128: break;
! 1129: default:
! 1130: /* the may have a content-length */
! 1131: break;
! 1132: }
! 1133:
! 1134:
! 1135: /* check if we have read post data */
! 1136: if (con_length_set) {
! 1137: /* don't handle more the SSIZE_MAX bytes in content-length */
! 1138: if (con->request.content_length > SSIZE_MAX) {
! 1139: con->http_status = 413;
! 1140: con->keep_alive = 0;
! 1141:
! 1142: log_error_write(srv, __FILE__, __LINE__, "sos",
! 1143: "request-size too long:", (off_t) con->request.content_length, "-> 413");
! 1144: return 0;
! 1145: }
! 1146:
! 1147: /* divide by 1024 as srvconf.max_request_size is in kBytes */
! 1148: if (srv->srvconf.max_request_size != 0 &&
! 1149: (con->request.content_length >> 10) > srv->srvconf.max_request_size) {
! 1150: /* the request body itself is larger then
! 1151: * our our max_request_size
! 1152: */
! 1153:
! 1154: con->http_status = 413;
! 1155: con->keep_alive = 0;
! 1156:
! 1157: log_error_write(srv, __FILE__, __LINE__, "sos",
! 1158: "request-size too long:", (off_t) con->request.content_length, "-> 413");
! 1159: return 0;
! 1160: }
! 1161:
! 1162:
! 1163: /* we have content */
! 1164: if (con->request.content_length != 0) {
! 1165: return 1;
! 1166: }
! 1167: }
! 1168:
! 1169: return 0;
! 1170: }
! 1171:
! 1172: int http_request_header_finished(server *srv, connection *con) {
! 1173: UNUSED(srv);
! 1174:
! 1175: if (con->request.request->used < 5) return 0;
! 1176:
! 1177: if (0 == memcmp(con->request.request->ptr + con->request.request->used - 5, "\r\n\r\n", 4)) return 1;
! 1178: if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1;
! 1179:
! 1180: return 0;
! 1181: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>