-
Notifications
You must be signed in to change notification settings - Fork 0
/
kline.go
149 lines (126 loc) · 3.12 KB
/
kline.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import (
"encoding/json"
"sync"
"github.com/goshuirc/irc-go/ircmatch"
"github.com/tidwall/buntdb"
)
const (
keyKlineEntry = "bans.kline %s"
)
// KLineInfo contains the address itself and expiration time for a given network.
type KLineInfo struct {
// Mask that is blocked.
Mask string
// Matcher, to facilitate fast matching.
Matcher ircmatch.Matcher
// Info contains information on the ban.
Info IPBanInfo
}
// KLineManager manages and klines.
type KLineManager struct {
sync.RWMutex // tier 1
// kline'd entries
entries map[string]*KLineInfo
}
// NewKLineManager returns a new KLineManager.
func NewKLineManager() *KLineManager {
var km KLineManager
km.entries = make(map[string]*KLineInfo)
return &km
}
// AllBans returns all bans (for use with APIs, etc).
func (km *KLineManager) AllBans() map[string]IPBanInfo {
allb := make(map[string]IPBanInfo)
km.RLock()
defer km.RUnlock()
for name, info := range km.entries {
allb[name] = info.Info
}
return allb
}
// AddMask adds to the blocked list.
func (km *KLineManager) AddMask(mask string, length *IPRestrictTime, reason, operReason, operName string) {
kln := KLineInfo{
Mask: mask,
Matcher: ircmatch.MakeMatch(mask),
Info: IPBanInfo{
Time: length,
Reason: reason,
OperReason: operReason,
OperName: operName,
},
}
km.Lock()
km.entries[mask] = &kln
km.Unlock()
}
// RemoveMask removes a mask from the blocked list.
func (km *KLineManager) RemoveMask(mask string) {
km.Lock()
delete(km.entries, mask)
km.Unlock()
}
// CheckMasks returns whether or not the hostmask(s) are banned, and how long they are banned for.
func (km *KLineManager) CheckMasks(masks ...string) (isBanned bool, info *IPBanInfo) {
doCleanup := false
defer func() {
// asynchronously remove expired bans
if doCleanup {
go func() {
km.Lock()
defer km.Unlock()
for key, entry := range km.entries {
if entry.Info.Time.IsExpired() {
delete(km.entries, key)
}
}
}()
}
}()
km.RLock()
defer km.RUnlock()
for _, entryInfo := range km.entries {
if entryInfo.Info.Time != nil && entryInfo.Info.Time.IsExpired() {
doCleanup = true
continue
}
matches := false
for _, mask := range masks {
if entryInfo.Matcher.Match(mask) {
matches = true
break
}
}
if matches {
return true, &entryInfo.Info
}
}
// no matches!
return false, nil
}
func (s *Server) loadKLines() {
s.klines = NewKLineManager()
// load from datastore
s.store.View(func(tx *buntdb.Tx) error {
//TODO(dan): We could make this safer
tx.AscendKeys("bans.kline *", func(key, value string) bool {
// get address name
key = key[len("bans.kline "):]
mask := key
// load ban info
var info IPBanInfo
json.Unmarshal([]byte(value), &info)
// add oper name if it doesn't exist already
if info.OperName == "" {
info.OperName = s.name
}
// add to the server
s.klines.AddMask(mask, info.Time, info.Reason, info.OperReason, info.OperName)
return true // true to continue I guess?
})
return nil
})
}