/* parses range-resp-spec and inits spec, returns true on success */ static int httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen) { const char *p; assert(spec); spec->offset = spec->length = range_spec_unknown; if (flen < 2) return 0; /* is spec given ? */ if (*field == '*') return 1; /* check format, must be %d-%d */ if (!((p = strchr(field, '-')) && (p - field < flen))) { debug(68, 2) ("invalid (no '-') resp-range-spec near: '%s'\n", field); return 0; } /* parse offset */ if (!httpHeaderParseSize(field, &spec->offset)) return 0; p++; /* do we have last-pos ? */ if (p - field < flen) { squid_off_t last_pos; if (!httpHeaderParseSize(p, &last_pos)) return 0; spec->length = size_diff(last_pos + 1, spec->offset); } /* we managed to parse, check if the result makes sence */ if (known_spec(spec->length) && !spec->length) { debug(68, 2) ("invalid range (%ld += %ld) in resp-range-spec near: '%s'\n", (long int) spec->offset, (long int) spec->length, field); return 0; } return 1; }
/* returns true if ranges are valid; inits HttpHdrContRange */ int httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) { const char *p; assert(range && str); debug(68, 8) ("parsing content-range field: '%s'\n", str); /* check range type */ if (strncasecmp(str, "bytes ", 6)) return 0; str += 6; /* split */ if (!(p = strchr(str, '/'))) return 0; if (*str == '*') range->spec.offset = range->spec.length = range_spec_unknown; else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str)) return 0; p++; if (*p == '*') range->elength = range_spec_unknown; else if (!httpHeaderParseSize(p, &range->elength)) return 0; debug(68, 8) ("parsed content-range field: %" PRINTF_OFF_T "-%" PRINTF_OFF_T " / %" PRINTF_OFF_T "\n", range->spec.offset, range->spec.offset + range->spec.length - 1, range->elength); return 1; }
squid_off_t httpHeaderGetSize(const HttpHeader * hdr, http_hdr_type id) { HttpHeaderEntry *e; squid_off_t value = -1; int ok; assert_eid(id); assert(Headers[id].type == ftSize); /* must be of an appropriate type */ if ((e = httpHeaderFindEntry(hdr, id))) { ok = httpHeaderParseSize(strBuf(e->value), &value); httpHeaderNoteParsedEntry(e->id, e->value, !ok); } return value; }
/* parses range-spec and returns new object on success */ static HttpHdrRangeSpec * httpHdrRangeSpecParseCreate(const char *field, int flen) { HttpHdrRangeSpec spec = {range_spec_unknown, range_spec_unknown}; const char *p; if (flen < 2) return NULL; /* is it a suffix-byte-range-spec ? */ if (*field == '-') { if (!httpHeaderParseSize(field + 1, &spec.length)) return NULL; } else /* must have a '-' somewhere in _this_ field */ if (!((p = strchr(field, '-')) || (p - field >= flen))) { debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field); return NULL; } else { if (!httpHeaderParseSize(field, &spec.offset)) return NULL; p++; /* do we have last-pos ? */ if (p - field < flen) { ssize_t last_pos; if (!httpHeaderParseSize(p, &last_pos)) return NULL; spec.length = size_diff(last_pos + 1, spec.offset); } } /* we managed to parse, check if the result makes sence */ if (known_spec(spec.length) && !spec.length) { debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field); return NULL; } return httpHdrRangeSpecDup(&spec); }
int httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end) { const char *field_ptr = header_start; HttpHeaderEntry *e; assert(hdr); assert(header_start && header_end); debug(55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix(header_start, header_end)); HttpHeaderStats[hdr->owner].parsedCount++; if (memchr(header_start, '\0', header_end - header_start)) { debug(55, 1) ("WARNING: HTTP header contains NULL characters {%s}\n", getStringPrefix(header_start, header_end)); return httpHeaderReset(hdr); } /* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>. * continuation lines start with a (single) space or tab */ while (field_ptr < header_end) { const char *field_start = field_ptr; const char *field_end; do { const char *this_line = field_ptr; field_ptr = memchr(field_ptr, '\n', header_end - field_ptr); if (!field_ptr) return httpHeaderReset(hdr); /* missing <LF> */ field_end = field_ptr; field_ptr++; /* Move to next line */ if (field_end > this_line && field_end[-1] == '\r') { field_end--; /* Ignore CR LF */ /* Ignore CR CR LF in relaxed mode */ if (Config.onoff.relaxed_header_parser && field_end > this_line + 1 && field_end[-1] == '\r') { debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2) ("WARNING: Double CR characters in HTTP header {%s}\n", getStringPrefix(field_start, field_end)); field_end--; } } /* Barf on stray CR characters */ if (memchr(this_line, '\r', field_end - this_line)) { debug(55, 1) ("WARNING: suspicious CR characters in HTTP header {%s}\n", getStringPrefix(field_start, field_end)); if (Config.onoff.relaxed_header_parser) { char *p = (char *) this_line; /* XXX Warning! This destroys original header content and violates specifications somewhat */ while ((p = memchr(p, '\r', field_end - p)) != NULL) *p++ = ' '; } else return httpHeaderReset(hdr); } if (this_line + 1 == field_end && this_line > field_start) { debug(55, 1) ("WARNING: Blank continuation line in HTTP header {%s}\n", getStringPrefix(header_start, header_end)); return httpHeaderReset(hdr); } } while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t')); if (field_start == field_end) { if (field_ptr < header_end) { debug(55, 1) ("WARNING: unparseable HTTP header field near {%s}\n", getStringPrefix(field_start, header_end)); return httpHeaderReset(hdr); } break; /* terminating blank line */ } e = httpHeaderEntryParseCreate(field_start, field_end); if (NULL == e) { debug(55, 1) ("WARNING: unparseable HTTP header field {%s}\n", getStringPrefix(field_start, field_end)); debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2) (" in {%s}\n", getStringPrefix(header_start, header_end)); if (Config.onoff.relaxed_header_parser) continue; else return httpHeaderReset(hdr); } if (e->id == HDR_CONTENT_LENGTH) { squid_off_t l1; HttpHeaderEntry *e2; if (!httpHeaderParseSize(strBuf(e->value), &l1)) { debug(55, 1) ("WARNING: Unparseable content-length '%s'\n", strBuf(e->value)); httpHeaderEntryDestroy(e); return httpHeaderReset(hdr); } e2 = httpHeaderFindEntry(hdr, e->id); if (e2 && strCmp(e->value, strBuf(e2->value)) != 0) { squid_off_t l2; debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2) ("WARNING: found two conflicting content-length headers in {%s}\n", getStringPrefix(header_start, header_end)); if (!Config.onoff.relaxed_header_parser) { httpHeaderEntryDestroy(e); return httpHeaderReset(hdr); } if (!httpHeaderParseSize(strBuf(e2->value), &l2)) { debug(55, 1) ("WARNING: Unparseable content-length '%s'\n", strBuf(e->value)); httpHeaderEntryDestroy(e); return httpHeaderReset(hdr); } if (l1 > l2) { httpHeaderDelById(hdr, e2->id); } else { httpHeaderEntryDestroy(e); continue; } } else if (e2) { debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2) ("NOTICE: found double content-length header\n"); if (Config.onoff.relaxed_header_parser) { httpHeaderEntryDestroy(e); continue; } else { httpHeaderEntryDestroy(e); return httpHeaderReset(hdr); } } } if (e->id == HDR_OTHER && stringHasWhitespace(strBuf(e->name))) { debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2) ("WARNING: found whitespace in HTTP header name {%s}\n", getStringPrefix(field_start, field_end)); if (!Config.onoff.relaxed_header_parser) { httpHeaderEntryDestroy(e); return httpHeaderReset(hdr); } } httpHeaderAddEntry(hdr, e); } return 1; /* even if no fields where found, it is a valid header */ }