2017-03-27 14:15:02 +02:00
|
|
|
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
|
2017-01-11 13:38:16 +01:00
|
|
|
// released under the MIT license
|
|
|
|
|
|
|
|
package irc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2017-09-29 08:07:09 +02:00
|
|
|
"sync"
|
2017-01-11 13:38:16 +01:00
|
|
|
|
2017-06-15 18:14:19 +02:00
|
|
|
"github.com/goshuirc/irc-go/ircmatch"
|
2017-01-11 13:38:16 +01:00
|
|
|
"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 {
|
2017-11-22 10:41:11 +01:00
|
|
|
sync.RWMutex // tier 1
|
2017-01-11 13:38:16 +01:00
|
|
|
// 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)
|
|
|
|
|
2017-09-29 08:07:09 +02:00
|
|
|
km.RLock()
|
|
|
|
defer km.RUnlock()
|
2017-01-11 13:38:16 +01:00
|
|
|
for name, info := range km.entries {
|
|
|
|
allb[name] = info.Info
|
|
|
|
}
|
|
|
|
|
|
|
|
return allb
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddMask adds to the blocked list.
|
2017-11-19 01:32:32 +01:00
|
|
|
func (km *KLineManager) AddMask(mask string, length *IPRestrictTime, reason, operReason, operName string) {
|
2017-01-11 13:38:16 +01:00
|
|
|
kln := KLineInfo{
|
|
|
|
Mask: mask,
|
|
|
|
Matcher: ircmatch.MakeMatch(mask),
|
|
|
|
Info: IPBanInfo{
|
|
|
|
Time: length,
|
|
|
|
Reason: reason,
|
|
|
|
OperReason: operReason,
|
2017-11-19 01:32:32 +01:00
|
|
|
OperName: operName,
|
2017-01-11 13:38:16 +01:00
|
|
|
},
|
|
|
|
}
|
2017-09-29 08:07:09 +02:00
|
|
|
km.Lock()
|
2017-01-11 13:38:16 +01:00
|
|
|
km.entries[mask] = &kln
|
2017-09-29 08:07:09 +02:00
|
|
|
km.Unlock()
|
2017-01-11 13:38:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveMask removes a mask from the blocked list.
|
|
|
|
func (km *KLineManager) RemoveMask(mask string) {
|
2017-09-29 08:07:09 +02:00
|
|
|
km.Lock()
|
2017-01-11 13:38:16 +01:00
|
|
|
delete(km.entries, mask)
|
2017-09-29 08:07:09 +02:00
|
|
|
km.Unlock()
|
2017-01-11 13:38:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2017-09-29 08:07:09 +02:00
|
|
|
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()
|
2017-01-11 13:38:16 +01:00
|
|
|
|
|
|
|
for _, entryInfo := range km.entries {
|
2017-09-29 08:07:09 +02:00
|
|
|
if entryInfo.Info.Time != nil && entryInfo.Info.Time.IsExpired() {
|
|
|
|
doCleanup = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
matches := false
|
2017-01-11 13:38:16 +01:00
|
|
|
for _, mask := range masks {
|
|
|
|
if entryInfo.Matcher.Match(mask) {
|
|
|
|
matches = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2017-09-29 08:07:09 +02:00
|
|
|
if matches {
|
2017-01-11 13:38:16 +01:00
|
|
|
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)
|
|
|
|
|
2017-11-19 01:32:32 +01:00
|
|
|
// add oper name if it doesn't exist already
|
|
|
|
if info.OperName == "" {
|
|
|
|
info.OperName = s.name
|
|
|
|
}
|
|
|
|
|
2017-01-11 13:38:16 +01:00
|
|
|
// add to the server
|
2017-11-19 01:32:32 +01:00
|
|
|
s.klines.AddMask(mask, info.Time, info.Reason, info.OperReason, info.OperName)
|
2017-01-11 13:38:16 +01:00
|
|
|
|
|
|
|
return true // true to continue I guess?
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|