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