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