From 9ea5c1abe16b99f80ae2a92ccdd094b5a7ea2695 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 20 Mar 2019 14:24:33 +0300 Subject: [PATCH 1/3] + control, dns, client: add ability to set DNS upstream per domain --- client/src/__locales/en.json | 1 + .../components/Settings/Upstream/Examples.js | 3 + .../src/components/Settings/Upstream/Form.js | 5 + .../src/components/Settings/Upstream/index.js | 3 - control.go | 100 +++++++++++++++--- control_test.go | 76 +++++++++++++ dns.go | 19 ++-- dnsforward/dnsforward.go | 28 ++--- go.mod | 4 +- go.sum | 10 +- 10 files changed, 200 insertions(+), 49 deletions(-) diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 92540ea6..74c2fa50 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -122,6 +122,7 @@ "example_upstream_doh": "encrypted DNS-over-HTTPS<\/a>", "example_upstream_sdns": "you can use DNS Stamps<\/a> for DNSCrypt<\/a> or DNS-over-HTTPS<\/a> resolvers", "example_upstream_tcp": "regular DNS (over TCP)", + "example_upstream_reserved": "you can specify DNS upstream for a specific domain(s)<\/a>", "all_filters_up_to_date_toast": "All filters are already up-to-date", "updated_upstream_dns_toast": "Updated the upstream DNS servers", "dns_test_ok_toast": "Specified DNS servers are working correctly", diff --git a/client/src/components/Settings/Upstream/Examples.js b/client/src/components/Settings/Upstream/Examples.js index 4ba54852..9d61b0de 100644 --- a/client/src/components/Settings/Upstream/Examples.js +++ b/client/src/components/Settings/Upstream/Examples.js @@ -21,6 +21,9 @@ const Examples = props => (
  • sdns://... -
  • +
  • + [/host.com/]1.1.1.1 - +
  • ); diff --git a/client/src/components/Settings/Upstream/Form.js b/client/src/components/Settings/Upstream/Form.js index 245e7d27..8ef916f5 100644 --- a/client/src/components/Settings/Upstream/Form.js +++ b/client/src/components/Settings/Upstream/Form.js @@ -7,6 +7,7 @@ import flow from 'lodash/flow'; import classnames from 'classnames'; import { renderSelectField } from '../../../helpers/form'; +import Examples from './Examples'; let Form = (props) => { const { @@ -55,6 +56,10 @@ let Form = (props) => { /> +
    + +
    +
    diff --git a/control.go b/control.go index 0bd7a5d7..952395a1 100644 --- a/control.go +++ b/control.go @@ -18,6 +18,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/dnsforward" "github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/golibs/log" + "github.com/AdguardTeam/golibs/utils" "github.com/miekg/dns" govalidator "gopkg.in/asaskevich/govalidator.v4" ) @@ -317,11 +318,10 @@ func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) { return } - for _, u := range newconfig.Upstreams { - if err = validateUpstream(u); err != nil { - httpError(w, http.StatusBadRequest, "%s can not be used as upstream cause: %s", u, err) - return - } + err = validateUpstreams(newconfig.Upstreams) + if err != nil { + httpError(w, http.StatusBadRequest, "wrong upstreams specification: %s", err) + return } config.DNS.UpstreamDNS = defaultDNS @@ -346,18 +346,81 @@ func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) { httpUpdateConfigReloadDNSReturnOK(w, r) } -func validateUpstream(upstream string) error { - for _, proto := range protocols { - if strings.HasPrefix(upstream, proto) { - return nil +// validateUpstreams validates each upstream and returns an error if any upstream is invalid or if there are no default upstreams specified +func validateUpstreams(upstreams []string) error { + var defaultUpstreamFound bool + for _, u := range upstreams { + d, err := validateUpstream(u) + if err != nil { + return err + } + + // Check this flag until default upstream will not be found + if !defaultUpstreamFound { + defaultUpstreamFound = d } } - if strings.Contains(upstream, "://") { - return fmt.Errorf("wrong protocol") + // Return error if there are no default upstreams + if !defaultUpstreamFound { + return fmt.Errorf("no default upstreams specified") } - return checkPlainDNS(upstream) + return nil +} + +func validateUpstream(u string) (defaultUpstream bool, err error) { + // Check if user tries to specify upstream for domain + defaultUpstream = true + u, defaultUpstream, err = separateUpstream(u) + if err != nil { + return + } + + // The special server address '#' means "use the default servers" + if u == "#" && !defaultUpstream { + return + } + + // Check if the upstream has a valid protocol prefix + for _, proto := range protocols { + if strings.HasPrefix(u, proto) { + return + } + } + + // Return error if the upstream contains '://' without any valid protocol + if strings.Contains(u, "://") { + return defaultUpstream, fmt.Errorf("wrong protocol") + } + + // Check if upstream is valid plain DNS + return defaultUpstream, checkPlainDNS(u) +} + +// separateUpstream returns upstream without specified domains and a bool flag that indicates if no domains were specified +// error will be returned if upstream per domain specification is invalid +func separateUpstream(upstream string) (string, bool, error) { + defaultUpstream := true + if strings.HasPrefix(upstream, "[/") { + defaultUpstream = false + // split domains and upstream string + domainsAndUpstream := strings.Split(strings.TrimPrefix(upstream, "[/"), "/]") + if len(domainsAndUpstream) != 2 { + return "", defaultUpstream, fmt.Errorf("wrong DNS upstream per domain specification: %s", upstream) + } + + // split domains list and validate each one + for _, host := range strings.Split(domainsAndUpstream[0], "/") { + if host != "" { + if err := utils.IsValidHostname(host); err != nil { + return "", defaultUpstream, err + } + } + } + upstream = domainsAndUpstream[1] + } + return upstream, defaultUpstream, nil } // checkPlainDNS checks if host is plain DNS @@ -425,7 +488,18 @@ func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { } func checkDNS(input string, bootstrap []string) error { - if err := validateUpstream(input); err != nil { + // separate upstream from domains list + input, defaultUpstream, err := separateUpstream(input) + if err != nil { + return fmt.Errorf("wrong upstream format: %s", err) + } + + // No need to check this entrance + if input == "#" && !defaultUpstream { + return nil + } + + if _, err := validateUpstream(input); err != nil { return fmt.Errorf("wrong upstream format: %s", err) } diff --git a/control_test.go b/control_test.go index b823b252..c04e1a2f 100644 --- a/control_test.go +++ b/control_test.go @@ -75,3 +75,79 @@ kXS9jgARhhiWXJrk t.Fatalf("valid cert & priv key: validateCertificates(): %v", data) } } + +func TestValidateUpstream(t *testing.T) { + invalidUpstreams := []string{"1.2.3.4.5", + "123.3.7m", + "htttps://google.com/dns-query", + "[/host.com]tls://dns.adguard.com", + "[host.ru]#", + } + + validDefaultUpstreams := []string{"1.1.1.1", + "tls://1.1.1.1", + "https://dns.adguard.com/dns-query", + "sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", + } + + validUpstreams := []string{"[/host.com/]1.1.1.1", + "[//]tls://1.1.1.1", + "[/www.host.com/]#", + "[/host.com/google.com/]8.8.8.8", + "[/host/]sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", + } + for _, u := range invalidUpstreams { + _, err := validateUpstream(u) + if err == nil { + t.Fatalf("upstream %s is invalid but it pass through validation", u) + } + } + + for _, u := range validDefaultUpstreams { + defaultUpstream, err := validateUpstream(u) + if err != nil { + t.Fatalf("upstream %s is valid but it doen't pass through validation cause: %s", u, err) + } + if !defaultUpstream { + t.Fatalf("upstream %s is default one!", u) + } + } + + for _, u := range validUpstreams { + defaultUpstream, err := validateUpstream(u) + if err != nil { + t.Fatalf("upstream %s is valid but it doen't pass through validation cause: %s", u, err) + } + if defaultUpstream { + t.Fatalf("upstream %s is default one!", u) + } + } +} + +func TestValidateUpstreamsSet(t *testing.T) { + // Set of valid upstreams. There is no default upstream specified + upstreamsSet := []string{"[/host.com/]1.1.1.1", + "[//]tls://1.1.1.1", + "[/www.host.com/]#", + "[/host.com/google.com/]8.8.8.8", + "[/host/]sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", + } + err := validateUpstreams(upstreamsSet) + if err == nil { + t.Fatalf("there is no default upstream") + } + + // Let's add default upstream + upstreamsSet = append(upstreamsSet, "8.8.8.8") + err = validateUpstreams(upstreamsSet) + if err != nil { + t.Fatalf("upstreams set is valid, but doesn't pass through validation cause: %s", err) + } + + // Let's add invalid upstream + upstreamsSet = append(upstreamsSet, "dhcp://fake.dns") + err = validateUpstreams(upstreamsSet) + if err == nil { + t.Fatalf("there is an invalid upstream in set, but it pass through validation") + } +} diff --git a/dns.go b/dns.go index 56fae956..9abbc80e 100644 --- a/dns.go +++ b/dns.go @@ -7,7 +7,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/dnsfilter" "github.com/AdguardTeam/AdGuardHome/dnsforward" - "github.com/AdguardTeam/dnsproxy/upstream" + "github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/golibs/log" "github.com/joomcode/errorx" ) @@ -58,19 +58,12 @@ func generateServerConfig() dnsforward.ServerConfig { } } - for _, u := range config.DNS.UpstreamDNS { - opts := upstream.Options{ - Timeout: dnsforward.DefaultTimeout, - Bootstrap: config.DNS.BootstrapDNS, - } - dnsUpstream, err := upstream.AddressToUpstream(u, opts) - if err != nil { - log.Printf("Couldn't get upstream: %s", err) - // continue, just ignore the upstream - continue - } - newconfig.Upstreams = append(newconfig.Upstreams, dnsUpstream) + upstreamConfig, err := proxy.ParseUpstreamsConfig(config.DNS.UpstreamDNS, config.DNS.BootstrapDNS, dnsforward.DefaultTimeout) + if err != nil { + log.Error("Couldn't get upstreams configuration cause: %s", err) } + newconfig.Upstreams = upstreamConfig.Upstreams + newconfig.DomainsReservedUpstreams = upstreamConfig.DomainReservedUpstreams newconfig.AllServers = config.DNS.AllServers return newconfig } diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 7b8430ed..6404a995 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -82,10 +82,11 @@ type TLSConfig struct { // ServerConfig represents server configuration. // The zero ServerConfig is empty and ready for use. type ServerConfig struct { - UDPListenAddr *net.UDPAddr // UDP listen address - TCPListenAddr *net.TCPAddr // TCP listen address - Upstreams []upstream.Upstream // Configured upstreams - Filters []dnsfilter.Filter // A list of filters to use + UDPListenAddr *net.UDPAddr // UDP listen address + TCPListenAddr *net.TCPAddr // TCP listen address + Upstreams []upstream.Upstream // Configured upstreams + DomainsReservedUpstreams map[string][]upstream.Upstream // Map of domains and lists of configured upstreams + Filters []dnsfilter.Filter // A list of filters to use FilteringConfig TLSConfig @@ -156,15 +157,16 @@ func (s *Server) startInternal(config *ServerConfig) error { }) proxyConfig := proxy.Config{ - UDPListenAddr: s.UDPListenAddr, - TCPListenAddr: s.TCPListenAddr, - Ratelimit: s.Ratelimit, - RatelimitWhitelist: s.RatelimitWhitelist, - RefuseAny: s.RefuseAny, - CacheEnabled: true, - Upstreams: s.Upstreams, - Handler: s.handleDNSRequest, - AllServers: s.AllServers, + UDPListenAddr: s.UDPListenAddr, + TCPListenAddr: s.TCPListenAddr, + Ratelimit: s.Ratelimit, + RatelimitWhitelist: s.RatelimitWhitelist, + RefuseAny: s.RefuseAny, + CacheEnabled: true, + Upstreams: s.Upstreams, + DomainsReservedUpstreams: s.DomainsReservedUpstreams, + Handler: s.handleDNSRequest, + AllServers: s.AllServers, } if s.TLSListenAddr != nil && s.CertificateChain != "" && s.PrivateKey != "" { diff --git a/go.mod b/go.mod index 01918844..dab19303 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/AdguardTeam/AdGuardHome go 1.12 require ( - github.com/AdguardTeam/dnsproxy v0.11.2 + github.com/AdguardTeam/dnsproxy v0.12.0 github.com/AdguardTeam/golibs v0.1.3 github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect - github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7 + github.com/bluele/gcache v0.0.0-20190203144525-2016d595ccb0 github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-test/deep v1.0.1 github.com/gobuffalo/packr v1.19.0 diff --git a/go.sum b/go.sum index fbd89b79..cec7fae6 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ -github.com/AdguardTeam/dnsproxy v0.11.2 h1:S/Ag2q9qoZsmW1fvMohPZP7/5amEtz8NmFCp8kxUalQ= -github.com/AdguardTeam/dnsproxy v0.11.2/go.mod h1:EPp92b5cYR7HZpO+OQu6xC7AyhUoBaXW3sfa3exq/0I= -github.com/AdguardTeam/golibs v0.1.0/go.mod h1:zhi6xGwK4cMpjDocybhhLgvcGkstiSIjlpKbvyxC5Yc= +github.com/AdguardTeam/dnsproxy v0.12.0 h1:BPgv2PlH2u4xakFcaW4EqU3Visk1BNidrqGSgxe5Qzg= +github.com/AdguardTeam/dnsproxy v0.12.0/go.mod h1:lcZM2QPwcWGEL3pz8RYy06nQdbjj4pr+94H45jnVSHg= +github.com/AdguardTeam/golibs v0.1.2/go.mod h1:b0XkhgIcn2TxwX6C5AQMtpIFAgjPehNgxJErWkwA3ko= github.com/AdguardTeam/golibs v0.1.3 h1:hmapdTtMtIk3T8eQDwTOLdqZLGDKNKk9325uC8z12xg= github.com/AdguardTeam/golibs v0.1.3/go.mod h1:b0XkhgIcn2TxwX6C5AQMtpIFAgjPehNgxJErWkwA3ko= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f h1:5ZfJxyXo8KyX8DgGXC5B7ILL8y51fci/qYz2B4j8iLY= @@ -15,8 +15,8 @@ github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4Jhn github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6 h1:KXlsf+qt/X5ttPGEjR0tPH1xaWWoKBEg9Q1THAj2h3I= github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA= -github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7 h1:NpQ+gkFOH27AyDypSCJ/LdsIi/b4rdnEb1N5+IpFfYs= -github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= +github.com/bluele/gcache v0.0.0-20190203144525-2016d595ccb0 h1:vUdUwmQLnT/yuk8PsDhhMVkrfr4aMdcv/0GWzIqOjEY= +github.com/bluele/gcache v0.0.0-20190203144525-2016d595ccb0/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= From a01ba5dd4d04bd722cfca2cee950411f3deaabff Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 20 Mar 2019 15:19:34 +0300 Subject: [PATCH 2/3] * control, client: fix issues from review --- client/src/components/Settings/Upstream/Examples.js | 2 +- control.go | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/src/components/Settings/Upstream/Examples.js b/client/src/components/Settings/Upstream/Examples.js index 9d61b0de..53a233a0 100644 --- a/client/src/components/Settings/Upstream/Examples.js +++ b/client/src/components/Settings/Upstream/Examples.js @@ -22,7 +22,7 @@ const Examples = props => ( sdns://... -
  • - [/host.com/]1.1.1.1 - + [/example.local/]1.1.1.1 -
  • diff --git a/control.go b/control.go index 952395a1..1b05fb73 100644 --- a/control.go +++ b/control.go @@ -369,23 +369,22 @@ func validateUpstreams(upstreams []string) error { return nil } -func validateUpstream(u string) (defaultUpstream bool, err error) { +func validateUpstream(u string) (bool, error) { // Check if user tries to specify upstream for domain - defaultUpstream = true - u, defaultUpstream, err = separateUpstream(u) + u, defaultUpstream, err := separateUpstream(u) if err != nil { - return + return defaultUpstream, err } // The special server address '#' means "use the default servers" if u == "#" && !defaultUpstream { - return + return defaultUpstream, nil } // Check if the upstream has a valid protocol prefix for _, proto := range protocols { if strings.HasPrefix(u, proto) { - return + return defaultUpstream, nil } } From f7860c893da81bc23220bcdec8161ce7b36dc558 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Mar 2019 15:23:36 +0300 Subject: [PATCH 3/3] * client: removed links from upstream DNS translations --- client/src/__locales/bg.json | 6 +- client/src/__locales/en.json | 8 +-- client/src/__locales/es.json | 6 +- client/src/__locales/fr.json | 6 +- client/src/__locales/ja.json | 6 +- client/src/__locales/pt-br.json | 6 +- client/src/__locales/ru.json | 6 +- client/src/__locales/sv.json | 6 +- client/src/__locales/vi.json | 6 +- client/src/__locales/zh-cn.json | 6 +- client/src/__locales/zh-tw.json | 15 +++-- .../components/Settings/Upstream/Examples.js | 60 +++++++++++++++++-- 12 files changed, 96 insertions(+), 41 deletions(-) diff --git a/client/src/__locales/bg.json b/client/src/__locales/bg.json index bb889139..1e06d2e8 100644 --- a/client/src/__locales/bg.json +++ b/client/src/__locales/bg.json @@ -118,9 +118,9 @@ "example_comment_hash": "# \u0422\u043e\u0432\u0430 \u0435 \u0441\u044a\u0449\u043e \u043a\u043e\u043c\u0435\u043d\u0442\u0430\u0440", "example_regex_meaning": "\u0411\u043b\u043e\u043a\u0438\u0440\u0430\u0439 \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u0434\u043e\u043c\u0435\u0439\u043d\u0438 \u043a\u043e\u0439\u0442\u043e \u0441\u044a\u0432\u043f\u0430\u0434\u0430\u0442 \u0441\u044a\u0441 \u0441\u043b\u0435\u0434\u043d\u043e\u0442\u043e", "example_upstream_regular": "\u043a\u043b\u0430\u0441\u0438\u0447\u0435\u0441\u043a\u0438 DNS (UDP \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b)", - "example_upstream_dot": "\u043a\u0440\u0438\u043f\u0442\u0438\u0440\u0430\u043d
    DNS-\u0432\u044a\u0440\u0445\u0443-TLS<\/a>", - "example_upstream_doh": "\u043a\u0440\u0438\u043f\u0442\u0438\u0440\u0430\u043d DNS-\u0432\u044a\u0440\u0445\u0443-HTTPS<\/a>", - "example_upstream_sdns": "\u043c\u043e\u0436\u0435 \u0434\u0430 \u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 DNS \u041f\u043e\u0434\u043f\u0438\u0441\u0432\u0430\u043d\u0435<\/a> \u0437\u0430 DNSCrypt<\/a> \u0438\u043b\u0438 DNS-\u0432\u044a\u0440\u0445\u0443-HTTPS<\/a> \u0441\u044a\u0440\u0432\u044a\u0440\u0438", + "example_upstream_dot": "\u043a\u0440\u0438\u043f\u0442\u0438\u0440\u0430\u043d <0>DNS-\u0432\u044a\u0440\u0445\u0443-TLS<\/0>", + "example_upstream_doh": "\u043a\u0440\u0438\u043f\u0442\u0438\u0440\u0430\u043d <0>DNS-\u0432\u044a\u0440\u0445\u0443-HTTPS<\/0>", + "example_upstream_sdns": "\u043c\u043e\u0436\u0435 \u0434\u0430 \u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 <0>DNS \u041f\u043e\u0434\u043f\u0438\u0441\u0432\u0430\u043d\u0435<\/0> \u0437\u0430 <1>DNSCrypt<\/1> \u0438\u043b\u0438 <2>DNS-\u0432\u044a\u0440\u0445\u0443-HTTPS<\/2> \u0441\u044a\u0440\u0432\u044a\u0440\u0438", "example_upstream_tcp": "\u043a\u043b\u0430\u0441\u0438\u0447\u0435\u0441\u043a\u0438 DNS (TCP \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b)", "all_filters_up_to_date_toast": "\u0412\u0441\u0438\u0447\u043a\u0438 \u0444\u0438\u043b\u0442\u0438 \u0441\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0438", "updated_upstream_dns_toast": "\u0413\u043b\u043e\u0431\u0430\u043b\u043d\u0438\u0442\u0435 DNS \u0441\u044a\u0440\u0432\u044a\u0440\u0438 \u0441\u0430 \u043e\u0431\u043d\u043e\u0432\u0435\u043d\u0438", diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 74c2fa50..264f79d2 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -118,11 +118,11 @@ "example_comment_hash": "# Also a comment", "example_regex_meaning": "block access to the domains matching the specified regular expression", "example_upstream_regular": "regular DNS (over UDP)", - "example_upstream_dot": "encrypted DNS-over-TLS<\/a>", - "example_upstream_doh": "encrypted DNS-over-HTTPS<\/a>", - "example_upstream_sdns": "you can use DNS Stamps<\/a> for DNSCrypt<\/a> or DNS-over-HTTPS<\/a> resolvers", + "example_upstream_dot": "encrypted <0>DNS-over-TLS", + "example_upstream_doh": "encrypted <0>DNS-over-HTTPS", + "example_upstream_sdns": "you can use <0>DNS Stamps for <1>DNSCrypt or <2>DNS-over-HTTPS resolvers", "example_upstream_tcp": "regular DNS (over TCP)", - "example_upstream_reserved": "you can specify DNS upstream for a specific domain(s)<\/a>", + "example_upstream_reserved": "you can specify DNS upstream <0>for a specific domain(s)", "all_filters_up_to_date_toast": "All filters are already up-to-date", "updated_upstream_dns_toast": "Updated the upstream DNS servers", "dns_test_ok_toast": "Specified DNS servers are working correctly", diff --git a/client/src/__locales/es.json b/client/src/__locales/es.json index 214ff969..de4e3c26 100644 --- a/client/src/__locales/es.json +++ b/client/src/__locales/es.json @@ -115,9 +115,9 @@ "example_comment_meaning": "solo un comentario", "example_comment_hash": "# Tambi\u00e9n un comentario", "example_upstream_regular": "DNS regular (a trav\u00e9s de UDP)", - "example_upstream_dot": "encriptado DNS-a-trav\u00e9s-de-TLS<\/a>", - "example_upstream_doh": "encriptado DNS-a-trav\u00e9s-de-TLS<\/a>", - "example_upstream_sdns": "puedes usar DNS Stamps<\/a> para DNSCrypt<\/a> o DNS-over-HTTPS<\/a> resolutores", + "example_upstream_dot": "encriptado <0>DNS-a-trav\u00e9s-de-TLS<\/0>", + "example_upstream_doh": "encriptado <0>DNS-a-trav\u00e9s-de-TLS<\/0>", + "example_upstream_sdns": "puedes usar <0>DNS Stamps<\/0> para <1>DNSCrypt<\/1> o <2>DNS-over-HTTPS<\/2> resolutores", "example_upstream_tcp": "DNS regular (a trav\u00e9s de TCP)", "all_filters_up_to_date_toast": "Todos los filtros son actualizados", "updated_upstream_dns_toast": "Servidores DNS upstream actualizados", diff --git a/client/src/__locales/fr.json b/client/src/__locales/fr.json index 606fa157..629ababe 100644 --- a/client/src/__locales/fr.json +++ b/client/src/__locales/fr.json @@ -116,9 +116,9 @@ "example_comment_meaning": "commentaire", "example_comment_hash": "# Et comme \u00e7a aussi on peut laisser des commentaires", "example_upstream_regular": "DNS classique (au-dessus de UDP)", - "example_upstream_dot": "DNS-au-dessus-de-TLS<\/a> chiffr\u00e9", - "example_upstream_doh": "DNS-au-dessus-de-HTTPS<\/a> chiffr\u00e9", - "example_upstream_sdns": "vous pouvez utiliser DNS Stamps<\/a> pour DNSCrypt<\/a> ou les resolveurs DNS-au-dessus-de-HTTPS<\/a>", + "example_upstream_dot": "<0>DNS-au-dessus-de-TLS<\/0> chiffr\u00e9", + "example_upstream_doh": "<0>DNS-au-dessus-de-HTTPS<\/0> chiffr\u00e9", + "example_upstream_sdns": "vous pouvez utiliser <0>DNS Stamps<\/0> pour <1>DNSCrypt<\/1> ou les resolveurs <2>DNS-au-dessus-de-HTTPS<\/2>", "example_upstream_tcp": "DNS classique (au-dessus de TCP)", "all_filters_up_to_date_toast": "Tous les filtres sont mis \u00e0 jour", "updated_upstream_dns_toast": "Les serveurs DNS upstream sont mis \u00e0 jour", diff --git a/client/src/__locales/ja.json b/client/src/__locales/ja.json index 7ecf2d1d..a6aac447 100644 --- a/client/src/__locales/ja.json +++ b/client/src/__locales/ja.json @@ -115,9 +115,9 @@ "example_comment_meaning": "\u305f\u3060\u306e\u30b3\u30e1\u30f3\u30c8\u3067\u3059", "example_comment_hash": "# \u3053\u3053\u3082\u30b3\u30e1\u30f3\u30c8\u3067\u3059", "example_upstream_regular": "\u901a\u5e38\u306eDNS\uff08UDP\u3067\u306e\u554f\u3044\u5408\u308f\u305b\uff09", - "example_upstream_dot": "\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u308b DNS-over-TLS<\/a>", - "example_upstream_doh": "\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u308b DNS-over-HTTPS<\/a>", - "example_upstream_sdns": "DNSCrypt<\/a> \u307e\u305f\u306f DNS-over-HTTPS<\/a> \u30ea\u30be\u30eb\u30d0\u306e\u305f\u3081\u306b DNS Stamps<\/a> \u3092\u4f7f\u3048\u307e\u3059", + "example_upstream_dot": "\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u308b <0>DNS-over-TLS<\/0>", + "example_upstream_doh": "\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u308b <0>DNS-over-HTTPS<\/0>", + "example_upstream_sdns": "<0>DNSCrypt<\/0> \u307e\u305f\u306f <1>DNS-over-HTTPS<\/1> \u30ea\u30be\u30eb\u30d0\u306e\u305f\u3081\u306b <2>DNS Stamps<\/2> \u3092\u4f7f\u3048\u307e\u3059", "example_upstream_tcp": "\u901a\u5e38\u306eDNS\uff08TCP\u3067\u306e\u554f\u3044\u5408\u308f\u305b\uff09", "all_filters_up_to_date_toast": "\u3059\u3079\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u306f\u65e2\u306b\u6700\u65b0\u3067\u3059", "updated_upstream_dns_toast": "\u4e0a\u6d41DNS\u30b5\u30fc\u30d0\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f", diff --git a/client/src/__locales/pt-br.json b/client/src/__locales/pt-br.json index 90591992..c4668c83 100644 --- a/client/src/__locales/pt-br.json +++ b/client/src/__locales/pt-br.json @@ -118,9 +118,9 @@ "example_comment_hash": "# Tamb\u00e9m um coment\u00e1rio", "example_regex_meaning": "bloqueia o acesso aos dom\u00ednios correspondentes \u00e0 express\u00e3o regular especificada", "example_upstream_regular": "DNS regular (atrav\u00e9s do UDP)", - "example_upstream_dot": "DNS criptografado atrav\u00e9s do TLS<\/a>", - "example_upstream_doh": "DNS criptografado atrav\u00e9s do HTTPS<\/a>", - "example_upstream_sdns": "Voc\u00ea pode usar DNS Stamps<\/a>para oDNSCrypt<\/a>ou usar resolvedoresDNS-sobre-HTTPS<\/a>", + "example_upstream_dot": "DNS criptografado <0>atrav\u00e9s do TLS<\/0>", + "example_upstream_doh": "DNS criptografado <0>atrav\u00e9s do HTTPS<\/0>", + "example_upstream_sdns": "Voc\u00ea pode usar <0>DNS Stamps<\/0> para o <1>DNSCrypt<\/1> ou usar resolvedores <2>DNS-sobre-HTTPS<\/2>", "example_upstream_tcp": "DNS regular (atrav\u00e9s do TCP)", "all_filters_up_to_date_toast": "Todos os filtros j\u00e1 est\u00e3o atualizados", "updated_upstream_dns_toast": "Atualizado os servidores DNS upstream", diff --git a/client/src/__locales/ru.json b/client/src/__locales/ru.json index 6784eceb..3b7ea611 100644 --- a/client/src/__locales/ru.json +++ b/client/src/__locales/ru.json @@ -115,9 +115,9 @@ "example_comment_meaning": "\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439", "example_comment_hash": "# \u0418 \u0432\u043e\u0442 \u0442\u0430\u043a \u0442\u043e\u0436\u0435", "example_upstream_regular": "\u043e\u0431\u044b\u0447\u043d\u044b\u0439 DNS (\u043f\u043e\u0432\u0435\u0440\u0445 UDP)", - "example_upstream_dot": "\u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 DNS-\u043f\u043e\u0432\u0435\u0440\u0445-TLS<\/a>", - "example_upstream_doh": "\u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 DNS-\u043f\u043e\u0432\u0435\u0440\u0445-HTTPS<\/a>", - "example_upstream_sdns": "\u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c DNS Stamps<\/a> \u0434\u043b\u044f DNSCrypt<\/a> \u0438\u043b\u0438 DNS-over-HTTPS<\/a> \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u043e\u0432", + "example_upstream_dot": "\u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 <0>DNS-\u043f\u043e\u0432\u0435\u0440\u0445-TLS<\/0>", + "example_upstream_doh": "\u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 <0>DNS-\u043f\u043e\u0432\u0435\u0440\u0445-HTTPS<\/0>", + "example_upstream_sdns": "\u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <0>DNS Stamps<\/0> \u0434\u043b\u044f <1>DNSCrypt<\/1> \u0438\u043b\u0438 <2>DNS-over-HTTPS<\/2> \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u043e\u0432", "example_upstream_tcp": "\u043e\u0431\u044b\u0447\u043d\u044b\u0439 DNS (\u043f\u043e\u0432\u0435\u0440\u0445 TCP)", "all_filters_up_to_date_toast": "\u0412\u0441\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b", "updated_upstream_dns_toast": "Upstream DNS-\u0441\u0435\u0440\u0432\u0435\u0440\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b", diff --git a/client/src/__locales/sv.json b/client/src/__locales/sv.json index 71b37466..bdd6e123 100644 --- a/client/src/__locales/sv.json +++ b/client/src/__locales/sv.json @@ -118,9 +118,9 @@ "example_comment_hash": "# Ocks\u00e5 en kommentar", "example_regex_meaning": "blockera \u00e5tkomst till en dom\u00e4n som matchar det angivna uttrycket", "example_upstream_regular": "vanlig DNS (\u00f6ver UDP)", - "example_upstream_dot": "krypterat DNS-over-TLS<\/a>", - "example_upstream_doh": "krypterat DNS-over-HTTPS<\/a>", - "example_upstream_sdns": "Du kan anv\u00e4nda DNS-stamps<\/a> f\u00f6r DNSCrypt<\/a> eller DNS-\u00f6ver-HTTPS<\/a>\n-resolvers", + "example_upstream_dot": "krypterat <0>DNS-over-TLS<\/0>", + "example_upstream_doh": "krypterat <0>DNS-over-HTTPS<\/0>", + "example_upstream_sdns": "Du kan anv\u00e4nda <0>DNS-stamps<\/0> f\u00f6r <1>DNSCrypt<\/1> eller <2>DNS-\u00f6ver-HTTPS<\/2>\n-resolvers", "example_upstream_tcp": "vanlig DNS (\u00f6ver UDP)", "all_filters_up_to_date_toast": "Alla filter \u00e4r redan aktuella", "updated_upstream_dns_toast": "Uppdaterade uppstr\u00f6ms-dns-servrar", diff --git a/client/src/__locales/vi.json b/client/src/__locales/vi.json index ba447ac2..9caa47ef 100644 --- a/client/src/__locales/vi.json +++ b/client/src/__locales/vi.json @@ -113,9 +113,9 @@ "example_comment_meaning": "Ch\u1ec9 l\u00e0 m\u1ed9t ch\u00fa th\u00edch", "example_comment_hash": "# C\u0169ng l\u00e0 m\u1ed9t ch\u00fa th\u00edch", "example_upstream_regular": "DNS th\u00f4ng th\u01b0\u1eddng (d\u00f9ng UDP)", - "example_upstream_dot": "\u0111\u01b0\u1ee3c m\u00e3 ho\u00e1 DNS-over-TLS<\/a>", - "example_upstream_doh": "\u0111\u01b0\u1ee3c m\u00e3 ho\u00e1 DNS-over-HTTPS<\/a>", - "example_upstream_sdns": "b\u1ea1n c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng DNS Stamps<\/a> for DNSCrypt<\/a> ho\u1eb7cDNS-over-HTTPS<\/a> ", + "example_upstream_dot": "\u0111\u01b0\u1ee3c m\u00e3 ho\u00e1 <0>DNS-over-TLS<\/0>", + "example_upstream_doh": "\u0111\u01b0\u1ee3c m\u00e3 ho\u00e1 <0>DNS-over-HTTPS<\/0>", + "example_upstream_sdns": "b\u1ea1n c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng <0>DNS Stamps<\/0> for <1>DNSCrypt<\/1> ho\u1eb7c <2>DNS-over-HTTPS<\/2> ", "example_upstream_tcp": "DNS th\u00f4ng th\u01b0\u1eddng(d\u00f9ng TCP)", "all_filters_up_to_date_toast": "T\u1ea5t c\u1ea3 b\u1ed9 l\u1ecdc \u0111\u00e3 \u0111\u01b0\u1ee3c c\u1eadp nh\u1eadt", "updated_upstream_dns_toast": "\u0110\u00e3 c\u1eadp nh\u1eadt m\u00e1y ch\u1ee7 DNS t\u00ecm ki\u1ebfm", diff --git a/client/src/__locales/zh-cn.json b/client/src/__locales/zh-cn.json index 7ac6abf3..6c3cf349 100644 --- a/client/src/__locales/zh-cn.json +++ b/client/src/__locales/zh-cn.json @@ -118,9 +118,9 @@ "example_comment_hash": "# \u8fd9\u4e5f\u662f\u4e00\u884c\u6ce8\u91ca", "example_regex_meaning": "\u963b\u6b62\u8bbf\u95ee\u4e0e\u6307\u5b9a\u7684\u6b63\u5219\u8868\u8fbe\u5f0f\u5339\u914d\u7684\u57df\u540d", "example_upstream_regular": "\u5e38\u89c4 DNS\uff08\u57fa\u4e8e UDP\uff09", - "example_upstream_dot": "\u52a0\u5bc6 DNS-over-TLS<\/a>", - "example_upstream_doh": "\u52a0\u5bc6 DNS-over-HTTPS<\/a>", - "example_upstream_sdns": "\u4f60\u53ef\u4ee5\u4f7f\u7528 DNSCrypt<\/a> \u7684 DNS Stamps<\/a> \u6216\u8005 DNS-over-HTTPS<\/a> \u89e3\u6790\u5668", + "example_upstream_dot": "\u52a0\u5bc6 <0>DNS-over-TLS<\/0>", + "example_upstream_doh": "\u52a0\u5bc6 <0>DNS-over-HTTPS<\/0>", + "example_upstream_sdns": "\u4f60\u53ef\u4ee5\u4f7f\u7528 <0>DNSCrypt<\/0> \u7684 <1>DNS Stamps<\/1> \u6216\u8005 <2>DNS-over-HTTPS<\/2> \u89e3\u6790\u5668", "example_upstream_tcp": "\u5e38\u89c4 DNS\uff08\u57fa\u4e8e TCP \uff09", "all_filters_up_to_date_toast": "\u6240\u6709\u8fc7\u6ee4\u5668\u5df2\u66f4\u65b0\u81f3\u6700\u65b0", "updated_upstream_dns_toast": "\u4e0a\u6e38 DNS \u5df2\u66f4\u65b0", diff --git a/client/src/__locales/zh-tw.json b/client/src/__locales/zh-tw.json index 4d3b7b85..35920049 100644 --- a/client/src/__locales/zh-tw.json +++ b/client/src/__locales/zh-tw.json @@ -1,4 +1,7 @@ { + "upstream_parallel": "\u900f\u904e\u540c\u6642\u5730\u67e5\u8a62\u6240\u6709\u4e0a\u6e38\u7684\u4f3a\u670d\u5668\uff0c\u4f7f\u7528\u4e26\u884c\u7684\u67e5\u8a62\u4ee5\u52a0\u901f\u89e3\u6790", + "bootstrap_dns": "\u81ea\u6211\u555f\u52d5\uff08Bootstrap\uff09DNS \u4f3a\u670d\u5668", + "bootstrap_dns_desc": "\u81ea\u6211\u555f\u52d5\uff08Bootstrap\uff09DNS\u4f3a\u670d\u5668\u88ab\u7528\u65bc\u89e3\u6790\u60a8\u660e\u78ba\u6307\u5b9a\u4f5c\u70ba\u4e0a\u6e38\u7684DoH\/DoT\u89e3\u6790\u5668\u4e4bIP\u4f4d\u5740\u3002", "url_added_successfully": "\u7db2\u5740\u88ab\u6210\u529f\u5730\u52a0\u5165", "check_dhcp_servers": "\u6aa2\u67e5\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668", "save_config": "\u5132\u5b58\u914d\u7f6e", @@ -79,7 +82,7 @@ "no_settings": "\u7121\u8a2d\u5b9a", "general_settings": "\u4e00\u822c\u7684\u8a2d\u5b9a", "upstream_dns": "\u4e0a\u6e38\u7684DNS\u4f3a\u670d\u5668", - "upstream_dns_hint": "\u5982\u679c\u60a8\u4fdd\u7559\u8a72\u6b04\u4f4d\u7a7a\u767d\u7684\uff0cAdGuard Home\u5c07\u4f7f\u7528Cloudflare DNS<\/a>\u4f5c\u70ba\u4e0a\u6e38\u3002\u5c0d\u65bcDNS over TLS\u4f3a\u670d\u5668\u4f7f\u7528 tls:\/\/ \u524d\u7db4\u3002", + "upstream_dns_hint": "\u5982\u679c\u60a8\u5c07\u8a72\u6b04\u4f4d\u7559\u7a7a\uff0cAdGuard Home\u5c07\u4f7f\u7528Cloudflare DNS<\/a>\u4f5c\u70ba\u4e0a\u6e38\u3002", "test_upstream_btn": "\u6e2c\u8a66\u4e0a\u884c\u8cc7\u6599\u6d41", "apply_btn": "\u5957\u7528", "disabled_filtering_toast": "\u5df2\u7981\u7528\u904e\u6ffe", @@ -118,9 +121,9 @@ "example_comment_hash": "# \u4e5f\u662f\u4e00\u500b\u8a3b\u89e3", "example_regex_meaning": "\u5c01\u9396\u81f3\u8207\u5df2\u660e\u78ba\u6307\u5b9a\u7684\u898f\u5247\u904b\u7b97\u5f0f\uff08Regular Expression\uff09\u76f8\u7b26\u7684\u7db2\u57df\u4e4b\u5b58\u53d6", "example_upstream_regular": "\u4e00\u822c\u7684 DNS\uff08\u900f\u904eUDP\uff09", - "example_upstream_dot": "\u52a0\u5bc6\u7684 DNS-over-TLS<\/a>", - "example_upstream_doh": "\u52a0\u5bc6\u7684 DNS-over-HTTPS <\/a>", - "example_upstream_sdns": "\u60a8\u53ef\u4f7f\u7528\u95dc\u65bc DNSCrypt<\/a> \u6216 DNS-over-HTTPS<\/a> \u89e3\u6790\u5668\u4e4b DNS \u6233\u8a18<\/a>", + "example_upstream_dot": "\u52a0\u5bc6\u7684 <0>DNS-over-TLS<\/0>", + "example_upstream_doh": "\u52a0\u5bc6\u7684 <0>DNS-over-HTTPS <\/0>", + "example_upstream_sdns": "\u60a8\u53ef\u4f7f\u7528\u95dc\u65bc <0>DNSCrypt<\/0> \u6216 <1>DNS-over-HTTPS<\/1> \u89e3\u6790\u5668\u4e4b <2>DNS \u6233\u8a18<\/2>", "example_upstream_tcp": "\u4e00\u822c\u7684 DNS\uff08\u900f\u904eTCP\uff09", "all_filters_up_to_date_toast": "\u6240\u6709\u7684\u904e\u6ffe\u5668\u5df2\u662f\u6700\u65b0\u7684", "updated_upstream_dns_toast": "\u5df2\u66f4\u65b0\u4e0a\u6e38\u7684DNS\u4f3a\u670d\u5668", @@ -246,5 +249,7 @@ "form_error_equal": "\u4e0d\u61c9\u70ba\u76f8\u7b49\u7684", "form_error_password": "\u4e0d\u76f8\u7b26\u7684\u5bc6\u78bc", "reset_settings": "\u91cd\u7f6e\u8a2d\u5b9a", - "update_announcement": "AdGuard Home {{version}} \u73fe\u70ba\u53ef\u7528\u7684\uff01\u95dc\u65bc\u66f4\u591a\u7684\u8cc7\u8a0a\uff0c<0>\u9ede\u64ca\u9019\u88e1<\/0>\u3002" + "update_announcement": "AdGuard Home {{version}} \u73fe\u70ba\u53ef\u7528\u7684\uff01\u95dc\u65bc\u66f4\u591a\u7684\u8cc7\u8a0a\uff0c<0>\u9ede\u64ca\u9019\u88e1<\/0>\u3002", + "setup_guide": "\u5b89\u88dd\u6307\u5357", + "dns_addresses": "DNS \u4f4d\u5740" } \ No newline at end of file diff --git a/client/src/components/Settings/Upstream/Examples.js b/client/src/components/Settings/Upstream/Examples.js index 53a233a0..b289112d 100644 --- a/client/src/components/Settings/Upstream/Examples.js +++ b/client/src/components/Settings/Upstream/Examples.js @@ -10,19 +10,69 @@ const Examples = props => ( 1.1.1.1 - { props.t('example_upstream_regular') }
  • - tls://1dot1dot1dot1.cloudflare-dns.com - + tls://1dot1dot1dot1.cloudflare-dns.com –  + + + DNS-over-TLS + , + ]} + > + example_upstream_dot + +
  • - https://cloudflare-dns.com/dns-query - + https://cloudflare-dns.com/dns-query –  + + + DNS-over-HTTPS + , + ]} + > + example_upstream_doh + +
  • - tcp://1.1.1.1 - { props.t('example_upstream_tcp') } + tcp://1.1.1.1example_upstream_tcp
  • - sdns://... - + sdns://... –  + + + DNS Stamps + , + + DNSCrypt + , + + DNS-over-HTTPS + , + ]} + > + example_upstream_sdns + +
  • - [/example.local/]1.1.1.1 - + [/example.local/]1.1.1.1 –  + + + Link + , + ]} + > + example_upstream_reserved + +