blocky/resolver/sudn_resolver.go

155 lines
3.5 KiB
Go

package resolver
import (
"fmt"
"net"
"strings"
"github.com/0xERR0R/blocky/model"
"github.com/miekg/dns"
)
const (
sudnTest = "test."
sudnInvalid = "invalid."
sudnLocalhost = "localhost."
mdnsLocal = "local."
)
func sudnArpaSlice() []string {
return []string{
"10.in-addr.arpa.",
"21.172.in-addr.arpa.",
"26.172.in-addr.arpa.",
"16.172.in-addr.arpa.",
"22.172.in-addr.arpa.",
"27.172.in-addr.arpa.",
"17.172.in-addr.arpa.",
"30.172.in-addr.arpa.",
"28.172.in-addr.arpa.",
"18.172.in-addr.arpa.",
"23.172.in-addr.arpa.",
"29.172.in-addr.arpa.",
"19.172.in-addr.arpa.",
"24.172.in-addr.arpa.",
"31.172.in-addr.arpa.",
"20.172.in-addr.arpa.",
"25.172.in-addr.arpa.",
"168.192.in-addr.arpa.",
}
}
type defaultIPs struct {
loopbackV4 net.IP
loopbackV6 net.IP
}
type SpecialUseDomainNamesResolver struct {
NextResolver
defaults *defaultIPs
}
func NewSpecialUseDomainNamesResolver() ChainedResolver {
return &SpecialUseDomainNamesResolver{
defaults: &defaultIPs{
loopbackV4: net.ParseIP("127.0.0.1"),
loopbackV6: net.IPv6loopback,
},
}
}
func (r *SpecialUseDomainNamesResolver) Resolve(request *model.Request) (*model.Response, error) {
// RFC 6761 - negative
if r.isSpecial(request, sudnArpaSlice()...) ||
r.isSpecial(request, sudnInvalid) ||
r.isSpecial(request, sudnTest) {
return r.negativeResponse(request)
}
// RFC 6761 - switched
if r.isSpecial(request, sudnLocalhost) {
return r.responseSwitch(request, sudnLocalhost, r.defaults.loopbackV4, r.defaults.loopbackV6)
}
// RFC 6762 - negative
if r.isSpecial(request, mdnsLocal) {
return r.negativeResponse(request)
}
return r.next.Resolve(request)
}
// RFC 6761 & 6762 are always active
func (r *SpecialUseDomainNamesResolver) Configuration() []string {
return []string{}
}
func (r *SpecialUseDomainNamesResolver) isSpecial(request *model.Request, names ...string) bool {
domainFromQuestion := request.Req.Question[0].Name
for _, n := range names {
if domainFromQuestion == n ||
strings.HasSuffix(domainFromQuestion, fmt.Sprintf(".%s", n)) {
return true
}
}
return false
}
func (r *SpecialUseDomainNamesResolver) responseSwitch(request *model.Request,
name string, ipV4, ipV6 net.IP) (*model.Response, error) {
qtype := request.Req.Question[0].Qtype
switch qtype {
case dns.TypeA:
return r.positiveResponse(request, name, dns.TypeA, ipV4)
case dns.TypeAAAA:
return r.positiveResponse(request, name, dns.TypeAAAA, ipV6)
default:
return r.negativeResponse(request)
}
}
func (r *SpecialUseDomainNamesResolver) positiveResponse(request *model.Request,
name string, rtype uint16, ip net.IP) (*model.Response, error) {
response := newResponseMsg(request)
response.Rcode = dns.RcodeSuccess
hdr := dns.RR_Header{
Name: name,
Rrtype: rtype,
Class: dns.ClassINET,
Ttl: 0,
}
if rtype != dns.TypeA && rtype != dns.TypeAAAA {
return nil, fmt.Errorf("invalid response type")
}
var rr dns.RR
if rtype == dns.TypeA {
rr = &dns.A{
A: ip,
Hdr: hdr,
}
} else {
rr = &dns.AAAA{
AAAA: ip,
Hdr: hdr,
}
}
response.Answer = []dns.RR{rr}
return r.returnResponseModel(response)
}
func (r *SpecialUseDomainNamesResolver) negativeResponse(request *model.Request) (*model.Response, error) {
response := newResponseMsg(request)
response.Rcode = dns.RcodeNameError
return r.returnResponseModel(response)
}
func (r *SpecialUseDomainNamesResolver) returnResponseModel(response *dns.Msg) (*model.Response, error) {
return returnResponseModel(response, model.ResponseTypeSPECIAL, "Special-Use Domain Name")
}