-
Notifications
You must be signed in to change notification settings - Fork 0
/
DnsFilter.go
121 lines (109 loc) · 2.44 KB
/
DnsFilter.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
package filter
import (
"bufio"
"errors"
"log"
"net/http"
"strings"
"sync"
"time"
)
type DnsFilter struct {
refreshUrl string
refreshInterval time.Duration
filterLock sync.RWMutex
denyList []string
HttpErrorChan chan error
}
type Option func(filter *DnsFilter) error
func NewDnsFilter(opts ...Option) (*DnsFilter, error) {
filter := &DnsFilter{
HttpErrorChan: make(chan error),
}
for _, opt := range opts {
err := opt(filter)
if err != nil {
return nil, err
}
}
return filter, nil
}
func WithZones(zones ...string) Option {
return func(filter *DnsFilter) error {
return filter.AddZones(zones...)
}
}
func WithHttpRefresh(refreshUrl string, refreshInterval time.Duration) Option {
return func(filter *DnsFilter) error {
filter.refreshUrl = refreshUrl
filter.refreshInterval = refreshInterval
go filter.startUpdatesRanges()
return nil
}
}
func (filter *DnsFilter) AddZones(zones ...string) (err error) {
for _, zone := range zones {
err = filter.AddZone(zone)
if err != nil {
return err
}
}
return nil
}
func (filter *DnsFilter) AddZone(zone string) (err error) {
if !filter.IsHostnameAllowed(zone) {
return nil
}
log.Printf("adding %s in blocklist", zone)
filter.filterLock.Lock()
defer filter.filterLock.Unlock()
filter.denyList = append(filter.denyList, zone)
return nil
}
func (filter *DnsFilter) IsHostnameAllowed(hostname string) bool {
filter.filterLock.RLock()
defer filter.filterLock.RUnlock()
for _, zone := range filter.denyList {
if zone == hostname || strings.HasSuffix(hostname, "."+zone) {
return false
}
}
return true
}
var ErrHttpRefreshZone = errors.New("network error refreshing blocklist from http source")
var ErrHttpRefreshStatus = errors.New("bad http status from http blocklist source")
func (filter *DnsFilter) startUpdatesRanges() {
for {
err := filter.updateRanges()
if err != nil {
select {
case filter.HttpErrorChan <- err:
default:
log.Println(err)
}
}
time.Sleep(filter.refreshInterval)
}
}
func (filter *DnsFilter) updateRanges() error {
resp, err := http.Get(filter.refreshUrl)
if err != nil {
return ErrHttpRefreshZone
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return ErrHttpRefreshStatus
}
reader := bufio.NewScanner(resp.Body)
for reader.Scan() {
err = filter.AddZone(reader.Text())
if err != nil {
select {
case filter.HttpErrorChan <- err:
default:
log.Println(err)
}
}
}
return nil
}