1
0
Fork 0
forked from External/ergo
Includes a partially backwards-incompatible config change
This commit is contained in:
Shivaram Lingamneni 2019-11-18 01:42:48 -05:00
parent 5cce365092
commit 4050b6571a
8 changed files with 332 additions and 319 deletions

View file

@ -4,28 +4,9 @@
package connection_limits
import (
"fmt"
"net"
"sync"
"time"
"github.com/oragono/oragono/irc/utils"
)
// ThrottlerConfig controls the automated connection throttling.
type ThrottlerConfig struct {
Enabled bool
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
ConnectionsPerCidr int `yaml:"max-connections"`
DurationString string `yaml:"duration"`
Duration time.Duration `yaml:"duration-time"`
BanDurationString string `yaml:"ban-duration"`
BanDuration time.Duration
BanMessage string `yaml:"ban-message"`
Exempted []string
}
// ThrottleDetails holds the connection-throttling details for a subnet/IP.
type ThrottleDetails struct {
Start time.Time
@ -68,111 +49,3 @@ func (g *GenericThrottle) touch(now time.Time) (throttled bool, remainingTime ti
return false, 0
}
}
// Throttler manages automated client connection throttling.
type Throttler struct {
sync.RWMutex
enabled bool
ipv4Mask net.IPMask
ipv6Mask net.IPMask
subnetLimit int
duration time.Duration
population map[string]ThrottleDetails
// used by the server to ban clients that go over this limit
banDuration time.Duration
banMessage string
// exemptedNets holds networks that are exempt from limits
exemptedNets []net.IPNet
}
// ResetFor removes any existing count for the given address.
func (ct *Throttler) ResetFor(addr net.IP) {
ct.Lock()
defer ct.Unlock()
if !ct.enabled {
return
}
// remove
addrString := addrToKey(addr, ct.ipv4Mask, ct.ipv6Mask)
delete(ct.population, addrString)
}
// AddClient introduces a new client connection if possible. If we can't, throws an error instead.
func (ct *Throttler) AddClient(addr net.IP) error {
ct.Lock()
defer ct.Unlock()
if !ct.enabled {
return nil
}
// check exempted lists
if utils.IPInNets(addr, ct.exemptedNets) {
return nil
}
// check throttle
addrString := addrToKey(addr, ct.ipv4Mask, ct.ipv6Mask)
details := ct.population[addrString] // retrieve mutable throttle state from the map
// add in constant state to process the limiting operation
g := GenericThrottle{
ThrottleDetails: details,
Duration: ct.duration,
Limit: ct.subnetLimit,
}
throttled, _ := g.Touch() // actually check the limit
ct.population[addrString] = g.ThrottleDetails // store modified mutable state
if throttled {
return errTooManyClients
} else {
return nil
}
}
func (ct *Throttler) BanDuration() time.Duration {
ct.RLock()
defer ct.RUnlock()
return ct.banDuration
}
func (ct *Throttler) BanMessage() string {
ct.RLock()
defer ct.RUnlock()
return ct.banMessage
}
// ApplyConfig atomically applies a config update to a throttler
func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error {
// assemble exempted nets
exemptedNets, err := utils.ParseNetList(config.Exempted)
if err != nil {
return fmt.Errorf("Could not parse throttle exemption list: %v", err.Error())
}
ct.Lock()
defer ct.Unlock()
if ct.population == nil {
ct.population = make(map[string]ThrottleDetails)
}
ct.enabled = config.Enabled
ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
ct.subnetLimit = config.ConnectionsPerCidr
ct.duration = config.Duration
ct.banDuration = config.BanDuration
ct.banMessage = config.BanMessage
ct.exemptedNets = exemptedNets
return nil
}