Skip to content

Commit d2736e5

Browse files
committed
Pull request 421: AGDNS-3523-add-ratelimit
Merge in GO/dnsproxy from AGDNS-3523-add-ratelimit to master Squashed commit of the following: commit 5d51eb1 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Feb 13 12:39:20 2026 +0700 handler: rename to middleware commit d281b05 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Feb 13 12:35:08 2026 +0700 handler: migrate middleware commit bcb660f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Feb 12 10:55:26 2026 +0700 proxy: imp code commit 53fc44c Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Feb 11 13:13:37 2026 +0700 ratelimit: imp tests commit ea3dd77 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Feb 11 12:56:26 2026 +0700 ratelimit: imp code commit 4460e5e Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Feb 11 12:41:27 2026 +0700 all: imp commit 5411a3f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Feb 10 10:20:16 2026 +0700 all: ratelimit package
1 parent 0647504 commit d2736e5

22 files changed

Lines changed: 512 additions & 136 deletions

internal/cmd/proxy.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"time"
1414

1515
"github.com/AdguardTeam/dnsproxy/internal/dnsmsg"
16-
"github.com/AdguardTeam/dnsproxy/internal/handler"
16+
"github.com/AdguardTeam/dnsproxy/internal/middleware"
1717
proxynetutil "github.com/AdguardTeam/dnsproxy/internal/netutil"
1818
"github.com/AdguardTeam/dnsproxy/proxy"
1919
"github.com/AdguardTeam/dnsproxy/upstream"
@@ -39,13 +39,13 @@ func createProxyConfig(
3939
return nil, err
4040
}
4141

42-
hosts, err := handler.ReadHosts(ctx, l, hostsFiles)
42+
hosts, err := middleware.ReadHosts(ctx, l, hostsFiles)
4343
if err != nil {
4444
return nil, fmt.Errorf("reading hosts files: %w", err)
4545
}
4646

47-
reqHdlr := handler.NewDefault(&handler.DefaultConfig{
48-
Logger: l.With(slogutil.KeyPrefix, "default_handler"),
47+
preMw := middleware.New(&middleware.Config{
48+
Logger: l.With(slogutil.KeyPrefix, "pre_handler_mw"),
4949
// TODO(e.burkov): Use the configured message constructor.
5050
MessageConstructor: dnsmsg.DefaultMessageConstructor{},
5151
HaltIPv6: conf.IPv6Disabled,
@@ -81,7 +81,7 @@ func createProxyConfig(
8181
MaxGoroutines: conf.MaxGoRoutines,
8282
UsePrivateRDNS: conf.UsePrivateRDNS,
8383
PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
84-
RequestHandler: reqHdlr,
84+
RequestHandler: preMw.Wrap(proxy.DefaultHandler{}),
8585
PendingRequests: &proxy.PendingRequestsConfig{
8686
Enabled: conf.PendingRequestsEnabled,
8787
},

internal/handler/default.go

Lines changed: 0 additions & 74 deletions
This file was deleted.

internal/handler/handler.go

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package handler
1+
package middleware
22

33
import (
44
"net/netip"
Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package handler
1+
package middleware
22

33
import (
44
"context"
@@ -91,43 +91,46 @@ func readHostsFile(ctx context.Context, strg *hostsfile.DefaultStorage, path str
9191

9292
// resolveFromHosts resolves the DNS query from the hosts file. It fills the
9393
// response with the A, AAAA, and PTR records from the hosts file.
94-
func (h *Default) resolveFromHosts(ctx context.Context, req *dns.Msg) (resp *dns.Msg) {
94+
func (mw *Default) resolveFromHosts(
95+
ctx context.Context,
96+
req *dns.Msg,
97+
) (resp *dns.Msg) {
9598
var addrs []netip.Addr
9699
var ptrs []string
97100

98101
q := req.Question[0]
99102
name := strings.TrimSuffix(q.Name, ".")
100103
switch q.Qtype {
101104
case dns.TypeA:
102-
addrs = slices.Clone(h.hosts.ByName(name))
105+
addrs = slices.Clone(mw.hosts.ByName(name))
103106
addrs = slices.DeleteFunc(addrs, netip.Addr.Is6)
104107
case dns.TypeAAAA:
105-
addrs = slices.Clone(h.hosts.ByName(name))
108+
addrs = slices.Clone(mw.hosts.ByName(name))
106109
addrs = slices.DeleteFunc(addrs, netip.Addr.Is4)
107110
case dns.TypePTR:
108111
addr, err := netutil.IPFromReversedAddr(name)
109112
if err != nil {
110-
h.logger.DebugContext(ctx, "failed parsing ptr", slogutil.KeyError, err)
113+
mw.logger.DebugContext(ctx, "failed parsing ptr", slogutil.KeyError, err)
111114

112115
return nil
113116
}
114117

115-
ptrs = h.hosts.ByAddr(addr)
118+
ptrs = mw.hosts.ByAddr(addr)
116119
default:
117120
return nil
118121
}
119122

120123
switch {
121124
case len(addrs) > 0:
122-
resp = h.messages.NewIPResponse(req, addrs)
125+
resp = mw.messages.NewIPResponse(req, addrs)
123126
case len(ptrs) > 0:
124-
resp = h.messages.NewCompressedResponse(req, dns.RcodeSuccess)
127+
resp = mw.messages.NewCompressedResponse(req, dns.RcodeSuccess)
125128
name = req.Question[0].Name
126129
for _, ptr := range ptrs {
127-
resp.Answer = append(resp.Answer, h.messages.NewPTRAnswer(name, dns.Fqdn(ptr)))
130+
resp.Answer = append(resp.Answer, mw.messages.NewPTRAnswer(name, dns.Fqdn(ptr)))
128131
}
129132
default:
130-
h.logger.DebugContext(ctx, "no hosts records found", "name", name, "qtype", q.Qtype)
133+
mw.logger.DebugContext(ctx, "no hosts records found", "name", name, "qtype", q.Qtype)
131134
}
132135

133136
return resp
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package handler
1+
package middleware
22

33
import (
44
"context"
@@ -8,15 +8,15 @@ import (
88

99
// haltAAAA halts the processing of AAAA requests if IPv6 is disabled. req must
1010
// not be nil.
11-
func (h *Default) haltAAAA(ctx context.Context, req *dns.Msg) (resp *dns.Msg) {
12-
if h.isIPv6Halted && req.Question[0].Qtype == dns.TypeAAAA {
13-
h.logger.DebugContext(
11+
func (mw *Default) haltAAAA(ctx context.Context, req *dns.Msg) (resp *dns.Msg) {
12+
if mw.haltIPv6 && req.Question[0].Qtype == dns.TypeAAAA {
13+
mw.logger.DebugContext(
1414
ctx,
1515
"ipv6 is disabled; replying with empty response",
1616
"req", req.Question[0].Name,
1717
)
1818

19-
return h.messages.NewMsgNODATA(req)
19+
return mw.messages.NewMsgNODATA(req)
2020
}
2121

2222
return nil

internal/middleware/middleware.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Package middleware provides some customizable DNS request handling logic used
2+
// in the proxy.
3+
package middleware
4+
5+
import (
6+
"context"
7+
"log/slog"
8+
9+
"github.com/AdguardTeam/dnsproxy/proxy"
10+
"github.com/AdguardTeam/golibs/hostsfile"
11+
)
12+
13+
// Config is the configuration for [Default].
14+
type Config struct {
15+
// HostsFiles is the index containing the records of the hosts files. It
16+
// must not be nil.
17+
HostsFiles hostsfile.Storage
18+
19+
// Logger is the logger. It must not be nil.
20+
Logger *slog.Logger
21+
22+
// MessageConstructor constructs DNS messages. It must not be nil.
23+
MessageConstructor proxy.MessageConstructor
24+
25+
// HaltIPv6 halts the processing of AAAA requests and makes the handler
26+
// reply with NODATA to them, if true.
27+
HaltIPv6 bool
28+
}
29+
30+
// Default implements [proxy.Middleware] with default DNS request handling
31+
// logic.
32+
type Default struct {
33+
hosts hostsfile.Storage
34+
logger *slog.Logger
35+
messages messageConstructor
36+
haltIPv6 bool
37+
}
38+
39+
// New creates a new [*Default].
40+
func New(conf *Config) (mw *Default) {
41+
mc, ok := conf.MessageConstructor.(messageConstructor)
42+
if !ok {
43+
mc = defaultConstructor{
44+
MessageConstructor: conf.MessageConstructor,
45+
}
46+
}
47+
48+
return &Default{
49+
hosts: conf.HostsFiles,
50+
logger: conf.Logger,
51+
messages: mc,
52+
haltIPv6: conf.HaltIPv6,
53+
}
54+
}
55+
56+
// type check
57+
var _ proxy.Middleware = (*Default)(nil)
58+
59+
// Wrap implements the [proxy.Middleware] interface for *Default. It validates
60+
// and resolves the DNS request within proxyCtx. It only calls h if the request
61+
// isn't handled by any of the internal handlers.
62+
func (mw *Default) Wrap(h proxy.Handler) (wrapped proxy.Handler) {
63+
f := func(p *proxy.Proxy, proxyCtx *proxy.DNSContext) (err error) {
64+
ctx := context.TODO()
65+
66+
mw.logger.DebugContext(ctx, "handling request", "req", &proxyCtx.Req.Question[0])
67+
68+
if proxyCtx.Res = mw.haltAAAA(ctx, proxyCtx.Req); proxyCtx.Res != nil {
69+
return nil
70+
}
71+
72+
if proxyCtx.Res = mw.resolveFromHosts(ctx, proxyCtx.Req); proxyCtx.Res != nil {
73+
return nil
74+
}
75+
76+
return h.ServeDNS(p, proxyCtx)
77+
}
78+
79+
return proxy.HandlerFunc(f)
80+
}

internal/handler/default_internal_test.go renamed to internal/middleware/middleware_internal_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package handler
1+
package middleware
22

33
import (
44
"net"
@@ -52,23 +52,23 @@ func TestDefault_haltAAAA(t *testing.T) {
5252
t.Run("disabled", func(t *testing.T) {
5353
t.Parallel()
5454

55-
hdlr := NewDefault(&DefaultConfig{
56-
Logger: slogutil.NewDiscardLogger(),
55+
mw := New(&Config{
56+
Logger: testLogger,
5757
MessageConstructor: messages,
5858
HaltIPv6: false,
5959
})
6060

6161
ctx := testutil.ContextWithTimeout(t, defaultTimeout)
6262

63-
assert.Nil(t, hdlr.haltAAAA(ctx, reqA))
64-
assert.Nil(t, hdlr.haltAAAA(ctx, reqAAAA))
63+
assert.Nil(t, mw.haltAAAA(ctx, reqA))
64+
assert.Nil(t, mw.haltAAAA(ctx, reqAAAA))
6565
})
6666

6767
t.Run("enabled", func(t *testing.T) {
6868
t.Parallel()
6969

70-
hdlr := NewDefault(&DefaultConfig{
71-
Logger: slogutil.NewDiscardLogger(),
70+
hdlr := New(&Config{
71+
Logger: testLogger,
7272
MessageConstructor: messages,
7373
HaltIPv6: true,
7474
})
@@ -94,9 +94,9 @@ func TestDefault_resolveFromHosts(t *testing.T) {
9494
strg, err := ReadHosts(ctx, testLogger, []string{absPath, relPath})
9595
require.NoError(t, err)
9696

97-
hdlr := NewDefault(&DefaultConfig{
97+
hdlr := New(&Config{
9898
MessageConstructor: messages,
99-
Logger: slogutil.NewDiscardLogger(),
99+
Logger: testLogger,
100100
HostsFiles: strg,
101101
HaltIPv6: true,
102102
})

internal/handler/testdata/TestDefault_resolveFromHosts/hosts renamed to internal/middleware/testdata/TestDefault_resolveFromHosts/hosts

File renamed without changes.

proxy/config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ type Config struct {
6161
BeforeRequestHandler BeforeRequestHandler
6262

6363
// RequestHandler is an optional custom handler for DNS requests. It's used
64-
// instead of DefaultRequestHandler if set.
65-
RequestHandler RequestHandler
64+
// instead of DefaultHandler if set. In case of [ErrDrop] error returned
65+
// from this handler, the proxy will not send any response to the client.
66+
RequestHandler Handler
6667

6768
// UpstreamConfig is a general set of DNS servers to forward requests to.
6869
UpstreamConfig *UpstreamConfig

0 commit comments

Comments
 (0)