]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
CONTRIB: hpack: add an hpack decoder
authorWilly Tarreau <w@1wt.eu>
Sat, 30 Dec 2017 12:18:25 +0000 (13:18 +0100)
committerWilly Tarreau <w@1wt.eu>
Sat, 30 Dec 2017 16:43:28 +0000 (17:43 +0100)
This decoder takes a series of hex codes on stdin using one line
per HEADERS frame and shows the decoded headers.

contrib/hpack/Makefile
contrib/hpack/decode.c [new file with mode: 0644]

index 1ecc4e482e82138dbac473a411604ecb76cd78df..128a6de36e3f9074911feed3020c5c149d56e172 100644 (file)
@@ -1,5 +1,5 @@
-CFLAGS = -O2 -W -Wall -Wextra -g
-OBJS = gen-rht
+CFLAGS = -O2 -Wall -g -I../../include -I../../ebtree -fwrapv -fno-strict-aliasing
+OBJS = gen-rht decode
 
 all: $(OBJS)
 
diff --git a/contrib/hpack/decode.c b/contrib/hpack/decode.c
new file mode 100644 (file)
index 0000000..2fd43b8
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * 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 | contrib/hpack/decode
+ *
+ * The DHT size may optionally be changed in argv[1].
+ *
+ * Build like this :
+ *    gcc -I../../include -I../../ebtree -O0 -g -fno-strict-aliasing -fwrapv \
+ *        -o decode decode.c
+ */
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <common/chunk.h>
+#include <common/hpack-dec.h>
+#include <common/mini-clist.h>
+#include <proto/h1.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];
+
+struct chunk trash = { .str = trash_buf, .len = 0, .size = sizeof(trash_buf) };
+struct chunk tmp   = { .str = tmp_buf,   .len = 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];
+       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--;
+       }
+
+       dht = hpack_dht_alloc(dht_size);
+       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.str, list[idx].n).ptr :
+                              h2_phdr_to_str(list[idx].n.len));
+
+                       printf("%s [n=(%p,%d) v=(%p,%d)]\n", istpad(trash.str, 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;
+}