From 7cdbc70938942514fdbf4e3679bb730eb242b1b2 Mon Sep 17 00:00:00 2001 From: YX Hao Date: Thu, 9 Oct 2025 18:47:23 +0800 Subject: [PATCH 1/2] Create completely new RegisteredServers when updateRegisteredServers() Exclude servers/relays that have been removed from the sources, had their privacy level lowered, or IPv6 has became unavailable while enabled IPv6 servers. --- dnscrypt-proxy/config.go | 4 +- dnscrypt-proxy/proxy.go | 174 +++++++++++++++++++++------------- dnscrypt-proxy/serversInfo.go | 16 ++++ 3 files changed, 126 insertions(+), 68 deletions(-) diff --git a/dnscrypt-proxy/config.go b/dnscrypt-proxy/config.go index 52033b45df..e936aba5fa 100644 --- a/dnscrypt-proxy/config.go +++ b/dnscrypt-proxy/config.go @@ -664,7 +664,7 @@ func (config *Config) loadSources(proxy *Proxy) error { if stamp.Proto == stamps.StampProtoTypeDNSCryptRelay || stamp.Proto == stamps.StampProtoTypeODoHRelay { dlog.Debugf("Adding [%s] to the set of available static relays", name) registeredServer := RegisteredServer{name: name, stamp: stamp, description: "static relay"} - proxy.registeredRelays = append(proxy.registeredRelays, registeredServer) + proxy.staticRelays = append(proxy.staticRelays, registeredServer) } } } @@ -685,7 +685,7 @@ func (config *Config) loadSources(proxy *Proxy) error { if err != nil { return fmt.Errorf("Stamp error for the static [%s] definition: [%v]", serverName, err) } - proxy.registeredServers = append(proxy.registeredServers, RegisteredServer{name: serverName, stamp: stamp}) + proxy.staticServers = append(proxy.staticServers, RegisteredServer{name: serverName, stamp: stamp}) } if err := proxy.updateRegisteredServers(); err != nil { return err diff --git a/dnscrypt-proxy/proxy.go b/dnscrypt-proxy/proxy.go index c971028cef..e72e0552c7 100644 --- a/dnscrypt-proxy/proxy.go +++ b/dnscrypt-proxy/proxy.go @@ -23,6 +23,7 @@ type Proxy struct { serversInfo ServersInfo questionSizeEstimator QuestionSizeEstimator registeredServers []RegisteredServer + staticServers []RegisteredServer dns64Resolvers []string dns64Prefixes []string serversBlockingFragments []string @@ -35,6 +36,7 @@ type Proxy struct { sources []*Source tcpListeners []*net.TCPListener registeredRelays []RegisteredServer + staticRelays []RegisteredServer listenAddresses []string localDoHListenAddresses []string monitoringUI MonitoringUIConfig @@ -336,10 +338,12 @@ func (proxy *Proxy) StartProxy() { } func (proxy *Proxy) updateRegisteredServers() error { + registeredServersNew := make([]RegisteredServer, 0) + registeredRelaysNew := make([]RegisteredServer, 0) for _, source := range proxy.sources { - registeredServers, err := source.Parse() + servers, err := source.Parse() if err != nil { - if len(registeredServers) == 0 { + if len(servers) == 0 { dlog.Criticalf("Unable to use source [%s]: [%s]", source.name, err) return err } @@ -347,88 +351,126 @@ func (proxy *Proxy) updateRegisteredServers() error { "Error in source [%s]: [%s] -- Continuing with reduced server count [%d]", source.name, err, - len(registeredServers), + len(servers), ) } - for _, registeredServer := range registeredServers { - if registeredServer.stamp.Proto != stamps.StampProtoTypeDNSCryptRelay && - registeredServer.stamp.Proto != stamps.StampProtoTypeODoHRelay { - if len(proxy.ServerNames) > 0 { - if !includesName(proxy.ServerNames, registeredServer.name) { - continue - } - } else if registeredServer.stamp.Props&proxy.requiredProps != proxy.requiredProps { + registeredServersNew, registeredRelaysNew = proxy.buildRegisteredServers(servers, registeredServersNew, registeredRelaysNew) + } + if len(proxy.staticServers) > 0 { + registeredServersNew = append(registeredServersNew, proxy.staticServers...) + } + if len(proxy.staticRelays) > 0 { + registeredRelaysNew = append(registeredRelaysNew, proxy.staticRelays...) + } + if len(registeredServersNew) > 0 { + proxy.registeredServers = registeredServersNew + } + if len(registeredRelaysNew) > 0 { + proxy.registeredRelays = registeredRelaysNew + } + dlog.Debugf("Total count of registered servers %v", len(proxy.registeredServers)) + dlog.Debugf("Total count of registered relays %v", len(proxy.registeredRelays)) + proxy.serversInfo.registerServers(proxy.registeredServers) + proxy.serversInfo.registerRelays(proxy.registeredRelays) + return nil +} + +func (proxy *Proxy) buildRegisteredServers(servers, registeredServersNew, registeredRelaysNew []RegisteredServer) (serversAppended, relaysAppended []RegisteredServer) { + for _, server := range servers { + if server.stamp.Proto != stamps.StampProtoTypeDNSCryptRelay && + server.stamp.Proto != stamps.StampProtoTypeODoHRelay { + if len(proxy.ServerNames) > 0 { + if !includesName(proxy.ServerNames, server.name) { continue } + } else if server.stamp.Props&proxy.requiredProps != proxy.requiredProps { + continue } - if includesName(proxy.DisabledServerNames, registeredServer.name) { + } + if includesName(proxy.DisabledServerNames, server.name) { + continue + } + if proxy.SourceIPv4 || proxy.SourceIPv6 { + isIPv4, isIPv6 := true, false + if server.stamp.Proto == stamps.StampProtoTypeDoH { + isIPv4, isIPv6 = true, true + } + if strings.HasPrefix(server.stamp.ServerAddrStr, "[") { + isIPv4, isIPv6 = false, true + } + if !(proxy.SourceIPv4 == isIPv4 || proxy.SourceIPv6 == isIPv6) { continue } - if proxy.SourceIPv4 || proxy.SourceIPv6 { - isIPv4, isIPv6 := true, false - if registeredServer.stamp.Proto == stamps.StampProtoTypeDoH { - isIPv4, isIPv6 = true, true - } - if strings.HasPrefix(registeredServer.stamp.ServerAddrStr, "[") { - isIPv4, isIPv6 = false, true - } - if !(proxy.SourceIPv4 == isIPv4 || proxy.SourceIPv6 == isIPv6) { + } + if server.stamp.Proto == stamps.StampProtoTypeDNSCryptRelay || + server.stamp.Proto == stamps.StampProtoTypeODoHRelay { + isNew, stampChanged, dupNewInstance := checkServerForNewBuild(&server, proxy.registeredRelays, registeredRelaysNew) + if dupNewInstance != nil { + dupNewInstance.stamp = server.stamp + } else { + registeredRelaysNew = append(registeredRelaysNew, server) + if isNew { + dlog.Debugf("Adding [%s] to the set of available relays", server.name) + } else if !stampChanged { continue } } - if registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCryptRelay || - registeredServer.stamp.Proto == stamps.StampProtoTypeODoHRelay { - var found bool - for i, currentRegisteredRelay := range proxy.registeredRelays { - if currentRegisteredRelay.name == registeredServer.name { - found = true - if currentRegisteredRelay.stamp.String() != registeredServer.stamp.String() { - dlog.Infof( - "Updating stamp for [%s] was: %s now: %s", - registeredServer.name, - currentRegisteredRelay.stamp.String(), - registeredServer.stamp.String(), - ) - proxy.registeredRelays[i].stamp = registeredServer.stamp - dlog.Debugf("Total count of registered relays %v", len(proxy.registeredRelays)) - } - } - } - if !found { - dlog.Debugf("Adding [%s] to the set of available relays", registeredServer.name) - proxy.registeredRelays = append(proxy.registeredRelays, registeredServer) - } + dlog.Debugf("Count of registered relays %v", len(registeredRelaysNew)) + } else { + if !((proxy.SourceDNSCrypt && server.stamp.Proto == stamps.StampProtoTypeDNSCrypt) || + (proxy.SourceDoH && server.stamp.Proto == stamps.StampProtoTypeDoH) || + (proxy.SourceODoH && server.stamp.Proto == stamps.StampProtoTypeODoHTarget)) { + continue + } + isNew, stampChanged, dupNewInstance := checkServerForNewBuild(&server, proxy.registeredServers, registeredServersNew) + if dupNewInstance != nil { + dupNewInstance.stamp = server.stamp } else { - if !((proxy.SourceDNSCrypt && registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCrypt) || - (proxy.SourceDoH && registeredServer.stamp.Proto == stamps.StampProtoTypeDoH) || - (proxy.SourceODoH && registeredServer.stamp.Proto == stamps.StampProtoTypeODoHTarget)) { + registeredServersNew = append(registeredServersNew, server) + if isNew { + dlog.Debugf("Adding [%s] to the set of wanted resolvers", server.name) + } else if !stampChanged { continue } - var found bool - for i, currentRegisteredServer := range proxy.registeredServers { - if currentRegisteredServer.name == registeredServer.name { - found = true - if currentRegisteredServer.stamp.String() != registeredServer.stamp.String() { - dlog.Infof("Updating stamp for [%s] was: %s now: %s", registeredServer.name, currentRegisteredServer.stamp.String(), registeredServer.stamp.String()) - proxy.registeredServers[i].stamp = registeredServer.stamp - } - } - } - if !found { - dlog.Debugf("Adding [%s] to the set of wanted resolvers", registeredServer.name) - proxy.registeredServers = append(proxy.registeredServers, registeredServer) - dlog.Debugf("Total count of registered servers %v", len(proxy.registeredServers)) - } } + dlog.Debugf("Count of registered servers %v", len(registeredServersNew)) } } - for _, registeredServer := range proxy.registeredServers { - proxy.serversInfo.registerServer(registeredServer.name, registeredServer.stamp) + return registeredServersNew, registeredRelaysNew +} + +func checkServerForNewBuild(server *RegisteredServer, serversCur, serversNew []RegisteredServer) (isNew, stampChanged bool, dupNewInstance *RegisteredServer) { + foundServer, stampChanged := serverStampChanged(serversCur, server) + if foundServer == nil || stampChanged { + // in case the same name exists in (different) sources + dupNewInstance, stampChanged2 := serverStampChanged(serversNew, server) + isNew = foundServer == nil && dupNewInstance == nil + if dupNewInstance != nil { + foundServer = dupNewInstance + } + stampChanged = stampChanged || stampChanged2 } - for _, registeredRelay := range proxy.registeredRelays { - proxy.serversInfo.registerRelay(registeredRelay.name, registeredRelay.stamp) + if stampChanged { + dlog.Infof( + "Updating stamp for [%s] was: %s now: %s", + server.name, + foundServer.stamp.String(), + server.stamp.String(), + ) } - return nil + return isNew, stampChanged, dupNewInstance +} + +func serverStampChanged(servers []RegisteredServer, server *RegisteredServer) (found *RegisteredServer, changed bool) { + for _, s := range servers { + if s.name == server.name { + found = &s + if s.stamp.String() != server.stamp.String() { + changed = true + } + } + } + return found, changed } func (proxy *Proxy) udpListener(clientPc *net.UDPConn) { diff --git a/dnscrypt-proxy/serversInfo.go b/dnscrypt-proxy/serversInfo.go index b696bb0866..a550eba1cc 100644 --- a/dnscrypt-proxy/serversInfo.go +++ b/dnscrypt-proxy/serversInfo.go @@ -175,6 +175,14 @@ func NewServersInfo() ServersInfo { } } +func (serversInfo *ServersInfo) registerServers(registeredServers []RegisteredServer) { + rs := make([]RegisteredServer, len(registeredServers)) + copy(rs, registeredServers) + serversInfo.Lock() + serversInfo.registeredServers = rs + serversInfo.Unlock() +} + func (serversInfo *ServersInfo) registerServer(name string, stamp stamps.ServerStamp) { newRegisteredServer := RegisteredServer{name: name, stamp: stamp} serversInfo.Lock() @@ -188,6 +196,14 @@ func (serversInfo *ServersInfo) registerServer(name string, stamp stamps.ServerS serversInfo.registeredServers = append(serversInfo.registeredServers, newRegisteredServer) } +func (serversInfo *ServersInfo) registerRelays(registeredRelays []RegisteredServer) { + rr := make([]RegisteredServer, len(registeredRelays)) + copy(rr, registeredRelays) + serversInfo.Lock() + serversInfo.registeredRelays = rr + serversInfo.Unlock() +} + func (serversInfo *ServersInfo) registerRelay(name string, stamp stamps.ServerStamp) { newRegisteredServer := RegisteredServer{name: name, stamp: stamp} serversInfo.Lock() From 54d59fb85aa2982c1134b6ddd22ab4d2c17eaeb2 Mon Sep 17 00:00:00 2001 From: YX Hao Date: Thu, 13 Nov 2025 21:14:20 +0800 Subject: [PATCH 2/2] Create completely new ServersInfo.inner when refresh() Exclude servers/relays that have been removed from the sources, had their privacy level lowered, or IPv6 has became unavailable while enabled IPv6 servers. --- dnscrypt-proxy/serversInfo.go | 42 ++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/dnscrypt-proxy/serversInfo.go b/dnscrypt-proxy/serversInfo.go index a550eba1cc..ad874e69d8 100644 --- a/dnscrypt-proxy/serversInfo.go +++ b/dnscrypt-proxy/serversInfo.go @@ -217,7 +217,7 @@ func (serversInfo *ServersInfo) registerRelay(name string, stamp stamps.ServerSt serversInfo.registeredRelays = append(serversInfo.registeredRelays, newRegisteredServer) } -func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp stamps.ServerStamp) error { +func (serversInfo *ServersInfo) initServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp) (*ServerInfo, bool, error) { serversInfo.RLock() isNew := true for _, oldServer := range serversInfo.inner { @@ -229,18 +229,25 @@ func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp s serversInfo.RUnlock() newServer, err := fetchServerInfo(proxy, name, stamp, isNew) if err != nil { - return err + return nil, isNew, err } if name != newServer.Name { dlog.Fatalf("[%s] != [%s]", name, newServer.Name) } newServer.rtt = ewma.NewMovingAverage(RTTEwmaDecay) newServer.rtt.Set(float64(newServer.initialRtt)) - isNew = true + return &newServer, isNew, err +} + +func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp stamps.ServerStamp) error { + newServer, isNew, err := serversInfo.initServerInfo(proxy, name, stamp) + if err != nil { + return err + } serversInfo.Lock() for i, oldServer := range serversInfo.inner { if oldServer.Name == name { - serversInfo.inner[i] = &newServer + serversInfo.inner[i] = newServer isNew = false break } @@ -248,7 +255,7 @@ func (serversInfo *ServersInfo) refreshServer(proxy *Proxy, name string, stamp s serversInfo.Unlock() if isNew { serversInfo.Lock() - serversInfo.inner = append(serversInfo.inner, &newServer) + serversInfo.inner = append(serversInfo.inner, newServer) serversInfo.Unlock() proxy.serversInfo.registerServer(name, stamp) } @@ -266,12 +273,21 @@ func (serversInfo *ServersInfo) refresh(proxy *Proxy) (int, error) { serversInfo.RUnlock() countChannel := make(chan struct{}, proxy.certRefreshConcurrency) errorChannel := make(chan error, serversCount) + serverInfoChannel := make(chan *ServerInfo, serversCount) + rebuildInner := len(serversInfo.inner) > 0 for i := range registeredServers { countChannel <- struct{}{} go func(registeredServer *RegisteredServer) { - err := serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp) - if err == nil { - proxy.xTransport.internalResolverReady = true + var serverInfo *ServerInfo + var err error + if rebuildInner { + serverInfo, _, err = serversInfo.initServerInfo(proxy, registeredServer.name, registeredServer.stamp) + serverInfoChannel <- serverInfo + } else { + err = serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp) + if err == nil { + proxy.xTransport.internalResolverReady = true + } } errorChannel <- err <-countChannel @@ -279,16 +295,26 @@ func (serversInfo *ServersInfo) refresh(proxy *Proxy) (int, error) { } liveServers := 0 var err error + var innerRefresh []*ServerInfo for i := 0; i < serversCount; i++ { err = <-errorChannel if err == nil { liveServers++ } + if rebuildInner { + serverInfo := <-serverInfoChannel + if serverInfo != nil { + innerRefresh = append(innerRefresh, serverInfo) + } + } } if liveServers > 0 { err = nil } serversInfo.Lock() + if len(innerRefresh) > 0 { + serversInfo.inner = innerRefresh + } sort.SliceStable(serversInfo.inner, func(i, j int) bool { return serversInfo.inner[i].initialRtt < serversInfo.inner[j].initialRtt })