Skip to content

Commit b7d4b05

Browse files
committed
Pull request: AGDNS-2848-imp-http-conf
Merge in GO/dnsproxy from AGDNS-2848-imp-http-conf to master Squashed commit of the following: commit daa12cc Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Mar 10 16:31:45 2026 +0700 all: imp code commit 30141c3 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Mar 10 16:28:36 2026 +0700 all: imp code commit 5718a97 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Mar 10 16:23:52 2026 +0700 proxy: mv UserInfo commit aa0bd41 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Mar 9 15:41:00 2026 +0700 proxy: imp docs commit 82048dc Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Mar 6 13:32:08 2026 +0700 cmd: imp commit 6af19f9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Mar 6 10:34:54 2026 +0700 proxy: imp docs commit a0d03fd Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Mar 6 09:16:23 2026 +0700 cmd: imp code commit cc82e1f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Mar 5 12:33:46 2026 +0700 proxy: imp http conf commit cde8d76 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Mar 5 12:20:53 2026 +0700 all: imp code commit 57547f3 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Mar 5 12:05:37 2026 +0700 proxy: doh routes commit 1b23b33 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Mar 4 13:29:15 2026 +0700 proxy: use http conf commit 60963a9 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Mar 3 12:51:38 2026 +0700 cmd: doh conf commit 4620723 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Mar 3 12:29:09 2026 +0700 proxy: http conf
1 parent b4607cf commit b7d4b05

File tree

12 files changed

+262
-129
lines changed

12 files changed

+262
-129
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ Usage of ./dnsproxy:
6363
Cache size (in bytes). Default: 64k.
6464
--config-path=path
6565
YAML configuration file. Minimal working configuration in config.yaml.dist. Options passed through command line will override the ones from this file.
66+
--doh-insecure-enabled
67+
If specified, the DoH server will skip TLS certificate verification.
68+
--doh-routes
69+
Routes for DNS-over-HTTPS. If not specified, the default routes are registered:
70+
- "GET /", deprecated and will soon be removed,
71+
- "POST /", deprecated and will soon be removed.
72+
- "GET /dns-query",
73+
- "POST /dns-query".
6674
--dns64
6775
If specified, dnsproxy will act as a DNS64 server.
6876
--dns64-prefix=subnet

internal/cmd/args.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ const (
6767
pendingRequestsEnabledIdx
6868
dns64Idx
6969
usePrivateRDNSIdx
70+
dohRoutesIdx
71+
dohInsecureEnabledIdx
7072
)
7173

7274
// commandLineOption contains information about a command-line option: its long
@@ -405,6 +407,18 @@ var commandLineOptions = []*commandLineOption{
405407
short: "",
406408
valueType: "",
407409
},
410+
dohRoutesIdx: {
411+
description: "List of routes for DNS-over-HTTPS, can be specified multiple times.",
412+
long: "doh-routes",
413+
short: "",
414+
valueType: "route",
415+
},
416+
dohInsecureEnabledIdx: {
417+
description: "If specified, the DoH server will skip TLS certificate verification.",
418+
long: "doh-insecure-enabled",
419+
short: "",
420+
valueType: "",
421+
},
408422
}
409423

410424
// parseCmdLineOptions parses the command-line options. conf must not be nil.
@@ -464,6 +478,8 @@ func parseCmdLineOptions(conf *configuration) (err error) {
464478
pendingRequestsEnabledIdx: &conf.PendingRequestsEnabled,
465479
dns64Idx: &conf.DNS64,
466480
usePrivateRDNSIdx: &conf.UsePrivateRDNS,
481+
dohRoutesIdx: &conf.DoHRoutes,
482+
dohInsecureEnabledIdx: &conf.DoHInsecureEnabled,
467483
} {
468484
addOption(flags, fieldPtr, commandLineOptions[i])
469485
}
@@ -536,8 +552,8 @@ func addOption(flags *flag.FlagSet, fieldPtr any, o *commandLineOption) {
536552
defineFlagVar(flags, (*uint32Value)(fieldPtr), o)
537553
case *float32:
538554
defineFlagVar(flags, (*float32Value)(fieldPtr), o)
539-
case *[]int:
540-
defineFlagVar(flags, newIntSliceValue(fieldPtr), o)
555+
case *[]uint16:
556+
defineFlagVar(flags, newUInt16SliceValue(fieldPtr), o)
541557
case *[]string:
542558
defineFlagVar(flags, newStringSliceValue(fieldPtr), o)
543559
case *timeutil.Duration:

internal/cmd/config.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,25 @@ type configuration struct {
4646
// ListenAddrs is the list of server's listen addresses.
4747
ListenAddrs []string `yaml:"listen-addrs"`
4848

49-
// ListenPorts are the ports server listens on.
50-
ListenPorts []int `yaml:"listen-ports"`
49+
// DoHRoutes is the list of routes for DNS-over-HTTPS. It must be a slice
50+
// of valid route patterns, if it is empty, the default routes are
51+
// registered.
52+
DoHRoutes []string `yaml:"doh-routes"`
53+
54+
// ListenPorts are the ports server listens on for plain DNS.
55+
ListenPorts []uint16 `yaml:"listen-ports"`
5156

5257
// HTTPSListenPorts are the ports server listens on for DNS-over-HTTPS.
53-
HTTPSListenPorts []int `yaml:"https-port"`
58+
HTTPSListenPorts []uint16 `yaml:"https-port"`
5459

5560
// TLSListenPorts are the ports server listens on for DNS-over-TLS.
56-
TLSListenPorts []int `yaml:"tls-port"`
61+
TLSListenPorts []uint16 `yaml:"tls-port"`
5762

5863
// QUICListenPorts are the ports server listens on for DNS-over-QUIC.
59-
QUICListenPorts []int `yaml:"quic-port"`
64+
QUICListenPorts []uint16 `yaml:"quic-port"`
6065

6166
// DNSCryptListenPorts are the ports server listens on for DNSCrypt.
62-
DNSCryptListenPorts []int `yaml:"dnscrypt-port"`
67+
DNSCryptListenPorts []uint16 `yaml:"dnscrypt-port"`
6368

6469
// Upstreams is the list of DNS upstream servers.
6570
Upstreams []string `yaml:"upstream"`
@@ -146,6 +151,10 @@ type configuration struct {
146151
// TODO(d.kolyshev): Use more suitable type.
147152
TLSMaxVersion float32 `yaml:"tls-max-version"`
148153

154+
// DoHInsecureEnabled controls whether the DoH server should skip TLS
155+
// certificate verification.
156+
DoHInsecureEnabled bool `yaml:"doh-insecure-enabled"`
157+
149158
// help, if true, prints the command-line option help message and quit with
150159
// a successful exit-code.
151160
help bool

internal/cmd/flag.go

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,47 +47,48 @@ func (i *float32Value) String() (out string) {
4747
return strconv.FormatFloat(float64(*i), 'f', 3, 32)
4848
}
4949

50-
// intSliceValue represent a struct with a slice of integers that can be defined
51-
// as a flag for [flag.FlagSet].
52-
type intSliceValue struct {
53-
// values is the pointer to a slice of integers to store parsed values.
54-
values *[]int
50+
// uint16SliceValue represent a struct with a slice of uint16 values that can be
51+
// defined as a flag for [flag.FlagSet].
52+
type uint16SliceValue struct {
53+
// values is the pointer to a slice of uint16 to store parsed values.
54+
values *[]uint16
5555

5656
// isSet is false until the corresponding flag is met for the first time.
5757
// When the flag is found, the default value is overwritten with zero value.
5858
isSet bool
5959
}
6060

61-
// newIntSliceValue returns a pointer to intSliceValue with the given value.
62-
func newIntSliceValue(p *[]int) (out *intSliceValue) {
63-
return &intSliceValue{
61+
// newUInt16SliceValue returns a pointer to uint16SliceValue with the given
62+
// value.
63+
func newUInt16SliceValue(p *[]uint16) (out *uint16SliceValue) {
64+
return &uint16SliceValue{
6465
values: p,
6566
isSet: false,
6667
}
6768
}
6869

6970
// type check
70-
var _ flag.Value = (*intSliceValue)(nil)
71+
var _ flag.Value = (*uint16SliceValue)(nil)
7172

72-
// Set implements the [flag.Value] interface for *intSliceValue.
73-
func (i *intSliceValue) Set(s string) (err error) {
74-
v, err := strconv.Atoi(s)
73+
// Set implements the [flag.Value] interface for *uint16SliceValue.
74+
func (i *uint16SliceValue) Set(s string) (err error) {
75+
v, err := strconv.ParseUint(s, 10, 16)
7576
if err != nil {
76-
return fmt.Errorf("parsing integer slice arg %q: %w", s, err)
77+
return fmt.Errorf("parsing uint16 slice arg %q: %w", s, err)
7778
}
7879

7980
if !i.isSet {
8081
i.isSet = true
81-
*i.values = []int{}
82+
*i.values = []uint16{}
8283
}
8384

84-
*i.values = append(*i.values, v)
85+
*i.values = append(*i.values, uint16(v))
8586

8687
return nil
8788
}
8889

89-
// String implements the [flag.Value] interface for *intSliceValue.
90-
func (i *intSliceValue) String() (out string) {
90+
// String implements the [flag.Value] interface for *uint16SliceValue.
91+
func (i *uint16SliceValue) String() (out string) {
9192
if i == nil || i.values == nil {
9293
return ""
9394
}
@@ -98,7 +99,7 @@ func (i *intSliceValue) String() (out string) {
9899
stringutil.WriteToBuilder(sb, ",")
99100
}
100101

101-
stringutil.WriteToBuilder(sb, strconv.Itoa(v))
102+
stringutil.WriteToBuilder(sb, strconv.FormatUint(uint64(v), 10))
102103
}
103104

104105
return sb.String()

internal/cmd/proxy.go

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ import (
2626
"gopkg.in/yaml.v3"
2727
)
2828

29+
// defaultHTTPTimeout is the default timeout for HTTP server operations.
30+
//
31+
// TODO(a.garipov): Consider making configurable.
32+
const defaultHTTPTimeout = 10 * time.Second
33+
2934
// TODO(e.burkov): Use a separate type for the YAML configuration file.
3035

3136
// createProxyConfig initializes [proxy.Config]. l must not be nil.
@@ -58,6 +63,24 @@ func createProxyConfig(
5863
return nil, fmt.Errorf("ratelimit mw: %w", err)
5964
}
6065

66+
httpConf := &proxy.HTTPConfig{
67+
ServerHeader: conf.HTTPSServerName,
68+
Routes: conf.DoHRoutes,
69+
ReadTimeout: defaultHTTPTimeout,
70+
WriteTimeout: defaultHTTPTimeout,
71+
HTTP3Enabled: conf.HTTP3,
72+
InsecureEnabled: conf.DoHInsecureEnabled,
73+
}
74+
75+
if uiStr := conf.HTTPSUserinfo; uiStr != "" {
76+
user, pass, ok := strings.Cut(uiStr, ":")
77+
if ok {
78+
httpConf.Userinfo = url.UserPassword(user, pass)
79+
} else {
80+
httpConf.Userinfo = url.User(user)
81+
}
82+
}
83+
6184
proxyConf = &proxy.Config{
6285
Logger: l.With(slogutil.KeyPrefix, proxy.LogPrefix),
6386
CacheEnabled: conf.Cache,
@@ -68,7 +91,6 @@ func createProxyConfig(
6891
CacheOptimisticMaxAge: time.Duration(conf.OptimisticMaxAge),
6992
CacheOptimistic: conf.CacheOptimistic,
7093
RefuseAny: conf.RefuseAny,
71-
HTTP3: conf.HTTP3,
7294
// TODO(e.burkov): The following CIDRs are aimed to match any address.
7395
// This is not quite proper approach to be used by default so think
7496
// about configuring it.
@@ -78,23 +100,14 @@ func createProxyConfig(
78100
},
79101
EnableEDNSClientSubnet: conf.EnableEDNSSubnet,
80102
UDPBufferSize: conf.UDPBufferSize,
81-
HTTPSServerName: conf.HTTPSServerName,
82103
MaxGoroutines: conf.MaxGoRoutines,
83104
UsePrivateRDNS: conf.UsePrivateRDNS,
84105
PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
85106
RequestHandler: ratelimitMw.Wrap(preMw.Wrap(proxy.DefaultHandler{})),
86107
PendingRequests: &proxy.PendingRequestsConfig{
87108
Enabled: conf.PendingRequestsEnabled,
88109
},
89-
}
90-
91-
if uiStr := conf.HTTPSUserinfo; uiStr != "" {
92-
user, pass, ok := strings.Cut(uiStr, ":")
93-
if ok {
94-
proxyConf.Userinfo = url.UserPassword(user, pass)
95-
} else {
96-
proxyConf.Userinfo = url.User(user)
97-
}
110+
HTTPConfig: httpConf,
98111
}
99112

100113
conf.initBogusNXDomain(ctx, l, proxyConf)
@@ -391,60 +404,58 @@ func (conf *configuration) initListenAddrs(config *proxy.Config) (err error) {
391404
if len(conf.ListenPorts) == 0 {
392405
// If ListenPorts has not been parsed through config file nor command
393406
// line we set it to 53.
394-
conf.ListenPorts = []int{53}
407+
conf.ListenPorts = []uint16{53}
395408
}
396409

397410
for _, port := range conf.ListenPorts {
398411
for _, ip := range addrs {
399-
addrPort := netip.AddrPortFrom(ip, uint16(port))
412+
addrPort := netip.AddrPortFrom(ip, port)
400413

401414
config.UDPListenAddr = append(config.UDPListenAddr, net.UDPAddrFromAddrPort(addrPort))
402415
config.TCPListenAddr = append(config.TCPListenAddr, net.TCPAddrFromAddrPort(addrPort))
403416
}
404417
}
405418

406-
initTLSListenAddrs(config, conf, addrs)
407-
initDNSCryptListenAddrs(config, conf, addrs)
419+
if config.TLSConfig != nil {
420+
initTLSListenAddrs(config, conf, addrs)
421+
}
422+
423+
if config.DNSCryptResolverCert != nil && config.DNSCryptProviderName != "" {
424+
initDNSCryptListenAddrs(config, conf, addrs)
425+
}
408426

409427
return nil
410428
}
411429

412-
// initTLSListenAddrs sets up proxy configuration TLS listen addresses.
430+
// initTLSListenAddrs sets up proxy configuration TLS listen addresses. conf,
431+
// proxyConf must not be nil. If conf.HTTPSListenPorts is not empty,
432+
// proxyConf.HTTPConfig must not be nil.
413433
func initTLSListenAddrs(proxyConf *proxy.Config, conf *configuration, addrs []netip.Addr) {
414-
if proxyConf.TLSConfig == nil {
415-
return
416-
}
417-
434+
httpConfig := proxyConf.HTTPConfig
418435
for _, ip := range addrs {
419436
for _, port := range conf.TLSListenPorts {
420-
a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port)))
437+
a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, port))
421438
proxyConf.TLSListenAddr = append(proxyConf.TLSListenAddr, a)
422439
}
423440

424441
for _, port := range conf.HTTPSListenPorts {
425-
a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port)))
426-
proxyConf.HTTPSListenAddr = append(proxyConf.HTTPSListenAddr, a)
442+
a := netip.AddrPortFrom(ip, port)
443+
httpConfig.ListenAddresses = append(httpConfig.ListenAddresses, a)
427444
}
428445

429446
for _, port := range conf.QUICListenPorts {
430-
a := net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port)))
447+
a := net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip, port))
431448
proxyConf.QUICListenAddr = append(proxyConf.QUICListenAddr, a)
432449
}
433450
}
434451
}
435452

436453
// initDNSCryptListenAddrs sets up proxy configuration DNSCrypt listen
437-
// addresses.
454+
// addresses. proxyConf and conf must not be nil.
438455
func initDNSCryptListenAddrs(proxyConf *proxy.Config, conf *configuration, addrs []netip.Addr) {
439-
if proxyConf.DNSCryptResolverCert == nil || proxyConf.DNSCryptProviderName == "" {
440-
return
441-
}
442-
443456
for _, port := range conf.DNSCryptListenPorts {
444-
p := uint16(port)
445-
446457
for _, ip := range addrs {
447-
addrPort := netip.AddrPortFrom(ip, p)
458+
addrPort := netip.AddrPortFrom(ip, port)
448459

449460
tcp := net.TCPAddrFromAddrPort(addrPort)
450461
proxyConf.DNSCryptTCPListenAddr = append(proxyConf.DNSCryptTCPListenAddr, tcp)

0 commit comments

Comments
 (0)