Просмотр исходного кода

Propagate DNS setting to configuration

tags/v2.1.13
9seconds 2 месяцев назад
Родитель
Сommit
897e6bf505
6 измененных файлов: 234 добавлений и 14 удалений
  1. 27
    14
      example.config.toml
  2. 12
    0
      internal/config/config.go
  3. 1
    0
      internal/config/parse.go
  4. 69
    0
      internal/config/type_dns_uri.go
  5. 117
    0
      internal/config/type_dns_uri_test.go
  6. 8
    0
      network/v2/dns.go

+ 27
- 14
example.config.toml Просмотреть файл

134
 # it has to access.
134
 # it has to access.
135
 #
135
 #
136
 # By default we use Cloudflare.
136
 # By default we use Cloudflare.
137
+#
138
+# DEPRECATED option:
139
+#  If dns option is specified, it will be used instead
137
 doh-ip = "1.1.1.1"
140
 doh-ip = "1.1.1.1"
138
 
141
 
142
+# Starting from mtg v2.1.12 we have changed a configuration for DNS. Now it
143
+# supports DNS-over-HTTPS, DNS-over-TLS, custom UDP resolver and system
144
+# resolver.
145
+#
146
+# Here is how to define DNS-over-HTTPS:
147
+#  - https://1.1.1.1
148
+#  - https://1.1.1.1/dns-query
149
+#  - https://cloudflare-dns.com/dns-query
150
+#  - https://cloudflare-dns.com
151
+#
152
+# Here is how to define DNS-over-TLS:
153
+#  - tls://1.1.1.1
154
+#  - tls://cloudflare-dns.com
155
+#
156
+# Here is how to define a custom UDP resolver (we support only IPs here)
157
+#  - 1.1.1.1
158
+#  - udp://1.1.1.1
159
+#
160
+# If you set it to empty string, default resolver will be used.
161
+# But please comment out doh-ip
162
+dns = "https://1.1.1.1"
163
+
139
 # mtg can work via proxies (for now, we support only socks5). Proxy
164
 # mtg can work via proxies (for now, we support only socks5). Proxy
140
 # configuration is done via list. So, you can specify many proxies
165
 # configuration is done via list. So, you can specify many proxies
141
 # there.
166
 # there.
149
 #
174
 #
150
 # Proxy configuration is done via ordinary URI schema:
175
 # Proxy configuration is done via ordinary URI schema:
151
 #
176
 #
152
-#     socks5://user:password@host:port?open_threshold=5&half_open_timeout=1m&reset_failures_timeout=10s
177
+#     socks5://user:password@host:port
153
 #
178
 #
154
 # Only socks5 proxy is used. user/password is optional. As you can
179
 # Only socks5 proxy is used. user/password is optional. As you can
155
 # see, you can specify some parameters in GET query. These parameters
180
 # see, you can specify some parameters in GET query. These parameters
156
 # configure circuit breaker.
181
 # configure circuit breaker.
157
-#
158
-# open_threshold means a number of errors which should happen so we stop
159
-# use a proxy.
160
-#
161
-# half_open_timeout means a time period (in Golang duration notation)
162
-# after which we can retry with this proxy
163
-#
164
-# reset_failures_timeout means a time period when we flush out errors
165
-# when circuit breaker in closed state.
166
-#
167
-# Please see https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker
168
-# on details about circuit breakers.
169
 proxies = [
182
 proxies = [
170
-    # "socks5://user:password@host:port?open_threshold=5&half_open_timeout=1m&reset_failures_timeout=10s"
183
+    # "socks5://user:password@host:port"
171
 ]
184
 ]
172
 
185
 
173
 # network timeouts define different settings for timeouts. tcp timeout
186
 # network timeouts define different settings for timeouts. tcp timeout

+ 12
- 0
internal/config/config.go Просмотреть файл

5
 	"encoding/json"
5
 	"encoding/json"
6
 	"fmt"
6
 	"fmt"
7
 	"net"
7
 	"net"
8
+	"net/url"
8
 
9
 
9
 	"github.com/9seconds/mtg/v2/mtglib"
10
 	"github.com/9seconds/mtg/v2/mtglib"
10
 )
11
 )
56
 			Idle TypeDuration `json:"idle"`
57
 			Idle TypeDuration `json:"idle"`
57
 		} `json:"timeout"`
58
 		} `json:"timeout"`
58
 		DOHIP   TypeIP         `json:"dohIp"`
59
 		DOHIP   TypeIP         `json:"dohIp"`
60
+		DNS     TypeDNSURI     `json:"dns"`
59
 		Proxies []TypeProxyURL `json:"proxies"`
61
 		Proxies []TypeProxyURL `json:"proxies"`
60
 	} `json:"network"`
62
 	} `json:"network"`
61
 	Stats struct {
63
 	Stats struct {
76
 	} `json:"stats"`
78
 	} `json:"stats"`
77
 }
79
 }
78
 
80
 
81
+func (c *Config) GetDNS() *url.URL {
82
+	var dohURL *url.URL
83
+
84
+	if dohIP := c.Network.DOHIP.Get(nil); dohIP != nil {
85
+		dohURL, _ = url.Parse("https://" + dohIP.String())
86
+	}
87
+
88
+	return c.Network.DNS.Get(dohURL)
89
+}
90
+
79
 func (c *Config) GetDomainFrontingPort(defaultValue uint) uint {
91
 func (c *Config) GetDomainFrontingPort(defaultValue uint) uint {
80
 	if port := c.DomainFronting.Port.Get(0); port != 0 {
92
 	if port := c.DomainFronting.Port.Get(0); port != 0 {
81
 		return port
93
 		return port

+ 1
- 0
internal/config/parse.go Просмотреть файл

52
 			Idle string `toml:"idle" json:"idle,omitempty"`
52
 			Idle string `toml:"idle" json:"idle,omitempty"`
53
 		} `toml:"timeout" json:"timeout,omitempty"`
53
 		} `toml:"timeout" json:"timeout,omitempty"`
54
 		DOHIP   string   `toml:"doh-ip" json:"dohIp,omitempty"`
54
 		DOHIP   string   `toml:"doh-ip" json:"dohIp,omitempty"`
55
+		DNS     string   `toml:"dns" json:"dns,omitempty"`
55
 		Proxies []string `toml:"proxies" json:"proxies,omitempty"`
56
 		Proxies []string `toml:"proxies" json:"proxies,omitempty"`
56
 	} `toml:"network" json:"network,omitempty"`
57
 	} `toml:"network" json:"network,omitempty"`
57
 	Stats struct {
58
 	Stats struct {

+ 69
- 0
internal/config/type_dns_uri.go Просмотреть файл

1
+package config
2
+
3
+import (
4
+	"fmt"
5
+	"net"
6
+	"net/url"
7
+)
8
+
9
+type TypeDNSURI struct {
10
+	Value *url.URL
11
+}
12
+
13
+func (t *TypeDNSURI) Set(value string) error {
14
+	parsed, err := url.Parse(value)
15
+	if err != nil {
16
+		return fmt.Errorf("value is not URI: %w", err)
17
+	}
18
+
19
+	if parsed.Host == "" {
20
+		parsed.Host = parsed.Path
21
+		parsed.Path = ""
22
+		parsed.Scheme = "udp"
23
+	}
24
+
25
+	switch parsed.Scheme {
26
+	case "https", "tls":
27
+	case "udp":
28
+		if ip := net.ParseIP(parsed.Hostname()); ip == nil {
29
+			return fmt.Errorf("simple DNS must IP address: %s", parsed.Hostname())
30
+		}
31
+	default:
32
+		return fmt.Errorf("unsupported DNS type %s", parsed.Scheme)
33
+	}
34
+
35
+	if parsed.Scheme != "https" && parsed.Path != "" {
36
+		return fmt.Errorf("path is supported only for DoH: %s", parsed)
37
+	}
38
+
39
+	if parsed.User != nil {
40
+		return fmt.Errorf("used info is not supported: %s", parsed.User.String())
41
+	}
42
+
43
+	t.Value = parsed
44
+
45
+	return nil
46
+}
47
+
48
+func (t *TypeDNSURI) Get(defaultValue *url.URL) *url.URL {
49
+	if t.Value != nil {
50
+		return t.Value
51
+	}
52
+
53
+	return defaultValue
54
+}
55
+
56
+func (t *TypeDNSURI) UnmarshalText(data []byte) error {
57
+	return t.Set(string(data))
58
+}
59
+
60
+func (t TypeDNSURI) MarshalText() ([]byte, error) {
61
+	return []byte(t.String()), nil
62
+}
63
+
64
+func (t TypeDNSURI) String() string {
65
+	if t.Value == nil {
66
+		return ""
67
+	}
68
+	return t.Value.String()
69
+}

+ 117
- 0
internal/config/type_dns_uri_test.go Просмотреть файл

1
+package config_test
2
+
3
+import (
4
+	"encoding/json"
5
+	"testing"
6
+
7
+	"github.com/9seconds/mtg/v2/internal/config"
8
+	"github.com/stretchr/testify/assert"
9
+	"github.com/stretchr/testify/suite"
10
+)
11
+
12
+type typeDNSURITestStruct struct {
13
+	Value config.TypeDNSURI `json:"value"`
14
+}
15
+
16
+type TypeDNSURITestSuite struct {
17
+	suite.Suite
18
+}
19
+
20
+func (suite *TypeDNSURITestSuite) TestUnmarshalFail() {
21
+	testData := []string{
22
+		"xx",
23
+		"ppar",
24
+		"",
25
+		"dns://hahaha",
26
+		"udp://xcxxcv",
27
+		"udp://1.1.1.1/xcv",
28
+		"1.1.1.1/xxx",
29
+		"tls://dns/xx",
30
+		"tls://1.1.1.1/xx",
31
+		"https://user:password@1.1.1.1",
32
+		"tls://user:password@1.1.1.1",
33
+		"udp://user:password@1.1.1.1",
34
+	}
35
+
36
+	for _, v := range testData {
37
+		data, err := json.Marshal(map[string]string{
38
+			"value": v,
39
+		})
40
+		suite.NoError(err)
41
+
42
+		suite.T().Run(v, func(t *testing.T) {
43
+			assert.Error(t, json.Unmarshal(data, &typeDNSURITestStruct{}))
44
+		})
45
+	}
46
+}
47
+
48
+func (suite *TypeDNSURITestSuite) TestUnmarshalOk() {
49
+	testData := []string{
50
+		"1.1.1.1",
51
+		"tls://1.1.1.1",
52
+		"tls://dns.google",
53
+		"https://1.1.1.1",
54
+		"https://1.1.1.1/dns-query",
55
+		"https://dns.google",
56
+		"https://dns.google/dns-query",
57
+		"udp://1.1.1.1",
58
+	}
59
+
60
+	for _, v := range testData {
61
+		data, err := json.Marshal(map[string]string{
62
+			"value": v,
63
+		})
64
+		suite.NoError(err)
65
+
66
+		suite.T().Run(v, func(t *testing.T) {
67
+			testStruct := &typeDNSURITestStruct{}
68
+			assert.NoError(t, json.Unmarshal(data, testStruct))
69
+			if v == "1.1.1.1" {
70
+				v = "udp://" + v
71
+			}
72
+			assert.Equal(t, v, testStruct.Value.String())
73
+		})
74
+	}
75
+}
76
+
77
+func (suite *TypeDNSURITestSuite) TestMarshalOk() {
78
+	testData := []string{
79
+		"tls://1.1.1.1",
80
+		"tls://dns.google",
81
+		"https://1.1.1.1",
82
+		"https://1.1.1.1/dns-query",
83
+	}
84
+
85
+	for _, v := range testData {
86
+		suite.T().Run(v, func(t *testing.T) {
87
+			testStruct := &typePreferIPTestStruct{
88
+				Value: config.TypePreferIP{
89
+					Value: v,
90
+				},
91
+			}
92
+
93
+			encodedJSON, err := json.Marshal(testStruct)
94
+			assert.NoError(t, err)
95
+
96
+			expectedJSON, err := json.Marshal(map[string]string{
97
+				"value": v,
98
+			})
99
+			assert.NoError(t, err)
100
+
101
+			assert.JSONEq(t, string(expectedJSON), string(encodedJSON))
102
+		})
103
+	}
104
+}
105
+
106
+func (suite *TypeDNSURITestSuite) TestGet() {
107
+	value := config.TypeDNSURI{}
108
+	suite.Nil(value.Get(nil))
109
+
110
+	suite.NoError(value.Set("tls://1.1.1.1"))
111
+	suite.NotNil(value.Get(nil))
112
+}
113
+
114
+func TestDNSURI(t *testing.T) {
115
+	t.Parallel()
116
+	suite.Run(t, &TypeDNSURITestSuite{})
117
+}

+ 8
- 0
network/v2/dns.go Просмотреть файл

21
 		return dns.NewCachingResolver(nil, dnsCacheOptions...), nil
21
 		return dns.NewCachingResolver(nil, dnsCacheOptions...), nil
22
 	}
22
 	}
23
 
23
 
24
+	if u.Scheme == "" {
25
+		u.Scheme = "udp"
26
+	}
27
+	if u.Scheme == "udp" && u.Host == "" {
28
+		u.Host = u.Path
29
+		u.Path = ""
30
+	}
31
+
24
 	switch u.Scheme {
32
 	switch u.Scheme {
25
 	case "tls":
33
 	case "tls":
26
 		return dns.NewDoTResolver(u.Host, dns.DoTCache(dnsCacheOptions...))
34
 		return dns.NewDoTResolver(u.Host, dns.DoTCache(dnsCacheOptions...))

Загрузка…
Отмена
Сохранить