MINOR: h2: add a generic frame checker

The new function h2_frame_check() checks the protocol limits for the
received frame (length, ID, direction) and returns a verdict made of
a connection error code. The purpose is to be able to validate any
frame regardless of the state and the ability to call the frame handler,
and to emit a GOAWAY early in this case.
diff --git a/include/common/h2.h b/include/common/h2.h
index 71be8ce..c434ed9 100644
--- a/include/common/h2.h
+++ b/include/common/h2.h
@@ -178,6 +178,25 @@
 #define H2_MSGF_BODY_CL        0x0002    // content-length is present
 #define H2_MSGF_BODY_TUNNEL    0x0004    // a tunnel is in use (CONNECT)
 
+#define H2_MAX_STREAM_ID       ((1U << 31) - 1)
+#define H2_MAX_FRAME_LEN       ((1U << 24) - 1)
+#define H2_DIR_REQ             1
+#define H2_DIR_RES             2
+#define H2_DIR_BOTH            3
+
+/* constraints imposed by the protocol on each frame type, in terms of stream
+ * ID values, frame sizes, and direction so that most connection-level checks
+ * can be centralized regardless of the frame's acceptance.
+ */
+struct h2_frame_definition {
+	int32_t dir;     /* 0=none, 1=request, 2=response, 3=both */
+	int32_t min_id;  /* minimum allowed stream ID */
+	int32_t max_id;  /* maximum allowed stream ID */
+	int32_t min_len; /* minimum frame length */
+	int32_t max_len; /* maximum frame length */
+};
+
+extern struct h2_frame_definition h2_frame_definition[H2_FT_ENTRIES];
 
 /* various protocol processing functions */
 
@@ -215,6 +234,41 @@
 	}
 }
 
+/* Returns an error code if the frame is valid protocol-wise, otherwise 0. <ft>
+ * is the frame type (H2_FT_*), <dir> is the direction (1=req, 2=res), <id> is
+ * the stream ID from the frame header, <len> is the frame length from the
+ * header. The purpose is to be able to quickly return a PROTOCOL_ERROR or
+ * FRAME_SIZE_ERROR connection error even for situations where the frame will
+ * be ignored. <mfs> must be the max frame size currently in place for the
+ * protocol.
+ */
+static inline int h2_frame_check(enum h2_ft ft, int dir, int32_t id, int32_t len, int32_t mfs)
+{
+	struct h2_frame_definition *fd;
+
+	if (ft >= H2_FT_ENTRIES)
+		return H2_ERR_NO_ERROR; // ignore unhandled frame types
+
+	fd = &h2_frame_definition[ft];
+
+	if (!(dir & fd->dir))
+		return H2_ERR_PROTOCOL_ERROR;
+
+	if (id < fd->min_id || id > fd->max_id)
+		return H2_ERR_PROTOCOL_ERROR;
+
+	if (len < fd->min_len || len > fd->max_len)
+		return H2_ERR_FRAME_SIZE_ERROR;
+
+	if (len > mfs)
+		return H2_ERR_FRAME_SIZE_ERROR;
+
+	if (ft == H2_FT_SETTINGS && (len % 6) != 0)
+		return H2_ERR_FRAME_SIZE_ERROR; // RFC7540#6.5
+
+	return H2_ERR_NO_ERROR;
+}
+
 /* returns the pseudo-header <str> corresponds to among H2_PHDR_IDX_*, 0 if not a
  * pseudo-header, or -1 if not a valid pseudo-header.
  */
diff --git a/src/h2.c b/src/h2.c
index 1a103e4..6a1debf 100644
--- a/src/h2.c
+++ b/src/h2.c
@@ -31,6 +31,18 @@
 #include <common/http-hdr.h>
 #include <common/ist.h>
 
+struct h2_frame_definition h2_frame_definition[H2_FT_ENTRIES] =	{
+	 [H2_FT_DATA         ] = { .dir = 3, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 0, .max_len = H2_MAX_FRAME_LEN, },
+	 [H2_FT_HEADERS      ] = { .dir = 3, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 1, .max_len = H2_MAX_FRAME_LEN, },
+	 [H2_FT_PRIORITY     ] = { .dir = 3, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 5, .max_len = 5,                },
+	 [H2_FT_RST_STREAM   ] = { .dir = 3, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 4, .max_len = 4,                },
+	 [H2_FT_SETTINGS     ] = { .dir = 3, .min_id = 0, .max_id = 0,                .min_len = 0, .max_len = H2_MAX_FRAME_LEN, },
+	 [H2_FT_PUSH_PROMISE ] = { .dir = 0, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 4, .max_len = H2_MAX_FRAME_LEN, },
+	 [H2_FT_PING         ] = { .dir = 3, .min_id = 0, .max_id = 0,                .min_len = 8, .max_len = 8,                },
+	 [H2_FT_GOAWAY       ] = { .dir = 3, .min_id = 0, .max_id = 0,                .min_len = 8, .max_len = H2_MAX_FRAME_LEN, },
+	 [H2_FT_WINDOW_UPDATE] = { .dir = 3, .min_id = 0, .max_id = H2_MAX_STREAM_ID, .min_len = 4, .max_len = 4,                },
+	 [H2_FT_CONTINUATION ] = { .dir = 3, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 0, .max_len = H2_MAX_FRAME_LEN, },
+};
 
 /* Prepare the request line into <*ptr> (stopping at <end>) from pseudo headers
  * stored in <phdr[]>. <fields> indicates what was found so far. This should be