forked from 0xERR0R/blocky
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.go
323 lines (252 loc) · 8.03 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
package config
import (
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"regexp"
"strconv"
"strings"
"blocky/log"
"gopkg.in/yaml.v2"
)
const (
validUpstream = `(?P<Host>(?:\[[^\]]+\])|[^\s/:]+):?(?P<Port>[^\s/:]*)?(?P<Path>/[^\s]*)?`
// NetUDP UDP protocol (deprecated)
NetUDP = "udp"
// NetTCP TCP protocol (deprecated)
NetTCP = "tcp"
// NetTCPUDP TCP and UDP protocols
NetTCPUDP = "tcp+udp"
// NetTCPTLS TCP-TLS protocol
NetTCPTLS = "tcp-tls"
// NetHTTPS HTTPS protocol
NetHTTPS = "https"
)
// nolint:gochecknoglobals
var netDefaultPort = map[string]uint16{
NetTCPUDP: 53,
NetTCPTLS: 853,
NetHTTPS: 443,
}
// Upstream is the definition of external DNS server
type Upstream struct {
Net string
Host string
Port uint16
Path string
}
// UnmarshalYAML creates Upstream from YAML
func (u *Upstream) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}
upstream, err := ParseUpstream(s)
if err != nil {
return err
}
*u = upstream
return nil
}
// UnmarshalYAML creates ConditionalUpstreamMapping from YAML
func (c *ConditionalUpstreamMapping) UnmarshalYAML(unmarshal func(interface{}) error) error {
var input map[string]string
if err := unmarshal(&input); err != nil {
return err
}
result := make(map[string][]Upstream)
for k, v := range input {
var upstreams []Upstream
for _, part := range strings.Split(v, ",") {
upstream, err := ParseUpstream(strings.TrimSpace(part))
if err != nil {
return err
}
upstreams = append(upstreams, upstream)
}
result[k] = upstreams
}
c.Upstreams = result
return nil
}
// UnmarshalYAML creates CustomDNSMapping from YAML
func (c *CustomDNSMapping) UnmarshalYAML(unmarshal func(interface{}) error) error {
var input map[string]string
if err := unmarshal(&input); err != nil {
return err
}
result := make(map[string][]net.IP)
for k, v := range input {
var ips []net.IP
for _, part := range strings.Split(v, ",") {
ip := net.ParseIP(strings.TrimSpace(part))
if ip == nil {
return fmt.Errorf("invalid IP address '%s'", part)
}
ips = append(ips, ip)
}
result[k] = ips
}
c.HostIPs = result
return nil
}
// ParseUpstream creates new Upstream from passed string in format [net]:host[:port][/path]
func ParseUpstream(upstream string) (result Upstream, err error) {
if strings.TrimSpace(upstream) == "" {
return Upstream{}, nil
}
var n string
n, upstream = extractNet(upstream)
r := regexp.MustCompile(validUpstream)
match := r.FindStringSubmatch(upstream)
host := match[1]
portPart := match[2]
path := match[3]
var port uint16
if len(portPart) > 0 {
var p uint64
p, err = strconv.ParseUint(strings.TrimSpace(portPart), 10, 16)
if err != nil {
err = fmt.Errorf("can't convert port to number (1 - 65535) %w", err)
return
}
port = uint16(p)
} else {
port = netDefaultPort[n]
}
host = regexp.MustCompile(`[\[\]]`).ReplaceAllString(host, "")
return Upstream{Net: n, Host: host, Port: port, Path: path}, nil
}
func extractNet(upstream string) (string, string) {
if strings.HasPrefix(upstream, NetTCP+":") {
log.Log().Warnf("net prefix tcp is deprecated, using tcp+udp as default fallback")
return NetTCPUDP, strings.Replace(upstream, NetTCP+":", "", 1)
}
if strings.HasPrefix(upstream, NetUDP+":") {
log.Log().Warnf("net prefix udp is deprecated, using tcp+udp as default fallback")
return NetTCPUDP, strings.Replace(upstream, NetUDP+":", "", 1)
}
if strings.HasPrefix(upstream, NetTCPUDP+":") {
return NetTCPUDP, strings.Replace(upstream, NetTCPUDP+":", "", 1)
}
if strings.HasPrefix(upstream, NetTCPTLS+":") {
return NetTCPTLS, strings.Replace(upstream, NetTCPTLS+":", "", 1)
}
if strings.HasPrefix(upstream, NetHTTPS+":") {
return NetHTTPS, strings.Replace(upstream, NetHTTPS+":", "", 1)
}
return NetTCPUDP, upstream
}
const (
cfgDefaultPort = "53"
cfgDefaultPrometheusPath = "/metrics"
)
// Config main configuration
// nolint:maligned
type Config struct {
Upstream UpstreamConfig `yaml:"upstream"`
CustomDNS CustomDNSConfig `yaml:"customDNS"`
Conditional ConditionalUpstreamConfig `yaml:"conditional"`
Blocking BlockingConfig `yaml:"blocking"`
ClientLookup ClientLookupConfig `yaml:"clientLookup"`
Caching CachingConfig `yaml:"caching"`
QueryLog QueryLogConfig `yaml:"queryLog"`
Prometheus PrometheusConfig `yaml:"prometheus"`
LogLevel string `yaml:"logLevel"`
LogFormat string `yaml:"logFormat"`
LogTimestamp bool `yaml:"logTimestamp"`
Port string `yaml:"port"`
HTTPPort uint16 `yaml:"httpPort"`
HTTPSPort uint16 `yaml:"httpsPort"`
DisableIPv6 bool `yaml:"disableIPv6"`
CertFile string `yaml:"httpsCertFile"`
KeyFile string `yaml:"httpsKeyFile"`
BootstrapDNS Upstream `yaml:"bootstrapDns"`
}
// PrometheusConfig contains the config values for prometheus
type PrometheusConfig struct {
Enable bool `yaml:"enable"`
Path string `yaml:"path"`
}
// UpstreamConfig upstream server configuration
type UpstreamConfig struct {
ExternalResolvers map[string][]Upstream `yaml:",inline"`
}
// CustomDNSConfig custom DNS configuration
type CustomDNSConfig struct {
Mapping CustomDNSMapping `yaml:"mapping"`
}
// CustomDNSMapping mapping for the custom DNS configuration
type CustomDNSMapping struct {
HostIPs map[string][]net.IP
}
// ConditionalUpstreamConfig conditional upstream configuration
type ConditionalUpstreamConfig struct {
Rewrite map[string]string `yaml:"rewrite"`
Mapping ConditionalUpstreamMapping `yaml:"mapping"`
}
// ConditionalUpstreamMapping mapping for conditional configuration
type ConditionalUpstreamMapping struct {
Upstreams map[string][]Upstream
}
// BlockingConfig configuration for query blocking
type BlockingConfig struct {
BlackLists map[string][]string `yaml:"blackLists"`
WhiteLists map[string][]string `yaml:"whiteLists"`
ClientGroupsBlock map[string][]string `yaml:"clientGroupsBlock"`
BlockType string `yaml:"blockType"`
RefreshPeriod int `yaml:"refreshPeriod"`
}
// ClientLookupConfig configuration for the client lookup
type ClientLookupConfig struct {
ClientnameIPMapping map[string][]net.IP `yaml:"clients"`
Upstream Upstream `yaml:"upstream"`
SingleNameOrder []uint `yaml:"singleNameOrder"`
}
// CachingConfig configuration for domain caching
type CachingConfig struct {
MinCachingTime int `yaml:"minTime"`
MaxCachingTime int `yaml:"maxTime"`
MaxItemsCount int `yaml:"maxItemsCount"`
Prefetching bool `yaml:"prefetching"`
PrefetchExpires int `yaml:"prefetchExpires"`
PrefetchThreshold int `yaml:"prefetchThreshold"`
PrefetchMaxItemsCount int `yaml:"prefetchMaxItemsCount"`
}
// QueryLogConfig configuration for the query logging
type QueryLogConfig struct {
Dir string `yaml:"dir"`
PerClient bool `yaml:"perClient"`
LogRetentionDays uint64 `yaml:"logRetentionDays"`
}
// NewConfig creates new config from YAML file
func NewConfig(path string, mandatory bool) Config {
cfg := Config{}
setDefaultValues(&cfg)
data, err := ioutil.ReadFile(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) && !mandatory {
// config file does not exist
// return config with default values
return cfg
}
log.Log().Fatal("Can't read config file: ", err)
}
err = yaml.UnmarshalStrict(data, &cfg)
if err != nil {
log.Log().Fatal("wrong file structure: ", err)
}
if cfg.LogFormat != log.CfgLogFormatText && cfg.LogFormat != log.CfgLogFormatJSON {
log.Log().Fatal("LogFormat should be 'text' or 'json'")
}
return cfg
}
func setDefaultValues(cfg *Config) {
cfg.Port = cfgDefaultPort
cfg.LogLevel = "info"
cfg.LogFormat = log.CfgLogFormatText
cfg.LogTimestamp = true
cfg.Prometheus.Path = cfgDefaultPrometheusPath
}