+ rewrites: support IP exception; support "pass A only" case
+ rewrites: support IP exception host.com -> 1.2.3.4 host.com -> AAAA Response: A: A = 1.2.3.4 AAAA: AAAA = <IPv6 address of host.com> + rewrites: support "pass A only" case host.com -> A Response: A: A = <IPv4 address of host.com> AAAA: <empty>
This commit is contained in:
parent
9df7aba9e1
commit
5980db1a2d
104
AGHTechDoc.md
104
AGHTechDoc.md
|
@ -980,6 +980,110 @@ Response:
|
|||
This section allows the administrator to easily configure custom DNS response for a specific domain name.
|
||||
A, AAAA and CNAME records are supported.
|
||||
|
||||
Syntax:
|
||||
|
||||
key -> value
|
||||
|
||||
where `key` is a host name or a wild card that matches Question in DNS request
|
||||
and `value` is either:
|
||||
* IPv4 address: use this IP in A response
|
||||
* IPv6 address: use this IP in AAAA response
|
||||
* canonical name: add CNAME record
|
||||
* "<key>": CNAME exception - pass request to upstream
|
||||
* "A": A exception - pass A request to upstream
|
||||
* "AAAA": AAAA exception - pass AAAA request to upstream
|
||||
|
||||
|
||||
#### Example: A record
|
||||
|
||||
host.com -> 1.2.3.4
|
||||
|
||||
Response:
|
||||
|
||||
A:
|
||||
A = 1.2.3.4
|
||||
AAAA:
|
||||
<empty>
|
||||
|
||||
#### Example: AAAA record
|
||||
|
||||
host.com -> ::1
|
||||
|
||||
Response:
|
||||
|
||||
A:
|
||||
<empty>
|
||||
AAAA:
|
||||
AAAA = ::1
|
||||
|
||||
#### Example: CNAME record
|
||||
|
||||
sub.host.com -> host.com
|
||||
|
||||
Response:
|
||||
|
||||
A:
|
||||
CNAME = host.com
|
||||
A = <IPv4 address of host.com>
|
||||
AAAA:
|
||||
CNAME = host.com
|
||||
AAAA = <IPv6 address of host.com>
|
||||
|
||||
#### Example: CNAME+A records
|
||||
|
||||
sub.host.com -> host.com
|
||||
host.com -> 1.2.3.4
|
||||
|
||||
Response:
|
||||
|
||||
A:
|
||||
CNAME = host.com
|
||||
A = 1.2.3.4
|
||||
AAAA:
|
||||
CNAME = host.com
|
||||
|
||||
#### Example: Wildcard CNAME+A record with CNAME exception
|
||||
|
||||
*.host.com -> 1.2.3.4
|
||||
pass.host.com -> pass.host.com
|
||||
|
||||
Response to `my.host.com`:
|
||||
|
||||
A:
|
||||
A = 1.2.3.4
|
||||
AAAA:
|
||||
<empty>
|
||||
|
||||
Response to `pass.host.com`:
|
||||
|
||||
A:
|
||||
A = <IPv4 address of pass.host.com>
|
||||
AAAA:
|
||||
AAAA = <IPv6 address of pass.host.com>
|
||||
|
||||
#### Example: A record with AAAA exception
|
||||
|
||||
host.com -> 1.2.3.4
|
||||
host.com -> AAAA
|
||||
|
||||
Response:
|
||||
|
||||
A:
|
||||
A = 1.2.3.4
|
||||
AAAA:
|
||||
AAAA = <IPv6 address of host.com>
|
||||
|
||||
#### Example: pass A only
|
||||
|
||||
host.com -> A
|
||||
|
||||
Response:
|
||||
|
||||
A:
|
||||
A = <IPv4 address of host.com>
|
||||
AAAA:
|
||||
<empty>
|
||||
|
||||
|
||||
### API: List rewrite entries
|
||||
|
||||
|
|
|
@ -316,7 +316,7 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
|||
var result Result
|
||||
var err error
|
||||
|
||||
result = d.processRewrites(host)
|
||||
result = d.processRewrites(host, qtype)
|
||||
if result.Reason == ReasonRewrite {
|
||||
return result, nil
|
||||
}
|
||||
|
@ -398,8 +398,8 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
|||
// . if found, set domain name to canonical name
|
||||
// . repeat for the new domain name (Note: we return only the last CNAME)
|
||||
// . Find A or AAAA record for a domain name (exact match or by wildcard)
|
||||
// . if found, return IP addresses (both IPv4 and IPv6)
|
||||
func (d *Dnsfilter) processRewrites(host string) Result {
|
||||
// . if found, set IP addresses (IPv4 or IPv6 depending on qtype) in Result.IPList array
|
||||
func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
|
||||
var res Result
|
||||
|
||||
d.confLock.RLock()
|
||||
|
@ -432,7 +432,14 @@ func (d *Dnsfilter) processRewrites(host string) Result {
|
|||
}
|
||||
|
||||
for _, r := range rr {
|
||||
if r.Type != dns.TypeCNAME {
|
||||
if (r.Type == dns.TypeA && qtype == dns.TypeA) ||
|
||||
(r.Type == dns.TypeAAAA && qtype == dns.TypeAAAA) {
|
||||
|
||||
if r.IP == nil { // IP exception
|
||||
res.Reason = 0
|
||||
return res
|
||||
}
|
||||
|
||||
res.IPList = append(res.IPList, r.IP)
|
||||
log.Debug("Rewrite: A/AAAA for %s is %s", host, r.IP)
|
||||
}
|
||||
|
|
|
@ -69,6 +69,16 @@ func (a rewritesArray) Less(i, j int) bool {
|
|||
|
||||
// Prepare entry for use
|
||||
func (r *RewriteEntry) prepare() {
|
||||
if r.Answer == "AAAA" {
|
||||
r.IP = nil
|
||||
r.Type = dns.TypeAAAA
|
||||
return
|
||||
} else if r.Answer == "A" {
|
||||
r.IP = nil
|
||||
r.Type = dns.TypeA
|
||||
return
|
||||
}
|
||||
|
||||
ip := net.ParseIP(r.Answer)
|
||||
if ip == nil {
|
||||
r.Type = dns.TypeCNAME
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -20,16 +21,21 @@ func TestRewrites(t *testing.T) {
|
|||
RewriteEntry{"www.host.com", "host.com", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
r := d.processRewrites("host2.com")
|
||||
r := d.processRewrites("host2.com", dns.TypeA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
|
||||
r = d.processRewrites("www.host.com")
|
||||
r = d.processRewrites("www.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, "host.com", r.CanonName)
|
||||
assert.True(t, len(r.IPList) == 3)
|
||||
assert.Equal(t, 2, len(r.IPList))
|
||||
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||
assert.True(t, r.IPList[1].Equal(net.ParseIP("1.2.3.5")))
|
||||
assert.True(t, r.IPList[2].Equal(net.ParseIP("1:2:3::4")))
|
||||
|
||||
r = d.processRewrites("www.host.com", dns.TypeAAAA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, "host.com", r.CanonName)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.True(t, r.IPList[0].Equal(net.ParseIP("1:2:3::4")))
|
||||
|
||||
// wildcard
|
||||
d.Rewrites = []RewriteEntry{
|
||||
|
@ -37,15 +43,15 @@ func TestRewrites(t *testing.T) {
|
|||
RewriteEntry{"*.host.com", "1.2.3.5", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
r = d.processRewrites("host.com")
|
||||
r = d.processRewrites("host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||
|
||||
r = d.processRewrites("www.host.com")
|
||||
r = d.processRewrites("www.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.5")))
|
||||
|
||||
r = d.processRewrites("www.host2.com")
|
||||
r = d.processRewrites("www.host2.com", dns.TypeA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
|
||||
// override a wildcard
|
||||
|
@ -54,7 +60,7 @@ func TestRewrites(t *testing.T) {
|
|||
RewriteEntry{"*.host.com", "1.2.3.5", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
r = d.processRewrites("a.host.com")
|
||||
r = d.processRewrites("a.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.True(t, len(r.IPList) == 1)
|
||||
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||
|
@ -65,7 +71,7 @@ func TestRewrites(t *testing.T) {
|
|||
RewriteEntry{"*.host.com", "host.com", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
r = d.processRewrites("www.host.com")
|
||||
r = d.processRewrites("www.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, "host.com", r.CanonName)
|
||||
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||
|
@ -77,7 +83,7 @@ func TestRewrites(t *testing.T) {
|
|||
RewriteEntry{"host.com", "1.2.3.4", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
r = d.processRewrites("b.host.com")
|
||||
r = d.processRewrites("b.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, "host.com", r.CanonName)
|
||||
assert.True(t, len(r.IPList) == 1)
|
||||
|
@ -90,7 +96,7 @@ func TestRewrites(t *testing.T) {
|
|||
RewriteEntry{"*.somehost.com", "1.2.3.4", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
r = d.processRewrites("b.host.com")
|
||||
r = d.processRewrites("b.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, "x.somehost.com", r.CanonName)
|
||||
assert.True(t, len(r.IPList) == 1)
|
||||
|
@ -108,25 +114,25 @@ func TestRewritesLevels(t *testing.T) {
|
|||
d.prepareRewrites()
|
||||
|
||||
// match exact
|
||||
r := d.processRewrites("host.com")
|
||||
r := d.processRewrites("host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "1.1.1.1", r.IPList[0].String())
|
||||
|
||||
// match L2
|
||||
r = d.processRewrites("sub.host.com")
|
||||
r = d.processRewrites("sub.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "2.2.2.2", r.IPList[0].String())
|
||||
|
||||
// match L3
|
||||
r = d.processRewrites("my.sub.host.com")
|
||||
r = d.processRewrites("my.sub.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "3.3.3.3", r.IPList[0].String())
|
||||
}
|
||||
|
||||
func TestRewritesException(t *testing.T) {
|
||||
func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
d := Dnsfilter{}
|
||||
// wildcard; exception for a sub-domain
|
||||
d.Rewrites = []RewriteEntry{
|
||||
|
@ -136,13 +142,13 @@ func TestRewritesException(t *testing.T) {
|
|||
d.prepareRewrites()
|
||||
|
||||
// match sub-domain
|
||||
r := d.processRewrites("my.host.com")
|
||||
r := d.processRewrites("my.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "2.2.2.2", r.IPList[0].String())
|
||||
|
||||
// match sub-domain, but handle exception
|
||||
r = d.processRewrites("sub.host.com")
|
||||
r = d.processRewrites("sub.host.com", dns.TypeA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
}
|
||||
|
||||
|
@ -156,12 +162,54 @@ func TestRewritesExceptionWC(t *testing.T) {
|
|||
d.prepareRewrites()
|
||||
|
||||
// match sub-domain
|
||||
r := d.processRewrites("my.host.com")
|
||||
r := d.processRewrites("my.host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "2.2.2.2", r.IPList[0].String())
|
||||
|
||||
// match sub-domain, but handle exception
|
||||
r = d.processRewrites("my.sub.host.com")
|
||||
r = d.processRewrites("my.sub.host.com", dns.TypeA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
}
|
||||
|
||||
func TestRewritesExceptionIP(t *testing.T) {
|
||||
d := Dnsfilter{}
|
||||
// exception for AAAA record
|
||||
d.Rewrites = []RewriteEntry{
|
||||
RewriteEntry{"host.com", "1.2.3.4", 0, nil},
|
||||
RewriteEntry{"host.com", "AAAA", 0, nil},
|
||||
RewriteEntry{"host2.com", "::1", 0, nil},
|
||||
RewriteEntry{"host2.com", "A", 0, nil},
|
||||
RewriteEntry{"host3.com", "A", 0, nil},
|
||||
}
|
||||
d.prepareRewrites()
|
||||
|
||||
// match domain
|
||||
r := d.processRewrites("host.com", dns.TypeA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "1.2.3.4", r.IPList[0].String())
|
||||
|
||||
// match exception
|
||||
r = d.processRewrites("host.com", dns.TypeAAAA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
|
||||
// match exception
|
||||
r = d.processRewrites("host2.com", dns.TypeA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
|
||||
// match domain
|
||||
r = d.processRewrites("host2.com", dns.TypeAAAA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 1, len(r.IPList))
|
||||
assert.Equal(t, "::1", r.IPList[0].String())
|
||||
|
||||
// match exception
|
||||
r = d.processRewrites("host3.com", dns.TypeA)
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
|
||||
// match domain
|
||||
r = d.processRewrites("host3.com", dns.TypeAAAA)
|
||||
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||
assert.Equal(t, 0, len(r.IPList))
|
||||
}
|
||||
|
|
|
@ -65,12 +65,11 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
|||
}
|
||||
|
||||
for _, ip := range res.IPList {
|
||||
ip4 := ip.To4()
|
||||
if req.Question[0].Qtype == dns.TypeA && ip4 != nil {
|
||||
a := s.genAAnswer(req, ip4)
|
||||
if req.Question[0].Qtype == dns.TypeA {
|
||||
a := s.genAAnswer(req, ip.To4())
|
||||
a.Hdr.Name = dns.Fqdn(name)
|
||||
resp.Answer = append(resp.Answer, a)
|
||||
} else if req.Question[0].Qtype == dns.TypeAAAA && ip4 == nil {
|
||||
} else if req.Question[0].Qtype == dns.TypeAAAA {
|
||||
a := s.genAAAAAnswer(req, ip)
|
||||
a.Hdr.Name = dns.Fqdn(name)
|
||||
resp.Answer = append(resp.Answer, a)
|
||||
|
|
Loading…
Reference in New Issue