diff --git a/dnscrypt-proxy/config.go b/dnscrypt-proxy/config.go index 08fc3010..7b73c181 100644 --- a/dnscrypt-proxy/config.go +++ b/dnscrypt-proxy/config.go @@ -42,6 +42,7 @@ type Config struct { Timeout int `toml:"timeout"` KeepAlive int `toml:"keepalive"` Proxy string `toml:"proxy"` + CertRefreshConcurrency int `toml:"cert_refresh_concurrency"` CertRefreshDelay int `toml:"cert_refresh_delay"` CertIgnoreTimestamp bool `toml:"cert_ignore_timestamp"` EphemeralKeys bool `toml:"dnscrypt_ephemeral_keys"` @@ -116,6 +117,7 @@ func newConfig() Config { LocalDoH: LocalDoHConfig{Path: "/dns-query"}, Timeout: 5000, KeepAlive: 5, + CertRefreshConcurrency: 10, CertRefreshDelay: 240, HTTP3: false, CertIgnoreTimestamp: false, @@ -437,6 +439,7 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error { if config.ForceTCP { proxy.mainProto = "tcp" } + proxy.certRefreshConcurrency = Max(1, config.CertRefreshConcurrency) proxy.certRefreshDelay = time.Duration(Max(60, config.CertRefreshDelay)) * time.Minute proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second) proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp diff --git a/dnscrypt-proxy/example-dnscrypt-proxy.toml b/dnscrypt-proxy/example-dnscrypt-proxy.toml index 0f973adb..7819cb8a 100644 --- a/dnscrypt-proxy/example-dnscrypt-proxy.toml +++ b/dnscrypt-proxy/example-dnscrypt-proxy.toml @@ -183,6 +183,12 @@ keepalive = 30 # use_syslog = true +## The maximum concurrency to reload certificates from the resolvers. +## Default is 10. + +# cert_refresh_concurrency = 10 + + ## Delay, in minutes, after which certificates are reloaded cert_refresh_delay = 240 diff --git a/dnscrypt-proxy/proxy.go b/dnscrypt-proxy/proxy.go index b2ef5e9a..cb9442a7 100644 --- a/dnscrypt-proxy/proxy.go +++ b/dnscrypt-proxy/proxy.go @@ -74,6 +74,7 @@ type Proxy struct { certRefreshDelayAfterFailure time.Duration timeout time.Duration certRefreshDelay time.Duration + certRefreshConcurrency int cacheSize int logMaxBackups int logMaxAge int diff --git a/dnscrypt-proxy/serversInfo.go b/dnscrypt-proxy/serversInfo.go index edbfeb43..d247583c 100644 --- a/dnscrypt-proxy/serversInfo.go +++ b/dnscrypt-proxy/serversInfo.go @@ -224,17 +224,34 @@ func (serversInfo *ServersInfo) refresh(proxy *Proxy) (int, error) { dlog.Debug("Refreshing certificates") serversInfo.RLock() // Appending registeredServers slice from sources may allocate new memory. - registeredServers := make([]RegisteredServer, len(serversInfo.registeredServers)) + serversCount := len(serversInfo.registeredServers) + registeredServers := make([]RegisteredServer, serversCount) copy(registeredServers, serversInfo.registeredServers) serversInfo.RUnlock() + countChannel := make(chan struct{}, proxy.certRefreshConcurrency) + errorChannel := make(chan error, serversCount) + 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 + } + errorChannel <- err + <-countChannel + }(®isteredServers[i]) + } liveServers := 0 var err error - for _, registeredServer := range registeredServers { - if err = serversInfo.refreshServer(proxy, registeredServer.name, registeredServer.stamp); err == nil { + for i := 0; i < serversCount; i++ { + err = <-errorChannel + if err == nil { liveServers++ - proxy.xTransport.internalResolverReady = true } } + if liveServers > 0 { + err = nil + } serversInfo.Lock() sort.SliceStable(serversInfo.inner, func(i, j int) bool { return serversInfo.inner[i].initialRtt < serversInfo.inner[j].initialRtt