mirror of
https://github.com/0xERR0R/blocky.git
synced 2026-10-12 02:01:13 +00:00
delivery of blocking group as EDNS error(560) (#611)
This commit is contained in:
@@ -13,3 +13,4 @@ todo.txt
|
||||
!docs/config.yml
|
||||
node_modules
|
||||
package-lock.json
|
||||
.vscode/
|
||||
@@ -428,6 +428,7 @@ type Config struct {
|
||||
HostsFile HostsFileConfig `yaml:"hostsFile"`
|
||||
FqdnOnly bool `yaml:"fqdnOnly" default:"false"`
|
||||
Filtering FilteringConfig `yaml:"filtering"`
|
||||
Ede EdeConfig `yaml:"ede"`
|
||||
}
|
||||
|
||||
type BootstrapConfig bootstrapConfig // to avoid infinite recursion. See BootstrapConfig.UnmarshalYAML.
|
||||
@@ -540,6 +541,10 @@ type FilteringConfig struct {
|
||||
QueryTypes QTypeSet `yaml:"queryTypes"`
|
||||
}
|
||||
|
||||
type EdeConfig struct {
|
||||
Enable bool `yaml:"enable" default:"false"`
|
||||
}
|
||||
|
||||
// nolint:gochecknoglobals
|
||||
var (
|
||||
config = &Config{}
|
||||
|
||||
@@ -218,3 +218,8 @@ logFormat: text
|
||||
logTimestamp: true
|
||||
# optional: obfuscate log output (replace all alphanumeric characters with *) for user sensitive data like request domains or responses to increase privacy. Default: false
|
||||
logPrivacy: false
|
||||
|
||||
# optional: add EDE error codes to dns response
|
||||
ede:
|
||||
# enabled if true, Default: false
|
||||
enable: true
|
||||
@@ -633,6 +633,23 @@ Configuration parameters:
|
||||
refreshPeriod: 30m
|
||||
```
|
||||
|
||||
### Deliver EDE codes as EDNS0 option
|
||||
|
||||
DNS responses can be extended with EDE codes according to [RFC8914](https://datatracker.ietf.org/doc/rfc8914/).
|
||||
|
||||
Configuration parameters:
|
||||
|
||||
| Parameter | Type | Mandatory | Default value | Description |
|
||||
|--------------------------|--------------------------------|-----------|---------------|----------------------------------------------------|
|
||||
| ede.enable | bool | no | false | If true, DNS responses are deliverd with EDE codes |
|
||||
|
||||
!!! example
|
||||
|
||||
```yaml
|
||||
ede:
|
||||
enable: true
|
||||
```
|
||||
|
||||
## SSL certificate configuration (DoH / TLS listener)
|
||||
|
||||
See [Wiki - Configuration of HTTPS](https://github.com/0xERR0R/blocky/wiki/Configuration-of-HTTPS-for-DoH-and-Rest-API)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"github.com/0xERR0R/blocky/config"
|
||||
"github.com/0xERR0R/blocky/model"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type EdeResolver struct {
|
||||
NextResolver
|
||||
config config.EdeConfig
|
||||
}
|
||||
|
||||
func NewEdeResolver(cfg config.EdeConfig) ChainedResolver {
|
||||
return &EdeResolver{
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *EdeResolver) Resolve(request *model.Request) (*model.Response, error) {
|
||||
resp, err := r.next.Resolve(request)
|
||||
|
||||
if r.config.Enable {
|
||||
addExtraReasoning(resp)
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (r *EdeResolver) Configuration() (result []string) {
|
||||
if r.config.Enable {
|
||||
result = []string{"activated"}
|
||||
} else {
|
||||
result = []string{"deactivated"}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func addExtraReasoning(res *model.Response) {
|
||||
// dns.ExtendedErrorCodeOther seams broken in some clients
|
||||
infocode := convertToExtendedErrorCode(res.RType)
|
||||
if infocode > 0 {
|
||||
opt := new(dns.OPT)
|
||||
opt.Hdr.Name = "."
|
||||
opt.Hdr.Rrtype = dns.TypeOPT
|
||||
opt.Option = append(opt.Option, convertExtendedError(res, infocode))
|
||||
res.Res.Extra = append(res.Res.Extra, opt)
|
||||
}
|
||||
}
|
||||
|
||||
func convertExtendedError(input *model.Response, infocode uint16) *dns.EDNS0_EDE {
|
||||
return &dns.EDNS0_EDE{
|
||||
InfoCode: infocode,
|
||||
ExtraText: input.Reason,
|
||||
}
|
||||
}
|
||||
|
||||
func convertToExtendedErrorCode(input model.ResponseType) uint16 {
|
||||
switch input {
|
||||
case model.ResponseTypeRESOLVED:
|
||||
return dns.ExtendedErrorCodeOther
|
||||
case model.ResponseTypeCACHED:
|
||||
return dns.ExtendedErrorCodeCachedError
|
||||
case model.ResponseTypeCONDITIONAL:
|
||||
return dns.ExtendedErrorCodeForgedAnswer
|
||||
case model.ResponseTypeCUSTOMDNS:
|
||||
return dns.ExtendedErrorCodeForgedAnswer
|
||||
case model.ResponseTypeHOSTSFILE:
|
||||
return dns.ExtendedErrorCodeForgedAnswer
|
||||
case model.ResponseTypeNOTFQDN:
|
||||
return dns.ExtendedErrorCodeBlocked
|
||||
case model.ResponseTypeBLOCKED:
|
||||
return dns.ExtendedErrorCodeBlocked
|
||||
case model.ResponseTypeFILTERED:
|
||||
return dns.ExtendedErrorCodeFiltered
|
||||
default:
|
||||
return dns.ExtendedErrorCodeOther
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"github.com/0xERR0R/blocky/config"
|
||||
"github.com/0xERR0R/blocky/model"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var _ = Describe("EdeResolver", func() {
|
||||
var (
|
||||
sut *EdeResolver
|
||||
sutConfig config.EdeConfig
|
||||
m *MockResolver
|
||||
mockAnswer *dns.Msg
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
mockAnswer = new(dns.Msg)
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
m = &MockResolver{}
|
||||
m.On("Resolve", mock.Anything).Return(&model.Response{
|
||||
Res: mockAnswer,
|
||||
RType: model.ResponseTypeCUSTOMDNS,
|
||||
Reason: "Test",
|
||||
}, nil)
|
||||
|
||||
sut = NewEdeResolver(sutConfig).(*EdeResolver)
|
||||
sut.Next(m)
|
||||
})
|
||||
|
||||
When("Ede is disabled", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = config.EdeConfig{
|
||||
Enable: false,
|
||||
}
|
||||
})
|
||||
It("Shouldn't add EDE information", func() {
|
||||
resp, err := sut.Resolve(newRequest("example.com", dns.Type(dns.TypeA)))
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Res.Rcode).Should(Equal(dns.RcodeSuccess))
|
||||
Expect(resp.RType).Should(Equal(model.ResponseTypeCUSTOMDNS))
|
||||
Expect(resp.Res.Answer).Should(BeEmpty())
|
||||
Expect(resp.Res.Extra).Should(BeEmpty())
|
||||
|
||||
// delegated to next resolver
|
||||
Expect(m.Calls).Should(HaveLen(1))
|
||||
})
|
||||
It("Configure should output deactivated", func() {
|
||||
c := sut.Configuration()
|
||||
Expect(c).Should(HaveLen(1))
|
||||
Expect(c[0]).Should(Equal("deactivated"))
|
||||
})
|
||||
})
|
||||
When("Ede is enabled", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = config.EdeConfig{
|
||||
Enable: true,
|
||||
}
|
||||
})
|
||||
It("Should add EDE information", func() {
|
||||
resp, err := sut.Resolve(newRequest("example.com", dns.Type(dns.TypeA)))
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Res.Rcode).Should(Equal(dns.RcodeSuccess))
|
||||
Expect(resp.RType).Should(Equal(model.ResponseTypeCUSTOMDNS))
|
||||
Expect(resp.Res.Answer).Should(BeEmpty())
|
||||
Expect(resp.Res.Extra).Should(HaveLen(1))
|
||||
opt, ok := resp.Res.Extra[0].(*dns.OPT)
|
||||
Expect(ok).Should(BeTrue())
|
||||
Expect(opt).ShouldNot(BeNil())
|
||||
ede, ok := opt.Option[0].(*dns.EDNS0_EDE)
|
||||
Expect(ok).Should(BeTrue())
|
||||
Expect(ede.InfoCode).Should(Equal(dns.ExtendedErrorCodeForgedAnswer))
|
||||
Expect(ede.ExtraText).Should(Equal("Test"))
|
||||
})
|
||||
It("Configure should output activated", func() {
|
||||
c := sut.Configuration()
|
||||
Expect(c).Should(HaveLen(1))
|
||||
Expect(c[0]).Should(Equal("activated"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -399,6 +399,7 @@ func createQueryResolver(
|
||||
resolver.NewFilteringResolver(cfg.Filtering),
|
||||
resolver.NewFqdnOnlyResolver(*cfg),
|
||||
clientNamesResolver,
|
||||
resolver.NewEdeResolver(cfg.Ede),
|
||||
resolver.NewQueryLoggingResolver(cfg.QueryLog),
|
||||
resolver.NewMetricsResolver(cfg.Prometheus),
|
||||
resolver.NewRewriterResolver(cfg.CustomDNS.RewriteConfig, resolver.NewCustomDNSResolver(cfg.CustomDNS)),
|
||||
|
||||
Reference in New Issue
Block a user