feat(ednsdiag): add DoQ/DoH3/DNSCrypt transports, proxy support, probe & compare

This commit is contained in:
windyboy
2026-08-14 18:17:26 +08:00
parent 8303d78caf
commit 70aea6cd72
21 changed files with 1665 additions and 236 deletions
@@ -4,6 +4,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"golang.org/x/net/dns/dnsmessage"
@@ -42,6 +43,7 @@ func TestExchangeDoHGETAndPOST(t *testing.T) {
return
}
writer.Header().Set("Content-Type", "application/dns-message")
writer.Header().Set("Age", "10")
_, _ = writer.Write(responseWire)
}))
defer server.Close()
@@ -56,13 +58,78 @@ func TestExchangeDoHGETAndPOST(t *testing.T) {
if err != nil {
t.Fatalf("exchange DoH: %v", err)
}
if len(response) == 0 || !info.Encrypted || !info.ServerAuthenticated {
if len(response) == 0 || !info.Encrypted || !info.ServerAuthenticated || info.HTTPAgeSeconds != 10 {
t.Fatalf("unexpected result: response=%d info=%#v", len(response), info)
}
})
}
}
func TestExchangeDoHThroughHTTPConnectProxy(t *testing.T) {
server := httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
payload, err := io.ReadAll(request.Body)
if err != nil {
http.Error(writer, err.Error(), http.StatusBadRequest)
return
}
var query dnsmessage.Message
if err := query.Unpack(payload); err != nil {
http.Error(writer, err.Error(), http.StatusBadRequest)
return
}
response := dnsmessage.Message{
Header: dnsmessage.Header{ID: query.Header.ID, Response: true},
Questions: query.Questions,
}
responseWire, err := response.Pack()
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/dns-message")
_, _ = writer.Write(responseWire)
}))
defer server.Close()
endpoint, err := url.Parse(server.URL)
if err != nil {
t.Fatalf("parse test endpoint: %v", err)
}
proxyURL, proxyError := startConnectProxy(t, endpoint.Host, "Basic dXNlcjpzZWNyZXQ=")
testTransport := server.Client().Transport.(*http.Transport)
client, proxyLabel, err := newDoHClientWithTLSConfig(server.URL, proxyURL, testTransport.TLSClientConfig)
if err != nil {
t.Fatalf("create proxied DoH client: %v", err)
}
if proxyLabel == "" || proxyLabel == proxyURL {
t.Fatalf("proxy label = %q, want sanitized URL", proxyLabel)
}
wire, _, _, err := BuildQuery("example.com", "A")
if err != nil {
t.Fatalf("build query: %v", err)
}
if _, _, err := exchangeDoHWithClient(t.Context(), client, server.URL, wire, "post"); err != nil {
t.Fatalf("exchange DoH through proxy: %v", err)
}
client.CloseIdleConnections()
if err := <-proxyError; err != nil {
t.Fatalf("serve CONNECT proxy: %v", err)
}
}
func TestExchangeDoHRejectsInvalidAge(t *testing.T) {
server := httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
writer.Header().Set("Content-Type", "application/dns-message")
writer.Header().Set("Age", "invalid")
_, _ = writer.Write([]byte{1})
}))
defer server.Close()
if _, _, err := exchangeDoHWithClient(t.Context(), server.Client(), server.URL, []byte{1}, "post"); err == nil {
t.Fatal("invalid HTTP Age was accepted")
}
}
func TestExchangeDoHRejectsHTTPError(t *testing.T) {
server := httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
http.Error(writer, "unavailable", http.StatusServiceUnavailable)