diff --git a/default.yaml b/default.yaml index 6415f072..eaf94402 100644 --- a/default.yaml +++ b/default.yaml @@ -263,13 +263,23 @@ server: # - "192.168.1.1" # - "2001:0db8::/32" - # custom connection limits for certain IPs/networks. note that CIDR - # widths defined here override the default CIDR width --- the limit - # will apply to the entire CIDR no matter how large or small it is + # custom connection limits for certain IPs/networks. custom-limits: - # "8.8.0.0/16": - # max-concurrent-connections: 128 - # max-connections-per-window: 1024 + #"irccloud": + # nets: + # - "192.184.9.108" # highgate.irccloud.com + # - "192.184.9.110" # ealing.irccloud.com + # - "192.184.9.112" # charlton.irccloud.com + # - "192.184.10.118" # brockwell.irccloud.com + # - "192.184.10.9" # tooting.irccloud.com + # - "192.184.8.73" # hathersage.irccloud.com + # - "192.184.8.103" # stonehaven.irccloud.com + # - "5.254.36.57" # tinside.irccloud.com + # - "5.254.36.56/29" # additional ipv4 net + # - "2001:67c:2f08::/48" + # - "2a03:5180:f::/64" + # max-concurrent-connections: 2048 + # max-connections-per-window: 2048 # pluggable IP ban mechanism, via subprocess invocation # this can be used to check new connections against a DNSBL, for example diff --git a/irc/connection_limits/limiter.go b/irc/connection_limits/limiter.go index 208eb4cf..988448ac 100644 --- a/irc/connection_limits/limiter.go +++ b/irc/connection_limits/limiter.go @@ -19,14 +19,17 @@ var ( ) type CustomLimitConfig struct { + Nets []string MaxConcurrent int `yaml:"max-concurrent-connections"` MaxPerWindow int `yaml:"max-connections-per-window"` } // tuples the key-value pair of a CIDR and its custom limit/throttle values type customLimit struct { - CustomLimitConfig - ipNet net.IPNet + name string + maxConcurrent int + maxPerWindow int + nets []net.IPNet } // LimiterConfig controls the automated connection limits. @@ -71,14 +74,29 @@ func (config *LimiterConfig) postprocess() (err error) { return fmt.Errorf("Could not parse limiter exemption list: %v", err.Error()) } - for netStr, customLimitConf := range config.CustomLimits { - normalizedNet, err := utils.NormalizedNetFromString(netStr) - if err != nil { - return fmt.Errorf("Could not parse custom limit specification: %v", err.Error()) + for identifier, customLimitConf := range config.CustomLimits { + nets := make([]net.IPNet, len(customLimitConf.Nets)) + for i, netStr := range customLimitConf.Nets { + normalizedNet, err := utils.NormalizedNetFromString(netStr) + if err != nil { + return fmt.Errorf("Bad net %s in custom-limits block %s: %w", netStr, identifier, err) + } + nets[i] = normalizedNet + } + if len(customLimitConf.Nets) == 0 { + // see #1421: this is the legacy config format where the + // dictionary key of the block is a CIDR string + normalizedNet, err := utils.NormalizedNetFromString(identifier) + if err != nil { + return fmt.Errorf("Custom limit block %s has no defined nets", identifier) + } + nets = []net.IPNet{normalizedNet} } config.customLimits = append(config.customLimits, customLimit{ - CustomLimitConfig: customLimitConf, - ipNet: normalizedNet, + maxConcurrent: customLimitConf.MaxConcurrent, + maxPerWindow: customLimitConf.MaxPerWindow, + name: "*" + identifier, + nets: nets, }) } @@ -105,8 +123,10 @@ type Limiter struct { func (cl *Limiter) addrToKey(addr net.IP) (key string, limit int, throttle int) { // `key` will be a CIDR string like "8.8.8.8/32" or "2001:0db8::/32" for _, custom := range cl.config.customLimits { - if custom.ipNet.Contains(addr) { - return custom.ipNet.String(), custom.MaxConcurrent, custom.MaxPerWindow + for _, net := range custom.nets { + if net.Contains(addr) { + return custom.name, custom.maxConcurrent, custom.maxPerWindow + } } } diff --git a/irc/connection_limits/limiter_test.go b/irc/connection_limits/limiter_test.go index 8ef74c14..bf852b58 100644 --- a/irc/connection_limits/limiter_test.go +++ b/irc/connection_limits/limiter_test.go @@ -32,7 +32,8 @@ var baseConfig = LimiterConfig{ Exempted: []string{"localhost"}, CustomLimits: map[string]CustomLimitConfig{ - "8.8.0.0/16": { + "google": { + Nets: []string{"8.8.0.0/16"}, MaxConcurrent: 128, MaxPerWindow: 256, }, @@ -57,7 +58,7 @@ func TestKeying(t *testing.T) { assertEqual(maxWin, 8, t) key, maxConc, maxWin = limiter.addrToKey(easyParseIP("8.8.4.4")) - assertEqual(key, "8.8.0.0/16", t) + assertEqual(key, "*google", t) assertEqual(maxConc, 128, t) assertEqual(maxWin, 256, t) } diff --git a/traditional.yaml b/traditional.yaml index 7e163919..225afc9b 100644 --- a/traditional.yaml +++ b/traditional.yaml @@ -236,13 +236,23 @@ server: # - "192.168.1.1" # - "2001:0db8::/32" - # custom connection limits for certain IPs/networks. note that CIDR - # widths defined here override the default CIDR width --- the limit - # will apply to the entire CIDR no matter how large or small it is + # custom connection limits for certain IPs/networks. custom-limits: - # "8.8.0.0/16": - # max-concurrent-connections: 128 - # max-connections-per-window: 1024 + #"irccloud": + # nets: + # - "192.184.9.108" # highgate.irccloud.com + # - "192.184.9.110" # ealing.irccloud.com + # - "192.184.9.112" # charlton.irccloud.com + # - "192.184.10.118" # brockwell.irccloud.com + # - "192.184.10.9" # tooting.irccloud.com + # - "192.184.8.73" # hathersage.irccloud.com + # - "192.184.8.103" # stonehaven.irccloud.com + # - "5.254.36.57" # tinside.irccloud.com + # - "5.254.36.56/29" # additional ipv4 net + # - "2001:67c:2f08::/48" + # - "2a03:5180:f::/64" + # max-concurrent-connections: 2048 + # max-connections-per-window: 2048 # pluggable IP ban mechanism, via subprocess invocation # this can be used to check new connections against a DNSBL, for example