summaryrefslogtreecommitdiffstats
path: root/dev/hpack/decode.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dev/hpack/decode.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/dev/hpack/decode.c b/dev/hpack/decode.c
new file mode 100644
index 0000000..13c95c7
--- /dev/null
+++ b/dev/hpack/decode.c
@@ -0,0 +1,215 @@
+/*
+ * HPACK stream decoder. Takes a series of hex codes on stdin using one line
+ * per HEADERS frame. Spaces, tabs, CR, '-' and ',' are silently skipped.
+ * e.g. :
+ * echo 82864188f439ce75c875fa5784 | dev/hpack/decode
+ *
+ * The DHT size may optionally be changed in argv[1].
+ *
+ * Build like this :
+ * gcc -I../../include -O0 -g -fno-strict-aliasing -fwrapv \
+ * -o decode decode.c
+ */
+
+#define HPACK_STANDALONE
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <haproxy/chunk.h>
+#include <haproxy/hpack-dec.h>
+
+#define MAX_RQ_SIZE 65536
+#define MAX_HDR_NUM 1000
+
+char hex[MAX_RQ_SIZE*3+3]; // enough for "[ XX]* <CR> <LF> \0"
+uint8_t buf[MAX_RQ_SIZE];
+
+char trash_buf[MAX_RQ_SIZE];
+char tmp_buf[MAX_RQ_SIZE];
+
+THREAD_LOCAL struct buffer trash = { .area = trash_buf, .data = 0, .size = sizeof(trash_buf) };
+struct buffer tmp = { .area = tmp_buf, .data = 0, .size = sizeof(tmp_buf) };
+
+/* displays a <len> long memory block at <buf>, assuming first byte of <buf>
+ * has address <baseaddr>. String <pfx> may be placed as a prefix in front of
+ * each line. It may be NULL if unused. The output is emitted to file <out>.
+ */
+void debug_hexdump(FILE *out, const char *pfx, const char *buf,
+ unsigned int baseaddr, int len)
+{
+ unsigned int i;
+ int b, j;
+
+ for (i = 0; i < (len + (baseaddr & 15)); i += 16) {
+ b = i - (baseaddr & 15);
+ fprintf(out, "%s%08x: ", pfx ? pfx : "", i + (baseaddr & ~15));
+ for (j = 0; j < 8; j++) {
+ if (b + j >= 0 && b + j < len)
+ fprintf(out, "%02x ", (unsigned char)buf[b + j]);
+ else
+ fprintf(out, " ");
+ }
+
+ if (b + j >= 0 && b + j < len)
+ fputc('-', out);
+ else
+ fputc(' ', out);
+
+ for (j = 8; j < 16; j++) {
+ if (b + j >= 0 && b + j < len)
+ fprintf(out, " %02x", (unsigned char)buf[b + j]);
+ else
+ fprintf(out, " ");
+ }
+
+ fprintf(out, " ");
+ for (j = 0; j < 16; j++) {
+ if (b + j >= 0 && b + j < len) {
+ if (isprint((unsigned char)buf[b + j]))
+ fputc((unsigned char)buf[b + j], out);
+ else
+ fputc('.', out);
+ }
+ else
+ fputc(' ', out);
+ }
+ fputc('\n', out);
+ }
+}
+
+/* enable DEBUG_HPACK to show each individual hpack code */
+#define DEBUG_HPACK
+#include "../src/hpack-huff.c"
+#include "../src/hpack-tbl.c"
+#include "../src/hpack-dec.c"
+
+/* display the message and exit with the code */
+__attribute__((noreturn)) void die(int code, const char *format, ...)
+{
+ va_list args;
+
+ if (format) {
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ }
+ exit(code);
+}
+
+/* reads <hex> and stops at the first LF, '#' or \0. Converts from hex to
+ * binary, ignoring spaces, tabs, CR, "-" and ','. The output is sent into
+ * <bin> for no more than <size> bytes. The number of bytes placed there is
+ * returned, or a negative value in case of parsing error.
+ */
+int hex2bin(const char *hex, uint8_t *bin, int size)
+{
+ int a, b, c;
+ uint8_t code;
+ int len = 0;
+
+ a = b = -1;
+
+ for (; *hex; hex++) {
+ c = *hex;
+ if (c == ' ' || c == '\t' || c == '\r' ||
+ c == '-' || c == ',')
+ continue;
+
+ if (c == '\n' || c == '#')
+ break;
+
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else
+ return -1;
+
+ if (a == -1)
+ a = c;
+ else
+ b = c;
+
+ if (b == -1)
+ continue;
+
+ code = (a << 4) | b;
+ a = b = -1;
+ if (len >= size)
+ return -2;
+
+ bin[len] = code;
+ len++;
+ }
+ if (a >= 0 || b >= 0)
+ return -3;
+ return len;
+}
+
+int main(int argc, char **argv)
+{
+ struct hpack_dht *dht;
+ struct http_hdr list[MAX_HDR_NUM];
+ struct pool_head pool;
+ int outlen;
+ int dht_size = 4096;
+ int len, idx;
+ int line;
+
+ /* first arg: dht size */
+ if (argc > 1) {
+ dht_size = atoi(argv[1]);
+ argv++; argc--;
+ }
+
+ pool.size = dht_size;
+ pool_head_hpack_tbl = &pool;
+ dht = hpack_dht_alloc();
+ if (!dht) {
+ die(1, "cannot initialize dht\n");
+ return 1;
+ }
+
+ for (line = 1; fgets(hex, sizeof(hex), stdin); line++) {
+ len = hex2bin(hex, buf, sizeof(buf));
+ if (len <= 0)
+ continue;
+ printf("###### line %d : frame len=%d #######\n", line, len);
+ debug_hexdump(stdout, " ", (const char *)buf, 0, len);
+
+ outlen = hpack_decode_frame(dht, buf, len, list,
+ sizeof(list)/sizeof(list[0]), &tmp);
+ if (outlen <= 0) {
+ printf(" HPACK decoding failed: %d\n", outlen);
+ continue;
+ }
+
+ printf("<<< Found %d headers :\n", outlen);
+ for (idx = 0; idx < outlen - 1; idx++) {
+ //printf(" \e[1;34m%s\e[0m: ",
+ // list[idx].n.ptr ? istpad(trash.str, list[idx].n).ptr : h2_phdr_to_str(list[idx].n.len));
+
+ //printf("\e[1;35m%s\e[0m\n", istpad(trash.str, list[idx].v).ptr);
+
+ printf(" %s: ", list[idx].n.ptr ?
+ istpad(trash.area, list[idx].n).ptr :
+ h2_phdr_to_str(list[idx].n.len));
+
+ printf("%s [n=(%p,%d) v=(%p,%d)]\n",
+ istpad(trash.area, list[idx].v).ptr,
+ list[idx].n.ptr, (int)list[idx].n.len, list[idx].v.ptr, (int)list[idx].v.len);
+ }
+ puts(">>>");
+#ifdef DEBUG_HPACK
+ printf("<<=== DHT dump [ptr=%p]:\n", dht);
+ hpack_dht_dump(stdout, dht);
+ puts("===>>");
+#endif
+ }
+ return 0;
+}