Browse Source

Propagate DNS setting to configuration

tags/v2.1.13
9seconds 2 months ago
parent
commit
897e6bf505

+ 27
- 14
example.config.toml View File

@@ -134,8 +134,33 @@ allow-fallback-on-unknown-dc = false
134 134
 # it has to access.
135 135
 #
136 136
 # By default we use Cloudflare.
137
+#
138
+# DEPRECATED option:
139
+#  If dns option is specified, it will be used instead
137 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 164
 # mtg can work via proxies (for now, we support only socks5). Proxy
140 165
 # configuration is done via list. So, you can specify many proxies
141 166
 # there.
@@ -149,25 +174,13 @@ doh-ip = "1.1.1.1"
149 174
 #
150 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 179
 # Only socks5 proxy is used. user/password is optional. As you can
155 180
 # see, you can specify some parameters in GET query. These parameters
156 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 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 186
 # network timeouts define different settings for timeouts. tcp timeout

+ 12
- 0
internal/config/config.go View File

@@ -5,6 +5,7 @@ import (
5 5
 	"encoding/json"
6 6
 	"fmt"
7 7
 	"net"
8
+	"net/url"
8 9
 
9 10
 	"github.com/9seconds/mtg/v2/mtglib"
10 11
 )
@@ -56,6 +57,7 @@ type Config struct {
56 57
 			Idle TypeDuration `json:"idle"`
57 58
 		} `json:"timeout"`
58 59
 		DOHIP   TypeIP         `json:"dohIp"`
60
+		DNS     TypeDNSURI     `json:"dns"`
59 61
 		Proxies []TypeProxyURL `json:"proxies"`
60 62
 	} `json:"network"`
61 63
 	Stats struct {
@@ -76,6 +78,16 @@ type Config struct {
76 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 91
 func (c *Config) GetDomainFrontingPort(defaultValue uint) uint {
80 92
 	if port := c.DomainFronting.Port.Get(0); port != 0 {
81 93
 		return port

+ 1
- 0
internal/config/parse.go View File

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

+ 69
- 0
internal/config/type_dns_uri.go View File

@@ -0,0 +1,69 @@
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 View File

@@ -0,0 +1,117 @@
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 View File

@@ -21,6 +21,14 @@ func GetDNS(u *url.URL) (*net.Resolver, error) {
21 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 32
 	switch u.Scheme {
25 33
 	case "tls":
26 34
 		return dns.NewDoTResolver(u.Host, dns.DoTCache(dnsCacheOptions...))

Loading…
Cancel
Save