2021-02-02 12:05:47 +01:00
|
|
|
// Package resolver contains a censoring DNS resolver. Most queries are
|
|
|
|
// answered without censorship, but selected queries could either be
|
|
|
|
// discarded or replied to with a bogon or NXDOMAIN answer.
|
|
|
|
package resolver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
2021-09-09 18:01:20 +02:00
|
|
|
// Resolver resolves domain names.
|
|
|
|
type Resolver interface {
|
|
|
|
LookupHost(ctx context.Context, hostname string) ([]string, error)
|
|
|
|
}
|
|
|
|
|
2021-02-02 12:05:47 +01:00
|
|
|
// CensoringResolver is a censoring resolver.
|
|
|
|
type CensoringResolver struct {
|
|
|
|
blocked []string
|
|
|
|
hijacked []string
|
|
|
|
ignored []string
|
|
|
|
lookupHost func(ctx context.Context, host string) ([]string, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewCensoringResolver creates a new CensoringResolver instance using
|
|
|
|
// the specified list of keywords to censor. blocked is the list of
|
|
|
|
// keywords that trigger NXDOMAIN if they appear in a query. hijacked
|
|
|
|
// is similar but redirects to 127.0.0.1, where the transparent HTTP
|
|
|
|
// and TLS proxies will pick them up. dnsNetwork and dnsAddress are the
|
|
|
|
// settings to configure the upstream, non censored DNS.
|
|
|
|
func NewCensoringResolver(
|
2021-09-09 18:01:20 +02:00
|
|
|
blocked, hijacked, ignored []string, uncensored Resolver,
|
2021-02-02 12:05:47 +01:00
|
|
|
) *CensoringResolver {
|
|
|
|
return &CensoringResolver{
|
|
|
|
blocked: blocked,
|
|
|
|
hijacked: hijacked,
|
|
|
|
ignored: ignored,
|
|
|
|
lookupHost: uncensored.LookupHost,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CensoringResolver) roundtrip(rw dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
name := req.Question[0].Name
|
|
|
|
addrs, err := r.lookupHost(context.Background(), name)
|
|
|
|
var ips []net.IP
|
|
|
|
if err == nil {
|
|
|
|
for _, addr := range addrs {
|
|
|
|
if ip := net.ParseIP(addr); ip != nil {
|
|
|
|
ips = append(ips, ip)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.reply(rw, req, ips)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CensoringResolver) reply(
|
|
|
|
rw dns.ResponseWriter, req *dns.Msg, ips []net.IP,
|
|
|
|
) {
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.Compress = true
|
|
|
|
m.MsgHdr.RecursionAvailable = true
|
|
|
|
m.SetReply(req)
|
|
|
|
for _, ip := range ips {
|
|
|
|
ipv6 := strings.Contains(ip.String(), ":")
|
|
|
|
if !ipv6 && req.Question[0].Qtype == dns.TypeA {
|
|
|
|
m.Answer = append(m.Answer, &dns.A{
|
|
|
|
Hdr: dns.RR_Header{
|
|
|
|
Name: req.Question[0].Name,
|
|
|
|
Rrtype: dns.TypeA,
|
|
|
|
Class: dns.ClassINET,
|
|
|
|
Ttl: 0,
|
|
|
|
},
|
|
|
|
A: ip,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if m.Answer == nil {
|
|
|
|
m.SetRcode(req, dns.RcodeNameError)
|
|
|
|
}
|
|
|
|
rw.WriteMsg(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CensoringResolver) failure(rw dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
m := new(dns.Msg)
|
|
|
|
m.Compress = true
|
|
|
|
m.MsgHdr.RecursionAvailable = true
|
|
|
|
m.SetRcode(req, dns.RcodeServerFailure)
|
|
|
|
rw.WriteMsg(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServeDNS serves a DNS request
|
|
|
|
func (r *CensoringResolver) ServeDNS(rw dns.ResponseWriter, req *dns.Msg) {
|
|
|
|
if len(req.Question) < 1 {
|
|
|
|
r.failure(rw, req)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
name := req.Question[0].Name
|
|
|
|
for _, pattern := range r.blocked {
|
|
|
|
if strings.Contains(name, pattern) {
|
|
|
|
r.reply(rw, req, nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, pattern := range r.hijacked {
|
|
|
|
if strings.Contains(name, pattern) {
|
|
|
|
r.reply(rw, req, []net.IP{net.IPv4(127, 0, 0, 1)})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, pattern := range r.ignored {
|
|
|
|
if strings.Contains(name, pattern) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.roundtrip(rw, req)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start starts the DNS resolver
|
|
|
|
func (r *CensoringResolver) Start(address string) (*dns.Server, error) {
|
|
|
|
packetconn, err := net.ListenPacket("udp", address)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
server := &dns.Server{
|
|
|
|
Addr: address,
|
|
|
|
Handler: r,
|
|
|
|
Net: "udp",
|
|
|
|
PacketConn: packetconn,
|
|
|
|
}
|
|
|
|
go server.ActivateAndServe()
|
|
|
|
return server, nil
|
|
|
|
}
|