Implement caching dns resolver

This commit is contained in:
9seconds
2021-03-30 14:14:37 +03:00
parent 841a4d2227
commit 10b78322a3
16 changed files with 163 additions and 47 deletions
-1
View File
@@ -5,7 +5,6 @@ import "github.com/9seconds/mtg/v2/mtglib"
type noop struct{} type noop struct{}
func (n noop) SeenBefore(_ []byte) bool { return false } func (n noop) SeenBefore(_ []byte) bool { return false }
func (n noop) Shutdown() {}
func NewNoop() mtglib.AntiReplayCache { func NewNoop() mtglib.AntiReplayCache {
return noop{} return noop{}
-2
View File
@@ -18,8 +18,6 @@ func (suite *NoopTestSuite) TestOp() {
suite.False(filter.SeenBefore([]byte{4, 5, 6})) suite.False(filter.SeenBefore([]byte{4, 5, 6}))
suite.False(filter.SeenBefore([]byte{1, 2, 3})) suite.False(filter.SeenBefore([]byte{1, 2, 3}))
suite.False(filter.SeenBefore([]byte{4, 5, 6})) suite.False(filter.SeenBefore([]byte{4, 5, 6}))
filter.Shutdown()
} }
func TestNoop(t *testing.T) { func TestNoop(t *testing.T) {
-2
View File
@@ -20,8 +20,6 @@ func (s *stableBloomFilter) SeenBefore(digest []byte) bool {
return s.filter.TestAndAdd(digest) return s.filter.TestAndAdd(digest)
} }
func (s *stableBloomFilter) Shutdown() {}
func NewStableBloomFilter(byteSize uint, errorRate float64) mtglib.AntiReplayCache { func NewStableBloomFilter(byteSize uint, errorRate float64) mtglib.AntiReplayCache {
sf := boom.NewDefaultStableBloomFilter(byteSize*8, errorRate) // nolint: gomnd sf := boom.NewDefaultStableBloomFilter(byteSize*8, errorRate) // nolint: gomnd
sf.SetHash(xxhash.New64()) sf.SetHash(xxhash.New64())
-2
View File
@@ -18,8 +18,6 @@ func (suite *StableBloomFilterTestSuite) TestOp() {
suite.False(filter.SeenBefore([]byte{4, 5, 6})) suite.False(filter.SeenBefore([]byte{4, 5, 6}))
suite.True(filter.SeenBefore([]byte{1, 2, 3})) suite.True(filter.SeenBefore([]byte{1, 2, 3}))
suite.True(filter.SeenBefore([]byte{4, 5, 6})) suite.True(filter.SeenBefore([]byte{4, 5, 6}))
filter.Shutdown()
} }
func TestStableBloomFilter(t *testing.T) { func TestStableBloomFilter(t *testing.T) {
-6
View File
@@ -59,12 +59,6 @@ func (c *Proxy) Execute() error { // nolint: funlen
PreferIP: c.Config.PreferIP.Value(mtglib.DefaultPreferIP), PreferIP: c.Config.PreferIP.Value(mtglib.DefaultPreferIP),
} }
defer func() {
opts.AntiReplayCache.Shutdown()
opts.IPBlocklist.Shutdown()
opts.EventStream.Shutdown()
}()
if opts.Concurrency == 0 { if opts.Concurrency == 0 {
opts.Concurrency = mtglib.DefaultConcurrency opts.Concurrency = mtglib.DefaultConcurrency
} }
+5 -5
View File
@@ -9,13 +9,13 @@ import (
"github.com/OneOfOne/xxhash" "github.com/OneOfOne/xxhash"
) )
type eventStream struct { type EventStream struct {
ctx context.Context ctx context.Context
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
chans []chan mtglib.Event chans []chan mtglib.Event
} }
func (e eventStream) Send(ctx context.Context, evt mtglib.Event) { func (e EventStream) Send(ctx context.Context, evt mtglib.Event) {
var chanNo uint32 var chanNo uint32
if streamID := evt.StreamID(); streamID != "" { if streamID := evt.StreamID(); streamID != "" {
@@ -31,17 +31,17 @@ func (e eventStream) Send(ctx context.Context, evt mtglib.Event) {
} }
} }
func (e eventStream) Shutdown() { func (e EventStream) Shutdown() {
e.ctxCancel() e.ctxCancel()
} }
func NewEventStream(observerFactories []ObserverFactory) mtglib.EventStream { func NewEventStream(observerFactories []ObserverFactory) EventStream {
if len(observerFactories) == 0 { if len(observerFactories) == 0 {
observerFactories = append(observerFactories, NewNoopObserver) observerFactories = append(observerFactories, NewNoopObserver)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
rv := eventStream{ rv := EventStream{
ctx: ctx, ctx: ctx,
ctxCancel: cancel, ctxCancel: cancel,
chans: make([]chan mtglib.Event, runtime.NumCPU()), chans: make([]chan mtglib.Event, runtime.NumCPU()),
+1 -1
View File
@@ -19,7 +19,7 @@ type EventStreamTestSuite struct {
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
observerMock1 *ObserverMock observerMock1 *ObserverMock
observerMock2 *ObserverMock observerMock2 *ObserverMock
stream mtglib.EventStream stream events.EventStream
} }
func (suite *EventStreamTestSuite) SetupTest() { func (suite *EventStreamTestSuite) SetupTest() {
-1
View File
@@ -9,7 +9,6 @@ import (
type noop struct{} type noop struct{}
func (n noop) Send(ctx context.Context, evt mtglib.Event) {} func (n noop) Send(ctx context.Context, evt mtglib.Event) {}
func (n noop) Shutdown() {}
func NewNoopStream() mtglib.EventStream { func NewNoopStream() mtglib.EventStream {
return noop{} return noop{}
-2
View File
@@ -70,8 +70,6 @@ func (suite *NoopTestSuite) TestStream() {
stream.Send(suite.ctx, value) stream.Send(suite.ctx, value)
}) })
} }
stream.Shutdown()
} }
func (suite *NoopTestSuite) TestObserver() { func (suite *NoopTestSuite) TestObserver() {
+1
View File
@@ -9,6 +9,7 @@ require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
github.com/d4l3k/messagediff v1.2.1 // indirect github.com/d4l3k/messagediff v1.2.1 // indirect
github.com/dgraph-io/ristretto v0.0.3 // indirect
github.com/jarcoal/httpmock v1.0.8 github.com/jarcoal/httpmock v1.0.8
github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1 github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1
github.com/libp2p/go-reuseport v0.0.2 github.com/libp2p/go-reuseport v0.0.2
+7
View File
@@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -38,6 +39,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
@@ -55,7 +58,10 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
@@ -277,6 +283,7 @@ github.com/smira/go-statsd v1.3.2 h1:1EeuzxNZ/TD9apbTOFSM9nulqfcsQFmT4u1A2DREabI
github.com/smira/go-statsd v1.3.2/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4= github.com/smira/go-statsd v1.3.2/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
-1
View File
@@ -9,7 +9,6 @@ import (
type noop struct{} type noop struct{}
func (n noop) Contains(ip net.IP) bool { return false } func (n noop) Contains(ip net.IP) bool { return false }
func (n noop) Shutdown() {}
func NewNoop() mtglib.IPBlocklist { func NewNoop() mtglib.IPBlocklist {
return noop{} return noop{}
-3
View File
@@ -35,12 +35,10 @@ type Network interface {
type AntiReplayCache interface { type AntiReplayCache interface {
SeenBefore(data []byte) bool SeenBefore(data []byte) bool
Shutdown()
} }
type IPBlocklist interface { type IPBlocklist interface {
Contains(net.IP) bool Contains(net.IP) bool
Shutdown()
} }
type Event interface { type Event interface {
@@ -50,7 +48,6 @@ type Event interface {
type EventStream interface { type EventStream interface {
Send(context.Context, Event) Send(context.Context, Event)
Shutdown()
} }
type TimeAttackDetector interface { type TimeAttackDetector interface {
+88
View File
@@ -0,0 +1,88 @@
package network
import (
"net/http"
"time"
doh "github.com/babolivier/go-doh-client"
"github.com/dgraph-io/ristretto"
)
const (
dnsResolverSize = 1024 * 1024 // 1mb
dnsResolverKeepTime = 10 * time.Minute
)
type dnsResolver struct {
resolver doh.Resolver
cache *ristretto.Cache
}
func (d dnsResolver) LookupA(hostname string) []string {
key := "\x00." + hostname
if value, ok := d.cache.Get(key); ok {
return value.([]string)
}
var ips []string
if recs, _, err := d.resolver.LookupA(hostname); err == nil {
for _, v := range recs {
ips = append(ips, v.IP4)
}
d.cache.SetWithTTL(key, ips, 0, dnsResolverKeepTime)
}
return ips
}
func (d dnsResolver) LookupAAAA(hostname string) []string {
key := "\x01." + hostname
if value, ok := d.cache.Get(key); ok {
return value.([]string)
}
var ips []string
if recs, _, err := d.resolver.LookupAAAA(hostname); err == nil {
for _, v := range recs {
ips = append(ips, v.IP6)
}
d.cache.SetWithTTL(key, ips, 0, dnsResolverKeepTime)
}
return ips
}
func newDNSResolver(hostname string, httpClient *http.Client) dnsResolver {
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 10 * dnsResolverSize, // nolint: gomnd // taken from official doc as a best practice value
MaxCost: dnsResolverSize,
BufferItems: 64, // nolint: gomnd // taken from official doc as a best practice value
Cost: func(value interface{}) int64 {
var cost int64
for _, v := range value.([]string) {
cost += int64(len([]byte(v)))
}
return cost
},
})
if err != nil {
panic(err)
}
return dnsResolver{
resolver: doh.Resolver{
Host: hostname,
Class: doh.IN,
HTTPClient: httpClient,
},
cache: cache,
}
}
+50
View File
@@ -0,0 +1,50 @@
package network
import (
"net"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/suite"
)
type DNSResolverTestSuite struct {
suite.Suite
d dnsResolver
}
func (suite *DNSResolverTestSuite) TestLookupA() {
suite.d.LookupA("google.com")
time.Sleep(10 * time.Millisecond)
addrs := suite.d.LookupA("google.com")
for _, v := range addrs {
suite.NotEmpty(v)
suite.NotNil(net.ParseIP(v).To4())
}
}
func (suite *DNSResolverTestSuite) TestLookupAAAA() {
suite.d.LookupAAAA("google.com")
time.Sleep(10 * time.Millisecond)
addrs := suite.d.LookupAAAA("google.com")
for _, v := range addrs {
suite.NotEmpty(v)
suite.Nil(net.ParseIP(v).To4())
suite.NotNil(net.ParseIP(v).To16())
}
}
func (suite *DNSResolverTestSuite) SetupTest() {
suite.d = newDNSResolver("1.1.1.1", &http.Client{})
}
func TestDNSResolver(t *testing.T) {
t.Parallel()
suite.Run(t, &DNSResolverTestSuite{})
}
+11 -21
View File
@@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/mtglib"
doh "github.com/babolivier/go-doh-client"
) )
type networkHTTPTransport struct { type networkHTTPTransport struct {
@@ -26,7 +25,7 @@ func (n networkHTTPTransport) RoundTrip(req *http.Request) (*http.Response, erro
type network struct { type network struct {
dialer Dialer dialer Dialer
dns doh.Resolver dns dnsResolver
httpTimeout time.Duration httpTimeout time.Duration
userAgent string userAgent string
} }
@@ -84,14 +83,11 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) {
go func() { go func() {
defer wg.Done() defer wg.Done()
if recs, _, err := n.dns.LookupA(address); err == nil { resolved := n.dns.LookupA(address)
mutex.Lock()
defer mutex.Unlock()
for _, v := range recs { mutex.Lock()
ips = append(ips, v.IP4) ips = append(ips, resolved...)
} mutex.Unlock()
}
}() }()
} }
@@ -102,14 +98,11 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) {
go func() { go func() {
defer wg.Done() defer wg.Done()
if recs, _, err := n.dns.LookupAAAA(address); err == nil { resolved := n.dns.LookupAAAA(address)
mutex.Lock()
defer mutex.Unlock()
for _, v := range recs { mutex.Lock()
ips = append(ips, v.IP6) ips = append(ips, resolved...)
} mutex.Unlock()
}
}() }()
} }
@@ -140,11 +133,8 @@ func NewNetwork(dialer Dialer,
dialer: dialer, dialer: dialer,
httpTimeout: httpTimeout, httpTimeout: httpTimeout,
userAgent: userAgent, userAgent: userAgent,
dns: doh.Resolver{ dns: newDNSResolver(dohHostname,
Host: dohHostname, makeHTTPClient(userAgent, DNSTimeout, dialer.DialContext)),
Class: doh.IN,
HTTPClient: makeHTTPClient(userAgent, DNSTimeout, dialer.DialContext),
},
}, nil }, nil
} }