static uint16_t http1_splitline(struct http *hp, const struct http_conn *htc, const int *hf) { char *p; int i; assert(hf == HTTP1_Req || hf == HTTP1_Resp); CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); assert(htc->rxbuf_e >= htc->rxbuf_b); AZ(hp->hd[hf[0]].b); AZ(hp->hd[hf[1]].b); AZ(hp->hd[hf[2]].b); /* Skip leading LWS */ for (p = htc->rxbuf_b ; vct_islws(*p); p++) continue; hp->hd[hf[0]].b = p; /* First field cannot contain SP or CTL */ for (; !vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[hf[0]].e = p; assert(Tlen(hp->hd[hf[0]])); *p++ = '\0'; /* Skip SP */ for (; vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[hf[1]].b = p; /* Second field cannot contain LWS or CTL */ for (; !vct_islws(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[hf[1]].e = p; if (!Tlen(hp->hd[hf[1]])) return (400); *p++ = '\0'; /* Skip SP */ for (; vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[hf[2]].b = p; /* Third field is optional and cannot contain CTL except TAB */ for (; !vct_iscrlf(p); p++) { if (vct_isctl(*p) && !vct_issp(*p)) { hp->hd[hf[2]].b = NULL; return (400); } } hp->hd[hf[2]].e = p; /* Skip CRLF */ i = vct_skipcrlf(p); *p = '\0'; p += i; return (http1_dissect_hdrs(hp, p, htc)); }
static uint16_t http1_dissect_hdrs(struct http *hp, char *p, const struct http_conn *htc) { char *q, *r; assert(p > htc->rxbuf_b); assert(p <= htc->rxbuf_e); hp->nhd = HTTP_HDR_FIRST; hp->conds = 0; r = NULL; /* For FlexeLint */ for (; p < htc->rxbuf_e; p = r) { /* Find end of next header */ q = r = p; while (r < htc->rxbuf_e) { if (!vct_iscrlf(r)) { r++; continue; } q = r; assert(r < htc->rxbuf_e); r += vct_skipcrlf(r); if (r >= htc->rxbuf_e) break; /* If line does not continue: got it. */ if (!vct_issp(*r)) break; /* Clear line continuation LWS to spaces */ while (vct_islws(*q)) *q++ = ' '; } if (q - p > htc->maxhdr) { VSLb(hp->vsl, SLT_BogoHeader, "Header too long: %.*s", (int)(q - p > 20 ? 20 : q - p), p); return (400); } /* Empty header = end of headers */ if (p == q) break; if (vct_islws(*p)) { VSLb(hp->vsl, SLT_BogoHeader, "1st header has white space: %.*s", (int)(q - p > 20 ? 20 : q - p), p); return (400); } if ((p[0] == 'i' || p[0] == 'I') && (p[1] == 'f' || p[1] == 'F') && p[2] == '-') hp->conds = 1; while (q > p && vct_issp(q[-1])) q--; *q = '\0'; if (strchr(p, ':') == NULL) { VSLb(hp->vsl, SLT_BogoHeader, "Header without ':' %.*s", (int)(q - p > 20 ? 20 : q - p), p); return (400); } if (hp->nhd < hp->shd) { hp->hdf[hp->nhd] = 0; hp->hd[hp->nhd].b = p; hp->hd[hp->nhd].e = q; hp->nhd++; } else { VSLb(hp->vsl, SLT_BogoHeader, "Too many headers: %.*s", (int)(q - p > 20 ? 20 : q - p), p); return (400); } } return (0); }
void VEP_Parse(struct vep_state *vep, const char *p, size_t l) { const char *e; struct vep_match *vm; int i; CHECK_OBJ_NOTNULL(vep, VEP_MAGIC); assert(l > 0); if (vep->startup) { /* * We must force the GZIP header out as a SKIP string, * otherwise an object starting with <esi:include would * have its GZIP header appear after the included object * (e000026.vtc) */ vep->ver_p = ""; vep->last_mark = SKIP; vep_mark_common(vep, vep->ver_p, VERBATIM); vep->startup = 0; AZ(vep->hack_p); vep->hack_p = p; } vep->ver_p = p; e = p + l; while (p < e) { AN(vep->state); i = e - p; if (i > 10) i = 10; Debug("EP %s %d (%.*s) [%.*s]\n", vep->state, vep->remove, vep->tag_i, vep->tag, i, p); assert(p >= vep->ver_p); /****************************************************** * SECTION A */ if (vep->state == VEP_START) { if (FEATURE(FEATURE_ESI_REMOVE_BOM) && *p == '\xeb') { vep->match = vep_match_bom; vep->state = VEP_MATCH; } else vep->state = VEP_BOM; } else if (vep->state == VEP_BOM) { vep_mark_skip(vep, p); if (FEATURE(FEATURE_ESI_DISABLE_XML_CHECK)) vep->state = VEP_NEXTTAG; else vep->state = VEP_TESTXML; } else if (vep->state == VEP_TESTXML) { /* * If the first non-whitespace char is different * from '<' we assume this is not XML. */ while (p < e && vct_islws(*p)) p++; vep_mark_verbatim(vep, p); if (p < e && *p == '<') { p++; vep->state = VEP_STARTTAG; } else if (p < e && *p == '\xeb') { VSLb(vep->vc->wrk->vsl, SLT_ESI_xmlerror, "No ESI processing, " "first char not '<' but BOM." " (See feature esi_remove_bom)" ); vep->state = VEP_NOTXML; } else if (p < e) { VSLb(vep->vc->wrk->vsl, SLT_ESI_xmlerror, "No ESI processing, " "first char not '<'." " (See feature esi_disable_xml_check)" ); vep->state = VEP_NOTXML; } } else if (vep->state == VEP_NOTXML) { /* * This is not recognized as XML, just skip thru * vfp_esi_end() will handle the rest */ p = e; vep_mark_verbatim(vep, p); /****************************************************** * SECTION B */ } else if (vep->state == VEP_NOTMYTAG) { if (FEATURE(FEATURE_ESI_IGNORE_OTHER_ELEMENTS)) { p++; vep->state = VEP_NEXTTAG; } else { vep->tag_i = 0; while (p < e) { if (*p++ == '>') { vep->state = VEP_NEXTTAG; break; } } } if (p == e && !vep->remove) vep_mark_verbatim(vep, p); } else if (vep->state == VEP_NEXTTAG) { /* * Hunt for start of next tag and keep an eye * out for end of EsiCmt if armed. */ vep->emptytag = 0; vep->endtag = 0; vep->attr = NULL; vep->dostuff = NULL; while (p < e && *p != '<') { if (vep->esicmt_p == NULL) { p++; continue; } if (*p != *vep->esicmt_p) { p++; vep->esicmt_p = vep->esicmt; continue; } if (!vep->remove && vep->esicmt_p == vep->esicmt) vep_mark_verbatim(vep, p); p++; if (*++vep->esicmt_p == '\0') { vep->esi_found = 1; vep->esicmt = NULL; vep->esicmt_p = NULL; /* * The end of the esicmt * should not be emitted. * But the stuff before should */ vep_mark_skip(vep, p); } } if (p < e) { if (!vep->remove) vep_mark_verbatim(vep, p); assert(*p == '<'); p++; vep->state = VEP_STARTTAG; } else if (vep->esicmt_p == vep->esicmt && !vep->remove) vep_mark_verbatim(vep, p); /****************************************************** * SECTION C */ } else if (vep->state == VEP_STARTTAG) { /* * Start of tag, set up match table */ if (p < e) { if (*p == '/') { vep->endtag = 1; p++; } vep->match = vep_match_starttag; vep->state = VEP_MATCH; } } else if (vep->state == VEP_COMMENT) { /* * We are in a comment, find out if it is an * ESI comment or a regular comment */ if (vep->esicmt == NULL) vep->esicmt_p = vep->esicmt = "esi"; while (p < e) { if (*p != *vep->esicmt_p) { vep->esicmt_p = vep->esicmt = NULL; vep->until_p = vep->until = "-->"; vep->until_s = VEP_NEXTTAG; vep->state = VEP_UNTIL; break; } p++; if (*++vep->esicmt_p != '\0') continue; if (vep->remove) vep_error(vep, "ESI 1.0 Nested <!--esi" " element in <esi:remove>"); vep->esicmt_p = vep->esicmt = "-->"; vep->state = VEP_NEXTTAG; vep_mark_skip(vep, p); break; } } else if (vep->state == VEP_CDATA) { /* * Easy: just look for the end of CDATA */ vep->until_p = vep->until = "]]>"; vep->until_s = VEP_NEXTTAG; vep->state = VEP_UNTIL; } else if (vep->state == VEP_ESITAG) { vep->in_esi_tag = 1; vep->esi_found = 1; vep_mark_skip(vep, p); vep->match = vep_match_esi; vep->state = VEP_MATCH; } else if (vep->state == VEP_ESIINCLUDE) { if (vep->remove) { vep_error(vep, "ESI 1.0 <esi:include> element" " nested in <esi:remove>"); vep->state = VEP_TAGERROR; } else if (vep->endtag) { vep_error(vep, "ESI 1.0 </esi:include> illegal end-tag"); vep->state = VEP_TAGERROR; } else { vep->dostuff = vep_do_include; vep->state = VEP_INTAG; vep->attr = vep_match_attr_include; } } else if (vep->state == VEP_ESIREMOVE) { vep->dostuff = vep_do_remove; vep->state = VEP_INTAG; } else if (vep->state == VEP_ESICOMMENT) { if (vep->remove) { vep_error(vep, "ESI 1.0 <esi:comment> element" " nested in <esi:remove>"); vep->state = VEP_TAGERROR; } else if (vep->endtag) { vep_error(vep, "ESI 1.0 </esi:comment> illegal end-tag"); vep->state = VEP_TAGERROR; } else { vep->dostuff = vep_do_comment; vep->state = VEP_INTAG; } } else if (vep->state == VEP_ESIBOGON) { vep_error(vep, "ESI 1.0 <esi:bogus> element"); vep->state = VEP_TAGERROR; /****************************************************** * SECTION D */ } else if (vep->state == VEP_INTAG) { vep->tag_i = 0; while (p < e && vct_islws(*p) && !vep->emptytag) { p++; vep->canattr = 1; } if (p < e && *p == '/' && !vep->emptytag) { p++; vep->emptytag = 1; vep->canattr = 0; } if (p < e && *p == '>') { p++; AN(vep->dostuff); vep_mark_skip(vep, p); vep->dostuff(vep, DO_TAG); vep->in_esi_tag = 0; vep->state = VEP_NEXTTAG; } else if (p < e && vep->emptytag) { vep_error(vep, "XML 1.0 '>' does not follow '/' in tag"); vep->state = VEP_TAGERROR; } else if (p < e && vep->canattr && vct_isxmlnamestart(*p)) { vep->state = VEP_ATTR; } else if (p < e) { vep_error(vep, "XML 1.0 Illegal attribute start char"); vep->state = VEP_TAGERROR; } } else if (vep->state == VEP_TAGERROR) { while (p < e && *p != '>') p++; if (p < e) { p++; vep_mark_skip(vep, p); vep->in_esi_tag = 0; vep->state = VEP_NEXTTAG; } /****************************************************** * SECTION E */ } else if (vep->state == VEP_ATTR) { AZ(vep->attr_delim); if (vep->attr == NULL) { p++; AZ(vep->attr_vsb); vep->state = VEP_SKIPATTR; } else { vep->match = vep->attr; vep->state = VEP_MATCH; } } else if (vep->state == VEP_SKIPATTR) { while (p < e && vct_isxmlname(*p)) p++; if (p < e && *p == '=') { p++; vep->state = VEP_ATTRDELIM; } else if (p < e && *p == '>') { vep->state = VEP_INTAG; } else if (p < e && *p == '/') { vep->state = VEP_INTAG; } else if (p < e && vct_issp(*p)) { vep->state = VEP_INTAG; } else if (p < e) { vep_error(vep, "XML 1.0 Illegal attr char"); vep->state = VEP_TAGERROR; } } else if (vep->state == VEP_ATTRGETVAL) { vep->attr_vsb = VSB_new_auto(); vep->state = VEP_ATTRDELIM; } else if (vep->state == VEP_ATTRDELIM) { AZ(vep->attr_delim); if (*p == '"' || *p == '\'') { vep->attr_delim = *p++; vep->state = VEP_ATTRVAL; } else if (!vct_issp(*p)) { vep->attr_delim = ' '; vep->state = VEP_ATTRVAL; } else { vep_error(vep, "XML 1.0 Illegal attribute delimiter"); vep->state = VEP_TAGERROR; } } else if (vep->state == VEP_ATTRVAL) { while (p < e && *p != '>' && *p != vep->attr_delim && (vep->attr_delim != ' ' || !vct_issp(*p))) { if (vep->attr_vsb != NULL) VSB_bcat(vep->attr_vsb, p, 1); p++; } if (p < e && *p == '>') { vep_error(vep, "XML 1.0 Missing end attribute delimiter"); vep->state = VEP_TAGERROR; vep->attr_delim = 0; if (vep->attr_vsb != NULL) { AZ(VSB_finish(vep->attr_vsb)); VSB_delete(vep->attr_vsb); vep->attr_vsb = NULL; } } else if (p < e) { vep->attr_delim = 0; p++; vep->state = VEP_INTAG; if (vep->attr_vsb != NULL) { AZ(VSB_finish(vep->attr_vsb)); AN(vep->dostuff); vep->dostuff(vep, DO_ATTR); vep->attr_vsb = NULL; } } /****************************************************** * Utility Section */ } else if (vep->state == VEP_MATCH) { /* * Match against a table */ vm = vep_match(vep, p, e); vep->match_hit = vm; if (vm != NULL) { if (vm->match != NULL) p += strlen(vm->match); vep->state = *vm->state; vep->match = NULL; vep->tag_i = 0; } else { memcpy(vep->tag, p, e - p); vep->tag_i = e - p; vep->state = VEP_MATCHBUF; p = e; } } else if (vep->state == VEP_MATCHBUF) { /* * Match against a table while split over input * sections. */ AN(vep->match); do { if (*p == '>') { for (vm = vep->match; vm->match != NULL; vm++) continue; AZ(vm->match); } else { vep->tag[vep->tag_i++] = *p++; vm = vep_match(vep, vep->tag, vep->tag + vep->tag_i); if (vm && vm->match == NULL) { vep->tag_i--; p--; } } } while (vm == NULL && p < e); vep->match_hit = vm; if (vm == NULL) { assert(p == e); } else { vep->state = *vm->state; vep->match = NULL; } } else if (vep->state == VEP_UNTIL) { /* * Skip until we see magic string */ while (p < e) { if (*p++ != *vep->until_p++) { vep->until_p = vep->until; } else if (*vep->until_p == '\0') { vep->state = vep->until_s; break; } } if (p == e && !vep->remove) vep_mark_verbatim(vep, p); } else { Debug("*** Unknown state %s\n", vep->state); INCOMPL(); } } /* * We must always mark up the storage we got, try to do so * in the most efficient way, in particular with respect to * minimizing and limiting use of pending. */ if (p == vep->ver_p) ; else if (vep->in_esi_tag) vep_mark_skip(vep, p); else if (vep->remove) vep_mark_skip(vep, p); else vep_mark_pending(vep, p); }
struct vsb * VRY_Create(struct req *req, const struct http *hp) { char *v, *p, *q, *h, *e; struct vsb *sb, *sbh; unsigned l; /* No Vary: header, no worries */ if (!http_GetHdr(hp, H_Vary, &v)) return (NULL); /* For vary matching string */ sb = VSB_new_auto(); AN(sb); /* For header matching strings */ sbh = VSB_new_auto(); AN(sbh); if (*v == ':') { VSLb(req->vsl, SLT_Error, "Vary header had extra ':', fix backend"); v++; } for (p = v; *p; p++) { /* Find next header-name */ if (vct_issp(*p)) continue; for (q = p; *q && !vct_issp(*q) && *q != ','; q++) continue; /* Build a header-matching string out of it */ VSB_clear(sbh); VSB_printf(sbh, "%c%.*s:%c", (char)(1 + (q - p)), (int)(q - p), p, 0); AZ(VSB_finish(sbh)); if (http_GetHdr(req->http, VSB_data(sbh), &h)) { AZ(vct_issp(*h)); /* Trim trailing space */ e = strchr(h, '\0'); while (e > h && vct_issp(e[-1])) e--; /* Encode two byte length and contents */ l = e - h; assert(!(l & ~0xffff)); } else { e = h; l = 0xffff; } VSB_printf(sb, "%c%c", (int)(l >> 8), (int)(l & 0xff)); /* Append to vary matching string */ VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh)); if (e != h) VSB_bcat(sb, h, e - h); while (vct_issp(*q)) q++; if (*q == '\0') break; xxxassert(*q == ','); p = q; } /* Terminate vary matching string */ VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0); VSB_delete(sbh); AZ(VSB_finish(sb)); return(sb); }
int VRY_Match(struct req *req, const uint8_t *vary) { uint8_t *vsp = req->vary_b; char *h, *e; unsigned lh, ln; int i, oflo = 0; AN(vsp); while (vary[2]) { if (vsp + 2 >= req->vary_e) { /* * Too little workspace to find out */ oflo = 1; break; } i = vry_cmp(vary, vsp); if (i == 1) { /* * Different header, build a new entry, * then compare again with that new entry. */ ln = 2 + vary[2] + 2; i = http_GetHdr(req->http, (const char*)(vary+2), &h); if (i) { /* Trim trailing space */ e = strchr(h, '\0'); while (e > h && vct_issp(e[-1])) e--; lh = e - h; assert(lh < 0xffff); ln += lh; } else { e = h = NULL; lh = 0xffff; } if (vsp + ln + 2 >= req->vary_e) { /* * Not enough space to build new entry * and put terminator behind it. */ oflo = 1; break; } vbe16enc(vsp, (uint16_t)lh); memcpy(vsp + 2, vary + 2, vary[2] + 2); if (h != NULL) memcpy(vsp + 2 + vsp[2] + 2, h, lh); vsp[ln + 0] = 0xff; vsp[ln + 1] = 0xff; vsp[ln + 2] = 0; VRY_Validate(vsp); req->vary_l = vsp + 3; i = vry_cmp(vary, vsp); assert(i == 0 || i == 2); } if (i == 0) { /* Same header, same contents*/ vsp += vry_len(vsp); vary += vry_len(vary); } else if (i == 2) { /* Same header, different contents, cannot match */ return (0); } } if (oflo) { vsp = req->vary_b; req->vary_l = NULL; if (vsp + 2 < req->vary_e) { vsp[0] = 0xff; vsp[1] = 0xff; vsp[2] = 0; } return (0); } else { return (1); } }
static void http_splitheader(struct http *hp, int req) { char *p, *q, **hh; int n; char buf[20]; CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); if (req) { memset(hp->req, 0, sizeof hp->req); hh = hp->req; } else { memset(hp->resp, 0, sizeof hp->resp); hh = hp->resp; } n = 0; p = hp->rxbuf; /* REQ/PROTO */ while (vct_islws(*p)) p++; hh[n++] = p; while (!vct_islws(*p)) p++; AZ(vct_iscrlf(p)); *p++ = '\0'; /* URL/STATUS */ while (vct_issp(*p)) /* XXX: H space only */ p++; AZ(vct_iscrlf(p)); hh[n++] = p; while (!vct_islws(*p)) p++; if (vct_iscrlf(p)) { hh[n++] = NULL; q = p; p += vct_skipcrlf(p); *q = '\0'; } else { *p++ = '\0'; /* PROTO/MSG */ while (vct_issp(*p)) /* XXX: H space only */ p++; hh[n++] = p; while (!vct_iscrlf(p)) p++; q = p; p += vct_skipcrlf(p); *q = '\0'; } assert(n == 3); while (*p != '\0') { assert(n < MAX_HDR); if (vct_iscrlf(p)) break; hh[n++] = p++; while (*p != '\0' && !vct_iscrlf(p)) p++; q = p; p += vct_skipcrlf(p); *q = '\0'; } p += vct_skipcrlf(p); assert(*p == '\0'); for (n = 0; n < 3 || hh[n] != NULL; n++) { sprintf(buf, "http[%2d] ", n); vtc_dump(hp->vl, 4, buf, hh[n], -1); } }