diff --git a/README.md b/README.md index 5f9f5bbf..3a7920a0 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,14 @@ Usage of ./dnsproxy: Cache size (in bytes). Default: 64k. --config-path=path YAML configuration file. Minimal working configuration in config.yaml.dist. Options passed through command line will override the ones from this file. + --doh-insecure-enabled + If specified, the DoH server will skip TLS certificate verification. + --doh-routes + Routes for DNS-over-HTTPS. If not specified, the default routes are registered: + - "GET /", deprecated and will soon be removed, + - "POST /", deprecated and will soon be removed. + - "GET /dns-query", + - "POST /dns-query". --dns64 If specified, dnsproxy will act as a DNS64 server. --dns64-prefix=subnet diff --git a/internal/cmd/args.go b/internal/cmd/args.go index c4984503..c55c848d 100644 --- a/internal/cmd/args.go +++ b/internal/cmd/args.go @@ -67,6 +67,8 @@ const ( pendingRequestsEnabledIdx dns64Idx usePrivateRDNSIdx + dohRoutesIdx + dohInsecureEnabledIdx ) // commandLineOption contains information about a command-line option: its long @@ -405,6 +407,18 @@ var commandLineOptions = []*commandLineOption{ short: "", valueType: "", }, + dohRoutesIdx: { + description: "List of routes for DNS-over-HTTPS, can be specified multiple times.", + long: "doh-routes", + short: "", + valueType: "route", + }, + dohInsecureEnabledIdx: { + description: "If specified, the DoH server will skip TLS certificate verification.", + long: "doh-insecure-enabled", + short: "", + valueType: "", + }, } // parseCmdLineOptions parses the command-line options. conf must not be nil. @@ -464,6 +478,8 @@ func parseCmdLineOptions(conf *configuration) (err error) { pendingRequestsEnabledIdx: &conf.PendingRequestsEnabled, dns64Idx: &conf.DNS64, usePrivateRDNSIdx: &conf.UsePrivateRDNS, + dohRoutesIdx: &conf.DoHRoutes, + dohInsecureEnabledIdx: &conf.DoHInsecureEnabled, } { addOption(flags, fieldPtr, commandLineOptions[i]) } @@ -536,8 +552,8 @@ func addOption(flags *flag.FlagSet, fieldPtr any, o *commandLineOption) { defineFlagVar(flags, (*uint32Value)(fieldPtr), o) case *float32: defineFlagVar(flags, (*float32Value)(fieldPtr), o) - case *[]int: - defineFlagVar(flags, newIntSliceValue(fieldPtr), o) + case *[]uint16: + defineFlagVar(flags, newUInt16SliceValue(fieldPtr), o) case *[]string: defineFlagVar(flags, newStringSliceValue(fieldPtr), o) case *timeutil.Duration: diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 376930cc..270ff3f4 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -46,20 +46,25 @@ type configuration struct { // ListenAddrs is the list of server's listen addresses. ListenAddrs []string `yaml:"listen-addrs"` - // ListenPorts are the ports server listens on. - ListenPorts []int `yaml:"listen-ports"` + // DoHRoutes is the list of routes for DNS-over-HTTPS. It must be a slice + // of valid route patterns, if it is empty, the default routes are + // registered. + DoHRoutes []string `yaml:"doh-routes"` + + // ListenPorts are the ports server listens on for plain DNS. + ListenPorts []uint16 `yaml:"listen-ports"` // HTTPSListenPorts are the ports server listens on for DNS-over-HTTPS. - HTTPSListenPorts []int `yaml:"https-port"` + HTTPSListenPorts []uint16 `yaml:"https-port"` // TLSListenPorts are the ports server listens on for DNS-over-TLS. - TLSListenPorts []int `yaml:"tls-port"` + TLSListenPorts []uint16 `yaml:"tls-port"` // QUICListenPorts are the ports server listens on for DNS-over-QUIC. - QUICListenPorts []int `yaml:"quic-port"` + QUICListenPorts []uint16 `yaml:"quic-port"` // DNSCryptListenPorts are the ports server listens on for DNSCrypt. - DNSCryptListenPorts []int `yaml:"dnscrypt-port"` + DNSCryptListenPorts []uint16 `yaml:"dnscrypt-port"` // Upstreams is the list of DNS upstream servers. Upstreams []string `yaml:"upstream"` @@ -146,6 +151,10 @@ type configuration struct { // TODO(d.kolyshev): Use more suitable type. TLSMaxVersion float32 `yaml:"tls-max-version"` + // DoHInsecureEnabled controls whether the DoH server should skip TLS + // certificate verification. + DoHInsecureEnabled bool `yaml:"doh-insecure-enabled"` + // help, if true, prints the command-line option help message and quit with // a successful exit-code. help bool diff --git a/internal/cmd/flag.go b/internal/cmd/flag.go index 10cc1321..2e3a5bd8 100644 --- a/internal/cmd/flag.go +++ b/internal/cmd/flag.go @@ -47,47 +47,48 @@ func (i *float32Value) String() (out string) { return strconv.FormatFloat(float64(*i), 'f', 3, 32) } -// intSliceValue represent a struct with a slice of integers that can be defined -// as a flag for [flag.FlagSet]. -type intSliceValue struct { - // values is the pointer to a slice of integers to store parsed values. - values *[]int +// uint16SliceValue represent a struct with a slice of uint16 values that can be +// defined as a flag for [flag.FlagSet]. +type uint16SliceValue struct { + // values is the pointer to a slice of uint16 to store parsed values. + values *[]uint16 // isSet is false until the corresponding flag is met for the first time. // When the flag is found, the default value is overwritten with zero value. isSet bool } -// newIntSliceValue returns a pointer to intSliceValue with the given value. -func newIntSliceValue(p *[]int) (out *intSliceValue) { - return &intSliceValue{ +// newUInt16SliceValue returns a pointer to uint16SliceValue with the given +// value. +func newUInt16SliceValue(p *[]uint16) (out *uint16SliceValue) { + return &uint16SliceValue{ values: p, isSet: false, } } // type check -var _ flag.Value = (*intSliceValue)(nil) +var _ flag.Value = (*uint16SliceValue)(nil) -// Set implements the [flag.Value] interface for *intSliceValue. -func (i *intSliceValue) Set(s string) (err error) { - v, err := strconv.Atoi(s) +// Set implements the [flag.Value] interface for *uint16SliceValue. +func (i *uint16SliceValue) Set(s string) (err error) { + v, err := strconv.ParseUint(s, 10, 16) if err != nil { - return fmt.Errorf("parsing integer slice arg %q: %w", s, err) + return fmt.Errorf("parsing uint16 slice arg %q: %w", s, err) } if !i.isSet { i.isSet = true - *i.values = []int{} + *i.values = []uint16{} } - *i.values = append(*i.values, v) + *i.values = append(*i.values, uint16(v)) return nil } -// String implements the [flag.Value] interface for *intSliceValue. -func (i *intSliceValue) String() (out string) { +// String implements the [flag.Value] interface for *uint16SliceValue. +func (i *uint16SliceValue) String() (out string) { if i == nil || i.values == nil { return "" } @@ -98,7 +99,7 @@ func (i *intSliceValue) String() (out string) { stringutil.WriteToBuilder(sb, ",") } - stringutil.WriteToBuilder(sb, strconv.Itoa(v)) + stringutil.WriteToBuilder(sb, strconv.FormatUint(uint64(v), 10)) } return sb.String() diff --git a/internal/cmd/proxy.go b/internal/cmd/proxy.go index 93a74713..14930881 100644 --- a/internal/cmd/proxy.go +++ b/internal/cmd/proxy.go @@ -26,6 +26,11 @@ import ( "gopkg.in/yaml.v3" ) +// defaultHTTPTimeout is the default timeout for HTTP server operations. +// +// TODO(a.garipov): Consider making configurable. +const defaultHTTPTimeout = 10 * time.Second + // TODO(e.burkov): Use a separate type for the YAML configuration file. // createProxyConfig initializes [proxy.Config]. l must not be nil. @@ -58,6 +63,24 @@ func createProxyConfig( return nil, fmt.Errorf("ratelimit mw: %w", err) } + httpConf := &proxy.HTTPConfig{ + ServerHeader: conf.HTTPSServerName, + Routes: conf.DoHRoutes, + ReadTimeout: defaultHTTPTimeout, + WriteTimeout: defaultHTTPTimeout, + HTTP3Enabled: conf.HTTP3, + InsecureEnabled: conf.DoHInsecureEnabled, + } + + if uiStr := conf.HTTPSUserinfo; uiStr != "" { + user, pass, ok := strings.Cut(uiStr, ":") + if ok { + httpConf.Userinfo = url.UserPassword(user, pass) + } else { + httpConf.Userinfo = url.User(user) + } + } + proxyConf = &proxy.Config{ Logger: l.With(slogutil.KeyPrefix, proxy.LogPrefix), CacheEnabled: conf.Cache, @@ -68,7 +91,6 @@ func createProxyConfig( CacheOptimisticMaxAge: time.Duration(conf.OptimisticMaxAge), CacheOptimistic: conf.CacheOptimistic, RefuseAny: conf.RefuseAny, - HTTP3: conf.HTTP3, // TODO(e.burkov): The following CIDRs are aimed to match any address. // This is not quite proper approach to be used by default so think // about configuring it. @@ -78,7 +100,6 @@ func createProxyConfig( }, EnableEDNSClientSubnet: conf.EnableEDNSSubnet, UDPBufferSize: conf.UDPBufferSize, - HTTPSServerName: conf.HTTPSServerName, MaxGoroutines: conf.MaxGoRoutines, UsePrivateRDNS: conf.UsePrivateRDNS, PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed), @@ -86,15 +107,7 @@ func createProxyConfig( PendingRequests: &proxy.PendingRequestsConfig{ Enabled: conf.PendingRequestsEnabled, }, - } - - if uiStr := conf.HTTPSUserinfo; uiStr != "" { - user, pass, ok := strings.Cut(uiStr, ":") - if ok { - proxyConf.Userinfo = url.UserPassword(user, pass) - } else { - proxyConf.Userinfo = url.User(user) - } + HTTPConfig: httpConf, } conf.initBogusNXDomain(ctx, l, proxyConf) @@ -391,60 +404,58 @@ func (conf *configuration) initListenAddrs(config *proxy.Config) (err error) { if len(conf.ListenPorts) == 0 { // If ListenPorts has not been parsed through config file nor command // line we set it to 53. - conf.ListenPorts = []int{53} + conf.ListenPorts = []uint16{53} } for _, port := range conf.ListenPorts { for _, ip := range addrs { - addrPort := netip.AddrPortFrom(ip, uint16(port)) + addrPort := netip.AddrPortFrom(ip, port) config.UDPListenAddr = append(config.UDPListenAddr, net.UDPAddrFromAddrPort(addrPort)) config.TCPListenAddr = append(config.TCPListenAddr, net.TCPAddrFromAddrPort(addrPort)) } } - initTLSListenAddrs(config, conf, addrs) - initDNSCryptListenAddrs(config, conf, addrs) + if config.TLSConfig != nil { + initTLSListenAddrs(config, conf, addrs) + } + + if config.DNSCryptResolverCert != nil && config.DNSCryptProviderName != "" { + initDNSCryptListenAddrs(config, conf, addrs) + } return nil } -// initTLSListenAddrs sets up proxy configuration TLS listen addresses. +// initTLSListenAddrs sets up proxy configuration TLS listen addresses. conf, +// proxyConf must not be nil. If conf.HTTPSListenPorts is not empty, +// proxyConf.HTTPConfig must not be nil. func initTLSListenAddrs(proxyConf *proxy.Config, conf *configuration, addrs []netip.Addr) { - if proxyConf.TLSConfig == nil { - return - } - + httpConfig := proxyConf.HTTPConfig for _, ip := range addrs { for _, port := range conf.TLSListenPorts { - a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port))) + a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, port)) proxyConf.TLSListenAddr = append(proxyConf.TLSListenAddr, a) } for _, port := range conf.HTTPSListenPorts { - a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port))) - proxyConf.HTTPSListenAddr = append(proxyConf.HTTPSListenAddr, a) + a := netip.AddrPortFrom(ip, port) + httpConfig.ListenAddresses = append(httpConfig.ListenAddresses, a) } for _, port := range conf.QUICListenPorts { - a := net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port))) + a := net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip, port)) proxyConf.QUICListenAddr = append(proxyConf.QUICListenAddr, a) } } } // initDNSCryptListenAddrs sets up proxy configuration DNSCrypt listen -// addresses. +// addresses. proxyConf and conf must not be nil. func initDNSCryptListenAddrs(proxyConf *proxy.Config, conf *configuration, addrs []netip.Addr) { - if proxyConf.DNSCryptResolverCert == nil || proxyConf.DNSCryptProviderName == "" { - return - } - for _, port := range conf.DNSCryptListenPorts { - p := uint16(port) - for _, ip := range addrs { - addrPort := netip.AddrPortFrom(ip, p) + addrPort := netip.AddrPortFrom(ip, port) tcp := net.TCPAddrFromAddrPort(addrPort) proxyConf.DNSCryptTCPListenAddr = append(proxyConf.DNSCryptTCPListenAddr, tcp) diff --git a/proxy/config.go b/proxy/config.go index fd8ca711..99ecf8f1 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -76,11 +76,6 @@ type Config struct { // TODO(e.burkov): Add explicit boolean for disabling fallbacks. Fallbacks *UpstreamConfig - // Userinfo is the sole permitted userinfo for the DoH basic authentication. - // If Userinfo is set, all DoH queries are required to have this basic - // authentication information. - Userinfo *url.Userinfo - // TLSConfig is the TLS configuration. Required for DNS-over-TLS, // DNS-over-HTTP, and DNS-over-QUIC servers. TLSConfig *tls.Config @@ -93,14 +88,14 @@ type Config struct { // retries are disabled. BindRetryConfig *BindRetryConfig + // HTTPConfig is the configuration for HTTP requests proxying. Required for + // DoH server. If nil, the DoH server is disabled. + HTTPConfig *HTTPConfig + // DNSCryptProviderName is the DNSCrypt provider name. Required for // DNSCrypt server. DNSCryptProviderName string - // HTTPSServerName sets the Server header of the HTTPS server responses, if - // not empty. - HTTPSServerName string - // UpstreamMode determines the logic through which upstreams will be used. // If not specified the [proxy.UpstreamModeLoadBalance] is used. UpstreamMode UpstreamMode @@ -113,10 +108,6 @@ type Config struct { // DNS-over-TCP requests. TCPListenAddr []*net.TCPAddr - // HTTPSListenAddr is the set of TCP addresses to listen for DNS-over-HTTPS - // requests. - HTTPSListenAddr []*net.TCPAddr - // TLSListenAddr is the set of TCP addresses to listen for DNS-over-TLS // requests. TLSListenAddr []*net.TCPAddr @@ -182,9 +173,6 @@ type Config struct { // RefuseAny makes proxy refuse the requests of type ANY. RefuseAny bool - // HTTP3 enables HTTP/3 support for HTTPS server. - HTTP3 bool - // Enable EDNS Client Subnet option DNS requests to the upstream server will // contain an OPT record with Client Subnet option. If the original request // already has this option set, we pass it through as is. Otherwise, we set @@ -237,6 +225,45 @@ type PendingRequestsConfig struct { Enabled bool } +// HTTPConfig is the configuration for HTTP requests proxying. +type HTTPConfig struct { + // Userinfo is the sole permitted userinfo for the DoH basic authentication. + // If Userinfo is set, all DoH queries are required to have this basic + // authentication information. + Userinfo *url.Userinfo + + // ServerHeader sets the Server header of the HTTPS server responses, if not + // empty. + ServerHeader string + + // ListenAddresses is the set of addresses to listen for DNS-over-HTTPS + // requests. If it is empty the proxy doesn't start the HTTPS server, but + // still can be used as an http.Handler with [Proxy.ServeHTTP]. + ListenAddresses []netip.AddrPort + + // Routes is the set of routes to handle. It must be a slice of valid route + // patterns, if it is empty, the default routes are registered. It is + // ignored if ListenAddresses is empty. + Routes []string + + // ReadTimeout is the maximum duration before timing out reads of the + // request. A zero or negative value means there will be no timeout. It is + // ignored if ListenAddresses is empty. + ReadTimeout time.Duration + + // WriteTimeout is the maximum duration before timing out writes of the + // response. A zero or negative value means there will be no timeout. It + // is ignored if ListenAddresses is empty. + WriteTimeout time.Duration + + // HTTP3Enabled specifies if HTTP/3 support for HTTPS server. It is ignored + // if ListenAddresses is empty. + HTTP3Enabled bool + + // InsecureEnabled specifies if unencrypted DoH requests are allowed. + InsecureEnabled bool +} + // validateConfig verifies that the supplied configuration is valid and returns // an error if it's not. // @@ -339,7 +366,7 @@ func (p *Proxy) validateTLSConfig() (err error) { return errors.Error("tls listener configuration not found") } - if p.HTTPSListenAddr != nil { + if p.HTTPConfig != nil && p.HTTPConfig.ListenAddresses != nil { return errors.Error("https listener configuration not found") } @@ -351,11 +378,11 @@ func (p *Proxy) validateTLSConfig() (err error) { } // hasListenAddrs - is there any addresses to listen to? -func (p *Proxy) hasListenAddrs() bool { +func (p *Proxy) hasListenAddrs() (ok bool) { return p.UDPListenAddr != nil || p.TCPListenAddr != nil || p.TLSListenAddr != nil || - p.HTTPSListenAddr != nil || + (p.HTTPConfig != nil && p.HTTPConfig.ListenAddresses != nil) || p.QUICListenAddr != nil || p.DNSCryptUDPListenAddr != nil || p.DNSCryptTCPListenAddr != nil diff --git a/proxy/proxy.go b/proxy/proxy.go index ce591425..8effbad1 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -293,15 +293,14 @@ func loggerOrDefault(l *slog.Logger) (logger *slog.Logger) { return slog.Default().With(slogutil.KeyPrefix, LogPrefix) } -// validateBasicAuth validates the basic-auth mode settings if p.Config.Userinfo -// is set. +// validateBasicAuth validates the HTTP settings if HTTPConfig.Userinfo is set. func (p *Proxy) validateBasicAuth() (err error) { - conf := p.Config - if conf.Userinfo == nil { + conf := p.Config.HTTPConfig + if conf == nil || conf.Userinfo == nil { return nil } - return validate.NotEmptySlice("HTTPSListenAddr", conf.HTTPSListenAddr) + return validate.NotEmptySlice("HTTPConfig.ListenAddresses", conf.ListenAddresses) } // Returns true if proxy is started. It is safe for concurrent use. diff --git a/proxy/route.go b/proxy/route.go new file mode 100644 index 00000000..afbbf666 --- /dev/null +++ b/proxy/route.go @@ -0,0 +1,37 @@ +package proxy + +import "net/http" + +// Default path pattern constants. +const ( + // Deprecated: Use pathPatternDNSQuery instead. + pathPatternRoot = "/" + pathPatternDNSQuery = "/dns-query" +) + +// Default route pattern constants. +const ( + routePatternRootGet = http.MethodGet + " " + pathPatternRoot + routePatternRootPost = http.MethodPost + " " + pathPatternRoot + routePatternDNSQueryGet = http.MethodGet + " " + pathPatternDNSQuery + routePatternDNSQueryPost = http.MethodPost + " " + pathPatternDNSQuery +) + +// routeDoH registers DoH handlers in mux. p.HTTPConfig must not be nil. +// p.HTTPConfig.Routes must be valid, if p.HTTPConfig.Routes is empty, the +// default routes are registered. +func (p *Proxy) routeDoH(mux *http.ServeMux) { + routes := p.HTTPConfig.Routes + if len(routes) == 0 { + mux.Handle(routePatternRootGet, p) + mux.Handle(routePatternRootPost, p) + mux.Handle(routePatternDNSQueryGet, p) + mux.Handle(routePatternDNSQueryPost, p) + + return + } + + for _, route := range routes { + mux.Handle(route, p) + } +} diff --git a/proxy/serverhttps.go b/proxy/serverhttps.go index 36677cd0..101c376f 100644 --- a/proxy/serverhttps.go +++ b/proxy/serverhttps.go @@ -75,29 +75,40 @@ func (p *Proxy) listenH3( // initHTTPSListeners creates TCP/UDP listeners and HTTP/H3 servers. func (p *Proxy) initHTTPSListeners(ctx context.Context) (err error) { - p.httpsServer = &http.Server{ - Handler: p, - ReadHeaderTimeout: defaultTimeout, - WriteTimeout: defaultTimeout, + httpConf := p.HTTPConfig + if httpConf == nil { + p.logger.DebugContext(ctx, "no https configuration provided") + + return nil } - if p.HTTP3 { + mux := http.NewServeMux() + p.routeDoH(mux) + + p.httpsServer = &http.Server{ + Handler: mux, + ReadTimeout: httpConf.ReadTimeout, + ReadHeaderTimeout: httpConf.ReadTimeout, + WriteTimeout: httpConf.WriteTimeout, + } + + if httpConf.HTTP3Enabled { p.h3Server = &http3.Server{ - Handler: p, + Handler: mux, } } - for _, addr := range p.HTTPSListenAddr { + for _, addr := range httpConf.ListenAddresses { p.logger.InfoContext(ctx, "creating an https server") - ln, tcpAddr, lErr := p.listenHTTP(ctx, addr) + ln, tcpAddr, lErr := p.listenHTTP(ctx, net.TCPAddrFromAddrPort(addr)) if lErr != nil { - return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, lErr) + return fmt.Errorf("failed to start https server on %s: %w", addr, lErr) } p.httpsListen = append(p.httpsListen, ln) - if p.HTTP3 { + if httpConf.HTTP3Enabled { // HTTP/3 server listens to the same pair IP:port as the one HTTP/2 // server listens to. udpAddr := &net.UDPAddr{IP: tcpAddr.IP, Port: tcpAddr.Port} @@ -105,7 +116,7 @@ func (p *Proxy) initHTTPSListeners(ctx context.Context) (err error) { var quicListen *quic.EarlyListener quicListen, err = p.listenH3(ctx, udpAddr) if err != nil { - return fmt.Errorf("failed to start HTTP/3 server on %s: %w", udpAddr, err) + return fmt.Errorf("failed to start h3 server on %s: %w", udpAddr, err) } p.h3Listen = append(p.h3Listen, quicListen) @@ -172,6 +183,8 @@ func newDoHReq(r *http.Request, l *slog.Logger) (req *dns.Msg, statusCode int) { // // Here is what it returns: // +// - http.StatusNotFound if the request is not encrypted and proxy is not +// configured to accept unencrypted requests, // - http.StatusBadRequest if there is no DNS request data, // - http.StatusUnsupportedMediaType if request content type is not // "application/dns-message", @@ -179,6 +192,13 @@ func newDoHReq(r *http.Request, l *slog.Logger) (req *dns.Msg, statusCode int) { func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.logger.Debug("incoming https request", "url", r.URL) + if !p.HTTPConfig.InsecureEnabled && r.TLS == nil { + statusCode := http.StatusNotFound + http.Error(w, http.StatusText(statusCode), statusCode) + + return + } + raddr, prx, err := remoteAddr(r, p.logger) if err != nil { p.logger.Debug("getting real ip", slogutil.KeyError, err) @@ -221,13 +241,13 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { // checkBasicAuth checks the basic authorization data, if necessary, and if the // data isn't valid, it writes an error. shouldHandle is false if the request -// has been denied. +// has been denied. p.HTTPConfig must not be nil. func (p *Proxy) checkBasicAuth( w http.ResponseWriter, r *http.Request, raddr netip.AddrPort, ) (shouldHandle bool) { - ui := p.Config.Userinfo + ui := p.HTTPConfig.Userinfo if ui == nil { return true } @@ -273,8 +293,8 @@ func (p *Proxy) respondHTTPS(d *DNSContext) (err error) { return fmt.Errorf("packing message: %w", err) } - if srvName := p.Config.HTTPSServerName; srvName != "" { - w.Header().Set(httphdr.Server, srvName) + if srvHeader := p.HTTPConfig.ServerHeader; srvHeader != "" { + w.Header().Set(httphdr.Server, srvHeader) } w.Header().Set(httphdr.ContentType, "application/dns-message") diff --git a/proxy/serverhttps_internal_test.go b/proxy/serverhttps_internal_test.go index 3c5c756f..9497921c 100644 --- a/proxy/serverhttps_internal_test.go +++ b/proxy/serverhttps_internal_test.go @@ -38,15 +38,19 @@ func TestHttpsProxy(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tlsConf, caPem := newTLSConfig(t) + + httpConf := &HTTPConfig{ + ListenAddresses: []netip.AddrPort{localhostAnyPort}, + HTTP3Enabled: tc.http3, + } dnsProxy := mustNew(t, &Config{ - Logger: testLogger, - TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - HTTPSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, - TLSConfig: tlsConf, - UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), - TrustedProxies: defaultTrustedProxies, - HTTP3: tc.http3, + Logger: testLogger, + TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, + QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, + TLSConfig: tlsConf, + UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), + TrustedProxies: defaultTrustedProxies, + HTTPConfig: httpConf, }) servicetest.RequireRun(t, dnsProxy, testTimeout) @@ -83,15 +87,18 @@ func TestProxy_trustedProxies(t *testing.T) { // Prepare the proxy server. tlsConf, caPem := newTLSConfig(t) + httpConf := &HTTPConfig{ + ListenAddresses: []netip.AddrPort{localhostAnyPort}, + } dnsProxy := mustNew(t, &Config{ - Logger: testLogger, - UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), - TrustedProxies: defaultTrustedProxies, - RequestHandler: reqHandler, - TLSConfig: tlsConf, - TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - HTTPSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, + Logger: testLogger, + UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), + TrustedProxies: defaultTrustedProxies, + RequestHandler: reqHandler, + TLSConfig: tlsConf, + TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, + QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, + HTTPConfig: httpConf, }) client := createTestHTTPClient(dnsProxy, caPem, false) diff --git a/proxy/serverquic_internal_test.go b/proxy/serverquic_internal_test.go index 983beb77..1106f0db 100644 --- a/proxy/serverquic_internal_test.go +++ b/proxy/serverquic_internal_test.go @@ -93,14 +93,13 @@ func TestProxy_quicLargePackets(t *testing.T) { serverConfig, caPem := newTLSConfig(t) dnsProxy := mustNew(t, &Config{ - Logger: testLogger, - UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), - TrustedProxies: defaultTrustedProxies, - RequestHandler: reqHandler, - TLSConfig: serverConfig, - TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - HTTPSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, + Logger: testLogger, + UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), + TrustedProxies: defaultTrustedProxies, + RequestHandler: reqHandler, + TLSConfig: serverConfig, + TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, + QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, }) servicetest.RequireRun(t, dnsProxy, testTimeout) diff --git a/proxy/servertcp_internal_test.go b/proxy/servertcp_internal_test.go index 137f5947..39992f11 100644 --- a/proxy/servertcp_internal_test.go +++ b/proxy/servertcp_internal_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestTcpProxy(t *testing.T) { +func TestProxy_tcp(t *testing.T) { dnsProxy := mustStartDefaultProxy(t) // Create a DNS-over-TCP client connection @@ -22,16 +22,15 @@ func TestTcpProxy(t *testing.T) { sendTestMessages(t, conn) } -func TestTlsProxy(t *testing.T) { +func TestProxy_tls(t *testing.T) { serverConfig, caPem := newTLSConfig(t) dnsProxy := mustNew(t, &Config{ - Logger: testLogger, - TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - HTTPSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, - QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, - TLSConfig: serverConfig, - UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), - TrustedProxies: defaultTrustedProxies, + Logger: testLogger, + TLSListenAddr: []*net.TCPAddr{net.TCPAddrFromAddrPort(localhostAnyPort)}, + QUICListenAddr: []*net.UDPAddr{net.UDPAddrFromAddrPort(localhostAnyPort)}, + TLSConfig: serverConfig, + UpstreamConfig: newTestUpstreamConfig(t, defaultTimeout, testDefaultUpstreamAddr), + TrustedProxies: defaultTrustedProxies, }) servicetest.RequireRun(t, dnsProxy, testTimeout)