ip_limit_allowlist.go 2.1 KB

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