Skip to content

Commit 13fbfec

Browse files
authored
Merge branch 'main' into feat/mips64-architecture-support
2 parents 601efad + 78d5367 commit 13fbfec

7 files changed

Lines changed: 210 additions & 2 deletions

File tree

resolver/ecs_resolver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func (r *ECSResolver) Resolve(ctx context.Context, request *model.Request) (*mod
116116
// and the forwardEcs option is not enabled
117117
if r.cfg.IPv4Mask == 0 && r.cfg.IPv6Mask == 0 && so != nil && !r.cfg.Forward {
118118
logger.Debug("remove edns0 subnet option")
119-
util.RemoveEdns0Option[*dns.EDNS0_SUBNET](request.Req)
119+
util.RemoveEdns0OptionKeepRecord[*dns.EDNS0_SUBNET](request.Req)
120120
}
121121
}
122122

resolver/ecs_resolver_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,43 @@ var _ = Describe("EcsResolver", func() {
209209
HaveReason("Test")))
210210
})
211211
})
212+
213+
When("remove ECS information", func() {
214+
// no mask configured and forwarding disabled: the option the client sent is dropped
215+
BeforeEach(func() {
216+
sutConfig.IPv4Mask = 0
217+
sutConfig.IPv6Mask = 0
218+
sutConfig.Forward = false
219+
})
220+
221+
It("should keep the OPT record when the subnet was the only option", func(ctx context.Context) {
222+
request := newRequest("example.com.", A)
223+
request.ClientIP = origIP
224+
225+
request.Req.SetEdns0(1232, true)
226+
addEcsOption(request.Req, ecsIP, 32)
227+
228+
m.ResolveFn = func(ctx context.Context, req *Request) (*Response, error) {
229+
Expect(req.Req).ShouldNot(HaveEdnsOption(dns.EDNS0SUBNET))
230+
231+
// the OPT record still carries the DO bit and the buffer size the client advertised
232+
opt := req.Req.IsEdns0()
233+
Expect(opt).ShouldNot(BeNil())
234+
Expect(opt.Do()).Should(BeTrue())
235+
Expect(opt.UDPSize()).Should(BeNumerically("==", 1232))
236+
237+
return respondWith(mockAnswer), nil
238+
}
239+
240+
Expect(sut.Resolve(ctx, request)).
241+
Should(
242+
SatisfyAll(
243+
HaveNoAnswer(),
244+
HaveResponseType(ResponseTypeRESOLVED),
245+
HaveReturnCode(dns.RcodeSuccess),
246+
HaveReason("Test")))
247+
})
248+
})
212249
})
213250

214251
Context("maskIP", func() {

server/client_query.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ func (q clientQuery) normalizeResponse(res *dns.Msg) {
8080
// don't return an OPT record to a client that didn't use EDNS0 (RFC 6891 section 7)
8181
util.RemoveEdns0Record(res)
8282
} else if opt := res.IsEdns0(); opt != nil {
83+
// Blocky doesn't implement DNS Cookies (RFC 7873), so a Server Cookie in the response is
84+
// one an upstream issued for blocky itself, and blocky can't validate it when the client
85+
// returns it. Passing it on also makes the presence of a cookie depend on which upstream
86+
// answered and on whether the answer came from the cache (stored without an OPT record),
87+
// and a client that tracks cookie support per server address — c-ares does — discards the
88+
// cookieless answers of such a flip-flopping server as spoofed.
89+
util.RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](res)
90+
8391
// RFC 3225 §3: the DO bit of the query is copied into the response
8492
opt.SetDo(q.wantsDNSSEC)
8593

server/server.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,14 @@ func (s *Server) resolve(ctx context.Context, request *model.Request) (response
829829
// up front: the response is normalized against that, not the mutated request.
830830
query := newClientQuery(request)
831831

832+
// Blocky doesn't implement DNS Cookies (RFC 7873), so a Server Cookie a client returns is one
833+
// an upstream issued and blocky can neither validate nor reissue it. Forwarding it is worse
834+
// than dropping it: it is likely to reach a different upstream than the one that issued it
835+
// (`parallel_best` picks at random), which can't validate it either and may answer BADCOOKIE.
836+
// The OPT record itself is kept, since it still carries the DO bit and the buffer size the
837+
// client advertised.
838+
util.RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](request.Req)
839+
832840
switch {
833841
case len(request.Req.Question) == 0:
834842
m := new(dns.Msg)

server/server_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,74 @@ var _ = Describe("Running DNS server", func() {
13291329
})
13301330
})
13311331

1332+
When("the client sent a DNS Cookie", func() {
1333+
// Blocky doesn't implement DNS Cookies (RFC 7873): it can neither produce a Server
1334+
// Cookie of its own nor validate one a client returns. An upstream's cookie must
1335+
// therefore not reach the client, and the client's cookie must not reach an upstream.
1336+
const (
1337+
clientCookie = "0102030405060708"
1338+
serverCookie = "1112131415161718"
1339+
)
1340+
1341+
var upstreamRequest *dns.Msg
1342+
1343+
// chainWithUpstreamCookie records the request as the chain saw it and answers like a
1344+
// cookie-supporting upstream.
1345+
chainWithUpstreamCookie := func(req *model.Request) *dns.Msg {
1346+
upstreamRequest = req.Req.Copy()
1347+
1348+
res := chainResponse.SetReply(req.Req)
1349+
res.SetEdns0(4096, false)
1350+
util.SetEdns0Option(res, &dns.EDNS0_COOKIE{
1351+
Code: dns.EDNS0COOKIE, Cookie: clientCookie + serverCookie,
1352+
})
1353+
1354+
return res
1355+
}
1356+
1357+
resolveWithCookie := func(udpSize uint16, do bool) *model.Response {
1358+
s := newServerWithChain(chainWithUpstreamCookie)
1359+
1360+
clientMsg := util.NewMsgWithQuestion("example.com.", A)
1361+
clientMsg.SetEdns0(udpSize, do)
1362+
util.SetEdns0Option(clientMsg, &dns.EDNS0_COOKIE{Code: dns.EDNS0COOKIE, Cookie: clientCookie})
1363+
1364+
_, req := newRequest(ctx, net.ParseIP("1.2.3.4"), "", model.RequestProtocolUDP, clientMsg)
1365+
1366+
resp, err := s.resolve(ctx, req)
1367+
Expect(err).Should(Succeed())
1368+
1369+
return resp
1370+
}
1371+
1372+
BeforeEach(func() {
1373+
upstreamRequest = nil
1374+
})
1375+
1376+
It("removes the upstream's COOKIE option from the response", func() {
1377+
resp := resolveWithCookie(1232, false)
1378+
1379+
Expect(resp.Res).ShouldNot(HaveEdnsOption(dns.EDNS0COOKIE))
1380+
Expect(resp.Res.IsEdns0()).ShouldNot(BeNil())
1381+
})
1382+
1383+
It("does not forward the client's COOKIE option upstream", func() {
1384+
resolveWithCookie(1232, false)
1385+
1386+
Expect(upstreamRequest).ShouldNot(BeNil())
1387+
Expect(upstreamRequest).ShouldNot(HaveEdnsOption(dns.EDNS0COOKIE))
1388+
})
1389+
1390+
It("keeps the OPT record sent upstream when the cookie was the only option", func() {
1391+
resolveWithCookie(1232, true)
1392+
1393+
Expect(upstreamRequest).ShouldNot(BeNil())
1394+
Expect(upstreamRequest.IsEdns0()).ShouldNot(BeNil())
1395+
Expect(upstreamRequest.IsEdns0().Do()).Should(BeTrue())
1396+
Expect(upstreamRequest.IsEdns0().UDPSize()).Should(BeNumerically("==", 1232))
1397+
})
1398+
})
1399+
13321400
When("the client left the DO bit clear", func() {
13331401
// RFC 4035 section 3.2.1: the DNSSEC records the chain requested upstream on the
13341402
// client's behalf must not be added to the response of a client that didn't ask.

util/edns0.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ func GetEdns0Option[T EDNS0Option](msg *dns.Msg) T {
6565
// If there are no more options in the OPT record, the OPT record will be removed.
6666
// If the option is successfully removed, true will be returned.
6767
func RemoveEdns0Option[T EDNS0Option](msg *dns.Msg) bool {
68+
return removeEdns0Option[T](msg, true)
69+
}
70+
71+
// RemoveEdns0OptionKeepRecord removes the option according to the given type from the OPT record
72+
// in the Extra section of the given message, keeping the OPT record itself even when it becomes
73+
// empty: on a request its header still carries the DO bit and the UDP buffer size the client
74+
// advertised, and a response to an EDNS0 query must have one (RFC 6891 section 6.1.1).
75+
// If the option is successfully removed, true will be returned.
76+
func RemoveEdns0OptionKeepRecord[T EDNS0Option](msg *dns.Msg) bool {
77+
return removeEdns0Option[T](msg, false)
78+
}
79+
80+
func removeEdns0Option[T EDNS0Option](msg *dns.Msg, dropEmptyRecord bool) bool {
6881
if msg == nil {
6982
return false
7083
}
@@ -88,7 +101,7 @@ func RemoveEdns0Option[T EDNS0Option](msg *dns.Msg) bool {
88101
}
89102
}
90103

91-
if len(opt.Option) == 0 {
104+
if dropEmptyRecord && len(opt.Option) == 0 {
92105
RemoveEdns0Record(msg)
93106
}
94107

util/edns0_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,80 @@ var _ = Describe("EDNS0 utils", func() {
167167
})
168168
})
169169

170+
Describe("RemoveEdns0OptionKeepRecord", func() {
171+
When("the removed option is the only one in the OPT record", func() {
172+
BeforeEach(func() {
173+
opt := new(dns.OPT)
174+
opt.Hdr.Name = "."
175+
opt.Hdr.Rrtype = dns.TypeOPT
176+
opt.SetUDPSize(1232)
177+
opt.SetDo(true)
178+
opt.Option = append(opt.Option, new(dns.EDNS0_COOKIE))
179+
baseMsg.Extra = append(baseMsg.Extra, opt)
180+
})
181+
182+
It("should keep the OPT record with its UDP size and DO bit", func() {
183+
Expect(RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](baseMsg)).Should(BeTrue())
184+
185+
Expect(baseMsg).ShouldNot(HaveEdnsOption(dns.EDNS0COOKIE))
186+
187+
opt := baseMsg.IsEdns0()
188+
Expect(opt).ShouldNot(BeNil())
189+
Expect(opt.UDPSize()).Should(BeNumerically("==", 1232))
190+
Expect(opt.Do()).Should(BeTrue())
191+
})
192+
})
193+
194+
When("other options are present", func() {
195+
BeforeEach(func() {
196+
opt := new(dns.OPT)
197+
opt.Hdr.Name = "."
198+
opt.Hdr.Rrtype = dns.TypeOPT
199+
opt.Option = append(opt.Option, new(dns.EDNS0_COOKIE), new(dns.EDNS0_SUBNET))
200+
baseMsg.Extra = append(baseMsg.Extra, opt)
201+
})
202+
203+
It("should remove only the given option", func() {
204+
Expect(RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](baseMsg)).Should(BeTrue())
205+
206+
Expect(baseMsg).ShouldNot(HaveEdnsOption(dns.EDNS0COOKIE))
207+
Expect(baseMsg).Should(HaveEdnsOption(dns.EDNS0SUBNET))
208+
})
209+
})
210+
211+
When("Option is not present", func() {
212+
BeforeEach(func() {
213+
opt := new(dns.OPT)
214+
opt.Hdr.Name = "."
215+
opt.Hdr.Rrtype = dns.TypeOPT
216+
opt.Option = append(opt.Option, new(dns.EDNS0_EDE))
217+
baseMsg.Extra = append(baseMsg.Extra, opt)
218+
})
219+
220+
It("should return false and keep the OPT record", func() {
221+
Expect(RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](baseMsg)).Should(BeFalse())
222+
223+
Expect(baseMsg.IsEdns0()).ShouldNot(BeNil())
224+
})
225+
})
226+
227+
When("Extra is nil", func() {
228+
BeforeEach(func() {
229+
baseMsg.Extra = nil
230+
})
231+
232+
It("should return false", func() {
233+
Expect(RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](baseMsg)).Should(BeFalse())
234+
})
235+
})
236+
237+
When("message is nil", func() {
238+
It("should return false", func() {
239+
Expect(RemoveEdns0OptionKeepRecord[*dns.EDNS0_COOKIE](nil)).Should(BeFalse())
240+
})
241+
})
242+
})
243+
170244
Describe("SetEdns0Option", func() {
171245
When("Option is not present", func() {
172246
var eso *dns.EDNS0_SUBNET

0 commit comments

Comments
 (0)