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