Browse Source

feature: support DNS over HTTP3

Nick Peng 9 months ago
parent
commit
fbb27b2e09
14 changed files with 2228 additions and 78 deletions
  1. 15 0
      etc/smartdns/smartdns.conf
  2. 1 5
      src/Makefile
  3. 249 26
      src/dns_client.c
  4. 1 0
      src/dns_client.h
  5. 19 3
      src/dns_conf.c
  6. 3 3
      src/dns_server.c
  7. 970 31
      src/http_parse.c
  8. 31 4
      src/http_parse.h
  9. 43 0
      src/include/qpack.h
  10. 709 0
      src/lib/qpack.c
  11. 3 3
      src/proxy.c
  12. 5 0
      src/smartdns.c
  13. 7 3
      test/Makefile
  14. 172 0
      test/cases/test-http.cc

+ 15 - 0
etc/smartdns/smartdns.conf

@@ -257,6 +257,21 @@ log-level info
 # default port is 853
 # server-quic 223.5.5.5
 
+# remote http3 dns server list
+# server-http3 [IP]:[PORT] [-blacklist-ip] [-whitelist-ip] [-spki-pin [sha256-pin]] [-group [group] ...] [-exclude-default-group]
+# server-h3 [IP]:[PORT] [-blacklist-ip] [-whitelist-ip] [-spki-pin [sha256-pin]] [-group [group] ...] [-exclude-default-group]
+#   -spki-pin: TLS spki pin to verify.
+#   -tls-host-verify: cert hostname to verify.
+#   -host-name: TLS sni hostname.
+#   k|-no-check-certificate: no check certificate.
+#   p|-proxy [proxy-name]: use proxy to connect to server.
+#   -bootstrap-dns: set as bootstrap dns server.
+# Get SPKI with this command:
+#    echo | openssl s_client -quic -alpn doq -connect '[ip]:853' | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
+# default port is 853
+# server-http3 https://223.5.5.5/dns-query
+# server-h3 h3://223.5.5.5/dns-query
+
 # remote https dns server list
 # server-https https://[host]:[port]/path [-blacklist-ip] [-whitelist-ip] [-spki-pin [sha256-pin]] [-group [group] ...] [-exclude-default-group]
 #   -spki-pin: TLS spki pin to verify.

+ 1 - 5
src/Makefile

@@ -25,11 +25,7 @@ OBJS=$(OBJS_MAIN) $(OBJS_LIB)
 
 # cflags
 ifndef CFLAGS
- ifdef DEBUG
-  CFLAGS = -g -DDEBUG
- else
-  CFLAGS = -O2
- endif
+ CFLAGS = -g -DDEBUG
  CFLAGS +=-Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing -funwind-tables -Wmissing-prototypes -Wshadow -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough
 endif
 

+ 249 - 26
src/dns_client.c

@@ -612,6 +612,9 @@ static const char *_dns_server_get_type_string(dns_server_type_t type)
 	case DNS_SERVER_MDNS:
 		type_str = "mdns";
 		break;
+	case DNS_SERVER_HTTP3:
+		type_str = "http3";
+		break;
 	case DNS_SERVER_QUIC:
 		type_str = "quic";
 		break;
@@ -1062,6 +1065,7 @@ static char *_dns_client_server_get_tls_host_verify(struct dns_server_info *serv
 	switch (server_info->type) {
 	case DNS_SERVER_UDP: {
 	} break;
+	case DNS_SERVER_HTTP3:
 	case DNS_SERVER_HTTPS: {
 		struct client_dns_server_flag_https *flag_https = &server_info->flags.https;
 		tls_host_verify = flag_https->tls_host_verify;
@@ -1096,6 +1100,7 @@ static char *_dns_client_server_get_spki(struct dns_server_info *server_info, in
 	switch (server_info->type) {
 	case DNS_SERVER_UDP: {
 	} break;
+	case DNS_SERVER_HTTP3:
 	case DNS_SERVER_HTTPS: {
 		struct client_dns_server_flag_https *flag_https = &server_info->flags.https;
 		spki = flag_https->spki;
@@ -1301,6 +1306,19 @@ static int _dns_client_server_add(const char *server_ip, const char *server_host
 
 		sock_type = SOCK_DGRAM;
 	} break;
+	case DNS_SERVER_HTTP3: {
+		struct client_dns_server_flag_https *flag_https = &flags->https;
+		spki_data_len = flag_https->spi_len;
+		if (flag_https->httphost[0] == 0) {
+			if (server_host) {
+				safe_strncpy(flag_https->httphost, server_host, DNS_MAX_CNAME_LEN);
+			} else {
+				set_http_host(server_ip, port, DEFAULT_DNS_HTTPS_PORT, flag_https->httphost);
+			}
+		}
+		sock_type = SOCK_DGRAM;
+		skip_check_cert = flag_https->skip_check_cert;
+	} break;
 	case DNS_SERVER_HTTPS: {
 		struct client_dns_server_flag_https *flag_https = &flags->https;
 		spki_data_len = flag_https->spi_len;
@@ -1417,8 +1435,9 @@ static int _dns_client_server_add(const char *server_ip, const char *server_host
 	}
 
 	/* if server type is TLS, create ssl context */
-	if (server_type == DNS_SERVER_TLS || server_type == DNS_SERVER_HTTPS || server_type == DNS_SERVER_QUIC) {
-		if (server_type == DNS_SERVER_QUIC) {
+	if (server_type == DNS_SERVER_TLS || server_type == DNS_SERVER_HTTPS || server_type == DNS_SERVER_QUIC ||
+		server_type == DNS_SERVER_HTTP3) {
+		if (server_type == DNS_SERVER_QUIC || server_type == DNS_SERVER_HTTP3) {
 			server_info->ssl_ctx = _ssl_ctx_get(1);
 		} else {
 			server_info->ssl_ctx = _ssl_ctx_get(0);
@@ -1573,6 +1592,7 @@ static void _dns_client_shutdown_socket(struct dns_server_info *server_info)
 		break;
 	case DNS_SERVER_QUIC:
 	case DNS_SERVER_TLS:
+	case DNS_SERVER_HTTP3:
 	case DNS_SERVER_HTTPS:
 		if (server_info->ssl) {
 			/* Shutdown ssl */
@@ -1979,7 +1999,7 @@ static void _dns_client_check_tcp(void)
 		}
 
 #ifdef OSSL_QUIC1_VERSION
-		if (server_info->type == DNS_SERVER_QUIC) {
+		if (server_info->type == DNS_SERVER_QUIC || server_info->type == DNS_SERVER_HTTP3) {
 			if (server_info->ssl) {
 				SSL_handle_events(server_info->ssl);
 				if (SSL_get_shutdown(server_info->ssl) != 0) {
@@ -2665,8 +2685,9 @@ static int _DNS_client_create_socket_quic(struct dns_server_info *server_info, c
 
 	SSL_set1_host(ssl, hostname);
 
-	if (alpn == NULL || alpn[0] == '\0') {
-		alpn = "doq";
+	if (alpn == NULL) {
+		tlog(TLOG_INFO, "alpn is null.");
+		goto errout;
 	}
 
 	alpn_len = strnlen(alpn, DNS_MAX_ALPN_LEN - 1);
@@ -2908,12 +2929,24 @@ static int _dns_client_create_socket(struct dns_server_info *server_info)
 		return _DNS_client_create_socket_tls(server_info, flag_tls->hostname, flag_tls->alpn);
 	} else if (server_info->type == DNS_SERVER_QUIC) {
 		struct client_dns_server_flag_tls *flag_tls = NULL;
+		const char *alpn = "doq";
 		flag_tls = &server_info->flags.tls;
-		return _DNS_client_create_socket_quic(server_info, flag_tls->hostname, flag_tls->alpn);
+		if (flag_tls->alpn[0] != 0) {
+			alpn = flag_tls->alpn;
+		}
+		return _DNS_client_create_socket_quic(server_info, flag_tls->hostname, alpn);
 	} else if (server_info->type == DNS_SERVER_HTTPS) {
 		struct client_dns_server_flag_https *flag_https = NULL;
 		flag_https = &server_info->flags.https;
 		return _DNS_client_create_socket_tls(server_info, flag_https->hostname, flag_https->alpn);
+	} else if (server_info->type == DNS_SERVER_HTTP3) {
+		struct client_dns_server_flag_https *flag_https = NULL;
+		const char *alpn = "h3";
+		flag_https = &server_info->flags.https;
+		if (flag_https->alpn[0] != 0) {
+			alpn = flag_https->alpn;
+		}
+		return _DNS_client_create_socket_quic(server_info, flag_https->hostname, alpn);
 	} else {
 		return -1;
 	}
@@ -3307,7 +3340,7 @@ static int _dns_client_socket_send(struct dns_server_info *server_info)
 	} else if (server_info->type == DNS_SERVER_TCP) {
 		return send(server_info->fd, server_info->send_buff.data, server_info->send_buff.len, MSG_NOSIGNAL);
 	} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS ||
-			   server_info->type == DNS_SERVER_QUIC) {
+			   server_info->type == DNS_SERVER_QUIC || server_info->type == DNS_SERVER_HTTP3) {
 		int write_len = server_info->send_buff.len;
 		if (server_info->ssl_write_len > 0) {
 			write_len = server_info->ssl_write_len;
@@ -3338,7 +3371,7 @@ static int _dns_client_socket_recv(struct dns_server_info *server_info)
 		return recv(server_info->fd, server_info->recv_buff.data + server_info->recv_buff.len,
 					DNS_TCP_BUFFER - server_info->recv_buff.len, 0);
 	} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS ||
-			   server_info->type == DNS_SERVER_QUIC) {
+			   server_info->type == DNS_SERVER_QUIC || server_info->type == DNS_SERVER_HTTP3) {
 		int ret = _dns_client_socket_ssl_recv(server_info, server_info->recv_buff.data + server_info->recv_buff.len,
 											  DNS_TCP_BUFFER - server_info->recv_buff.len);
 		if (ret == -SSL_ERROR_WANT_WRITE && errno == EAGAIN) {
@@ -3366,12 +3399,12 @@ static int _dns_client_process_tcp_buff(struct dns_server_info *server_info)
 
 	while (1) {
 		if (server_info->type == DNS_SERVER_HTTPS) {
-			http_head = http_head_init(4096);
+			http_head = http_head_init(4096, HTTP_VERSION_1_1);
 			if (http_head == NULL) {
 				goto out;
 			}
 
-			len = http_head_parse(http_head, (char *)server_info->recv_buff.data, server_info->recv_buff.len);
+			len = http_head_parse(http_head, server_info->recv_buff.data, server_info->recv_buff.len);
 			if (len < 0) {
 				if (len == -1) {
 					ret = 0;
@@ -3449,9 +3482,59 @@ out:
 	return ret;
 }
 
+#ifdef OSSL_QUIC1_VERSION
+static int _dns_client_process_recv_http3(struct dns_server_info *server_info, struct dns_conn_stream *conn_stream)
+{
+	int ret = 0;
+	struct http_head *http_head = NULL;
+	uint8_t *pkg_data = NULL;
+	int pkg_len = 0;
+
+	http_head = http_head_init(4096, HTTP_VERSION_3_0);
+	if (http_head == NULL) {
+		goto errout;
+	}
+
+	ret = http_head_parse(http_head, conn_stream->recv_buff.data, conn_stream->recv_buff.len);
+	if (ret < 0) {
+		if (ret == -1) {
+			goto out;
+		}
+
+		tlog(TLOG_DEBUG, "remote server not supported.");
+		goto errout;
+	}
+
+	if (http_head_get_httpcode(http_head) != 200) {
+		tlog(TLOG_WARN, "http3 server query from %s:%d failed, server return http code : %d, %s", server_info->ip,
+			 server_info->port, http_head_get_httpcode(http_head), http_head_get_httpcode_msg(http_head));
+		server_info->prohibit = 1;
+		goto errout;
+	}
+
+	pkg_data = (uint8_t *)http_head_get_data(http_head);
+	pkg_len = http_head_get_data_len(http_head);
+	if (pkg_data == NULL || pkg_len <= 0) {
+		goto errout;
+	}
+
+	if (_dns_client_recv(server_info, pkg_data, pkg_len, &server_info->addr, server_info->ai_addrlen) != 0) {
+		goto errout;
+	}
+out:
+	http_head_destroy(http_head);
+	return 0;
+errout:
+
+	if (http_head) {
+		http_head_destroy(http_head);
+	}
+
+	return -1;
+}
+
 static int _dns_client_process_quic(struct dns_server_info *server_info, struct epoll_event *event, unsigned long now)
 {
-#ifdef OSSL_QUIC1_VERSION
 	if (event->events & EPOLLIN) {
 		/* connection is closed, reconnect */
 		if (SSL_get_shutdown(server_info->ssl) != 0) {
@@ -3515,12 +3598,39 @@ static int _dns_client_process_quic(struct dns_server_info *server_info, struct
 
 				int read_len = _dns_client_socket_ssl_recv_ext(server_info, poll_items[i].desc.value.ssl,
 															   conn_stream->recv_buff.data, DNS_TCP_BUFFER);
-				unsigned short qid = htons(conn_stream->query->sid);
-				memcpy(conn_stream->recv_buff.data + 2, &qid, 2);
-				if (_dns_client_recv(server_info, conn_stream->recv_buff.data + 2, read_len - 2, &server_info->addr,
-									 server_info->ai_addrlen) != 0) {
+
+				if (read_len < 0) {
+					if (errno == EAGAIN) {
+						continue;
+					}
+
+					tlog(TLOG_ERROR, "recv failed, %s", strerror(errno));
 					goto errout;
 				}
+
+				conn_stream->recv_buff.len += read_len;
+
+				if (server_info->type == DNS_SERVER_HTTP3) {
+					ret = _dns_client_process_recv_http3(server_info, conn_stream);
+					if (ret != 0) {
+						tlog(TLOG_ERROR, "process http3 failed.");
+						goto errout;
+					}
+
+				} else if (server_info->type == DNS_SERVER_QUIC) {
+					unsigned short qid = htons(conn_stream->query->sid);
+					int msg_len = ntohs(*((unsigned short *)(conn_stream->recv_buff.data)));
+					if (msg_len <= 0 || msg_len >= DNS_IN_PACKSIZE) {
+						/* data len is invalid */
+						goto errout;
+					}
+
+					memcpy(conn_stream->recv_buff.data + 2, &qid, 2);
+					if (_dns_client_recv(server_info, conn_stream->recv_buff.data + 2, conn_stream->recv_buff.len - 2,
+										 &server_info->addr, server_info->ai_addrlen) != 0) {
+						goto errout;
+					}
+				}
 			}
 		}
 	}
@@ -3541,8 +3651,8 @@ static int _dns_client_process_quic(struct dns_server_info *server_info, struct
 				goto errout;
 			}
 
-			memset(conn_stream->send_buff.data + 2, 0, 2);
 			SSL_set_ex_data(conn_stream->quic_stream, 0, conn_stream);
+
 			int send_len =
 				_dns_client_socket_ssl_send_ext(server_info, conn_stream->quic_stream, conn_stream->send_buff.data,
 												conn_stream->send_buff.len, SSL_WRITE_FLAG_CONCLUDE);
@@ -3565,15 +3675,11 @@ static int _dns_client_process_quic(struct dns_server_info *server_info, struct
 			goto errout;
 		}
 	}
-#else
-	tlog(TLOG_ERROR, "quic not supported.");
-	goto errout;
-#endif
 	return 0;
-
 errout:
 	return -1;
 }
+#endif
 
 static int _dns_client_process_tcp(struct dns_server_info *server_info, struct epoll_event *event, unsigned long now)
 {
@@ -3998,9 +4104,14 @@ static int _dns_client_process_tls(struct dns_server_info *server_info, struct e
 		event->events = EPOLLOUT;
 	}
 
-	if (server_info->type == DNS_SERVER_QUIC) {
-		/* QUIC */
+	if (server_info->type == DNS_SERVER_QUIC || server_info->type == DNS_SERVER_HTTP3) {
+/* QUIC */
+#ifdef OSSL_QUIC1_VERSION
 		return _dns_client_process_quic(server_info, event, now);
+#else
+		tlog(TLOG_ERROR, "quic/http3 is not supported.");
+		goto errout;
+#endif
 	}
 
 	return _dns_client_process_tcp(server_info, event, now);
@@ -4097,7 +4208,7 @@ static int _dns_client_process(struct dns_server_info *server_info, struct epoll
 		/* receive from tcp */
 		return _dns_client_process_tcp(server_info, event, now);
 	} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS ||
-			   server_info->type == DNS_SERVER_QUIC) {
+			   server_info->type == DNS_SERVER_QUIC || server_info->type == DNS_SERVER_HTTP3) {
 		/* receive from tls */
 		return _dns_client_process_tls(server_info, event, now);
 	} else {
@@ -4409,6 +4520,9 @@ static int _dns_client_send_quic(struct dns_query_struct *query, struct dns_serv
 	memcpy(inpacket + 2, packet, len);
 	len += 2;
 
+	/* set query id to zero */
+	memset(inpacket + 2, 0, 2);
+
 	struct dns_conn_stream *stream = NULL;
 	stream = malloc(sizeof(struct dns_conn_stream));
 	if (stream == NULL) {
@@ -4443,8 +4557,6 @@ static int _dns_client_send_quic(struct dns_query_struct *query, struct dns_serv
 	/* bind stream */
 	SSL_set_ex_data(quic_stream, 0, stream);
 
-	/* set query id to zero */
-	memset(inpacket + 2, 0, 2);
 	send_len = _dns_client_socket_ssl_send_ext(server_info, quic_stream, inpacket, len, SSL_WRITE_FLAG_CONCLUDE);
 	if (send_len <= 0) {
 		if (errno == EAGAIN || errno == EPIPE || server_info->ssl == NULL) {
@@ -4464,6 +4576,112 @@ static int _dns_client_send_quic(struct dns_query_struct *query, struct dns_serv
 	return 0;
 }
 
+static int _dns_client_send_http3(struct dns_query_struct *query, struct dns_server_info *server_info, void *packet,
+								  unsigned short len)
+{
+#ifdef OSSL_QUIC1_VERSION
+	int send_len = 0;
+	int http_len = 0;
+	unsigned char inpacket_data[DNS_IN_PACKSIZE];
+	unsigned char *inpacket = inpacket_data;
+	struct client_dns_server_flag_https *https_flag = NULL;
+	struct http_head *http_head = NULL;
+
+	if (len > sizeof(inpacket_data) - 128) {
+		tlog(TLOG_ERROR, "packet size is invalid.");
+		goto errout;
+	}
+
+	if (query->conn_stream) {
+		_dns_client_conn_stream_free(query->conn_stream);
+		query->conn_stream = NULL;
+	}
+
+	https_flag = &server_info->flags.https;
+
+	http_head = http_head_init(4096, HTTP_VERSION_3_0);
+	if (http_head == NULL) {
+		tlog(TLOG_ERROR, "init http head failed.");
+		goto errout;
+	}
+
+	http_head_set_method(http_head, HTTP_METHOD_POST);
+	http_head_set_url(http_head, https_flag->path);
+	http_head_set_head_type(http_head, HTTP_HEAD_REQUEST);
+	http_head_add_fields(http_head, ":authority", https_flag->httphost);
+	http_head_add_fields(http_head, "user-agent", "smartdns");
+	http_head_add_fields(http_head, "content-type", "application/dns-message");
+	http_head_add_fields(http_head, "accept-encoding", "identity");
+	http_head_set_data(http_head, packet, len);
+
+	http_len = http_head_serialize(http_head, inpacket_data, DNS_IN_PACKSIZE);
+	if (http_len <= 0) {
+		tlog(TLOG_ERROR, "serialize http head failed.");
+		goto errout;
+	}
+
+	struct dns_conn_stream *stream = NULL;
+	stream = malloc(sizeof(struct dns_conn_stream));
+	if (stream == NULL) {
+		tlog(TLOG_ERROR, "malloc memory failed.");
+		goto errout;
+	}
+
+	memset(stream, 0, sizeof(struct dns_conn_stream));
+	stream->query = query;
+	stream->server_info = server_info;
+	query->conn_stream = stream;
+	INIT_LIST_HEAD(&stream->list);
+
+	if (server_info->status != DNS_SERVER_STATUS_CONNECTED) {
+		return _dns_client_quic_pending_data(stream, server_info, inpacket, http_len);
+	}
+
+	/* run hand shake */
+	SSL_handle_events(server_info->ssl);
+
+	SSL *quic_stream = SSL_new_stream(server_info->ssl, 0);
+	if (quic_stream == NULL) {
+		_dns_client_shutdown_socket(server_info);
+		return _dns_client_quic_pending_data(stream, server_info, inpacket, http_len);
+	}
+
+	pthread_mutex_lock(&server_info->lock);
+	list_add_tail(&stream->list, &server_info->conn_stream_list);
+	stream->quic_stream = quic_stream;
+	pthread_mutex_unlock(&server_info->lock);
+
+	/* bind stream */
+	SSL_set_ex_data(quic_stream, 0, stream);
+
+	send_len = _dns_client_socket_ssl_send_ext(server_info, quic_stream, inpacket, http_len, SSL_WRITE_FLAG_CONCLUDE);
+	if (send_len <= 0) {
+		if (errno == EAGAIN || errno == EPIPE || server_info->ssl == NULL) {
+			/* save data to buffer, and retry when EPOLLOUT is available */
+			return _dns_client_quic_pending_data(stream, server_info, inpacket, http_len);
+		} else if (server_info->ssl && errno != ENOMEM) {
+			_dns_client_shutdown_socket(server_info);
+		}
+		return -1;
+	} else if (send_len < len) {
+		/* save remain data to buffer, and retry when EPOLLOUT is available */
+		return _dns_client_quic_pending_data(stream, server_info, inpacket + send_len, http_len - send_len);
+	}
+
+	http_head_destroy(http_head);
+	return 0;
+errout:
+	if (http_head) {
+		http_head_destroy(http_head);
+	}
+
+	return -1;
+#else
+	tlog(TLOG_ERROR, "http3 is not supported.");
+#endif
+	return 0;
+}
+
 static int _dns_client_setup_server_packet(struct dns_server_info *server_info, struct dns_query_struct *query,
 										   void *default_packet, int default_packet_len,
 										   unsigned char *packet_data_buffer, void **packet_data, int *packet_data_len)
@@ -4675,6 +4893,11 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
 				ret = _dns_client_send_quic(query, server_info, packet_data, packet_data_len);
 				send_err = errno;
 				break;
+			case DNS_SERVER_HTTP3:
+				/* http3 query */
+				ret = _dns_client_send_http3(query, server_info, packet_data, packet_data_len);
+				send_err = errno;
+				break;
 			default:
 				/* unsupported query type */
 				ret = -1;

+ 1 - 0
src/dns_client.h

@@ -45,6 +45,7 @@ typedef enum {
 	DNS_SERVER_HTTPS,
 	DNS_SERVER_MDNS,
 	DNS_SERVER_QUIC,
+	DNS_SERVER_HTTP3,
 	DNS_SERVER_TYPE_END,
 } dns_server_type_t;
 

+ 19 - 3
src/dns_conf.c

@@ -907,6 +907,12 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 		if (strcasecmp(scheme, "https") == 0) {
 			type = DNS_SERVER_HTTPS;
 			default_port = DEFAULT_DNS_HTTPS_PORT;
+		} else if (strcasecmp(scheme, "http3") == 0) {
+			type = DNS_SERVER_HTTP3;
+			default_port = DEFAULT_DNS_HTTPS_PORT;
+		} else if (strcasecmp(scheme, "h3") == 0) {
+			type = DNS_SERVER_HTTP3;
+			default_port = DEFAULT_DNS_HTTPS_PORT;
 		} else if (strcasecmp(scheme, "quic") == 0) {
 			type = DNS_SERVER_QUIC;
 			default_port = DEFAULT_DNS_QUIC_PORT;
@@ -926,8 +932,8 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 	}
 
 #ifndef OSSL_QUIC1_VERSION
-	if (type == DNS_SERVER_QUIC) {
-		tlog(TLOG_ERROR, "quic not support.");
+	if (type == DNS_SERVER_QUIC || type == DNS_SERVER_HTTP3) {
+		tlog(TLOG_ERROR, "quic / http3 not support.");
 		return -1;
 	}
 #endif
@@ -1105,7 +1111,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 	server->ttl = ttl;
 	server->drop_packet_latency_ms = drop_packet_latency_ms;
 
-	if (server->type == DNS_SERVER_HTTPS) {
+	if (server->type == DNS_SERVER_HTTPS || server->type == DNS_SERVER_HTTP3) {
 		if (server->path[0] == 0) {
 			safe_strncpy(server->path, "/", sizeof(server->path));
 		}
@@ -3232,6 +3238,14 @@ static int _config_server_quic(void *data, int argc, char *argv[])
 	return ret;
 }
 
+static int _config_server_http3(void *data, int argc, char *argv[])
+{
+	int ret = 0;
+	ret = _config_server(argc, argv, DNS_SERVER_HTTP3, DEFAULT_DNS_HTTPS_PORT);
+
+	return ret;
+}
+
 static int _conf_domain_rule_nameserver(const char *domain, const char *group_name)
 {
 	struct dns_nameserver_rule *nameserver_rule = NULL;
@@ -5979,6 +5993,8 @@ static struct config_item _config_item[] = {
 	CONF_CUSTOM("server-tcp", _config_server_tcp, NULL),
 	CONF_CUSTOM("server-tls", _config_server_tls, NULL),
 	CONF_CUSTOM("server-https", _config_server_https, NULL),
+	CONF_CUSTOM("server-h3", _config_server_http3, NULL),
+	CONF_CUSTOM("server-http3", _config_server_http3, NULL),
 	CONF_CUSTOM("server-quic", _config_server_quic, NULL),
 	CONF_YESNO("mdns-lookup", &dns_conf.mdns_lookup),
 	CONF_YESNO("local-ptr-enable", &dns_conf.local_ptr_enable),

+ 3 - 3
src/dns_server.c

@@ -153,7 +153,7 @@ struct local_addr_cache {
 };
 
 struct dns_conn_buf {
-	char buf[DNS_CONN_BUFF_SIZE];
+	uint8_t buf[DNS_CONN_BUFF_SIZE];
 	int buffsize;
 	int size;
 };
@@ -8045,12 +8045,12 @@ static int _dns_server_tcp_process_one_request(struct dns_server_conn_tcp_client
 				goto out;
 			}
 
-			http_head = http_head_init(4096);
+			http_head = http_head_init(4096, HTTP_VERSION_1_1);
 			if (http_head == NULL) {
 				goto out;
 			}
 
-			len = http_head_parse(http_head, (char *)tcpclient->recvbuff.buf, tcpclient->recvbuff.size);
+			len = http_head_parse(http_head, tcpclient->recvbuff.buf, tcpclient->recvbuff.size);
 			if (len < 0) {
 				if (len == -1) {
 					ret = 0;

+ 970 - 31
src/http_parse.c

@@ -21,40 +21,46 @@
 #include "hashtable.h"
 #include "jhash.h"
 #include "list.h"
+#include "qpack.h"
 #include "util.h"
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+#define HTTP3_HEADER_FRAME 1
+#define HTTP3_DATA_FRAME 0
+
 struct http_head_fields {
 	struct hlist_node node;
 	struct list_head list;
 
-	char *name;
-	char *value;
+	const char *name;
+	const char *value;
 };
 
 struct http_params {
 	struct hlist_node node;
 	struct list_head list;
 
-	char *name;
-	char *value;
+	const char *name;
+	const char *value;
 };
 
 struct http_head {
+	HTTP_VERSION http_version;
 	HTTP_HEAD_TYPE head_type;
 	HTTP_METHOD method;
-	char *url;
-	char *version;
+	const char *url;
+	const char *version;
 	int code;
-	char *code_msg;
+	const char *code_msg;
 	int buff_size;
 	int buff_len;
-	char *buff;
+	uint8_t *buff;
 	int head_ok;
 	int head_len;
-	char *data;
+	const uint8_t *data;
 	int data_len;
 	int expect_data_len;
 	struct http_head_fields field_head;
@@ -69,10 +75,10 @@ struct http_head {
  *  -1   - Incomplete request
  *  -2   - parse failed
  */
-struct http_head *http_head_init(int buffsize)
+struct http_head *http_head_init(int buffsize, HTTP_VERSION version)
 {
 	struct http_head *http_head = NULL;
-	char *buffer = NULL;
+	unsigned char *buffer = NULL;
 
 	http_head = malloc(sizeof(*http_head));
 	if (http_head == NULL) {
@@ -91,6 +97,7 @@ struct http_head *http_head_init(int buffsize)
 
 	http_head->buff = buffer;
 	http_head->buff_size = buffsize;
+	http_head->http_version = version;
 
 	return http_head;
 
@@ -217,7 +224,7 @@ int http_head_get_httpcode(struct http_head *http_head)
 	return http_head->code;
 }
 
-char *http_head_get_httpcode_msg(struct http_head *http_head)
+const char *http_head_get_httpcode_msg(struct http_head *http_head)
 {
 	return http_head->code_msg;
 }
@@ -227,7 +234,7 @@ HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head)
 	return http_head->head_type;
 }
 
-char *http_head_get_data(struct http_head *http_head)
+const unsigned char *http_head_get_data(struct http_head *http_head)
 {
 	return http_head->data;
 }
@@ -237,7 +244,35 @@ int http_head_get_data_len(struct http_head *http_head)
 	return http_head->data_len;
 }
 
-static int _http_head_add_param(struct http_head *http_head, char *name, char *value)
+static int _http_head_buffer_left_len(struct http_head *http_head)
+{
+	return http_head->buff_size - http_head->buff_len;
+}
+
+static uint8_t *_http_head_buffer_get_end(struct http_head *http_head)
+{
+	return http_head->buff + http_head->buff_len;
+}
+
+static uint8_t *_http_head_buffer_append(struct http_head *http_head, const uint8_t *data, int data_len)
+{
+	if (http_head == NULL || data_len < 0) {
+		return NULL;
+	}
+
+	if (http_head->buff_len + data_len > http_head->buff_size) {
+		return NULL;
+	}
+
+	if (data != NULL) {
+		memcpy(http_head->buff + http_head->buff_len, data, data_len);
+	}
+	http_head->buff_len += data_len;
+
+	return (http_head->buff + http_head->buff_len);
+}
+
+static int _http_head_add_param(struct http_head *http_head, const char *name, const char *value)
 {
 	uint32_t key = 0;
 	struct http_params *params = NULL;
@@ -257,7 +292,84 @@ static int _http_head_add_param(struct http_head *http_head, char *name, char *v
 	return 0;
 }
 
-static int _http_head_add_fields(struct http_head *http_head, char *name, char *value)
+int http_head_add_param(struct http_head *http_head, const char *name, const char *value)
+{
+	if (http_head == NULL || name == NULL || value == NULL) {
+		return -1;
+	}
+
+	return _http_head_add_param(http_head, name, value);
+}
+
+int http_head_set_url(struct http_head *http_head, const char *url)
+{
+	if (http_head == NULL || url == NULL) {
+		return -1;
+	}
+
+	http_head->url = url;
+
+	return 0;
+}
+
+int http_head_set_httpversion(struct http_head *http_head, const char *version)
+{
+	if (http_head == NULL || version == NULL) {
+		return -1;
+	}
+
+	http_head->version = version;
+
+	return 0;
+}
+
+int http_head_set_httpcode(struct http_head *http_head, int code, const char *msg)
+{
+	if (http_head == NULL || code < 0 || msg == NULL) {
+		return -1;
+	}
+
+	http_head->code = code;
+	http_head->code_msg = msg;
+
+	return 0;
+}
+
+int http_head_set_head_type(struct http_head *http_head, HTTP_HEAD_TYPE head_type)
+{
+	if (http_head == NULL || head_type == HTTP_HEAD_INVALID) {
+		return -1;
+	}
+
+	http_head->head_type = head_type;
+
+	return 0;
+}
+
+int http_head_set_method(struct http_head *http_head, HTTP_METHOD method)
+{
+	if (http_head == NULL || method == HTTP_METHOD_INVALID) {
+		return -1;
+	}
+
+	http_head->method = method;
+
+	return 0;
+}
+
+int http_head_set_data(struct http_head *http_head, const void *data, int len)
+{
+	if (http_head == NULL || data == NULL || len < 0) {
+		return -1;
+	}
+
+	http_head->data = (unsigned char *)data;
+	http_head->data_len = len;
+
+	return 0;
+}
+
+static int _http_head_add_fields(struct http_head *http_head, const char *name, const char *value)
 {
 	uint32_t key = 0;
 	struct http_head_fields *fields = NULL;
@@ -277,6 +389,15 @@ static int _http_head_add_fields(struct http_head *http_head, char *name, char *
 	return 0;
 }
 
+int http_head_add_fields(struct http_head *http_head, const char *name, const char *value)
+{
+	if (http_head == NULL || name == NULL || value == NULL) {
+		return -1;
+	}
+
+	return _http_head_add_fields(http_head, name, value);
+}
+
 static int _http_head_parse_response(struct http_head *http_head, char *key, char *value)
 {
 	char *field_start = NULL;
@@ -372,6 +493,49 @@ static int _http_head_parse_params(struct http_head *http_head, char *url, int u
 	return 0;
 }
 
+const char *http_method_str(HTTP_METHOD method)
+{
+	switch (method) {
+	case HTTP_METHOD_GET:
+		return "GET";
+	case HTTP_METHOD_POST:
+		return "POST";
+	case HTTP_METHOD_PUT:
+		return "PUT";
+	case HTTP_METHOD_DELETE:
+		return "DELETE";
+	case HTTP_METHOD_TRACE:
+		return "TRACE";
+	case HTTP_METHOD_CONNECT:
+		return "CONNECT";
+	default:
+		return "INVALID";
+	}
+}
+
+static HTTP_METHOD _http_method_parse(const char *method)
+{
+	if (method == NULL) {
+		return HTTP_METHOD_INVALID;
+	}
+
+	if (strncmp(method, "GET", sizeof("GET")) == 0) {
+		return HTTP_METHOD_GET;
+	} else if (strncmp(method, "POST", sizeof("POST")) == 0) {
+		return HTTP_METHOD_POST;
+	} else if (strncmp(method, "PUT", sizeof("PUT")) == 0) {
+		return HTTP_METHOD_PUT;
+	} else if (strncmp(method, "DELETE", sizeof("DELETE")) == 0) {
+		return HTTP_METHOD_DELETE;
+	} else if (strncmp(method, "TRACE", sizeof("TRACE")) == 0) {
+		return HTTP_METHOD_TRACE;
+	} else if (strncmp(method, "CONNECT", sizeof("CONNECT")) == 0) {
+		return HTTP_METHOD_CONNECT;
+	}
+
+	return HTTP_METHOD_INVALID;
+}
+
 static int _http_head_parse_request(struct http_head *http_head, char *key, char *value)
 {
 	int method = HTTP_METHOD_INVALID;
@@ -380,19 +544,8 @@ static int _http_head_parse_request(struct http_head *http_head, char *key, char
 	char *tmp_ptr = value;
 	char *field_start = NULL;
 
-	if (strncmp(key, "GET", sizeof("GET")) == 0) {
-		method = HTTP_METHOD_GET;
-	} else if (strncmp(key, "POST", sizeof("POST")) == 0) {
-		method = HTTP_METHOD_POST;
-	} else if (strncmp(key, "PUT", sizeof("PUT")) == 0) {
-		method = HTTP_METHOD_PUT;
-	} else if (strncmp(key, "DELETE", sizeof("DELETE")) == 0) {
-		method = HTTP_METHOD_DELETE;
-	} else if (strncmp(key, "TRACE", sizeof("TRACE")) == 0) {
-		method = HTTP_METHOD_TRACE;
-	} else if (strncmp(key, "CONNECT", sizeof("CONNECT")) == 0) {
-		method = HTTP_METHOD_CONNECT;
-	} else {
+	method = _http_method_parse(key);
+	if (method == HTTP_METHOD_INVALID) {
 		return _http_head_parse_response(http_head, key, value);
 	}
 
@@ -412,6 +565,7 @@ static int _http_head_parse_request(struct http_head *http_head, char *key, char
 
 	if (field_start && version == NULL) {
 		version = field_start;
+		tmp_ptr = field_start;
 	}
 
 	if (_http_head_parse_params(http_head, url, tmp_ptr - url) != 0) {
@@ -437,7 +591,7 @@ static int _http_head_parse(struct http_head *http_head)
 	int inkey = 1;
 	int invalue = 0;
 
-	data = http_head->buff;
+	data = (char *)http_head->buff;
 	for (i = 0; i < http_head->head_len; i++, data++) {
 		if (inkey) {
 			if (key == NULL && *data != ' ' && *data != '\r' && *data != '\n') {
@@ -489,10 +643,10 @@ static int _http_head_parse(struct http_head *http_head)
 	return 0;
 }
 
-int http_head_parse(struct http_head *http_head, const char *data, int data_len)
+static int _http_head_parse_http1_1(struct http_head *http_head, const uint8_t *data, int data_len)
 {
 	int i = 0;
-	char *buff_end = NULL;
+	uint8_t *buff_end = NULL;
 	int left_size = 0;
 	int process_data_len = 0;
 
@@ -553,6 +707,10 @@ int http_head_parse(struct http_head *http_head, const char *data, int data_len)
 
 	if (http_head->head_ok == 1) {
 		int get_data_len = (http_head->expect_data_len > data_len) ? data_len : http_head->expect_data_len;
+		if (get_data_len == 0 && data_len > 0) {
+			get_data_len = data_len;
+		}
+
 		if (http_head->data == NULL) {
 			http_head->data = buff_end;
 		}
@@ -560,6 +718,12 @@ int http_head_parse(struct http_head *http_head, const char *data, int data_len)
 		memcpy(buff_end, data, get_data_len);
 		process_data_len += get_data_len;
 		http_head->data_len += get_data_len;
+		buff_end += get_data_len;
+
+		/* try append null byte */
+		if (process_data_len < http_head->buff_size - 1) {
+			buff_end[0] = '\0';
+		}
 	}
 
 	http_head->buff_len += process_data_len;
@@ -570,6 +734,781 @@ int http_head_parse(struct http_head *http_head, const char *data, int data_len)
 	return process_data_len;
 }
 
+static int _http_head_serialize_http1_1(struct http_head *http_head, char *buffer, int buffer_len)
+{
+	int len = 0;
+	char *buff_start = buffer;
+	struct http_head_fields *fields = NULL;
+	struct http_params *params = NULL;
+
+	if (http_head->head_type == HTTP_HEAD_INVALID) {
+		return -1;
+	}
+
+	if (http_head->head_type == HTTP_HEAD_REQUEST) {
+		if (http_head->method == HTTP_METHOD_INVALID || http_head->url == NULL || http_head->version == NULL) {
+			return -1;
+		}
+
+		len = snprintf(buffer, buffer_len, "%s %s", http_method_str(http_head->method), http_head->url);
+		if (len < 0) {
+			return -2;
+		}
+
+		buffer += len;
+		buffer_len -= len;
+
+		if (buffer_len < 2) {
+			return -3;
+		}
+
+		int count = 0;
+		list_for_each_entry(params, &http_head->params.list, list)
+		{
+			if (count == 0) {
+				len = snprintf(buffer, buffer_len, "?%s=%s", params->name, params->value);
+			} else {
+				len = snprintf(buffer, buffer_len, "&%s=%s", params->name, params->value);
+			}
+
+			count++;
+			buffer += len;
+			buffer_len -= len;
+
+			if (buffer_len < 2) {
+				return -3;
+			}
+		}
+
+		if (buffer_len < 2) {
+			return -3;
+		}
+
+		len = snprintf(buffer, buffer_len, " %s\r\n", http_head->version);
+		if (len < 0) {
+			return -2;
+		}
+		buffer += len;
+		buffer_len -= len;
+	}
+
+	if (http_head->head_type == HTTP_HEAD_RESPONSE) {
+		if (http_head->code < 0 || http_head->code_msg == NULL || http_head->version == NULL) {
+			return -1;
+		}
+
+		len = snprintf(buffer, buffer_len, "%s %d %s\r\n", http_head->version, http_head->code, http_head->code_msg);
+		if (len < 0) {
+			return -2;
+		}
+
+		buffer += len;
+		buffer_len -= len;
+		if (buffer_len < 2) {
+			return -3;
+		}
+	}
+
+	list_for_each_entry(fields, &http_head->field_head.list, list)
+	{
+		len = snprintf(buffer, buffer_len, "%s: %s\r\n", fields->name, fields->value);
+		if (len < 0) {
+			return -2;
+		}
+
+		buffer += len;
+		buffer_len -= len;
+		if (buffer_len < 2) {
+			return -3;
+		}
+	}
+
+	if (buffer_len < 2) {
+		return -3;
+	}
+
+	*(buffer) = '\r';
+	*(buffer + 1) = '\n';
+	buffer += 2;
+	buffer_len -= 2;
+
+	if (http_head->data_len > buffer_len) {
+		return -3;
+	}
+
+	if (http_head->data && http_head->data_len > 0) {
+		memcpy(buffer, http_head->data, http_head->data_len);
+		buffer += http_head->data_len;
+		buffer_len -= http_head->data_len;
+	}
+
+	return buffer - buff_start;
+}
+
+static int _http_head_parse_http2_0(struct http_head *http_head, const uint8_t *data, int data_len)
+{
+	return -2;
+}
+
+static int _http_head_serialize_http2_0(struct http_head *http_head, uint8_t *buffer, int buffer_len)
+{
+	return -2;
+}
+
+static int _quicvarint_encode(uint64_t value, uint8_t *buffer, int buffer_size)
+{
+	if (value <= 63) {
+		if (buffer_size < 1) {
+			return -1;
+		}
+		buffer[0] = (uint8_t)value;
+		return 1;
+	} else if (value <= 16383) {
+		if (buffer_size < 2) {
+			return -1;
+		}
+
+		buffer[0] = (uint8_t)((value >> 8) | 0x40);
+		buffer[1] = (uint8_t)value;
+		return 2;
+	} else if (value <= 1073741823) {
+		if (buffer_size < 4) {
+			return -1;
+		}
+		buffer[0] = (uint8_t)((value >> 24) | 0x80);
+		buffer[1] = (uint8_t)(value >> 16);
+		buffer[2] = (uint8_t)(value >> 8);
+		buffer[3] = (uint8_t)value;
+		return 4;
+	} else {
+		if (buffer_size < 8) {
+			return -1;
+		}
+		buffer[0] = (uint8_t)((value >> 56) | 0xC0);
+		buffer[1] = (uint8_t)(value >> 48);
+		buffer[2] = (uint8_t)(value >> 40);
+		buffer[3] = (uint8_t)(value >> 32);
+		buffer[4] = (uint8_t)(value >> 24);
+		buffer[5] = (uint8_t)(value >> 16);
+		buffer[6] = (uint8_t)(value >> 8);
+		buffer[7] = (uint8_t)value;
+		return 8;
+	}
+}
+
+static int _qpack_build_header(const char *name, const char *value, uint8_t *buffer, int buffer_size)
+{
+	int offset = 0;
+	int offset_ret = 0;
+	int name_len = strlen(name);
+	int value_len = strlen(value);
+
+	if (buffer_size - offset < 2) {
+		return -1;
+	}
+
+	if (name_len < 7) {
+		buffer[offset++] = 0x20 | name_len;
+	} else {
+		buffer[offset++] = 0x20 | 7;
+		buffer[offset++] = name_len - 7;
+	}
+
+	if (buffer_size - offset < name_len) {
+		return -1;
+	}
+
+	memcpy(buffer + offset, name, name_len);
+	offset += name_len;
+
+	if (buffer_size - offset < 2) {
+		return -1;
+	}
+
+	offset_ret = _quicvarint_encode(value_len, buffer + offset, buffer_size - offset);
+	if (offset_ret < 0) {
+		return -1;
+	}
+	offset += offset_ret;
+
+	if (buffer_size - offset < value_len) {
+		return -1;
+	}
+
+	memcpy(buffer + offset, value, value_len);
+	offset += value_len;
+
+	return offset;
+}
+
+static int _http3_build_headers_payload(struct http_head *http_head, uint8_t *buffer, int buffer_len)
+{
+	int offset = 0;
+	int offset_ret = 0;
+	struct http_head_fields *fields = NULL;
+	struct http_params *params = NULL;
+
+	/* Insert count and delta base */
+	if (buffer_len - offset < 2) {
+		return -1;
+	}
+
+	buffer[offset++] = 0;
+	buffer[offset++] = 0;
+
+	if (http_head->head_type == HTTP_HEAD_REQUEST) {
+		char request_path[1024];
+		char *request_path_buffer = request_path;
+		int request_path_buffer_len = sizeof(request_path);
+
+		int request_path_len = snprintf(request_path, sizeof(request_path), "%s", http_head->url);
+		if (request_path_len < 0) {
+			return -1;
+		}
+
+		request_path_buffer += request_path_len;
+		request_path_buffer_len -= request_path_len;
+
+		int count = 0;
+		list_for_each_entry(params, &http_head->params.list, list)
+		{
+			if (count == 0) {
+				request_path_len =
+					snprintf(request_path_buffer, request_path_buffer_len, "?%s=%s", params->name, params->value);
+			} else {
+				request_path_len =
+					snprintf(request_path_buffer, request_path_buffer_len, "&%s=%s", params->name, params->value);
+			}
+
+			count++;
+			request_path_buffer += request_path_len;
+			request_path_buffer_len -= request_path_len;
+
+			if (request_path_buffer_len < 2) {
+				return -3;
+			}
+		}
+
+		offset_ret =
+			_qpack_build_header(":method", http_method_str(http_head->method), buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+
+		offset_ret = _qpack_build_header(":path", request_path, buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+		offset_ret = _qpack_build_header(":scheme", "https", buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+	} else if (http_head->head_type == HTTP_HEAD_RESPONSE) {
+		char status_str[12];
+		sprintf(status_str, "%d", http_head->code);
+		offset_ret = _qpack_build_header(":status", status_str, buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+	}
+
+	list_for_each_entry(fields, &http_head->field_head.list, list)
+	{
+		offset_ret = _qpack_build_header(fields->name, fields->value, buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+	}
+
+	if (http_head->data_len > 0 && http_head->data) {
+		char len_str[12];
+		sprintf(len_str, "%d", http_head->data_len);
+		offset_ret = _qpack_build_header("content-length", len_str, buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+	}
+	return offset;
+}
+
+static int http3_build_body_payload(const uint8_t *body, int body_len, uint8_t *buffer, int buffer_len)
+{
+	int offset = 0;
+	int offset_ret = 0;
+
+	offset_ret = _quicvarint_encode(body_len, buffer + offset, buffer_len - offset);
+	if (offset_ret < 0) {
+		return -1;
+	}
+	offset += offset_ret;
+
+	if (buffer_len - offset < body_len) {
+		return -1;
+	}
+
+	memcpy(buffer + offset, body, body_len);
+	offset += body_len;
+
+	return offset;
+}
+
+static int _quicvarint_decode(const uint8_t *buffer, int buffer_len, uint64_t *value)
+{
+	if ((buffer[0] & 0xC0) == 0x00) {
+		if (buffer_len < 1) {
+			return -1;
+		}
+
+		*value = buffer[0];
+		return 1;
+	} else if ((buffer[0] & 0xC0) == 0x40) {
+		if (buffer_len < 2) {
+			return -1;
+		}
+		*value = ((uint64_t)(buffer[0] & 0x3F) << 8) | buffer[1];
+		return 2;
+	} else if ((buffer[0] & 0xC0) == 0x80) {
+		if (buffer_len < 4) {
+			return -1;
+		}
+		*value =
+			((uint64_t)(buffer[0] & 0x3F) << 24) | ((uint64_t)buffer[1] << 16) | ((uint64_t)buffer[2] << 8) | buffer[3];
+		return 4;
+	} else {
+		if (buffer_len < 8) {
+			return -1;
+		}
+		*value = ((uint64_t)(buffer[0] & 0x3F) << 56) | ((uint64_t)buffer[1] << 48) | ((uint64_t)buffer[2] << 40) |
+				 ((uint64_t)buffer[3] << 32) | ((uint64_t)buffer[4] << 24) | ((uint64_t)buffer[5] << 16) |
+				 ((uint64_t)buffer[6] << 8) | buffer[7];
+		return 8;
+	}
+}
+
+static int _quic_read_varint(const uint8_t *buffer, int buffer_len, uint64_t *value, int n)
+{
+	uint64_t i;
+	if (n < 1 || n > 8) {
+		return -2;
+	}
+	if (buffer_len == 0) {
+		return -1;
+	}
+
+	const uint8_t *p = buffer;
+	i = *p;
+	if (n < 8) {
+		i &= (1 << (uint64_t)n) - 1;
+	}
+
+	if (i < (uint64_t)(1 << (uint64_t)n) - 1) {
+		*value = i;
+		return 1;
+	}
+
+	p++;
+	uint64_t m = 0;
+
+	while (p < buffer + buffer_len) {
+		uint8_t b = *p;
+		i += (uint64_t)(b & 127) << m;
+		if ((b & 128) == 0) {
+			*value = i;
+			return p - buffer + 1;
+		}
+		m += 7;
+		if (m >= 63) {
+			return -1;
+		}
+		p++;
+	}
+	return -1;
+}
+
+static int _quic_read_string(const uint8_t *buffer, int buffer_len, char *str, int max_str_len, size_t *str_len, int n,
+							 int huffman)
+{
+	uint64_t len = 0;
+	int offset = 0;
+	int offset_ret = 0;
+
+	offset_ret = _quic_read_varint(buffer, buffer_len, &len, n);
+	if (offset_ret < 0) {
+		return -1;
+	}
+	offset += offset_ret;
+
+	if ((uint64_t)(buffer_len - offset) < len) {
+		return -1;
+	}
+
+	if ((uint64_t)max_str_len < len) {
+		return -1;
+	}
+
+	if (huffman) {
+		size_t char_len = 0;
+		if (qpack_huffman_decode(buffer + offset, buffer + offset + len, (uint8_t *)str, max_str_len, &char_len) < 0) {
+			return -1;
+		}
+
+		str[char_len] = '\0';
+		*str_len = char_len;
+	} else {
+		memcpy(str, buffer + offset, len);
+		str[len] = '\0';
+		*str_len = len;
+	}
+
+	return offset + len;
+}
+
+static int _http3_parse_headers_payload(struct http_head *http_head, const uint8_t *data, int data_len)
+{
+	int offset = 0;
+	int offset_ret = 0;
+	int insert_count = 0;
+	int delta_base = 0;
+	struct qpack_header_field *header = NULL;
+	const char *name = NULL;
+	const char *value = NULL;
+	uint64_t index = -1;
+	int use_huffman = 0;
+	uint8_t b = 0;
+	size_t str_len = 0;
+	int buffer_left_len = 0;
+
+	if (data_len < 2) {
+		return -1;
+	}
+
+	insert_count = data[0];
+	delta_base = data[1];
+
+	if (insert_count != 0 || delta_base != 0) {
+		return -2;
+	}
+
+	offset += 2;
+
+	while (offset < data_len) {
+		index = -1;
+		use_huffman = 0;
+		name = NULL;
+		value = NULL;
+
+		char *buffer_name = NULL;
+		char *buffer_value = NULL;
+
+		str_len = 0;
+		b = data[offset];
+
+		if (b & 0x80) {
+			/* indexed header*/
+			if ((b & 0x40) == 0) {
+				return -2;
+			}
+
+			offset_ret = _quic_read_varint(data + offset, data_len - offset, &index, 6);
+			if (offset_ret < 0) {
+				return -1;
+			}
+			offset += offset_ret;
+
+			header = qpack_get_static_header_field(index);
+			if (header == NULL) {
+				return -2;
+			}
+
+			name = header->name;
+			value = header->value;
+		} else if (b & 0x40) {
+			/* literal header with indexing */
+			if ((b & 0x10) == 0) {
+				return -2;
+			}
+			offset_ret = _quic_read_varint(data + offset, data_len - offset, &index, 4);
+			if (offset_ret < 0) {
+				return -1;
+			}
+			offset += offset_ret;
+
+			header = qpack_get_static_header_field(index);
+			if (header == NULL) {
+				return -2;
+			}
+
+			name = header->name;
+
+			b = data[offset];
+			if ((b & 0x80) > 0) {
+				use_huffman = 1;
+			}
+
+			buffer_value = (char *)_http_head_buffer_get_end(http_head);
+			buffer_left_len = _http_head_buffer_left_len(http_head);
+
+			offset_ret = _quic_read_string(data + offset, data_len - offset, buffer_value, buffer_left_len - 1,
+										   &str_len, 7, use_huffman);
+			if (offset_ret < 0) {
+				return -1;
+			}
+			offset += offset_ret;
+			buffer_value[str_len] = '\0';
+			if (_http_head_buffer_append(http_head, NULL, str_len + 1) == NULL) {
+				return -1;
+			}
+			value = buffer_value;
+		} else if (b & 0x20) {
+			/* literal header without indexing */
+			b = data[offset];
+			if ((b & 0x8) > 0) {
+				use_huffman = 1;
+			}
+
+			buffer_name = (char *)_http_head_buffer_get_end(http_head);
+			buffer_left_len = _http_head_buffer_left_len(http_head);
+
+			offset_ret = _quic_read_string(data + offset, data_len - offset, buffer_name, buffer_left_len - 1, &str_len,
+										   3, use_huffman);
+			if (offset_ret < 0) {
+				return -1;
+			}
+			offset += offset_ret;
+			buffer_name[str_len] = '\0';
+			if (_http_head_buffer_append(http_head, NULL, str_len + 1) == NULL) {
+				return -1;
+			}
+			name = buffer_name;
+
+			b = data[offset];
+			if ((b & 0x80) > 0) {
+				use_huffman = 1;
+			}
+
+			buffer_value = (char *)_http_head_buffer_get_end(http_head);
+			buffer_left_len = _http_head_buffer_left_len(http_head);
+			offset_ret = _quic_read_string(data + offset, data_len - offset, buffer_value, buffer_left_len - 1,
+										   &str_len, 7, use_huffman);
+			if (offset_ret < 0) {
+				return -1;
+			}
+			offset += offset_ret;
+			buffer_value[str_len] = '\0';
+			if (_http_head_buffer_append(http_head, NULL, str_len + 1) == NULL) {
+				return -1;
+			}
+			value = buffer_value;
+		} else {
+			return -2;
+		}
+
+		if (_http_head_add_fields(http_head, name, value) != 0) {
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int _http_head_setup_http3_0_httpcode(struct http_head *http_head)
+{
+	const char *status = NULL;
+	int status_code = 0;
+	const char *method = NULL;
+	const char *url = NULL;
+
+	method = http_head_get_fields_value(http_head, ":method");
+	if (method) {
+		http_head->method = _http_method_parse(method);
+		if (http_head->method == HTTP_METHOD_INVALID) {
+			return -1;
+		}
+
+		url = http_head_get_fields_value(http_head, ":path");
+		if (url == NULL) {
+			return -1;
+		}
+
+		http_head->url = url;
+		http_head->head_type = HTTP_HEAD_REQUEST;
+
+		if (_http_head_parse_params(http_head, (char *)url, strlen(url) + 1) != 0) {
+			return -1;
+		}
+
+		return 0;
+	}
+
+	status = http_head_get_fields_value(http_head, ":status");
+	if (status == NULL) {
+		return 0;
+	}
+
+	http_head->head_type = HTTP_HEAD_RESPONSE;
+
+	status_code = atoi(status);
+	if (status_code < 100 || status_code > 999) {
+		return -1;
+	}
+
+	http_head->code = status_code;
+	if (status_code == 200) {
+		return 0;
+	}
+
+	return 0;
+}
+
+static int _http_head_parse_http3_0(struct http_head *http_head, const uint8_t *data, int data_len)
+{
+	uint64_t frame_type = 0;
+	uint64_t frame_len = 0;
+	int offset = 0;
+	int offset_ret = 0;
+
+	while (offset < data_len) {
+		offset_ret = _quicvarint_decode((uint8_t *)data + offset, data_len - offset, &frame_type);
+		if (offset_ret < 0) {
+			return offset_ret;
+		}
+		offset += offset_ret;
+
+		offset_ret = _quicvarint_decode((uint8_t *)data + offset, data_len - offset, &frame_len);
+		if (offset_ret < 0) {
+			return offset_ret;
+		}
+		offset += offset_ret;
+
+		if ((uint64_t)(data_len - offset) < frame_len) {
+			return -1;
+		}
+
+		if (frame_type == HTTP3_HEADER_FRAME) {
+			int header_len = 0;
+			header_len = _http3_parse_headers_payload(http_head, data + offset, frame_len);
+			if (header_len < 0) {
+				return -1;
+			}
+
+			if (_http_head_setup_http3_0_httpcode(http_head) != 0) {
+				return -1;
+			}
+
+		} else if (frame_type == HTTP3_DATA_FRAME) {
+			if (http_head->code != 200 && http_head->head_type == HTTP_HEAD_RESPONSE) {
+				if (frame_len > (uint64_t)(http_head->buff_size - http_head->buff_len)) {
+					http_head->code_msg = "Unknow Error";
+					return 0;
+				}
+
+				memcpy(http_head->buff + http_head->buff_len, data + offset, frame_len);
+				http_head->code_msg = (const char *)(http_head->buff + http_head->buff_len);
+				http_head->buff_len += frame_len;
+			} else {
+				http_head->data = data + offset;
+				http_head->data_len = frame_len;
+			}
+		} else {
+			return -2;
+		}
+		offset += frame_len;
+	}
+
+	http_head->version = "HTTP/3.0";
+
+	return offset;
+}
+
+static int _http_head_serialize_http3_0(struct http_head *http_head, uint8_t *buffer, int buffer_len)
+{
+	int offset = 0;
+	int offset_ret = 0;
+	uint8_t header_data[1024];
+	int header_data_len = 0;
+
+	/* serialize header frame. */
+	header_data_len = _http3_build_headers_payload(http_head, header_data, sizeof(header_data));
+	if (header_data_len < 0) {
+		return -1;
+	}
+
+	/* Frame Type: Header*/
+	offset_ret = _quicvarint_encode(HTTP3_HEADER_FRAME, buffer + offset, buffer_len - offset);
+	if (offset_ret < 0) {
+		return -1;
+	}
+	offset += offset_ret;
+
+	/* Header Frmae Length */
+	offset_ret = _quicvarint_encode(header_data_len, buffer + offset, buffer_len - offset);
+	if (offset_ret < 0) {
+		return -1;
+	}
+	offset += offset_ret;
+
+	if (buffer_len - offset < header_data_len) {
+		return -1;
+	}
+	memcpy(buffer + offset, header_data, header_data_len);
+	offset += header_data_len;
+
+	/* Frame Type: Data */
+	if (http_head->data_len > 0 && http_head->data) {
+		/* Data Frame Length */
+		offset_ret = _quicvarint_encode(HTTP3_DATA_FRAME, buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+
+		offset_ret =
+			http3_build_body_payload(http_head->data, http_head->data_len, buffer + offset, buffer_len - offset);
+		if (offset_ret < 0) {
+			return -1;
+		}
+		offset += offset_ret;
+	}
+
+	return offset;
+}
+
+int http_head_parse(struct http_head *http_head, const unsigned char *data, int data_len)
+{
+	if (http_head->http_version == HTTP_VERSION_1_1) {
+		return _http_head_parse_http1_1(http_head, data, data_len);
+	} else if (http_head->http_version == HTTP_VERSION_2_0) {
+		return _http_head_parse_http2_0(http_head, data, data_len);
+	} else if (http_head->http_version == HTTP_VERSION_3_0) {
+		return _http_head_parse_http3_0(http_head, data, data_len);
+	}
+
+	return -2;
+}
+
+int http_head_serialize(struct http_head *http_head, void *buffer, int buffer_len)
+{
+	if (http_head == NULL || buffer == NULL || buffer_len <= 0) {
+		return -1;
+	}
+
+	if (http_head->http_version == HTTP_VERSION_1_1) {
+		return _http_head_serialize_http1_1(http_head, buffer, buffer_len);
+	} else if (http_head->http_version == HTTP_VERSION_2_0) {
+		return _http_head_serialize_http2_0(http_head, buffer, buffer_len);
+	} else if (http_head->http_version == HTTP_VERSION_3_0) {
+		return _http_head_serialize_http3_0(http_head, buffer, buffer_len);
+	}
+
+	return -2;
+}
+
 void http_head_destroy(struct http_head *http_head)
 {
 	struct http_head_fields *fields, *tmp;

+ 31 - 4
src/http_parse.h

@@ -27,6 +27,13 @@ struct http_head;
 struct http_head_fields;
 struct http_params;
 
+typedef enum HTTP_VERSION {
+	HTTP_VERSION_INVALID = 0,
+	HTTP_VERSION_1_1,
+	HTTP_VERSION_2_0,
+	HTTP_VERSION_3_0,
+} HTTP_VERSION;
+
 typedef enum HTTP_METHOD {
 	HTTP_METHOD_INVALID = 0,
 	HTTP_METHOD_GET,
@@ -44,7 +51,25 @@ typedef enum HTTP_HEAD_TYPE {
 	HTTP_HEAD_RESPONSE = 2,
 } HTTP_HEAD_TYPE;
 
-struct http_head *http_head_init(int buffsize);
+struct http_head *http_head_init(int buffsize, HTTP_VERSION version);
+
+const char *http_method_str(HTTP_METHOD method);
+
+int http_head_add_fields(struct http_head *http_head, const char *name, const char *value);
+
+int http_head_add_param(struct http_head *http_head, const char *name, const char *value);
+
+int http_head_set_url(struct http_head *http_head, const char *url);
+
+int http_head_set_httpversion(struct http_head *http_head, const char *version);
+
+int http_head_set_httpcode(struct http_head *http_head, int code, const char *msg);
+
+int http_head_set_head_type(struct http_head *http_head, HTTP_HEAD_TYPE head_type);
+
+int http_head_set_method(struct http_head *http_head, HTTP_METHOD method);
+
+int http_head_set_data(struct http_head *http_head, const void *data, int len);
 
 HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head);
 
@@ -56,9 +81,9 @@ const char *http_head_get_httpversion(struct http_head *http_head);
 
 int http_head_get_httpcode(struct http_head *http_head);
 
-char *http_head_get_httpcode_msg(struct http_head *http_head);
+const char *http_head_get_httpcode_msg(struct http_head *http_head);
 
-char *http_head_get_data(struct http_head *http_head);
+const unsigned char *http_head_get_data(struct http_head *http_head);
 
 int http_head_get_data_len(struct http_head *http_head);
 
@@ -83,7 +108,9 @@ int http_head_lookup_fields(struct http_head_fields *fields, const char **name,
  *  -2   - parse failed
  *  -3   - buffer is small
  */
-int http_head_parse(struct http_head *http_head, const char *data, int data_len);
+int http_head_parse(struct http_head *http_head, const unsigned char *data, int data_len);
+
+int http_head_serialize(struct http_head *http_head, void *buffer, int buffer_len);
 
 void http_head_destroy(struct http_head *http_head);
 

+ 43 - 0
src/include/qpack.h

@@ -0,0 +1,43 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2025 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * smartdns is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * smartdns is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _QPACK_H
+#define _QPACK_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qpack_header_field {
+	const char *name;
+	const char *value;
+};
+
+struct qpack_header_field *qpack_get_static_header_field(int index);
+
+int qpack_huffman_decode(const uint8_t *bytes, const uint8_t *bytes_max, uint8_t *decoded, size_t max_decoded,
+						 size_t *nb_decoded);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // !_QPACK_H

+ 709 - 0
src/lib/qpack.c

@@ -0,0 +1,709 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2025 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * smartdns is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * smartdns is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qpack.h"
+
+static const uint8_t _qpack_huffman_bit[64] = {
+	249, 50,  115, 39,  38,  79, 147, 39, 38,  100, 249, 50,  114, 100, 242, 100, 228, 228, 206, 77,  52, 228,
+	204, 203, 202, 114, 102, 79, 147, 39, 76,  156, 153, 62,  76,  156, 156, 153, 62,  76,  156, 153, 60, 154,
+	100, 242, 102, 79,  147, 39, 38,  83, 228, 201, 201, 147, 211, 39,  38,  79,  38,  78,  76,  178};
+
+static const uint8_t _qpack_huffman_val[512] = {
+	/* 0: |  X: 44 */ 44,
+	/* 1: |0  X: 17 */ 16,
+	/* 2: |00  X: 10 */ 8,
+	/* 3: |000  X: 7 */ 4,
+	/* 4: |0000  X: 6 */ 2,
+	/* 5: |00000  V: 48 */ 48,
+	/* 6: |00001  V: 49 */ 49,
+	/* 7: |0001  X: 9 */ 2,
+	/* 8: |00010  V: 50 */ 50,
+	/* 9: |00011  V: 97 */ 97,
+	/* 10: |001  X: 14 */ 4,
+	/* 11: |0010  X: 13 */ 2,
+	/* 12: |00100  V: 99 */ 99,
+	/* 13: |00101  V: 101 */ 101,
+	/* 14: |0011  X: 16 */ 2,
+	/* 15: |00110  V: 105 */ 105,
+	/* 16: |00111  V: 111 */ 111,
+	/* 17: |01  X: 29 */ 12,
+	/* 18: |010  X: 22 */ 4,
+	/* 19: |0100  X: 21 */ 2,
+	/* 20: |01000  V: 115 */ 115,
+	/* 21: |01001  V: 116 */ 116,
+	/* 22: |0101  X: 26 */ 4,
+	/* 23: |01010  X: 25 */ 2,
+	/* 24: |010100  V: 32 */ 32,
+	/* 25: |010101  V: 37 */ 37,
+	/* 26: |01011  X: 28 */ 2,
+	/* 27: |010110  V: 45 */ 45,
+	/* 28: |010111  V: 46 */ 46,
+	/* 29: |011  X: 37 */ 8,
+	/* 30: |0110  X: 34 */ 4,
+	/* 31: |01100  X: 33 */ 2,
+	/* 32: |011000  V: 47 */ 47,
+	/* 33: |011001  V: 51 */ 51,
+	/* 34: |01101  X: 36 */ 2,
+	/* 35: |011010  V: 52 */ 52,
+	/* 36: |011011  V: 53 */ 53,
+	/* 37: |0111  X: 41 */ 4,
+	/* 38: |01110  X: 40 */ 2,
+	/* 39: |011100  V: 54 */ 54,
+	/* 40: |011101  V: 55 */ 55,
+	/* 41: |01111  X: 43 */ 2,
+	/* 42: |011110  V: 56 */ 56,
+	/* 43: |011111  V: 57 */ 57,
+	/* 44: |1  X: 80 */ 36,
+	/* 45: |10  X: 61 */ 16,
+	/* 46: |100  X: 54 */ 8,
+	/* 47: |1000  X: 51 */ 4,
+	/* 48: |10000  X: 50 */ 2,
+	/* 49: |100000  V: 61 */ 61,
+	/* 50: |100001  V: 65 */ 65,
+	/* 51: |10001  X: 53 */ 2,
+	/* 52: |100010  V: 95 */ 95,
+	/* 53: |100011  V: 98 */ 98,
+	/* 54: |1001  X: 58 */ 4,
+	/* 55: |10010  X: 57 */ 2,
+	/* 56: |100100  V: 100 */ 100,
+	/* 57: |100101  V: 102 */ 102,
+	/* 58: |10011  X: 60 */ 2,
+	/* 59: |100110  V: 103 */ 103,
+	/* 60: |100111  V: 104 */ 104,
+	/* 61: |101  X: 69 */ 8,
+	/* 62: |1010  X: 66 */ 4,
+	/* 63: |10100  X: 65 */ 2,
+	/* 64: |101000  V: 108 */ 108,
+	/* 65: |101001  V: 109 */ 109,
+	/* 66: |10101  X: 68 */ 2,
+	/* 67: |101010  V: 110 */ 110,
+	/* 68: |101011  V: 112 */ 112,
+	/* 69: |1011  X: 73 */ 4,
+	/* 70: |10110  X: 72 */ 2,
+	/* 71: |101100  V: 114 */ 114,
+	/* 72: |101101  V: 117 */ 117,
+	/* 73: |10111  X: 77 */ 4,
+	/* 74: |101110  X: 76 */ 2,
+	/* 75: |1011100  V: 58 */ 58,
+	/* 76: |1011101  V: 66 */ 66,
+	/* 77: |101111  X: 79 */ 2,
+	/* 78: |1011110  V: 67 */ 67,
+	/* 79: |1011111  V: 68 */ 68,
+	/* 80: |11  X: 112 */ 32,
+	/* 81: |110  X: 97 */ 16,
+	/* 82: |1100  X: 90 */ 8,
+	/* 83: |11000  X: 87 */ 4,
+	/* 84: |110000  X: 86 */ 2,
+	/* 85: |1100000  V: 69 */ 69,
+	/* 86: |1100001  V: 70 */ 70,
+	/* 87: |110001  X: 89 */ 2,
+	/* 88: |1100010  V: 71 */ 71,
+	/* 89: |1100011  V: 72 */ 72,
+	/* 90: |11001  X: 94 */ 4,
+	/* 91: |110010  X: 93 */ 2,
+	/* 92: |1100100  V: 73 */ 73,
+	/* 93: |1100101  V: 74 */ 74,
+	/* 94: |110011  X: 96 */ 2,
+	/* 95: |1100110  V: 75 */ 75,
+	/* 96: |1100111  V: 76 */ 76,
+	/* 97: |1101  X: 105 */ 8,
+	/* 98: |11010  X: 102 */ 4,
+	/* 99: |110100  X: 101 */ 2,
+	/* 100: |1101000  V: 77 */ 77,
+	/* 101: |1101001  V: 78 */ 78,
+	/* 102: |110101  X: 104 */ 2,
+	/* 103: |1101010  V: 79 */ 79,
+	/* 104: |1101011  V: 80 */ 80,
+	/* 105: |11011  X: 109 */ 4,
+	/* 106: |110110  X: 108 */ 2,
+	/* 107: |1101100  V: 81 */ 81,
+	/* 108: |1101101  V: 82 */ 82,
+	/* 109: |110111  X: 111 */ 2,
+	/* 110: |1101110  V: 83 */ 83,
+	/* 111: |1101111  V: 84 */ 84,
+	/* 112: |111  X: 128 */ 16,
+	/* 113: |1110  X: 121 */ 8,
+	/* 114: |11100  X: 118 */ 4,
+	/* 115: |111000  X: 117 */ 2,
+	/* 116: |1110000  V: 85 */ 85,
+	/* 117: |1110001  V: 86 */ 86,
+	/* 118: |111001  X: 120 */ 2,
+	/* 119: |1110010  V: 87 */ 87,
+	/* 120: |1110011  V: 89 */ 89,
+	/* 121: |11101  X: 125 */ 4,
+	/* 122: |111010  X: 124 */ 2,
+	/* 123: |1110100  V: 106 */ 106,
+	/* 124: |1110101  V: 107 */ 107,
+	/* 125: |111011  X: 127 */ 2,
+	/* 126: |1110110  V: 113 */ 113,
+	/* 127: |1110111  V: 118 */ 118,
+	/* 128: |1111  X: 136 */ 8,
+	/* 129: |11110  X: 133 */ 4,
+	/* 130: |111100  X: 132 */ 2,
+	/* 131: |1111000  V: 119 */ 119,
+	/* 132: |1111001  V: 120 */ 120,
+	/* 133: |111101  X: 135 */ 2,
+	/* 134: |1111010  V: 121 */ 121,
+	/* 135: |1111011  V: 122 */ 122,
+	/* 136: |11111  X: 144 */ 8,
+	/* 137: |111110  X: 141 */ 4,
+	/* 138: |1111100  X: 140 */ 2,
+	/* 139: |11111000  V: 38 */ 38,
+	/* 140: |11111001  V: 42 */ 42,
+	/* 141: |1111101  X: 143 */ 2,
+	/* 142: |11111010  V: 44 */ 44,
+	/* 143: |11111011  V: 59 */ 59,
+	/* 144: |111111  X: 148 */ 4,
+	/* 145: |1111110  X: 147 */ 2,
+	/* 146: |11111100  V: 88 */ 88,
+	/* 147: |11111101  V: 90 */ 90,
+	/* 148: |1111111  X: 156 */ 8,
+	/* 149: |11111110  X: 153 */ 4,
+	/* 150: |11111110|0  X: 152 */ 2,
+	/* 151: |11111110|00  V: 33 */ 33,
+	/* 152: |11111110|01  V: 34 */ 34,
+	/* 153: |11111110|1  X: 155 */ 2,
+	/* 154: |11111110|10  V: 40 */ 40,
+	/* 155: |11111110|11  V: 41 */ 41,
+	/* 156: |11111111|  X: 162 */ 6,
+	/* 157: |11111111|0  X: 159 */ 2,
+	/* 158: |11111111|00  V: 63 */ 63,
+	/* 159: |11111111|01  X: 161 */ 2,
+	/* 160: |11111111|010  V: 39 */ 39,
+	/* 161: |11111111|011  V: 43 */ 43,
+	/* 162: |11111111|1  X: 168 */ 6,
+	/* 163: |11111111|10  X: 165 */ 2,
+	/* 164: |11111111|100  V: 124 */ 124,
+	/* 165: |11111111|101  X: 167 */ 2,
+	/* 166: |11111111|1010  V: 35 */ 35,
+	/* 167: |11111111|1011  V: 62 */ 62,
+	/* 168: |11111111|11  X: 176 */ 8,
+	/* 169: |11111111|110  X: 173 */ 4,
+	/* 170: |11111111|1100  X: 172 */ 2,
+	/* 171: |11111111|11000  V: 0 */ 0,
+	/* 172: |11111111|11001  V: 36 */ 36,
+	/* 173: |11111111|1101  X: 175 */ 2,
+	/* 174: |11111111|11010  V: 64 */ 64,
+	/* 175: |11111111|11011  V: 91 */ 91,
+	/* 176: |11111111|111  X: 180 */ 4,
+	/* 177: |11111111|1110  X: 179 */ 2,
+	/* 178: |11111111|11100  V: 93 */ 93,
+	/* 179: |11111111|11101  V: 126 */ 126,
+	/* 180: |11111111|1111  X: 184 */ 4,
+	/* 181: |11111111|11110  X: 183 */ 2,
+	/* 182: |11111111|111100  V: 94 */ 94,
+	/* 183: |11111111|111101  V: 125 */ 125,
+	/* 184: |11111111|11111  X: 188 */ 4,
+	/* 185: |11111111|111110  X: 187 */ 2,
+	/* 186: |11111111|1111100  V: 60 */ 60,
+	/* 187: |11111111|1111101  V: 96 */ 96,
+	/* 188: |11111111|111111  X: 190 */ 2,
+	/* 189: |11111111|1111110  V: 123 */ 123,
+	/* 190: |11111111|1111111  X: 220 */ 30,
+	/* 191: |11111111|11111110|  X: 201 */ 10,
+	/* 192: |11111111|11111110|0  X: 196 */ 4,
+	/* 193: |11111111|11111110|00  X: 195 */ 2,
+	/* 194: |11111111|11111110|000  V: 92 */ 92,
+	/* 195: |11111111|11111110|001  V: 195 */ 195,
+	/* 196: |11111111|11111110|01  X: 198 */ 2,
+	/* 197: |11111111|11111110|010  V: 208 */ 208,
+	/* 198: |11111111|11111110|011  X: 200 */ 2,
+	/* 199: |11111111|11111110|0110  V: 128 */ 128,
+	/* 200: |11111111|11111110|0111  V: 130 */ 130,
+	/* 201: |11111111|11111110|1  X: 209 */ 8,
+	/* 202: |11111111|11111110|10  X: 206 */ 4,
+	/* 203: |11111111|11111110|100  X: 205 */ 2,
+	/* 204: |11111111|11111110|1000  V: 131 */ 131,
+	/* 205: |11111111|11111110|1001  V: 162 */ 162,
+	/* 206: |11111111|11111110|101  X: 208 */ 2,
+	/* 207: |11111111|11111110|1010  V: 184 */ 184,
+	/* 208: |11111111|11111110|1011  V: 194 */ 194,
+	/* 209: |11111111|11111110|11  X: 213 */ 4,
+	/* 210: |11111111|11111110|110  X: 212 */ 2,
+	/* 211: |11111111|11111110|1100  V: 224 */ 224,
+	/* 212: |11111111|11111110|1101  V: 226 */ 226,
+	/* 213: |11111111|11111110|111  X: 217 */ 4,
+	/* 214: |11111111|11111110|1110  X: 216 */ 2,
+	/* 215: |11111111|11111110|11100  V: 153 */ 153,
+	/* 216: |11111111|11111110|11101  V: 161 */ 161,
+	/* 217: |11111111|11111110|1111  X: 219 */ 2,
+	/* 218: |11111111|11111110|11110  V: 167 */ 167,
+	/* 219: |11111111|11111110|11111  V: 172 */ 172,
+	/* 220: |11111111|11111111|  X: 266 */ 46,
+	/* 221: |11111111|11111111|0  X: 237 */ 16,
+	/* 222: |11111111|11111111|00  X: 230 */ 8,
+	/* 223: |11111111|11111111|000  X: 227 */ 4,
+	/* 224: |11111111|11111111|0000  X: 226 */ 2,
+	/* 225: |11111111|11111111|00000  V: 176 */ 176,
+	/* 226: |11111111|11111111|00001  V: 177 */ 177,
+	/* 227: |11111111|11111111|0001  X: 229 */ 2,
+	/* 228: |11111111|11111111|00010  V: 179 */ 179,
+	/* 229: |11111111|11111111|00011  V: 209 */ 209,
+	/* 230: |11111111|11111111|001  X: 234 */ 4,
+	/* 231: |11111111|11111111|0010  X: 233 */ 2,
+	/* 232: |11111111|11111111|00100  V: 216 */ 216,
+	/* 233: |11111111|11111111|00101  V: 217 */ 217,
+	/* 234: |11111111|11111111|0011  X: 236 */ 2,
+	/* 235: |11111111|11111111|00110  V: 227 */ 227,
+	/* 236: |11111111|11111111|00111  V: 229 */ 229,
+	/* 237: |11111111|11111111|01  X: 251 */ 14,
+	/* 238: |11111111|11111111|010  X: 244 */ 6,
+	/* 239: |11111111|11111111|0100  X: 241 */ 2,
+	/* 240: |11111111|11111111|01000  V: 230 */ 230,
+	/* 241: |11111111|11111111|01001  X: 243 */ 2,
+	/* 242: |11111111|11111111|010010  V: 129 */ 129,
+	/* 243: |11111111|11111111|010011  V: 132 */ 132,
+	/* 244: |11111111|11111111|0101  X: 248 */ 4,
+	/* 245: |11111111|11111111|01010  X: 247 */ 2,
+	/* 246: |11111111|11111111|010100  V: 133 */ 133,
+	/* 247: |11111111|11111111|010101  V: 134 */ 134,
+	/* 248: |11111111|11111111|01011  X: 250 */ 2,
+	/* 249: |11111111|11111111|010110  V: 136 */ 136,
+	/* 250: |11111111|11111111|010111  V: 146 */ 146,
+	/* 251: |11111111|11111111|011  X: 259 */ 8,
+	/* 252: |11111111|11111111|0110  X: 256 */ 4,
+	/* 253: |11111111|11111111|01100  X: 255 */ 2,
+	/* 254: |11111111|11111111|011000  V: 154 */ 154,
+	/* 255: |11111111|11111111|011001  V: 156 */ 156,
+	/* 256: |11111111|11111111|01101  X: 257 */ 2,
+	/* 257: |11111111|11111111|011010  V: 160 */ 160,
+	/* 258: |11111111|11111111|011011  V: 163 */ 163,
+	/* 259: |11111111|11111111|0111  X: 263 */ 4,
+	/* 260: |11111111|11111111|01110  X: 262 */ 2,
+	/* 261: |11111111|11111111|011100  V: 164 */ 164,
+	/* 262: |11111111|11111111|011101  V: 169 */ 169,
+	/* 263: |11111111|11111111|01111  X: 265 */ 2,
+	/* 264: |11111111|11111111|011110  V: 170 */ 170,
+	/* 265: |11111111|11111111|011111  V: 173 */ 173,
+	/* 266: |11111111|11111111|1  X: 306 */ 40,
+	/* 267: |11111111|11111111|10  X: 283 */ 16,
+	/* 268: |11111111|11111111|100  X: 276 */ 8,
+	/* 269: |11111111|11111111|1000  X: 273 */ 4,
+	/* 270: |11111111|11111111|10000  X: 272 */ 2,
+	/* 271: |11111111|11111111|100000  V: 178 */ 178,
+	/* 272: |11111111|11111111|100001  V: 181 */ 181,
+	/* 273: |11111111|11111111|10001  X: 275 */ 2,
+	/* 274: |11111111|11111111|100010  V: 185 */ 185,
+	/* 275: |11111111|11111111|100011  V: 186 */ 186,
+	/* 276: |11111111|11111111|1001  X: 280 */ 4,
+	/* 277: |11111111|11111111|10010  X: 279 */ 2,
+	/* 278: |11111111|11111111|100100  V: 187 */ 187,
+	/* 279: |11111111|11111111|100101  V: 189 */ 189,
+	/* 280: |11111111|11111111|10011  X: 282 */ 2,
+	/* 281: |11111111|11111111|100110  V: 190 */ 190,
+	/* 282: |11111111|11111111|100111  V: 196 */ 196,
+	/* 283: |11111111|11111111|101  X: 291 */ 8,
+	/* 284: |11111111|11111111|1010  X: 288 */ 4,
+	/* 285: |11111111|11111111|10100  X: 287 */ 2,
+	/* 286: |11111111|11111111|101000  V: 198 */ 198,
+	/* 287: |11111111|11111111|101001  V: 228 */ 228,
+	/* 288: |11111111|11111111|10101  X: 290 */ 2,
+	/* 289: |11111111|11111111|101010  V: 232 */ 232,
+	/* 290: |11111111|11111111|101011  V: 233 */ 233,
+	/* 291: |11111111|11111111|1011  X: 299 */ 8,
+	/* 292: |11111111|11111111|10110  X: 296 */ 4,
+	/* 293: |11111111|11111111|101100  X: 295 */ 2,
+	/* 294: |11111111|11111111|1011000  V: 1 */ 1,
+	/* 295: |11111111|11111111|1011001  V: 135 */ 135,
+	/* 296: |11111111|11111111|101101  X: 298 */ 2,
+	/* 297: |11111111|11111111|1011010  V: 137 */ 137,
+	/* 298: |11111111|11111111|1011011  V: 138 */ 138,
+	/* 299: |11111111|11111111|10111  X: 303 */ 4,
+	/* 300: |11111111|11111111|101110  X: 302 */ 2,
+	/* 301: |11111111|11111111|1011100  V: 139 */ 139,
+	/* 302: |11111111|11111111|1011101  V: 140 */ 140,
+	/* 303: |11111111|11111111|101111  X: 305 */ 2,
+	/* 304: |11111111|11111111|1011110  V: 141 */ 141,
+	/* 305: |11111111|11111111|1011111  V: 143 */ 143,
+	/* 306: |11111111|11111111|11  X: 338 */ 32,
+	/* 307: |11111111|11111111|110  X: 323 */ 16,
+	/* 308: |11111111|11111111|1100  X: 316 */ 8,
+	/* 309: |11111111|11111111|11000  X: 313 */ 4,
+	/* 310: |11111111|11111111|110000  X: 312 */ 2,
+	/* 311: |11111111|11111111|1100000  V: 147 */ 147,
+	/* 312: |11111111|11111111|1100001  V: 149 */ 149,
+	/* 313: |11111111|11111111|110001  X: 315 */ 2,
+	/* 314: |11111111|11111111|1100010  V: 150 */ 150,
+	/* 315: |11111111|11111111|1100011  V: 151 */ 151,
+	/* 316: |11111111|11111111|11001  X: 320 */ 4,
+	/* 317: |11111111|11111111|110010  X: 319 */ 2,
+	/* 318: |11111111|11111111|1100100  V: 152 */ 152,
+	/* 319: |11111111|11111111|1100101  V: 155 */ 155,
+	/* 320: |11111111|11111111|110011  X: 322 */ 2,
+	/* 321: |11111111|11111111|1100110  V: 157 */ 157,
+	/* 322: |11111111|11111111|1100111  V: 158 */ 158,
+	/* 323: |11111111|11111111|1101  X: 331 */ 8,
+	/* 324: |11111111|11111111|11010  X: 328 */ 4,
+	/* 325: |11111111|11111111|110100  X: 327 */ 2,
+	/* 326: |11111111|11111111|1101000  V: 165 */ 165,
+	/* 327: |11111111|11111111|1101001  V: 166 */ 166,
+	/* 328: |11111111|11111111|110101  X: 330 */ 2,
+	/* 329: |11111111|11111111|1101010  V: 168 */ 168,
+	/* 330: |11111111|11111111|1101011  V: 174 */ 174,
+	/* 331: |11111111|11111111|11011  X: 335 */ 4,
+	/* 332: |11111111|11111111|110110  X: 334 */ 2,
+	/* 333: |11111111|11111111|1101100  V: 175 */ 175,
+	/* 334: |11111111|11111111|1101101  V: 180 */ 180,
+	/* 335: |11111111|11111111|110111  X: 337 */ 2,
+	/* 336: |11111111|11111111|1101110  V: 182 */ 182,
+	/* 337: |11111111|11111111|1101111  V: 183 */ 183,
+	/* 338: |11111111|11111111|111  X: 360 */ 22,
+	/* 339: |11111111|11111111|1110  X: 347 */ 8,
+	/* 340: |11111111|11111111|11100  X: 344 */ 4,
+	/* 341: |11111111|11111111|111000  X: 343 */ 2,
+	/* 342: |11111111|11111111|1110000  V: 188 */ 188,
+	/* 343: |11111111|11111111|1110001  V: 191 */ 191,
+	/* 344: |11111111|11111111|111001  X: 346 */ 2,
+	/* 345: |11111111|11111111|1110010  V: 197 */ 197,
+	/* 346: |11111111|11111111|1110011  V: 231 */ 231,
+	/* 347: |11111111|11111111|11101  X: 353 */ 6,
+	/* 348: |11111111|11111111|111010  X: 350 */ 2,
+	/* 349: |11111111|11111111|1110100  V: 239 */ 239,
+	/* 350: |11111111|11111111|1110101  X: 352 */ 2,
+	/* 351: |11111111|11111111|11101010  V: 9 */ 9,
+	/* 352: |11111111|11111111|11101011  V: 142 */ 142,
+	/* 353: |11111111|11111111|111011  X: 357 */ 4,
+	/* 354: |11111111|11111111|1110110  X: 356 */ 2,
+	/* 355: |11111111|11111111|11101100  V: 144 */ 144,
+	/* 356: |11111111|11111111|11101101  V: 145 */ 145,
+	/* 357: |11111111|11111111|1110111  X: 359 */ 2,
+	/* 358: |11111111|11111111|11101110  V: 148 */ 148,
+	/* 359: |11111111|11111111|11101111  V: 159 */ 159,
+	/* 360: |11111111|11111111|1111  X: 380 */ 20,
+	/* 361: |11111111|11111111|11110  X: 369 */ 8,
+	/* 362: |11111111|11111111|111100  X: 366 */ 4,
+	/* 363: |11111111|11111111|1111000  X: 365 */ 2,
+	/* 364: |11111111|11111111|11110000  V: 171 */ 171,
+	/* 365: |11111111|11111111|11110001  V: 206 */ 206,
+	/* 366: |11111111|11111111|1111001  X: 368 */ 2,
+	/* 367: |11111111|11111111|11110010  V: 215 */ 215,
+	/* 368: |11111111|11111111|11110011  V: 225 */ 225,
+	/* 369: |11111111|11111111|111101  X: 373 */ 4,
+	/* 370: |11111111|11111111|1111010  X: 372 */ 2,
+	/* 371: |11111111|11111111|11110100  V: 236 */ 236,
+	/* 372: |11111111|11111111|11110101  V: 237 */ 237,
+	/* 373: |11111111|11111111|1111011  X: 377 */ 4,
+	/* 374: |11111111|11111111|11110110|  X: 376 */ 2,
+	/* 375: |11111111|11111111|11110110|0  V: 199 */ 199,
+	/* 376: |11111111|11111111|11110110|1  V: 207 */ 207,
+	/* 377: |11111111|11111111|11110111|  X: 379 */ 2,
+	/* 378: |11111111|11111111|11110111|0  V: 234 */ 234,
+	/* 379: |11111111|11111111|11110111|1  V: 235 */ 235,
+	/* 380: |11111111|11111111|11111  X: 414 */ 34,
+	/* 381: |11111111|11111111|111110  X: 397 */ 16,
+	/* 382: |11111111|11111111|1111100  X: 390 */ 8,
+	/* 383: |11111111|11111111|11111000|  X: 387 */ 4,
+	/* 384: |11111111|11111111|11111000|0  X: 386 */ 2,
+	/* 385: |11111111|11111111|11111000|00  V: 192 */ 192,
+	/* 386: |11111111|11111111|11111000|01  V: 193 */ 193,
+	/* 387: |11111111|11111111|11111000|1  X: 389 */ 2,
+	/* 388: |11111111|11111111|11111000|10  V: 200 */ 200,
+	/* 389: |11111111|11111111|11111000|11  V: 201 */ 201,
+	/* 390: |11111111|11111111|11111001|  X: 394 */ 4,
+	/* 391: |11111111|11111111|11111001|0  X: 393 */ 2,
+	/* 392: |11111111|11111111|11111001|00  V: 202 */ 202,
+	/* 393: |11111111|11111111|11111001|01  V: 205 */ 205,
+	/* 394: |11111111|11111111|11111001|1  X: 396 */ 2,
+	/* 395: |11111111|11111111|11111001|10  V: 210 */ 210,
+	/* 396: |11111111|11111111|11111001|11  V: 213 */ 213,
+	/* 397: |11111111|11111111|1111101  X: 405 */ 8,
+	/* 398: |11111111|11111111|11111010|  X: 402 */ 4,
+	/* 399: |11111111|11111111|11111010|0  X: 401 */ 2,
+	/* 400: |11111111|11111111|11111010|00  V: 218 */ 218,
+	/* 401: |11111111|11111111|11111010|01  V: 219 */ 219,
+	/* 402: |11111111|11111111|11111010|1  X: 404 */ 2,
+	/* 403: |11111111|11111111|11111010|10  V: 238 */ 238,
+	/* 404: |11111111|11111111|11111010|11  V: 240 */ 240,
+	/* 405: |11111111|11111111|11111011|  X: 409 */ 4,
+	/* 406: |11111111|11111111|11111011|0  X: 408 */ 2,
+	/* 407: |11111111|11111111|11111011|00  V: 242 */ 242,
+	/* 408: |11111111|11111111|11111011|01  V: 243 */ 243,
+	/* 409: |11111111|11111111|11111011|1  X: 411 */ 2,
+	/* 410: |11111111|11111111|11111011|10  V: 255 */ 255,
+	/* 411: |11111111|11111111|11111011|11  X: 413 */ 2,
+	/* 412: |11111111|11111111|11111011|110  V: 203 */ 203,
+	/* 413: |11111111|11111111|11111011|111  V: 204 */ 204,
+	/* 414: |11111111|11111111|111111  X: 446 */ 32,
+	/* 415: |11111111|11111111|1111110  X: 431 */ 16,
+	/* 416: |11111111|11111111|11111100|  X: 424 */ 8,
+	/* 417: |11111111|11111111|11111100|0  X: 421 */ 4,
+	/* 418: |11111111|11111111|11111100|00  X: 420 */ 2,
+	/* 419: |11111111|11111111|11111100|000  V: 211 */ 211,
+	/* 420: |11111111|11111111|11111100|001  V: 212 */ 212,
+	/* 421: |11111111|11111111|11111100|01  X: 423 */ 2,
+	/* 422: |11111111|11111111|11111100|010  V: 214 */ 214,
+	/* 423: |11111111|11111111|11111100|011  V: 221 */ 221,
+	/* 424: |11111111|11111111|11111100|1  X: 428 */ 4,
+	/* 425: |11111111|11111111|11111100|10  X: 427 */ 2,
+	/* 426: |11111111|11111111|11111100|100  V: 222 */ 222,
+	/* 427: |11111111|11111111|11111100|101  V: 223 */ 223,
+	/* 428: |11111111|11111111|11111100|11  X: 430 */ 2,
+	/* 429: |11111111|11111111|11111100|110  V: 241 */ 241,
+	/* 430: |11111111|11111111|11111100|111  V: 244 */ 244,
+	/* 431: |11111111|11111111|11111101|  X: 439 */ 8,
+	/* 432: |11111111|11111111|11111101|0  X: 436 */ 4,
+	/* 433: |11111111|11111111|11111101|00  X: 435 */ 2,
+	/* 434: |11111111|11111111|11111101|000  V: 245 */ 245,
+	/* 435: |11111111|11111111|11111101|001  V: 246 */ 246,
+	/* 436: |11111111|11111111|11111101|01  X: 438 */ 2,
+	/* 437: |11111111|11111111|11111101|010  V: 247 */ 247,
+	/* 438: |11111111|11111111|11111101|011  V: 248 */ 248,
+	/* 439: |11111111|11111111|11111101|1  X: 443 */ 4,
+	/* 440: |11111111|11111111|11111101|10  X: 442 */ 2,
+	/* 441: |11111111|11111111|11111101|100  V: 250 */ 250,
+	/* 442: |11111111|11111111|11111101|101  V: 251 */ 251,
+	/* 443: |11111111|11111111|11111101|11  X: 445 */ 2,
+	/* 444: |11111111|11111111|11111101|110  V: 252 */ 252,
+	/* 445: |11111111|11111111|11111101|111  V: 253 */ 253,
+	/* 446: |11111111|11111111|1111111  X: 476 */ 30,
+	/* 447: |11111111|11111111|11111110|  X: 461 */ 14,
+	/* 448: |11111111|11111111|11111110|0  X: 454 */ 6,
+	/* 449: |11111111|11111111|11111110|00  X: 451 */ 2,
+	/* 450: |11111111|11111111|11111110|000  V: 254 */ 254,
+	/* 451: |11111111|11111111|11111110|001  X: 453 */ 2,
+	/* 452: |11111111|11111111|11111110|0010  V: 2 */ 2,
+	/* 453: |11111111|11111111|11111110|0011  V: 3 */ 3,
+	/* 454: |11111111|11111111|11111110|01  X: 458 */ 4,
+	/* 455: |11111111|11111111|11111110|010  X: 457 */ 2,
+	/* 456: |11111111|11111111|11111110|0100  V: 4 */ 4,
+	/* 457: |11111111|11111111|11111110|0101  V: 5 */ 5,
+	/* 458: |11111111|11111111|11111110|011  X: 460 */ 2,
+	/* 459: |11111111|11111111|11111110|0110  V: 6 */ 6,
+	/* 460: |11111111|11111111|11111110|0111  V: 7 */ 7,
+	/* 461: |11111111|11111111|11111110|1  X: 469 */ 8,
+	/* 462: |11111111|11111111|11111110|10  X: 466 */ 4,
+	/* 463: |11111111|11111111|11111110|100  X: 465 */ 2,
+	/* 464: |11111111|11111111|11111110|1000  V: 8 */ 8,
+	/* 465: |11111111|11111111|11111110|1001  V: 11 */ 11,
+	/* 466: |11111111|11111111|11111110|101  X: 468 */ 2,
+	/* 467: |11111111|11111111|11111110|1010  V: 12 */ 12,
+	/* 468: |11111111|11111111|11111110|1011  V: 14 */ 14,
+	/* 469: |11111111|11111111|11111110|11  X: 473 */ 4,
+	/* 470: |11111111|11111111|11111110|110  X: 472 */ 2,
+	/* 471: |11111111|11111111|11111110|1100  V: 15 */ 15,
+	/* 472: |11111111|11111111|11111110|1101  V: 16 */ 16,
+	/* 473: |11111111|11111111|11111110|111  X: 475 */ 2,
+	/* 474: |11111111|11111111|11111110|1110  V: 17 */ 17,
+	/* 475: |11111111|11111111|11111110|1111  V: 18 */ 18,
+	/* 476: |11111111|11111111|11111111|  X: 492 */ 16,
+	/* 477: |11111111|11111111|11111111|0  X: 485 */ 8,
+	/* 478: |11111111|11111111|11111111|00  X: 482 */ 4,
+	/* 479: |11111111|11111111|11111111|000  X: 481 */ 2,
+	/* 480: |11111111|11111111|11111111|0000  V: 19 */ 19,
+	/* 481: |11111111|11111111|11111111|0001  V: 20 */ 20,
+	/* 482: |11111111|11111111|11111111|001  X: 484 */ 2,
+	/* 483: |11111111|11111111|11111111|0010  V: 21 */ 21,
+	/* 484: |11111111|11111111|11111111|0011  V: 23 */ 23,
+	/* 485: |11111111|11111111|11111111|01  X: 489 */ 4,
+	/* 486: |11111111|11111111|11111111|010  X: 488 */ 2,
+	/* 487: |11111111|11111111|11111111|0100  V: 24 */ 24,
+	/* 488: |11111111|11111111|11111111|0101  V: 25 */ 25,
+	/* 489: |11111111|11111111|11111111|011  X: 491 */ 2,
+	/* 490: |11111111|11111111|11111111|0110  V: 26 */ 26,
+	/* 491: |11111111|11111111|11111111|0111  V: 27 */ 27,
+	/* 492: |11111111|11111111|11111111|1  X: 500 */ 8,
+	/* 493: |11111111|11111111|11111111|10  X: 497 */ 4,
+	/* 494: |11111111|11111111|11111111|100  X: 496 */ 2,
+	/* 495: |11111111|11111111|11111111|1000  V: 28 */ 28,
+	/* 496: |11111111|11111111|11111111|1001  V: 29 */ 29,
+	/* 497: |11111111|11111111|11111111|101  X: 499 */ 2,
+	/* 498: |11111111|11111111|11111111|1010  V: 30 */ 30,
+	/* 499: |11111111|11111111|11111111|1011  V: 31 */ 31,
+	/* 500: |11111111|11111111|11111111|11  X: 504 */ 4,
+	/* 501: |11111111|11111111|11111111|110  X: 503 */ 2,
+	/* 502: |11111111|11111111|11111111|1100  V: 127 */ 127,
+	/* 503: |11111111|11111111|11111111|1101  V: 220 */ 220,
+	/* 504: |11111111|11111111|11111111|111  X: 506 */ 2,
+	/* 505: |11111111|11111111|11111111|1110  V: 249 */ 249,
+	/* 506: |11111111|11111111|11111111|1111  X: 510 */ 4,
+	/* 507: |11111111|11111111|11111111|11110  X: 509 */ 2,
+	/* 508: |11111111|11111111|11111111|111100  V: 10 */ 10,
+	/* 509: |11111111|11111111|11111111|111101  V: 13 */ 13,
+	/* 510: |11111111|11111111|11111111|11111  X: 512 */ 2,
+	/* 511: |11111111|11111111|11111111|111110  V: 22 */ 22};
+
+int qpack_huffman_decode(const uint8_t *bytes, const uint8_t *bytes_max, uint8_t *decoded, size_t max_decoded,
+						 size_t *nb_decoded)
+{
+	int ret = 0;
+	uint64_t val_in = 0;
+	int bits_in = 0;
+	size_t decoded_index = 0;
+	int index = 0;
+	int was_all_ones = 1;
+
+	while (1) {
+		int bit;
+		int index_64 = index >> 3;
+		int b_index = 7 - (index & 7);
+
+		while (bits_in < 57 && bytes < bytes_max) {
+			uint64_t added = *bytes++;
+			int shift = 64 - bits_in - 8;
+			added <<= shift;
+			val_in |= added;
+			bits_in += 8;
+		}
+
+		if ((_qpack_huffman_bit[index_64] >> b_index) & 1) {
+			if (bits_in <= 0) {
+				break;
+			}
+			bit = (val_in >> 63) & 1;
+			val_in <<= 1;
+			bits_in--;
+			if (bit) {
+				index += _qpack_huffman_val[index];
+			} else {
+				index++;
+				was_all_ones = 0;
+			}
+			if (index >= 512) {
+				break;
+			}
+		} else if (decoded_index < max_decoded) {
+			decoded[decoded_index++] = _qpack_huffman_val[index];
+			index = 0;
+			was_all_ones = 1;
+		} else {
+			was_all_ones = 1;
+			break;
+		}
+	}
+
+	if (!was_all_ones) {
+		ret = -1;
+	}
+
+	*nb_decoded = decoded_index;
+
+	return ret;
+}
+
+/* clang-format off */
+static struct qpack_header_field _http3_static_header_table[] = {
+    {.name = ":authority", .value = ""},
+    {.name = ":path", .value = "/"},
+    {.name = "age", .value = "0"},
+    {.name = "content-disposition", .value = ""},
+    {.name = "content-length", .value = "0"},
+    {.name = "cookie", .value = ""},
+    {.name = "date", .value = ""},
+    {.name = "etag", .value = ""},
+    {.name = "if-modified-since", .value = ""},
+    {.name = "if-none-match", .value = ""},
+    {.name = "last-modified", .value = ""},
+    {.name = "link", .value = ""},
+    {.name = "location", .value = ""},
+    {.name = "referer", .value = ""},
+    {.name = "set-cookie", .value = ""},
+    {.name = ":method", .value = "CONNECT"},
+    {.name = ":method", .value = "DELETE"},
+    {.name = ":method", .value = "GET"},
+    {.name = ":method", .value = "HEAD"},
+    {.name = ":method", .value = "OPTIONS"},
+    {.name = ":method", .value = "POST"},
+    {.name = ":method", .value = "PUT"},
+    {.name = ":scheme", .value = "http"},
+    {.name = ":scheme", .value = "https"},
+    {.name = ":status", .value = "103"},
+    {.name = ":status", .value = "200"},
+    {.name = ":status", .value = "304"},
+    {.name = ":status", .value = "404"},
+    {.name = ":status", .value = "503"},
+    {.name = "accept", .value = "*/*"},
+    {.name = "accept", .value = "application/dns-message"},
+    {.name = "accept-encoding", .value = "gzip, .value = deflate, .value = br"},
+    {.name = "accept-ranges", .value = "bytes"},
+    {.name = "access-control-allow-headers", .value = "cache-control"},
+    {.name = "access-control-allow-headers", .value = "content-type"},
+    {.name = "access-control-allow-origin", .value = "*"},
+    {.name = "cache-control", .value = "max-age=0"},
+    {.name = "cache-control", .value = "max-age=2592000"},
+    {.name = "cache-control", .value = "max-age=604800"},
+    {.name = "cache-control", .value = "no-cache"},
+    {.name = "cache-control", .value = "no-store"},
+    {.name = "cache-control", .value = "public, .value = max-age=31536000"},
+    {.name = "content-encoding", .value = "br"},
+    {.name = "content-encoding", .value = "gzip"},
+    {.name = "content-type", .value = "application/dns-message"},
+    {.name = "content-type", .value = "application/javascript"},
+    {.name = "content-type", .value = "application/json"},
+    {.name = "content-type", .value = "application/x-www-form-urlencoded"},
+    {.name = "content-type", .value = "image/gif"},
+    {.name = "content-type", .value = "image/jpeg"},
+    {.name = "content-type", .value = "image/png"},
+    {.name = "content-type", .value = "text/css"},
+    {.name = "content-type", .value = "text/html; charset=utf-8"},
+    {.name = "content-type", .value = "text/plain"},
+    {.name = "content-type", .value = "text/plain;charset=utf-8"},
+    {.name = "range", .value = "bytes=0-"},
+    {.name = "strict-transport-security", .value = "max-age=31536000"},
+    {.name = "strict-transport-security", .value = "max-age=31536000; includesubdomains"},
+    {.name = "strict-transport-security", .value = "max-age=31536000; includesubdomains; preload"},
+    {.name = "vary", .value = "accept-encoding"},
+    {.name = "vary", .value = "origin"},
+    {.name = "x-content-type-options", .value = "nosniff"},
+    {.name = "x-xss-protection", .value = "1; mode=block"},
+    {.name = ":status", .value = "100"},
+    {.name = ":status", .value = "204"},
+    {.name = ":status", .value = "206"},
+    {.name = ":status", .value = "302"},
+    {.name = ":status", .value = "400"},
+    {.name = ":status", .value = "403"},
+    {.name = ":status", .value = "421"},
+    {.name = ":status", .value = "425"},
+    {.name = ":status", .value = "500"},
+    {.name = "accept-language", .value = ""},
+    {.name = "access-control-allow-credentials", .value = "FALSE"},
+    {.name = "access-control-allow-credentials", .value = "TRUE"},
+    {.name = "access-control-allow-headers", .value = "*"},
+    {.name = "access-control-allow-methods", .value = "get"},
+    {.name = "access-control-allow-methods", .value = "get, .value = post, .value = options"},
+    {.name = "access-control-allow-methods", .value = "options"},
+    {.name = "access-control-expose-headers", .value = "content-length"},
+    {.name = "access-control-request-headers", .value = "content-type"},
+    {.name = "access-control-request-method", .value = "get"},
+    {.name = "access-control-request-method", .value = "post"},
+    {.name = "alt-svc", .value = "clear"},
+    {.name = "authorization"},
+    {.name = "content-security-policy", .value = "script-src 'none'; object-src 'none'; base-uri 'none'"},
+    {.name = "early-data", .value = "1"},
+    {.name = "expect-ct", .value = ""},
+    {.name = "forwarded", .value = ""},
+    {.name = "if-range", .value = ""},
+    {.name = "origin", .value = ""},
+    {.name = "purpose", .value = "prefetch"},
+    {.name = "server", .value = ""},
+    {.name = "timing-allow-origin", .value = "*"},
+    {.name = "upgrade-insecure-requests", .value = "1"},
+    {.name = "user-agent", .value = ""},
+    {.name = "x-forwarded-for", .value = ""},
+    {.name = "x-frame-options", .value = "deny"},
+    {.name = "x-frame-options", .value = "sameorigin"},
+ };
+ static int _http3_static_header_table_len = sizeof(_http3_static_header_table) / sizeof(struct qpack_header_field);
+/* clang-format on */
+
+struct qpack_header_field *qpack_get_static_header_field(int index)
+{
+	if (index < 0 || index >= _http3_static_header_table_len) {
+		return NULL;
+	}
+	return &_http3_static_header_table[index];
+}

+ 3 - 3
src/proxy.c

@@ -59,7 +59,7 @@ typedef enum PROXY_CONN_STATE {
 
 struct proxy_conn_buffer {
 	int len;
-	char buffer[PROXY_BUFFER_SIZE];
+	uint8_t buffer[PROXY_BUFFER_SIZE];
 };
 
 struct proxy_conn {
@@ -600,7 +600,7 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 		unsigned char addr[16];
 		unsigned short port = 0;
 		int use_dest_ip = 0;
-		char *recv_buff = NULL;
+		unsigned char *recv_buff = NULL;
 
 		int addr_len = 0;
 		len = recv(proxy_conn->fd, proxy_conn->buffer.buffer + proxy_conn->buffer.len,
@@ -792,7 +792,7 @@ static int _proxy_handshake_http(struct proxy_conn *proxy_conn)
 		goto out;
 	} break;
 	case PROXY_CONN_CONNECTING: {
-		http_head = http_head_init(4096);
+		http_head = http_head_init(4096, HTTP_VERSION_1_1);
 		if (http_head == NULL) {
 			goto out;
 		}

+ 5 - 0
src/smartdns.c

@@ -195,6 +195,7 @@ static int _smartdns_prepare_server_flags(struct client_dns_server_flags *flags,
 		struct client_dns_server_flag_udp *flag_udp = &flags->udp;
 		flag_udp->ttl = server->ttl;
 	} break;
+	case DNS_SERVER_HTTP3:
 	case DNS_SERVER_HTTPS: {
 		struct client_dns_server_flag_https *flag_http = &flags->https;
 		if (server->spki[0] != 0) {
@@ -812,6 +813,10 @@ static int _set_rlimit(void)
 	value.rlim_cur = 40;
 	value.rlim_max = 40;
 	setrlimit(RLIMIT_NICE, &value);
+
+	value.rlim_cur = 1024 * 10;
+	value.rlim_max = 1024 * 10;
+	setrlimit(RLIMIT_NOFILE, &value);
 	return 0;
 }
 

+ 7 - 3
test/Makefile

@@ -27,19 +27,23 @@ TEST_SOURCES := $(wildcard *.cc) $(wildcard */*.cc) $(wildcard */*/*.cc)
 TEST_OBJECTS := $(patsubst %.cc, %.o, $(TEST_SOURCES))
 OBJS += $(TEST_OBJECTS)
 
+SMARTDNS_SRC_FILES := $(wildcard $(SMARTDNS_SRC_DIR)/*.c) $(wildcard $(SMARTDNS_SRC_DIR)/*.h) $(wildcard $(SMARTDNS_SRC_DIR)/lib/*.c)
+
+
 LDFLAGS += -lssl -lcrypto -lpthread -ldl -lgtest -lstdc++ -lm
+LDFLAGS += $(EXTRA_LDFLAGS)
 
-.PHONY: all clean test
+.PHONY: all clean test $(SMARTDNS_TEST_LIB)
 
 all: $(BIN)
 
 $(BIN) : $(OBJS) $(SMARTDNS_TEST_LIB)
-	$(CC) $^ -o $@ $(LDFLAGS)
+	$(CC) $^  -o $@ $(LDFLAGS)
 
 test: $(BIN)
 	./$(BIN)
 
-$(SMARTDNS_TEST_LIB):
+$(SMARTDNS_TEST_LIB): 
 	$(MAKE) -C $(SMARTDNS_SRC_DIR) libsmartdns-test.a
 
 clean:

+ 172 - 0
test/cases/test-http.cc

@@ -0,0 +1,172 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * smartdns is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * smartdns is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "client.h"
+#include "dns.h"
+#include "http_parse.h"
+#include "include/utils.h"
+#include "server.h"
+#include "util.h"
+#include "gtest/gtest.h"
+#include <fstream>
+
+class HTTP : public ::testing::Test
+{
+  protected:
+	virtual void SetUp() {}
+	virtual void TearDown() {}
+};
+
+TEST_F(HTTP, http1_1_request_parse)
+{
+	const char *data = "GET /?q=question&lang=cn HTTP/1.1\r\n"
+					   "Host: www.example.com\r\n"
+					   "User-Agent: smartdns/46\r\n"
+					   "Accept: */*\r\n"
+					   "\r\n"
+					   "hello world";
+	struct http_head *http_head = http_head_init(1024, HTTP_VERSION_1_1);
+	ASSERT_NE(http_head, nullptr);
+	int ret = http_head_parse(http_head, (const unsigned char *)data, strlen(data));
+	ASSERT_GT(ret, 0);
+	EXPECT_STREQ(http_head_get_httpversion(http_head), "HTTP/1.1");
+	EXPECT_EQ(http_head_get_method(http_head), HTTP_METHOD_GET);
+	EXPECT_STREQ(http_head_get_url(http_head), "/");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Host"), "www.example.com");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "User-Agent"), "smartdns/46");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Accept"), "*/*");
+	EXPECT_STREQ((const char *)http_head_get_data(http_head), "hello world");
+	EXPECT_STREQ(http_head_get_params_value(http_head, "q"), "question");
+	EXPECT_STREQ(http_head_get_params_value(http_head, "lang"), "cn");
+
+	http_head_destroy(http_head);
+}
+
+TEST_F(HTTP, http1_1_request_serialize)
+{
+	const char *data = "GET /?q=question&lang=cn HTTP/1.1\r\n"
+					   "Host: www.example.com\r\n"
+					   "User-Agent: smartdns/46\r\n"
+					   "Accept: */*\r\n"
+					   "\r\n"
+					   "hello world";
+	struct http_head *http_head = http_head_init(1024, HTTP_VERSION_1_1);
+	ASSERT_NE(http_head, nullptr);
+	http_head_set_httpversion(http_head, "HTTP/1.1");
+	http_head_set_method(http_head, HTTP_METHOD_GET);
+	http_head_set_url(http_head, "/");
+	http_head_add_fields(http_head, "Host", "www.example.com");
+	http_head_add_fields(http_head, "User-Agent", "smartdns/46");
+	http_head_add_fields(http_head, "Accept", "*/*");
+	http_head_set_data(http_head, "hello world", strlen("hello world") + 1);
+	http_head_set_head_type(http_head, HTTP_HEAD_REQUEST);
+	http_head_add_param(http_head, "q", "question");
+	http_head_add_param(http_head, "lang", "cn");
+	char buffer[1024];
+	int ret = http_head_serialize(http_head, buffer, 1024);
+	ASSERT_GT(ret, 0);
+	EXPECT_STREQ(buffer, data);
+	http_head_destroy(http_head);
+}
+
+TEST_F(HTTP, http1_1_response_parse)
+{
+	const char *data = "HTTP/1.1 200 OK\r\n"
+					   "Server: smartdns\r\n"
+					   "Content-Type: text/html\r\n"
+					   "Content-Length: 11\r\n"
+					   "\r\n"
+					   "hello world";
+	struct http_head *http_head = http_head_init(1024, HTTP_VERSION_1_1);
+	ASSERT_NE(http_head, nullptr);
+	int ret = http_head_parse(http_head, (const unsigned char *)data, strlen(data));
+	ASSERT_GT(ret, 0);
+	EXPECT_STREQ(http_head_get_httpversion(http_head), "HTTP/1.1");
+	EXPECT_EQ(http_head_get_httpcode(http_head), 200);
+	EXPECT_STREQ(http_head_get_httpcode_msg(http_head), "OK");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Server"), "smartdns");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Content-Type"), "text/html");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Content-Length"), "11");
+	EXPECT_STREQ((const char *)http_head_get_data(http_head), "hello world");
+
+	http_head_destroy(http_head);
+}
+
+TEST_F(HTTP, http1_1_response_serialize)
+{
+	const char *data = "HTTP/1.1 200 OK\r\n"
+					   "Server: smartdns\r\n"
+					   "Content-Type: text/html\r\n"
+					   "Content-Length: 11\r\n"
+					   "\r\n"
+					   "hello world";
+	struct http_head *http_head = http_head_init(1024, HTTP_VERSION_1_1);
+	ASSERT_NE(http_head, nullptr);
+
+	http_head_set_httpversion(http_head, "HTTP/1.1");
+	http_head_set_httpcode(http_head, 200, "OK");
+	http_head_add_fields(http_head, "Server", "smartdns");
+	http_head_add_fields(http_head, "Content-Type", "text/html");
+	http_head_add_fields(http_head, "Content-Length", "11");
+	http_head_set_data(http_head, "hello world", strlen("hello world") + 1);
+	http_head_set_head_type(http_head, HTTP_HEAD_RESPONSE);
+	char buffer[1024];
+
+	int ret = http_head_serialize(http_head, buffer, 1024);
+	ASSERT_GT(ret, 0);
+	EXPECT_STREQ(buffer, data);
+	http_head_destroy(http_head);
+}
+
+
+TEST_F(HTTP, http3_0_parse)
+{
+	struct http_head *http_head = http_head_init(1024, HTTP_VERSION_3_0);
+	ASSERT_NE(http_head, nullptr);
+	http_head_set_httpversion(http_head, "HTTP/3");
+	http_head_set_method(http_head, HTTP_METHOD_GET);
+	http_head_set_url(http_head, "/");
+	http_head_add_fields(http_head, "Host", "www.example.com");
+	http_head_add_fields(http_head, "User-Agent", "smartdns/46");
+	http_head_add_fields(http_head, "Accept", "*/*");
+	http_head_set_data(http_head, "hello world", strlen("hello world") + 1);
+	http_head_set_head_type(http_head, HTTP_HEAD_REQUEST);
+	http_head_add_param(http_head, "q", "question");
+	http_head_add_param(http_head, "lang", "cn");
+	unsigned char buffer[1024];
+	int ret = http_head_serialize(http_head, buffer, 1024);
+	ASSERT_EQ(ret, 149);
+	http_head_destroy(http_head);
+
+	http_head = http_head_init(1024, HTTP_VERSION_3_0);
+	ASSERT_NE(http_head, nullptr);
+
+	ret = http_head_parse(http_head, buffer, ret);
+	ASSERT_EQ(ret, 149);
+	EXPECT_STREQ(http_head_get_httpversion(http_head), "HTTP/3.0");
+	EXPECT_EQ(http_head_get_method(http_head), HTTP_METHOD_GET);
+	EXPECT_STREQ(http_head_get_url(http_head), "/");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Host"), "www.example.com");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "User-Agent"), "smartdns/46");
+	EXPECT_STREQ(http_head_get_fields_value(http_head, "Accept"), "*/*");
+	EXPECT_STREQ((const char *)http_head_get_data(http_head), "hello world");
+	EXPECT_STREQ(http_head_get_params_value(http_head, "q"), "question");
+	EXPECT_STREQ(http_head_get_params_value(http_head, "lang"), "cn");
+
+	http_head_destroy(http_head);
+}