From fae0e83dc628df9c8ba3810222b46f16cb69989d Mon Sep 17 00:00:00 2001 From: "ondrej.benkovsky" Date: Mon, 28 Oct 2024 12:36:32 +0100 Subject: [PATCH] use configuration options for constructing client --- .gitignore | 3 ++ README.md | 20 ++++++------- doq/client.go | 39 +++++++++---------------- doq/client_test.go | 9 +++--- doq/opts.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 40 deletions(-) create mode 100644 doq/opts.go diff --git a/.gitignore b/.gitignore index 66fd13c..6e94476 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +*.iml +.idea diff --git a/README.md b/README.md index 4427646..e8b27cb 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,18 @@ go get github.com/tantalor93/doq-go ## Examples ``` -// create new DoQ Client -client, err := doq.NewClient("dns.adguard-dns.com:853", doq.Options{}) -if err != nil { - panic(err) -} +// create client with default settings resolving via AdGuard DoQ Server +client := doq.NewClient("dns.adguard-dns.com:853") -// create new query +// prepare payload q := dns.Msg{} q.SetQuestion("www.google.com.", dns.TypeA) -// send query -var resp *dns.Msg -resp, err = client.Send(context.Background(), &q) -fmt.Println(resp ,err) +// send DNS query +r, err := client.Send(context.Background(), &q) +if err != nil { + panic(err) +} +// do something with response +fmt.Println(dns.RcodeToString[r.Rcode]) ``` diff --git a/doq/client.go b/doq/client.go index 57f49dc..b296eee 100644 --- a/doq/client.go +++ b/doq/client.go @@ -19,40 +19,27 @@ type Client struct { conn quic.Connection addr string - tlsconfig *tls.Config + tlsConfig *tls.Config writeTimeout time.Duration readTimeout time.Duration connectTimeout time.Duration } -// Options encapsulates configuration options for doq.Client. -// By default, WriteTimeout, ReadTimeout and ConnectTimeout is zero, meaning there is no timeout. -type Options struct { - TLSConfig *tls.Config - WriteTimeout time.Duration - ReadTimeout time.Duration - ConnectTimeout time.Duration -} - // NewClient creates a new doq.Client used for sending DoQ queries. -func NewClient(addr string, options Options) *Client { - client := Client{} - - client.addr = addr - - if options.TLSConfig == nil { - client.tlsconfig = &tls.Config{MinVersion: tls.VersionTLS12} - } else { - client.tlsconfig = options.TLSConfig.Clone() +func NewClient(addr string, opts ...Option) *Client { + client := &Client{ + addr: addr, + tlsConfig: &tls.Config{MinVersion: tls.VersionTLS12}, + } + for _, opt := range opts { + opt.apply(client) } - // override protocol negotiation to DoQ, all the other stuff (like certificates, cert pools, insecure skip) is up to the user of library - client.tlsconfig.NextProtos = []string{"doq"} - client.readTimeout = options.ReadTimeout - client.writeTimeout = options.WriteTimeout - client.connectTimeout = options.ConnectTimeout + // override protocol negotiation to DoQ, all the other stuff (like certificates, cert pools, insecure skip) + // is up to the user of library + client.tlsConfig.NextProtos = []string{"doq"} - return &client + return client } func (c *Client) dial(ctx context.Context) error { @@ -76,7 +63,7 @@ func (c *Client) dial(ctx context.Context) error { done := make(chan interface{}) go func() { - conn, err := quic.DialAddrEarly(connectCtx, c.addr, c.tlsconfig, nil) + conn, err := quic.DialAddrEarly(connectCtx, c.addr, c.tlsConfig, nil) if err != nil { done <- err return diff --git a/doq/client_test.go b/doq/client_test.go index 966373a..9de5157 100644 --- a/doq/client_test.go +++ b/doq/client_test.go @@ -1,4 +1,4 @@ -package doq +package doq_test import ( "context" @@ -15,6 +15,7 @@ import ( "github.com/quic-go/quic-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tantalor93/doq-go/doq" ) type doqServer struct { @@ -85,7 +86,7 @@ func Test(t *testing.T) { server.start() defer server.stop() - client := NewClient(server.addr, Options{TLSConfig: generateTLSConfig()}) + client := doq.NewClient(server.addr, doq.WithTLSConfig(generateTLSConfig())) msg := dns.Msg{} msg.SetQuestion("example.org.", dns.TypeA) @@ -102,7 +103,7 @@ func TestWriteTimeout(t *testing.T) { server.start() defer server.stop() - client := NewClient(server.addr, Options{TLSConfig: generateTLSConfig(), WriteTimeout: 1 * time.Nanosecond}) + client := doq.NewClient(server.addr, doq.WithTLSConfig(generateTLSConfig()), doq.WithWriteTimeout(time.Nanosecond)) msg := dns.Msg{} msg.SetQuestion("example.org.", dns.TypeA) @@ -117,7 +118,7 @@ func TestReadTimeout(t *testing.T) { server.start() defer server.stop() - client := NewClient(server.addr, Options{TLSConfig: generateTLSConfig(), ReadTimeout: 1 * time.Nanosecond}) + client := doq.NewClient(server.addr, doq.WithTLSConfig(generateTLSConfig()), doq.WithReadTimeout(time.Nanosecond)) msg := dns.Msg{} msg.SetQuestion("example.org.", dns.TypeA) diff --git a/doq/opts.go b/doq/opts.go new file mode 100644 index 0000000..eb1ced9 --- /dev/null +++ b/doq/opts.go @@ -0,0 +1,71 @@ +package doq + +import ( + "crypto/tls" + "time" +) + +// Option represents configuration options for doq.Client. +type Option interface { + apply(c *Client) +} + +type tlsConfigOption struct { + tlsConfig *tls.Config +} + +func (o *tlsConfigOption) apply(c *Client) { + c.tlsConfig = o.tlsConfig.Clone() +} + +// WithTLSConfig is a configuration option that sets TLS configuration for the doq.Client. +func WithTLSConfig(c *tls.Config) Option { + return &tlsConfigOption{ + tlsConfig: c, + } +} + +type writeTimeoutOption struct { + writeTimeout time.Duration +} + +func (o *writeTimeoutOption) apply(c *Client) { + c.writeTimeout = o.writeTimeout +} + +// WithWriteTimeout is a configuration option that sets write timeout for the doq.Client. +func WithWriteTimeout(t time.Duration) Option { + return &writeTimeoutOption{ + writeTimeout: t, + } +} + +type readTimeoutOption struct { + readTimeout time.Duration +} + +func (o *readTimeoutOption) apply(c *Client) { + c.readTimeout = o.readTimeout +} + +// WithReadTimeout is a configuration option that sets read timeout for the doq.Client. +func WithReadTimeout(t time.Duration) Option { + return &readTimeoutOption{ + readTimeout: t, + } +} + +type connectTimeoutOption struct { + connectTimeout time.Duration +} + +func (o *connectTimeoutOption) apply(c *Client) { + c.connectTimeout = o.connectTimeout +} + +// WithConnectTimeout is a configuration option that sets connect timeout for the doq.Client. +func WithConnectTimeout(t time.Duration) Option { + return &connectTimeoutOption{ + connectTimeout: t, + } +}