Skip to content

Commit f661fdc

Browse files
committed
Pull request 300: AG-27616-ratelimit-whitelist-netip-addr
Squashed commit of the following: commit 6b8f86f Merge: 8cc3b79 280cca3 Author: Stanislav Chzhen <[email protected]> Date: Tue Nov 28 18:59:53 2023 +0300 Merge branch 'master' into AG-27616-ratelimit-whitelist-netip-addr commit 8cc3b79 Author: Stanislav Chzhen <[email protected]> Date: Fri Nov 24 16:17:48 2023 +0300 all: imp logs commit 53b095f Author: Stanislav Chzhen <[email protected]> Date: Thu Nov 23 15:46:08 2023 +0300 proxy: imp err msg commit d5d5d8f Author: Stanislav Chzhen <[email protected]> Date: Wed Nov 22 17:08:45 2023 +0300 proxy: imp docs commit f2d1c21 Author: Stanislav Chzhen <[email protected]> Date: Tue Nov 21 19:57:33 2023 +0300 proxy: imp code commit 5deb3ec Author: Stanislav Chzhen <[email protected]> Date: Tue Nov 21 19:26:45 2023 +0300 all: imp code commit 5f36d90 Author: Stanislav Chzhen <[email protected]> Date: Tue Nov 21 17:49:54 2023 +0300 all: add todo commit e63df13 Author: Stanislav Chzhen <[email protected]> Date: Mon Nov 20 20:34:14 2023 +0300 all: dnscontext addr netip addrport commit b6588c0 Author: Stanislav Chzhen <[email protected]> Date: Thu Nov 16 17:46:29 2023 +0300 proxy: ratelimit whitelsit netip addr
1 parent 280cca3 commit f661fdc

17 files changed

+219
-206
lines changed

Diff for: internal/netutil/udp.go

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ func UDPSetOptions(c *net.UDPConn) (err error) {
1616
// UDPRead reads the message from conn using buf and receives a control-message
1717
// payload of size udpOOBSize from it. It returns the number of bytes copied
1818
// into buf and the source address of the message.
19+
//
20+
// TODO(s.chzhen): Consider using netip.Addr.
1921
func UDPRead(
2022
conn *net.UDPConn,
2123
buf []byte,
@@ -25,6 +27,8 @@ func UDPRead(
2527
}
2628

2729
// UDPWrite writes the data to the remoteAddr using conn.
30+
//
31+
// TODO(s.chzhen): Consider using netip.Addr.
2832
func UDPWrite(
2933
data []byte,
3034
conn *net.UDPConn,

Diff for: main.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/AdguardTeam/dnsproxy/upstream"
2424
"github.com/AdguardTeam/golibs/log"
2525
"github.com/AdguardTeam/golibs/mathutil"
26-
"github.com/AdguardTeam/golibs/netutil"
2726
"github.com/AdguardTeam/golibs/timeutil"
2827
"github.com/ameshkov/dnscrypt/v2"
2928
goFlags "github.com/jessevdk/go-flags"
@@ -153,7 +152,7 @@ type Options struct {
153152

154153
// RatelimitSubnetLenIPv6 is a subnet length for IPv6 addresses used for
155154
// rate limiting requests
156-
RatelimitSubnetLenIPv6 int `yaml:"ratelimit-subnet-len-ipv6" long:"ratelimit-subnet-len-ipv6" description:"Ratelimit subnet length for IPv6." default:"64"`
155+
RatelimitSubnetLenIPv6 int `yaml:"ratelimit-subnet-len-ipv6" long:"ratelimit-subnet-len-ipv6" description:"Ratelimit subnet length for IPv6." default:"56"`
157156

158157
// If true, refuse ANY requests
159158
RefuseAny bool `yaml:"refuse-any" long:"refuse-any" description:"If specified, refuse ANY requests" optional:"yes" optional-value:"true"`
@@ -336,8 +335,8 @@ func runPprof(options *Options) {
336335
// createProxyConfig creates proxy.Config from the command line arguments
337336
func createProxyConfig(options *Options) (conf proxy.Config) {
338337
conf = proxy.Config{
339-
RatelimitSubnetMaskIPv4: net.CIDRMask(options.RatelimitSubnetLenIPv4, netutil.IPv4BitLen),
340-
RatelimitSubnetMaskIPv6: net.CIDRMask(options.RatelimitSubnetLenIPv6, netutil.IPv6BitLen),
338+
RatelimitSubnetLenIPv4: options.RatelimitSubnetLenIPv4,
339+
RatelimitSubnetLenIPv6: options.RatelimitSubnetLenIPv6,
341340

342341
Ratelimit: options.Ratelimit,
343342
CacheEnabled: options.Cache,

Diff for: proxy/cache_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package proxy
22

33
import (
44
"net"
5+
"net/netip"
56
"strings"
67
"sync"
78
"testing"
@@ -324,7 +325,7 @@ func TestCacheExpirationWithTTLOverride(t *testing.T) {
324325

325326
t.Run("replace_min", func(t *testing.T) {
326327
d.Req = createHostTestMessage("host")
327-
d.Addr = &net.TCPAddr{}
328+
d.Addr = netip.AddrPort{}
328329

329330
u.ans = []dns.RR{&dns.A{
330331
Hdr: dns.RR_Header{
@@ -348,7 +349,7 @@ func TestCacheExpirationWithTTLOverride(t *testing.T) {
348349

349350
t.Run("replace_max", func(t *testing.T) {
350351
d.Req = createHostTestMessage("host2")
351-
d.Addr = &net.TCPAddr{}
352+
d.Addr = netip.AddrPort{}
352353

353354
u.ans = []dns.RR{&dns.A{
354355
Hdr: dns.RR_Header{

Diff for: proxy/config.go

+33-23
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,26 @@ type Config struct {
6565

6666
// Rate-limiting and anti-DNS amplification measures
6767
// --
68+
//
69+
// TODO(s.chzhen): Extract ratelimit settings to a separate structure.
6870

69-
// RatelimitSubnetMaskIPv4 is a subnet mask for IPv4 addresses used for
71+
// RatelimitSubnetLenIPv4 is a subnet length for IPv4 addresses used for
7072
// rate limiting requests.
71-
RatelimitSubnetMaskIPv4 net.IPMask
73+
RatelimitSubnetLenIPv4 int
7274

73-
// RatelimitSubnetMaskIPv6 is a subnet mask for IPv6 addresses used for
75+
// RatelimitSubnetLenIPv6 is a subnet length for IPv6 addresses used for
7476
// rate limiting requests.
75-
RatelimitSubnetMaskIPv6 net.IPMask
77+
RatelimitSubnetLenIPv6 int
78+
79+
// Ratelimit is a maximum number of requests per second from a given IP (0
80+
// to disable).
81+
Ratelimit int
82+
83+
// RatelimitWhitelist is a list of IP addresses excluded from rate limiting.
84+
RatelimitWhitelist []netip.Addr
7685

77-
Ratelimit int // max number of requests per second from a given IP (0 to disable)
78-
RatelimitWhitelist []string // a list of whitelisted client IP addresses
79-
RefuseAny bool // if true, refuse ANY requests
86+
// RefuseAny makes proxy refuse the requests of type ANY.
87+
RefuseAny bool
8088

8189
// TrustedProxies is the list of IP addresses and CIDR networks to
8290
// detect proxy servers addresses the DoH requests from which should be
@@ -240,22 +248,27 @@ func (p *Proxy) validateRatelimit() (err error) {
240248
return nil
241249
}
242250

243-
if p.RatelimitSubnetMaskIPv4 == nil {
244-
return errors.Error("ipv4 subnet mask is nil")
251+
err = checkInclusion(p.RatelimitSubnetLenIPv4, 0, netutil.IPv4BitLen)
252+
if err != nil {
253+
return fmt.Errorf("ratelimit subnet len ipv4 is invalid: %w", err)
245254
}
246255

247-
_, bits := p.RatelimitSubnetMaskIPv4.Size()
248-
if bits != netutil.IPv4BitLen {
249-
return fmt.Errorf("ipv4 subnet mask must contain %d bits, got %d", netutil.IPv4BitLen, bits)
256+
err = checkInclusion(p.RatelimitSubnetLenIPv6, 0, netutil.IPv6BitLen)
257+
if err != nil {
258+
return fmt.Errorf("ratelimit subnet len ipv6 is invalid: %w", err)
250259
}
251260

252-
if p.RatelimitSubnetMaskIPv6 == nil {
253-
return errors.Error("ipv6 subnet is nil")
254-
}
261+
return nil
262+
}
255263

256-
_, bits = p.RatelimitSubnetMaskIPv6.Size()
257-
if bits != netutil.IPv6BitLen {
258-
return fmt.Errorf("ipv6 subnet mask must contain %d bits, got %d", netutil.IPv6BitLen, bits)
264+
// checkInclusion returns an error if a n is not in the inclusive range between
265+
// minN and maxN.
266+
func checkInclusion(n, minN, maxN int) (err error) {
267+
switch {
268+
case n < minN:
269+
return fmt.Errorf("value %d less than min %d", n, minN)
270+
case n > maxN:
271+
return fmt.Errorf("value %d greater than max %d", n, maxN)
259272
}
260273

261274
return nil
@@ -268,14 +281,11 @@ func (p *Proxy) logConfigInfo() {
268281
}
269282

270283
if p.Ratelimit > 0 {
271-
sizeV4, _ := p.RatelimitSubnetMaskIPv4.Size()
272-
sizeV6, _ := p.RatelimitSubnetMaskIPv6.Size()
273-
274284
log.Info(
275285
"Ratelimit is enabled and set to %d rps, IPv4 subnet mask len %d, IPv6 subnet mask len %d",
276286
p.Ratelimit,
277-
sizeV4,
278-
sizeV6,
287+
p.RatelimitSubnetLenIPv4,
288+
p.RatelimitSubnetLenIPv6,
279289
)
280290
}
281291

Diff for: proxy/dns64_test.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,7 @@ func TestProxy_Resolve_dns64(t *testing.T) {
156156
require.NoError(t, err)
157157
ptrGlobDomain = dns.Fqdn(ptrGlobDomain)
158158

159-
cliIP := &net.TCPAddr{
160-
IP: net.IP{192, 168, 1, 1},
161-
Port: 1234,
162-
}
159+
cliAddrPort := netip.MustParseAddrPort("192.168.1.1:1234")
163160

164161
const (
165162
sectionAnswer = iota
@@ -354,7 +351,7 @@ func TestProxy_Resolve_dns64(t *testing.T) {
354351
req := (&dns.Msg{}).SetQuestion(tc.qname, tc.qtype)
355352
dctx := &DNSContext{
356353
Req: req,
357-
Addr: cliIP,
354+
Addr: cliAddrPort,
358355
}
359356

360357
err = p.Resolve(dctx)

Diff for: proxy/dnscontext.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package proxy
33
import (
44
"net"
55
"net/http"
6+
"net/netip"
67
"time"
78

89
"github.com/AdguardTeam/dnsproxy/upstream"
@@ -27,9 +28,7 @@ type DNSContext struct {
2728
QUICStream quic.Stream
2829

2930
// Addr is the address of the client.
30-
//
31-
// TODO(s.chzhen): Use [netip.AddrPort].
32-
Addr net.Addr
31+
Addr netip.AddrPort
3332

3433
// Upstream is the upstream that resolved the request. In case of cached
3534
// response it's nil.

Diff for: proxy/proxy.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
gocache "github.com/patrickmn/go-cache"
2525
"github.com/quic-go/quic-go"
2626
"github.com/quic-go/quic-go/http3"
27+
"golang.org/x/exp/slices"
2728
)
2829

2930
const (
@@ -183,6 +184,7 @@ type Proxy struct {
183184

184185
// Init populates fields of p but does not start listeners.
185186
func (p *Proxy) Init() (err error) {
187+
// TODO(s.chzhen): Consider moving to [Proxy.validateConfig].
186188
err = p.validateBasicAuth()
187189
if err != nil {
188190
return fmt.Errorf("basic auth: %w", err)
@@ -233,6 +235,9 @@ func (p *Proxy) Init() (err error) {
233235
return fmt.Errorf("setting up DNS64: %w", err)
234236
}
235237

238+
p.RatelimitWhitelist = slices.Clone(p.RatelimitWhitelist)
239+
slices.SortFunc(p.RatelimitWhitelist, netip.Addr.Compare)
240+
236241
return nil
237242
}
238243

@@ -512,10 +517,9 @@ func (p *Proxy) selectUpstreams(d *DNSContext) (upstreams []upstream.Upstream) {
512517
return nil
513518
}
514519

515-
ip, _ := netutil.IPAndPortFromAddr(d.Addr)
516520
// TODO(e.burkov): Detect against the actual configured subnet set.
517521
// Perhaps, even much earlier.
518-
if !netutil.IsLocallyServed(ip) {
522+
if !netutil.IsLocallyServedAddr(d.Addr.Addr()) {
519523
return nil
520524
}
521525

@@ -708,10 +712,7 @@ func (dctx *DNSContext) processECS(cliIP net.IP) {
708712

709713
// Set ECS.
710714
if cliIP == nil {
711-
cliIP, _ = netutil.IPAndPortFromAddr(dctx.Addr)
712-
if cliIP == nil {
713-
return
714-
}
715+
cliIP = dctx.Addr.Addr().AsSlice()
715716
}
716717

717718
if !netutil.IsSpecialPurpose(cliIP) {

Diff for: proxy/proxy_test.go

+13-17
Original file line numberDiff line numberDiff line change
@@ -852,10 +852,8 @@ func TestProxy_ReplyFromUpstream_badResponse(t *testing.T) {
852852
0,
853853
false,
854854
),
855-
Req: createHostTestMessage("host"),
856-
Addr: &net.TCPAddr{
857-
IP: net.IP{1, 2, 3, 0},
858-
},
855+
Req: createHostTestMessage("host"),
856+
Addr: netip.MustParseAddrPort("1.2.3.0:1234"),
859857
}
860858

861859
var err error
@@ -893,7 +891,7 @@ func TestExchangeCustomUpstreamConfig(t *testing.T) {
893891
false,
894892
),
895893
Req: createHostTestMessage("host"),
896-
Addr: &net.TCPAddr{IP: net.IP{1, 2, 3, 0}},
894+
Addr: netip.MustParseAddrPort("1.2.3.0:1234"),
897895
}
898896

899897
err = prx.Resolve(&d)
@@ -945,7 +943,7 @@ func TestExchangeCustomUpstreamConfigCache(t *testing.T) {
945943
d := DNSContext{
946944
CustomUpstreamConfig: customUpstreamConfig,
947945
Req: createHostTestMessage("host"),
948-
Addr: &net.TCPAddr{IP: net.IP{1, 2, 3, 0}},
946+
Addr: netip.MustParseAddrPort("1.2.3.0:1234"),
949947
}
950948

951949
err = prx.Resolve(&d)
@@ -1036,7 +1034,7 @@ func TestECSProxy(t *testing.T) {
10361034
t.Run("cache_subnet", func(t *testing.T) {
10371035
d := DNSContext{
10381036
Req: createHostTestMessage("host"),
1039-
Addr: &net.TCPAddr{IP: ip1230},
1037+
Addr: netip.MustParseAddrPort("1.2.3.0:1234"),
10401038
}
10411039

10421040
err = prx.Resolve(&d)
@@ -1049,7 +1047,7 @@ func TestECSProxy(t *testing.T) {
10491047
t.Run("serve_subnet_cache", func(t *testing.T) {
10501048
d := DNSContext{
10511049
Req: createHostTestMessage("host"),
1052-
Addr: &net.TCPAddr{IP: net.IP{1, 2, 3, 1}},
1050+
Addr: netip.MustParseAddrPort("1.2.3.1:1234"),
10531051
}
10541052
u.ans, u.ecsIP, u.ecsReqIP = nil, nil, nil
10551053

@@ -1063,7 +1061,7 @@ func TestECSProxy(t *testing.T) {
10631061
t.Run("another_subnet", func(t *testing.T) {
10641062
d := DNSContext{
10651063
Req: createHostTestMessage("host"),
1066-
Addr: &net.TCPAddr{IP: ip2230},
1064+
Addr: netip.MustParseAddrPort("2.2.3.0:1234"),
10671065
}
10681066
u.ans = []dns.RR{&dns.A{
10691067
Hdr: dns.RR_Header{Rrtype: dns.TypeA, Name: "host.", Ttl: 60},
@@ -1081,7 +1079,7 @@ func TestECSProxy(t *testing.T) {
10811079
t.Run("cache_general", func(t *testing.T) {
10821080
d := DNSContext{
10831081
Req: createHostTestMessage("host"),
1084-
Addr: &net.TCPAddr{IP: net.IP{127, 0, 0, 1}},
1082+
Addr: netip.MustParseAddrPort("127.0.0.1:1234"),
10851083
}
10861084
u.ans = []dns.RR{&dns.A{
10871085
Hdr: dns.RR_Header{Rrtype: dns.TypeA, Name: "host.", Ttl: 60},
@@ -1099,7 +1097,7 @@ func TestECSProxy(t *testing.T) {
10991097
t.Run("serve_general_cache", func(t *testing.T) {
11001098
d := DNSContext{
11011099
Req: createHostTestMessage("host"),
1102-
Addr: &net.TCPAddr{IP: net.IP{127, 0, 0, 2}},
1100+
Addr: netip.MustParseAddrPort("127.0.0.2:1234"),
11031101
}
11041102
u.ans, u.ecsIP, u.ecsReqIP = nil, nil, nil
11051103

@@ -1138,7 +1136,7 @@ func TestECSProxyCacheMinMaxTTL(t *testing.T) {
11381136
// first request
11391137
d := DNSContext{
11401138
Req: createHostTestMessage("host"),
1141-
Addr: &net.TCPAddr{IP: clientIP},
1139+
Addr: netip.MustParseAddrPort("1.2.3.0:1234"),
11421140
}
11431141
err = prx.Resolve(&d)
11441142
require.NoError(t, err)
@@ -1156,9 +1154,7 @@ func TestECSProxyCacheMinMaxTTL(t *testing.T) {
11561154
// 2nd request
11571155
clientIP = net.IP{1, 2, 4, 0}
11581156
d.Req = createHostTestMessage("host")
1159-
d.Addr = &net.TCPAddr{
1160-
IP: clientIP,
1161-
}
1157+
d.Addr = netip.MustParseAddrPort("1.2.4.0:1234")
11621158
u.ans = []dns.RR{&dns.A{
11631159
Hdr: dns.RR_Header{
11641160
Rrtype: dns.TypeA,
@@ -1242,8 +1238,8 @@ func createTestProxy(t *testing.T, tlsConfig *tls.Config) *Proxy {
12421238

12431239
p.TrustedProxies = []string{"0.0.0.0/0", "::0/0"}
12441240

1245-
p.RatelimitSubnetMaskIPv4 = net.CIDRMask(24, 32)
1246-
p.RatelimitSubnetMaskIPv6 = net.CIDRMask(64, 128)
1241+
p.RatelimitSubnetLenIPv4 = 24
1242+
p.RatelimitSubnetLenIPv6 = 64
12471243

12481244
return &p
12491245
}

0 commit comments

Comments
 (0)