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); }
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 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); }
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 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)); }