From df215fc7e1b2fc991325a91e5fac564fa8052013 Mon Sep 17 00:00:00 2001 From: 0ln <8427756+0ln@users.noreply.github.com> Date: Mon, 8 Dec 2025 06:11:57 +0100 Subject: [PATCH 1/4] Add basic implementation for DNS cookies support --- internal/cmd/args.go | 16 +++ internal/cmd/config.go | 7 ++ internal/cmd/proxy.go | 2 + proxy/config.go | 8 ++ proxy/cookie.go | 247 +++++++++++++++++++++++++++++++++++++++++ proxy/dnscontext.go | 4 + proxy/proxy.go | 15 +++ 7 files changed, 299 insertions(+) create mode 100644 proxy/cookie.go diff --git a/internal/cmd/args.go b/internal/cmd/args.go index c4984503e..54def396f 100644 --- a/internal/cmd/args.go +++ b/internal/cmd/args.go @@ -24,6 +24,8 @@ const ( httpsUserinfoIdx dnsCryptConfigPathIdx ednsAddrIdx + disableDNSCookiesIdx + dnsCookieSecretIdx upstreamModeIdx listenAddrsIdx listenPortsIdx @@ -132,6 +134,18 @@ var commandLineOptions = []*commandLineOption{ short: "", valueType: "address", }, + disableDNSCookiesIdx: { + description: "Disable EDNS Cookies support.", + long: "disable-dns-cookies", + short: "", + valueType: "", + }, + dnsCookieSecretIdx: { + description: "Hex-encoded 16-byte secret used to generate EDNS Cookies.", + long: "dns-cookie-secret", + short: "", + valueType: "hex", + }, upstreamModeIdx: { description: "Defines the upstreams logic mode, possible values: load_balance, parallel, " + "fastest_addr (default: load_balance).", @@ -421,6 +435,8 @@ func parseCmdLineOptions(conf *configuration) (err error) { httpsUserinfoIdx: &conf.HTTPSUserinfo, dnsCryptConfigPathIdx: &conf.DNSCryptConfigPath, ednsAddrIdx: &conf.EDNSAddr, + disableDNSCookiesIdx: &conf.DisableDNSCookies, + dnsCookieSecretIdx: &conf.DNSCookieSecret, upstreamModeIdx: &conf.UpstreamMode, listenAddrsIdx: &conf.ListenAddrs, listenPortsIdx: &conf.ListenPorts, diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 8a246c926..255e7e515 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -39,6 +39,13 @@ type configuration struct { // EDNSAddr is the custom EDNS Client Address to send. EDNSAddr string `yaml:"edns-addr"` + // DisableDNSCookies strips EDNS Cookies from both requests and responses. + DisableDNSCookies bool `yaml:"disable_dns_cookies"` + + // DNSCookieSecret is a hex-encoded 16-byte secret used to generate server + // cookies. + DNSCookieSecret string `yaml:"dns_cookie_secret"` + // UpstreamMode determines the logic through which upstreams will be used. // If not specified the [proxy.UpstreamModeLoadBalance] is used. UpstreamMode string `yaml:"upstream-mode"` diff --git a/internal/cmd/proxy.go b/internal/cmd/proxy.go index 9e39c8a3d..5d17cbdd4 100644 --- a/internal/cmd/proxy.go +++ b/internal/cmd/proxy.go @@ -75,6 +75,8 @@ func createProxyConfig( netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::0/0"), }, + DisableDNSCookies: conf.DisableDNSCookies, + DNSCookieSecret: conf.DNSCookieSecret, EnableEDNSClientSubnet: conf.EnableEDNSSubnet, UDPBufferSize: conf.UDPBufferSize, HTTPSServerName: conf.HTTPSServerName, diff --git a/proxy/config.go b/proxy/config.go index 2a8282eda..eaa179b29 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -175,6 +175,14 @@ type Config struct { // EDNSAddr is the ECS IP used in request. EDNSAddr net.IP + // DisableDNSCookies strips EDNS Cookies from both requests and responses. + // If false, the proxy responds with EDNS Cookies per RFC 7873 and RFC 9018. + DisableDNSCookies bool `yaml:"disable_dns_cookies" json:"disable_dns_cookies"` + + // DNSCookieSecret is a hex-encoded 16-byte secret used to generate server + // cookies. If empty, a random secret is generated on start. + DNSCookieSecret string `yaml:"dns_cookie_secret" json:"dns_cookie_secret"` + // TODO(s.chzhen): Extract ratelimit settings to a separate structure. // RatelimitSubnetLenIPv4 is a subnet length for IPv4 addresses used for diff --git a/proxy/cookie.go b/proxy/cookie.go new file mode 100644 index 000000000..6a19e92a8 --- /dev/null +++ b/proxy/cookie.go @@ -0,0 +1,247 @@ +package proxy + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "fmt" + "net/netip" + + "github.com/miekg/dns" +) + +const ( + // clientCookieLen is the length of the client cookie in bytes. + clientCookieLen = 8 + + // serverCookieLen is the length of the server cookie in bytes. + serverCookieLen = 16 + + // cookieSecretLen is the size of the secret used to generate server + // cookies. + cookieSecretLen = 16 + + // doBitMask is the mask of the DO bit in the TTL field of an OPT record. + doBitMask = uint32(1 << 15) +) + +// parseCookie returns the client and server cookies from m if present. The +// client cookie is required to be at least eight bytes long. +func parseCookie(m *dns.Msg) (client, server []byte) { + if m == nil { + return nil, nil + } + + opt := m.IsEdns0() + if opt == nil { + return nil, nil + } + + for _, o := range opt.Option { + c, ok := o.(*dns.EDNS0_COOKIE) + if !ok { + continue + } + + raw, err := hex.DecodeString(c.Cookie) + if err != nil || len(raw) < clientCookieLen { + continue + } + + client = raw[:clientCookieLen] + if len(raw) > clientCookieLen { + server = raw[clientCookieLen:] + } + + return client, server + } + + return nil, nil +} + +// stripCookie removes any EDNS cookie options from m. +func stripCookie(m *dns.Msg) { + if m == nil { + return + } + + opt := m.IsEdns0() + if opt == nil { + return + } + + opts := opt.Option[:0] + for _, o := range opt.Option { + if o.Option() == dns.EDNS0COOKIE { + continue + } + + opts = append(opts, o) + } + + opt.Option = opts +} + +// handleRequestCookies parses client cookies and strips cookie options before +// forwarding requests upstream. +func (p *Proxy) handleRequestCookies(dctx *DNSContext) { + if dctx == nil || dctx.Req == nil { + return + } + + if !p.DisableDNSCookies { + if client, _ := parseCookie(dctx.Req); len(client) > 0 { + dctx.ReqClientCookie = client + } + + stripCookie(dctx.Req) + + return + } + + stripCookie(dctx.Req) +} + +// handleResponseCookies sets the response cookie if needed or strips cookie +// options when cookies are disabled. +func (p *Proxy) handleResponseCookies(dctx *DNSContext) { + if dctx == nil || dctx.Res == nil { + return + } + + if p.DisableDNSCookies { + stripCookie(dctx.Res) + + return + } + + if len(dctx.ReqClientCookie) == 0 { + stripCookie(dctx.Res) + + return + } + + udpSize := dctx.udpSize + if udpSize == 0 { + udpSize = defaultUDPBufSize + } + + stripCookie(dctx.Res) + + server := p.serverCookie(dctx.Addr.Addr(), dctx.ReqClientCookie) + if len(server) == 0 { + return + } + + setCookie(dctx.Res, dctx.ReqClientCookie, server, udpSize, dctx.doBit) +} + +// serverCookie returns the server cookie for the provided address and client +// cookie using an HMAC-SHA256. +func (p *Proxy) serverCookie(ip netip.Addr, client []byte) (server []byte) { + if len(client) < clientCookieLen || !ip.IsValid() { + return nil + } + + p.cookieMu.RLock() + secret := p.cookieSecret + p.cookieMu.RUnlock() + + if len(secret) != cookieSecretLen { + return nil + } + + h := hmac.New(sha256.New, secret) + _, _ = h.Write(client) + _, _ = h.Write(ip.AsSlice()) + + // Truncate to sixteen bytes. + return h.Sum(nil)[:serverCookieLen] +} + +// setCookie ensures msg has the OPT record and sets the cookie option to +// client+server. udpSize is the UDP buffer size advertised to the client. +func setCookie(msg *dns.Msg, client, server []byte, udpSize uint16, do bool) { + if msg == nil || len(client) == 0 || len(server) == 0 { + return + } + + opt := msg.IsEdns0() + if opt == nil { + msg.SetEdns0(udpSize, do) + opt = msg.IsEdns0() + } else { + opt.SetUDPSize(udpSize) + if do { + opt.SetDo() + } else { + opt.Hdr.Ttl &^= doBitMask + } + } + + stripCookie(msg) + + cookie := make([]byte, 0, len(client)+len(server)) + cookie = append(cookie, client...) + cookie = append(cookie, server...) + + opt = msg.IsEdns0() + opt.Option = append(opt.Option, &dns.EDNS0_COOKIE{ + Code: dns.EDNS0COOKIE, + Cookie: hex.EncodeToString(cookie), + }) +} + +// decodeCookieSecret decodes hexSecret and validates its length. +func decodeCookieSecret(hexSecret string) (secret []byte, err error) { + secret, err = hex.DecodeString(hexSecret) + if err != nil { + return nil, fmt.Errorf("decoding dns cookie secret: %w", err) + } + + if len(secret) != cookieSecretLen { + return nil, fmt.Errorf( + "decoding dns cookie secret: invalid length %d, want %d", + len(secret), + cookieSecretLen, + ) + } + + return secret, nil +} + +// generateCookieSecret returns a new random secret suitable for cookie +// generation. +func generateCookieSecret() (secret []byte, err error) { + secret = make([]byte, cookieSecretLen) + _, err = rand.Read(secret) + if err != nil { + return nil, fmt.Errorf("reading random secret: %w", err) + } + + return secret, nil +} + +// initCookieSecret prepares the cookie secret depending on the configuration. +func (p *Proxy) initCookieSecret() (err error) { + if p.DisableDNSCookies { + return nil + } + + var secret []byte + if p.DNSCookieSecret != "" { + secret, err = decodeCookieSecret(p.DNSCookieSecret) + } else { + secret, err = generateCookieSecret() + } + if err != nil { + return fmt.Errorf("creating dns cookie secret: %w", err) + } + + p.cookieMu.Lock() + p.cookieSecret = secret + p.cookieMu.Unlock() + + return nil +} diff --git a/proxy/dnscontext.go b/proxy/dnscontext.go index bd3acbae3..a92da3cb2 100644 --- a/proxy/dnscontext.go +++ b/proxy/dnscontext.go @@ -96,6 +96,10 @@ type DNSContext struct { // doBit is the DNSSEC OK flag from request's EDNS0 RR if presented. doBit bool + + // ReqClientCookie is the client part of the EDNS Cookie option from the + // request, if any. + ReqClientCookie []byte } // newDNSContext returns a new properly initialized *DNSContext. diff --git a/proxy/proxy.go b/proxy/proxy.go index 44ca67ca2..ebf6aa75b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -179,6 +179,12 @@ type Proxy struct { // TODO(a.garipov): Remove this embed and create a proper initializer. Config + // cookieSecret is the secret used to generate EDNS cookies. + cookieSecret []byte + + // cookieMu protects cookieSecret. + cookieMu sync.RWMutex + // udpOOBSize is the size of the out-of-band data for UDP connections. udpOOBSize int @@ -282,6 +288,11 @@ func New(c *Config) (p *Proxy, err error) { return nil, fmt.Errorf("setting up DNS64: %w", err) } + err = p.initCookieSecret() + if err != nil { + return nil, fmt.Errorf("initializing dns cookies: %w", err) + } + // TODO(e.burkov): Clone all mutable fields of Config. p.RatelimitWhitelist = slices.Clone(p.RatelimitWhitelist) slices.SortFunc(p.RatelimitWhitelist, netip.Addr.Compare) @@ -703,6 +714,7 @@ func (p *Proxy) Resolve(dctx *DNSContext) (err error) { } dctx.calcFlagsAndSize() + p.handleRequestCookies(dctx) cacheWorks := p.cacheWorks(dctx) if cacheWorks { @@ -719,6 +731,7 @@ func (p *Proxy) Resolve(dctx *DNSContext) (err error) { if p.replyFromCache(dctx) { // Complete the response from cache. + p.handleResponseCookies(dctx) dctx.scrub() return nil @@ -748,6 +761,8 @@ func (p *Proxy) Resolve(dctx *DNSContext) (err error) { filterMsg(dctx.Res, dctx.Res, dctx.adBit, dctx.doBit, 0) } + p.handleResponseCookies(dctx) + // Complete the response. dctx.scrub() From 5db545e8d55100fcbd90451f3a3ec38d8e1620f7 Mon Sep 17 00:00:00 2001 From: 0ln <8427756+0ln@users.noreply.github.com> Date: Mon, 8 Dec 2025 07:19:09 +0100 Subject: [PATCH 2/4] Add basic tests for DNS cookies implementation --- proxy/cookie_test.go | 174 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 proxy/cookie_test.go diff --git a/proxy/cookie_test.go b/proxy/cookie_test.go new file mode 100644 index 000000000..c7e89dde8 --- /dev/null +++ b/proxy/cookie_test.go @@ -0,0 +1,174 @@ +package proxy + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "net/netip" + "sync" + "testing" + + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" +) + +// newMsgWithCookie returns a DNS message with a single question and a COOKIE +// option containing cc and sc concatenated. +func newMsgWithCookie(q dns.Question, cc, sc []byte, udpSize uint16, do bool) (m *dns.Msg) { + m = &dns.Msg{Question: []dns.Question{q}} + m.SetEdns0(udpSize, do) + + opt := m.IsEdns0() + opt.Option = append(opt.Option, &dns.EDNS0_COOKIE{ + Code: dns.EDNS0COOKIE, + Cookie: hex.EncodeToString(append(cc, sc...)), + }) + + return m +} + +func TestParseCookie(t *testing.T) { + q := dns.Question{Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET} + cc := []byte("12345678") + sc := []byte("server_cookie") + + m := newMsgWithCookie(q, cc, sc, 1232, false) + + gotCC, gotSC := parseCookie(m) + assert.Equal(t, cc, gotCC) + assert.Equal(t, sc, gotSC) + + t.Run("invalid_hex", func(t *testing.T) { + m := &dns.Msg{Question: []dns.Question{q}} + m.SetEdns0(defaultUDPBufSize, false) + opt := m.IsEdns0() + opt.Option = append(opt.Option, &dns.EDNS0_COOKIE{Code: dns.EDNS0COOKIE, Cookie: "zzzz"}) + + cc, sc := parseCookie(m) + assert.Nil(t, cc) + assert.Nil(t, sc) + }) + + t.Run("too_short", func(t *testing.T) { + m := newMsgWithCookie(q, []byte("short"), nil, defaultUDPBufSize, false) + cc, sc := parseCookie(m) + assert.Nil(t, cc) + assert.Nil(t, sc) + }) +} + +func TestStripCookie(t *testing.T) { + q := dns.Question{Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET} + cc := []byte("12345678") + + m := newMsgWithCookie(q, cc, nil, defaultUDPBufSize, false) + opt := m.IsEdns0() + opt.Option = append(opt.Option, &dns.EDNS0_LOCAL{Code: 65001, Data: []byte{1}}) + + stripCookie(m) + + opt = m.IsEdns0() + assert.Len(t, opt.Option, 1) + _, hasCookie := opt.Option[0].(*dns.EDNS0_COOKIE) + assert.False(t, hasCookie) +} + +func TestServerCookieDeterministic(t *testing.T) { + p := &Proxy{ + Config: Config{ + DisableDNSCookies: false, + DNSCookieSecret: "000102030405060708090a0b0c0d0e0f", + }, + cookieMu: sync.RWMutex{}, + } + err := p.initCookieSecret() + assert.NoError(t, err) + + client := []byte("12345678") + ip1 := netip.MustParseAddr("192.0.2.1") + ip2 := netip.MustParseAddr("192.0.2.2") + + c1 := p.serverCookie(ip1, client) + c2 := p.serverCookie(ip1, client) + assert.Equal(t, c1, c2, "same IP and client cookie must be stable") + + c3 := p.serverCookie(ip2, client) + assert.NotEqual(t, c1, c3, "different IP must yield different cookie") + + expected := hmac.New(sha256.New, p.cookieSecret) + _, _ = expected.Write(client) + _, _ = expected.Write(ip1.AsSlice()) + assert.Equal(t, expected.Sum(nil)[:serverCookieLen], c1) +} + +func TestHandleCookiesEnabled(t *testing.T) { + secretHex := "000102030405060708090a0b0c0d0e0f" + p := &Proxy{ + Config: Config{ + DisableDNSCookies: false, + DNSCookieSecret: secretHex, + }, + cookieMu: sync.RWMutex{}, + } + assert.NoError(t, p.initCookieSecret()) + + q := dns.Question{Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET} + cc := []byte("12345678") + + req := newMsgWithCookie(q, cc, nil, 1232, true) + dctx := &DNSContext{ + Req: req, + Res: &dns.Msg{Question: []dns.Question{q}}, + Addr: netip.MustParseAddrPort("192.0.2.5:53"), + doBit: true, + udpSize: 1500, + } + + // Incoming request: parse and strip. + p.handleRequestCookies(dctx) + assert.Equal(t, cc, dctx.ReqClientCookie) + assert.Empty(t, dctx.Req.IsEdns0().Option, "cookie must be stripped before upstream") + + // Prepare response with a bogus upstream cookie that must be removed. + respOpt := dctx.Res.IsEdns0() + if respOpt == nil { + dctx.Res.SetEdns0(1200, false) + respOpt = dctx.Res.IsEdns0() + } + respOpt.Option = append(respOpt.Option, &dns.EDNS0_COOKIE{Code: dns.EDNS0COOKIE, Cookie: hex.EncodeToString([]byte("badcookie"))}) + + p.handleResponseCookies(dctx) + + opt := dctx.Res.IsEdns0() + if assert.NotNil(t, opt) && assert.Len(t, opt.Option, 1) { + cook, ok := opt.Option[0].(*dns.EDNS0_COOKIE) + if assert.True(t, ok) { + raw, err := hex.DecodeString(cook.Cookie) + assert.NoError(t, err) + assert.Equal(t, append(cc, p.serverCookie(dctx.Addr.Addr(), cc)...), raw) + } + } + assert.Equal(t, uint16(1500), opt.UDPSize()) + assert.True(t, opt.Do()) +} + +func TestHandleCookiesDisabled(t *testing.T) { + p := &Proxy{Config: Config{DisableDNSCookies: true}} + q := dns.Question{Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET} + cc := []byte("12345678") + + req := newMsgWithCookie(q, cc, nil, 1232, false) + res := newMsgWithCookie(q, cc, []byte("servercookie"), 1232, false) + + dctx := &DNSContext{ + Req: req, + Res: res, + } + + p.handleRequestCookies(dctx) + assert.Nil(t, dctx.ReqClientCookie) + assert.Empty(t, req.IsEdns0().Option) + + p.handleResponseCookies(dctx) + assert.Empty(t, res.IsEdns0().Option) +} From 1b96acdd0781c3d2f12aa0001fba8dcca95ecc1a Mon Sep 17 00:00:00 2001 From: 0ln <8427756+0ln@users.noreply.github.com> Date: Mon, 8 Dec 2025 07:25:33 +0100 Subject: [PATCH 3/4] Update README.md to add DNS cookies support documentation --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 5f9f5bbf3..0f04f4cb6 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,8 @@ Usage of ./dnsproxy: If specified, dnsproxy will act as a DNS64 server. --dns64-prefix=subnet Prefix used to handle DNS64. If not specified, dnsproxy uses the 'Well-Known Prefix' 64:ff9b::. Can be specified multiple times. + --disable-dns-cookies + Disable DNS cookies support (which is enabled by default). --dnscrypt-config=path/-g path Path to a file with DNSCrypt configuration. You can generate one using https://github.com/ameshkov/dnscrypt. --dnscrypt-port=port/-y port @@ -304,6 +306,12 @@ Loads upstreams list from a file. ./dnsproxy -l 127.0.0.1 -p 5353 -u ./upstreams.txt ``` +DNS cookies are enabled by default. Disable them if needed: + +```shell +./dnsproxy --disable-dns-cookies +``` + ### DNS64 server `dnsproxy` is capable of working as a DNS64 server. From e5c5b29f2d44da502d9c5140092b5148d9574ff4 Mon Sep 17 00:00:00 2001 From: 0ln <8427756+0ln@users.noreply.github.com> Date: Tue, 9 Dec 2025 07:41:08 +0100 Subject: [PATCH 4/4] Refactor DNS cookies configuration and handling for consistency --- internal/cmd/config.go | 4 ++-- proxy/config.go | 4 ++-- proxy/cookie.go | 16 ++++++---------- proxy/cookie_test.go | 8 ++++---- proxy/dnscontext.go | 4 ++-- proxy/proxy.go | 2 +- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 255e7e515..2d9844ebc 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -40,11 +40,11 @@ type configuration struct { EDNSAddr string `yaml:"edns-addr"` // DisableDNSCookies strips EDNS Cookies from both requests and responses. - DisableDNSCookies bool `yaml:"disable_dns_cookies"` + DisableDNSCookies bool `yaml:"disable-dns-cookies"` // DNSCookieSecret is a hex-encoded 16-byte secret used to generate server // cookies. - DNSCookieSecret string `yaml:"dns_cookie_secret"` + DNSCookieSecret string `yaml:"dns-cookie-secret"` // UpstreamMode determines the logic through which upstreams will be used. // If not specified the [proxy.UpstreamModeLoadBalance] is used. diff --git a/proxy/config.go b/proxy/config.go index eaa179b29..2621572f2 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -177,11 +177,11 @@ type Config struct { // DisableDNSCookies strips EDNS Cookies from both requests and responses. // If false, the proxy responds with EDNS Cookies per RFC 7873 and RFC 9018. - DisableDNSCookies bool `yaml:"disable_dns_cookies" json:"disable_dns_cookies"` + DisableDNSCookies bool `yaml:"disable-dns-cookies" json:"disable_dns_cookies"` // DNSCookieSecret is a hex-encoded 16-byte secret used to generate server // cookies. If empty, a random secret is generated on start. - DNSCookieSecret string `yaml:"dns_cookie_secret" json:"dns_cookie_secret"` + DNSCookieSecret string `yaml:"dns-cookie-secret" json:"dns_cookie_secret"` // TODO(s.chzhen): Extract ratelimit settings to a separate structure. diff --git a/proxy/cookie.go b/proxy/cookie.go index 6a19e92a8..7960dbb4d 100644 --- a/proxy/cookie.go +++ b/proxy/cookie.go @@ -92,12 +92,8 @@ func (p *Proxy) handleRequestCookies(dctx *DNSContext) { if !p.DisableDNSCookies { if client, _ := parseCookie(dctx.Req); len(client) > 0 { - dctx.ReqClientCookie = client + dctx.reqClientCookie = client } - - stripCookie(dctx.Req) - - return } stripCookie(dctx.Req) @@ -116,7 +112,7 @@ func (p *Proxy) handleResponseCookies(dctx *DNSContext) { return } - if len(dctx.ReqClientCookie) == 0 { + if len(dctx.reqClientCookie) == 0 { stripCookie(dctx.Res) return @@ -129,12 +125,12 @@ func (p *Proxy) handleResponseCookies(dctx *DNSContext) { stripCookie(dctx.Res) - server := p.serverCookie(dctx.Addr.Addr(), dctx.ReqClientCookie) + server := p.serverCookie(dctx.Addr.Addr(), dctx.reqClientCookie) if len(server) == 0 { return } - setCookie(dctx.Res, dctx.ReqClientCookie, server, udpSize, dctx.doBit) + setCookie(dctx.Res, dctx.reqClientCookie, server, udpSize, dctx.doBit) } // serverCookie returns the server cookie for the provided address and client @@ -144,9 +140,9 @@ func (p *Proxy) serverCookie(ip netip.Addr, client []byte) (server []byte) { return nil } - p.cookieMu.RLock() + p.cookieMu.Lock() secret := p.cookieSecret - p.cookieMu.RUnlock() + p.cookieMu.Unlock() if len(secret) != cookieSecretLen { return nil diff --git a/proxy/cookie_test.go b/proxy/cookie_test.go index c7e89dde8..e51b35b91 100644 --- a/proxy/cookie_test.go +++ b/proxy/cookie_test.go @@ -79,7 +79,7 @@ func TestServerCookieDeterministic(t *testing.T) { DisableDNSCookies: false, DNSCookieSecret: "000102030405060708090a0b0c0d0e0f", }, - cookieMu: sync.RWMutex{}, + cookieMu: sync.Mutex{}, } err := p.initCookieSecret() assert.NoError(t, err) @@ -108,7 +108,7 @@ func TestHandleCookiesEnabled(t *testing.T) { DisableDNSCookies: false, DNSCookieSecret: secretHex, }, - cookieMu: sync.RWMutex{}, + cookieMu: sync.Mutex{}, } assert.NoError(t, p.initCookieSecret()) @@ -126,7 +126,7 @@ func TestHandleCookiesEnabled(t *testing.T) { // Incoming request: parse and strip. p.handleRequestCookies(dctx) - assert.Equal(t, cc, dctx.ReqClientCookie) + assert.Equal(t, cc, dctx.reqClientCookie) assert.Empty(t, dctx.Req.IsEdns0().Option, "cookie must be stripped before upstream") // Prepare response with a bogus upstream cookie that must be removed. @@ -166,7 +166,7 @@ func TestHandleCookiesDisabled(t *testing.T) { } p.handleRequestCookies(dctx) - assert.Nil(t, dctx.ReqClientCookie) + assert.Nil(t, dctx.reqClientCookie) assert.Empty(t, req.IsEdns0().Option) p.handleResponseCookies(dctx) diff --git a/proxy/dnscontext.go b/proxy/dnscontext.go index a92da3cb2..e2f4cee8f 100644 --- a/proxy/dnscontext.go +++ b/proxy/dnscontext.go @@ -97,9 +97,9 @@ type DNSContext struct { // doBit is the DNSSEC OK flag from request's EDNS0 RR if presented. doBit bool - // ReqClientCookie is the client part of the EDNS Cookie option from the + // reqClientCookie is the client part of the EDNS Cookie option from the // request, if any. - ReqClientCookie []byte + reqClientCookie []byte } // newDNSContext returns a new properly initialized *DNSContext. diff --git a/proxy/proxy.go b/proxy/proxy.go index ebf6aa75b..ef109c3f0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -183,7 +183,7 @@ type Proxy struct { cookieSecret []byte // cookieMu protects cookieSecret. - cookieMu sync.RWMutex + cookieMu sync.Mutex // udpOOBSize is the size of the out-of-band data for UDP connections. udpOOBSize int