-
Notifications
You must be signed in to change notification settings - Fork 62
/
blocklistdb-hosts.go
123 lines (113 loc) · 2.62 KB
/
blocklistdb-hosts.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package rdns
import (
"net"
"strings"
"github.com/miekg/dns"
)
// HostsDB holds a list of hosts-file entries that are used in blocklists to spoof or bloc requests.
// IP4 and IP6 records can be spoofed independently, however it's not possible to block only one type. If
// IP4 is given but no IP6, then a domain match will still result in an NXDOMAIN for the IP6 address.
type HostsDB struct {
name string
filters map[string]ipRecords
ptrMap map[string][]string // PTR lookup map
loader BlocklistLoader
}
// Max number of A/AAAA records created for hosts blocklist
const maxHostsResponses = 10
type ipRecords struct {
ip4 []net.IP
ip6 []net.IP
}
var _ BlocklistDB = &HostsDB{}
// NewHostsDB returns a new instance of a matcher for a list of regular expressions.
func NewHostsDB(name string, loader BlocklistLoader) (*HostsDB, error) {
rules, err := loader.Load()
if err != nil {
return nil, err
}
filters := make(map[string]ipRecords)
ptrMap := make(map[string][]string)
for _, r := range rules {
r = strings.TrimSpace(r)
fields := strings.Fields(r)
if len(fields) == 0 {
continue
}
ipString := fields[0]
names := fields[1:]
if strings.HasPrefix(ipString, "#") {
continue
}
if len(names) == 0 {
continue
}
ip := net.ParseIP(ipString)
var isIP4 bool
if ip4 := ip.To4(); len(ip4) == net.IPv4len {
isIP4 = true
}
if ip.IsUnspecified() {
ip = nil
}
for _, name := range names {
name = strings.TrimSuffix(name, ".")
ips := filters[name]
if isIP4 {
if len(ips.ip4) > maxHostsResponses {
continue
}
ips.ip4 = append(ips.ip4, ip)
} else {
if len(ips.ip6) > maxHostsResponses {
continue
}
ips.ip6 = append(ips.ip6, ip)
}
filters[name] = ips
}
reverseAddr, err := dns.ReverseAddr(ipString)
if err != nil {
continue
}
ptrMap[reverseAddr] = append(ptrMap[reverseAddr], names...)
}
return &HostsDB{name, filters, ptrMap, loader}, nil
}
func (m *HostsDB) Reload() (BlocklistDB, error) {
return NewHostsDB(m.name, m.loader)
}
func (m *HostsDB) Match(q dns.Question) ([]net.IP, []string, *BlocklistMatch, bool) {
if q.Qtype == dns.TypePTR {
names, ok := m.ptrMap[q.Name]
var rule string
if len(names) > 0 {
rule = names[0]
}
return nil, names, &BlocklistMatch{
List: m.name,
Rule: rule,
}, ok
}
name := strings.TrimSuffix(q.Name, ".")
ips, ok := m.filters[name]
if q.Qtype == dns.TypeA {
return ips.ip4,
nil,
&BlocklistMatch{
List: m.name,
Rule: name,
},
ok
}
return ips.ip6,
nil,
&BlocklistMatch{
List: m.name,
Rule: name,
},
ok
}
func (m *HostsDB) String() string {
return "Hosts"
}