Commit cdcae28a authored by Dridi Boukelmoune's avatar Dridi Boukelmoune

http2_hpack: Also rewrite h2h_checkhdr() for clarity

It does a first pass on header names and values, and only logs errors,
so the signature is updated accordingly and the call site is moved into
h2h_addhdr().
parent d9928c3b
...@@ -53,9 +53,10 @@ h2h_assert_ready(struct h2h_decode *d) ...@@ -53,9 +53,10 @@ h2h_assert_ready(struct h2h_decode *d)
// rfc9113,l,2493,2528 // rfc9113,l,2493,2528
static h2_error static h2_error
h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len) h2h_checkhdr(struct vsl_log *vsl, txt nm, txt val)
{ {
const char *p; const char *p;
int l;
enum { enum {
FLD_NAME_FIRST, FLD_NAME_FIRST,
FLD_NAME, FLD_NAME,
...@@ -63,23 +64,17 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len) ...@@ -63,23 +64,17 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
FLD_VALUE FLD_VALUE
} state; } state;
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); if (Tlen(nm) == 0) {
AN(b); VSLb(vsl, SLT_BogoHeader, "Empty name");
assert(namelen >= 2); /* 2 chars from the ': ' that we added */
assert(namelen <= len);
assert(b[namelen - 2] == ':');
assert(b[namelen - 1] == ' ');
if (namelen == 2) {
VSLb(hp->vsl, SLT_BogoHeader, "Empty name");
return (H2SE_PROTOCOL_ERROR); return (H2SE_PROTOCOL_ERROR);
} }
// VSLb(hp->vsl, SLT_Debug, "CHDR [%.*s] [%.*s]", // VSLb(vsl, SLT_Debug, "CHDR [%.*s] [%.*s]",
// (int)namelen, b, (int)(len - namelen), b + namelen); // (int)Tlen(nm), nm.b, (int)Tlen(val), val.b);
l = vmin_t(int, Tlen(nm) + 2 + Tlen(val), 20);
state = FLD_NAME_FIRST; state = FLD_NAME_FIRST;
for (p = b; p < b + namelen - 2; p++) { for (p = nm.b; p < nm.e; p++) {
switch(state) { switch(state) {
case FLD_NAME_FIRST: case FLD_NAME_FIRST:
state = FLD_NAME; state = FLD_NAME;
...@@ -88,15 +83,15 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len) ...@@ -88,15 +83,15 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
/* FALL_THROUGH */ /* FALL_THROUGH */
case FLD_NAME: case FLD_NAME:
if (isupper(*p)) { if (isupper(*p)) {
VSLb(hp->vsl, SLT_BogoHeader, VSLb(vsl, SLT_BogoHeader,
"Illegal field header name (upper-case): %.*s", "Illegal field header name (upper-case): %.*s",
(int)(len > 20 ? 20 : len), b); l, nm.b);
return (H2SE_PROTOCOL_ERROR); return (H2SE_PROTOCOL_ERROR);
} }
if (!vct_istchar(*p) || *p == ':') { if (!vct_istchar(*p) || *p == ':') {
VSLb(hp->vsl, SLT_BogoHeader, VSLb(vsl, SLT_BogoHeader,
"Illegal field header name (non-token): %.*s", "Illegal field header name (non-token): %.*s",
(int)(len > 20 ? 20 : len), b); l, nm.b);
return (H2SE_PROTOCOL_ERROR); return (H2SE_PROTOCOL_ERROR);
} }
break; break;
...@@ -106,22 +101,20 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len) ...@@ -106,22 +101,20 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
} }
state = FLD_VALUE_FIRST; state = FLD_VALUE_FIRST;
for (p = b + namelen; p < b + len; p++) { for (p = val.b; p < val.e; p++) {
switch(state) { switch(state) {
case FLD_VALUE_FIRST: case FLD_VALUE_FIRST:
if (vct_issp(*p)) { if (vct_issp(*p)) {
VSLb(hp->vsl, SLT_BogoHeader, VSLb(vsl, SLT_BogoHeader,
"Illegal field value start %.*s", "Illegal field value start %.*s", l, nm.b);
(int)(len > 20 ? 20 : len), b);
return (H2SE_PROTOCOL_ERROR); return (H2SE_PROTOCOL_ERROR);
} }
state = FLD_VALUE; state = FLD_VALUE;
/* FALL_THROUGH */ /* FALL_THROUGH */
case FLD_VALUE: case FLD_VALUE:
if (!vct_ishdrval(*p)) { if (!vct_ishdrval(*p)) {
VSLb(hp->vsl, SLT_BogoHeader, VSLb(vsl, SLT_BogoHeader,
"Illegal field value %.*s", "Illegal field value %.*s", l, nm.b);
(int)(len > 20 ? 20 : len), b);
return (H2SE_PROTOCOL_ERROR); return (H2SE_PROTOCOL_ERROR);
} }
break; break;
...@@ -129,10 +122,9 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len) ...@@ -129,10 +122,9 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
WRONG("http2 field value validation state"); WRONG("http2 field value validation state");
} }
} }
if (state == FLD_VALUE && vct_issp(b[len - 1])) { if (state == FLD_VALUE && vct_issp(val.e[-1])) {
VSLb(hp->vsl, SLT_BogoHeader, VSLb(vsl, SLT_BogoHeader,
"Illegal field value (end) %.*s", "Illegal field value (end) %.*s", l, nm.b);
(int)(len > 20 ? 20 : len), b);
return (H2SE_PROTOCOL_ERROR); return (H2SE_PROTOCOL_ERROR);
} }
return (0); return (0);
...@@ -146,6 +138,7 @@ h2h_addhdr(struct http *hp, struct h2h_decode *d) ...@@ -146,6 +138,7 @@ h2h_addhdr(struct http *hp, struct h2h_decode *d)
int disallow_empty; int disallow_empty;
const char *p; const char *p;
unsigned n, has_dup; unsigned n, has_dup;
h2_error err;
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
h2h_assert_ready(d); h2h_assert_ready(d);
...@@ -161,6 +154,10 @@ h2h_addhdr(struct http *hp, struct h2h_decode *d) ...@@ -161,6 +154,10 @@ h2h_addhdr(struct http *hp, struct h2h_decode *d)
val.b = nm.e + 2; val.b = nm.e + 2;
val.e = hdr.e; val.e = hdr.e;
err = h2h_checkhdr(hp->vsl, nm, val);
if (err != NULL)
return (err);
disallow_empty = 0; disallow_empty = 0;
has_dup = 0; has_dup = 0;
...@@ -393,10 +390,6 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) ...@@ -393,10 +390,6 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
d->error = H2SE_ENHANCE_YOUR_CALM; d->error = H2SE_ENHANCE_YOUR_CALM;
break; break;
} }
d->error = h2h_checkhdr(hp, d->out, d->namelen,
d->out_u);
if (d->error)
break;
d->error = h2h_addhdr(hp, d); d->error = h2h_addhdr(hp, d);
if (d->error) if (d->error)
break; break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment