Skip to content

Commit

Permalink
Added an option with which you can specify file with hostnames to query
Browse files Browse the repository at this point in the history
  • Loading branch information
ameshkov committed Dec 3, 2024
1 parent c7ed36d commit ddeb9f0
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 48 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ adheres to [Semantic Versioning][semver].

## [Unreleased]

### Added

* Added `-f` / `--file` flag with which you can specify source file with
hostnames that will be queried.

[unreleased]: https://github.com/ameshkov/godnsbench/compare/v1.9.0...HEAD

## [1.9.0] - 2024-09-05
Expand Down
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,14 @@ Usage:
godnsbench [OPTIONS]
Application Options:
-a, --address= Address of the DNS server you're trying to test. Note, that
for encrypted DNS it should include the protocol (tls://,
-a, --address= Address of the DNS server you're trying to test. Note, that for encrypted DNS it should include the protocol (tls://,
https://, quic://, h3://)
-p, --parallel= The number of connections you would like to open
simultaneously (default: 1)
-q, --query= The host name you would like to resolve. {random} will be
replaced with a random string (default: example.org)
-p, --parallel= The number of connections you would like to open simultaneously (default: 1)
-q, --query= The host name you would like to resolve. {random} will be replaced with a random string (default: example.org)
-f, --file= The path to the file with domain names to query
-t, --timeout= Query timeout in seconds (default: 10)
-r, --rate-limit= Rate limit (per second) (default: 0)
-c, --count= The overall number of queries we should send (default:
10000)
-c, --count= The overall number of queries we should send (default: 10000)
--insecure Do not validate the server certificate
-v, --verbose Verbose output (optional)
-o, --output= Path to the log file. If not set, write to stdout.
Expand Down
26 changes: 12 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
module github.com/ameshkov/godnsbench

go 1.22.6

toolchain go1.23.0
go 1.23.3

require (
github.com/AdguardTeam/dnsproxy v0.73.1
github.com/AdguardTeam/golibs v0.27.0
github.com/AdguardTeam/dnsproxy v0.73.4
github.com/AdguardTeam/golibs v0.30.5
github.com/jessevdk/go-flags v1.6.1
github.com/miekg/dns v1.1.62
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
go.uber.org/ratelimit v0.3.1
)

Expand All @@ -30,14 +28,14 @@ require (
github.com/quic-go/qpack v0.5.0 // indirect
github.com/quic-go/quic-go v0.46.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/tools v0.27.0 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
44 changes: 22 additions & 22 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/AdguardTeam/dnsproxy v0.73.1 h1:4eMiaDm1NnowKltdx/LXLJ5FTTClBECPnLxtvJy3Q0Q=
github.com/AdguardTeam/dnsproxy v0.73.1/go.mod h1:ZcvmyQY2EiX5B0yCTkiYTgtm+1lBWA0lajbEI9dOhW4=
github.com/AdguardTeam/golibs v0.27.0 h1:YxCFK6HBGp/ZXp3bv5uei+oLH12UfIYB8u2rh1B6nnU=
github.com/AdguardTeam/golibs v0.27.0/go.mod h1:iWdjXPCwmK2g2FKIb/OwEPnovSXeMqRhI8FWLxF5oxE=
github.com/AdguardTeam/dnsproxy v0.73.4 h1:FTIXX34wQqePjtWUD1I4QfWTq2B2N1gfOW/TzZDdR4o=
github.com/AdguardTeam/dnsproxy v0.73.4/go.mod h1:18ssqhDgOCiVIwYmmVuXVM05wSwrzkO2yjKhVRWJX/g=
github.com/AdguardTeam/golibs v0.30.5 h1:xqat/N9o/V/AnakaWpqq+fGU/qJhKtL4A2pj66kC+TE=
github.com/AdguardTeam/golibs v0.30.5/go.mod h1:2wOvoAsubo/REnBiuu/YWYmkkzyFR52/QljMdQ2R58M=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
Expand Down Expand Up @@ -46,32 +46,32 @@ github.com/quic-go/qpack v0.5.0 h1:jldbr38Ef/swDfxtvNvvUIYNg5LNm3Oa9W+IZvCm4q0=
github.com/quic-go/qpack v0.5.0/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y=
github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
Expand Down
53 changes: 49 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"syscall"
"time"

"github.com/AdguardTeam/golibs/stringutil"

"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/log"
goFlags "github.com/jessevdk/go-flags"
Expand Down Expand Up @@ -42,6 +44,9 @@ type Options struct {
// Query is the host name you would like to resolve during the bench.
Query string `short:"q" long:"query" description:"The host name you would like to resolve. {random} will be replaced with a random string" default:"example.org"`

// QueriesPath is the path to the file with domain names to query.
QueriesPath string `short:"f" long:"file" description:"The path to the file with domain names to query"`

// Timeout is timeout for a query.
Timeout int `short:"t" long:"timeout" description:"Query timeout in seconds" default:"10"`

Expand Down Expand Up @@ -116,6 +121,11 @@ type runState struct {
errors int
// queriesToSend is the number of queries left to send.
queriesToSend int
// queriesSent is the number of queries sent.
queriesSent int

// hostnames is the list of hostnames to query.
hostnames []string

// lastPrintedState is the last time we printed the intermediate state.
// It is printed on every 100's query.
Expand All @@ -133,6 +143,7 @@ func (r *runState) qpsTotal() (q float64) {
defer r.m.Unlock()

e := r.elapsed()

return float64(r.processed+r.errors) / e.Seconds()
}

Expand All @@ -148,15 +159,26 @@ func (r *runState) elapsedPerQuery() (e time.Duration) {
if r.processed > 0 {
avgElapsed = elapsed / time.Duration(r.processed)
}

return avgElapsed
}

// nextHostname returns the next hostname to be queried.
func (r *runState) nextHostname() (h string) {
r.m.Lock()
defer r.m.Unlock()

return r.hostnames[r.queriesSent%len(r.hostnames)]
}

// incProcessed increments processed number, returns the new value.
func (r *runState) incProcessed() (p int) {
r.m.Lock()
defer r.m.Unlock()

r.processed++
r.printIntermediateResults()

return r.processed
}

Expand Down Expand Up @@ -187,16 +209,21 @@ func (r *runState) printIntermediateResults() {
func (r *runState) incErrors() (e int) {
r.m.Lock()
defer r.m.Unlock()

r.errors++
r.printIntermediateResults()

return r.errors
}

// decQueriesToSend decrements queriesToSend number, returns the new value.
func (r *runState) decQueriesToSend() (q int) {
r.m.Lock()
defer r.m.Unlock()

r.queriesToSend--
r.queriesSent++

return r.queriesToSend
}

Expand Down Expand Up @@ -234,10 +261,30 @@ func run(options *Options) (state *runState) {
rate = ratelimit.NewUnlimited()
}

var hostnames []string

if options.QueriesPath != "" {
log.Info("Reading hostnames from the file %s", options.QueriesPath)

b, err := os.ReadFile(options.QueriesPath)
if err != nil {
log.Fatalf("Failed to read from %s: %v", options.QueriesPath, err)
}

hostnames = stringutil.SplitTrimmed(string(b), "\n")
} else {
hostnames = []string{options.Query}
}

if len(hostnames) == 0 {
log.Fatalf("Empty list of hostnames in the file %s", options.QueriesPath)
}

state = &runState{
startTime: time.Now(),
queriesToSend: options.QueriesCount + 1,
rate: rate,
hostnames: hostnames,
}

// Subscribe to the bench run close event.
Expand Down Expand Up @@ -283,13 +330,11 @@ func runConnection(options *Options, state *runState) {
},
)

randomize := strings.Contains(options.Query, "{random}")

queriesToSend := state.decQueriesToSend()
for queriesToSend > 0 {
domainName := options.Query
domainName := state.nextHostname()

if randomize {
if strings.Contains(domainName, "{random}") {
domainName = strings.ReplaceAll(domainName, "{random}", randString(randomLen))
}

Expand Down
50 changes: 50 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"fmt"
"math/big"
"net"
"os"
"path"
"testing"
"time"

Expand Down Expand Up @@ -59,6 +61,54 @@ func Test_run(t *testing.T) {
require.Equal(t, 0, state.errors)
}

func Test_runWithQueriesFile(t *testing.T) {
str := `example.org
example.com
example.net`
filePath := path.Join(os.TempDir(), "queries.txt")
err := os.WriteFile(filePath, []byte(str), 0644)
require.NoError(t, err)

defer func() {
_ = os.Remove(filePath)
}()

tlsConfig, _ := createServerTLSConfig(t, "example.org")
p := createTestProxy(t, tlsConfig)

p.RequestHandler = func(_ *proxy.Proxy, d *proxy.DNSContext) (err error) {
resp := &dns.Msg{}
resp.SetReply(d.Req)
d.Res = resp

return nil
}

err = p.Start(context.Background())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) {
return p.Shutdown(context.Background())
})

addr := p.Addr(proxy.ProtoHTTPS)
serverAddress := fmt.Sprintf("https://%s/dns-query", addr)

o := &Options{
Address: serverAddress,
Connections: 1,
QueriesPath: filePath,
Timeout: 10,
Rate: 50,
QueriesCount: 100,
InsecureSkipVerify: true,
}

state := run(o)

require.Equal(t, o.QueriesCount, state.processed)
require.Equal(t, 0, state.errors)
}

// createTestProxy creates a test DNS proxy that listens to all protocols.
func createTestProxy(t *testing.T, tlsConfig *tls.Config) (p *proxy.Proxy) {
listenIP := "127.0.0.1"
Expand Down

0 comments on commit ddeb9f0

Please sign in to comment.