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