dns: switch to active upstream pool and wave fallback behavior
This commit is contained in:
@@ -56,6 +56,30 @@ func handleDNSUpstreams(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleDNSUpstreamPool(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
items := loadDNSUpstreamPoolState()
|
||||
writeJSON(w, http.StatusOK, DNSUpstreamPoolState{Items: items})
|
||||
case http.MethodPost:
|
||||
var body DNSUpstreamPoolState
|
||||
if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
if err := json.NewDecoder(io.LimitReader(r.Body, 1<<20)).Decode(&body); err != nil && err != io.EOF {
|
||||
http.Error(w, "bad json", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := saveDNSUpstreamPoolState(body.Items); err != nil {
|
||||
http.Error(w, "write error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, DNSUpstreamPoolState{Items: loadDNSUpstreamPoolState()})
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `handleDNSStatus` is an HTTP handler for dns status.
|
||||
// RU: `handleDNSStatus` - HTTP-обработчик для dns status.
|
||||
@@ -95,13 +119,23 @@ func handleDNSBenchmark(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
upstreams := normalizeBenchmarkUpstreams(req.Upstreams)
|
||||
if len(upstreams) == 0 {
|
||||
cfg := loadDNSUpstreamsConf()
|
||||
upstreams = normalizeBenchmarkUpstreamStrings([]string{
|
||||
cfg.Default1,
|
||||
cfg.Default2,
|
||||
cfg.Meta1,
|
||||
cfg.Meta2,
|
||||
})
|
||||
pool := loadDNSUpstreamPoolState()
|
||||
if len(pool) > 0 {
|
||||
tmp := make([]DNSBenchmarkUpstream, 0, len(pool))
|
||||
for _, item := range pool {
|
||||
tmp = append(tmp, DNSBenchmarkUpstream{Addr: item.Addr, Enabled: item.Enabled})
|
||||
}
|
||||
upstreams = normalizeBenchmarkUpstreams(tmp)
|
||||
}
|
||||
if len(upstreams) == 0 {
|
||||
cfg := loadDNSUpstreamsConf()
|
||||
upstreams = normalizeBenchmarkUpstreamStrings([]string{
|
||||
cfg.Default1,
|
||||
cfg.Default2,
|
||||
cfg.Meta1,
|
||||
cfg.Meta2,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(upstreams) == 0 {
|
||||
http.Error(w, "no upstreams", http.StatusBadRequest)
|
||||
@@ -197,9 +231,6 @@ func normalizeBenchmarkUpstreams(in []DNSBenchmarkUpstream) []string {
|
||||
out := make([]string, 0, len(in))
|
||||
seen := map[string]struct{}{}
|
||||
for _, item := range in {
|
||||
if !item.Enabled {
|
||||
continue
|
||||
}
|
||||
n := normalizeDNSUpstream(item.Addr, "53")
|
||||
if n == "" {
|
||||
continue
|
||||
@@ -842,11 +873,7 @@ func prewarmAggressiveFromEnv() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `loadDNSUpstreamsConf` loads dns upstreams conf from storage or config.
|
||||
// RU: `loadDNSUpstreamsConf` - загружает dns upstreams conf из хранилища или конфига.
|
||||
// ---------------------------------------------------------------------
|
||||
func loadDNSUpstreamsConf() DNSUpstreams {
|
||||
func loadDNSUpstreamsConfFile() DNSUpstreams {
|
||||
cfg := DNSUpstreams{
|
||||
Default1: defaultDNS1,
|
||||
Default2: defaultDNS2,
|
||||
@@ -903,11 +930,139 @@ func loadDNSUpstreamsConf() DNSUpstreams {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `saveDNSUpstreamsConf` saves dns upstreams conf to persistent storage.
|
||||
// RU: `saveDNSUpstreamsConf` - сохраняет dns upstreams conf в постоянное хранилище.
|
||||
// ---------------------------------------------------------------------
|
||||
func saveDNSUpstreamsConf(cfg DNSUpstreams) error {
|
||||
func normalizeDNSUpstreamPoolItems(items []DNSUpstreamPoolItem) []DNSUpstreamPoolItem {
|
||||
out := make([]DNSUpstreamPoolItem, 0, len(items))
|
||||
seen := map[string]struct{}{}
|
||||
for _, item := range items {
|
||||
addr := normalizeDNSUpstream(item.Addr, "53")
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[addr]; ok {
|
||||
continue
|
||||
}
|
||||
seen[addr] = struct{}{}
|
||||
out = append(out, DNSUpstreamPoolItem{
|
||||
Addr: addr,
|
||||
Enabled: item.Enabled,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func dnsUpstreamPoolFromLegacy(cfg DNSUpstreams) []DNSUpstreamPoolItem {
|
||||
raw := []string{cfg.Default1, cfg.Default2, cfg.Meta1, cfg.Meta2}
|
||||
out := make([]DNSUpstreamPoolItem, 0, len(raw))
|
||||
for _, item := range raw {
|
||||
n := normalizeDNSUpstream(item, "53")
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, DNSUpstreamPoolItem{Addr: n, Enabled: true})
|
||||
}
|
||||
return normalizeDNSUpstreamPoolItems(out)
|
||||
}
|
||||
|
||||
func dnsUpstreamPoolToLegacy(items []DNSUpstreamPoolItem) DNSUpstreams {
|
||||
enabled := make([]string, 0, len(items))
|
||||
all := make([]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
n := normalizeDNSUpstream(item.Addr, "53")
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
all = append(all, n)
|
||||
if item.Enabled {
|
||||
enabled = append(enabled, n)
|
||||
}
|
||||
}
|
||||
list := enabled
|
||||
if len(list) == 0 {
|
||||
list = all
|
||||
}
|
||||
if len(list) == 0 {
|
||||
list = []string{defaultDNS1, defaultDNS2, defaultMeta1, defaultMeta2}
|
||||
}
|
||||
pick := func(idx int, fallback string) string {
|
||||
if len(list) == 0 {
|
||||
return fallback
|
||||
}
|
||||
if idx < len(list) {
|
||||
return list[idx]
|
||||
}
|
||||
return list[idx%len(list)]
|
||||
}
|
||||
return DNSUpstreams{
|
||||
Default1: pick(0, defaultDNS1),
|
||||
Default2: pick(1, defaultDNS2),
|
||||
Meta1: pick(2, defaultMeta1),
|
||||
Meta2: pick(3, defaultMeta2),
|
||||
}
|
||||
}
|
||||
|
||||
func saveDNSUpstreamPoolFile(items []DNSUpstreamPoolItem) error {
|
||||
state := DNSUpstreamPoolState{Items: normalizeDNSUpstreamPoolItems(items)}
|
||||
if err := os.MkdirAll(filepath.Dir(dnsUpstreamPool), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
tmp := dnsUpstreamPool + ".tmp"
|
||||
b, err := json.MarshalIndent(state, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(tmp, b, 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, dnsUpstreamPool)
|
||||
}
|
||||
|
||||
func loadDNSUpstreamPoolState() []DNSUpstreamPoolItem {
|
||||
data, err := os.ReadFile(dnsUpstreamPool)
|
||||
if err == nil {
|
||||
var st DNSUpstreamPoolState
|
||||
if json.Unmarshal(data, &st) == nil {
|
||||
items := normalizeDNSUpstreamPoolItems(st.Items)
|
||||
if len(items) > 0 {
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
legacy := loadDNSUpstreamsConfFile()
|
||||
items := dnsUpstreamPoolFromLegacy(legacy)
|
||||
if len(items) > 0 {
|
||||
_ = saveDNSUpstreamPoolFile(items)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func saveDNSUpstreamPoolState(items []DNSUpstreamPoolItem) error {
|
||||
items = normalizeDNSUpstreamPoolItems(items)
|
||||
if len(items) == 0 {
|
||||
items = dnsUpstreamPoolFromLegacy(loadDNSUpstreamsConfFile())
|
||||
}
|
||||
if err := saveDNSUpstreamPoolFile(items); err != nil {
|
||||
return err
|
||||
}
|
||||
return saveDNSUpstreamsConfFile(dnsUpstreamPoolToLegacy(items))
|
||||
}
|
||||
|
||||
func loadEnabledDNSUpstreamPool() []string {
|
||||
items := loadDNSUpstreamPoolState()
|
||||
out := make([]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
if !item.Enabled {
|
||||
continue
|
||||
}
|
||||
n := normalizeDNSUpstream(item.Addr, "53")
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, n)
|
||||
}
|
||||
return uniqueStrings(out)
|
||||
}
|
||||
|
||||
func saveDNSUpstreamsConfFile(cfg DNSUpstreams) error {
|
||||
cfg.Default1 = normalizeDNSUpstream(cfg.Default1, "53")
|
||||
cfg.Default2 = normalizeDNSUpstream(cfg.Default2, "53")
|
||||
cfg.Meta1 = normalizeDNSUpstream(cfg.Meta1, "53")
|
||||
@@ -947,10 +1102,32 @@ func saveDNSUpstreamsConf(cfg DNSUpstreams) error {
|
||||
if b, err := json.MarshalIndent(cfg, "", " "); err == nil {
|
||||
_ = os.WriteFile(dnsUpstreamsPath, b, 0o644)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `loadDNSUpstreamsConf` loads dns upstreams conf from storage or config.
|
||||
// RU: `loadDNSUpstreamsConf` - загружает dns upstreams conf из хранилища или конфига.
|
||||
// ---------------------------------------------------------------------
|
||||
func loadDNSUpstreamsConf() DNSUpstreams {
|
||||
pool := loadDNSUpstreamPoolState()
|
||||
if len(pool) > 0 {
|
||||
return dnsUpstreamPoolToLegacy(pool)
|
||||
}
|
||||
return loadDNSUpstreamsConfFile()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `saveDNSUpstreamsConf` saves dns upstreams conf to persistent storage.
|
||||
// RU: `saveDNSUpstreamsConf` - сохраняет dns upstreams conf в постоянное хранилище.
|
||||
// ---------------------------------------------------------------------
|
||||
func saveDNSUpstreamsConf(cfg DNSUpstreams) error {
|
||||
if err := saveDNSUpstreamsConfFile(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
return saveDNSUpstreamPoolFile(dnsUpstreamPoolFromLegacy(cfg))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `loadDNSMode` loads dns mode from storage or config.
|
||||
// RU: `loadDNSMode` - загружает dns mode из хранилища или конфига.
|
||||
|
||||
Reference in New Issue
Block a user