mirror of
				https://git.haproxy.org/git/haproxy.git/
				synced 2025-10-31 00:21:00 +01:00 
			
		
		
		
	Since 7d84439 ("BUILD: hpack: include global.h for the trash that is needed
in debug mode"), hpack decode tool fails to compile on targets that enable
USE_THREAD. (ie: linux-glibc target as reported by Christian Ruppert)
When building hpack devtool, we are including src/hpack-dec.c as a dependency.
src/hpack-dec.c relies on the global trash whe debug mode is enabled.
But as we're building hpack tool with a limited scope of haproxy
sources, global trash (which is declared in src/chunk.c) is not available.
Thus, src/hpack-dec.c relies on a local 'trash' variable declared within
dev/hpack/decode.c
This used to work fine until 7d84439.
But now that global.h is explicitely included in src/hpack-dec.c,
trash variable definition from decode.c conflicts with the one from global.h:
  In file included from include/../src/hpack-dec.c:35,
                   from dev/hpack/decode.c:87:
  include/haproxy/global.h:52:35: error: thread-local declaration of 'trash' follows non-thread-local declaration
     52 | extern THREAD_LOCAL struct buffer trash;
Adding THREAD_LOCAL attribute to 'decode.c' local trash variable definition
makes the compiler happy again.
This should fix GH issue #2009 and should be backported to 2.7.
		
	
			
		
			
				
	
	
		
			216 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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];
 | |
| 
 | |
| THREAD_LOCAL 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;
 | |
| }
 |