blob: 82f28c7f18f6e5b16b0cc44ab6d29868230477bd [file] [log] [blame]
Willy Tarreau45764242017-12-30 13:18:25 +01001/*
2 * HPACK stream decoder. Takes a series of hex codes on stdin using one line
3 * per HEADERS frame. Spaces, tabs, CR, '-' and ',' are silently skipped.
4 * e.g. :
5 * echo 82864188f439ce75c875fa5784 | contrib/hpack/decode
6 *
7 * The DHT size may optionally be changed in argv[1].
8 *
9 * Build like this :
10 * gcc -I../../include -I../../ebtree -O0 -g -fno-strict-aliasing -fwrapv \
11 * -o decode decode.c
12 */
Willy Tarreau318adf42020-05-22 12:09:16 +020013
14#define HPACK_STANDALONE
15
Willy Tarreau45764242017-12-30 13:18:25 +010016#include <ctype.h>
Willy Tarreaua1bd1fa2019-03-29 17:26:33 +010017#include <inttypes.h>
Willy Tarreau45764242017-12-30 13:18:25 +010018#include <stdio.h>
19#include <stdlib.h>
20#include <unistd.h>
21#include <common/chunk.h>
22#include <common/hpack-dec.h>
23#include <common/mini-clist.h>
Willy Tarreau45764242017-12-30 13:18:25 +010024
25#define MAX_RQ_SIZE 65536
26#define MAX_HDR_NUM 1000
27
28char hex[MAX_RQ_SIZE*3+3]; // enough for "[ XX]* <CR> <LF> \0"
29uint8_t buf[MAX_RQ_SIZE];
30
31char trash_buf[MAX_RQ_SIZE];
32char tmp_buf[MAX_RQ_SIZE];
33
Willy Tarreau83061a82018-07-13 11:56:34 +020034struct buffer trash = { .area = trash_buf, .data = 0, .size = sizeof(trash_buf) };
35struct buffer tmp = { .area = tmp_buf, .data = 0, .size = sizeof(tmp_buf) };
Willy Tarreau45764242017-12-30 13:18:25 +010036
37/* displays a <len> long memory block at <buf>, assuming first byte of <buf>
38 * has address <baseaddr>. String <pfx> may be placed as a prefix in front of
39 * each line. It may be NULL if unused. The output is emitted to file <out>.
40 */
41void debug_hexdump(FILE *out, const char *pfx, const char *buf,
42 unsigned int baseaddr, int len)
43{
44 unsigned int i;
45 int b, j;
46
47 for (i = 0; i < (len + (baseaddr & 15)); i += 16) {
48 b = i - (baseaddr & 15);
49 fprintf(out, "%s%08x: ", pfx ? pfx : "", i + (baseaddr & ~15));
50 for (j = 0; j < 8; j++) {
51 if (b + j >= 0 && b + j < len)
52 fprintf(out, "%02x ", (unsigned char)buf[b + j]);
53 else
54 fprintf(out, " ");
55 }
56
57 if (b + j >= 0 && b + j < len)
58 fputc('-', out);
59 else
60 fputc(' ', out);
61
62 for (j = 8; j < 16; j++) {
63 if (b + j >= 0 && b + j < len)
64 fprintf(out, " %02x", (unsigned char)buf[b + j]);
65 else
66 fprintf(out, " ");
67 }
68
69 fprintf(out, " ");
70 for (j = 0; j < 16; j++) {
71 if (b + j >= 0 && b + j < len) {
72 if (isprint((unsigned char)buf[b + j]))
73 fputc((unsigned char)buf[b + j], out);
74 else
75 fputc('.', out);
76 }
77 else
78 fputc(' ', out);
79 }
80 fputc('\n', out);
81 }
82}
83
84/* enable DEBUG_HPACK to show each individual hpack code */
85#define DEBUG_HPACK
86#include "../src/hpack-huff.c"
87#include "../src/hpack-tbl.c"
88#include "../src/hpack-dec.c"
89
90/* display the message and exit with the code */
91__attribute__((noreturn)) void die(int code, const char *format, ...)
92{
93 va_list args;
94
95 if (format) {
96 va_start(args, format);
97 vfprintf(stderr, format, args);
98 va_end(args);
99 }
100 exit(code);
101}
102
103/* reads <hex> and stops at the first LF, '#' or \0. Converts from hex to
104 * binary, ignoring spaces, tabs, CR, "-" and ','. The output is sent into
105 * <bin> for no more than <size> bytes. The number of bytes placed there is
106 * returned, or a negative value in case of parsing error.
107 */
108int hex2bin(const char *hex, uint8_t *bin, int size)
109{
110 int a, b, c;
111 uint8_t code;
112 int len = 0;
113
114 a = b = -1;
115
116 for (; *hex; hex++) {
117 c = *hex;
118 if (c == ' ' || c == '\t' || c == '\r' ||
119 c == '-' || c == ',')
120 continue;
121
122 if (c == '\n' || c == '#')
123 break;
124
125 if (c >= '0' && c <= '9')
126 c -= '0';
127 else if (c >= 'a' && c <= 'f')
128 c -= 'a' - 10;
129 else if (c >= 'A' && c <= 'F')
130 c -= 'A' - 10;
131 else
132 return -1;
133
134 if (a == -1)
135 a = c;
136 else
137 b = c;
138
139 if (b == -1)
140 continue;
141
142 code = (a << 4) | b;
143 a = b = -1;
144 if (len >= size)
145 return -2;
146
147 bin[len] = code;
148 len++;
149 }
150 if (a >= 0 || b >= 0)
151 return -3;
152 return len;
153}
154
155int main(int argc, char **argv)
156{
157 struct hpack_dht *dht;
158 struct http_hdr list[MAX_HDR_NUM];
Willy Tarreau318adf42020-05-22 12:09:16 +0200159 struct pool_head pool;
Willy Tarreau45764242017-12-30 13:18:25 +0100160 int outlen;
161 int dht_size = 4096;
162 int len, idx;
163 int line;
164
165 /* first arg: dht size */
166 if (argc > 1) {
167 dht_size = atoi(argv[1]);
168 argv++; argc--;
169 }
170
Willy Tarreau318adf42020-05-22 12:09:16 +0200171 pool.size = dht_size;
172 pool_head_hpack_tbl = &pool;
173 dht = hpack_dht_alloc();
Willy Tarreau45764242017-12-30 13:18:25 +0100174 if (!dht) {
175 die(1, "cannot initialize dht\n");
176 return 1;
177 }
178
179 for (line = 1; fgets(hex, sizeof(hex), stdin); line++) {
180 len = hex2bin(hex, buf, sizeof(buf));
181 if (len <= 0)
182 continue;
183 printf("###### line %d : frame len=%d #######\n", line, len);
184 debug_hexdump(stdout, " ", (const char *)buf, 0, len);
185
186 outlen = hpack_decode_frame(dht, buf, len, list,
187 sizeof(list)/sizeof(list[0]), &tmp);
188 if (outlen <= 0) {
189 printf(" HPACK decoding failed: %d\n", outlen);
190 continue;
191 }
192
193 printf("<<< Found %d headers :\n", outlen);
194 for (idx = 0; idx < outlen - 1; idx++) {
195 //printf(" \e[1;34m%s\e[0m: ",
196 // list[idx].n.ptr ? istpad(trash.str, list[idx].n).ptr : h2_phdr_to_str(list[idx].n.len));
197
198 //printf("\e[1;35m%s\e[0m\n", istpad(trash.str, list[idx].v).ptr);
199
200 printf(" %s: ", list[idx].n.ptr ?
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200201 istpad(trash.area, list[idx].n).ptr :
Willy Tarreau45764242017-12-30 13:18:25 +0100202 h2_phdr_to_str(list[idx].n.len));
203
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200204 printf("%s [n=(%p,%d) v=(%p,%d)]\n",
205 istpad(trash.area, list[idx].v).ptr,
Willy Tarreau45764242017-12-30 13:18:25 +0100206 list[idx].n.ptr, (int)list[idx].n.len, list[idx].v.ptr, (int)list[idx].v.len);
207 }
208 puts(">>>");
209#ifdef DEBUG_HPACK
210 printf("<<=== DHT dump [ptr=%p]:\n", dht);
211 hpack_dht_dump(stdout, dht);
212 puts("===>>");
213#endif
214 }
215 return 0;
216}