| /* |
| * 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]; |
| |
| 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; |
| } |