diff --git a/internal/cmd/args.go b/internal/cmd/args.go index 4d5bf4144..017455a0f 100644 --- a/internal/cmd/args.go +++ b/internal/cmd/args.go @@ -368,12 +368,12 @@ var commandLineOptions = []*commandLineOption{ }, } -// parseCmdLineOptions parses the command-line options. opts must not be nil. -func parseCmdLineOptions(opts *configuration) (err error) { +// parseCmdLineOptions parses the command-line options. conf must not be nil. +func parseCmdLineOptions(conf *configuration) (err error) { cmdName, args := os.Args[0], os.Args[1:] flags := flag.NewFlagSet(cmdName, flag.ContinueOnError) - for i, fieldPtr := range mapOptions(opts) { + for i, fieldPtr := range mapOptions(conf) { addOption(flags, fieldPtr, commandLineOptions[i]) } @@ -443,7 +443,7 @@ func addOption(flags *flag.FlagSet, fieldPtr any, o *commandLineOption) { case *[]int: defineFlagVar(flags, newIntSliceValue(*fieldPtr, fieldPtr), o) case *[]string: - defineFlagVar(flags, newStringsValue(*fieldPtr, fieldPtr), o) + defineFlagVar(flags, newStringSliceValue(*fieldPtr, fieldPtr), o) case encoding.TextUnmarshaler: defineTextUnmarshalerFlag(flags, fieldPtr, o) default: @@ -493,19 +493,19 @@ func writeUsageLine(b *strings.Builder, o *commandLineOption) { // processCmdLineOptions decides if dnsproxy should exit depending on the // results of command-line option parsing. -func processCmdLineOptions(opts *configuration, parseErr error) (exitCode int, needExit bool) { +func processCmdLineOptions(conf *configuration, parseErr error) (exitCode int, needExit bool) { if parseErr != nil { // Assume that usage has already been printed. return osutil.ExitCodeArgumentError, true } - if opts.help { + if conf.help { usage(os.Args[0], os.Stdout) return osutil.ExitCodeSuccess, true } - if opts.Version { + if conf.Version { fmt.Printf("dnsproxy version %s\n", version.Version()) return osutil.ExitCodeSuccess, true @@ -514,56 +514,56 @@ func processCmdLineOptions(opts *configuration, parseErr error) (exitCode int, n return osutil.ExitCodeSuccess, false } -// mapOptions returns options mapped with commandLineOptions. -func mapOptions(opts *configuration) map[int]any { +// mapOptions returns configuration mapped with commandLineOptions. +func mapOptions(conf *configuration) (m map[int]any) { return map[int]any{ - configPathIdx: &opts.ConfigPath, - logOutputIdx: &opts.LogOutput, - tlsCertPathIdx: &opts.TLSCertPath, - tlsKeyPathIdx: &opts.TLSKeyPath, - httpsServerNameIdx: &opts.HTTPSServerName, - httpsUserinfoIdx: &opts.HTTPSUserinfo, - dnsCryptConfigPathIdx: &opts.DNSCryptConfigPath, - ednsAddrIdx: &opts.EDNSAddr, - upstreamModeIdx: &opts.UpstreamMode, - listenAddrsIdx: &opts.ListenAddrs, - listenPortsIdx: &opts.ListenPorts, - httpsListenPortsIdx: &opts.HTTPSListenPorts, - tlsListenPortsIdx: &opts.TLSListenPorts, - quicListenPortsIdx: &opts.QUICListenPorts, - dnsCryptListenPortsIdx: &opts.DNSCryptListenPorts, - upstreamsIdx: &opts.Upstreams, - bootstrapDNSIdx: &opts.BootstrapDNS, - fallbacksIdx: &opts.Fallbacks, - privateRDNSUpstreamsIdx: &opts.PrivateRDNSUpstreams, - dns64PrefixIdx: &opts.DNS64Prefix, - privateSubnetsIdx: &opts.PrivateSubnets, - bogusNXDomainIdx: &opts.BogusNXDomain, - hostsFilesIdx: &opts.HostsFiles, - timeoutIdx: &opts.Timeout, - cacheMinTTLIdx: &opts.CacheMinTTL, - cacheMaxTTLIdx: &opts.CacheMaxTTL, - cacheSizeBytesIdx: &opts.CacheSizeBytes, - ratelimitIdx: &opts.Ratelimit, - ratelimitSubnetLenIPv4Idx: &opts.RatelimitSubnetLenIPv4, - ratelimitSubnetLenIPv6Idx: &opts.RatelimitSubnetLenIPv6, - udpBufferSizeIdx: &opts.UDPBufferSize, - maxGoRoutinesIdx: &opts.MaxGoRoutines, - tlsMinVersionIdx: &opts.TLSMinVersion, - tlsMaxVersionIdx: &opts.TLSMaxVersion, - helpIdx: &opts.help, - hostsFileEnabledIdx: &opts.HostsFileEnabled, - pprofIdx: &opts.Pprof, - versionIdx: &opts.Version, - verboseIdx: &opts.Verbose, - insecureIdx: &opts.Insecure, - ipv6DisabledIdx: &opts.IPv6Disabled, - http3Idx: &opts.HTTP3, - cacheOptimisticIdx: &opts.CacheOptimistic, - cacheIdx: &opts.Cache, - refuseAnyIdx: &opts.RefuseAny, - enableEDNSSubnetIdx: &opts.EnableEDNSSubnet, - dns64Idx: &opts.DNS64, - usePrivateRDNSIdx: &opts.UsePrivateRDNS, + configPathIdx: &conf.ConfigPath, + logOutputIdx: &conf.LogOutput, + tlsCertPathIdx: &conf.TLSCertPath, + tlsKeyPathIdx: &conf.TLSKeyPath, + httpsServerNameIdx: &conf.HTTPSServerName, + httpsUserinfoIdx: &conf.HTTPSUserinfo, + dnsCryptConfigPathIdx: &conf.DNSCryptConfigPath, + ednsAddrIdx: &conf.EDNSAddr, + upstreamModeIdx: &conf.UpstreamMode, + listenAddrsIdx: &conf.ListenAddrs, + listenPortsIdx: &conf.ListenPorts, + httpsListenPortsIdx: &conf.HTTPSListenPorts, + tlsListenPortsIdx: &conf.TLSListenPorts, + quicListenPortsIdx: &conf.QUICListenPorts, + dnsCryptListenPortsIdx: &conf.DNSCryptListenPorts, + upstreamsIdx: &conf.Upstreams, + bootstrapDNSIdx: &conf.BootstrapDNS, + fallbacksIdx: &conf.Fallbacks, + privateRDNSUpstreamsIdx: &conf.PrivateRDNSUpstreams, + dns64PrefixIdx: &conf.DNS64Prefix, + privateSubnetsIdx: &conf.PrivateSubnets, + bogusNXDomainIdx: &conf.BogusNXDomain, + hostsFilesIdx: &conf.HostsFiles, + timeoutIdx: &conf.Timeout, + cacheMinTTLIdx: &conf.CacheMinTTL, + cacheMaxTTLIdx: &conf.CacheMaxTTL, + cacheSizeBytesIdx: &conf.CacheSizeBytes, + ratelimitIdx: &conf.Ratelimit, + ratelimitSubnetLenIPv4Idx: &conf.RatelimitSubnetLenIPv4, + ratelimitSubnetLenIPv6Idx: &conf.RatelimitSubnetLenIPv6, + udpBufferSizeIdx: &conf.UDPBufferSize, + maxGoRoutinesIdx: &conf.MaxGoRoutines, + tlsMinVersionIdx: &conf.TLSMinVersion, + tlsMaxVersionIdx: &conf.TLSMaxVersion, + helpIdx: &conf.help, + hostsFileEnabledIdx: &conf.HostsFileEnabled, + pprofIdx: &conf.Pprof, + versionIdx: &conf.Version, + verboseIdx: &conf.Verbose, + insecureIdx: &conf.Insecure, + ipv6DisabledIdx: &conf.IPv6Disabled, + http3Idx: &conf.HTTP3, + cacheOptimisticIdx: &conf.CacheOptimistic, + cacheIdx: &conf.Cache, + refuseAnyIdx: &conf.RefuseAny, + enableEDNSSubnetIdx: &conf.EnableEDNSSubnet, + dns64Idx: &conf.DNS64, + usePrivateRDNSIdx: &conf.UsePrivateRDNS, } } diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index f8cb32db6..f0a2a0b43 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -22,20 +22,20 @@ import ( // Main is the entrypoint of dnsproxy CLI. Main may accept arguments, such as // embedded assets and command-line arguments. func Main() { - opts, exitCode, err := parseConfig() + conf, exitCode, err := parseConfig() if err != nil { _, _ = fmt.Fprintln(os.Stderr, fmt.Errorf("parsing options: %w", err)) } - if opts == nil { + if conf == nil { os.Exit(exitCode) } logOutput := os.Stdout - if opts.LogOutput != "" { + if conf.LogOutput != "" { // #nosec G302 -- Trust the file path that is given in the // configuration. - logOutput, err = os.OpenFile(opts.LogOutput, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) + logOutput, err = os.OpenFile(conf.LogOutput, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) if err != nil { _, _ = fmt.Fprintln(os.Stderr, fmt.Errorf("cannot create a log file: %s", err)) @@ -50,16 +50,16 @@ func Main() { Format: slogutil.FormatDefault, // TODO(d.kolyshev): Consider making configurable. AddTimestamp: true, - Verbose: opts.Verbose, + Verbose: conf.Verbose, }) ctx := context.Background() - if opts.Pprof { + if conf.Pprof { runPprof(l) } - err = runProxy(ctx, l, opts) + err = runProxy(ctx, l, conf) if err != nil { l.ErrorContext(ctx, "running dnsproxy", slogutil.KeyError, err) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 869db395f..d9aef0a81 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -191,8 +191,8 @@ type configuration struct { // parseConfig returns options parsed from the command args or config file. If // no options have been parsed, it returns a suitable exit code and an error. -func parseConfig() (opts *configuration, exitCode int, err error) { - opts = &configuration{ +func parseConfig() (conf *configuration, exitCode int, err error) { + conf = &configuration{ HTTPSServerName: "dnsproxy", UpstreamMode: string(proxy.UpstreamModeLoadBalance), CacheSizeBytes: 64 * 1024, @@ -201,21 +201,21 @@ func parseConfig() (opts *configuration, exitCode int, err error) { RatelimitSubnetLenIPv6: 56, } - err = parseCmdLineOptions(opts) - exitCode, needExit := processCmdLineOptions(opts, err) + err = parseCmdLineOptions(conf) + exitCode, needExit := processCmdLineOptions(conf, err) if needExit { return nil, exitCode, err } - confPath := opts.ConfigPath + confPath := conf.ConfigPath if confPath == "" { - return opts, exitCode, nil + return conf, exitCode, nil } // TODO(d.kolyshev): Bootstrap and use slog. fmt.Printf("dnsproxy config path: %s\n", confPath) - err = parseConfigFile(opts, confPath) + err = parseConfigFile(conf, confPath) if err != nil { return nil, osutil.ExitCodeFailure, fmt.Errorf( "parsing config file %s: %w", @@ -225,13 +225,13 @@ func parseConfig() (opts *configuration, exitCode int, err error) { } // Parse command-line args again as it has priority over YAML config. - err = parseCmdLineOptions(opts) + err = parseCmdLineOptions(conf) if err != nil { // Don't wrap the error, because it's informative enough as is. return nil, osutil.ExitCodeFailure, err } - return opts, exitCode, nil + return conf, exitCode, nil } // parseConfigFile fills options with the settings from file read by the given diff --git a/internal/cmd/flag.go b/internal/cmd/flag.go index 93ab4280d..42626d9f1 100644 --- a/internal/cmd/flag.go +++ b/internal/cmd/flag.go @@ -48,7 +48,7 @@ var _ flag.Value = (*float32Value)(nil) // Set implements the [flag.Value] interface for *float32Value. func (i *float32Value) Set(s string) (err error) { - v, err := strconv.ParseInt(s, 0, 32) + v, err := strconv.ParseFloat(s, 32) *i = float32Value(v) return err @@ -117,9 +117,9 @@ func (i *intSliceValue) String() (out string) { return out } -// stringsValue represent a struct with a slice of strings that can be defined -// as a flag for [flag.FlagSet]. -type stringsValue struct { +// stringSliceValue represent a struct with a slice of strings that can be +// defined as a flag for [flag.FlagSet]. +type stringSliceValue struct { // values is the pointer to a slice of string to store parsed values. values *[]string @@ -127,21 +127,22 @@ type stringsValue struct { wiped bool } -// newStringsValue returns a pointer to stringsValue with the given value. -func newStringsValue(val []string, p *[]string) (out *stringsValue) { +// newStringSliceValue returns a pointer to stringSliceValue with the given +// value. +func newStringSliceValue(val []string, p *[]string) (out *stringSliceValue) { *p = val - return &stringsValue{ + return &stringSliceValue{ values: p, wiped: false, } } // type check -var _ flag.Value = (*stringsValue)(nil) +var _ flag.Value = (*stringSliceValue)(nil) -// Set implements the [flag.Value] interface for *stringsValue. -func (i *stringsValue) Set(s string) (err error) { +// Set implements the [flag.Value] interface for *stringSliceValue. +func (i *stringSliceValue) Set(s string) (err error) { if !i.wiped { i.wiped = true *i.values = []string{} @@ -152,7 +153,7 @@ func (i *stringsValue) Set(s string) (err error) { return nil } -// String implements the [flag.Value] interface for *stringsValue. -func (i *stringsValue) String() (out string) { +// String implements the [flag.Value] interface for *stringSliceValue. +func (i *stringSliceValue) String() (out string) { return fmt.Sprintf("%s", *i.values) } diff --git a/internal/cmd/proxy.go b/internal/cmd/proxy.go index 43bfe6852..fbbc5a258 100644 --- a/internal/cmd/proxy.go +++ b/internal/cmd/proxy.go @@ -121,13 +121,13 @@ const defaultLocalTimeout = 1 * time.Second // initUpstreams inits upstream-related config fields. // // TODO(d.kolyshev): Join errors. -func (opts *configuration) initUpstreams( +func (conf *configuration) initUpstreams( ctx context.Context, l *slog.Logger, config *proxy.Config, ) (err error) { httpVersions := upstream.DefaultHTTPVersions - if opts.HTTP3 { + if conf.HTTP3 { httpVersions = []upstream.HTTPVersion{ upstream.HTTPVersion3, upstream.HTTPVersion2, @@ -135,14 +135,14 @@ func (opts *configuration) initUpstreams( } } - timeout := opts.Timeout.Duration + timeout := conf.Timeout.Duration bootOpts := &upstream.Options{ Logger: l, HTTPVersions: httpVersions, - InsecureSkipVerify: opts.Insecure, + InsecureSkipVerify: conf.Insecure, Timeout: timeout, } - boot, err := initBootstrap(ctx, l, opts.BootstrapDNS, bootOpts) + boot, err := initBootstrap(ctx, l, conf.BootstrapDNS, bootOpts) if err != nil { return fmt.Errorf("initializing bootstrap: %w", err) } @@ -150,11 +150,11 @@ func (opts *configuration) initUpstreams( upsOpts := &upstream.Options{ Logger: l, HTTPVersions: httpVersions, - InsecureSkipVerify: opts.Insecure, + InsecureSkipVerify: conf.Insecure, Bootstrap: boot, Timeout: timeout, } - upstreams := loadServersList(opts.Upstreams) + upstreams := loadServersList(conf.Upstreams) config.UpstreamConfig, err = proxy.ParseUpstreamsConfig(upstreams, upsOpts) if err != nil { @@ -167,7 +167,7 @@ func (opts *configuration) initUpstreams( Bootstrap: boot, Timeout: min(defaultLocalTimeout, timeout), } - privateUpstreams := loadServersList(opts.PrivateRDNSUpstreams) + privateUpstreams := loadServersList(conf.PrivateRDNSUpstreams) private, err := proxy.ParseUpstreamsConfig(privateUpstreams, privateUpsOpts) if err != nil { @@ -178,7 +178,7 @@ func (opts *configuration) initUpstreams( config.PrivateRDNSUpstreamConfig = private } - fallbackUpstreams := loadServersList(opts.Fallbacks) + fallbackUpstreams := loadServersList(conf.Fallbacks) fallbacks, err := proxy.ParseUpstreamsConfig(fallbackUpstreams, upsOpts) if err != nil { return fmt.Errorf("parsing fallback upstreams configuration: %w", err) @@ -188,8 +188,8 @@ func (opts *configuration) initUpstreams( config.Fallbacks = fallbacks } - if opts.UpstreamMode != "" { - err = config.UpstreamMode.UnmarshalText([]byte(opts.UpstreamMode)) + if conf.UpstreamMode != "" { + err = config.UpstreamMode.UnmarshalText([]byte(conf.UpstreamMode)) if err != nil { return fmt.Errorf("parsing upstream mode: %w", err) } @@ -241,22 +241,22 @@ func initBootstrap( } // initEDNS inits EDNS-related config fields. -func (opts *configuration) initEDNS( +func (conf *configuration) initEDNS( ctx context.Context, l *slog.Logger, config *proxy.Config, ) (err error) { - if opts.EDNSAddr == "" { + if conf.EDNSAddr == "" { return nil } - if !opts.EnableEDNSSubnet { - l.WarnContext(ctx, "--edns is required", "--edns-addr", opts.EDNSAddr) + if !conf.EnableEDNSSubnet { + l.WarnContext(ctx, "--edns is required", "--edns-addr", conf.EDNSAddr) return nil } - config.EDNSAddr, err = netutil.ParseIP(opts.EDNSAddr) + config.EDNSAddr, err = netutil.ParseIP(conf.EDNSAddr) if err != nil { return fmt.Errorf("parsing edns-addr: %w", err) } @@ -265,12 +265,12 @@ func (opts *configuration) initEDNS( } // initBogusNXDomain inits BogusNXDomain structure. -func (opts *configuration) initBogusNXDomain(ctx context.Context, l *slog.Logger, config *proxy.Config) { - if len(opts.BogusNXDomain) == 0 { +func (conf *configuration) initBogusNXDomain(ctx context.Context, l *slog.Logger, config *proxy.Config) { + if len(conf.BogusNXDomain) == 0 { return } - for i, s := range opts.BogusNXDomain { + for i, s := range conf.BogusNXDomain { p, err := proxynetutil.ParseSubnet(s) if err != nil { // TODO(a.garipov): Consider returning this err as a proper error. @@ -282,10 +282,10 @@ func (opts *configuration) initBogusNXDomain(ctx context.Context, l *slog.Logger } // initTLSConfig inits the TLS config. -func (opts *configuration) initTLSConfig(config *proxy.Config) (err error) { - if opts.TLSCertPath != "" && opts.TLSKeyPath != "" { +func (conf *configuration) initTLSConfig(config *proxy.Config) (err error) { + if conf.TLSCertPath != "" && conf.TLSKeyPath != "" { var tlsConfig *tls.Config - tlsConfig, err = newTLSConfig(opts) + tlsConfig, err = newTLSConfig(conf) if err != nil { return fmt.Errorf("loading TLS config: %w", err) } @@ -297,14 +297,14 @@ func (opts *configuration) initTLSConfig(config *proxy.Config) (err error) { } // initDNSCryptConfig inits the DNSCrypt config. -func (opts *configuration) initDNSCryptConfig(config *proxy.Config) (err error) { - if opts.DNSCryptConfigPath == "" { +func (conf *configuration) initDNSCryptConfig(config *proxy.Config) (err error) { + if conf.DNSCryptConfigPath == "" { return } - b, err := os.ReadFile(opts.DNSCryptConfigPath) + b, err := os.ReadFile(conf.DNSCryptConfigPath) if err != nil { - return fmt.Errorf("reading DNSCrypt config %q: %w", opts.DNSCryptConfigPath, err) + return fmt.Errorf("reading DNSCrypt config %q: %w", conf.DNSCryptConfigPath, err) } rc := &dnscrypt.ResolverConfig{} @@ -352,19 +352,19 @@ func parseListenAddrs(addrStrs []string) (addrs []netip.Addr, err error) { } // initListenAddrs sets up proxy configuration listen IP addresses. -func (opts *configuration) initListenAddrs(config *proxy.Config) (err error) { - addrs, err := parseListenAddrs(opts.ListenAddrs) +func (conf *configuration) initListenAddrs(config *proxy.Config) (err error) { + addrs, err := parseListenAddrs(conf.ListenAddrs) if err != nil { return fmt.Errorf("parsing listen addresses: %w", err) } - if len(opts.ListenPorts) == 0 { + if len(conf.ListenPorts) == 0 { // If ListenPorts has not been parsed through config file nor command // line we set it to 53. - opts.ListenPorts = []int{53} + conf.ListenPorts = []int{53} } - for _, port := range opts.ListenPorts { + for _, port := range conf.ListenPorts { for _, ip := range addrs { addrPort := netip.AddrPortFrom(ip, uint16(port)) @@ -373,8 +373,8 @@ func (opts *configuration) initListenAddrs(config *proxy.Config) (err error) { } } - initTLSListenAddrs(config, opts, addrs) - initDNSCryptListenAddrs(config, opts, addrs) + initTLSListenAddrs(config, conf, addrs) + initDNSCryptListenAddrs(config, conf, addrs) return nil } @@ -428,30 +428,30 @@ func initDNSCryptListenAddrs(config *proxy.Config, options *configuration, addrs // initSubnets sets the DNS64 configuration into conf. // // TODO(d.kolyshev): Join errors. -func (opts *configuration) initSubnets(conf *proxy.Config) (err error) { - if conf.UseDNS64 = opts.DNS64; conf.UseDNS64 { - for i, p := range opts.DNS64Prefix { +func (conf *configuration) initSubnets(proxyConf *proxy.Config) (err error) { + if proxyConf.UseDNS64 = conf.DNS64; proxyConf.UseDNS64 { + for i, p := range conf.DNS64Prefix { var pref netip.Prefix pref, err = netip.ParsePrefix(p) if err != nil { return fmt.Errorf("parsing dns64 prefix at index %d: %w", i, err) } - conf.DNS64Prefs = append(conf.DNS64Prefs, pref) + proxyConf.DNS64Prefs = append(proxyConf.DNS64Prefs, pref) } } - if !opts.UsePrivateRDNS { + if !conf.UsePrivateRDNS { return nil } - return opts.initPrivateSubnets(conf) + return conf.initPrivateSubnets(proxyConf) } // initSubnets sets the private subnets configuration into conf. -func (opts *configuration) initPrivateSubnets(conf *proxy.Config) (err error) { - private := make([]netip.Prefix, 0, len(opts.PrivateSubnets)) - for i, p := range opts.PrivateSubnets { +func (conf *configuration) initPrivateSubnets(proxyConf *proxy.Config) (err error) { + private := make([]netip.Prefix, 0, len(conf.PrivateSubnets)) + for i, p := range conf.PrivateSubnets { var pref netip.Prefix pref, err = netip.ParsePrefix(p) if err != nil { @@ -462,7 +462,7 @@ func (opts *configuration) initPrivateSubnets(conf *proxy.Config) (err error) { } if len(private) > 0 { - conf.PrivateSubnets = netutil.SliceSubnetSet(private) + proxyConf.PrivateSubnets = netutil.SliceSubnetSet(private) } return nil @@ -504,8 +504,8 @@ func loadServersList(sources []string) []string { // hostsFiles returns the list of hosts files to resolve from. It's empty if // resolving from hosts files is disabled. -func (opts *configuration) hostsFiles(ctx context.Context, l *slog.Logger) (paths []string, err error) { - if opts.HostsFileEnabled { +func (conf *configuration) hostsFiles(ctx context.Context, l *slog.Logger) (paths []string, err error) { + if conf.HostsFileEnabled { l.DebugContext(ctx, "hosts files are disabled") return nil, nil @@ -513,8 +513,8 @@ func (opts *configuration) hostsFiles(ctx context.Context, l *slog.Logger) (path l.DebugContext(ctx, "hosts files are enabled") - if len(opts.HostsFiles) > 0 { - return opts.HostsFiles, nil + if len(conf.HostsFiles) > 0 { + return conf.HostsFiles, nil } paths, err = hostsfile.DefaultHostsPaths()