From 230d44946fcdf013e61fb41ecc36a253cb65ee01 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 1/7] config --- app/dns/config.pb.go | 61 ++++++++++++++++++++++++++------------------ app/dns/config.proto | 2 ++ app/dns/dns.go | 2 ++ infra/conf/dns.go | 2 ++ 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index 9cc6398610ce..6778e3de87a0 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -312,6 +312,7 @@ type Config struct { QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"` DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"` + EnableParallelQuery bool `protobuf:"varint,14,opt,name=enableParallelQuery,proto3" json:"enableParallelQuery,omitempty"` } func (x *Config) Reset() { @@ -414,6 +415,13 @@ func (x *Config) GetDisableFallbackIfMatch() bool { return false } +func (x *Config) GetEnableParallelQuery() bool { + if x != nil { + return x.EnableParallelQuery + } + return false +} + type NameServer_PriorityDomain struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -657,7 +665,7 @@ var file_app_dns_config_proto_rawDesc = []byte{ 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x22, 0xe6, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x22, 0x98, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, @@ -686,30 +694,33 @@ var file_app_dns_config_proto_rawDesc = []byte{ 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, - 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, - 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, - 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, - 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, - 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, - 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, - 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, - 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, - 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, - 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, - 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, - 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, - 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, - 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, - 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, - 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, + 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, + 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, + 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, + 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, + 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, + 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, + 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, + 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, + 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, + 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46, 0x0a, + 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, + 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, + 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, + 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/dns/config.proto b/app/dns/config.proto index a87725a3fd47..e1cc7c6cdcb3 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -89,4 +89,6 @@ message Config { bool disableFallback = 10; bool disableFallbackIfMatch = 11; + + bool enableParallelQuery = 14; } diff --git a/app/dns/dns.go b/app/dns/dns.go index 78e913896ceb..6a2a3c09e58e 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -25,6 +25,7 @@ type DNS struct { sync.Mutex disableFallback bool disableFallbackIfMatch bool + enableParallelQuery bool ipOption *dns.IPOption hosts *StaticHosts clients []*Client @@ -157,6 +158,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) { matcherInfos: matcherInfos, disableFallback: config.DisableFallback, disableFallbackIfMatch: config.DisableFallbackIfMatch, + enableParallelQuery: config.EnableParallelQuery, checkSystem: checkSystem, }, nil } diff --git a/infra/conf/dns.go b/infra/conf/dns.go index 74b892c8bc6e..e25f0f17ff47 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -206,6 +206,7 @@ type DNSConfig struct { ServeExpiredTTL uint32 `json:"serveExpiredTTL"` DisableFallback bool `json:"disableFallback"` DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` + EnableParallelQuery bool `json:"enableParallelQuery"` UseSystemHosts bool `json:"useSystemHosts"` } @@ -405,6 +406,7 @@ func (c *DNSConfig) Build() (*dns.Config, error) { ServeExpiredTTL: c.ServeExpiredTTL, DisableFallback: c.DisableFallback, DisableFallbackIfMatch: c.DisableFallbackIfMatch, + EnableParallelQuery: c.EnableParallelQuery, QueryStrategy: resolveQueryStrategy(c.QueryStrategy), } From 64e1a14b086e3201798639bb642b242d54c53c80 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 2/7] parallel query --- app/dns/dns.go | 105 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/app/dns/dns.go b/app/dns/dns.go index 6a2a3c09e58e..5efce0d602bb 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -238,29 +238,96 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er // Name servers lookup var errs []error - for _, client := range s.sortClients(domain) { - if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { - errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) - continue - } + clients := s.sortClients(domain) + numClients := len(clients) + if !s.enableParallelQuery { + for _, client := range clients { + if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { + errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) + continue + } + + ips, ttl, err := client.QueryIP(s.ctx, domain, option) - ips, ttl, err := client.QueryIP(s.ctx, domain, option) + if len(ips) > 0 { + return ips, ttl, nil + } - if len(ips) > 0 { - if ttl == 0 { - ttl = 1 + errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode") + if err == nil { + err = dns.ErrEmptyResponse } - return ips, ttl, nil + errs = append(errs, err) } + } else { + strictMode := false + + ctx, cancel := context.WithCancel(s.ctx) + defer cancel() - errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name()) - if err == nil { - err = dns.ErrEmptyResponse + type result struct { + ips []net.IP + ttl uint32 + err error + index int } - errs = append(errs, err) + resultsChan := make(chan result, numClients) - if client.IsFinalQuery() { - break + for i, client := range clients { + if !strictMode && (len(client.expectedIPs) != 0 || len(client.unexpectedIPs) != 0) { + strictMode = true + } + + if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { + errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) + go func(i int) { + resultsChan <- result{err: dns.ErrEmptyResponse, index: i} + }(i) + continue + } + + go func(i int, c *Client) { + ips, ttl, err := c.QueryIP(ctx, domain, option) + resultsChan <- result{ips: ips, ttl: ttl, err: err, index: i} + }(i, client) + } + + if strictMode { + results := make([]*result, numClients) + for range numClients { + res := <-resultsChan + results[res.index] = &res + for j := 0; j < numClients; j++ { + if results[j] == nil { + break + } + if results[j].index == -1 { + continue + } + res := results[j] + if len(res.ips) > 0 { + return res.ips, res.ttl, nil + } + errors.LogInfoInner(ctx, res.err, "failed to lookup ip for domain ", domain, " at server ", clients[res.index].Name(), " in strict parallel query mode") + if res.err == nil { + res.err = dns.ErrEmptyResponse + } + errs = append(errs, res.err) + res.index = -1 + } + } + } else { + for range numClients { + res := <-resultsChan + if len(res.ips) > 0 { + return res.ips, res.ttl, nil + } + errors.LogInfoInner(ctx, res.err, "failed to lookup ip for domain ", domain, " at server ", clients[res.index].Name(), " in opportunistic parallel query mode") + if res.err == nil { + res.err = dns.ErrEmptyResponse + } + errs = append(errs, res.err) + } } } @@ -302,6 +369,9 @@ func (s *DNS) sortClients(domain string) []*Client { clients = append(clients, client) clientNames = append(clientNames, client.Name()) hasMatch = true + if client.finalQuery { + break + } } if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) { @@ -313,6 +383,9 @@ func (s *DNS) sortClients(domain string) []*Client { clientUsed[idx] = true clients = append(clients, client) clientNames = append(clientNames, client.Name()) + if client.finalQuery { + break + } } } From 843eba90aef5b77c8db9cf2fb4fadaf59f9bb393 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 3/7] v2 nb plus --- app/dns/config.pb.go | 146 ++++++++++---------- app/dns/config.proto | 1 + app/dns/dns.go | 302 +++++++++++++++++++++++++++--------------- app/dns/nameserver.go | 6 +- infra/conf/dns.go | 62 +++++++++ 5 files changed, 336 insertions(+), 181 deletions(-) diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index 6778e3de87a0..d7ac3803a4eb 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -147,6 +147,7 @@ type NameServer struct { FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"` UnexpectedGeoip []*router.GeoIP `protobuf:"bytes,13,rep,name=unexpected_geoip,json=unexpectedGeoip,proto3" json:"unexpected_geoip,omitempty"` ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"` + PolicyID uint32 `protobuf:"varint,17,opt,name=policyID,proto3" json:"policyID,omitempty"` } func (x *NameServer) Reset() { @@ -291,6 +292,13 @@ func (x *NameServer) GetActUnprior() bool { return false } +func (x *NameServer) GetPolicyID() uint32 { + if x != nil { + return x.PolicyID + } + return 0 +} + type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -607,7 +615,7 @@ var file_app_dns_config_proto_rawDesc = []byte{ 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x07, 0x0a, 0x0a, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x07, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, @@ -654,73 +662,75 @@ var file_app_dns_config_proto_rawDesc = []byte{ 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, - 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, - 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x22, 0x98, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, - 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, - 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, - 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, - 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, - 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, - 0x61, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x53, 0x74, 0x61, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, - 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x12, - 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, - 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, - 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, - 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x36, 0x0a, - 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, - 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, - 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, - 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, - 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, - 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, - 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, - 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, - 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, - 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, - 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, - 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, - 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, - 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, - 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, - 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46, 0x0a, - 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, - 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, - 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, - 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x44, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x44, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, + 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, + 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x42, + 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, + 0x54, 0x54, 0x4c, 0x22, 0x98, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, + 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, + 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, + 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, + 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, + 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, + 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, + 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, + 0x64, 0x54, 0x54, 0x4c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x12, 0x42, 0x0a, 0x0e, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, + 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, + 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, + 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6c, + 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, + 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, + 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, + 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x45, + 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, + 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, + 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, + 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, + 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, + 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, + 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/dns/config.proto b/app/dns/config.proto index e1cc7c6cdcb3..f1b47bc06e7c 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -37,6 +37,7 @@ message NameServer { bool finalQuery = 12; repeated xray.app.router.GeoIP unexpected_geoip = 13; bool actUnprior = 14; + uint32 policyID = 17; } enum DomainMatchingType { diff --git a/app/dns/dns.go b/app/dns/dns.go index 5efce0d602bb..cafffa83a2ac 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -237,112 +237,11 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er } // Name servers lookup - var errs []error - clients := s.sortClients(domain) - numClients := len(clients) - if !s.enableParallelQuery { - for _, client := range clients { - if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { - errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) - continue - } - - ips, ttl, err := client.QueryIP(s.ctx, domain, option) - - if len(ips) > 0 { - return ips, ttl, nil - } - - errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode") - if err == nil { - err = dns.ErrEmptyResponse - } - errs = append(errs, err) - } + if s.enableParallelQuery { + return s.parallelQuery(domain, option) } else { - strictMode := false - - ctx, cancel := context.WithCancel(s.ctx) - defer cancel() - - type result struct { - ips []net.IP - ttl uint32 - err error - index int - } - resultsChan := make(chan result, numClients) - - for i, client := range clients { - if !strictMode && (len(client.expectedIPs) != 0 || len(client.unexpectedIPs) != 0) { - strictMode = true - } - - if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { - errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) - go func(i int) { - resultsChan <- result{err: dns.ErrEmptyResponse, index: i} - }(i) - continue - } - - go func(i int, c *Client) { - ips, ttl, err := c.QueryIP(ctx, domain, option) - resultsChan <- result{ips: ips, ttl: ttl, err: err, index: i} - }(i, client) - } - - if strictMode { - results := make([]*result, numClients) - for range numClients { - res := <-resultsChan - results[res.index] = &res - for j := 0; j < numClients; j++ { - if results[j] == nil { - break - } - if results[j].index == -1 { - continue - } - res := results[j] - if len(res.ips) > 0 { - return res.ips, res.ttl, nil - } - errors.LogInfoInner(ctx, res.err, "failed to lookup ip for domain ", domain, " at server ", clients[res.index].Name(), " in strict parallel query mode") - if res.err == nil { - res.err = dns.ErrEmptyResponse - } - errs = append(errs, res.err) - res.index = -1 - } - } - } else { - for range numClients { - res := <-resultsChan - if len(res.ips) > 0 { - return res.ips, res.ttl, nil - } - errors.LogInfoInner(ctx, res.err, "failed to lookup ip for domain ", domain, " at server ", clients[res.index].Name(), " in opportunistic parallel query mode") - if res.err == nil { - res.err = dns.ErrEmptyResponse - } - errs = append(errs, res.err) - } - } + return s.serialQuery(domain, option) } - - if len(errs) > 0 { - allErrs := errors.Combine(errs...) - err0 := errs[0] - if errors.AllEqual(err0, allErrs) { - if go_errors.Is(err0, dns.ErrEmptyResponse) { - return nil, 0, dns.ErrEmptyResponse - } - return nil, 0, errors.New("returning nil for domain ", domain).Base(err0) - } - return nil, 0, errors.New("returning nil for domain ", domain).Base(allErrs) - } - return nil, 0, dns.ErrEmptyResponse } func (s *DNS) sortClients(domain string) []*Client { @@ -370,7 +269,7 @@ func (s *DNS) sortClients(domain string) []*Client { clientNames = append(clientNames, client.Name()) hasMatch = true if client.finalQuery { - break + return clients } } @@ -384,7 +283,7 @@ func (s *DNS) sortClients(domain string) []*Client { clients = append(clients, client) clientNames = append(clientNames, client.Name()) if client.finalQuery { - break + return clients } } } @@ -397,14 +296,199 @@ func (s *DNS) sortClients(domain string) []*Client { } if len(clients) == 0 { - clients = append(clients, s.clients[0]) - clientNames = append(clientNames, s.clients[0].Name()) - errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) + if len(s.clients) > 0 { + clients = append(clients, s.clients[0]) + clientNames = append(clientNames, s.clients[0].Name()) + errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) + } else { + errors.LogDebug(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured") + } } return clients } +func mergeQueryErrors(domain string, errs []error) error { + if len(errs) > 0 { + allErrs := errors.Combine(errs...) + err0 := errs[0] + if errors.AllEqual(err0, allErrs) { + if go_errors.Is(err0, dns.ErrEmptyResponse) { + return dns.ErrEmptyResponse + } + return errors.New("returning nil for domain ", domain).Base(err0) + } + return errors.New("returning nil for domain ", domain).Base(allErrs) + } + return dns.ErrEmptyResponse +} + +func (s *DNS) serialQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) { + var errs []error + for _, client := range s.sortClients(domain) { + if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { + errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) + continue + } + + ips, ttl, err := client.QueryIP(s.ctx, domain, option) + + if len(ips) > 0 { + return ips, ttl, nil + } + + errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode") + if err == nil { + err = dns.ErrEmptyResponse + } + errs = append(errs, err) + } + return nil, 0, mergeQueryErrors(domain, errs) +} + +func (s *DNS) parallelQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) { + var errs []error + clients := s.sortClients(domain) + + resultsChan := asyncQueryAll(domain, option, clients, s.ctx) + + groups, groupOf := makeGroups( /*s.ctx,*/ clients) + results := make([]*queryResult, len(clients)) + pending := make([]int, len(groups)) + for gi, g := range groups { + pending[gi] = g.end - g.start + 1 + } + + nextGroup := 0 + for range clients { + result := <-resultsChan + results[result.index] = &result + + gi := groupOf[result.index] + pending[gi]-- + + for nextGroup < len(groups) { + g := groups[nextGroup] + + // group race, minimum rtt -> return + for j := g.start; j <= g.end; j++ { + r := results[j] + if r != nil && r.err == nil && len(r.ips) > 0 { + return r.ips, r.ttl, nil + } + } + + // current group is incomplete and no one success -> continue pending + if pending[nextGroup] > 0 { + break + } + + // all failed -> log and continue next group + for j := g.start; j <= g.end; j++ { + r := results[j] + e := r.err + if e == nil { + e = dns.ErrEmptyResponse + } + errors.LogInfoInner(s.ctx, e, "failed to lookup ip for domain ", domain, " at server ", clients[j].Name(), " in parallel query mode") + errs = append(errs, e) + } + nextGroup++ + } + } + + return nil, 0, mergeQueryErrors(domain, errs) +} + +type queryResult struct { + ips []net.IP + ttl uint32 + err error + index int +} + +func asyncQueryAll(domain string, option dns.IPOption, clients []*Client, ctx context.Context) chan queryResult { + if len(clients) == 0 { + ch := make(chan queryResult) + close(ch) + return ch + } + + ch := make(chan queryResult, len(clients)) + for i, client := range clients { + if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { + errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) + ch <- queryResult{err: dns.ErrEmptyResponse, index: i} + continue + } + + go func(i int, c *Client) { + ips, ttl, err := c.QueryIP(ctx, domain, option) + ch <- queryResult{ips: ips, ttl: ttl, err: err, index: i} + }(i, client) + } + return ch +} + +type group struct{ start, end int } + +// merge only adjacent and rule-equivalent Client into a single group +func makeGroups( /*ctx context.Context,*/ clients []*Client) ([]group, []int) { + n := len(clients) + if n == 0 { + return nil, nil + } + groups := make([]group, 0, n) + groupOf := make([]int, n) + + s, e := 0, 0 + for i := 1; i < n; i++ { + if clients[i-1].policyID == clients[i].policyID { + e = i + } else { + for k := s; k <= e; k++ { + groupOf[k] = len(groups) + } + groups = append(groups, group{start: s, end: e}) + s, e = i, i + } + } + for k := s; k <= e; k++ { + groupOf[k] = len(groups) + } + groups = append(groups, group{start: s, end: e}) + + // var b strings.Builder + // b.WriteString("dns grouping: total clients=") + // b.WriteString(strconv.Itoa(n)) + // b.WriteString(", groups=") + // b.WriteString(strconv.Itoa(len(groups))) + + // for gi, g := range groups { + // b.WriteString("\n [") + // b.WriteString(strconv.Itoa(g.start)) + // b.WriteString("..") + // b.WriteString(strconv.Itoa(g.end)) + // b.WriteString("] gid=") + // b.WriteString(strconv.Itoa(gi)) + // b.WriteString(" pid=") + // b.WriteString(strconv.FormatUint(uint64(clients[g.start].policyID), 10)) + // b.WriteString(" members: ") + + // for i := g.start; i <= g.end; i++ { + // if i > g.start { + // b.WriteString(", ") + // } + // b.WriteString(strconv.Itoa(i)) + // b.WriteByte(':') + // b.WriteString(clients[i].Name()) + // } + // } + // errors.LogDebug(ctx, b.String()) + + return groups, groupOf +} + func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 3f25833e0752..0e1e6574bb37 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -38,6 +38,7 @@ type Client struct { finalQuery bool ipOption *dns.IPOption checkSystem bool + policyID uint32 } // NewServer creates a name server object according to the network destination url. @@ -199,6 +200,7 @@ func NewClient( client.finalQuery = ns.FinalQuery client.ipOption = &ipOption client.checkSystem = checkSystem + client.policyID = ns.PolicyID return nil }) return client, err @@ -209,10 +211,6 @@ func (c *Client) Name() string { return c.server.Name() } -func (c *Client) IsFinalQuery() bool { - return c.finalQuery -} - // QueryIP sends DNS query to the name server with the client's IP. func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) { if c.checkSystem { diff --git a/infra/conf/dns.go b/infra/conf/dns.go index e25f0f17ff47..655b141bdeda 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -417,11 +417,73 @@ func (c *DNSConfig) Build() (*dns.Config, error) { config.ClientIp = []byte(c.ClientIP.IP()) } + // Build PolicyID + policyMap := map[string]uint32{} + nextPolicyID := uint32(1) + buildPolicyID := func(nsc *NameServerConfig) uint32 { + var sb strings.Builder + + // ClientIP + if nsc.ClientIP != nil { + sb.WriteString("client=") + sb.WriteString(nsc.ClientIP.String()) + sb.WriteByte('|') + } else { + sb.WriteString("client=none|") + } + + // SkipFallback + if nsc.SkipFallback { + sb.WriteString("skip=1|") + } else { + sb.WriteString("skip=0|") + } + + // QueryStrategy + sb.WriteString("qs=") + sb.WriteString(strings.ToLower(strings.TrimSpace(nsc.QueryStrategy))) + sb.WriteByte('|') + + // []string helper + writeList := func(tag string, lst []string) { + if len(lst) == 0 { + sb.WriteString(tag) + sb.WriteString("=[]|") + return + } + cp := make([]string, len(lst)) + for i, s := range lst { + cp[i] = strings.TrimSpace(strings.ToLower(s)) + } + sort.Strings(cp) + sb.WriteString(tag) + sb.WriteByte('=') + sb.WriteString(strings.Join(cp, ",")) + sb.WriteByte('|') + } + + writeList("domains", nsc.Domains) + writeList("expected", nsc.ExpectedIPs) + writeList("expect", nsc.ExpectIPs) + writeList("unexpected", nsc.UnexpectedIPs) + + key := sb.String() + + if id, ok := policyMap[key]; ok { + return id + } + id := nextPolicyID + nextPolicyID++ + policyMap[key] = id + return id + } + for _, server := range c.Servers { ns, err := server.Build() if err != nil { return nil, errors.New("failed to build nameserver").Base(err) } + ns.PolicyID = buildPolicyID(server) config.NameServer = append(config.NameServer, ns) } From 2f7b28d665a5cb39512f4bba1003b0db9b36f445 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 4/7] fix --- infra/conf/dns.go | 5 +++++ infra/conf/dns_test.go | 1 + 2 files changed, 6 insertions(+) diff --git a/infra/conf/dns.go b/infra/conf/dns.go index 655b141bdeda..7c94a488bf93 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -444,6 +444,11 @@ func (c *DNSConfig) Build() (*dns.Config, error) { sb.WriteString(strings.ToLower(strings.TrimSpace(nsc.QueryStrategy))) sb.WriteByte('|') + // Tag + sb.WriteString("tag=") + sb.WriteString(strings.ToLower(strings.TrimSpace(nsc.Tag))) + sb.WriteByte('|') + // []string helper writeList := func(tag string, lst []string) { if len(lst) == 0 { diff --git a/infra/conf/dns_test.go b/infra/conf/dns_test.go index 3329f5a23295..af51b7341d99 100644 --- a/infra/conf/dns_test.go +++ b/infra/conf/dns_test.go @@ -74,6 +74,7 @@ func TestDNSConfigParsing(t *testing.T) { }, ServeStale: true, ServeExpiredTTL: &expectedServeExpiredTTL, + PolicyID: 1, // Servers with certain identical fields share this ID, incrementing starting from 1. See: Build PolicyID }, }, StaticHosts: []*dns.Config_HostMapping{ From 7c3b57d487ad50b2e78ad134b685355febbc9b63 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 5/7] rejects caller ctx.cancel() --- app/dns/dns.go | 8 +++++++- app/dns/nameserver.go | 3 +++ app/dns/nameserver_doh.go | 5 +++++ app/dns/nameserver_fakedns.go | 5 +++++ app/dns/nameserver_local.go | 5 +++++ app/dns/nameserver_quic.go | 5 +++++ app/dns/nameserver_tcp.go | 5 +++++ app/dns/nameserver_udp.go | 5 +++++ 8 files changed, 40 insertions(+), 1 deletion(-) diff --git a/app/dns/dns.go b/app/dns/dns.go index cafffa83a2ac..7a6bcc8680f5 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -423,7 +423,13 @@ func asyncQueryAll(domain string, option dns.IPOption, clients []*Client, ctx co } go func(i int, c *Client) { - ips, ttl, err := c.QueryIP(ctx, domain, option) + qctx := ctx + if !c.server.IsDisableCache() { + nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), c.timeoutMs*2) + qctx = nctx + defer cancel() + } + ips, ttl, err := c.QueryIP(qctx, domain, option) ch <- queryResult{ips: ips, ttl: ttl, err: err, index: i} }(i, client) } diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 0e1e6574bb37..e606d4b38f6a 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -20,6 +20,9 @@ import ( type Server interface { // Name of the Client. Name() string + + IsDisableCache() bool + // QueryIP sends IP queries to its configured server. QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) } diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go index 88198aed49c7..0dbd71bbcb9d 100644 --- a/app/dns/nameserver_doh.go +++ b/app/dns/nameserver_doh.go @@ -116,6 +116,11 @@ func (s *DoHNameServer) Name() string { return s.cacheController.name } +// IsDisableCache implements Server. +func (s *DoHNameServer) IsDisableCache() bool { + return s.cacheController.disableCache +} + func (s *DoHNameServer) newReqID() uint16 { return 0 } diff --git a/app/dns/nameserver_fakedns.go b/app/dns/nameserver_fakedns.go index 8c598ac88ef1..bed11bd606a0 100644 --- a/app/dns/nameserver_fakedns.go +++ b/app/dns/nameserver_fakedns.go @@ -20,6 +20,11 @@ func (FakeDNSServer) Name() string { return "FakeDNS" } +// IsDisableCache implements Server. +func (s *FakeDNSServer) IsDisableCache() bool { + return true +} + func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) { if f.fakeDNSEngine == nil { return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError() diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go index 91b003e393c7..576c259b72ea 100644 --- a/app/dns/nameserver_local.go +++ b/app/dns/nameserver_local.go @@ -35,6 +35,11 @@ func (s *LocalNameServer) Name() string { return "localhost" } +// IsDisableCache implements Server. +func (s *LocalNameServer) IsDisableCache() bool { + return true +} + // NewLocalNameServer creates localdns server object for directly lookup in system DNS. func NewLocalNameServer() *LocalNameServer { errors.LogInfo(context.Background(), "DNS: created localhost client") diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go index 3d271d74c3b9..075bc6737529 100644 --- a/app/dns/nameserver_quic.go +++ b/app/dns/nameserver_quic.go @@ -63,6 +63,11 @@ func (s *QUICNameServer) Name() string { return s.cacheController.name } +// IsDisableCache implements Server. +func (s *QUICNameServer) IsDisableCache() bool { + return s.cacheController.disableCache +} + func (s *QUICNameServer) newReqID() uint16 { return 0 } diff --git a/app/dns/nameserver_tcp.go b/app/dns/nameserver_tcp.go index f549a1615cc6..46f174ea650d 100644 --- a/app/dns/nameserver_tcp.go +++ b/app/dns/nameserver_tcp.go @@ -93,6 +93,11 @@ func (s *TCPNameServer) Name() string { return s.cacheController.name } +// IsDisableCache implements Server. +func (s *TCPNameServer) IsDisableCache() bool { + return s.cacheController.disableCache +} + func (s *TCPNameServer) newReqID() uint16 { return uint16(atomic.AddUint32(&s.reqID, 1)) } diff --git a/app/dns/nameserver_udp.go b/app/dns/nameserver_udp.go index 4449ac14dce7..b2adb954401a 100644 --- a/app/dns/nameserver_udp.go +++ b/app/dns/nameserver_udp.go @@ -63,6 +63,11 @@ func (s *ClassicNameServer) Name() string { return s.cacheController.name } +// IsDisableCache implements Server. +func (s *ClassicNameServer) IsDisableCache() bool { + return s.cacheController.disableCache +} + // RequestsCleanup clears expired items from cache func (s *ClassicNameServer) RequestsCleanup() error { now := time.Now() From f8b78d8f332db5c8c3c70fd52505376b029adc70 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 6/7] Ignore no rep servers when merging errors --- app/dns/dns.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/app/dns/dns.go b/app/dns/dns.go index 7a6bcc8680f5..01d2d8fe944f 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -309,18 +309,27 @@ func (s *DNS) sortClients(domain string) []*Client { } func mergeQueryErrors(domain string, errs []error) error { - if len(errs) > 0 { - allErrs := errors.Combine(errs...) - err0 := errs[0] - if errors.AllEqual(err0, allErrs) { - if go_errors.Is(err0, dns.ErrEmptyResponse) { - return dns.ErrEmptyResponse - } - return errors.New("returning nil for domain ", domain).Base(err0) + if len(errs) == 0 { + return dns.ErrEmptyResponse + } + + var noRNF error + for _, err := range errs { + if go_errors.Is(err, errRecordNotFound) { + continue // server no response, ignore + } else if noRNF == nil { + noRNF = err + } else if !go_errors.Is(err, noRNF) { + return errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...)) } - return errors.New("returning nil for domain ", domain).Base(allErrs) } - return dns.ErrEmptyResponse + if go_errors.Is(noRNF, dns.ErrEmptyResponse) { + return dns.ErrEmptyResponse + } + if noRNF == nil { + noRNF = errRecordNotFound + } + return errors.New("returning nil for domain ", domain).Base(noRNF) } func (s *DNS) serialQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) { From 77c064ca72ef62eb9dbde154791094bc43a61c1e Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:55:12 +0800 Subject: [PATCH 7/7] refine log --- app/dns/cache_controller.go | 4 ++-- app/dns/dns.go | 4 ++-- app/dns/nameserver_cached.go | 4 ++-- app/dns/nameserver_doh.go | 2 +- app/dns/nameserver_quic.go | 3 +-- app/dns/nameserver_tcp.go | 4 +++- app/dns/nameserver_udp.go | 7 ++++--- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/dns/cache_controller.go b/app/dns/cache_controller.go index 24ae3cb250a9..a303b264a9ca 100644 --- a/app/dns/cache_controller.go +++ b/app/dns/cache_controller.go @@ -194,7 +194,7 @@ func (c *CacheController) migrate() { return } - errors.LogDebug(context.Background(), c.name, " starting background cache migration for ", len(dirtyips), " items.") + errors.LogDebug(context.Background(), c.name, " starting background cache migration for ", len(dirtyips), " items") batch := make([]migrationEntry, 0, migrationBatchSize) for domain, recD := range dirtyips { @@ -214,7 +214,7 @@ func (c *CacheController) migrate() { c.dirtyips = nil c.Unlock() - errors.LogDebug(context.Background(), c.name, " cache migration completed.") + errors.LogDebug(context.Background(), c.name, " cache migration completed") } func (c *CacheController) flush(batch []migrationEntry) { diff --git a/app/dns/dns.go b/app/dns/dns.go index 01d2d8fe944f..9233d9053d73 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -299,9 +299,9 @@ func (s *DNS) sortClients(domain string) []*Client { if len(s.clients) > 0 { clients = append(clients, s.clients[0]) clientNames = append(clientNames, s.clients[0].Name()) - errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) + errors.LogWarning(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) } else { - errors.LogDebug(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured") + errors.LogError(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured") } } diff --git a/app/dns/nameserver_cached.go b/app/dns/nameserver_cached.go index e27dda4992c8..cbd2f0319f08 100644 --- a/app/dns/nameserver_cached.go +++ b/app/dns/nameserver_cached.go @@ -28,12 +28,12 @@ func queryIP(ctx context.Context, s CachedNameserver, domain string, option dns. ips, ttl, err := merge(option, rec.A, rec.AAAA) if !go_errors.Is(err, errRecordNotFound) { if ttl > 0 { - // errors.LogDebugInner(ctx, err, cache.name, " cache HIT ", fqdn, " -> ", ips) + errors.LogDebugInner(ctx, err, cache.name, " cache HIT ", fqdn, " -> ", ips) log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) return ips, uint32(ttl), err } if cache.serveStale && (cache.serveExpiredTTL == 0 || cache.serveExpiredTTL < ttl) { - // errors.LogDebugInner(ctx, err, cache.name, " cache OPTIMISTE ", fqdn, " -> ", ips) + errors.LogDebugInner(ctx, err, cache.name, " cache OPTIMISTE ", fqdn, " -> ", ips) log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheOptimiste, Elapsed: 0, Error: err}) go pull(ctx, s, fqdn, option) return ips, 1, err diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go index 0dbd71bbcb9d..9557c3252519 100644 --- a/app/dns/nameserver_doh.go +++ b/app/dns/nameserver_doh.go @@ -135,7 +135,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er errors.LogInfo(ctx, s.Name(), " querying: ", fqdn) if s.Name()+"." == "DOH//"+fqdn { - errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.") + errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead") if noResponseErrCh != nil { noResponseErrCh <- errors.New("tries to resolve itself!", s.Name()) } diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go index 075bc6737529..4c7ac0329cac 100644 --- a/app/dns/nameserver_quic.go +++ b/app/dns/nameserver_quic.go @@ -37,8 +37,6 @@ type QUICNameServer struct { // NewQUICNameServer creates DNS-over-QUIC client object for local resolving func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*QUICNameServer, error) { - errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String()) - var err error port := net.Port(853) if url.Port() != "" { @@ -55,6 +53,7 @@ func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveEx clientIP: clientIP, } + errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String()) return s, nil } diff --git a/app/dns/nameserver_tcp.go b/app/dns/nameserver_tcp.go index 46f174ea650d..283a42a706f6 100644 --- a/app/dns/nameserver_tcp.go +++ b/app/dns/nameserver_tcp.go @@ -52,6 +52,7 @@ func NewTCPNameServer( ), nil } + errors.LogInfo(context.Background(), "DNS: created TCP client initialized for ", url.String()) return s, nil } @@ -66,6 +67,7 @@ func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, ser return internet.DialSystem(ctx, *s.destination, nil) } + errors.LogInfo(context.Background(), "DNS: created Local TCP client initialized for ", url.String()) return s, nil } @@ -109,7 +111,7 @@ func (s *TCPNameServer) getCacheController() *CacheController { // sendQuery implements CachedNameserver. func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) { - errors.LogDebug(ctx, s.Name(), " querying DNS for: ", fqdn) + errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn) reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) diff --git a/app/dns/nameserver_udp.go b/app/dns/nameserver_udp.go index b2adb954401a..f263a0f8d1d0 100644 --- a/app/dns/nameserver_udp.go +++ b/app/dns/nameserver_udp.go @@ -54,6 +54,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher Execute: s.RequestsCleanup, } s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse) + errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr()) return s } @@ -97,7 +98,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot ipRec, err := parseResponse(payload.Bytes()) payload.Release() if err != nil { - errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp") + errors.LogErrorInner(ctx, err, s.Name(), " fail to parse responded DNS udp") return } @@ -110,7 +111,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot } s.Unlock() if !ok { - errors.LogError(ctx, s.Name(), " cannot find the pending request") + errors.LogErrorInner(ctx, err, s.Name(), " cannot find the pending request") return } @@ -160,7 +161,7 @@ func (s *ClassicNameServer) getCacheController() *CacheController { // sendQuery implements CachedNameserver. func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn string, option dns_feature.IPOption) { - errors.LogDebug(ctx, s.Name(), " querying DNS for: ", fqdn) + errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn) reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))