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