package proxy import ( "sneak.berlin/go/smallwebwaf/internal/config" ) // whole is the percentage of each limit a client gets when no biased // threshold lowers its limits. const whole = 100 // percentage is a client's limit percentage for the rate limits or for // the byte limits, as the biased thresholds give it, and the setting that // gave it: "" with whole when none lowers that kind of limit. type percentage struct { percent int64 setting string } // biasedThresholdsSet reports whether a biased threshold can lower a // client's limits: one of its lists is not empty, or // SWWAF_UNKNOWN_LIMIT_PERCENT is below 100. The client's lookup is then // needed before its request goes on. func biasedThresholdsSet(cfg *config.Config) bool { return len(cfg.ASNLimitPercent) > 0 || len(cfg.CountryLimitPercent) > 0 || len(cfg.ASNBytesPercent) > 0 || len(cfg.CountryBytesPercent) > 0 || cfg.UnknownLimitPercent < whole } // limitPercentages returns a client's limit percentages, for the rate // limits and for the byte limits, by its AS number and country as looked // up, each "" when unknown. Each is the lowest of those the settings give // it, the first of them in the order below when several are lowest: the // percentage SWWAF_ASN_LIMIT_PERCENT gives its AS number, the one // SWWAF_COUNTRY_LIMIT_PERCENT gives its country, and, for a client // without a country, SWWAF_UNKNOWN_LIMIT_PERCENT. For the byte limits, // SWWAF_ASN_BYTES_PERCENT and SWWAF_COUNTRY_BYTES_PERCENT take the place // of the first two for an AS number or a country they list. func limitPercentages( cfg *config.Config, asn, country string, ) (percentage, percentage) { unknown := percentage{percent: whole} if country == "" { unknown = percentage{cfg.UnknownLimitPercent, "SWWAF_UNKNOWN_LIMIT_PERCENT"} } asnRequests := given(cfg.ASNLimitPercent, asn, "SWWAF_ASN_LIMIT_PERCENT") countryRequests := given(cfg.CountryLimitPercent, country, "SWWAF_COUNTRY_LIMIT_PERCENT") asnBytes, countryBytes := asnRequests, countryRequests if _, listed := cfg.ASNBytesPercent[asn]; listed { asnBytes = given(cfg.ASNBytesPercent, asn, "SWWAF_ASN_BYTES_PERCENT") } if _, listed := cfg.CountryBytesPercent[country]; listed { countryBytes = given(cfg.CountryBytesPercent, country, "SWWAF_COUNTRY_BYTES_PERCENT") } return lowest(asnRequests, countryRequests, unknown), lowest(asnBytes, countryBytes, unknown) } // given returns the percentage percents, the setting named setting, gives // code, an AS number or a country, or whole when it does not list code. func given(percents map[string]int64, code, setting string) percentage { percent, listed := percents[code] if !listed { return percentage{percent: whole} } return percentage{percent, setting} } // lowest returns the lowest of percentages below whole, the first of them // when several are lowest, or whole when none is below it. func lowest(percentages ...percentage) percentage { low := percentage{percent: whole} for _, p := range percentages { if p.percent < low.percent { low = p } } return low } // logged returns p as the log line and the notes of a ban give it: its // percent and setting, or nil and "" for whole, which they leave out. func (p percentage) logged() (*int64, string) { if p.percent == whole { return nil, "" } return &p.percent, p.setting }