1package main
2
3import (
4	"encoding/json"
5	"errors"
6	"flag"
7	"fmt"
8	"math/rand"
9	"net"
10	"net/http"
11	"net/url"
12	"os"
13	"path"
14	"path/filepath"
15	"strconv"
16	"strings"
17	"time"
18
19	"github.com/BurntSushi/toml"
20	"github.com/jedisct1/dlog"
21	stamps "github.com/jedisct1/go-dnsstamps"
22	netproxy "golang.org/x/net/proxy"
23)
24
25const (
26	MaxTimeout             = 3600
27	DefaultNetprobeAddress = "9.9.9.9:53"
28)
29
30type Config struct {
31	LogLevel                 int            `toml:"log_level"`
32	LogFile                  *string        `toml:"log_file"`
33	LogFileLatest            bool           `toml:"log_file_latest"`
34	UseSyslog                bool           `toml:"use_syslog"`
35	ServerNames              []string       `toml:"server_names"`
36	DisabledServerNames      []string       `toml:"disabled_server_names"`
37	ListenAddresses          []string       `toml:"listen_addresses"`
38	LocalDoH                 LocalDoHConfig `toml:"local_doh"`
39	UserName                 string         `toml:"user_name"`
40	ForceTCP                 bool           `toml:"force_tcp"`
41	Timeout                  int            `toml:"timeout"`
42	KeepAlive                int            `toml:"keepalive"`
43	Proxy                    string         `toml:"proxy"`
44	CertRefreshDelay         int            `toml:"cert_refresh_delay"`
45	CertIgnoreTimestamp      bool           `toml:"cert_ignore_timestamp"`
46	EphemeralKeys            bool           `toml:"dnscrypt_ephemeral_keys"`
47	LBStrategy               string         `toml:"lb_strategy"`
48	LBEstimator              bool           `toml:"lb_estimator"`
49	BlockIPv6                bool           `toml:"block_ipv6"`
50	BlockUnqualified         bool           `toml:"block_unqualified"`
51	BlockUndelegated         bool           `toml:"block_undelegated"`
52	Cache                    bool
53	CacheSize                int                         `toml:"cache_size"`
54	CacheNegTTL              uint32                      `toml:"cache_neg_ttl"`
55	CacheNegMinTTL           uint32                      `toml:"cache_neg_min_ttl"`
56	CacheNegMaxTTL           uint32                      `toml:"cache_neg_max_ttl"`
57	CacheMinTTL              uint32                      `toml:"cache_min_ttl"`
58	CacheMaxTTL              uint32                      `toml:"cache_max_ttl"`
59	RejectTTL                uint32                      `toml:"reject_ttl"`
60	CloakTTL                 uint32                      `toml:"cloak_ttl"`
61	QueryLog                 QueryLogConfig              `toml:"query_log"`
62	NxLog                    NxLogConfig                 `toml:"nx_log"`
63	BlockName                BlockNameConfig             `toml:"blocked_names"`
64	BlockNameLegacy          BlockNameConfigLegacy       `toml:"blacklist"`
65	WhitelistNameLegacy      WhitelistNameConfigLegacy   `toml:"whitelist"`
66	AllowedName              AllowedNameConfig           `toml:"allowed_names"`
67	BlockIP                  BlockIPConfig               `toml:"blocked_ips"`
68	BlockIPLegacy            BlockIPConfigLegacy         `toml:"ip_blacklist"`
69	AllowIP                  AllowIPConfig               `toml:"allowed_ips"`
70	ForwardFile              string                      `toml:"forwarding_rules"`
71	CloakFile                string                      `toml:"cloaking_rules"`
72	CaptivePortals           CaptivePortalsConfig        `toml:"captive_portals"`
73	StaticsConfig            map[string]StaticConfig     `toml:"static"`
74	SourcesConfig            map[string]SourceConfig     `toml:"sources"`
75	BrokenImplementations    BrokenImplementationsConfig `toml:"broken_implementations"`
76	SourceRequireDNSSEC      bool                        `toml:"require_dnssec"`
77	SourceRequireNoLog       bool                        `toml:"require_nolog"`
78	SourceRequireNoFilter    bool                        `toml:"require_nofilter"`
79	SourceDNSCrypt           bool                        `toml:"dnscrypt_servers"`
80	SourceDoH                bool                        `toml:"doh_servers"`
81	SourceODoH               bool                        `toml:"odoh_servers"`
82	SourceIPv4               bool                        `toml:"ipv4_servers"`
83	SourceIPv6               bool                        `toml:"ipv6_servers"`
84	MaxClients               uint32                      `toml:"max_clients"`
85	BootstrapResolversLegacy []string                    `toml:"fallback_resolvers"`
86	BootstrapResolvers       []string                    `toml:"bootstrap_resolvers"`
87	IgnoreSystemDNS          bool                        `toml:"ignore_system_dns"`
88	AllWeeklyRanges          map[string]WeeklyRangesStr  `toml:"schedules"`
89	LogMaxSize               int                         `toml:"log_files_max_size"`
90	LogMaxAge                int                         `toml:"log_files_max_age"`
91	LogMaxBackups            int                         `toml:"log_files_max_backups"`
92	TLSDisableSessionTickets bool                        `toml:"tls_disable_session_tickets"`
93	TLSCipherSuite           []uint16                    `toml:"tls_cipher_suite"`
94	NetprobeAddress          string                      `toml:"netprobe_address"`
95	NetprobeTimeout          int                         `toml:"netprobe_timeout"`
96	OfflineMode              bool                        `toml:"offline_mode"`
97	HTTPProxyURL             string                      `toml:"http_proxy"`
98	RefusedCodeInResponses   bool                        `toml:"refused_code_in_responses"`
99	BlockedQueryResponse     string                      `toml:"blocked_query_response"`
100	QueryMeta                []string                    `toml:"query_meta"`
101	AnonymizedDNS            AnonymizedDNSConfig         `toml:"anonymized_dns"`
102	DoHClientX509Auth        DoHClientX509AuthConfig     `toml:"doh_client_x509_auth"`
103	DoHClientX509AuthLegacy  DoHClientX509AuthConfig     `toml:"tls_client_auth"`
104	DNS64                    DNS64Config                 `toml:"dns64"`
105	EDNSClientSubnet         []string                    `toml:"edns_client_subnet"`
106}
107
108func newConfig() Config {
109	return Config{
110		LogLevel:                 int(dlog.LogLevel()),
111		LogFileLatest:            true,
112		ListenAddresses:          []string{"127.0.0.1:53"},
113		LocalDoH:                 LocalDoHConfig{Path: "/dns-query"},
114		Timeout:                  5000,
115		KeepAlive:                5,
116		CertRefreshDelay:         240,
117		CertIgnoreTimestamp:      false,
118		EphemeralKeys:            false,
119		Cache:                    true,
120		CacheSize:                512,
121		CacheNegTTL:              0,
122		CacheNegMinTTL:           60,
123		CacheNegMaxTTL:           600,
124		CacheMinTTL:              60,
125		CacheMaxTTL:              86400,
126		RejectTTL:                600,
127		CloakTTL:                 600,
128		SourceRequireNoLog:       true,
129		SourceRequireNoFilter:    true,
130		SourceIPv4:               true,
131		SourceIPv6:               false,
132		SourceDNSCrypt:           true,
133		SourceDoH:                true,
134		SourceODoH:               false,
135		MaxClients:               250,
136		BootstrapResolvers:       []string{DefaultBootstrapResolver},
137		IgnoreSystemDNS:          false,
138		LogMaxSize:               10,
139		LogMaxAge:                7,
140		LogMaxBackups:            1,
141		TLSDisableSessionTickets: false,
142		TLSCipherSuite:           nil,
143		NetprobeTimeout:          60,
144		OfflineMode:              false,
145		RefusedCodeInResponses:   false,
146		LBEstimator:              true,
147		BlockedQueryResponse:     "hinfo",
148		BrokenImplementations: BrokenImplementationsConfig{
149			FragmentsBlocked: []string{
150				"cisco", "cisco-ipv6", "cisco-familyshield", "cisco-familyshield-ipv6",
151				"cleanbrowsing-adult", "cleanbrowsing-adult-ipv6", "cleanbrowsing-family", "cleanbrowsing-family-ipv6", "cleanbrowsing-security", "cleanbrowsing-security-ipv6",
152			},
153		},
154		AnonymizedDNS: AnonymizedDNSConfig{
155			DirectCertFallback: true,
156		},
157	}
158}
159
160type StaticConfig struct {
161	Stamp string
162}
163
164type SourceConfig struct {
165	URL            string
166	URLs           []string
167	MinisignKeyStr string `toml:"minisign_key"`
168	CacheFile      string `toml:"cache_file"`
169	FormatStr      string `toml:"format"`
170	RefreshDelay   int    `toml:"refresh_delay"`
171	Prefix         string
172}
173
174type QueryLogConfig struct {
175	File          string
176	Format        string
177	IgnoredQtypes []string `toml:"ignored_qtypes"`
178}
179
180type NxLogConfig struct {
181	File   string
182	Format string
183}
184
185type BlockNameConfig struct {
186	File    string `toml:"blocked_names_file"`
187	LogFile string `toml:"log_file"`
188	Format  string `toml:"log_format"`
189}
190
191type BlockNameConfigLegacy struct {
192	File    string `toml:"blacklist_file"`
193	LogFile string `toml:"log_file"`
194	Format  string `toml:"log_format"`
195}
196
197type WhitelistNameConfigLegacy struct {
198	File    string `toml:"whitelist_file"`
199	LogFile string `toml:"log_file"`
200	Format  string `toml:"log_format"`
201}
202
203type AllowedNameConfig struct {
204	File    string `toml:"allowed_names_file"`
205	LogFile string `toml:"log_file"`
206	Format  string `toml:"log_format"`
207}
208
209type BlockIPConfig struct {
210	File    string `toml:"blocked_ips_file"`
211	LogFile string `toml:"log_file"`
212	Format  string `toml:"log_format"`
213}
214
215type BlockIPConfigLegacy struct {
216	File    string `toml:"blacklist_file"`
217	LogFile string `toml:"log_file"`
218	Format  string `toml:"log_format"`
219}
220
221type AllowIPConfig struct {
222	File    string `toml:"allowed_ips_file"`
223	LogFile string `toml:"log_file"`
224	Format  string `toml:"log_format"`
225}
226
227type AnonymizedDNSRouteConfig struct {
228	ServerName string   `toml:"server_name"`
229	RelayNames []string `toml:"via"`
230}
231
232type AnonymizedDNSConfig struct {
233	Routes             []AnonymizedDNSRouteConfig `toml:"routes"`
234	SkipIncompatible   bool                       `toml:"skip_incompatible"`
235	DirectCertFallback bool                       `toml:"direct_cert_fallback"`
236}
237
238type BrokenImplementationsConfig struct {
239	BrokenQueryPadding []string `toml:"broken_query_padding"`
240	FragmentsBlocked   []string `toml:"fragments_blocked"`
241}
242
243type LocalDoHConfig struct {
244	ListenAddresses []string `toml:"listen_addresses"`
245	Path            string   `toml:"path"`
246	CertFile        string   `toml:"cert_file"`
247	CertKeyFile     string   `toml:"cert_key_file"`
248}
249
250type ServerSummary struct {
251	Name        string   `json:"name"`
252	Proto       string   `json:"proto"`
253	IPv6        bool     `json:"ipv6"`
254	Addrs       []string `json:"addrs,omitempty"`
255	Ports       []int    `json:"ports"`
256	DNSSEC      bool     `json:"dnssec"`
257	NoLog       bool     `json:"nolog"`
258	NoFilter    bool     `json:"nofilter"`
259	Description string   `json:"description,omitempty"`
260	Stamp       string   `json:"stamp"`
261}
262
263type TLSClientAuthCredsConfig struct {
264	ServerName string `toml:"server_name"`
265	ClientCert string `toml:"client_cert"`
266	ClientKey  string `toml:"client_key"`
267	RootCA     string `toml:"root_ca"`
268}
269
270type DoHClientX509AuthConfig struct {
271	Creds []TLSClientAuthCredsConfig `toml:"creds"`
272}
273
274type DNS64Config struct {
275	Prefixes  []string `toml:"prefix"`
276	Resolvers []string `toml:"resolver"`
277}
278
279type CaptivePortalsConfig struct {
280	MapFile string `toml:"map_file"`
281}
282
283type ConfigFlags struct {
284	Resolve                 *string
285	List                    *bool
286	ListAll                 *bool
287	JSONOutput              *bool
288	Check                   *bool
289	ConfigFile              *string
290	Child                   *bool
291	NetprobeTimeoutOverride *int
292	ShowCerts               *bool
293}
294
295func findConfigFile(configFile *string) (string, error) {
296	if _, err := os.Stat(*configFile); os.IsNotExist(err) {
297		cdLocal()
298		if _, err := os.Stat(*configFile); err != nil {
299			return "", err
300		}
301	}
302	pwd, err := os.Getwd()
303	if err != nil {
304		return "", err
305	}
306	if filepath.IsAbs(*configFile) {
307		return *configFile, nil
308	}
309	return path.Join(pwd, *configFile), nil
310}
311
312func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
313	foundConfigFile, err := findConfigFile(flags.ConfigFile)
314	if err != nil {
315		return fmt.Errorf("Unable to load the configuration file [%s] -- Maybe use the -config command-line switch?", *flags.ConfigFile)
316	}
317	config := newConfig()
318	md, err := toml.DecodeFile(foundConfigFile, &config)
319	if err != nil {
320		return err
321	}
322
323	if flags.Resolve != nil && len(*flags.Resolve) > 0 {
324		addr := "127.0.0.1:53"
325		if len(config.ListenAddresses) > 0 {
326			addr = config.ListenAddresses[0]
327		}
328		Resolve(addr, *flags.Resolve, len(config.ServerNames) == 1)
329		os.Exit(0)
330	}
331
332	if err := cdFileDir(foundConfigFile); err != nil {
333		return err
334	}
335	if config.LogLevel >= 0 && config.LogLevel < int(dlog.SeverityLast) {
336		dlog.SetLogLevel(dlog.Severity(config.LogLevel))
337	}
338	if dlog.LogLevel() <= dlog.SeverityDebug && os.Getenv("DEBUG") == "" {
339		dlog.SetLogLevel(dlog.SeverityInfo)
340	}
341	dlog.TruncateLogFile(config.LogFileLatest)
342	if config.UseSyslog {
343		dlog.UseSyslog(true)
344	} else if config.LogFile != nil {
345		dlog.UseLogFile(*config.LogFile)
346		if !*flags.Child {
347			FileDescriptors = append(FileDescriptors, dlog.GetFileDescriptor())
348		} else {
349			dlog.SetFileDescriptor(os.NewFile(uintptr(InheritedDescriptorsBase+FileDescriptorNum), "logFile"))
350			FileDescriptorNum++
351		}
352	}
353	if !*flags.Child {
354		dlog.Noticef("dnscrypt-proxy %s", AppVersion)
355	}
356	undecoded := md.Undecoded()
357	if len(undecoded) > 0 {
358		return fmt.Errorf("Unsupported key in configuration file: [%s]", undecoded[0])
359	}
360
361	proxy.logMaxSize = config.LogMaxSize
362	proxy.logMaxAge = config.LogMaxAge
363	proxy.logMaxBackups = config.LogMaxBackups
364
365	proxy.userName = config.UserName
366
367	proxy.child = *flags.Child
368	proxy.xTransport = NewXTransport()
369	proxy.xTransport.tlsDisableSessionTickets = config.TLSDisableSessionTickets
370	proxy.xTransport.tlsCipherSuite = config.TLSCipherSuite
371	proxy.xTransport.mainProto = proxy.mainProto
372	if len(config.BootstrapResolvers) == 0 && len(config.BootstrapResolversLegacy) > 0 {
373		dlog.Warnf("fallback_resolvers was renamed to bootstrap_resolvers - Please update your configuration")
374		config.BootstrapResolvers = config.BootstrapResolversLegacy
375	}
376	if len(config.BootstrapResolvers) > 0 {
377		for _, resolver := range config.BootstrapResolvers {
378			if err := isIPAndPort(resolver); err != nil {
379				return fmt.Errorf("Bootstrap resolver [%v]: %v", resolver, err)
380			}
381		}
382		proxy.xTransport.ignoreSystemDNS = config.IgnoreSystemDNS
383	}
384	proxy.xTransport.bootstrapResolvers = config.BootstrapResolvers
385	proxy.xTransport.useIPv4 = config.SourceIPv4
386	proxy.xTransport.useIPv6 = config.SourceIPv6
387	proxy.xTransport.keepAlive = time.Duration(config.KeepAlive) * time.Second
388	if len(config.HTTPProxyURL) > 0 {
389		httpProxyURL, err := url.Parse(config.HTTPProxyURL)
390		if err != nil {
391			return fmt.Errorf("Unable to parse the HTTP proxy URL [%v]", config.HTTPProxyURL)
392		}
393		proxy.xTransport.httpProxyFunction = http.ProxyURL(httpProxyURL)
394	}
395
396	if len(config.Proxy) > 0 {
397		proxyDialerURL, err := url.Parse(config.Proxy)
398		if err != nil {
399			return fmt.Errorf("Unable to parse the proxy URL [%v]", config.Proxy)
400		}
401		proxyDialer, err := netproxy.FromURL(proxyDialerURL, netproxy.Direct)
402		if err != nil {
403			return fmt.Errorf("Unable to use the proxy: [%v]", err)
404		}
405		proxy.xTransport.proxyDialer = &proxyDialer
406		proxy.mainProto = "tcp"
407	}
408
409	proxy.xTransport.rebuildTransport()
410
411	if md.IsDefined("refused_code_in_responses") {
412		dlog.Notice("config option `refused_code_in_responses` is deprecated, use `blocked_query_response`")
413		if config.RefusedCodeInResponses {
414			config.BlockedQueryResponse = "refused"
415		} else {
416			config.BlockedQueryResponse = "hinfo"
417		}
418	}
419	proxy.blockedQueryResponse = config.BlockedQueryResponse
420	proxy.timeout = time.Duration(config.Timeout) * time.Millisecond
421	proxy.maxClients = config.MaxClients
422	proxy.mainProto = "udp"
423	if config.ForceTCP {
424		proxy.mainProto = "tcp"
425	}
426	proxy.certRefreshDelay = time.Duration(Max(60, config.CertRefreshDelay)) * time.Minute
427	proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second)
428	proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp
429	proxy.ephemeralKeys = config.EphemeralKeys
430	if len(config.ListenAddresses) == 0 && len(config.LocalDoH.ListenAddresses) == 0 {
431		dlog.Debug("No local IP/port configured")
432	}
433	lbStrategy := LBStrategy(DefaultLBStrategy)
434	switch lbStrategyStr := strings.ToLower(config.LBStrategy); lbStrategyStr {
435	case "":
436		// default
437	case "p2":
438		lbStrategy = LBStrategyP2{}
439	case "ph":
440		lbStrategy = LBStrategyPH{}
441	case "fastest":
442	case "first":
443		lbStrategy = LBStrategyFirst{}
444	case "random":
445		lbStrategy = LBStrategyRandom{}
446	default:
447		if strings.HasPrefix(lbStrategyStr, "p") {
448			n, err := strconv.ParseInt(strings.TrimPrefix(lbStrategyStr, "p"), 10, 32)
449			if err != nil || n <= 0 {
450				dlog.Warnf("Invalid load balancing strategy: [%s]", config.LBStrategy)
451			} else {
452				lbStrategy = LBStrategyPN{n: int(n)}
453			}
454		} else {
455			dlog.Warnf("Unknown load balancing strategy: [%s]", config.LBStrategy)
456		}
457	}
458	proxy.serversInfo.lbStrategy = lbStrategy
459	proxy.serversInfo.lbEstimator = config.LBEstimator
460
461	proxy.listenAddresses = config.ListenAddresses
462	proxy.localDoHListenAddresses = config.LocalDoH.ListenAddresses
463	if len(config.LocalDoH.Path) > 0 && config.LocalDoH.Path[0] != '/' {
464		return fmt.Errorf("local DoH: [%s] cannot be a valid URL path. Read the documentation", config.LocalDoH.Path)
465	}
466	proxy.localDoHPath = config.LocalDoH.Path
467	proxy.localDoHCertFile = config.LocalDoH.CertFile
468	proxy.localDoHCertKeyFile = config.LocalDoH.CertKeyFile
469	proxy.pluginBlockIPv6 = config.BlockIPv6
470	proxy.pluginBlockUnqualified = config.BlockUnqualified
471	proxy.pluginBlockUndelegated = config.BlockUndelegated
472	proxy.cache = config.Cache
473	proxy.cacheSize = config.CacheSize
474
475	if config.CacheNegTTL > 0 {
476		proxy.cacheNegMinTTL = config.CacheNegTTL
477		proxy.cacheNegMaxTTL = config.CacheNegTTL
478	} else {
479		proxy.cacheNegMinTTL = config.CacheNegMinTTL
480		proxy.cacheNegMaxTTL = config.CacheNegMaxTTL
481	}
482
483	proxy.cacheMinTTL = config.CacheMinTTL
484	proxy.cacheMaxTTL = config.CacheMaxTTL
485	proxy.rejectTTL = config.RejectTTL
486	proxy.cloakTTL = config.CloakTTL
487
488	proxy.queryMeta = config.QueryMeta
489
490	if len(config.EDNSClientSubnet) != 0 {
491		proxy.ednsClientSubnets = make([]*net.IPNet, 0)
492		for _, cidr := range config.EDNSClientSubnet {
493			_, net, err := net.ParseCIDR(cidr)
494			if err != nil {
495				return fmt.Errorf("Invalid EDNS-client-subnet CIDR: [%v]", cidr)
496			}
497			proxy.ednsClientSubnets = append(proxy.ednsClientSubnets, net)
498		}
499	}
500
501	if len(config.QueryLog.Format) == 0 {
502		config.QueryLog.Format = "tsv"
503	} else {
504		config.QueryLog.Format = strings.ToLower(config.QueryLog.Format)
505	}
506	if config.QueryLog.Format != "tsv" && config.QueryLog.Format != "ltsv" {
507		return errors.New("Unsupported query log format")
508	}
509	proxy.queryLogFile = config.QueryLog.File
510	proxy.queryLogFormat = config.QueryLog.Format
511	proxy.queryLogIgnoredQtypes = config.QueryLog.IgnoredQtypes
512
513	if len(config.NxLog.Format) == 0 {
514		config.NxLog.Format = "tsv"
515	} else {
516		config.NxLog.Format = strings.ToLower(config.NxLog.Format)
517	}
518	if config.NxLog.Format != "tsv" && config.NxLog.Format != "ltsv" {
519		return errors.New("Unsupported NX log format")
520	}
521	proxy.nxLogFile = config.NxLog.File
522	proxy.nxLogFormat = config.NxLog.Format
523
524	if len(config.BlockName.File) > 0 && len(config.BlockNameLegacy.File) > 0 {
525		return errors.New("Don't specify both [blocked_names] and [blacklist] sections - Update your config file")
526	}
527	if len(config.BlockNameLegacy.File) > 0 {
528		dlog.Notice("Use of [blacklist] is deprecated - Update your config file")
529		config.BlockName.File = config.BlockNameLegacy.File
530		config.BlockName.Format = config.BlockNameLegacy.Format
531		config.BlockName.LogFile = config.BlockNameLegacy.LogFile
532	}
533	if len(config.BlockName.Format) == 0 {
534		config.BlockName.Format = "tsv"
535	} else {
536		config.BlockName.Format = strings.ToLower(config.BlockName.Format)
537	}
538	if config.BlockName.Format != "tsv" && config.BlockName.Format != "ltsv" {
539		return errors.New("Unsupported block log format")
540	}
541	proxy.blockNameFile = config.BlockName.File
542	proxy.blockNameFormat = config.BlockName.Format
543	proxy.blockNameLogFile = config.BlockName.LogFile
544
545	if len(config.AllowedName.File) > 0 && len(config.WhitelistNameLegacy.File) > 0 {
546		return errors.New("Don't specify both [whitelist] and [allowed_names] sections - Update your config file")
547	}
548	if len(config.WhitelistNameLegacy.File) > 0 {
549		dlog.Notice("Use of [whitelist] is deprecated - Update your config file")
550		config.AllowedName.File = config.WhitelistNameLegacy.File
551		config.AllowedName.Format = config.WhitelistNameLegacy.Format
552		config.AllowedName.LogFile = config.WhitelistNameLegacy.LogFile
553	}
554	if len(config.AllowedName.Format) == 0 {
555		config.AllowedName.Format = "tsv"
556	} else {
557		config.AllowedName.Format = strings.ToLower(config.AllowedName.Format)
558	}
559	if config.AllowedName.Format != "tsv" && config.AllowedName.Format != "ltsv" {
560		return errors.New("Unsupported allowed_names log format")
561	}
562	proxy.allowNameFile = config.AllowedName.File
563	proxy.allowNameFormat = config.AllowedName.Format
564	proxy.allowNameLogFile = config.AllowedName.LogFile
565
566	if len(config.BlockIP.File) > 0 && len(config.BlockIPLegacy.File) > 0 {
567		return errors.New("Don't specify both [blocked_ips] and [ip_blacklist] sections - Update your config file")
568	}
569	if len(config.BlockIPLegacy.File) > 0 {
570		dlog.Notice("Use of [ip_blacklist] is deprecated - Update your config file")
571		config.BlockIP.File = config.BlockIPLegacy.File
572		config.BlockIP.Format = config.BlockIPLegacy.Format
573		config.BlockIP.LogFile = config.BlockIPLegacy.LogFile
574	}
575	if len(config.BlockIP.Format) == 0 {
576		config.BlockIP.Format = "tsv"
577	} else {
578		config.BlockIP.Format = strings.ToLower(config.BlockIP.Format)
579	}
580	if config.BlockIP.Format != "tsv" && config.BlockIP.Format != "ltsv" {
581		return errors.New("Unsupported IP block log format")
582	}
583	proxy.blockIPFile = config.BlockIP.File
584	proxy.blockIPFormat = config.BlockIP.Format
585	proxy.blockIPLogFile = config.BlockIP.LogFile
586
587	if len(config.AllowIP.Format) == 0 {
588		config.AllowIP.Format = "tsv"
589	} else {
590		config.AllowIP.Format = strings.ToLower(config.AllowIP.Format)
591	}
592	if config.AllowIP.Format != "tsv" && config.AllowIP.Format != "ltsv" {
593		return errors.New("Unsupported allowed_ips log format")
594	}
595	proxy.allowedIPFile = config.AllowIP.File
596	proxy.allowedIPFormat = config.AllowIP.Format
597	proxy.allowedIPLogFile = config.AllowIP.LogFile
598
599	proxy.forwardFile = config.ForwardFile
600	proxy.cloakFile = config.CloakFile
601	proxy.captivePortalMapFile = config.CaptivePortals.MapFile
602
603	allWeeklyRanges, err := ParseAllWeeklyRanges(config.AllWeeklyRanges)
604	if err != nil {
605		return err
606	}
607	proxy.allWeeklyRanges = allWeeklyRanges
608
609	if configRoutes := config.AnonymizedDNS.Routes; configRoutes != nil {
610		routes := make(map[string][]string)
611		for _, configRoute := range configRoutes {
612			routes[configRoute.ServerName] = configRoute.RelayNames
613		}
614		proxy.routes = &routes
615	}
616	proxy.skipAnonIncompatibleResolvers = config.AnonymizedDNS.SkipIncompatible
617	proxy.anonDirectCertFallback = config.AnonymizedDNS.DirectCertFallback
618
619	if config.DoHClientX509AuthLegacy.Creds != nil {
620		return errors.New("[tls_client_auth] has been renamed to [doh_client_x509_auth] - Update your config file")
621	}
622	dohClientCreds := config.DoHClientX509Auth.Creds
623	if len(dohClientCreds) > 0 {
624		dlog.Noticef("Enabling TLS authentication")
625		configClientCred := dohClientCreds[0]
626		if len(dohClientCreds) > 1 {
627			dlog.Fatal("Only one tls_client_auth entry is currently supported")
628		}
629		proxy.xTransport.tlsClientCreds = DOHClientCreds{
630			clientCert: configClientCred.ClientCert,
631			clientKey:  configClientCred.ClientKey,
632			rootCA:     configClientCred.RootCA,
633		}
634		proxy.xTransport.rebuildTransport()
635	}
636
637	// Backwards compatibility
638	config.BrokenImplementations.FragmentsBlocked = append(config.BrokenImplementations.FragmentsBlocked, config.BrokenImplementations.BrokenQueryPadding...)
639
640	proxy.serversBlockingFragments = config.BrokenImplementations.FragmentsBlocked
641
642	proxy.dns64Prefixes = config.DNS64.Prefixes
643	proxy.dns64Resolvers = config.DNS64.Resolvers
644
645	if *flags.ListAll {
646		config.ServerNames = nil
647		config.DisabledServerNames = nil
648		config.SourceRequireDNSSEC = false
649		config.SourceRequireNoFilter = false
650		config.SourceRequireNoLog = false
651		config.SourceIPv4 = true
652		config.SourceIPv6 = true
653		config.SourceDNSCrypt = true
654		config.SourceDoH = true
655		config.SourceODoH = true
656	}
657
658	var requiredProps stamps.ServerInformalProperties
659	if config.SourceRequireDNSSEC {
660		requiredProps |= stamps.ServerInformalPropertyDNSSEC
661	}
662	if config.SourceRequireNoLog {
663		requiredProps |= stamps.ServerInformalPropertyNoLog
664	}
665	if config.SourceRequireNoFilter {
666		requiredProps |= stamps.ServerInformalPropertyNoFilter
667	}
668	proxy.requiredProps = requiredProps
669	proxy.ServerNames = config.ServerNames
670	proxy.DisabledServerNames = config.DisabledServerNames
671	proxy.SourceIPv4 = config.SourceIPv4
672	proxy.SourceIPv6 = config.SourceIPv6
673	proxy.SourceDNSCrypt = config.SourceDNSCrypt
674	proxy.SourceDoH = config.SourceDoH
675	proxy.SourceODoH = config.SourceODoH
676
677	netprobeTimeout := config.NetprobeTimeout
678	flag.Visit(func(flag *flag.Flag) {
679		if flag.Name == "netprobe-timeout" && flags.NetprobeTimeoutOverride != nil {
680			netprobeTimeout = *flags.NetprobeTimeoutOverride
681		}
682	})
683	netprobeAddress := DefaultNetprobeAddress
684	if len(config.NetprobeAddress) > 0 {
685		netprobeAddress = config.NetprobeAddress
686	} else if len(config.BootstrapResolvers) > 0 {
687		netprobeAddress = config.BootstrapResolvers[0]
688	}
689	proxy.showCerts = *flags.ShowCerts || len(os.Getenv("SHOW_CERTS")) > 0
690	if !*flags.Check && !*flags.ShowCerts && !*flags.List && !*flags.ListAll {
691		if err := NetProbe(proxy, netprobeAddress, netprobeTimeout); err != nil {
692			return err
693		}
694		for _, listenAddrStr := range proxy.listenAddresses {
695			proxy.addDNSListener(listenAddrStr)
696		}
697		for _, listenAddrStr := range proxy.localDoHListenAddresses {
698			proxy.addLocalDoHListener(listenAddrStr)
699		}
700		if err := proxy.addSystemDListeners(); err != nil {
701			return err
702		}
703	}
704	// if 'userName' is set and we are the parent process drop privilege and exit
705	if len(proxy.userName) > 0 && !proxy.child {
706		proxy.dropPrivilege(proxy.userName, FileDescriptors)
707		return errors.New("Dropping privileges is not supporting on this operating system. Unset `user_name` in the configuration file")
708	}
709	if !config.OfflineMode {
710		if err := config.loadSources(proxy); err != nil {
711			return err
712		}
713		if len(proxy.registeredServers) == 0 {
714			return errors.New("No servers configured")
715		}
716	}
717	if *flags.List || *flags.ListAll {
718		if err := config.printRegisteredServers(proxy, *flags.JSONOutput); err != nil {
719			return err
720		}
721		os.Exit(0)
722	}
723	if proxy.routes != nil && len(*proxy.routes) > 0 {
724		hasSpecificRoutes := false
725		for _, server := range proxy.registeredServers {
726			if via, ok := (*proxy.routes)[server.name]; ok {
727				if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt && server.stamp.Proto != stamps.StampProtoTypeODoHTarget {
728					dlog.Errorf("DNS anonymization is only supported with the DNSCrypt and ODoH protocols - Connections to [%v] cannot be anonymized", server.name)
729				} else {
730					dlog.Noticef("Anonymized DNS: routing [%v] via %v", server.name, via)
731				}
732				hasSpecificRoutes = true
733			}
734		}
735		if via, ok := (*proxy.routes)["*"]; ok {
736			if hasSpecificRoutes {
737				dlog.Noticef("Anonymized DNS: routing everything else via %v", via)
738			} else {
739				dlog.Noticef("Anonymized DNS: routing everything via %v", via)
740			}
741		}
742	}
743	if *flags.Check {
744		dlog.Notice("Configuration successfully checked")
745		os.Exit(0)
746	}
747	return nil
748}
749
750func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool) error {
751	var summary []ServerSummary
752	for _, registeredServer := range proxy.registeredServers {
753		addrStr, port := registeredServer.stamp.ServerAddrStr, stamps.DefaultPort
754		var hostAddr string
755		hostAddr, port = ExtractHostAndPort(addrStr, port)
756		addrs := make([]string, 0)
757		if registeredServer.stamp.Proto == stamps.StampProtoTypeDoH && len(registeredServer.stamp.ProviderName) > 0 {
758			providerName := registeredServer.stamp.ProviderName
759			var host string
760			host, port = ExtractHostAndPort(providerName, port)
761			addrs = append(addrs, host)
762		}
763		if len(addrStr) > 0 {
764			addrs = append(addrs, hostAddr)
765		}
766		serverSummary := ServerSummary{
767			Name:        registeredServer.name,
768			Proto:       registeredServer.stamp.Proto.String(),
769			IPv6:        strings.HasPrefix(addrStr, "["),
770			Ports:       []int{port},
771			Addrs:       addrs,
772			DNSSEC:      registeredServer.stamp.Props&stamps.ServerInformalPropertyDNSSEC != 0,
773			NoLog:       registeredServer.stamp.Props&stamps.ServerInformalPropertyNoLog != 0,
774			NoFilter:    registeredServer.stamp.Props&stamps.ServerInformalPropertyNoFilter != 0,
775			Description: registeredServer.description,
776			Stamp:       registeredServer.stamp.String(),
777		}
778		if jsonOutput {
779			summary = append(summary, serverSummary)
780		} else {
781			fmt.Println(serverSummary.Name)
782		}
783	}
784	if jsonOutput {
785		jsonStr, err := json.MarshalIndent(summary, "", " ")
786		if err != nil {
787			return err
788		}
789		fmt.Print(string(jsonStr))
790	}
791	return nil
792}
793
794func (config *Config) loadSources(proxy *Proxy) error {
795	for cfgSourceName, cfgSource_ := range config.SourcesConfig {
796		cfgSource := cfgSource_
797		rand.Shuffle(len(cfgSource.URLs), func(i, j int) {
798			cfgSource.URLs[i], cfgSource.URLs[j] = cfgSource.URLs[j], cfgSource.URLs[i]
799		})
800		if err := config.loadSource(proxy, cfgSourceName, &cfgSource); err != nil {
801			return err
802		}
803	}
804	for name, config := range config.StaticsConfig {
805		if stamp, err := stamps.NewServerStampFromString(config.Stamp); err == nil {
806			if stamp.Proto == stamps.StampProtoTypeDNSCryptRelay || stamp.Proto == stamps.StampProtoTypeODoHRelay {
807				dlog.Debugf("Adding [%s] to the set of available static relays", name)
808				registeredServer := RegisteredServer{name: name, stamp: stamp, description: "static relay"}
809				proxy.registeredRelays = append(proxy.registeredRelays, registeredServer)
810			}
811		}
812	}
813	if len(config.ServerNames) == 0 {
814		for serverName := range config.StaticsConfig {
815			config.ServerNames = append(config.ServerNames, serverName)
816		}
817	}
818	for _, serverName := range config.ServerNames {
819		staticConfig, ok := config.StaticsConfig[serverName]
820		if !ok {
821			continue
822		}
823		if len(staticConfig.Stamp) == 0 {
824			return fmt.Errorf("Missing stamp for the static [%s] definition", serverName)
825		}
826		stamp, err := stamps.NewServerStampFromString(staticConfig.Stamp)
827		if err != nil {
828			return fmt.Errorf("Stamp error for the static [%s] definition: [%v]", serverName, err)
829		}
830		proxy.registeredServers = append(proxy.registeredServers, RegisteredServer{name: serverName, stamp: stamp})
831	}
832	proxy.updateRegisteredServers()
833	rs1 := proxy.registeredServers
834	rs2 := proxy.serversInfo.registeredServers
835	rand.Shuffle(len(rs1), func(i, j int) {
836		rs1[i], rs1[j] = rs1[j], rs1[i]
837	})
838	rand.Shuffle(len(rs2), func(i, j int) {
839		rs2[i], rs2[j] = rs2[j], rs2[i]
840	})
841	return nil
842}
843
844func (config *Config) loadSource(proxy *Proxy, cfgSourceName string, cfgSource *SourceConfig) error {
845	if len(cfgSource.URLs) == 0 {
846		if len(cfgSource.URL) == 0 {
847			dlog.Debugf("Missing URLs for source [%s]", cfgSourceName)
848		} else {
849			cfgSource.URLs = []string{cfgSource.URL}
850		}
851	}
852	if cfgSource.MinisignKeyStr == "" {
853		return fmt.Errorf("Missing Minisign key for source [%s]", cfgSourceName)
854	}
855	if cfgSource.CacheFile == "" {
856		return fmt.Errorf("Missing cache file for source [%s]", cfgSourceName)
857	}
858	if cfgSource.FormatStr == "" {
859		cfgSource.FormatStr = "v2"
860	}
861	if cfgSource.RefreshDelay <= 0 {
862		cfgSource.RefreshDelay = 72
863	} else if cfgSource.RefreshDelay > 168 {
864		cfgSource.RefreshDelay = 168
865	}
866	source, err := NewSource(cfgSourceName, proxy.xTransport, cfgSource.URLs, cfgSource.MinisignKeyStr, cfgSource.CacheFile, cfgSource.FormatStr, time.Duration(cfgSource.RefreshDelay)*time.Hour, cfgSource.Prefix)
867	if err != nil {
868		if len(source.in) <= 0 {
869			dlog.Criticalf("Unable to retrieve source [%s]: [%s]", cfgSourceName, err)
870			return err
871		}
872		dlog.Infof("Downloading [%s] failed: %v, using cache file to startup", source.name, err)
873	}
874	proxy.sources = append(proxy.sources, source)
875	return nil
876}
877
878func includesName(names []string, name string) bool {
879	for _, found := range names {
880		if strings.EqualFold(found, name) {
881			return true
882		}
883	}
884	return false
885}
886
887func cdFileDir(fileName string) error {
888	return os.Chdir(filepath.Dir(fileName))
889}
890
891func cdLocal() {
892	exeFileName, err := os.Executable()
893	if err != nil {
894		dlog.Warnf("Unable to determine the executable directory: [%s] -- You will need to specify absolute paths in the configuration file", err)
895	} else if err := os.Chdir(filepath.Dir(exeFileName)); err != nil {
896		dlog.Warnf("Unable to change working directory to [%s]: %s", exeFileName, err)
897	}
898}
899
900func isIPAndPort(addrStr string) error {
901	host, port := ExtractHostAndPort(addrStr, -1)
902	if ip := ParseIP(host); ip == nil {
903		return fmt.Errorf("Host does not parse as IP '%s'", addrStr)
904	} else if port == -1 {
905		return fmt.Errorf("Port missing '%s'", addrStr)
906	} else if _, err := strconv.ParseUint(strconv.Itoa(port), 10, 16); err != nil {
907		return fmt.Errorf("Port does not parse '%s' [%v]", addrStr, err)
908	}
909	return nil
910}
911