dnscrypt-proxy/dnscrypt-proxy/config.go

886 lines
29 KiB
Go
Raw Normal View History

2018-01-10 12:01:49 +01:00
package main
import (
"encoding/json"
2018-01-10 12:01:49 +01:00
"errors"
"flag"
2018-01-10 12:01:49 +01:00
"fmt"
"math/rand"
"net"
"net/http"
2018-06-06 15:54:51 +02:00
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
2018-01-10 12:01:49 +01:00
"time"
"github.com/BurntSushi/toml"
2018-01-11 11:50:54 +01:00
"github.com/jedisct1/dlog"
2018-04-18 18:58:39 +02:00
stamps "github.com/jedisct1/go-dnsstamps"
2018-06-06 15:54:51 +02:00
netproxy "golang.org/x/net/proxy"
2018-01-10 12:01:49 +01:00
)
const (
MaxTimeout = 3600
DefaultNetprobeAddress = "9.9.9.9:53"
)
2018-01-10 12:01:49 +01:00
type Config struct {
2020-11-14 14:46:59 +01:00
LocalDoH LocalDoHConfig
QueryLog QueryLogConfig
AllowedName AllowedNameConfig
DNS64 DNS64Config
BlockIP BlockIPConfig
BlockName BlockNameConfig
BlockNameLegacy BlockNameConfigLegacy
BlockIPLegacy BlockIPConfigLegacy
WhitelistNameLegacy WhitelistNameConfigLegacy
BrokenImplementations BrokenImplementationsConfig
NxLog NxLogConfig
AnonymizedDNS AnonymizedDNSConfig
DoHClientX509Auth DoHClientX509AuthConfig
DoHClientX509AuthLegacy DoHClientX509AuthConfig
ListenAddresses []string
DisabledServerNames []string
QueryMeta []string
ServerNames []string
FallbackResolvers []string
TLSCipherSuite []uint16
EDNSClientSubnet []string
SourcesConfig map[string]SourceConfig
AllWeeklyRanges map[string]WeeklyRangesStr
StaticsConfig map[string]StaticConfig
LogFile *string
UserName string
CloakFile string
ForwardFile string
FallbackResolver string
HTTPProxyURL string
NetprobeAddress string
Proxy string
LBStrategy string
BlockedQueryResponse string
CaptivePortalFile string
LogMaxBackups int
LogMaxAge int
CertRefreshDelay int
LogMaxSize int
LogLevel int
CacheSize int
NetprobeTimeout int
Timeout int
KeepAlive int
MaxClients uint32
CloakTTL uint32
RejectTTL uint32
CacheMinTTL uint32
CacheNegMaxTTL uint32
CacheNegTTL uint32
CacheNegMinTTL uint32
CacheMaxTTL uint32
SourceIPv6 bool
SourceIPv4 bool
SourceDoH bool
IgnoreSystemDNS bool
SourceDNSCrypt bool
SourceRequireNoFilter bool
SourceRequireNoLog bool
SourceRequireDNSSEC bool
TLSDisableSessionTickets bool
Cache bool
2020-11-14 14:46:59 +01:00
BlockUndelegated bool
BlockUnqualified bool
BlockIPv6 bool
LBEstimator bool
RefusedCodeInResponses bool
EphemeralKeys bool
CertIgnoreTimestamp bool
ForceTCP bool
Daemonize bool
UseSyslog bool
LogFileLatest bool
OfflineMode bool
2018-01-10 12:01:49 +01:00
}
func newConfig() Config {
return Config{
LogLevel: int(dlog.LogLevel()),
2020-06-08 22:31:03 +02:00
LogFileLatest: true,
ListenAddresses: []string{"127.0.0.1:53"},
LocalDoH: LocalDoHConfig{Path: "/dns-query"},
Timeout: 5000,
KeepAlive: 5,
CertRefreshDelay: 240,
CertIgnoreTimestamp: false,
EphemeralKeys: false,
Cache: true,
2018-04-10 13:24:13 +02:00
CacheSize: 512,
CacheNegTTL: 0,
CacheNegMinTTL: 60,
CacheNegMaxTTL: 600,
CacheMinTTL: 60,
CacheMaxTTL: 86400,
RejectTTL: 600,
CloakTTL: 600,
SourceRequireNoLog: true,
SourceRequireNoFilter: true,
SourceIPv4: true,
SourceIPv6: false,
SourceDNSCrypt: true,
SourceDoH: true,
MaxClients: 250,
2020-01-15 19:58:14 +01:00
FallbackResolvers: []string{DefaultFallbackResolver},
IgnoreSystemDNS: false,
LogMaxSize: 10,
LogMaxAge: 7,
LogMaxBackups: 1,
TLSDisableSessionTickets: false,
TLSCipherSuite: nil,
NetprobeTimeout: 60,
2018-07-05 18:05:24 +02:00
OfflineMode: false,
RefusedCodeInResponses: false,
LBEstimator: true,
BlockedQueryResponse: "hinfo",
2019-11-17 21:44:46 +01:00
BrokenImplementations: BrokenImplementationsConfig{
FragmentsBlocked: []string{
"cisco", "cisco-ipv6", "cisco-familyshield", "cisco-familyshield-ipv6",
"cleanbrowsing-adult", "cleanbrowsing-family-ipv6", "cleanbrowsing-family", "cleanbrowsing-security",
},
2019-11-17 21:44:46 +01:00
},
2018-01-10 12:01:49 +01:00
}
}
type StaticConfig struct {
Stamp string
2018-01-10 12:01:49 +01:00
}
2018-01-13 23:52:44 +01:00
type SourceConfig struct {
URL string
URLs []string
2018-01-13 23:52:44 +01:00
MinisignKeyStr string `toml:"minisign_key"`
CacheFile string `toml:"cache_file"`
FormatStr string `toml:"format"`
RefreshDelay int `toml:"refresh_delay"`
Prefix string
2018-01-13 23:52:44 +01:00
}
2018-01-16 00:23:16 +01:00
type QueryLogConfig struct {
File string
Format string
IgnoredQtypes []string `toml:"ignored_qtypes"`
2018-01-16 00:23:16 +01:00
}
type NxLogConfig struct {
File string
Format string
}
type BlockNameConfig struct {
File string `toml:"blocked_names_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type BlockNameConfigLegacy struct {
2018-01-17 17:03:42 +01:00
File string `toml:"blacklist_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type WhitelistNameConfigLegacy struct {
2018-04-07 23:02:40 +02:00
File string `toml:"whitelist_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type AllowedNameConfig struct {
File string `toml:"allowed_names_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
2018-01-21 16:07:44 +01:00
type BlockIPConfig struct {
File string `toml:"blocked_ips_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
type BlockIPConfigLegacy struct {
2018-01-21 16:07:44 +01:00
File string `toml:"blacklist_file"`
LogFile string `toml:"log_file"`
Format string `toml:"log_format"`
}
2019-10-14 01:45:38 +02:00
type AnonymizedDNSRouteConfig struct {
ServerName string `toml:"server_name"`
RelayNames []string `toml:"via"`
2019-10-14 01:45:38 +02:00
}
type AnonymizedDNSConfig struct {
Routes []AnonymizedDNSRouteConfig `toml:"routes"`
SkipIncompatible bool `toml:"skip_incompatible"`
DirectCertFallback bool `toml:"direct_cert_fallback"`
2019-10-14 01:45:38 +02:00
}
type BrokenImplementationsConfig struct {
BrokenQueryPadding []string `toml:"broken_query_padding"`
FragmentsBlocked []string `toml:"fragments_blocked"`
}
type LocalDoHConfig struct {
ListenAddresses []string `toml:"listen_addresses"`
2019-11-28 23:49:28 +01:00
Path string `toml:"path"`
CertFile string `toml:"cert_file"`
CertKeyFile string `toml:"cert_key_file"`
}
type ServerSummary struct {
Name string `json:"name"`
Proto string `json:"proto"`
IPv6 bool `json:"ipv6"`
Addrs []string `json:"addrs,omitempty"`
Ports []int `json:"ports"`
DNSSEC bool `json:"dnssec"`
NoLog bool `json:"nolog"`
NoFilter bool `json:"nofilter"`
Description string `json:"description,omitempty"`
2019-10-23 23:28:46 +02:00
Stamp string `json:"stamp"`
}
type TLSClientAuthCredsConfig struct {
ServerName string `toml:"server_name"`
ClientCert string `toml:"client_cert"`
ClientKey string `toml:"client_key"`
RootCA string `toml:"root_ca"`
}
type DoHClientX509AuthConfig struct {
Creds []TLSClientAuthCredsConfig `toml:"creds"`
}
2020-06-08 18:23:03 +02:00
type DNS64Config struct {
Prefixes []string `toml:"prefix"`
Resolvers []string `toml:"resolver"`
}
2019-10-30 14:30:31 +01:00
type ConfigFlags struct {
List *bool
ListAll *bool
2019-12-11 14:08:48 +01:00
JSONOutput *bool
2019-10-30 14:30:31 +01:00
Check *bool
ConfigFile *string
Child *bool
NetprobeTimeoutOverride *int
ShowCerts *bool
}
func findConfigFile(configFile *string) (string, error) {
if _, err := os.Stat(*configFile); os.IsNotExist(err) {
cdLocal()
if _, err := os.Stat(*configFile); err != nil {
return "", err
}
}
pwd, err := os.Getwd()
if err != nil {
return "", err
}
if filepath.IsAbs(*configFile) {
return *configFile, nil
}
return path.Join(pwd, *configFile), nil
}
2019-10-30 14:30:31 +01:00
func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
foundConfigFile, err := findConfigFile(flags.ConfigFile)
if err != nil {
return fmt.Errorf("Unable to load the configuration file [%s] -- Maybe use the -config command-line switch?", *flags.ConfigFile)
}
2018-01-10 12:01:49 +01:00
config := newConfig()
md, err := toml.DecodeFile(foundConfigFile, &config)
if err != nil {
2018-01-10 12:01:49 +01:00
return err
}
2019-12-09 12:11:24 +01:00
if err := cdFileDir(foundConfigFile); err != nil {
return err
}
if config.LogLevel >= 0 && config.LogLevel < int(dlog.SeverityLast) {
dlog.SetLogLevel(dlog.Severity(config.LogLevel))
}
if dlog.LogLevel() <= dlog.SeverityDebug && os.Getenv("DEBUG") == "" {
dlog.SetLogLevel(dlog.SeverityInfo)
}
2020-06-08 22:31:03 +02:00
dlog.TruncateLogFile(config.LogFileLatest)
if config.UseSyslog {
dlog.UseSyslog(true)
} else if config.LogFile != nil {
dlog.UseLogFile(*config.LogFile)
2019-10-30 14:30:31 +01:00
if !*flags.Child {
FileDescriptors = append(FileDescriptors, dlog.GetFileDescriptor())
} else {
FileDescriptorNum++
dlog.SetFileDescriptor(os.NewFile(uintptr(3), "logFile"))
}
}
if !*flags.Child {
dlog.Noticef("dnscrypt-proxy %s", AppVersion)
}
undecoded := md.Undecoded()
if len(undecoded) > 0 {
return fmt.Errorf("Unsupported key in configuration file: [%s]", undecoded[0])
}
proxy.logMaxSize = config.LogMaxSize
proxy.logMaxAge = config.LogMaxAge
proxy.logMaxBackups = config.LogMaxBackups
2018-07-07 19:58:37 +02:00
proxy.userName = config.UserName
2019-10-30 14:30:31 +01:00
proxy.child = *flags.Child
proxy.xTransport = NewXTransport()
proxy.xTransport.tlsDisableSessionTickets = config.TLSDisableSessionTickets
proxy.xTransport.tlsCipherSuite = config.TLSCipherSuite
proxy.xTransport.mainProto = proxy.mainProto
if len(config.FallbackResolver) > 0 {
2020-01-15 19:58:14 +01:00
config.FallbackResolvers = []string{config.FallbackResolver}
}
if len(config.FallbackResolvers) > 0 {
for _, resolver := range config.FallbackResolvers {
if err := isIPAndPort(resolver); err != nil {
return fmt.Errorf("Fallback resolver [%v]: %v", resolver, err)
2020-01-15 19:58:14 +01:00
}
}
proxy.xTransport.ignoreSystemDNS = config.IgnoreSystemDNS
}
2020-01-15 19:58:14 +01:00
proxy.xTransport.fallbackResolvers = config.FallbackResolvers
proxy.xTransport.useIPv4 = config.SourceIPv4
proxy.xTransport.useIPv6 = config.SourceIPv6
proxy.xTransport.keepAlive = time.Duration(config.KeepAlive) * time.Second
if len(config.HTTPProxyURL) > 0 {
httpProxyURL, err := url.Parse(config.HTTPProxyURL)
if err != nil {
return fmt.Errorf("Unable to parse the HTTP proxy URL [%v]", config.HTTPProxyURL)
}
proxy.xTransport.httpProxyFunction = http.ProxyURL(httpProxyURL)
}
2018-06-06 15:54:51 +02:00
if len(config.Proxy) > 0 {
proxyDialerURL, err := url.Parse(config.Proxy)
if err != nil {
return fmt.Errorf("Unable to parse the proxy URL [%v]", config.Proxy)
2018-06-06 15:54:51 +02:00
}
proxyDialer, err := netproxy.FromURL(proxyDialerURL, netproxy.Direct)
if err != nil {
return fmt.Errorf("Unable to use the proxy: [%v]", err)
2018-06-06 15:54:51 +02:00
}
proxy.xTransport.proxyDialer = &proxyDialer
proxy.mainProto = "tcp"
2018-06-06 15:54:51 +02:00
}
proxy.xTransport.rebuildTransport()
2018-04-07 22:33:40 +02:00
if md.IsDefined("refused_code_in_responses") {
dlog.Notice("config option `refused_code_in_responses` is deprecated, use `blocked_query_response`")
if config.RefusedCodeInResponses {
config.BlockedQueryResponse = "refused"
} else {
config.BlockedQueryResponse = "hinfo"
}
}
proxy.blockedQueryResponse = config.BlockedQueryResponse
2018-01-10 12:01:49 +01:00
proxy.timeout = time.Duration(config.Timeout) * time.Millisecond
proxy.maxClients = config.MaxClients
2018-01-10 12:01:49 +01:00
proxy.mainProto = "udp"
if config.ForceTCP {
proxy.mainProto = "tcp"
}
proxy.certRefreshDelay = time.Duration(Max(60, config.CertRefreshDelay)) * time.Minute
proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second)
proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp
proxy.ephemeralKeys = config.EphemeralKeys
if len(config.ListenAddresses) == 0 && len(config.LocalDoH.ListenAddresses) == 0 {
dlog.Debug("No local IP/port configured")
2018-01-10 12:01:49 +01:00
}
lbStrategy := LBStrategy(DefaultLBStrategy)
2020-03-20 17:48:54 +01:00
switch lbStrategyStr := strings.ToLower(config.LBStrategy); lbStrategyStr {
2018-02-05 01:53:23 +01:00
case "":
// default
case "p2":
lbStrategy = LBStrategyP2{}
case "ph":
lbStrategy = LBStrategyPH{}
case "fastest":
case "first":
lbStrategy = LBStrategyFirst{}
case "random":
lbStrategy = LBStrategyRandom{}
default:
2020-03-20 17:48:54 +01:00
if strings.HasPrefix(lbStrategyStr, "p") {
n, err := strconv.ParseInt(strings.TrimPrefix(lbStrategyStr, "p"), 10, 32)
if err != nil || n <= 0 {
dlog.Warnf("Invalid load balancing strategy: [%s]", config.LBStrategy)
} else {
lbStrategy = LBStrategyPN{n: int(n)}
2020-03-20 17:48:54 +01:00
}
} else {
dlog.Warnf("Unknown load balancing strategy: [%s]", config.LBStrategy)
}
}
proxy.serversInfo.lbStrategy = lbStrategy
proxy.serversInfo.lbEstimator = config.LBEstimator
2018-01-10 12:01:49 +01:00
proxy.listenAddresses = config.ListenAddresses
proxy.localDoHListenAddresses = config.LocalDoH.ListenAddresses
2019-12-10 16:04:37 +01:00
if len(config.LocalDoH.Path) > 0 && config.LocalDoH.Path[0] != '/' {
return fmt.Errorf("local DoH: [%s] cannot be a valid URL path. Read the documentation", config.LocalDoH.Path)
2019-12-10 16:04:37 +01:00
}
2019-11-28 23:49:28 +01:00
proxy.localDoHPath = config.LocalDoH.Path
proxy.localDoHCertFile = config.LocalDoH.CertFile
proxy.localDoHCertKeyFile = config.LocalDoH.CertKeyFile
2018-01-10 12:01:49 +01:00
proxy.daemonize = config.Daemonize
2018-01-10 17:23:20 +01:00
proxy.pluginBlockIPv6 = config.BlockIPv6
proxy.pluginBlockUnqualified = config.BlockUnqualified
proxy.pluginBlockUndelegated = config.BlockUndelegated
2018-01-10 18:32:05 +01:00
proxy.cache = config.Cache
proxy.cacheSize = config.CacheSize
if config.CacheNegTTL > 0 {
proxy.cacheNegMinTTL = config.CacheNegTTL
proxy.cacheNegMaxTTL = config.CacheNegTTL
} else {
proxy.cacheNegMinTTL = config.CacheNegMinTTL
proxy.cacheNegMaxTTL = config.CacheNegMaxTTL
}
proxy.cacheMinTTL = config.CacheMinTTL
proxy.cacheMaxTTL = config.CacheMaxTTL
proxy.rejectTTL = config.RejectTTL
proxy.cloakTTL = config.CloakTTL
2018-01-17 17:03:42 +01:00
2019-09-07 16:19:47 +02:00
proxy.queryMeta = config.QueryMeta
if len(config.EDNSClientSubnet) != 0 {
proxy.ednsClientSubnets = make([]*net.IPNet, 0)
for _, cidr := range config.EDNSClientSubnet {
_, net, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("Invalid EDNS-client-subnet CIDR: [%v]", cidr)
}
proxy.ednsClientSubnets = append(proxy.ednsClientSubnets, net)
}
}
2018-01-16 00:23:16 +01:00
if len(config.QueryLog.Format) == 0 {
config.QueryLog.Format = "tsv"
} else {
config.QueryLog.Format = strings.ToLower(config.QueryLog.Format)
}
2018-01-16 18:10:04 +01:00
if config.QueryLog.Format != "tsv" && config.QueryLog.Format != "ltsv" {
2018-01-16 00:23:16 +01:00
return errors.New("Unsupported query log format")
}
proxy.queryLogFile = config.QueryLog.File
proxy.queryLogFormat = config.QueryLog.Format
proxy.queryLogIgnoredQtypes = config.QueryLog.IgnoredQtypes
2018-01-17 17:03:42 +01:00
if len(config.NxLog.Format) == 0 {
config.NxLog.Format = "tsv"
} else {
config.NxLog.Format = strings.ToLower(config.NxLog.Format)
}
if config.NxLog.Format != "tsv" && config.NxLog.Format != "ltsv" {
return errors.New("Unsupported NX log format")
}
proxy.nxLogFile = config.NxLog.File
proxy.nxLogFormat = config.NxLog.Format
if len(config.BlockName.File) > 0 && len(config.BlockNameLegacy.File) > 0 {
return errors.New("Don't specify both [blocked_names] and [blacklist] sections - Update your config file.")
}
if len(config.BlockNameLegacy.File) > 0 {
dlog.Notice("Use of [blacklist] is deprecated - Update your config file.")
config.BlockName.File = config.BlockNameLegacy.File
config.BlockName.Format = config.BlockNameLegacy.Format
config.BlockName.LogFile = config.BlockNameLegacy.LogFile
}
2018-01-17 17:03:42 +01:00
if len(config.BlockName.Format) == 0 {
config.BlockName.Format = "tsv"
} else {
config.BlockName.Format = strings.ToLower(config.BlockName.Format)
}
if config.BlockName.Format != "tsv" && config.BlockName.Format != "ltsv" {
return errors.New("Unsupported block log format")
}
proxy.blockNameFile = config.BlockName.File
2018-01-17 17:03:42 +01:00
proxy.blockNameFormat = config.BlockName.Format
proxy.blockNameLogFile = config.BlockName.LogFile
if len(config.AllowedName.File) > 0 && len(config.WhitelistNameLegacy.File) > 0 {
return errors.New("Don't specify both [whitelist] and [allowed_names] sections - Update your config file.")
}
if len(config.WhitelistNameLegacy.File) > 0 {
dlog.Notice("Use of [whitelist] is deprecated - Update your config file.")
config.AllowedName.File = config.WhitelistNameLegacy.File
config.AllowedName.Format = config.WhitelistNameLegacy.Format
config.AllowedName.LogFile = config.WhitelistNameLegacy.LogFile
}
if len(config.AllowedName.Format) == 0 {
config.AllowedName.Format = "tsv"
2018-04-07 23:02:40 +02:00
} else {
config.AllowedName.Format = strings.ToLower(config.AllowedName.Format)
2018-04-07 23:02:40 +02:00
}
if config.AllowedName.Format != "tsv" && config.AllowedName.Format != "ltsv" {
return errors.New("Unsupported allowed_names log format")
2018-04-07 23:02:40 +02:00
}
proxy.whitelistNameFile = config.AllowedName.File
proxy.whitelistNameFormat = config.AllowedName.Format
proxy.whitelistNameLogFile = config.AllowedName.LogFile
2018-04-07 23:02:40 +02:00
if len(config.BlockIP.File) > 0 && len(config.BlockIPLegacy.File) > 0 {
return errors.New("Don't specify both [blocked_ips] and [ip_blacklist] sections - Update your config file.")
}
if len(config.BlockIPLegacy.File) > 0 {
dlog.Notice("Use of [ip_blacklist] is deprecated - Update your config file.")
config.BlockIP.File = config.BlockIPLegacy.File
config.BlockIP.Format = config.BlockIPLegacy.Format
config.BlockIP.LogFile = config.BlockIPLegacy.LogFile
}
2018-01-21 16:07:44 +01:00
if len(config.BlockIP.Format) == 0 {
config.BlockIP.Format = "tsv"
} else {
config.BlockIP.Format = strings.ToLower(config.BlockIP.Format)
}
if config.BlockIP.Format != "tsv" && config.BlockIP.Format != "ltsv" {
return errors.New("Unsupported IP block log format")
}
proxy.blockIPFile = config.BlockIP.File
proxy.blockIPFormat = config.BlockIP.Format
proxy.blockIPLogFile = config.BlockIP.LogFile
2018-01-17 09:44:03 +01:00
proxy.forwardFile = config.ForwardFile
proxy.cloakFile = config.CloakFile
proxy.captivePortalFile = config.CaptivePortalFile
allWeeklyRanges, err := ParseAllWeeklyRanges(config.AllWeeklyRanges)
if err != nil {
2018-01-31 22:49:40 +01:00
return err
}
proxy.allWeeklyRanges = allWeeklyRanges
2018-01-31 22:18:11 +01:00
2019-10-14 01:45:38 +02:00
if configRoutes := config.AnonymizedDNS.Routes; configRoutes != nil {
routes := make(map[string][]string)
2019-10-14 01:45:38 +02:00
for _, configRoute := range configRoutes {
routes[configRoute.ServerName] = configRoute.RelayNames
2019-10-14 01:45:38 +02:00
}
proxy.routes = &routes
}
proxy.skipAnonIncompatbibleResolvers = config.AnonymizedDNS.SkipIncompatible
proxy.anonDirectCertFallback = config.AnonymizedDNS.DirectCertFallback
if config.DoHClientX509AuthLegacy.Creds != nil {
return errors.New("[tls_client_auth] has been renamed to [doh_client_x509_auth] - Update your config file.")
}
configClientCreds := config.DoHClientX509Auth.Creds
creds := make(map[string]DOHClientCreds)
for _, configClientCred := range configClientCreds {
credFiles := DOHClientCreds{
clientCert: configClientCred.ClientCert,
clientKey: configClientCred.ClientKey,
rootCA: configClientCred.RootCA,
}
creds[configClientCred.ServerName] = credFiles
}
proxy.dohCreds = &creds
// Backwards compatibility
config.BrokenImplementations.FragmentsBlocked = append(config.BrokenImplementations.FragmentsBlocked, config.BrokenImplementations.BrokenQueryPadding...)
proxy.serversBlockingFragments = config.BrokenImplementations.FragmentsBlocked
2019-10-14 01:45:38 +02:00
2020-06-08 18:23:03 +02:00
proxy.dns64Prefixes = config.DNS64.Prefixes
proxy.dns64Resolvers = config.DNS64.Resolvers
2019-10-30 14:30:31 +01:00
if *flags.ListAll {
config.ServerNames = nil
2019-02-23 14:54:22 +01:00
config.DisabledServerNames = nil
config.SourceRequireDNSSEC = false
config.SourceRequireNoFilter = false
config.SourceRequireNoLog = false
config.SourceIPv4 = true
config.SourceIPv6 = true
config.SourceDNSCrypt = true
2018-02-06 14:11:58 +01:00
config.SourceDoH = true
}
netprobeTimeout := config.NetprobeTimeout
flag.Visit(func(flag *flag.Flag) {
2019-10-30 14:30:31 +01:00
if flag.Name == "netprobe-timeout" && flags.NetprobeTimeoutOverride != nil {
netprobeTimeout = *flags.NetprobeTimeoutOverride
}
})
netprobeAddress := DefaultNetprobeAddress
if len(config.NetprobeAddress) > 0 {
netprobeAddress = config.NetprobeAddress
2020-01-15 19:58:14 +01:00
} else if len(config.FallbackResolvers) > 0 {
netprobeAddress = config.FallbackResolvers[0]
}
2019-10-30 14:30:31 +01:00
proxy.showCerts = *flags.ShowCerts || len(os.Getenv("SHOW_CERTS")) > 0
if !*flags.Check && !*flags.ShowCerts && !*flags.List && !*flags.ListAll {
if err := NetProbe(proxy, netprobeAddress, netprobeTimeout); err != nil {
return err
}
for _, listenAddrStr := range proxy.listenAddresses {
proxy.addDNSListener(listenAddrStr)
}
for _, listenAddrStr := range proxy.localDoHListenAddresses {
proxy.addLocalDoHListener(listenAddrStr)
}
if err := proxy.addSystemDListeners(); err != nil {
return err
}
}
2020-04-20 12:27:53 +02:00
// if 'userName' is set and we are the parent process drop privilege and exit
if len(proxy.userName) > 0 && !proxy.child {
proxy.dropPrivilege(proxy.userName, FileDescriptors)
return errors.New("Dropping privileges is not supporting on this operating system. Unset `user_name` in the configuration file.")
2020-04-20 12:27:53 +02:00
}
2018-07-05 18:05:24 +02:00
if !config.OfflineMode {
if err := config.loadSources(proxy); err != nil {
return err
}
if len(proxy.registeredServers) == 0 {
return errors.New("No servers configured")
}
2018-01-31 08:27:59 +01:00
}
2019-10-30 14:30:31 +01:00
if *flags.List || *flags.ListAll {
if err := config.printRegisteredServers(proxy, *flags.JSONOutput); err != nil {
return err
}
2018-01-31 08:27:59 +01:00
os.Exit(0)
}
2019-10-14 11:02:13 +02:00
if proxy.routes != nil && len(*proxy.routes) > 0 {
hasSpecificRoutes := false
2019-10-14 11:02:13 +02:00
for _, server := range proxy.registeredServers {
if via, ok := (*proxy.routes)[server.name]; ok {
if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt {
dlog.Errorf("DNS anonymization is only supported with the DNSCrypt protocol - Connections to [%v] cannot be anonymized", server.name)
} else {
dlog.Noticef("Anonymized DNS: routing [%v] via %v", server.name, via)
}
hasSpecificRoutes = true
}
}
if via, ok := (*proxy.routes)["*"]; ok {
if hasSpecificRoutes {
dlog.Noticef("Anonymized DNS: routing everything else via %v", via)
} else {
dlog.Noticef("Anonymized DNS: routing everything via %v", via)
2019-10-14 11:02:13 +02:00
}
}
}
2019-10-30 14:30:31 +01:00
if *flags.Check {
2018-02-19 18:35:06 +01:00
dlog.Notice("Configuration successfully checked")
os.Exit(0)
}
2018-01-31 08:27:59 +01:00
return nil
}
func (config *Config) printRegisteredServers(proxy *Proxy, jsonOutput bool) error {
var summary []ServerSummary
for _, registeredServer := range proxy.registeredServers {
2018-04-14 15:03:21 +02:00
addrStr, port := registeredServer.stamp.ServerAddrStr, stamps.DefaultPort
var hostAddr string
hostAddr, port = ExtractHostAndPort(addrStr, port)
addrs := make([]string, 0)
2018-04-14 15:03:21 +02:00
if registeredServer.stamp.Proto == stamps.StampProtoTypeDoH && len(registeredServer.stamp.ProviderName) > 0 {
providerName := registeredServer.stamp.ProviderName
var host string
host, port = ExtractHostAndPort(providerName, port)
addrs = append(addrs, host)
}
if len(addrStr) > 0 {
addrs = append(addrs, hostAddr)
}
serverSummary := ServerSummary{
Name: registeredServer.name,
2018-04-14 15:03:21 +02:00
Proto: registeredServer.stamp.Proto.String(),
IPv6: strings.HasPrefix(addrStr, "["),
Ports: []int{port},
Addrs: addrs,
2018-04-14 15:03:21 +02:00
DNSSEC: registeredServer.stamp.Props&stamps.ServerInformalPropertyDNSSEC != 0,
NoLog: registeredServer.stamp.Props&stamps.ServerInformalPropertyNoLog != 0,
NoFilter: registeredServer.stamp.Props&stamps.ServerInformalPropertyNoFilter != 0,
Description: registeredServer.description,
2019-10-23 23:30:29 +02:00
Stamp: registeredServer.stamp.String(),
}
if jsonOutput {
summary = append(summary, serverSummary)
} else {
fmt.Println(serverSummary.Name)
}
}
if jsonOutput {
jsonStr, err := json.MarshalIndent(summary, "", " ")
if err != nil {
return err
}
fmt.Print(string(jsonStr))
}
return nil
}
2018-01-31 08:27:59 +01:00
func (config *Config) loadSources(proxy *Proxy) error {
2018-04-14 15:03:21 +02:00
var requiredProps stamps.ServerInformalProperties
if config.SourceRequireDNSSEC {
2018-04-14 15:03:21 +02:00
requiredProps |= stamps.ServerInformalPropertyDNSSEC
}
if config.SourceRequireNoLog {
2018-04-14 15:03:21 +02:00
requiredProps |= stamps.ServerInformalPropertyNoLog
}
2018-01-30 19:13:50 +01:00
if config.SourceRequireNoFilter {
2018-04-14 15:03:21 +02:00
requiredProps |= stamps.ServerInformalPropertyNoFilter
2018-01-30 19:13:50 +01:00
}
for cfgSourceName, cfgSource_ := range config.SourcesConfig {
cfgSource := cfgSource_
2018-01-31 08:38:22 +01:00
if err := config.loadSource(proxy, requiredProps, cfgSourceName, &cfgSource); err != nil {
2018-01-31 08:32:44 +01:00
return err
}
2018-01-10 12:01:49 +01:00
}
if len(config.ServerNames) == 0 {
for serverName := range config.StaticsConfig {
config.ServerNames = append(config.ServerNames, serverName)
}
}
2018-01-10 12:01:49 +01:00
for _, serverName := range config.ServerNames {
staticConfig, ok := config.StaticsConfig[serverName]
2018-01-10 12:01:49 +01:00
if !ok {
continue
2018-01-10 12:01:49 +01:00
}
if len(staticConfig.Stamp) == 0 {
return fmt.Errorf("Missing stamp for the static [%s] definition", serverName)
2018-01-20 14:13:11 +01:00
}
2018-04-14 15:03:21 +02:00
stamp, err := stamps.NewServerStampFromString(staticConfig.Stamp)
2018-01-20 14:13:11 +01:00
if err != nil {
return fmt.Errorf("Stamp error for the static [%s] definition: [%v]", serverName, err)
2018-01-10 12:01:49 +01:00
}
2018-01-31 08:27:59 +01:00
proxy.registeredServers = append(proxy.registeredServers, RegisteredServer{name: serverName, stamp: stamp})
}
rand.Shuffle(len(proxy.registeredServers), func(i, j int) {
proxy.registeredServers[i], proxy.registeredServers[j] = proxy.registeredServers[j], proxy.registeredServers[i]
})
2018-01-10 12:01:49 +01:00
return nil
}
2018-04-14 15:03:21 +02:00
func (config *Config) loadSource(proxy *Proxy, requiredProps stamps.ServerInformalProperties, cfgSourceName string, cfgSource *SourceConfig) error {
if len(cfgSource.URLs) == 0 {
if len(cfgSource.URL) == 0 {
dlog.Debugf("Missing URLs for source [%s]", cfgSourceName)
} else {
cfgSource.URLs = []string{cfgSource.URL}
}
2018-01-31 08:32:44 +01:00
}
if cfgSource.MinisignKeyStr == "" {
return fmt.Errorf("Missing Minisign key for source [%s]", cfgSourceName)
}
if cfgSource.CacheFile == "" {
return fmt.Errorf("Missing cache file for source [%s]", cfgSourceName)
}
if cfgSource.FormatStr == "" {
cfgSource.FormatStr = "v2"
2018-01-31 08:32:44 +01:00
}
if cfgSource.RefreshDelay <= 0 {
cfgSource.RefreshDelay = 72
2018-01-31 08:32:44 +01:00
}
source, err := NewSource(cfgSourceName, proxy.xTransport, cfgSource.URLs, cfgSource.MinisignKeyStr, cfgSource.CacheFile, cfgSource.FormatStr, time.Duration(cfgSource.RefreshDelay)*time.Hour)
2018-01-31 08:32:44 +01:00
if err != nil {
if len(source.in) <= 0 {
dlog.Criticalf("Unable to retrieve source [%s]: [%s]", cfgSourceName, err)
return err
}
2020-05-31 13:46:44 +02:00
dlog.Infof("Downloading [%s] failed: %v, using cache file to startup", source.name, err)
2018-01-31 08:32:44 +01:00
}
proxy.sources = append(proxy.sources, source)
2018-01-31 08:32:44 +01:00
registeredServers, err := source.Parse(cfgSource.Prefix)
if err != nil {
if len(registeredServers) == 0 {
dlog.Criticalf("Unable to use source [%s]: [%s]", cfgSourceName, err)
return err
}
dlog.Warnf("Error in source [%s]: [%s] -- Continuing with reduced server count [%d]", cfgSourceName, err, len(registeredServers))
2018-01-31 08:32:44 +01:00
}
for _, registeredServer := range registeredServers {
if registeredServer.stamp.Proto != stamps.StampProtoTypeDNSCryptRelay {
if len(config.ServerNames) > 0 {
if !includesName(config.ServerNames, registeredServer.name) {
continue
}
} else if registeredServer.stamp.Props&requiredProps != requiredProps {
2018-01-31 08:32:44 +01:00
continue
}
}
2019-02-23 14:54:22 +01:00
if includesName(config.DisabledServerNames, registeredServer.name) {
continue
}
2018-01-31 08:32:44 +01:00
if config.SourceIPv4 || config.SourceIPv6 {
isIPv4, isIPv6 := true, false
2018-04-14 15:03:21 +02:00
if registeredServer.stamp.Proto == stamps.StampProtoTypeDoH {
isIPv4, isIPv6 = true, true
}
2018-04-14 15:03:21 +02:00
if strings.HasPrefix(registeredServer.stamp.ServerAddrStr, "[") {
2018-01-31 08:32:44 +01:00
isIPv4, isIPv6 = false, true
}
if !(config.SourceIPv4 == isIPv4 || config.SourceIPv6 == isIPv6) {
continue
}
}
if registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCryptRelay {
dlog.Debugf("Adding [%s] to the set of available relays", registeredServer.name)
proxy.registeredRelays = append(proxy.registeredRelays, registeredServer)
} else {
if !((config.SourceDNSCrypt && registeredServer.stamp.Proto == stamps.StampProtoTypeDNSCrypt) ||
(config.SourceDoH && registeredServer.stamp.Proto == stamps.StampProtoTypeDoH)) {
continue
}
dlog.Debugf("Adding [%s] to the set of wanted resolvers", registeredServer.name)
proxy.registeredServers = append(proxy.registeredServers, registeredServer)
2018-02-06 14:11:58 +01:00
}
2018-01-31 08:32:44 +01:00
}
return nil
}
func includesName(names []string, name string) bool {
for _, found := range names {
if strings.EqualFold(found, name) {
return true
}
}
return false
}
2019-12-09 12:11:24 +01:00
func cdFileDir(fileName string) error {
return os.Chdir(filepath.Dir(fileName))
}
func cdLocal() {
exeFileName, err := os.Executable()
if err != nil {
dlog.Warnf("Unable to determine the executable directory: [%s] -- You will need to specify absolute paths in the configuration file", err)
2020-03-13 18:44:30 +01:00
} else if err := os.Chdir(filepath.Dir(exeFileName)); err != nil {
2019-12-09 10:07:05 +01:00
dlog.Warnf("Unable to change working directory to [%s]: %s", exeFileName, err)
2019-12-09 09:49:33 +01:00
}
}
2019-11-01 23:06:42 +01:00
func isIPAndPort(addrStr string) error {
host, port := ExtractHostAndPort(addrStr, -1)
if ip := ParseIP(host); ip == nil {
return fmt.Errorf("Host does not parse as IP '%s'", addrStr)
} else if port == -1 {
return fmt.Errorf("Port missing '%s'", addrStr)
} else if _, err := strconv.ParseUint(strconv.Itoa(port), 10, 16); err != nil {
return fmt.Errorf("Port does not parse '%s' [%v]", addrStr, err)
}
return nil
}