static int vbf_fetch_chunked(struct busyobj *bo, struct http_conn *htc) { int i; char buf[20]; /* XXX: 20 is arbitrary */ unsigned u; ssize_t cl; assert(htc->body_status == BS_CHUNKED); do { /* Skip leading whitespace */ do { if (HTTP1_Read(htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (vct_islws(buf[0])); if (!vct_ishex(buf[0])) return (VFP_Error(bo, "chunked header non-hex")); /* Collect hex digits, skipping leading zeros */ for (u = 1; u < sizeof buf; u++) { do { if (HTTP1_Read(htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (u == 1 && buf[0] == '0' && buf[u] == '0'); if (!vct_ishex(buf[u])) break; } if (u >= sizeof buf) return (VFP_Error(bo,"chunked header too long")); /* Skip trailing white space */ while(vct_islws(buf[u]) && buf[u] != '\n') if (HTTP1_Read(htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[u] != '\n') return (VFP_Error(bo,"chunked header no NL")); buf[u] = '\0'; cl = vbf_fetch_number(buf, 16); if (cl < 0) return (VFP_Error(bo,"chunked header number syntax")); if (cl > 0 && bo->vfp->bytes(bo, htc, cl) <= 0) return (VFP_Error(bo, "chunked read err")); i = HTTP1_Read(htc, buf, 1); if (i <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] == '\r' && HTTP1_Read( htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] != '\n') return (VFP_Error(bo,"chunked tail no NL")); } while (cl > 0); return (0); }
static void cmd_http_expect_close(CMD_ARGS) { struct http *hp; struct pollfd fds[1]; char c; int i; (void)cmd; (void)vl; CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); AZ(av[1]); vtc_log(vl, 4, "Expecting close (fd = %d)", hp->fd); while (1) { fds[0].fd = hp->fd; fds[0].events = POLLIN | POLLERR; fds[0].revents = 0; i = poll(fds, 1, hp->timeout); if (i == 0) vtc_log(vl, hp->fatal, "Expected close: timeout"); if (i != 1 || !(fds[0].revents & POLLIN)) vtc_log(vl, hp->fatal, "Expected close: poll = %d, revents = 0x%x", i, fds[0].revents); i = read(hp->fd, &c, 1); if (VTCP_Check(i)) break; if (i == 1 && vct_islws(c)) continue; vtc_log(vl, hp->fatal, "Expecting close: read = %d, c = 0x%02x", i, c); } vtc_log(vl, 4, "fd=%d EOF, as expected", hp->fd); }
static int htc_header_complete(txt *t) { const char *p; Tcheck(*t); assert(*t->e == '\0'); /* Skip any leading white space */ for (p = t->b ; vct_islws(*p); p++) continue; if (p == t->e) { /* All white space */ t->e = t->b; *t->e = '\0'; return (-3); } while (1) { p = strchr(p, '\n'); if (p == NULL) return (0); p++; if (*p == '\r') p++; if (*p == '\n') break; } p++; return (p - t->b); }
HTTP1_Complete(struct http_conn *htc) { char *p; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); assert(htc->rxbuf_e >= htc->rxbuf_b); assert(*htc->rxbuf_e == '\0'); /* Skip any leading white space */ for (p = htc->rxbuf_b ; vct_islws(*p); p++) continue; if (p == htc->rxbuf_e) return (HTC_S_EMPTY); /* * Here we just look for NL[CR]NL to see that reception * is completed. More stringent validation happens later. */ while (1) { p = strchr(p, '\n'); if (p == NULL) return (HTC_S_MORE); p++; if (*p == '\r') p++; if (*p == '\n') break; } return (HTC_S_COMPLETE); }
static void http_swallow_body(struct http *hp, char * const *hh, int body) { char *p, *q; int i, l, ll; ll = 0; p = http_find_header(hh, "content-length"); if (p != NULL) { l = strtoul(p, NULL, 0); hp->body = hp->rxbuf + hp->prxbuf; http_rxchar(hp, l); vtc_dump(hp->vl, 4, "body", hp->body); sprintf(hp->bodylen, "%d", l); return; } p = http_find_header(hh, "transfer-encoding"); if (p != NULL && !strcmp(p, "chunked")) { hp->body = hp->rxbuf + hp->prxbuf; while (1) { l = hp->prxbuf; do http_rxchar(hp, 1); while (hp->rxbuf[hp->prxbuf - 1] != '\n'); vtc_dump(hp->vl, 4, "len", hp->rxbuf + l); i = strtoul(hp->rxbuf + l, &q, 16); assert(q != hp->rxbuf + l); assert(*q == '\0' || vct_islws(*q)); hp->prxbuf = l; if (i > 0) { ll += i; http_rxchar(hp, i); vtc_dump(hp->vl, 4, "chunk", hp->rxbuf + l); } l = hp->prxbuf; http_rxchar(hp, 2); assert(vct_iscrlf(hp->rxbuf[l])); assert(vct_iscrlf(hp->rxbuf[l + 1])); hp->prxbuf = l; hp->rxbuf[l] = '\0'; if (i == 0) break; } vtc_dump(hp->vl, 4, "body", hp->body); sprintf(hp->bodylen, "%d", ll); return; } if (body) { hp->body = hp->rxbuf + hp->prxbuf; do { i = http_rxchar_eof(hp, 1); ll += i; } while (i > 0); vtc_dump(hp->vl, 4, "rxeof", hp->body); } sprintf(hp->bodylen, "%d", ll); }
static int http_rxchunk(struct http *hp) { char *q; int l, i; l = hp->prxbuf; do (void)http_rxchar(hp, 1, 0); while (hp->rxbuf[hp->prxbuf - 1] != '\n'); vtc_dump(hp->vl, 4, "len", hp->rxbuf + l, -1); i = strtoul(hp->rxbuf + l, &q, 16); bprintf(hp->chunklen, "%d", i); if ((q == hp->rxbuf + l) || (*q != '\0' && !vct_islws(*q))) { vtc_log(hp->vl, hp->fatal, "chunked fail %02x @ %d", *q, q - (hp->rxbuf + l)); } assert(q != hp->rxbuf + l); assert(*q == '\0' || vct_islws(*q)); hp->prxbuf = l; if (i > 0) { (void)http_rxchar(hp, i, 0); vtc_dump(hp->vl, 4, "chunk", hp->rxbuf + l, i); } l = hp->prxbuf; (void)http_rxchar(hp, 2, 0); if(!vct_iscrlf(hp->rxbuf[l])) vtc_log(hp->vl, hp->fatal, "Wrong chunk tail[0] = %02x", hp->rxbuf[l] & 0xff); if(!vct_iscrlf(hp->rxbuf[l + 1])) vtc_log(hp->vl, hp->fatal, "Wrong chunk tail[1] = %02x", hp->rxbuf[l + 1] & 0xff); hp->prxbuf = l; hp->rxbuf[l] = '\0'; return (i); }
int http_GetHdrData(const struct http *hp, const char *hdr, const char *field, char **ptr) { char *h, *e; unsigned fl; if (ptr != NULL) *ptr = NULL; if (!http_GetHdr(hp, hdr, &h)) return (0); AN(h); e = strchr(h, '\0'); fl = strlen(field); while (h + fl <= e) { /* Skip leading whitespace and commas */ if (vct_islws(*h) || *h == ',') { h++; continue; } /* Check for substrings before memcmp() */ if ((h + fl == e || vct_issepctl(h[fl])) && !memcmp(h, field, fl)) { if (ptr != NULL) { h += fl; while (vct_islws(*h)) h++; *ptr = h; } return (1); } /* Skip until end of header or comma */ while (*h && *h != ',') h++; } return (0); }
enum htc_status_e HTTP1_Complete(struct http_conn *htc) { char *p; txt *t; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); AZ(htc->pipeline.b); AZ(htc->pipeline.e); t = &htc->rxbuf; Tcheck(*t); assert(*t->e == '\0'); /* Skip any leading white space */ for (p = t->b ; vct_islws(*p); p++) continue; if (p == t->e) { /* All white space */ t->e = t->b; *t->e = '\0'; return (HTTP1_ALL_WHITESPACE); } while (1) { p = strchr(p, '\n'); if (p == NULL) return (HTTP1_NEED_MORE); p++; if (*p == '\r') p++; if (*p == '\n') break; } p++; WS_ReleaseP(htc->ws, t->e); if (p < t->e) { htc->pipeline.b = p; htc->pipeline.e = t->e; t->e = p; } return (HTTP1_COMPLETE); }
enum http1_status_e HTTP1_Complete(struct http_conn *htc) { char *p; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); AZ(htc->pipeline_b); AZ(htc->pipeline_e); assert(htc->rxbuf_e >= htc->rxbuf_b); assert(*htc->rxbuf_e == '\0'); /* Skip any leading white space */ for (p = htc->rxbuf_b ; vct_islws(*p); p++) continue; if (p == htc->rxbuf_e) { /* All white space */ htc->rxbuf_e = htc->rxbuf_b; *htc->rxbuf_e = '\0'; return (HTTP1_ALL_WHITESPACE); } while (1) { p = strchr(p, '\n'); if (p == NULL) return (HTTP1_NEED_MORE); p++; if (*p == '\r') p++; if (*p == '\n') break; } p++; WS_ReleaseP(htc->ws, htc->rxbuf_e); if (p < htc->rxbuf_e) { htc->pipeline_b = p; htc->pipeline_e = htc->rxbuf_e; htc->rxbuf_e = p; } return (HTTP1_COMPLETE); }
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++; assert(!vct_iscrlf(*p)); *p++ = '\0'; /* URL/STATUS */ while (vct_issp(*p)) /* XXX: H space only */ p++; assert(!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); } }
static uint16_t http_splitline(struct worker *w, int fd, struct http *hp, const struct http_conn *htc, int h1, int h2, int h3) { char *p, *q; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); /* XXX: Assert a NUL at rx.e ? */ Tcheck(htc->rxbuf); /* Skip leading LWS */ for (p = htc->rxbuf.b ; vct_islws(*p); p++) continue; /* First field cannot contain SP, CRLF or CTL */ q = p; for (; !vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[h1].b = q; hp->hd[h1].e = p; /* Skip SP */ for (; vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } /* Second field cannot contain LWS or CTL */ q = p; for (; !vct_islws(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[h2].b = q; hp->hd[h2].e = p; if (!Tlen(hp->hd[h2])) return (413); /* Skip SP */ for (; vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } /* Third field is optional and cannot contain CTL */ q = p; if (!vct_iscrlf(*p)) { for (; !vct_iscrlf(*p); p++) if (!vct_issep(*p) && vct_isctl(*p)) return (400); } hp->hd[h3].b = q; hp->hd[h3].e = p; /* Skip CRLF */ p += vct_skipcrlf(p); *hp->hd[h1].e = '\0'; WSLH(w, fd, hp, h1); *hp->hd[h2].e = '\0'; WSLH(w, fd, hp, h2); if (hp->hd[h3].e != NULL) { *hp->hd[h3].e = '\0'; WSLH(w, fd, hp, h3); } return (http_dissect_hdrs(w, hp, fd, p, htc)); }
static uint16_t http_dissect_hdrs(struct worker *w, struct http *hp, int fd, char *p, const struct http_conn *htc) { char *q, *r; txt t = htc->rxbuf; if (*p == '\r') p++; hp->nhd = HTTP_HDR_FIRST; hp->conds = 0; r = NULL; /* For FlexeLint */ for (; p < t.e; p = r) { /* Find end of next header */ q = r = p; while (r < t.e) { if (!vct_iscrlf(*r)) { r++; continue; } q = r; assert(r < t.e); r += vct_skipcrlf(r); if (r >= t.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) { VSC_C_main->losthdr++; WSL(w, SLT_LostHeader, fd, "%.*s", q - p > 20 ? 20 : q - p, p); return (413); } /* Empty header = end of headers */ if (p == q) break; 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 (hp->nhd < hp->shd) { hp->hdf[hp->nhd] = 0; hp->hd[hp->nhd].b = p; hp->hd[hp->nhd].e = q; WSLH(w, fd, hp, hp->nhd); hp->nhd++; } else { VSC_C_main->losthdr++; WSL(w, SLT_LostHeader, fd, "%.*s", q - p > 20 ? 20 : q - p, p); return (413); } } return (0); }
static int fetch_chunked(struct sess *sp, struct http_conn *htc) { int i; char buf[20]; /* XXX: 20 is arbitrary */ unsigned u; ssize_t cl; sp->wrk->vfp->begin(sp, 0); assert(sp->wrk->body_status == BS_CHUNKED); do { /* Skip leading whitespace */ do { i = HTC_Read(htc, buf, 1); CERR(); } while (vct_islws(buf[0])); /* Collect hex digits, skipping leading zeros */ for (u = 1; u < sizeof buf; u++) { do { i = HTC_Read(htc, buf + u, 1); CERR(); } while (u == 1 && buf[0] == '0' && buf[u] == '0'); if (!vct_ishex(buf[u])) break; } if (u >= sizeof buf) { WSP(sp, SLT_FetchError, "chunked header too long"); return (-1); } /* Skip trailing white space */ while(vct_islws(buf[u]) && buf[u] != '\n') { i = HTC_Read(htc, buf + u, 1); CERR(); } if (buf[u] != '\n') { WSP(sp, SLT_FetchError, "chunked header char syntax"); return (-1); } buf[u] = '\0'; cl = fetch_number(buf, 16); if (cl < 0) { WSP(sp, SLT_FetchError, "chunked header nbr syntax"); return (-1); } else if (cl > 0) { i = sp->wrk->vfp->bytes(sp, htc, cl); CERR(); } i = HTC_Read(htc, buf, 1); CERR(); if (buf[0] == '\r') { i = HTC_Read(htc, buf, 1); CERR(); } if (buf[0] != '\n') { WSP(sp, SLT_FetchError, "chunked tail syntax"); return (-1); } } while (cl > 0); return (0); }
static uint16_t http1_splitline(struct http *hp, struct http_conn *htc, const int *hf, unsigned maxhdr) { 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, maxhdr)); }
static uint16_t htc_splitline(struct http *hp, const struct http_conn *htc, int req) { char *p; int h1, h2, h3; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); Tcheck(htc->rxbuf); if (req) { h1 = HTTP_HDR_METHOD; h2 = HTTP_HDR_URL; h3 = HTTP_HDR_PROTO; } else { h1 = HTTP_HDR_PROTO; h2 = HTTP_HDR_STATUS; h3 = HTTP_HDR_RESPONSE; } /* Skip leading LWS */ for (p = htc->rxbuf.b ; vct_islws(*p); p++) continue; hp->hd[h1].b = p; /* First field cannot contain SP or CTL */ for (; !vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[h1].e = p; assert(Tlen(hp->hd[h1])); /* Skip SP */ for (; vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[h2].b = p; /* Second field cannot contain LWS or CTL */ for (; !vct_islws(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[h2].e = p; if (!Tlen(hp->hd[h2])) return (400); /* Skip SP */ for (; vct_issp(*p); p++) { if (vct_isctl(*p)) return (400); } hp->hd[h3].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[h3].b = NULL; return (400); } } hp->hd[h3].e = p; /* Skip CRLF */ p += vct_skipcrlf(p); *hp->hd[h1].e = '\0'; *hp->hd[h2].e = '\0'; if (hp->hd[h3].e != NULL) *hp->hd[h3].e = '\0'; return (htc_dissect_hdrs(hp, p, htc)); }
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); }
static uint16_t http1_dissect_hdrs(struct http *hp, char *p, struct http_conn *htc, unsigned maxhdr) { 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; if (vct_iscrlf(p)) break; while (r < htc->rxbuf_e) { if (!vct_isctl(*r) || vct_issp(*r)) { r++; continue; } if (!vct_iscrlf(r)) { VSLb(hp->vsl, SLT_BogoHeader, "Header has ctrl char 0x%02x", *r); return (400); } q = r; assert(r < htc->rxbuf_e); r += vct_skipcrlf(r); if (r >= htc->rxbuf_e) break; if (vct_iscrlf(r)) 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 > 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); } } if (p < htc->rxbuf_e) p += vct_skipcrlf(p); HTC_RxPipeline(htc, p); htc->rxbuf_e = p; return (0); }
vep_do_include(struct vep_state *vep, enum dowhat what) { const char *p, *q, *h; ssize_t l; Debug("DO_INCLUDE(%d)\n", what); if (what == DO_ATTR) { Debug("ATTR (%s) (%s)\n", vep->match_hit->match, VSB_data(vep->attr_vsb)); if (vep->include_src != NULL) { vep_error(vep, "ESI 1.0 <esi:include> " "has multiple src= attributes"); vep->state = VEP_TAGERROR; VSB_destroy(&vep->attr_vsb); VSB_destroy(&vep->include_src); return; } for (p = VSB_data(vep->attr_vsb); *p != '\0'; p++) if (vct_islws(*p)) break; if (*p != '\0') { vep_error(vep, "ESI 1.0 <esi:include> " "has whitespace in src= attribute"); vep->state = VEP_TAGERROR; VSB_destroy(&vep->attr_vsb); if (vep->include_src != NULL) VSB_destroy(&vep->include_src); return; } vep->include_src = vep->attr_vsb; vep->attr_vsb = NULL; return; } assert(what == DO_TAG); if (!vep->emptytag) vep_warn(vep, "ESI 1.0 <esi:include> lacks final '/'"); if (vep->include_src == NULL) { vep_error(vep, "ESI 1.0 <esi:include> lacks src attr"); return; } /* * Strictly speaking, we ought to spit out any piled up skip before * emitting the VEC for the include, but objectively that makes no * difference and robs us of a chance to collapse another skip into * this on so we don't do that. * However, we cannot tolerate any verbatim stuff piling up. * The mark_skip() before calling dostuff should have taken * care of that. Make sure. */ assert(vep->o_wait == 0 || vep->last_mark == SKIP); /* XXX: what if it contains NUL bytes ?? */ p = VSB_data(vep->include_src); l = VSB_len(vep->include_src); h = 0; if (l > 7 && !memcmp(p, "http://", 7)) { h = p + 7; p = strchr(h, '/'); if (p == NULL) { vep_error(vep, "ESI 1.0 <esi:include> invalid src= URL"); vep->state = VEP_TAGERROR; AZ(vep->attr_vsb); VSB_destroy(&vep->include_src); return; } Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p); VSB_printf(vep->vsb, "%c", VEC_INCL); VSB_printf(vep->vsb, "Host: %.*s%c", (int)(p-h), h, 0); } else if (l > 8 && !memcmp(p, "https://", 8)) { if (!FEATURE(FEATURE_ESI_IGNORE_HTTPS)) { vep_warn(vep, "ESI 1.0 <esi:include> with https:// ignored"); vep->state = VEP_TAGERROR; AZ(vep->attr_vsb); VSB_destroy(&vep->include_src); return; } vep_warn(vep, "ESI 1.0 <esi:include> https:// treated as http://"); h = p + 8; p = strchr(h, '/'); if (p == NULL) { vep_error(vep, "ESI 1.0 <esi:include> invalid src= URL"); vep->state = VEP_TAGERROR; AZ(vep->attr_vsb); VSB_destroy(&vep->include_src); return; } VSB_printf(vep->vsb, "%c", VEC_INCL); VSB_printf(vep->vsb, "Host: %.*s%c", (int)(p-h), h, 0); } else if (*p == '/') { VSB_printf(vep->vsb, "%c", VEC_INCL); VSB_printf(vep->vsb, "%c", 0); } else { VSB_printf(vep->vsb, "%c", VEC_INCL); VSB_printf(vep->vsb, "%c", 0); /* Look for the last / before a '?' */ h = NULL; for (q = vep->url; *q && *q != '?'; q++) if (*q == '/') h = q; if (h == NULL) h = q + 1; Debug("INCL:: [%.*s]/[%s]\n", (int)(h - vep->url), vep->url, p); VSB_printf(vep->vsb, "%.*s/", (int)(h - vep->url), vep->url); } l -= (p - VSB_data(vep->include_src)); for (q = p; *q != '\0'; ) { if (*q == '&') { #define R(w,f,r) \ if (q + w <= p + l && !memcmp(q, f, w)) { \ VSB_printf(vep->vsb, "%c", r); \ q += w; \ continue; \ } R(6, "'", '\''); R(6, """, '"'); R(4, "<", '<'); R(4, ">", '>'); R(5, "&", '&'); } VSB_printf(vep->vsb, "%c", *q++); } #undef R VSB_printf(vep->vsb, "%c", 0); VSB_destroy(&vep->include_src); }
h2_deliver(struct req *req, struct boc *boc, int sendbody) { ssize_t sz, sz1; uint8_t *p; unsigned u; const char *r; struct http *hp; struct sess *sp; struct h2_req *r2; int i, err; const struct hpack_static *hps; CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_ORNULL(boc, BOC_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); sp = req->sp; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); (void)sendbody; VSLb(req->vsl, SLT_Debug, "H2: Deliver"); (void)WS_Reserve(req->ws, 0); p = (void*)req->ws->f; switch (req->resp->status) { case 200: *p++ = 0x80 | 8; break; case 204: *p++ = 0x80 | 9; break; case 206: *p++ = 0x80 | 10; break; case 304: *p++ = 0x80 | 11; break; case 400: *p++ = 0x80 | 12; break; case 404: *p++ = 0x80 | 13; break; case 500: *p++ = 0x80 | 14; break; default: *p++ = 0x18; *p++ = 0x03; (void)sprintf((char*)p, "%03d", req->resp->status); p += 3; break; } hp = req->resp; for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) { assert((char*)p < req->ws->e); r = strchr(hp->hd[u].b, ':'); AN(r); hps = hp_idx[tolower(*hp->hd[u].b)]; sz = 1 + r - hp->hd[u].b; assert(sz > 0); while (hps != NULL && hps->idx > 0) { i = strncasecmp(hps->name, hp->hd[u].b, sz); if (i < 0) { hps++; continue; } if (i > 0) hps = NULL; break; } if (hps != NULL) { VSLb(req->vsl, SLT_Debug, "HP {%d, \"%s\", \"%s\"} <%s>", hps->idx, hps->name, hps->val, hp->hd[u].b); if (hps->idx < 15) { *p++ = 0x10 | hps->idx; } else { *p++ = 0x1f; *p++ = hps->idx - 0x0f; } } else { *p++ = 0x10; sz--; if (sz < 127) { *p++ = (uint8_t)sz; } else { *p++ = 0x7f; *p++ = (uint8_t)sz - 0x7f; } for(sz1 = 0; sz1 < sz; sz1++) *p++ = (uint8_t)tolower(hp->hd[u].b[sz1]); } while(vct_islws(*++r)) continue; sz = hp->hd[u].e - r; assert(sz <= 254); if (sz < 127) { *p++ = (uint8_t)sz; } else if (sz < 127 * 2) { *p++ = 0x7f; *p++ = (uint8_t)sz - 0x7f; } memcpy(p, r, sz); p += sz; assert((char*)p < req->ws->e); } sz = (char*)p - req->ws->f; /* XXX: Optimize !sendbody case */ H2_Send(req->wrk, r2, 1, H2_FRAME_HEADERS, H2FF_HEADERS_END_HEADERS, sz, req->ws->f); WS_Release(req->ws, 0); if (sendbody && req->resp_len != 0) VDP_push(req, h2_bytes, NULL, 1, "H2"); AZ(req->wrk->v1l); if (sendbody && req->resp_len != 0) err = VDP_DeliverObj(req); /*XXX*/(void)err; H2_Send(req->wrk, r2, 1, H2_FRAME_DATA, H2FF_DATA_END_STREAM, 0, NULL); AZ(req->wrk->v1l); VDP_close(req); }
v1f_pull_chunked(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv) { int i; char buf[20]; /* XXX: 20 is arbitrary */ unsigned u; ssize_t cl, l, lr; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); if (p == vfp_init) return (VFP_OK); if (p == vfp_fini) return (VFP_ERROR); AN(p); AN(lp); AN(priv); l = *lp; *lp = 0; if (*priv == -1) { /* Skip leading whitespace */ do { if (HTTP1_Read(&bo->htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (vct_islws(buf[0])); if (!vct_ishex(buf[0])) return (VFP_Error(bo, "chunked header non-hex")); /* Collect hex digits, skipping leading zeros */ for (u = 1; u < sizeof buf; u++) { do { if (HTTP1_Read(&bo->htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (u == 1 && buf[0] == '0' && buf[u] == '0'); if (!vct_ishex(buf[u])) break; } if (u >= sizeof buf) return (VFP_Error(bo,"chunked header too long")); /* Skip trailing white space */ while(vct_islws(buf[u]) && buf[u] != '\n') if (HTTP1_Read(&bo->htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[u] != '\n') return (VFP_Error(bo,"chunked header no NL")); buf[u] = '\0'; cl = vbf_fetch_number(buf, 16); if (cl < 0) return (VFP_Error(bo,"chunked header number syntax")); *priv = cl; } if (*priv > 0) { if (*priv < l) l = *priv; lr = HTTP1_Read(&bo->htc, p, l); if (lr <= 0) return (VFP_Error(bo, "straight insufficient bytes")); *lp = lr; *priv -= lr; if (*priv == 0) *priv = -1; return (VFP_OK); } AZ(*priv); i = HTTP1_Read(&bo->htc, buf, 1); if (i <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] == '\r' && HTTP1_Read(&bo->htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] != '\n') return (VFP_Error(bo,"chunked tail no NL")); return (VFP_END); }