ip_limit_allowlist.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package job
  2. import (
  3. "net/netip"
  4. "slices"
  5. "strings"
  6. )
  7. // An address that matches is neither counted towards a client's IP limit nor
  8. // banned: counting it would still cut the shared network it protects (#5378).
  9. type ipLimitAllowlist struct {
  10. prefixes []netip.Prefix
  11. addrs []netip.Addr
  12. }
  13. // Comma-separated, each entry a CIDR or a bare address. Unparseable entries are
  14. // skipped: the validator uses these same rules, so only a hand-edited DB differs.
  15. func parseIpLimitAllowlist(raw string) ipLimitAllowlist {
  16. var list ipLimitAllowlist
  17. for field := range strings.SplitSeq(raw, ",") {
  18. field = strings.TrimSpace(field)
  19. if field == "" {
  20. continue
  21. }
  22. if prefix, err := netip.ParsePrefix(field); err == nil {
  23. // Unmapped: contains() unmaps the queried address, and Prefix.Contains
  24. // is false whenever the bit lengths disagree.
  25. if addr := prefix.Addr(); addr.Is4In6() {
  26. if p4, perr := addr.Unmap().Prefix(prefix.Bits() - 96); perr == nil {
  27. prefix = p4
  28. }
  29. }
  30. list.prefixes = append(list.prefixes, prefix.Masked())
  31. continue
  32. }
  33. if addr, err := netip.ParseAddr(field); err == nil {
  34. list.addrs = append(list.addrs, addr.Unmap())
  35. }
  36. }
  37. return list
  38. }
  39. func (l ipLimitAllowlist) empty() bool {
  40. return len(l.prefixes) == 0 && len(l.addrs) == 0
  41. }
  42. func (l ipLimitAllowlist) contains(ip string) bool {
  43. if l.empty() {
  44. return false
  45. }
  46. addr, err := netip.ParseAddr(strings.TrimSpace(ip))
  47. if err != nil {
  48. return false
  49. }
  50. addr = addr.Unmap()
  51. if slices.Contains(l.addrs, addr) {
  52. return true
  53. }
  54. for _, prefix := range l.prefixes {
  55. if prefix.Contains(addr) {
  56. return true
  57. }
  58. }
  59. return false
  60. }
  61. // split separates the entries an allowlist protects from the ones the limit
  62. // still applies to, preserving the caller's ordering in both.
  63. func (l ipLimitAllowlist) split(entries []IPWithTimestamp) (limited, allowed []IPWithTimestamp) {
  64. if l.empty() {
  65. return entries, nil
  66. }
  67. limited = make([]IPWithTimestamp, 0, len(entries))
  68. for _, entry := range entries {
  69. if l.contains(entry.IP) {
  70. allowed = append(allowed, entry)
  71. continue
  72. }
  73. limited = append(limited, entry)
  74. }
  75. return limited, allowed
  76. }